line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
## -*- Mode: CPerl -*- |
2
|
|
|
|
|
|
|
## File: DiaColloDB::PackedFile::MMap.pm |
3
|
|
|
|
|
|
|
## Author: Bryan Jurish <moocow@cpan.org> |
4
|
|
|
|
|
|
|
## Description: collocation db: flat fixed-length record-oriented files; mmap variant |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package DiaColloDB::PackedFile::MMap; |
7
|
1
|
|
|
1
|
|
8
|
use DiaColloDB::PackedFile; |
|
1
|
|
|
|
|
55
|
|
|
1
|
|
|
|
|
38
|
|
8
|
1
|
|
|
1
|
|
6
|
use DiaColloDB::Utils qw(:fcntl :file :pack); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
9
|
1
|
|
|
1
|
|
323
|
use File::Map qw(map_handle); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
10
|
1
|
|
|
1
|
|
155
|
use Fcntl qw(:DEFAULT :seek); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
196
|
|
11
|
1
|
|
|
1
|
|
425
|
use Carp; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
20
|
|
12
|
1
|
|
|
1
|
|
143
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
27
|
|
13
|
1
|
|
|
1
|
|
5
|
no warnings 'portable'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2267
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
##============================================================================== |
16
|
|
|
|
|
|
|
## Globals & Constants |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our @ISA = qw(DiaColloDB::PackedFile); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
##============================================================================== |
21
|
|
|
|
|
|
|
## Constructors etc. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
## $pf = CLASS_OR_OBJECT->new(%opts) |
24
|
|
|
|
|
|
|
## + %opts, %$pf: |
25
|
|
|
|
|
|
|
## ##-- PackedFile: user options |
26
|
|
|
|
|
|
|
## file => $filename, ##-- default: undef (none) |
27
|
|
|
|
|
|
|
## flags => $flags, ##-- fcntl flags or open-mode (default='r') |
28
|
|
|
|
|
|
|
## perms => $perms, ##-- creation permissions (default=(0666 &~umask)) |
29
|
|
|
|
|
|
|
## reclen => $reclen, ##-- record-length in bytes: (default: guess from pack format if available) |
30
|
|
|
|
|
|
|
## packas => $packas, ##-- pack-format or array; see DiaColloDB::Utils::packFilterStore(); |
31
|
|
|
|
|
|
|
## temp => $bool, ##-- if true, data file(s) will be unlinked on DESTROY |
32
|
|
|
|
|
|
|
## ## |
33
|
|
|
|
|
|
|
## ##-- PackedFile: filters |
34
|
|
|
|
|
|
|
## filter_fetch => $filter, ##-- DB_File-style filter for fetch |
35
|
|
|
|
|
|
|
## filter_store => $filter, ##-- DB_File-style filter for store |
36
|
|
|
|
|
|
|
## ## |
37
|
|
|
|
|
|
|
## ##-- PackedFile: low-level data |
38
|
|
|
|
|
|
|
## fh => $fh, ##-- underlying filehandle |
39
|
|
|
|
|
|
|
## ## |
40
|
|
|
|
|
|
|
## ##-- PackedFile::MMap: buffers |
41
|
|
|
|
|
|
|
## bufr => \$buf, ##-- mmap $fh |
42
|
|
|
|
|
|
|
## bufp => $bufp, ##-- current buffer position (logical record number) |
43
|
|
|
|
|
|
|
sub new { |
44
|
0
|
|
|
0
|
1
|
|
my $that = shift; |
45
|
0
|
|
|
|
|
|
return $that->SUPER::new( |
46
|
|
|
|
|
|
|
#$bufr=>undef, |
47
|
|
|
|
|
|
|
#bufp=>0, |
48
|
|
|
|
|
|
|
@_, |
49
|
|
|
|
|
|
|
); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
##============================================================================== |
54
|
|
|
|
|
|
|
## API: open/close |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
## $pf = $pf->open() |
57
|
|
|
|
|
|
|
## $pf = $pf->open($file) |
58
|
|
|
|
|
|
|
## $pf = $pf->open($file,$flags,%opts) |
59
|
|
|
|
|
|
|
## + %opts are as for new() |
60
|
|
|
|
|
|
|
## + $file defaults to $pf->{file} |
61
|
|
|
|
|
|
|
sub open { |
62
|
0
|
|
|
0
|
1
|
|
my ($pf,$file,$flags,%opts) = @_; |
63
|
0
|
0
|
|
|
|
|
$pf->SUPER::open($file,$flags,%opts) or return undef; |
64
|
0
|
0
|
|
|
|
|
return $pf if (!$pf->isa(__PACKAGE__)); ##-- superclass open() promoted us to another class |
65
|
0
|
|
|
|
|
|
$pf->{bufp} = 0; |
66
|
0
|
|
|
|
|
|
return $pf->remap(); |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
## $bool = $pf->remap() |
70
|
|
|
|
|
|
|
## + re-maps $pf->{bufr} from $pf->{fh} |
71
|
|
|
|
|
|
|
sub remap { |
72
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
##-- try to ensure filehandle is flushed to disk to handle recent writes |
75
|
0
|
0
|
0
|
|
|
|
if (fcwrite($pf->{flags}//'r')) { |
76
|
0
|
0
|
|
|
|
|
CORE::seek($pf->{fh},0,SEEK_END) or return undef; |
77
|
0
|
0
|
|
|
|
|
CORE::truncate($pf->{fh}, $pf->{fh}->tell) or return undef; |
78
|
|
|
|
|
|
|
} |
79
|
0
|
0
|
|
|
|
|
CORE::seek($pf->{fh},0,SEEK_SET) or return undef; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
##-- mmap handles |
82
|
0
|
|
|
|
|
|
my ($buf); |
83
|
|
|
|
|
|
|
##-- BUGHUNT/birmingham.2016-07: "could not map errors" after 5 calls to remap() (xf.dba2, called from Unigrams::loadTextFile via flush()) |
84
|
0
|
|
|
|
|
|
map_handle($buf, $pf->{fh}, fcperl($pf->{flags})); |
85
|
0
|
|
|
|
|
|
$pf->{bufr} = \$buf; |
86
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
return $pf; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
## $bool = $pf->opened() |
91
|
|
|
|
|
|
|
sub opened { |
92
|
0
|
|
|
0
|
1
|
|
return defined($_[0]{bufr}); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
## $bool = $pf->reopen() |
96
|
|
|
|
|
|
|
## + re-opens datafile |
97
|
|
|
|
|
|
|
sub reopen { |
98
|
0
|
|
|
0
|
0
|
|
my $pf = shift; |
99
|
0
|
|
0
|
|
|
|
return $pf->SUPER::reopen() && $pf->remap(); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
## $bool = $pf->close() |
103
|
|
|
|
|
|
|
sub close { |
104
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
105
|
0
|
|
|
|
|
|
my $rc = $pf->SUPER::close(); |
106
|
0
|
|
|
|
|
|
delete $pf->{bufr}; |
107
|
0
|
|
|
|
|
|
return $rc; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
## $bool = $pf->setsize($nrecords) |
111
|
|
|
|
|
|
|
sub setsize { |
112
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
113
|
0
|
0
|
|
|
|
|
$pf->SUPER::setsize(@_) || return undef; |
114
|
0
|
|
|
|
|
|
$pf->remap(); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
## $bool = $pf->truncate() |
118
|
|
|
|
|
|
|
## + truncates $pf->{fh} or $pf->{file}; otherwise a no-nop |
119
|
|
|
|
|
|
|
sub truncate { |
120
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
121
|
0
|
0
|
|
|
|
|
$pf->SUPER::truncate(@_) || return undef; |
122
|
0
|
|
|
|
|
|
$pf->remap(); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
## $bool = $pf->flush() |
126
|
|
|
|
|
|
|
## + attempt to flush underlying filehandle, may not work |
127
|
|
|
|
|
|
|
## + INHERITED |
128
|
|
|
|
|
|
|
sub flush { |
129
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
130
|
0
|
0
|
|
|
|
|
$pf->SUPER::flush(@_) or return undef; |
131
|
0
|
|
|
|
|
|
$pf->remap(); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
##============================================================================== |
135
|
|
|
|
|
|
|
## API: filters |
136
|
|
|
|
|
|
|
## + INHERITED from PackedFile |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
##============================================================================== |
139
|
|
|
|
|
|
|
## API: positioning |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
## $nrecords = $pf->size() |
142
|
|
|
|
|
|
|
## + returns number of records |
143
|
|
|
|
|
|
|
sub size { |
144
|
0
|
0
|
|
0
|
1
|
|
return undef if (!$_[0]{bufr}); |
145
|
0
|
|
|
|
|
|
return length(${$_[0]{bufr}})/$_[0]{reclen}; |
|
0
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
## $bool = $pf->seek($recno) |
149
|
|
|
|
|
|
|
## + seek to record-number $recno |
150
|
|
|
|
|
|
|
sub seek { |
151
|
0
|
|
|
0
|
1
|
|
$_[0]{bufp} = $_[1]; |
152
|
0
|
|
|
|
|
|
return 1; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
## $recno = $pf->tell() |
156
|
|
|
|
|
|
|
## + report current record-number |
157
|
|
|
|
|
|
|
sub tell { |
158
|
0
|
|
|
0
|
1
|
|
return $_[0]{bufp}; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
## $bool = $pf->reset(); |
162
|
|
|
|
|
|
|
## + reset position to beginning of file |
163
|
|
|
|
|
|
|
## + INHERITED from PackedFile |
164
|
|
|
|
|
|
|
sub reset { |
165
|
0
|
|
|
0
|
1
|
|
return $_[0]->seek(0); |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
## $bool = $pf->seekend() |
169
|
|
|
|
|
|
|
## + seek to end-of file |
170
|
|
|
|
|
|
|
sub seekend { |
171
|
0
|
|
|
0
|
1
|
|
return $_[0]->seek($_[0]->size); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
## $bool = $pf->eof() |
175
|
|
|
|
|
|
|
## + returns true iff current position is end-of-file |
176
|
|
|
|
|
|
|
sub eof { |
177
|
0
|
|
|
0
|
1
|
|
return $_[0]{bufp} >= $_[0]->size; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
##============================================================================== |
181
|
|
|
|
|
|
|
## API: record access |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
##-------------------------------------------------------------- |
184
|
|
|
|
|
|
|
## API: record access: read |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
## $bool = $pf->read(\$buf) |
187
|
|
|
|
|
|
|
## + read a raw record into \$buf |
188
|
|
|
|
|
|
|
sub read { |
189
|
0
|
|
|
0
|
1
|
|
${$_[1]} = substr(${$_[0]{bufr}}, $_[0]{bufp}*$_[0]{reclen}, $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
++$_[0]{bufp}; |
191
|
0
|
|
|
|
|
|
return length(${$_[1]})==$_[0]{reclen}; |
|
0
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
## $bool = $pf->readraw(\$buf, $nrecords) |
195
|
|
|
|
|
|
|
## + batch-reads $nrecords into \$buf |
196
|
|
|
|
|
|
|
sub readraw { |
197
|
0
|
|
|
0
|
1
|
|
${$_[1]} = substr(${$_[0]{bufr}}, $_[0]{bufp}*$_[0]{reclen}, $_[2]*$_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
|
$_[0]{bufp} += $_[2]; |
199
|
0
|
|
|
|
|
|
return length(${$_[1]})==$_[2]*$_[0]{reclen}; |
|
0
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
## $value_or_undef = $pf->get() |
203
|
|
|
|
|
|
|
## + get (unpacked) value of current record, increments filehandle position to next record |
204
|
|
|
|
|
|
|
sub get { |
205
|
0
|
|
|
0
|
1
|
|
local $_ = substr(${$_[0]{bufr}}, $_[0]{bufp}*$_[0]{reclen}, $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
206
|
0
|
0
|
|
|
|
|
return undef if (length($_) != $_[0]{reclen}); |
207
|
0
|
|
|
|
|
|
++$_[0]{bufp}; |
208
|
0
|
0
|
|
|
|
|
$_[0]{filter_fetch}->() if ($_[0]{filter_fetch}); |
209
|
0
|
|
|
|
|
|
return $_; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
## \$buf_or_undef = $pf->getraw(\$buf) |
213
|
|
|
|
|
|
|
## + get (packed) value of current record, increments filehandle position to next record |
214
|
|
|
|
|
|
|
sub getraw { |
215
|
0
|
|
|
0
|
1
|
|
${$_[1]} = substr(${$_[0]{bufr}}, $_[0]{bufp}*$_[0]{reclen}, $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
|
++$_[0]{bufp}; |
217
|
0
|
0
|
|
|
|
|
return undef if (length(${$_[1]}) != $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
218
|
0
|
|
|
|
|
|
return $_[1]; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
## $value_or_undef = $pf->fetch($index) |
222
|
|
|
|
|
|
|
## + get (unpacked) value of record $index |
223
|
|
|
|
|
|
|
sub fetch { |
224
|
0
|
|
|
0
|
1
|
|
local $_ = substr(${$_[0]{bufr}}, $_[1]*$_[0]{reclen}, $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
225
|
0
|
|
|
|
|
|
++$_[0]{bufp}; |
226
|
0
|
0
|
|
|
|
|
return undef if (length($_) != $_[0]{reclen}); |
227
|
0
|
0
|
|
|
|
|
$_[0]{filter_fetch}->() if ($_[0]{filter_fetch}); |
228
|
0
|
|
|
|
|
|
return $_; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
## $buf_or_undef = $pf->fetchraw($index,\$buf) |
232
|
|
|
|
|
|
|
## + get (packed) value of record $index |
233
|
|
|
|
|
|
|
sub fetchraw { |
234
|
0
|
|
|
0
|
1
|
|
${$_[2]} = substr(${$_[0]{bufr}}, $_[1]*$_[0]{reclen}, $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
|
++$_[0]{bufp}; |
236
|
0
|
0
|
|
|
|
|
return undef if (length(${$_[2]}) != $_[0]{reclen}); |
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
return ${$_[2]}; |
|
0
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
##-------------------------------------------------------------- |
241
|
|
|
|
|
|
|
## API: record access: write |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
## $bool = $pf->write($buf) |
244
|
|
|
|
|
|
|
## + write a raw record $buf to current position; increments position |
245
|
|
|
|
|
|
|
sub write { |
246
|
0
|
|
|
0
|
1
|
|
$_[0]->logconfess("write(): method not supported"); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
## $value_or_undef = $pf->set($value) |
250
|
|
|
|
|
|
|
## + set (packed) value of current record, increments filehandle position to next record |
251
|
|
|
|
|
|
|
sub set { |
252
|
0
|
|
|
0
|
1
|
|
$_[0]->logconfess("set(): method not supported"); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
## $value_or_undef = $pf->store($index,$value) |
256
|
|
|
|
|
|
|
## + store (packed) $value as record-number $index |
257
|
|
|
|
|
|
|
sub store { |
258
|
0
|
|
|
0
|
1
|
|
$_[0]->logconfess("store(): method not supported"); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
## $value_or_undef = $pf->push($value) |
262
|
|
|
|
|
|
|
## + store (packed) $value at end of record |
263
|
|
|
|
|
|
|
sub push { |
264
|
0
|
|
|
0
|
1
|
|
$_[0]->logconfess("push(): method not supported"); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
##============================================================================== |
268
|
|
|
|
|
|
|
## API: batch I/O |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
## \@data = $pf->toArray(%opts) |
271
|
|
|
|
|
|
|
## + read entire contents to an array |
272
|
|
|
|
|
|
|
## + %opts : override %$pf: |
273
|
|
|
|
|
|
|
## packas => $packas |
274
|
|
|
|
|
|
|
sub toArray { |
275
|
0
|
|
|
0
|
1
|
|
my ($pf,%opts) = @_; |
276
|
0
|
0
|
|
|
|
|
$pf->setFilters($opts{packas}) if (exists($opts{packas})); |
277
|
0
|
|
|
|
|
|
my ($bufr,$filter_fetch,$reclen) = @$pf{qw(bufr filter_fetch reclen)}; |
278
|
0
|
|
|
|
|
|
my @data = qw(); |
279
|
0
|
|
|
|
|
|
local $_; |
280
|
0
|
|
|
|
|
|
my $off = 0; |
281
|
0
|
|
|
|
|
|
my $end = length($$bufr); |
282
|
0
|
|
|
|
|
|
for ($off=0; $off < $end; $off += $reclen) { |
283
|
0
|
|
|
|
|
|
$_ = substr($$bufr, $off, $reclen); |
284
|
0
|
0
|
|
|
|
|
$filter_fetch->() if ($filter_fetch); |
285
|
0
|
|
|
|
|
|
CORE::push(@data,$_); |
286
|
|
|
|
|
|
|
} |
287
|
0
|
|
|
|
|
|
$pf->setFilters(); |
288
|
0
|
|
|
|
|
|
return \@data; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
## $pf = $pf->fromArray(\@data,%opts) |
292
|
|
|
|
|
|
|
## + write file contents from an array |
293
|
|
|
|
|
|
|
## + %opts : override %$pf: |
294
|
|
|
|
|
|
|
## packas => $packas |
295
|
|
|
|
|
|
|
sub fromArray { |
296
|
0
|
|
|
0
|
1
|
|
my ($pf,$data,%opts) = @_; |
297
|
0
|
0
|
|
|
|
|
$pf->setFilters($opts{packas}) if (exists($opts{packas})); |
298
|
0
|
|
|
|
|
|
local $_; |
299
|
0
|
0
|
|
|
|
|
$pf->setsize(scalar @$data) |
300
|
|
|
|
|
|
|
or $pf->logconfess("fromArray(): failed to set file size = ", scalar(@$data), ": $!"); |
301
|
0
|
|
|
|
|
|
my ($bufr,$reclen,$filter_store) = @$pf{qw(bufr reclen filter_store)}; |
302
|
0
|
|
|
|
|
|
my $i = 0; |
303
|
0
|
|
|
|
|
|
foreach (@$data) { |
304
|
0
|
0
|
|
|
|
|
$filter_store->() if ($filter_store); |
305
|
0
|
|
|
|
|
|
substr($bufr, $i*$reclen, $reclen) = $_; |
306
|
0
|
|
|
|
|
|
++$i; |
307
|
|
|
|
|
|
|
} |
308
|
0
|
|
|
|
|
|
$pf->setFilters(); |
309
|
0
|
|
|
|
|
|
return $pf; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
## $pdl = $pf->toPdl(%options) |
313
|
|
|
|
|
|
|
## + returns a piddle for $pf |
314
|
|
|
|
|
|
|
## + %options: |
315
|
|
|
|
|
|
|
## type => $pdl_type, ##-- pdl type (default:'auto':guess) |
316
|
|
|
|
|
|
|
## swap => $bool_or_sub, ##-- byte-swap? (default:'auto':guess) |
317
|
|
|
|
|
|
|
## mmap => $bool, ##-- mmap data? (default: 0) |
318
|
|
|
|
|
|
|
## ... ##-- other options passed to DiaColloDB::Utils::readPdlFile() |
319
|
|
|
|
|
|
|
## + INHERITED from PackedFile |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
##============================================================================== |
322
|
|
|
|
|
|
|
## API: binary search |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
## $nbits_or_undef = $pf->vnbits() |
325
|
|
|
|
|
|
|
## + returns number of bits for using vec()-style search via Algorithm::BinarySearch::Vec, or undef if not supported |
326
|
|
|
|
|
|
|
## + currently UNUSED |
327
|
|
|
|
|
|
|
sub vnbits { |
328
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
329
|
0
|
|
|
|
|
|
my $packas = $pf->{packas}; |
330
|
0
|
|
|
|
|
|
my $reclen = $pf->{reclen}; |
331
|
0
|
0
|
|
|
|
|
if ($reclen==1) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
332
|
0
|
|
|
|
|
|
return 8; |
333
|
|
|
|
|
|
|
} elsif ($reclen==2) { |
334
|
0
|
0
|
|
|
|
|
return 16 if (unpack('n',pack($packas,0xfedc)) == 0xfedc); |
335
|
|
|
|
|
|
|
} elsif ($reclen==4) { |
336
|
0
|
0
|
|
|
|
|
return 32 if (unpack('N',pack($packas,0xfedca987)) == 0xfedca987); |
337
|
|
|
|
|
|
|
} elsif ($reclen==8) { |
338
|
0
|
0
|
|
|
|
|
return 64 if (unpack('Q>',pack($packas,0xfedca9876543210f)) == 0xfedca9876543210f); |
339
|
|
|
|
|
|
|
} |
340
|
0
|
|
|
|
|
|
return undef; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
## $index_or_undef = $pf->bsearch($key, %opts) |
344
|
|
|
|
|
|
|
## + %opts: |
345
|
|
|
|
|
|
|
## lo => $ilo, ##-- index lower-bound for search (default=0) |
346
|
|
|
|
|
|
|
## hi => $ihi, ##-- index upper-bound for search (default=size) |
347
|
|
|
|
|
|
|
## packas => $packas, ##-- key-pack template (default=$pf->{packas}) |
348
|
|
|
|
|
|
|
## + returns the minimum index $i such that unpack($packas,$pf->[$i]) == $key and $ilo <= $j < $i, |
349
|
|
|
|
|
|
|
## or undef if no such $i exists. |
350
|
|
|
|
|
|
|
## + $key must be a numeric value, and records must be stored in ascending order |
351
|
|
|
|
|
|
|
## by numeric value of key (as unpacked by $packas) between $ilo and $ihi |
352
|
|
|
|
|
|
|
## + TODO: optimize this to use Algorithm::BinarySearch::Vec (only applicable for scalar pack-templates) |
353
|
|
|
|
|
|
|
sub bsearch { |
354
|
0
|
|
|
0
|
1
|
|
my ($pf,$key,%opts) = @_; |
355
|
0
|
|
0
|
|
|
|
my $ilo = $opts{lo} // 0; |
356
|
0
|
|
0
|
|
|
|
my $ihi = $opts{hi} // $pf->size; |
357
|
0
|
|
0
|
|
|
|
my $packas = $opts{packas} // $pf->{packas}; |
358
|
0
|
|
|
|
|
|
my $reclen = $pf->{reclen}; |
359
|
0
|
|
|
|
|
|
my $bufr = $pf->{bufr}; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
##-- binary search guts |
362
|
0
|
|
|
|
|
|
my ($imid,$keymid); |
363
|
0
|
|
|
|
|
|
while ($ilo < $ihi) { |
364
|
0
|
|
|
|
|
|
$imid = ($ihi+$ilo) >> 1; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
##-- get item[$imid] |
367
|
0
|
|
|
|
|
|
($keymid) = unpack($packas, substr($$bufr, $imid*$reclen, $reclen)); |
368
|
|
|
|
|
|
|
|
369
|
0
|
0
|
|
|
|
|
if ($keymid < $key) { |
370
|
0
|
|
|
|
|
|
$ilo = $imid + 1; |
371
|
|
|
|
|
|
|
} else { |
372
|
0
|
|
|
|
|
|
$ihi = $imid; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
0
|
0
|
|
|
|
|
if ($ilo==$ihi) { |
377
|
|
|
|
|
|
|
##-- get item[$ilo] |
378
|
0
|
|
|
|
|
|
($keymid) = unpack($packas, substr($$bufr, $ilo*$reclen, $reclen)); |
379
|
0
|
0
|
|
|
|
|
return $ilo if ($keymid == $key); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
|
return undef; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
##============================================================================== |
386
|
|
|
|
|
|
|
## disk usage, timestamp, etc |
387
|
|
|
|
|
|
|
## + see DiaColloDB::Persistent |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
## @files = $obj->diskFiles() |
390
|
|
|
|
|
|
|
## + returns disk storage files, used by du() and timestamp() |
391
|
|
|
|
|
|
|
## + default implementation returns $obj->{file} or glob("$obj->{base}*") |
392
|
|
|
|
|
|
|
## + INHERITED from PackedFile |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
##============================================================================== |
396
|
|
|
|
|
|
|
## I/O |
397
|
|
|
|
|
|
|
## + largely INHERITED from DiaColloDB::Persistent, DiaColloDB::PackedFile |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
##-------------------------------------------------------------- |
400
|
|
|
|
|
|
|
## I/O: header |
401
|
|
|
|
|
|
|
## + largely INHERITED from DiaColloDB::Persistent |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
## @keys = $coldb->headerKeys() |
404
|
|
|
|
|
|
|
## + keys to save as header |
405
|
|
|
|
|
|
|
sub headerKeys { |
406
|
0
|
|
|
0
|
1
|
|
my $pf = shift; |
407
|
0
|
|
0
|
|
|
|
return grep {!ref($_[0]{$_}) && $_ !~ m{^(?:bufp)$}} $pf->SUPER::headerKeys(@_); |
|
0
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
##-------------------------------------------------------------- |
411
|
|
|
|
|
|
|
## I/O: text |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
## $bool = $obj->saveTextFile($filename_or_handle, %opts) |
414
|
|
|
|
|
|
|
## + wraps saveTextFh() |
415
|
|
|
|
|
|
|
## + INHERITED from DiaColloDB::Persistent |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
## $bool = $pf->saveTextFh($fh, %opts) |
418
|
|
|
|
|
|
|
## + save from text file with lines of the form "KEY? VALUE(s)..." |
419
|
|
|
|
|
|
|
## + %opts: |
420
|
|
|
|
|
|
|
## keys=>$bool, ##-- do/don't save keys (default=true) |
421
|
|
|
|
|
|
|
## key2s=>$key2s, ##-- code-ref for key formatting, called as $s=$key2s->($key) |
422
|
|
|
|
|
|
|
sub saveTextFh { |
423
|
0
|
|
|
0
|
1
|
|
my ($pf,$outfh,%opts) = @_; |
424
|
0
|
0
|
|
|
|
|
$pf->logconfess("saveTextFh(): no packed-file opened!") if (!$pf->opened); |
425
|
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
|
my $key2s = $opts{key2s}; |
427
|
0
|
|
0
|
|
|
|
my $keys = $opts{keys} // 1; |
428
|
0
|
|
|
|
|
|
my $bufr = $pf->{bufr}; |
429
|
0
|
|
|
|
|
|
my $size = $pf->size; |
430
|
0
|
|
|
|
|
|
my ($i,$key,$val); |
431
|
0
|
|
|
|
|
|
for ($i=0, $pf->reset; $i < $size; ++$i) { |
432
|
0
|
|
|
|
|
|
$val = $pf->get(); |
433
|
0
|
0
|
|
|
|
|
$outfh->print(($keys |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
434
|
|
|
|
|
|
|
? (($key2s ? $key2s->($i) : $i),"\t") |
435
|
|
|
|
|
|
|
: qw()), |
436
|
|
|
|
|
|
|
(UNIVERSAL::isa($val,'ARRAY') ? join(' ',@$val) : $val), |
437
|
|
|
|
|
|
|
"\n"); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
|
return $pf; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
## $bool = $obj->loadTextFile($filename_or_handle, %opts) |
444
|
|
|
|
|
|
|
## + wraps loadTextFh() |
445
|
|
|
|
|
|
|
## + INHERITED from DiaColloDB::Persistent |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
## $bool = $pf->loadTextFh($fh, %opts) |
448
|
|
|
|
|
|
|
## + load from text file with lines of the form "KEY? VALUE(s)..." |
449
|
|
|
|
|
|
|
## + %opts: |
450
|
|
|
|
|
|
|
## keys=>$bool, ##-- expect keys in input? (default=true) |
451
|
|
|
|
|
|
|
## gaps=>$bool, ##-- expect gaps or out-of-order elements in input? (default=false; implies keys=>1) |
452
|
|
|
|
|
|
|
## + INHERITED from DiaColloDB::Persistent |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
##============================================================================== |
456
|
|
|
|
|
|
|
## Footer |
457
|
|
|
|
|
|
|
1; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
__END__ |