line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Tie::FileLRUCache; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
79168
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
24
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
436
|
use Class::ParmList qw (simple_parms parse_parms); |
|
1
|
|
|
|
|
1340
|
|
|
1
|
|
|
|
|
53
|
|
7
|
1
|
|
|
1
|
|
432
|
use Digest::SHA1 qw(sha1_hex); |
|
1
|
|
|
|
|
653
|
|
|
1
|
|
|
|
|
56
|
|
8
|
1
|
|
|
1
|
|
5
|
use Fcntl qw (:flock); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
111
|
|
9
|
1
|
|
|
1
|
|
6
|
use File::Spec; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
22
|
|
10
|
1
|
|
|
1
|
|
610
|
use Storable qw (nstore nfreeze retrieve); |
|
1
|
|
|
|
|
2727
|
|
|
1
|
|
|
|
|
56
|
|
11
|
1
|
|
|
1
|
|
6
|
use Symbol qw (gensym); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
45
|
|
12
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
4
|
use vars qw ($VERSION); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
33
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
BEGIN { |
16
|
1
|
|
|
1
|
|
3422
|
$VERSION = "1.06"; |
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
########################################################################### |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Tie::FileLRUCache - A lightweight but robust filesystem based persistent LRU cache |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 CHANGES |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
1.06 2020.10.08 - Changed license to MIT License. Updated maintainer info. |
28
|
|
|
|
|
|
|
Updated build files. Added GitHub repo meta to build. |
29
|
|
|
|
|
|
|
Changed minimum supported version of Perl to 5.6. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
1.05 2005.09.13 - Changes to pod tests to make them more CPANTS friendly. |
32
|
|
|
|
|
|
|
No functional changes. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
1.04 2005.09.13 - Removed use of lexical warnings pragma to fix compatibility |
35
|
|
|
|
|
|
|
with Perl 5.005. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Fixed minor typographical errors in documentation. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
1.03 2005.09.10 - Changed build test to handle difference in treatment of hashes |
40
|
|
|
|
|
|
|
in scalar context between 5.6.x and 5.8.x versions of Perl that |
41
|
|
|
|
|
|
|
caused a test failure under Perl 5.6.x. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
1.02 2005.09.08 - Added build tests. Major code cleanup. Improved platform portability. |
44
|
|
|
|
|
|
|
Added and documented 'cache_dir', 'keep_last' and 'number_of_entries' |
45
|
|
|
|
|
|
|
methods. Added Module::Build support. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
1.01 1999.12.09 - Added detainting in internal cache maintaining |
50
|
|
|
|
|
|
|
functions to evade Taint's tainting |
51
|
|
|
|
|
|
|
of filenames read via readdir(). |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 SYNOPSIS |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 OBJECT INTERFACE |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
use Tie::FileLRUCache; |
58
|
|
|
|
|
|
|
my $cache = Tie::FileLRUCache->new({ -cache_dir => $directory, -keep_last => 100 }); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Inserting value into LRU cache using '-key' |
61
|
|
|
|
|
|
|
$cache->update({ -key => $key, -value => $value }); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Inserting value into LRU cache using '-cache_key' |
65
|
|
|
|
|
|
|
my $cache_key = $cache->make_cache_key({ -key => $key }); |
66
|
|
|
|
|
|
|
$cache->update({ -cache_key => $cache_key, -value => $value }); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Checking LRU cache |
70
|
|
|
|
|
|
|
my ($in_cache,$value) = $cache->check({ -key => $key }); |
71
|
|
|
|
|
|
|
if ($in_cache) { |
72
|
|
|
|
|
|
|
return $value; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
# Not in cache - do something else |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Checking LRU cache with speed up hack for objects, hashes, arrays etc used as keys |
78
|
|
|
|
|
|
|
my $cache_key = $cache->make_cache_key({ -key => $something }); |
79
|
|
|
|
|
|
|
my ($in_cache,$value) = $cache->check({ -cache_key => $cache_key }); |
80
|
|
|
|
|
|
|
if ($in_cache) { |
81
|
|
|
|
|
|
|
return $value; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
# Not in cache - do something else |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Deleting a key and its value from the cache |
87
|
|
|
|
|
|
|
$cache->delete({ -key => $key }); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# Clearing LRU cache |
91
|
|
|
|
|
|
|
$cache->clear; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 TIED INTERFACE |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
use Tie::FileLRUCache; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
[$X =] tie %hash, 'Tie::FileLRUCache', $cache_dir, $keep_last_n; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Adding a key/value to the cache |
100
|
|
|
|
|
|
|
$hash{$key} = $value; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Checking the cache |
103
|
|
|
|
|
|
|
if (not exists $hash{$key}) {; |
104
|
|
|
|
|
|
|
# No match |
105
|
|
|
|
|
|
|
. |
106
|
|
|
|
|
|
|
. |
107
|
|
|
|
|
|
|
. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
} else { |
110
|
|
|
|
|
|
|
my $value = $hash{$key}; |
111
|
|
|
|
|
|
|
. |
112
|
|
|
|
|
|
|
. |
113
|
|
|
|
|
|
|
. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# Removing a value from the cache; |
118
|
|
|
|
|
|
|
delete $hash{$key}; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Clearing the cache |
121
|
|
|
|
|
|
|
%hash = (); |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Note: Iteration over the cache (each, keys, values) is _NOT_ supported. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head1 DESCRIPTION |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Provides a lightweight persistent filesystem based LRU cache. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
It uses the 'last accessed' timestamp generated by the file system |
132
|
|
|
|
|
|
|
to determine the 'oldest' cache entry and discards the oldest |
133
|
|
|
|
|
|
|
cache entries when needed to stay under the -keep_last limit. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
If you store thing very fast (such that many entries receive the |
136
|
|
|
|
|
|
|
same time stamp), it is essentially a coin toss which entry |
137
|
|
|
|
|
|
|
within a single timestamped second gets purged from the cache |
138
|
|
|
|
|
|
|
to make room for new ones. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
It is not designed to handle huge numbers of cached items. It is probably |
141
|
|
|
|
|
|
|
unwise to set the 'keep_last' higher than around 100. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=cut |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=cut |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
####################################################################### |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=over 4 |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item new({[ -cache_dir => $cache_directory] [, -keep_last => $keep_last_n ] }); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Creates and optionally initializes a Tie::FileLRUCache object: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Example: |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my $cache = Tie::FileLRUCache->new({ |
160
|
|
|
|
|
|
|
-cache_dir => '/tmp/testing', |
161
|
|
|
|
|
|
|
-keep_last => 100, |
162
|
|
|
|
|
|
|
}); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
The default cache size is 100 entries unless specified. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=back |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub new { |
171
|
13
|
|
|
13
|
1
|
2537
|
my $proto = shift; |
172
|
13
|
|
|
|
|
17
|
my $package = __PACKAGE__; |
173
|
13
|
|
66
|
|
|
47
|
my $class = ref ($proto) || $proto || $package; |
174
|
13
|
|
|
|
|
22
|
my $self = bless {}, $class; |
175
|
|
|
|
|
|
|
|
176
|
13
|
|
|
|
|
66
|
my $parms = parse_parms({ -parms => \@_, |
177
|
|
|
|
|
|
|
-legal => [-cache_dir, -keep_last], |
178
|
|
|
|
|
|
|
-required => [], |
179
|
|
|
|
|
|
|
-defaults => { -keep_last => 100, |
180
|
|
|
|
|
|
|
-cache_dir => undef, |
181
|
|
|
|
|
|
|
}, |
182
|
|
|
|
|
|
|
}); |
183
|
13
|
100
|
|
|
|
1231
|
if (not defined $parms) { |
184
|
1
|
|
|
|
|
4
|
my $error_message = Class::ParmList->error; |
185
|
1
|
|
|
|
|
7
|
require Carp; |
186
|
1
|
|
|
|
|
160
|
Carp::croak ($package . "::new() - Parameter error '$error_message'\n"); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Save settings |
190
|
12
|
|
|
|
|
31
|
my ($cache_dir,$keep_last) = $parms->get(-cache_dir,-keep_last); |
191
|
12
|
|
|
|
|
313
|
$self->cache_dir($cache_dir); |
192
|
12
|
|
|
|
|
27
|
$self->keep_last($keep_last); |
193
|
|
|
|
|
|
|
|
194
|
12
|
|
|
|
|
40
|
$self; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
####################################################################### |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=over 4 |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item check({ -key => $key }); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Reads the cache for the key. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Returns two values: $cache_hit (true if a hit was found, false if not) |
206
|
|
|
|
|
|
|
$value (the cached value, undef if no hit) |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Examples: |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my ($cache_hit,$value) = $cache->check({ -key => $key }); |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
my ($cache_hit,$value) = $cache->check({ -cache_key => $cache_key }); |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
The '-key' form is used when you just want to use a raw key. It can use |
215
|
|
|
|
|
|
|
blessed objects, hash refs, scalars, or array refs as keys. The more complex |
216
|
|
|
|
|
|
|
structures take a speed penalty for computing a canonical form. |
217
|
|
|
|
|
|
|
You can minimize this penalty by using the '-cache_key' form instead. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
The '-cache_key' form is used for performance reasons when using keys |
220
|
|
|
|
|
|
|
such as complex blessed objects or hashes as a key. The -cache_key |
221
|
|
|
|
|
|
|
is obtained with a call to 'make_cache_key'. It is legal to mix |
222
|
|
|
|
|
|
|
-cache_key and -key based calls - they are cross-compatible. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=back |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=cut |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub check { |
229
|
48
|
|
|
48
|
1
|
325
|
my $self = shift; |
230
|
48
|
|
|
|
|
57
|
my $package = __PACKAGE__; |
231
|
|
|
|
|
|
|
|
232
|
48
|
100
|
|
|
|
82
|
if (not wantarray) { |
233
|
1
|
|
|
|
|
3
|
require Carp; |
234
|
1
|
|
|
|
|
54
|
Carp::croak ($package . "::check() - Called in a scalar context\n"); |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
47
|
|
|
|
|
231
|
my $parms = parse_parms({ -parms => \@_, |
238
|
|
|
|
|
|
|
-legal => [-cache_key, -key], |
239
|
|
|
|
|
|
|
-required => [], |
240
|
|
|
|
|
|
|
-defaults => {}, |
241
|
|
|
|
|
|
|
}); |
242
|
46
|
100
|
|
|
|
4820
|
if (not defined $parms) { |
243
|
1
|
|
|
|
|
3
|
my $error_message = Class::ParmList->error; |
244
|
1
|
|
|
|
|
7
|
require Carp; |
245
|
1
|
|
|
|
|
76
|
Carp::croak ($package . "::check() - $error_message\n"); |
246
|
|
|
|
|
|
|
} |
247
|
45
|
|
|
|
|
136
|
my ($key,$cache_key) = $parms->get(-key,-cache_key); |
248
|
45
|
100
|
100
|
|
|
1314
|
if (not (defined ($key) or defined ($cache_key))) { |
249
|
1
|
|
|
|
|
4
|
require Carp; |
250
|
1
|
|
|
|
|
55
|
Carp::croak ($package . "::check() - Called without either a -key or -cache_key\n"); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
44
|
|
|
|
|
96
|
my $cache_dir = $self->cache_dir; |
254
|
44
|
100
|
|
|
|
74
|
unless (defined $cache_dir) { |
255
|
1
|
|
|
|
|
4
|
require Carp; |
256
|
1
|
|
|
|
|
62
|
Carp::croak ($package . "::check - No cache directory set.\n"); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# Ok. Set our lock on the cache |
260
|
43
|
|
|
|
|
87
|
$self->_lock_cache; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Generate the cache_key (done by making a cannonical |
263
|
|
|
|
|
|
|
# network order Storable string out of the key) if we |
264
|
|
|
|
|
|
|
# don't already have it |
265
|
43
|
100
|
|
|
|
98
|
unless (defined $cache_key) { |
266
|
5
|
|
|
|
|
18
|
$cache_key = $self->make_cache_key({ -key => $key }); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# Generate a unique cache file name by taking a SHA1 hash of $cache_key |
270
|
43
|
|
|
|
|
287
|
my $cache_hash = lc(sha1_hex($cache_key)); |
271
|
43
|
|
|
|
|
136
|
$cache_hash =~ s/\s//gs; |
272
|
43
|
|
|
|
|
249
|
my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s; |
273
|
43
|
|
|
|
|
480
|
my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash"); |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Check if there is a cache entry for this key |
276
|
43
|
100
|
|
|
|
768
|
unless (-e $cache_file) { |
277
|
22
|
|
|
|
|
94
|
$self->_unlock_cache; |
278
|
22
|
|
|
|
|
147
|
return (0,undef); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Yes. Get it. And update the last modified and last accessed dates. |
282
|
21
|
|
|
|
|
49
|
my $entry; |
283
|
21
|
|
|
|
|
32
|
eval { |
284
|
21
|
|
|
|
|
67
|
$entry = retrieve($cache_file); |
285
|
21
|
|
|
|
|
1653
|
my $now = time; |
286
|
21
|
|
|
|
|
361
|
utime ($now, $now, $cache_file); |
287
|
|
|
|
|
|
|
}; |
288
|
21
|
50
|
|
|
|
67
|
if ($@) { |
289
|
0
|
|
|
|
|
0
|
my $error = $@; |
290
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
291
|
0
|
|
|
|
|
0
|
require Carp; |
292
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::check - Error while retrieving cache entry file '$cache_file': $error\n"); |
293
|
|
|
|
|
|
|
} |
294
|
21
|
50
|
|
|
|
65
|
unless (defined $entry) { |
295
|
0
|
|
|
|
|
0
|
my $error = $!; |
296
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
297
|
0
|
|
|
|
|
0
|
require Carp; |
298
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::update - Failed to retrieve cache entry file '$cache_file': $error\n"); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Release the lock. |
302
|
21
|
|
|
|
|
66
|
$self->_unlock_cache; |
303
|
|
|
|
|
|
|
|
304
|
21
|
|
|
|
|
45
|
my $cache_value = $entry->{'-value'}; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# Give them their cupie doll |
307
|
21
|
|
|
|
|
165
|
return (1, $cache_value); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
####################################################################### |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=over 4 |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=item make_cache_key({ -key => $key }); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Generates a cache key by canonicalizing a passed |
317
|
|
|
|
|
|
|
key as a network ordered canonical Storable string. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Example: |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
my $cache_key = $cache->make_cache_key({ -key => $key }); |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=back |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=cut |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub make_cache_key { |
328
|
11
|
|
|
11
|
1
|
25
|
my $self = shift; |
329
|
11
|
|
|
|
|
12
|
my $package = __PACKAGE__; |
330
|
11
|
|
|
|
|
45
|
my $parms = parse_parms({ -parms => \@_, |
331
|
|
|
|
|
|
|
-legal => [], |
332
|
|
|
|
|
|
|
-required => ['-key'], |
333
|
|
|
|
|
|
|
-defaults => {}, |
334
|
|
|
|
|
|
|
}); |
335
|
11
|
100
|
|
|
|
1000
|
unless (defined $parms) { |
336
|
1
|
|
|
|
|
5
|
my $error_message = Class::ParmList->error; |
337
|
1
|
|
|
|
|
8
|
require Carp; |
338
|
1
|
|
|
|
|
97
|
Carp::croak ($package . "::make_cache_key() - $error_message\n"); |
339
|
|
|
|
|
|
|
} |
340
|
10
|
|
|
|
|
28
|
my ($key) = $parms->get(-key); |
341
|
|
|
|
|
|
|
|
342
|
10
|
|
|
|
|
176
|
my $temp = $Storable::canonical; |
343
|
10
|
|
|
|
|
28
|
my $result = nfreeze(\$key); |
344
|
10
|
|
|
|
|
369
|
$Storable::canonical = $temp; |
345
|
|
|
|
|
|
|
|
346
|
10
|
50
|
|
|
|
22
|
if (not $result) { |
347
|
0
|
|
|
|
|
0
|
my $error = $!; |
348
|
0
|
|
|
|
|
0
|
require Carp; |
349
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::check() - Unable to serialize passed -key value: $error"); |
350
|
|
|
|
|
|
|
} |
351
|
10
|
|
|
|
|
33
|
return $result; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
####################################################################### |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=over 4 |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=item clear; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Completely clears the cache of all cache entries. |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=back |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=cut |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub clear { |
367
|
4
|
|
|
4
|
1
|
22
|
my $self = shift; |
368
|
4
|
|
|
|
|
8
|
my $package = __PACKAGE__; |
369
|
4
|
|
|
|
|
7
|
my $cache_dir = $self->cache_dir; |
370
|
|
|
|
|
|
|
|
371
|
4
|
100
|
|
|
|
20
|
unless (defined $cache_dir) { |
372
|
1
|
|
|
|
|
10
|
require Carp; |
373
|
1
|
|
|
|
|
168
|
Carp::croak ($package . "::clear - No cache directory set.\n"); |
374
|
|
|
|
|
|
|
} |
375
|
3
|
100
|
|
|
|
10
|
if ($cache_dir eq '') { |
376
|
1
|
|
|
|
|
5
|
require Carp; |
377
|
1
|
|
|
|
|
69
|
Carp::croak ($package . "::clear - Cannot use root directory as cache directory.\n"); |
378
|
|
|
|
|
|
|
} |
379
|
2
|
50
|
33
|
|
|
38
|
if ((-e $cache_dir) and (not -d _)) { |
380
|
0
|
|
|
|
|
0
|
require Carp; |
381
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::clear - '$cache_dir' already exists and is not a directory.\n"); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
2
|
|
|
|
|
9
|
$self->_lock_cache; |
385
|
|
|
|
|
|
|
|
386
|
2
|
|
|
|
|
8
|
my $cache_dir_fh = gensym; |
387
|
2
|
50
|
|
|
|
71
|
if (not opendir ($cache_dir_fh, $cache_dir)) { |
388
|
0
|
|
|
|
|
0
|
my $error = $!; |
389
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
390
|
0
|
|
|
|
|
0
|
require Carp; |
391
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::clear - Failed to open directory '$cache_dir' for reading: $error\n"); |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
2
|
|
|
|
|
55
|
my @raw_directory_list = readdir($cache_dir_fh); |
395
|
2
|
50
|
|
|
|
30
|
unless (closedir ($cache_dir_fh)) { |
396
|
0
|
|
|
|
|
0
|
my $error = $!; |
397
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
398
|
0
|
|
|
|
|
0
|
require Carp; |
399
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::clear - Failed to close directory '$cache_dir': $error\n"); |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# Untaint the filenames, convert them to absolute file paths and unlink them. |
403
|
2
|
|
|
|
|
35
|
my @raw_files_list = grep(/^(cacheline_[a-zA-Z0-9]{1,50}|cl_[a-zA-Z0-9]{1,50})$/s, @raw_directory_list); |
404
|
2
|
|
|
|
|
5
|
my @file_list = (); |
405
|
2
|
|
|
|
|
6
|
foreach my $item (@raw_files_list) { |
406
|
10
|
|
|
|
|
69
|
my ($filename) = $item =~ m/^(.*)$/s; |
407
|
10
|
|
|
|
|
94
|
my $file_path = File::Spec->catfile($cache_dir, $filename); |
408
|
10
|
50
|
|
|
|
485
|
unless (unlink $file_path) { |
409
|
0
|
|
|
|
|
0
|
my $error = $!; |
410
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
411
|
0
|
|
|
|
|
0
|
require Carp; |
412
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::clear - Failed to unlink file '$file_path': $error"); |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
2
|
|
|
|
|
14
|
$self->_unlock_cache; |
417
|
|
|
|
|
|
|
|
418
|
2
|
|
|
|
|
13
|
return; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
####################################################################### |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=over 4 |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=item update({ [-key => $key,] [-cache_key => $cache_key, ], -value => $value [, -keep_last => $keep_last_n ] }); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Updates the Least Recently Used (LRU) cache for the specified |
428
|
|
|
|
|
|
|
key with the passed value. '-keep_last' is optional after the first access |
429
|
|
|
|
|
|
|
to a dataset. It will use the I 'keep_last' used |
430
|
|
|
|
|
|
|
if not specified. |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
It is legal to use ordinary scalars, hash references, or array references |
433
|
|
|
|
|
|
|
as keys as well as objects as -keys or -values. Basically, anything that |
434
|
|
|
|
|
|
|
Storable can reproducibly serialize can be used. |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Examples: |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
$cache->update({ -key => $key, -value => $value }); |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
$cache->update({ -key => $key, -value => $value, -keep_last => 100}); |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
my $cache_key = $cache->make_cache_key({ -key => $key }); |
443
|
|
|
|
|
|
|
$cache->update({ -cache_key => $cache_key, -value => $value }); |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
my $cache_key = $cache->make_cache_key({ -key => $key }); |
446
|
|
|
|
|
|
|
$cache->update({ -cache_key => $cache_key, -value => $value, -keep_last => 50 }); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
-cache_key is assumed to be a simple scalar value for use as a key. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
-key can be pretty much anything Storable can successfully and reproducibly serialize. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
One or the other I be passed. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=back |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=cut |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
sub update { |
459
|
21
|
|
|
21
|
1
|
6001426
|
my $self = shift; |
460
|
21
|
|
|
|
|
64
|
my $package = __PACKAGE__; |
461
|
|
|
|
|
|
|
|
462
|
21
|
|
|
|
|
164
|
my $parms = parse_parms({ -parms => \@_, |
463
|
|
|
|
|
|
|
-legal => ['-keep_last', '-key', '-cache_key'], |
464
|
|
|
|
|
|
|
-required => ['-value'], |
465
|
|
|
|
|
|
|
-defaults => {'-keep_last' => $self->keep_last}, |
466
|
|
|
|
|
|
|
}); |
467
|
21
|
100
|
|
|
|
3555
|
unless (defined $parms) { |
468
|
1
|
|
|
|
|
3
|
my $error_message = Class::ParmList->error; |
469
|
1
|
|
|
|
|
6
|
require Carp; |
470
|
1
|
|
|
|
|
83
|
Carp::croak ($package . "::update() - $error_message\n"); |
471
|
|
|
|
|
|
|
} |
472
|
20
|
|
|
|
|
95
|
my ($key,$cache_key,$value,$keep_last) = $parms->get('-key', '-cache_key', '-value', '-keep_last'); |
473
|
20
|
100
|
100
|
|
|
981
|
unless (defined ($key) or defined ($cache_key)) { |
474
|
1
|
|
|
|
|
4
|
require Carp; |
475
|
1
|
|
|
|
|
79
|
Carp::croak ($package . "::update() - Called without either a -key or -cache_key. At least one of them must be passed.\n"); |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
19
|
|
|
|
|
64
|
my ($cache_dir) = $self->cache_dir; |
479
|
19
|
100
|
|
|
|
47
|
unless (defined $cache_dir) { |
480
|
1
|
|
|
|
|
4
|
require Carp; |
481
|
1
|
|
|
|
|
75
|
Carp::croak ($package . "::update - No cache directory set.\n"); |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Generate the cache_key (done by making a cannonical |
485
|
|
|
|
|
|
|
# network order Storable string out of the key) if we |
486
|
|
|
|
|
|
|
# don't already have one. |
487
|
18
|
100
|
|
|
|
54
|
unless (defined $cache_key) { |
488
|
2
|
|
|
|
|
7
|
$cache_key = $self->make_cache_key({ -key => $key }); |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# Generate a unique cache file |
492
|
|
|
|
|
|
|
# name by taking a SHA1 hash of |
493
|
|
|
|
|
|
|
# $cache_key and dumping it as hex |
494
|
18
|
|
|
|
|
268
|
my $cache_hash = lc(sha1_hex($cache_key)); |
495
|
18
|
|
|
|
|
99
|
$cache_hash =~ s/\s//gs; |
496
|
18
|
|
|
|
|
151
|
my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s; |
497
|
18
|
|
|
|
|
383
|
my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash"); |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
# Serialize the $value for storage |
500
|
18
|
|
|
|
|
77
|
my $entry = { -value => $value }; |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# Set our lock on the cache directory |
503
|
18
|
|
|
|
|
86
|
$self->_lock_cache; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
########## |
506
|
|
|
|
|
|
|
# Store the cache entry. |
507
|
18
|
|
|
|
|
29
|
my $result; |
508
|
18
|
|
|
|
|
37
|
eval { $result = nstore($entry,$cache_file); }; |
|
18
|
|
|
|
|
117
|
|
509
|
18
|
50
|
|
|
|
5491
|
if ($@) { |
510
|
0
|
|
|
|
|
0
|
my $error = $@; |
511
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
512
|
0
|
|
|
|
|
0
|
require Carp; |
513
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::update - Error while saving cache entry file '$cache_file': $error"); |
514
|
|
|
|
|
|
|
} |
515
|
18
|
50
|
|
|
|
55
|
unless (defined $result) { |
516
|
0
|
|
|
|
|
0
|
my $error = $!; |
517
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
518
|
0
|
|
|
|
|
0
|
require Carp; |
519
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::update - Error while saving cache entry file '$cache_file': $error\n"); |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
######################################## |
523
|
|
|
|
|
|
|
# Check if we need to purge old entries |
524
|
18
|
|
|
|
|
50
|
my $cache_dir_fh = gensym; |
525
|
18
|
50
|
|
|
|
672
|
unless (opendir ($cache_dir_fh, $cache_dir)) { |
526
|
0
|
|
|
|
|
0
|
my $error = $!; |
527
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
528
|
0
|
|
|
|
|
0
|
require Carp; |
529
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::update - Failed to open directory '$cache_dir' for reading: $error\n"); |
530
|
|
|
|
|
|
|
} |
531
|
18
|
|
|
|
|
701
|
my @raw_file_list = grep(/^(cacheline_[a-fA-F0-9]{1,50}|cl_[a-fA-F0-9]{1,50})$/s,readdir($cache_dir_fh)); |
532
|
18
|
50
|
|
|
|
292
|
unless (closedir ($cache_dir_fh)) { |
533
|
0
|
|
|
|
|
0
|
my $error = $!; |
534
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
535
|
0
|
|
|
|
|
0
|
require Carp; |
536
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::update - Failed to close directory '$cache_dir': $error\n"); |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Untainting the filenames and converting them to absolute file paths. |
540
|
18
|
|
|
|
|
58
|
my @file_list = (); |
541
|
18
|
|
|
|
|
45
|
foreach my $item (@raw_file_list) { |
542
|
58
|
|
|
|
|
221
|
my ($filename) = $item =~ m/^(.*)$/s; |
543
|
58
|
|
|
|
|
460
|
my $file_path = File::Spec->catfile($cache_dir, $filename); |
544
|
58
|
|
|
|
|
150
|
push (@file_list,$file_path); |
545
|
|
|
|
|
|
|
} |
546
|
18
|
|
|
|
|
45
|
my $n_files = $#file_list + 1; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# No problems. All done. |
549
|
18
|
100
|
|
|
|
58
|
if ($n_files <= $keep_last) { |
550
|
14
|
|
|
|
|
48
|
$self->_unlock_cache; |
551
|
14
|
|
|
|
|
164
|
return; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# Too many entries. Delete the excess entries (usually only one) |
555
|
4
|
|
|
|
|
10
|
my %file_last_access = (); |
556
|
4
|
|
|
|
|
10
|
foreach my $file (@file_list) { |
557
|
24
|
|
|
|
|
271
|
my $last_accessed = (stat($file))[9]; |
558
|
24
|
|
|
|
|
91
|
$file_last_access{$file} = $last_accessed; |
559
|
|
|
|
|
|
|
} |
560
|
4
|
|
|
|
|
35
|
my @sorted_file_list = sort { $file_last_access{$b} <=> $file_last_access{$a} } @file_list; |
|
34
|
|
|
|
|
70
|
|
561
|
4
|
|
66
|
|
|
30
|
while (($n_files > $keep_last) and ($n_files > 0)) { |
562
|
4
|
|
|
|
|
6
|
$n_files--; |
563
|
4
|
|
|
|
|
8
|
my $pruned_file = $sorted_file_list[$n_files]; |
564
|
4
|
50
|
|
|
|
266
|
unless (unlink $pruned_file) { |
565
|
0
|
|
|
|
|
0
|
my $error = $!; |
566
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
567
|
0
|
|
|
|
|
0
|
require Carp; |
568
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::update - Failed to unlink file '$pruned_file': $error"); |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
# Release our lock and return |
573
|
4
|
|
|
|
|
85
|
$self->_unlock_cache; |
574
|
4
|
|
|
|
|
58
|
return; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
####################################################################### |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=over 4 |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=item delete({ -key => $key }); |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Forces the deletion of a specific key from the cache. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
Example: |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
$cache->delete({ -key => $key }); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
=back |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=cut |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub delete { |
594
|
8
|
|
|
8
|
1
|
101
|
my $self = shift; |
595
|
8
|
|
|
|
|
8
|
my $package = __PACKAGE__; |
596
|
|
|
|
|
|
|
|
597
|
8
|
|
|
|
|
31
|
my $parms = parse_parms({ -parms => \@_, |
598
|
|
|
|
|
|
|
-legal => [-key, -cache_key], |
599
|
|
|
|
|
|
|
-required => [], |
600
|
|
|
|
|
|
|
-defaults => {}, |
601
|
|
|
|
|
|
|
}); |
602
|
7
|
100
|
|
|
|
647
|
if (not defined $parms) { |
603
|
1
|
|
|
|
|
3
|
my $error_message = Class::ParmList->error; |
604
|
1
|
|
|
|
|
6
|
require Carp; |
605
|
1
|
|
|
|
|
76
|
Carp::croak ($package . "::delete() - $error_message\n"); |
606
|
|
|
|
|
|
|
} |
607
|
6
|
|
|
|
|
19
|
my ($key,$cache_key) = $parms->get(-key, -cache_key); |
608
|
6
|
100
|
100
|
|
|
146
|
if (not (defined ($key) or defined ($cache_key))) { |
609
|
1
|
|
|
|
|
5
|
require Carp; |
610
|
1
|
|
|
|
|
57
|
Carp::croak ($package . "::delete() - Called without either a -key or -cache_key\n"); |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
5
|
|
|
|
|
9
|
my $cache_dir = $self->cache_dir; |
614
|
5
|
100
|
|
|
|
10
|
unless (defined $cache_dir) { |
615
|
1
|
|
|
|
|
4
|
require Carp; |
616
|
1
|
|
|
|
|
62
|
Carp::croak ($package . "::delete - No cache directory set.\n"); |
617
|
|
|
|
|
|
|
} |
618
|
4
|
50
|
|
|
|
19
|
if ($cache_dir eq '') { |
619
|
0
|
|
|
|
|
0
|
require Carp; |
620
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::delete - Cannot use root directory as cache directory.\n"); |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# Generate the cache_key (done by making a cannonical |
624
|
|
|
|
|
|
|
# network order Storable string out of the key) if we |
625
|
|
|
|
|
|
|
# don't already have it |
626
|
4
|
100
|
|
|
|
14
|
if (not defined $cache_key) { |
627
|
2
|
|
|
|
|
7
|
$cache_key = $self->make_cache_key({ -key => $key }); |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# Generate a unique cache file |
631
|
|
|
|
|
|
|
# name by taking a SHA1 hash of |
632
|
|
|
|
|
|
|
# $cache_key |
633
|
4
|
|
|
|
|
21
|
my $cache_hash = lc(sha1_hex($cache_key)); |
634
|
4
|
|
|
|
|
12
|
$cache_hash =~ s/\s//gs; |
635
|
4
|
|
|
|
|
17
|
my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s; |
636
|
4
|
|
|
|
|
42
|
my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash"); |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# Ok. Set our lock on the cache directory |
639
|
4
|
|
|
|
|
13
|
$self->_lock_cache; |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# If it is in the cache, remove it |
642
|
4
|
50
|
33
|
|
|
218
|
if ((-e $cache_file) and (not unlink $cache_file)) { |
643
|
0
|
|
|
|
|
0
|
my $error = $!; |
644
|
0
|
|
|
|
|
0
|
$self->_unlock_cache; |
645
|
0
|
|
|
|
|
0
|
require Carp; |
646
|
0
|
|
|
|
|
0
|
Carp::croak($package . "::delete - Failed to unlink file '$cache_file': $error"); |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# Release our lock and return |
650
|
4
|
|
|
|
|
16
|
$self->_unlock_cache; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
####################################################################### |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=over 4 |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item cache_dir([$cache_directory_path]); |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Get/Set accessor for the cache directory path. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Ex. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
my $cache_directory = $cache->cache_dir; |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
$cache->cache_dir($cache_directory); |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=back |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=cut |
670
|
|
|
|
|
|
|
|
671
|
163
|
|
|
163
|
1
|
348
|
sub cache_dir { return shift->_property('cache_dir', @_); } |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
####################################################################### |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=over 4 |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=item keep_last([$keep_last_n]); |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Get/Set accessor for the keep last N setting. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Ex. |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
my $n_last = $cache->keep_last; |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
$cache->keep_last(20); |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=back |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=cut |
690
|
|
|
|
|
|
|
|
691
|
37
|
|
|
37
|
1
|
137
|
sub keep_last { return shift->_property('keep_last', @_); } |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
####################################################################### |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=over 4 |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=item number_of_entries; |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Returns the current number of entries in the cache. |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=back |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=cut |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
sub number_of_entries { |
707
|
2
|
|
|
2
|
1
|
2000426
|
my $self = shift; |
708
|
2
|
|
|
|
|
10
|
my $package = __PACKAGE__; |
709
|
|
|
|
|
|
|
|
710
|
2
|
|
|
|
|
16
|
my $cache_dir_fh = gensym; |
711
|
2
|
|
|
|
|
95
|
my $cache_dir = $self->cache_dir; |
712
|
2
|
50
|
|
|
|
20
|
unless (defined $cache_dir) { |
713
|
0
|
|
|
|
|
0
|
require Carp; |
714
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::delete - No cache directory set.\n"); |
715
|
|
|
|
|
|
|
} |
716
|
2
|
50
|
|
|
|
10
|
if ($cache_dir eq '') { |
717
|
0
|
|
|
|
|
0
|
require Carp; |
718
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::delete - Cannot use root directory as cache directory.\n"); |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
2
|
50
|
|
|
|
157
|
unless (opendir ($cache_dir_fh, $cache_dir)) { |
722
|
0
|
|
|
|
|
0
|
my $error = $!; |
723
|
0
|
|
|
|
|
0
|
require Carp; |
724
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::update - Failed to open directory '$cache_dir' for reading: $error\n"); |
725
|
|
|
|
|
|
|
} |
726
|
2
|
|
|
|
|
149
|
my @raw_file_list = grep(/^(cacheline_[a-fA-F0-9]{1,50}|cl_[a-fA-F0-9]{1,50})$/s,readdir($cache_dir_fh)); |
727
|
2
|
50
|
|
|
|
57
|
unless (closedir ($cache_dir_fh)) { |
728
|
0
|
|
|
|
|
0
|
my $error = $!; |
729
|
0
|
|
|
|
|
0
|
require Carp; |
730
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::update - Failed to close directory '$cache_dir': $error\n"); |
731
|
|
|
|
|
|
|
} |
732
|
2
|
|
|
|
|
8
|
my $n_entries = $#raw_file_list + 1; |
733
|
2
|
|
|
|
|
23
|
return $n_entries; |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
####################################################################### |
737
|
|
|
|
|
|
|
# # |
738
|
|
|
|
|
|
|
# PRIVATE METHODS # |
739
|
|
|
|
|
|
|
# # |
740
|
|
|
|
|
|
|
# Internals. Documented for maintainance reasons only. # |
741
|
|
|
|
|
|
|
# Do not use these methods from outside this module. # |
742
|
|
|
|
|
|
|
# # |
743
|
|
|
|
|
|
|
####################################################################### |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
####################################################################### |
746
|
|
|
|
|
|
|
# _cache_lock_fh([$fh]); |
747
|
|
|
|
|
|
|
# |
748
|
|
|
|
|
|
|
# Get/Set accessor used to store a reference to the filehandle |
749
|
|
|
|
|
|
|
# used for locking. |
750
|
|
|
|
|
|
|
|
751
|
134
|
|
|
134
|
|
286
|
sub _cache_lock_fh { return shift->_property('_cache_lock_fh', @_); } |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
####################################################################### |
754
|
|
|
|
|
|
|
# _lock_cache; |
755
|
|
|
|
|
|
|
# |
756
|
|
|
|
|
|
|
# Obtains a lock on the 'cache.lock' file for this LRU cache. |
757
|
|
|
|
|
|
|
# |
758
|
|
|
|
|
|
|
# Example: |
759
|
|
|
|
|
|
|
# $self->_lock_cache; |
760
|
|
|
|
|
|
|
# |
761
|
|
|
|
|
|
|
# This will create the 'cache.lock' file if it does not already exist, |
762
|
|
|
|
|
|
|
# creating any intermediate directories as needed. |
763
|
|
|
|
|
|
|
# |
764
|
|
|
|
|
|
|
# It also writes the current PID to the lock file. |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
sub _lock_cache { |
767
|
67
|
|
|
67
|
|
82
|
my $self = shift; |
768
|
67
|
|
|
|
|
79
|
my $package = __PACKAGE__; |
769
|
|
|
|
|
|
|
|
770
|
67
|
|
|
|
|
122
|
my $cache_dir = $self->cache_dir; |
771
|
67
|
50
|
|
|
|
135
|
if (not defined $cache_dir) { |
772
|
0
|
|
|
|
|
0
|
require Carp; |
773
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - No cache directory set.\n"); |
774
|
|
|
|
|
|
|
} |
775
|
67
|
50
|
|
|
|
135
|
if ($cache_dir eq '') { |
776
|
0
|
|
|
|
|
0
|
require Carp; |
777
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Cannot use root directory as cache directory.\n"); |
778
|
|
|
|
|
|
|
} |
779
|
67
|
50
|
33
|
|
|
1870
|
if ((-e $cache_dir) and (not -d _)) { |
780
|
0
|
|
|
|
|
0
|
require Carp; |
781
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - '$cache_dir' already exists and is not a directory.\n"); |
782
|
|
|
|
|
|
|
} |
783
|
67
|
50
|
|
|
|
730
|
if (not -e $cache_dir) { |
784
|
0
|
|
|
|
|
0
|
eval { |
785
|
0
|
|
|
|
|
0
|
require File::Path; |
786
|
0
|
|
|
|
|
0
|
File::Path::mkpath ($cache_dir); |
787
|
|
|
|
|
|
|
}; |
788
|
0
|
0
|
|
|
|
0
|
if ($@) { |
789
|
0
|
|
|
|
|
0
|
my $error = $@; |
790
|
0
|
|
|
|
|
0
|
require Carp; |
791
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - unable to create directory '$cache_dir': $error"); |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
} |
794
|
67
|
50
|
33
|
|
|
747
|
if (not ((-e $cache_dir) and (-d _))) { |
795
|
0
|
|
|
|
|
0
|
require Carp; |
796
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Unable to create directory '$cache_dir'\n"); |
797
|
|
|
|
|
|
|
} |
798
|
67
|
|
|
|
|
792
|
my $document_name = File::Spec->catfile($cache_dir,'.cache.lock'); |
799
|
67
|
|
|
|
|
265
|
my $cache_lock_fh = gensym; |
800
|
67
|
50
|
|
|
|
3633
|
unless (open ($cache_lock_fh,">>$document_name")) { |
801
|
0
|
|
|
|
|
0
|
my $error = $!; |
802
|
0
|
|
|
|
|
0
|
require Carp; |
803
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Unable to open '$document_name': $error\n"); |
804
|
|
|
|
|
|
|
} |
805
|
67
|
|
|
|
|
254
|
my $lock_timeout = 100; |
806
|
67
|
|
|
|
|
729
|
while (not flock($cache_lock_fh, LOCK_EX()|LOCK_NB())) { |
807
|
0
|
|
|
|
|
0
|
$lock_timeout--; |
808
|
0
|
|
|
|
|
0
|
select (undef,undef,undef,0.1); |
809
|
0
|
0
|
|
|
|
0
|
if ($lock_timeout == 0) { |
810
|
0
|
|
|
|
|
0
|
my $error = $!; |
811
|
0
|
|
|
|
|
0
|
require Carp; |
812
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Unable to get an exclusive lock on '$document_name': $error\n"); |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
} |
815
|
67
|
|
|
|
|
350
|
my $fh = select ($cache_lock_fh); |
816
|
67
|
|
|
|
|
241
|
$|++; |
817
|
67
|
|
|
|
|
161
|
select ($fh); |
818
|
67
|
50
|
|
|
|
1750
|
unless (truncate ($cache_lock_fh, 0)) { |
819
|
0
|
|
|
|
|
0
|
my $error = $!; |
820
|
0
|
|
|
|
|
0
|
require Carp; |
821
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Unable to truncate '$document_name': $error\n"); |
822
|
|
|
|
|
|
|
} |
823
|
67
|
|
|
|
|
1997
|
print $cache_lock_fh "$$\n"; |
824
|
67
|
|
|
|
|
312
|
$self->_cache_lock_fh($cache_lock_fh); |
825
|
|
|
|
|
|
|
|
826
|
67
|
|
|
|
|
116
|
return; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
####################################################################### |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
# _unlock_cache; |
832
|
|
|
|
|
|
|
# |
833
|
|
|
|
|
|
|
# Release a lock on the 'cache.lock' file for this LRU cache. |
834
|
|
|
|
|
|
|
# |
835
|
|
|
|
|
|
|
# Example: |
836
|
|
|
|
|
|
|
# $self->_unlock_cache; |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
sub _unlock_cache { |
839
|
67
|
|
|
67
|
|
109
|
my $self = shift; |
840
|
67
|
|
|
|
|
87
|
my $package = __PACKAGE__; |
841
|
|
|
|
|
|
|
|
842
|
67
|
|
|
|
|
107
|
my $cache_lock_fh = $self->_cache_lock_fh; |
843
|
67
|
50
|
|
|
|
1876
|
unless (truncate ($cache_lock_fh,0)) { |
844
|
0
|
|
|
|
|
0
|
my $error = $!; |
845
|
0
|
|
|
|
|
0
|
require Carp; |
846
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_lock_cache - Unable to truncate cache.lock file: $error\n"); |
847
|
|
|
|
|
|
|
} |
848
|
67
|
50
|
|
|
|
921
|
unless (close ($cache_lock_fh)) { |
849
|
0
|
|
|
|
|
0
|
my $error = $!; |
850
|
0
|
|
|
|
|
0
|
require Carp; |
851
|
0
|
|
|
|
|
0
|
Carp::croak ($package . "::_unlock_cache - Error while closing cache.lock file: $error\n"); |
852
|
|
|
|
|
|
|
} |
853
|
67
|
|
|
|
|
220
|
return; |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
#################################################################### |
857
|
|
|
|
|
|
|
# _property('property_name' => $property_value) |
858
|
|
|
|
|
|
|
# |
859
|
|
|
|
|
|
|
# get/set base accessor for property values |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
sub _property { |
862
|
334
|
|
|
334
|
|
430
|
my $self = shift; |
863
|
|
|
|
|
|
|
|
864
|
334
|
|
|
|
|
409
|
my $property = shift; |
865
|
|
|
|
|
|
|
|
866
|
334
|
|
|
|
|
400
|
my $package = __PACKAGE__; |
867
|
334
|
100
|
|
|
|
652
|
if (0 == @_) { |
|
|
50
|
|
|
|
|
|
868
|
229
|
|
|
|
|
382
|
my $output = $self->{$package}->{$property}; |
869
|
229
|
|
|
|
|
669
|
return $output; |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
} elsif (1 == @_) { |
872
|
105
|
|
|
|
|
119
|
my $input = shift; |
873
|
105
|
|
|
|
|
210
|
$self->{$package}->{$property} = $input; |
874
|
105
|
|
|
|
|
380
|
return; |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
} else { |
877
|
0
|
|
|
|
|
0
|
require Carp; |
878
|
0
|
|
|
|
|
0
|
Carp::croak("Bad calling parameters to ${package}::${property}()\n"); |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
#################################################################### |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
sub TIEHASH { |
885
|
4
|
|
|
4
|
|
815
|
my $proto = shift; |
886
|
4
|
|
|
|
|
5
|
my $package = __PACKAGE__; |
887
|
4
|
|
33
|
|
|
18
|
my $class = ref ($proto) || $proto || $package; |
888
|
4
|
|
|
|
|
8
|
my $self = bless {}, $class; |
889
|
|
|
|
|
|
|
|
890
|
4
|
|
|
|
|
7
|
my ($cache_dir,$keep_last) = @_; |
891
|
|
|
|
|
|
|
|
892
|
4
|
100
|
|
|
|
8
|
$keep_last = 100 unless (defined $keep_last); |
893
|
4
|
100
|
66
|
|
|
22
|
unless (defined ($cache_dir) and ($cache_dir ne '')) { |
894
|
1
|
|
|
|
|
12
|
require Carp; |
895
|
1
|
|
|
|
|
174
|
Carp::croak($package . ": Missing required parameter (cache_dir)\n"); |
896
|
|
|
|
|
|
|
} |
897
|
3
|
|
|
|
|
10
|
$self->cache_dir($cache_dir); |
898
|
3
|
|
|
|
|
7
|
$self->keep_last($keep_last); |
899
|
3
|
|
|
|
|
7
|
return $self; |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
####################################################################### |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
sub STORE { |
905
|
9
|
|
|
9
|
|
6001338
|
my $self = shift; |
906
|
|
|
|
|
|
|
|
907
|
9
|
|
|
|
|
48
|
my ($key,$value) = @_; |
908
|
|
|
|
|
|
|
|
909
|
9
|
100
|
|
|
|
66
|
if (ref(\$key) eq 'SCALAR') { |
910
|
8
|
|
|
|
|
85
|
$self->update({ -cache_key => $key, -value => $value }); |
911
|
|
|
|
|
|
|
} else { |
912
|
1
|
|
|
|
|
4
|
$self->update({ -key => $key, -value => $value }); |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
####################################################################### |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
sub FETCH { |
919
|
7
|
|
|
7
|
|
53
|
my $self = shift; |
920
|
|
|
|
|
|
|
|
921
|
7
|
|
|
|
|
14
|
my ($key) = @_; |
922
|
|
|
|
|
|
|
|
923
|
7
|
100
|
|
|
|
21
|
if (ref(\$key) eq 'SCALAR') { |
924
|
6
|
|
|
|
|
22
|
my ($cache_hit, $value) = $self->check({ -cache_key => $key }); |
925
|
6
|
|
|
|
|
49
|
return $value; |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
} else { |
928
|
1
|
|
|
|
|
4
|
my ($cache_hit,$value) = $self->check({ -key => $key }); |
929
|
1
|
|
|
|
|
7
|
return $value; |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
####################################################################### |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
sub DELETE { |
937
|
2
|
|
|
2
|
|
20
|
my $self = shift; |
938
|
|
|
|
|
|
|
|
939
|
2
|
|
|
|
|
3
|
my ($key) = @_; |
940
|
|
|
|
|
|
|
|
941
|
2
|
100
|
|
|
|
9
|
if (ref(\$key) eq 'SCALAR') { |
942
|
1
|
|
|
|
|
6
|
$self->delete({ -cache_key => $key }); |
943
|
|
|
|
|
|
|
} else { |
944
|
1
|
|
|
|
|
5
|
$self->delete({ -key => $key }); |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
####################################################################### |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
sub CLEAR { |
951
|
1
|
|
|
1
|
|
8
|
my $self = shift; |
952
|
|
|
|
|
|
|
|
953
|
1
|
|
|
|
|
6
|
$self->clear; |
954
|
|
|
|
|
|
|
} |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
####################################################################### |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
sub EXISTS { |
959
|
18
|
|
|
18
|
|
176
|
my $self = shift; |
960
|
|
|
|
|
|
|
|
961
|
18
|
|
|
|
|
33
|
my ($key) = @_; |
962
|
|
|
|
|
|
|
|
963
|
18
|
100
|
|
|
|
47
|
if (ref(\$key) eq 'SCALAR') { |
964
|
16
|
|
|
|
|
56
|
my ($cache_hit,$value) = $self->check({ -cache_key => $key }); |
965
|
16
|
|
|
|
|
74
|
return $cache_hit; |
966
|
|
|
|
|
|
|
} else { |
967
|
2
|
|
|
|
|
8
|
my ($cache_hit,$value) = $self->check({ -key => $key }); |
968
|
2
|
|
|
|
|
11
|
return $cache_hit; |
969
|
|
|
|
|
|
|
} |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
####################################################################### |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# |
975
|
|
|
|
|
|
|
# Iteration over the cache is not supported |
976
|
|
|
|
|
|
|
# |
977
|
|
|
|
|
|
|
|
978
|
1
|
|
|
1
|
|
15
|
sub FIRSTKEY { undef; } |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
####################################################################### |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
# |
983
|
|
|
|
|
|
|
# Iteration over the cache is not supported |
984
|
|
|
|
|
|
|
# |
985
|
|
|
|
|
|
|
|
986
|
0
|
|
|
0
|
|
|
sub NEXTKEY { undef; } |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
####################################################################### |
989
|
|
|
|
|
|
|
# |
990
|
|
|
|
|
|
|
# We return the number of cache entries in a scalar context |
991
|
|
|
|
|
|
|
# |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
sub SCALAR { |
994
|
0
|
|
|
0
|
|
|
my $self = shift; |
995
|
|
|
|
|
|
|
|
996
|
0
|
|
|
|
|
|
return $self->number_of_entries; |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
####################################################################### |
1000
|
|
|
|
|
|
|
####################################################################### |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
Copyright 1999, 2020 Jerilyn Franz and FreeRun Technologies, Inc. All Rights Reserved. |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=head1 VERSION |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
1.06 released 2020.10.08 |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
=head1 LICENSE |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
MIT License |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
Copyright (c) 2020 Jerilyn Franz |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
1017
|
|
|
|
|
|
|
of this software and associated documentation files (the "Software"), to deal |
1018
|
|
|
|
|
|
|
in the Software without restriction, including without limitation the rights |
1019
|
|
|
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
1020
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the Software is |
1021
|
|
|
|
|
|
|
furnished to do so, subject to the following conditions: |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all |
1024
|
|
|
|
|
|
|
copies or substantial portions of the Software. |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
1027
|
|
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
1028
|
|
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
1029
|
|
|
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
1030
|
|
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
1031
|
|
|
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
1032
|
|
|
|
|
|
|
SOFTWARE. |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
=head1 DISCLAIMER |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS |
1037
|
|
|
|
|
|
|
OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE |
1038
|
|
|
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
1039
|
|
|
|
|
|
|
PARTICULAR PURPOSE. |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
Use of this software in any way or in any form, source or binary, |
1042
|
|
|
|
|
|
|
is not allowed in any country which prohibits disclaimers of any |
1043
|
|
|
|
|
|
|
implied warranties of merchantability or fitness for a particular |
1044
|
|
|
|
|
|
|
purpose or any disclaimers of a similar nature. |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
IN NO EVENT SHALL I BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, |
1047
|
|
|
|
|
|
|
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
1048
|
|
|
|
|
|
|
USE OF THIS SOFTWARE AND ITS DOCUMENTATION (INCLUDING, BUT NOT |
1049
|
|
|
|
|
|
|
LIMITED TO, LOST PROFITS) EVEN IF I HAVE BEEN ADVISED OF THE |
1050
|
|
|
|
|
|
|
POSSIBILITY OF SUCH DAMAGE |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
=head1 AUTHOR |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
Jerilyn Franz |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
=head1 TODO |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
Nothing. |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
=cut |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
1; |