| 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__ |