line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package SimpleCDB; |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
######################################################################## |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Perl-only Constant Database |
6
|
|
|
|
|
|
|
# (c) Benjamin D. Low |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# See end of file for pod documentation. |
9
|
|
|
|
|
|
|
# See HISTORY file for commentary on major developments. |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
######################################################################## |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
|
14
|
7
|
|
|
7
|
|
36
|
use strict; |
|
7
|
|
|
|
|
8
|
|
|
7
|
|
|
|
|
234
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# prefer 5.004, but can do with 5.003 |
17
|
|
|
|
|
|
|
# - all the comments in this file re. 5.003 are with respect to a |
18
|
|
|
|
|
|
|
# Solaris 2.5.1 machine. It may well be the issues are the fault |
19
|
|
|
|
|
|
|
# of the o/s, not perl - YMMV. |
20
|
7
|
|
|
7
|
|
186
|
use 5.003; |
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
241
|
|
21
|
|
|
|
|
|
|
|
22
|
7
|
|
|
7
|
|
36
|
use Carp; |
|
7
|
|
|
|
|
7
|
|
|
7
|
|
|
|
|
649
|
|
23
|
|
|
|
|
|
|
|
24
|
7
|
|
|
7
|
|
6226
|
use Tie::Hash; |
|
7
|
|
|
|
|
7030
|
|
|
7
|
|
|
|
|
212
|
|
25
|
|
|
|
|
|
|
|
26
|
7
|
|
|
7
|
|
43
|
use vars qw/@ISA @EXPORT $VERSION $DEBUG/; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
548
|
|
27
|
|
|
|
|
|
|
|
28
|
7
|
|
|
7
|
|
30
|
use Exporter (); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
305
|
|
29
|
|
|
|
|
|
|
@ISA = qw/Exporter Tie::Hash/; |
30
|
|
|
|
|
|
|
@EXPORT = @Fcntl::EXPORT; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
$VERSION = '1.0'; |
33
|
|
|
|
|
|
|
|
34
|
7
|
|
|
7
|
|
35
|
use vars qw/$NFILES $SEP $METAFILE $LOCKRDTIMEOUT $LOCKWRTIMEOUT $ERROR/; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
1512
|
|
35
|
|
|
|
|
|
|
$NFILES = 16; |
36
|
|
|
|
|
|
|
$SEP = "\x00"; # default separator |
37
|
|
|
|
|
|
|
$METAFILE = '_info'; # info about the DB, reqd for reading |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
$LOCKRDTIMEOUT = 5; # how long to block for read access |
40
|
|
|
|
|
|
|
$LOCKWRTIMEOUT = 900; # " write " |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
$ERROR = undef; # error message |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
BEGIN |
45
|
|
|
|
|
|
|
{ |
46
|
7
|
|
|
7
|
|
21
|
my @flock = qw/:DEFAULT/; |
47
|
7
|
50
|
|
|
|
42
|
if ($] >= 5.004) # should have a complete Fcntl |
48
|
|
|
|
|
|
|
{ |
49
|
7
|
|
|
|
|
268
|
push @flock, ':flock'; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
else # hope for the best... |
52
|
|
|
|
|
|
|
{ |
53
|
|
|
|
|
|
|
sub LOCK_SH () { 1 }; |
54
|
|
|
|
|
|
|
sub LOCK_EX () { 2 }; |
55
|
|
|
|
|
|
|
sub LOCK_NB () { 4 }; |
56
|
|
|
|
|
|
|
sub LOCK_UN () { 8 }; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
7
|
|
|
7
|
|
30
|
use Fcntl @flock; |
|
7
|
|
|
|
|
8
|
|
|
7
|
|
|
|
|
29939
|
|
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# don't let POSIX's EXPORT list (:flock) clash w/ Fcntl |
63
|
7
|
|
|
7
|
|
6555
|
{ package SimpleDB::POSIX; use POSIX; } |
|
7
|
|
|
|
|
139495
|
|
|
7
|
|
|
|
|
58
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
BEGIN |
66
|
|
|
|
|
|
|
# what to do if EWOULDBLOCK isn't defined... |
67
|
|
|
|
|
|
|
# - unfortunately different systems have different values for |
68
|
|
|
|
|
|
|
# EWOULDBLOCK (11 on Solaris/Linux, 246 on HP/UX). Oh well. |
69
|
|
|
|
|
|
|
{ |
70
|
7
|
|
|
7
|
|
60335
|
no strict 'subs'; |
|
7
|
|
|
|
|
21
|
|
|
7
|
|
|
|
|
366
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
#print "POSIX::EWOULDBLOCK is " . (eval 'POSIX::EWOULDBLOCK' |
73
|
|
|
|
|
|
|
# eq 'POSIX::EWOULDBLOCK' ? 'not ' : '') . "defined\n"; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# if EWOULDBLOCK is defined (as a sub, string, or whatever), |
76
|
|
|
|
|
|
|
# the test eval will pick it up, otherwise it'll just return |
77
|
|
|
|
|
|
|
# the string |
78
|
7
|
50
|
|
7
|
|
7668
|
eval 'package POSIX; sub EWOULDBLOCK() { 11 } ' |
79
|
|
|
|
|
|
|
if (eval 'POSIX::EWOULDBLOCK' eq 'POSIX::EWOULDBLOCK'); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
7
|
|
|
7
|
|
14045
|
use FileHandle; # IO::File wasn't around for < 5.004 |
|
7
|
|
|
|
|
130980
|
|
|
7
|
|
|
|
|
51
|
|
83
|
|
|
|
|
|
|
|
84
|
0
|
0
|
0
|
0
|
0
|
0
|
sub debug ($@) { $^W=0; if ($DEBUG and $_[0]<=$DEBUG) { shift; warn @_,"\n" } } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $digest; # sub ref to routine to create a hash of a string |
87
|
|
|
|
|
|
|
my %_digest = # randomly sorted mapping of decimal -> hex numbers |
88
|
|
|
|
|
|
|
qw/ |
89
|
|
|
|
|
|
|
0 fc 1 81 2 ab 3 c8 4 82 5 ad 6 f2 7 ff 8 c2 9 bd |
90
|
|
|
|
|
|
|
10 dd 11 84 12 dc 13 a2 14 db 15 c9 16 a1 17 b5 18 d9 19 b4 |
91
|
|
|
|
|
|
|
20 d7 21 ae 22 ce 23 92 24 cd 25 99 26 87 27 c1 28 a7 29 a5 |
92
|
|
|
|
|
|
|
30 bf 31 8e 32 e6 33 e7 34 ea 35 98 36 f5 37 f9 38 fb 39 df |
93
|
|
|
|
|
|
|
40 cb 41 d2 42 8f 43 d5 44 b2 45 da 46 b9 47 0d 48 0e 49 11 |
94
|
|
|
|
|
|
|
50 12 51 14 52 17 53 19 54 1a 55 1b 56 1c 57 1e 58 1f 59 20 |
95
|
|
|
|
|
|
|
60 21 61 22 62 23 63 24 64 25 65 26 66 27 67 28 68 2a 69 2b |
96
|
|
|
|
|
|
|
70 2c 71 2d 72 2f 73 30 74 31 75 32 76 33 77 34 78 35 79 37 |
97
|
|
|
|
|
|
|
80 39 81 3a 82 3b 83 3d 84 3e 85 40 86 41 87 42 88 43 89 45 |
98
|
|
|
|
|
|
|
90 46 91 48 92 4d 93 4f 94 51 95 52 96 55 97 56 98 57 99 58 |
99
|
|
|
|
|
|
|
100 59 101 5c 102 5d 103 5f 104 60 105 61 106 62 107 64 108 66 109 67 |
100
|
|
|
|
|
|
|
110 68 111 6b 112 6c 113 6d 114 6e 115 6f 116 70 117 71 118 72 119 73 |
101
|
|
|
|
|
|
|
120 74 121 76 122 79 123 7b 124 7c 125 7d 126 7e 127 7f 128 80 129 83 |
102
|
|
|
|
|
|
|
130 85 131 86 132 88 133 89 134 8a 135 8b 136 8c 137 8d 138 90 139 91 |
103
|
|
|
|
|
|
|
140 93 141 94 142 95 143 96 144 97 145 9a 146 9b 147 9c 148 9d 149 9e |
104
|
|
|
|
|
|
|
150 9f 151 a0 152 a3 153 a4 154 a6 155 a8 156 a9 157 aa 158 ac 159 af |
105
|
|
|
|
|
|
|
160 b0 161 b1 162 b3 163 b6 164 b7 165 b8 166 ba 167 bb 168 bc 169 be |
106
|
|
|
|
|
|
|
170 c0 171 c3 172 c4 173 c5 174 c6 175 c7 176 ca 177 cc 178 cf 179 d0 |
107
|
|
|
|
|
|
|
180 d1 181 d3 182 d4 183 d6 184 d8 185 de 186 e0 187 e1 188 e2 189 e3 |
108
|
|
|
|
|
|
|
190 e4 191 e5 192 00 193 01 194 e8 195 e9 196 02 197 eb 198 ec 199 ed |
109
|
|
|
|
|
|
|
200 ee 201 ef 202 f0 203 f1 204 04 205 f3 206 f4 207 05 208 f6 209 f7 |
110
|
|
|
|
|
|
|
210 f8 211 07 212 fa 213 09 214 0a 215 fd 216 fe 217 0c 218 16 219 1d |
111
|
|
|
|
|
|
|
220 5e 221 13 222 2e 223 69 224 15 225 0f 226 10 227 08 228 47 229 03 |
112
|
|
|
|
|
|
|
230 75 231 44 232 78 233 38 234 50 235 6a 236 4c 237 36 238 7a 239 29 |
113
|
|
|
|
|
|
|
240 5b 241 18 242 4b 243 5a 244 4a 245 49 246 63 247 54 248 0b 249 77 |
114
|
|
|
|
|
|
|
250 3f 251 65 252 53 253 06 254 3c 255 4e |
115
|
|
|
|
|
|
|
/; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
BEGIN |
118
|
|
|
|
|
|
|
{ |
119
|
7
|
50
|
|
7
|
|
44788
|
if (eval 'use Digest::MD5 (); 1') |
|
7
|
|
|
7
|
|
65
|
|
|
7
|
|
|
|
|
21
|
|
|
7
|
|
|
|
|
57
|
|
120
|
|
|
|
|
|
|
{ |
121
|
|
|
|
|
|
|
#debug 1, "using Digest::MD5"; |
122
|
7
|
|
|
|
|
27303
|
$digest = \&Digest::MD5::md5_hex; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
else |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# crypt is waaaayyyyy too slow (which is to be expected I suppose, presumably |
127
|
|
|
|
|
|
|
# it's purposely designed to be an expensive operation :-) |
128
|
|
|
|
|
|
|
# else |
129
|
|
|
|
|
|
|
# { |
130
|
|
|
|
|
|
|
# # resort to crypt - both much slower and less rigorous than Digest::MD5 |
131
|
|
|
|
|
|
|
# # - fudge crypt's output to be a hex string, skipping the salt and |
132
|
|
|
|
|
|
|
# # omitting the tail fractional-byte |
133
|
|
|
|
|
|
|
# #debug 1, "using crypt"; |
134
|
|
|
|
|
|
|
# $digest = sub { unpack('@1H10', pack('H*', crypt ($_[0], 'kylan'))) }; |
135
|
|
|
|
|
|
|
# } |
136
|
|
|
|
|
|
|
{ |
137
|
|
|
|
|
|
|
$digest = sub |
138
|
|
|
|
|
|
|
{ |
139
|
|
|
|
|
|
|
# yeah, well, this works but no guarantees |
140
|
0
|
|
0
|
|
|
0
|
my $d = $_[0] || 'bcc3b7b7b80'; |
141
|
0
|
|
|
|
|
0
|
my $cs = unpack('%16C*', $d) . length($d); |
142
|
0
|
|
|
|
|
0
|
join '', map {$_digest{(($_ ^ $cs) + 3) % 256}} unpack('C*', $d); |
|
0
|
|
|
|
|
0
|
|
143
|
0
|
|
|
|
|
0
|
}; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub digest |
148
|
|
|
|
|
|
|
# returns a hex-encoded digest of a string, of a given length |
149
|
|
|
|
|
|
|
{ |
150
|
181642
|
|
|
181642
|
0
|
472941
|
local $^W = 0; |
151
|
|
|
|
|
|
|
# 5.003 doesn't support the $c->() syntax |
152
|
181642
|
100
|
|
|
|
256214
|
substr (&{$digest}($_[0]), 0, $_[1]) || '0'; |
|
181642
|
|
|
|
|
1098912
|
|
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub newFileHandle ($;$$) |
156
|
|
|
|
|
|
|
{ |
157
|
|
|
|
|
|
|
# 5.003's FileHandle doesn't support 'perm' field |
158
|
933
|
50
|
|
933
|
0
|
11285
|
return ($] >= 5.004) ? new FileHandle (@_) : new FileHandle ($_[0], $_[1]); |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _lock |
162
|
|
|
|
|
|
|
{ |
163
|
138
|
|
|
138
|
|
328
|
my ($s, $op) = @_; |
164
|
138
|
|
|
|
|
240
|
my $l; |
165
|
|
|
|
|
|
|
eval |
166
|
138
|
|
|
|
|
452
|
{ |
167
|
138
|
|
|
0
|
|
5177
|
local $SIG{ALRM} = sub { $! = POSIX::EWOULDBLOCK; die "$!\n" }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
168
|
138
|
100
|
|
|
|
2190
|
alarm(($op & LOCK_EX) ? $s->{wrt} : $s->{rdt}); |
169
|
138
|
|
|
|
|
5707720
|
$l = flock($s->{lockfh}, $op); |
170
|
138
|
|
|
|
|
3577
|
alarm(0); |
171
|
|
|
|
|
|
|
}; |
172
|
138
|
50
|
|
|
|
1053
|
if ($@) { chomp $@; } elsif (!$l) { $@ = "$!" } |
|
0
|
50
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
173
|
138
|
|
|
|
|
853
|
return $l; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub TIEHASH |
177
|
|
|
|
|
|
|
# args compatible w/ DB_File: |
178
|
|
|
|
|
|
|
# dir - where to put files |
179
|
|
|
|
|
|
|
# flags - file open (DB) flags |
180
|
|
|
|
|
|
|
# perms - file creation permissions |
181
|
|
|
|
|
|
|
# plus: |
182
|
|
|
|
|
|
|
# nfiles - number of files to use when creating the DB (rounded to power of 16) |
183
|
|
|
|
|
|
|
# sep - character to use as the internal field separator |
184
|
|
|
|
|
|
|
# |
185
|
|
|
|
|
|
|
# - to avoid problems with system file decriptor limits, files are opened |
186
|
|
|
|
|
|
|
# and closed as-needed in the access routines. |
187
|
|
|
|
|
|
|
{ |
188
|
138
|
|
|
138
|
|
674
|
$ERROR = undef; |
189
|
|
|
|
|
|
|
|
190
|
138
|
|
|
|
|
1117
|
my ($class, $dir, $flags, $perms, $nfiles, $sep, $rdt, $wrt) = @_; |
191
|
138
|
|
50
|
|
|
1170
|
$dir ||= '.'; |
192
|
138
|
100
|
|
|
|
588
|
$flags |= O_CREAT if ($flags & O_WRONLY); |
193
|
138
|
|
50
|
|
|
1899
|
$perms ||= 0666; # don't restrict the user's umask |
194
|
138
|
|
33
|
|
|
1013
|
$nfiles ||= $NFILES; |
195
|
138
|
|
33
|
|
|
990
|
$sep ||= $SEP; |
196
|
|
|
|
|
|
|
|
197
|
138
|
|
33
|
|
|
904
|
$rdt ||= $LOCKRDTIMEOUT; |
198
|
138
|
|
33
|
|
|
814
|
$wrt ||= $LOCKWRTIMEOUT; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
#debug 2, sprintf 'TIEHASH (%s, %s, 0x%x, %s, %d, \x%x)', |
201
|
|
|
|
|
|
|
# $class, $dir, $flags, $perms, $nfiles, ord($sep); |
202
|
|
|
|
|
|
|
|
203
|
138
|
50
|
|
|
|
1488
|
$ERROR = 'must specify flags', return undef unless defined $flags; |
204
|
138
|
50
|
|
|
|
639
|
$ERROR = 'invalid flag: O_APPEND', return undef if ($flags & O_APPEND); |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# check base directory exists, create if necessary |
207
|
138
|
100
|
|
|
|
4991
|
unless (-d $dir) |
208
|
1
|
50
|
|
|
|
116
|
{ mkdir($dir, $perms|0700) or $ERROR="mkdir failed: $!", return undef; } |
209
|
|
|
|
|
|
|
|
210
|
138
|
|
|
|
|
588
|
my $s = {}; # object data |
211
|
|
|
|
|
|
|
|
212
|
138
|
|
|
|
|
756
|
$s->{rdt} = $rdt; |
213
|
138
|
|
|
|
|
432
|
$s->{wrt} = $wrt; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# acquire lock (held till object is destroyed) |
216
|
138
|
100
|
|
|
|
602
|
if ($flags & (O_WRONLY|O_RDWR)) |
217
|
|
|
|
|
|
|
{ |
218
|
10
|
50
|
|
|
|
76
|
$s->{lockfh} = newFileHandle ("$dir/$METAFILE", |
219
|
|
|
|
|
|
|
O_WRONLY|O_TRUNC|O_CREAT, $perms) |
220
|
|
|
|
|
|
|
or $ERROR="could not write [$dir/$METAFILE]: $!", return undef; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# there may be current readers, wait my turn |
223
|
10
|
50
|
|
|
|
3278
|
_lock($s, LOCK_EX) or $ERROR = "lock_ex failed: $@", return undef; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
#debug 1, 'LOCK_EX'; |
226
|
|
|
|
|
|
|
# nfiles, sep written to info file at end of this sub |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
else |
229
|
|
|
|
|
|
|
{ |
230
|
|
|
|
|
|
|
# well, how about that - yet another broken part of 5.003 |
231
|
|
|
|
|
|
|
# - flock won't give you a shared lock (gives errno = "Bad file |
232
|
|
|
|
|
|
|
# number", as if the file mode was wrong (e.g. EX lock on a |
233
|
|
|
|
|
|
|
# readonly file)). Anyhow, using a O_RDWR file partially works - |
234
|
|
|
|
|
|
|
# you get an exclusive lock (even for LOCK_SH). |
235
|
|
|
|
|
|
|
# - want to avoid a reader waiting for a database update (which can |
236
|
|
|
|
|
|
|
# take quite a while), so don't block (for long) |
237
|
128
|
50
|
|
|
|
1001
|
my $m = ($] < 5.004) ? O_RDWR : O_RDONLY; |
238
|
128
|
50
|
|
|
|
1913
|
$s->{lockfh} = newFileHandle ("$dir/$METAFILE", $m, $perms) |
239
|
|
|
|
|
|
|
or $ERROR="could not read [$dir/$METAFILE]: $!", return undef; |
240
|
|
|
|
|
|
|
|
241
|
128
|
50
|
|
|
|
27003
|
_lock($s, LOCK_SH) or $ERROR = "lock_sh failed: $@", return undef; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
#debug 1, 'LOCK_SH'; |
244
|
128
|
|
|
|
|
12540
|
$nfiles = $s->{lockfh}->getline(); |
245
|
128
|
|
|
|
|
14027
|
$sep = $s->{lockfh}->getline(); |
246
|
128
|
50
|
33
|
|
|
4898
|
$ERROR = "invalid info file [$dir/$METAFILE]", return undef |
247
|
|
|
|
|
|
|
unless (defined $nfiles and defined $sep); |
248
|
128
|
|
|
|
|
380
|
chomp $nfiles; |
249
|
128
|
|
|
|
|
639
|
chomp $sep; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
138
|
|
|
|
|
415
|
$s->{perms} = $perms; |
253
|
138
|
|
|
|
|
813
|
$s->{nfiles} = $nfiles; |
254
|
138
|
|
|
|
|
707
|
$s->{sep} = $sep; |
255
|
138
|
|
|
|
|
512
|
$s->{sep_ord} = ord($sep); |
256
|
|
|
|
|
|
|
|
257
|
138
|
50
|
|
|
|
1236
|
$ERROR = "invalid number of files [$nfiles]", return undef |
258
|
|
|
|
|
|
|
unless $nfiles =~ /^[1-9]\d*$/; |
259
|
|
|
|
|
|
|
|
260
|
138
|
|
|
|
|
755
|
$s->{dir} = $dir; |
261
|
|
|
|
|
|
|
# hang on to open flags, for use in _open |
262
|
|
|
|
|
|
|
# - exclude TRUNC and friends (files may be opened multiple times) |
263
|
138
|
|
|
|
|
393
|
$s->{fflags} = $flags & (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT); |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# create the file/s (round nfiles up to a power of 16) |
266
|
|
|
|
|
|
|
# - a digest length of 0 is fine (nfiles == 1) |
267
|
138
|
|
|
|
|
2638
|
$s->{dlen} = POSIX::ceil(log($nfiles)/log(16)); # filename/digest len |
268
|
138
|
|
|
|
|
538
|
$nfiles = 16 ** $s->{dlen}; |
269
|
|
|
|
|
|
|
#debug 6, "digest length = [$s->{dlen}], nfiles = [$nfiles]"; |
270
|
|
|
|
|
|
|
|
271
|
138
|
|
|
|
|
427
|
$s->{f} = {}; # digest => filename |
272
|
138
|
|
|
|
|
463
|
$s->{fh} = {}; # digest => filehandle |
273
|
138
|
|
|
|
|
323
|
$s->{fpos} = {}; # digest => fileposition |
274
|
138
|
|
|
|
|
380
|
$s->{dlist} = []; # list of digest values |
275
|
|
|
|
|
|
|
|
276
|
138
|
|
|
|
|
263
|
my $i; |
277
|
138
|
|
|
|
|
558
|
for ($i = 0; $i < $nfiles; $i++) |
278
|
|
|
|
|
|
|
{ |
279
|
|
|
|
|
|
|
# 5.003's printf doesn't support '*' |
280
|
|
|
|
|
|
|
#my $d = sprintf ('%0*x', $s->{dlen}, $i); |
281
|
2208
|
|
50
|
|
|
11771
|
my $d = substr(('0' x 16) . sprintf ('%x', $i), -($s->{dlen}||1)); |
282
|
2208
|
|
|
|
|
3703
|
my $f = "$dir/$d"; |
283
|
|
|
|
|
|
|
#debug 6, "filename [$f]"; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# hang on to the digest values + filenames for _open |
286
|
2208
|
|
|
|
|
2271
|
push (@{$s->{dlist}}, $d); |
|
2208
|
|
|
|
|
5327
|
|
287
|
2208
|
|
|
|
|
9372
|
$s->{f}{$d} = $f; |
288
|
|
|
|
|
|
|
|
289
|
2208
|
100
|
|
|
|
24394
|
truncate($f, 0) if ($flags & O_TRUNC); # start afresh if required |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
138
|
100
|
|
|
|
441
|
if ($flags & (O_WRONLY|O_RDWR)) |
293
|
|
|
|
|
|
|
{ |
294
|
10
|
|
|
|
|
179
|
$s->{lockfh}->print($nfiles . "\n"); |
295
|
10
|
|
|
|
|
234
|
$s->{lockfh}->print($sep . "\n"); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
138
|
|
|
|
|
1335
|
bless $s, $class; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
#my $_opens = 0; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub _open |
304
|
|
|
|
|
|
|
# open a DB file, transparently staying within the system file descriptor |
305
|
|
|
|
|
|
|
# limits |
306
|
|
|
|
|
|
|
{ |
307
|
795
|
|
|
795
|
|
1553
|
my ($self, $d) = @_; |
308
|
795
|
|
|
|
|
1283
|
my $fh; |
309
|
|
|
|
|
|
|
|
310
|
795
|
|
|
|
|
1006
|
my $n = int rand @{$self->{dlist}}; |
|
795
|
|
|
|
|
2244
|
|
311
|
795
|
|
|
|
|
1977
|
my $i = 0; |
312
|
795
|
|
|
|
|
1548
|
while ($i < @{$self->{dlist}}) |
|
795
|
|
|
|
|
2241
|
|
313
|
|
|
|
|
|
|
{ |
314
|
795
|
|
|
|
|
4407
|
$fh = $self->{fh}{$d} = |
315
|
|
|
|
|
|
|
newFileHandle ($self->{f}{$d}, $self->{fflags}, $self->{perms}); |
316
|
795
|
50
|
|
|
|
114965
|
last if defined ($fh); # good, opened the file |
317
|
|
|
|
|
|
|
|
318
|
0
|
0
|
|
|
|
0
|
last unless $! == POSIX::EMFILE; # abort on any other condition |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# if we're out of descriptors, close a random file to free one up |
321
|
|
|
|
|
|
|
# - would like to just grab the next one off the fh hash, but can't |
322
|
|
|
|
|
|
|
# efficiently use 'each' over the filehandles hash as there's |
323
|
|
|
|
|
|
|
# no simple way to reset the iterator |
324
|
|
|
|
|
|
|
# - use a separate array containing digest values |
325
|
|
|
|
|
|
|
# - remember the file position of closed files, to restore later |
326
|
0
|
|
|
|
|
0
|
my $t = $self->{dlist}[($n + $i) % @{$self->{dlist}}]; # target |
|
0
|
|
|
|
|
0
|
|
327
|
0
|
0
|
|
|
|
0
|
if (defined $self->{fh}{$t}) |
328
|
|
|
|
|
|
|
{ |
329
|
|
|
|
|
|
|
#debug 5, "\$! = EMFILE -> closing [$t] (" . |
330
|
|
|
|
|
|
|
# fileno($self->{fh}{$t}) . ")"; |
331
|
0
|
|
|
|
|
0
|
$self->{fpos}{$t} = $self->{fh}{$t}->tell(); |
332
|
0
|
|
|
|
|
0
|
close($self->{fh}{$t}); |
333
|
0
|
|
|
|
|
0
|
$self->{fh}{$t} = undef; |
334
|
|
|
|
|
|
|
} |
335
|
0
|
|
|
|
|
0
|
$i++; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
795
|
50
|
|
|
|
1902
|
$ERROR = "could not open [$self->{f}{$d}]: $!", return undef |
339
|
|
|
|
|
|
|
unless defined $fh; |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# reposition the file pointer |
342
|
795
|
50
|
|
|
|
1257
|
$fh->seek(0, ${$self->{fpos}}{$d}) if defined ${$self->{fpos}}{$d}; |
|
0
|
|
|
|
|
0
|
|
|
795
|
|
|
|
|
3473
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
#$_opens++; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
#debug 4, "opened [$d] (" . fileno($fh) . ")"; |
347
|
|
|
|
|
|
|
|
348
|
795
|
|
|
|
|
3621
|
return $fh; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub _escape ($;$$) |
352
|
|
|
|
|
|
|
# 'special' characters (newlines and the field separator) need to be escaped |
353
|
|
|
|
|
|
|
# when they appear within a hash key or value |
354
|
|
|
|
|
|
|
# - these special values are replaced with their 'base64' encoding |
355
|
|
|
|
|
|
|
# - further, special note must be made for undef and empty strings, I use the |
356
|
|
|
|
|
|
|
# _ and - characters to do this, and escape them if present in the 'user' |
357
|
|
|
|
|
|
|
# string |
358
|
|
|
|
|
|
|
{ |
359
|
362642
|
50
|
|
362642
|
|
1240783
|
if (not defined $_[0]) { $_[0] = '-' } # undef |
|
0
|
50
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
360
|
0
|
|
|
|
|
0
|
elsif ($_[0] eq '' ) { $_[0] = '_' } # empty string |
361
|
0
|
|
|
|
|
0
|
elsif ($_[0] eq '_') { $_[0] = '%5F' } |
362
|
0
|
|
|
|
|
0
|
elsif ($_[0] eq '-') { $_[0] = '%2D' } |
363
|
|
|
|
|
|
|
else # non-empty, incl "false" (e.g. '0') |
364
|
|
|
|
|
|
|
{ |
365
|
362642
|
|
|
|
|
781156
|
$_[0] =~ s/%/%25/sg; # percents |
366
|
362642
|
|
|
|
|
736331
|
$_[0] =~ s/\n/%0a/sg; # newlines |
367
|
362642
|
100
|
|
|
|
994071
|
$_[0] =~ s/\Q$_[1]\E/\%{$_[2]}/sge if $_[1]; # separator |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub _unescape ($) |
372
|
|
|
|
|
|
|
{ |
373
|
242641
|
50
|
|
242641
|
|
565279
|
if ($_[0] eq '_') { $_[0] = '' } |
|
0
|
50
|
|
|
|
0
|
|
374
|
0
|
|
|
|
|
0
|
elsif ($_[0] eq '-') { $_[0] = undef } |
|
242641
|
|
|
|
|
567200
|
|
375
|
120018
|
|
|
|
|
481051
|
else {$_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg } |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub FETCH |
379
|
|
|
|
|
|
|
{ |
380
|
121642
|
|
|
121642
|
|
140898
|
$ERROR = undef; |
381
|
|
|
|
|
|
|
|
382
|
121642
|
|
|
|
|
156787
|
my ($self, $key) = @_; |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# is this a call via NEXTKEY? |
385
|
121642
|
100
|
|
|
|
260654
|
if (exists $self->{nextval}) |
386
|
|
|
|
|
|
|
{ |
387
|
|
|
|
|
|
|
#debug 6, " record cached via NEXTKEY"; |
388
|
121000
|
|
|
|
|
169003
|
my $v = $self->{nextval}; |
389
|
121000
|
|
|
|
|
184215
|
delete $self->{nextval}; # make sure stale results don't arise |
390
|
121000
|
|
|
|
|
451926
|
return $v; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
642
|
50
|
|
|
|
2953
|
croak("FETCH: DB is WRONLY") if ($self->{fflags} & O_WRONLY); |
394
|
|
|
|
|
|
|
|
395
|
642
|
|
|
|
|
3193
|
my $d = digest($key, $self->{dlen}); |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# escape newlines and separators |
398
|
|
|
|
|
|
|
# - as per STORE - compare apples with apples |
399
|
642
|
|
|
|
|
3213
|
_escape($key, $self->{sep}, $self->{sep_ord}); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#debug 2, "FETCH ($self, $key [$d])"; |
402
|
|
|
|
|
|
|
|
403
|
642
|
100
|
|
|
|
4326
|
my $fh = defined $self->{fh}{$d} ? $self->{fh}{$d} : _open($self, $d); |
404
|
642
|
50
|
|
|
|
1785
|
return undef unless defined $fh; |
405
|
|
|
|
|
|
|
#debug 4, " fileno: " . fileno($fh); |
406
|
|
|
|
|
|
|
|
407
|
642
|
|
|
|
|
4573
|
$fh->seek(0, 0); # rewind |
408
|
642
|
|
|
|
|
8910
|
my $l = $self->{cache}; # last line read is cached, presuming multiple reads |
409
|
|
|
|
|
|
|
#debug 6, " line cached" if defined $l; |
410
|
642
|
100
|
|
|
|
9642
|
$l = $fh->getline() unless defined $l; |
411
|
642
|
|
|
|
|
7783
|
while (defined $l) |
412
|
|
|
|
|
|
|
{ |
413
|
|
|
|
|
|
|
#debug 9, " at " . $fh->tell(); |
414
|
411336
|
100
|
|
|
|
15832413
|
last if $l =~ /^\Q$key$self->{sep}\E/; |
415
|
410695
|
|
|
|
|
11173384
|
$l = $fh->getline(); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
641
|
50
|
|
|
|
4540
|
if ($l) |
419
|
|
|
|
|
|
|
{ |
420
|
641
|
|
|
|
|
2833
|
$self->{cache} = $l; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
#debug 3, " found at " . $fh->tell(); |
423
|
641
|
|
|
|
|
2303
|
chomp $l; |
424
|
641
|
|
|
|
|
4553
|
my ($k, $v) = split($self->{sep}, $l, 2); |
425
|
641
|
|
|
|
|
3222
|
_unescape($v); |
426
|
641
|
|
|
|
|
5621
|
return $v; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
else |
429
|
|
|
|
|
|
|
{ |
430
|
|
|
|
|
|
|
#debug 3, "\tkey not found"; |
431
|
0
|
|
|
|
|
0
|
return undef; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
sub EXISTS |
436
|
|
|
|
|
|
|
{ |
437
|
0
|
|
|
0
|
|
0
|
$ERROR = undef; |
438
|
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
0
|
my ($self, $key) = @_; |
440
|
|
|
|
|
|
|
|
441
|
0
|
0
|
|
|
|
0
|
croak("EXISTS: DB is WRONLY") if ($self->{fflags} & O_WRONLY); |
442
|
|
|
|
|
|
|
|
443
|
0
|
|
|
|
|
0
|
my $d = digest($key, $self->{dlen}); |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# escape newlines and separators |
446
|
|
|
|
|
|
|
# - as per STORE - compare apples with apples |
447
|
0
|
|
|
|
|
0
|
_escape($key, $self->{sep}, $self->{sep_ord}); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
#debug 2, "EXISTS ($self, $key [$d])"; |
450
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
0
|
my $fh = defined $self->{fh}{$d} ? $self->{fh}{$d} : _open($self, $d); |
452
|
0
|
0
|
|
|
|
0
|
return undef unless defined $fh; |
453
|
|
|
|
|
|
|
#debug 4, " fileno: " . fileno($fh); |
454
|
|
|
|
|
|
|
|
455
|
0
|
|
|
|
|
0
|
$fh->seek(0, 0); # rewind |
456
|
0
|
|
|
|
|
0
|
my $l; |
457
|
0
|
|
|
|
|
0
|
while (defined ($l = $fh->getline())) |
458
|
|
|
|
|
|
|
{ |
459
|
0
|
0
|
|
|
|
0
|
last if $l =~ /^\Q$key$self->{sep}\E/; |
460
|
|
|
|
|
|
|
} |
461
|
0
|
0
|
|
|
|
0
|
$self->{cache} = $l if defined $l; # cache for FETCH, if found |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
#debug 3, $_ ? " found at " . $fh->tell() : " not found"; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# returning undef seems to cause perl to try to FETCH the key |
466
|
|
|
|
|
|
|
# - presumably this is some kind of fall-back if the exists operator |
467
|
|
|
|
|
|
|
# "fails" |
468
|
0
|
0
|
|
|
|
0
|
return $l ? 1 : 0; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub _nextfile |
472
|
|
|
|
|
|
|
# find and open the next non-null file |
473
|
|
|
|
|
|
|
# - i.e. skip files which don't exist (recall that files are opened (created) |
474
|
|
|
|
|
|
|
# on-demand) |
475
|
|
|
|
|
|
|
# - note that a file may be used (contain data), but not open |
476
|
|
|
|
|
|
|
{ |
477
|
119
|
|
|
119
|
|
263
|
my ($self) = @_; |
478
|
119
|
|
|
|
|
182
|
my $fh; |
479
|
|
|
|
|
|
|
|
480
|
119
|
100
|
|
|
|
393
|
$self->{'next'} = 0 unless defined $self->{'next'}; |
481
|
|
|
|
|
|
|
|
482
|
119
|
|
100
|
|
|
469
|
while (not defined $fh and $self->{'next'} < @{$self->{dlist}}) |
|
119
|
|
|
|
|
534
|
|
483
|
|
|
|
|
|
|
{ |
484
|
|
|
|
|
|
|
# lookup next fh hash key |
485
|
112
|
|
|
|
|
399
|
my $d = $self->{dlist}->[$self->{'next'}]; |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# if fh=defined, file is already open (which also implies it exists :-) |
488
|
|
|
|
|
|
|
# - otherwise, open the filename, if it exists |
489
|
112
|
50
|
|
|
|
437
|
unless (defined ($fh = $self->{fh}{$d})) |
490
|
|
|
|
|
|
|
{ |
491
|
112
|
50
|
|
|
|
4614
|
$fh = _open($self, $d) if -e $self->{f}{$d}; |
492
|
|
|
|
|
|
|
} |
493
|
112
|
|
|
|
|
449
|
$self->{'next'}++; # get ready for next time round |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
#warn "_next = " . ($self->{'next'} - 1) . "\n" if $fh; |
496
|
119
|
|
|
|
|
3297
|
return $fh; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
sub FIRSTKEY |
500
|
|
|
|
|
|
|
{ |
501
|
7
|
|
|
7
|
|
8
|
my $self = shift; |
502
|
|
|
|
|
|
|
|
503
|
7
|
50
|
|
|
|
35
|
croak("FIRSTKEY: DB is WRONLY") if ($self->{fflags} & O_WRONLY); |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
#debug 2, "FIRSTKEY ($self)"; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
# find the first file |
508
|
7
|
|
|
|
|
15
|
$self->{'next'} = undef; # index into $self->{dlist} |
509
|
7
|
|
|
|
|
27
|
$self->{NEXTKEYfh} = _nextfile($self); |
510
|
|
|
|
|
|
|
|
511
|
7
|
|
|
|
|
22
|
NEXTKEY($self); |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub NEXTKEY |
515
|
|
|
|
|
|
|
# return the 'next' key in an iteration sequence started via FIRSTKEY |
516
|
|
|
|
|
|
|
# - perl will call FETCH on this key to extract the value |
517
|
|
|
|
|
|
|
# - kind of wasteful, would end up doing multiple reads for the same |
518
|
|
|
|
|
|
|
# piece of data, so cache the result (carefully - the value may well |
519
|
|
|
|
|
|
|
# be undef) |
520
|
|
|
|
|
|
|
{ |
521
|
121007
|
|
|
121007
|
|
139511
|
$ERROR = undef; |
522
|
|
|
|
|
|
|
|
523
|
121007
|
|
|
|
|
131986
|
my $self = shift; # 'lastkey' is unused |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
#debug 2, "NEXTKEY ($self) [$self->{'next'}]"; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# read next record, over all files |
528
|
121007
|
|
|
|
|
112984
|
my $l; |
529
|
121007
|
|
|
|
|
160007
|
my $fh = $self->{NEXTKEYfh}; # initialised by FIRSTKEY |
530
|
|
|
|
|
|
|
|
531
|
121007
|
|
100
|
|
|
2991933
|
while (defined $fh and not defined ($l = $fh->getline())) |
532
|
|
|
|
|
|
|
{ |
533
|
112
|
|
|
|
|
4726
|
$fh = $self->{NEXTKEYfh} = _nextfile($self); |
534
|
|
|
|
|
|
|
} |
535
|
121007
|
100
|
|
|
|
3788555
|
return undef unless defined $l; |
536
|
|
|
|
|
|
|
|
537
|
121000
|
|
|
|
|
144960
|
chomp $l; |
538
|
121000
|
|
|
|
|
410991
|
my ($k, $v) = split($self->{sep}, $l, 2); |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# unescape key and value |
541
|
|
|
|
|
|
|
# - value is 'cached' to be returned by the next FETCH |
542
|
121000
|
|
|
|
|
229448
|
_unescape($k); |
543
|
121000
|
|
|
|
|
181629
|
_unescape($v); |
544
|
|
|
|
|
|
|
|
545
|
121000
|
|
|
|
|
296377
|
$self->{nextval} = $v; |
546
|
|
|
|
|
|
|
# undef keys will cause perl to stop iterating, thinking NEXTKEY |
547
|
|
|
|
|
|
|
# has finished... (need an "undef but true" value :-) |
548
|
|
|
|
|
|
|
# - this creates a small discrepancy in that you can directly STORE |
549
|
|
|
|
|
|
|
# and FETCH undef and empty keys, but both return empty strings |
550
|
121000
|
50
|
|
|
|
632252
|
return defined $k ? $k : ''; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub STORE |
554
|
|
|
|
|
|
|
{ |
555
|
181000
|
|
|
181000
|
|
234311
|
$ERROR = undef; |
556
|
|
|
|
|
|
|
|
557
|
181000
|
|
|
|
|
316398
|
my ($self, $key, $value) = @_; |
558
|
|
|
|
|
|
|
|
559
|
181000
|
50
|
|
|
|
481707
|
croak("STORE: DB is RDONLY") unless ($self->{fflags} & (O_WRONLY|O_RDWR)); |
560
|
|
|
|
|
|
|
|
561
|
181000
|
|
|
|
|
391885
|
my $d = digest($key, $self->{dlen}); |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# escape newlines and separators |
564
|
181000
|
|
|
|
|
480155
|
_escape($key, $self->{sep}, $self->{sep_ord}); |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
#debug 2, "STORE ($self, $key [$d])"; |
567
|
|
|
|
|
|
|
|
568
|
181000
|
100
|
|
|
|
505846
|
my $fh = defined $self->{fh}{$d} ? $self->{fh}{$d} : _open($self, $d); |
569
|
181000
|
50
|
|
|
|
350470
|
return undef unless defined $fh; |
570
|
|
|
|
|
|
|
#debug 4, " fileno: " . fileno($fh); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
# only do newlines for value |
573
|
181000
|
|
|
|
|
288093
|
_escape($value); |
574
|
|
|
|
|
|
|
|
575
|
181000
|
|
|
|
|
405824
|
my $s = join($self->{sep}, $key, $value); |
576
|
|
|
|
|
|
|
|
577
|
181000
|
|
|
|
|
497209
|
$fh->seek(0,2); |
578
|
181000
|
|
|
|
|
4100823
|
$fh->print($s . "\n"); |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub DESTROY |
583
|
|
|
|
|
|
|
{ |
584
|
138
|
|
|
138
|
|
412
|
$ERROR = undef; |
585
|
|
|
|
|
|
|
|
586
|
138
|
|
|
|
|
272
|
my ($self) = @_; |
587
|
|
|
|
|
|
|
#debug 2, join(', ', 'DESTROY', @_); |
588
|
|
|
|
|
|
|
#debug 3, "$_opens opens"; |
589
|
|
|
|
|
|
|
#debug 4, "currently opened files = " . |
590
|
|
|
|
|
|
|
# scalar map {defined $self->{fh}{$_}} keys %{$self->{fh}}; |
591
|
|
|
|
|
|
|
|
592
|
138
|
50
|
|
|
|
357
|
map {close $self->{fh}{$_} if defined $self->{fh}{$_}} keys %{$self->{fh}}; |
|
795
|
|
|
|
|
32306
|
|
|
138
|
|
|
|
|
1601
|
|
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
#debug 1, 'LOCK_UN'; |
595
|
|
|
|
|
|
|
|
596
|
138
|
|
|
|
|
60893
|
flock($self->{lockfh}, LOCK_UN); |
597
|
138
|
|
|
|
|
8971
|
close($self->{lockfh}); |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub nop |
601
|
|
|
|
|
|
|
{ |
602
|
0
|
|
|
0
|
0
|
|
$ERROR = undef; |
603
|
|
|
|
|
|
|
|
604
|
0
|
|
|
|
|
|
my ($self, $method) = @_; |
605
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
|
croak ref($self) . " does not define the method ${method}"; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
0
|
|
|
0
|
|
|
sub CLEAR { my $self = shift; $self->nop("CLEAR") } |
|
0
|
|
|
|
|
|
|
610
|
0
|
|
|
0
|
|
|
sub DELETE { my $self = shift; $self->nop("DELETE") } |
|
0
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
1; # return true, as require requires |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
__END__ |