| 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
|
|
7
|
use DiaColloDB::PackedFile; |
|
|
1
|
|
|
|
|
53
|
|
|
|
1
|
|
|
|
|
31
|
|
|
8
|
1
|
|
|
1
|
|
6
|
use DiaColloDB::Utils qw(:fcntl :file :pack); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
36
|
|
|
9
|
1
|
|
|
1
|
|
284
|
use File::Map qw(map_handle); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
6
|
|
|
10
|
1
|
|
|
1
|
|
128
|
use Fcntl qw(:DEFAULT :seek); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
48
|
|
|
11
|
1
|
|
|
1
|
|
359
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
17
|
|
|
12
|
1
|
|
|
1
|
|
111
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
21
|
|
|
13
|
1
|
|
|
1
|
|
4
|
no warnings 'portable'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1922
|
|
|
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__ |