| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package MMapDB; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
47547
|
use 5.008008; |
|
|
2
|
|
|
|
|
8
|
|
|
|
2
|
|
|
|
|
78
|
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
61
|
|
|
5
|
2
|
|
|
2
|
|
7
|
use warnings; |
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
74
|
|
|
6
|
2
|
|
|
2
|
|
7
|
no warnings qw/uninitialized/; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
65
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
9
|
|
|
|
|
|
|
# keep this in mind |
|
10
|
2
|
|
|
2
|
|
1708
|
use integer; |
|
|
2
|
|
|
|
|
17
|
|
|
|
2
|
|
|
|
|
9
|
|
|
11
|
|
|
|
|
|
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
12
|
|
|
|
|
|
|
|
|
13
|
2
|
|
|
2
|
|
55
|
use Fcntl qw/:seek :flock/; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
278
|
|
|
14
|
2
|
|
|
2
|
|
10
|
use File::Spec; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
43
|
|
|
15
|
2
|
|
|
2
|
|
1601
|
use File::Map qw/map_handle protect/; |
|
|
2
|
|
|
|
|
15845
|
|
|
|
2
|
|
|
|
|
11
|
|
|
16
|
2
|
|
|
2
|
|
265
|
use Exporter qw/import/; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
53
|
|
|
17
|
2
|
|
|
2
|
|
433121
|
use Encode (); |
|
|
2
|
|
|
|
|
57985
|
|
|
|
2
|
|
|
|
|
388
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
{ # limit visibility of "our"/"my" variables |
|
20
|
|
|
|
|
|
|
our $VERSION = '0.15'; |
|
21
|
|
|
|
|
|
|
our %EXPORT_TAGS= |
|
22
|
|
|
|
|
|
|
( |
|
23
|
|
|
|
|
|
|
mode =>[qw/DATAMODE_NORMAL DATAMODE_SIMPLE/], |
|
24
|
|
|
|
|
|
|
error=>[qw/E_READONLY E_TWICE E_TRANSACTION E_FULL E_DUPLICATE |
|
25
|
|
|
|
|
|
|
E_OPEN E_READ E_WRITE E_CLOSE E_RENAME E_TRUNCATE E_LOCK |
|
26
|
|
|
|
|
|
|
E_RANGE E_NOT_IMPLEMENTED/], |
|
27
|
|
|
|
|
|
|
); |
|
28
|
|
|
|
|
|
|
my %seen; |
|
29
|
|
|
|
|
|
|
undef @seen{map {@$_} values %EXPORT_TAGS}; |
|
30
|
|
|
|
|
|
|
our @EXPORT_OK=keys %seen; |
|
31
|
|
|
|
|
|
|
$EXPORT_TAGS{all}=\@EXPORT_OK; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
require XSLoader; |
|
34
|
|
|
|
|
|
|
XSLoader::load('MMapDB', $VERSION); |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
our @attributes; |
|
37
|
|
|
|
|
|
|
BEGIN { |
|
38
|
|
|
|
|
|
|
# define attributes and implement accessor methods |
|
39
|
|
|
|
|
|
|
# !! keep in sync with MMapDB.xs !! |
|
40
|
2
|
|
|
2
|
|
12
|
@attributes=(qw/filename readonly intfmt _data _intsize _stringfmt |
|
41
|
|
|
|
|
|
|
_stringtbl mainidx _ididx main_index id_index |
|
42
|
|
|
|
|
|
|
_nextid _idmap _tmpfh _tmpname _stringfh _stringmap |
|
43
|
|
|
|
|
|
|
_strpos lockfile flags dbformat_in dbformat_out |
|
44
|
|
|
|
|
|
|
_stringfmt_out stringmap_prealloc _stringmap_end |
|
45
|
|
|
|
|
|
|
index_prealloc _index_end _tmpmap |
|
46
|
|
|
|
|
|
|
/); |
|
47
|
2
|
|
|
|
|
12
|
for( my $i=0; $i<@attributes; $i++ ) { |
|
48
|
56
|
|
|
|
|
62
|
my $method_num=$i; |
|
49
|
|
|
|
|
|
|
## no critic |
|
50
|
2
|
|
|
2
|
|
19
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
145
|
|
|
51
|
56
|
|
|
50736
|
|
706
|
*{__PACKAGE__.'::'.$attributes[$method_num]}= |
|
|
50736
|
|
|
|
|
112467
|
|
|
52
|
56
|
|
|
|
|
276
|
sub : lvalue {$_[0]->[$method_num]}; |
|
53
|
|
|
|
|
|
|
## use critic |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
my @dbformats=qw/MMDB MMDC/; |
|
59
|
|
|
|
|
|
|
my %dbformats=do { my $i=0; map {($_=>$i++)} @dbformats }; |
|
60
|
|
|
|
|
|
|
|
|
61
|
2
|
|
|
2
|
|
19603
|
BEGIN { |
|
62
|
|
|
|
|
|
|
use constant { |
|
63
|
2
|
|
|
|
|
1087
|
FORMATVERSION => 0, # magic number position (in bytes) |
|
64
|
|
|
|
|
|
|
INTFMT => 4, # INTFMT byte position (in bytes) |
|
65
|
|
|
|
|
|
|
BASEOFFSET => 8, |
|
66
|
|
|
|
|
|
|
MAINIDX => 0, # (in words (units of _intsize bytes)) |
|
67
|
|
|
|
|
|
|
IDIDX => 1, # (in words) |
|
68
|
|
|
|
|
|
|
NEXTID => 2, # (in words) |
|
69
|
|
|
|
|
|
|
STRINGTBL => 3, # (in words) |
|
70
|
|
|
|
|
|
|
DATASTART => 4, # (in words) |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
DBFMT0 => 0, # MMDB format |
|
73
|
|
|
|
|
|
|
DBFMT1 => 1, # MMDC format with utf8 support |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# iterator questions |
|
76
|
|
|
|
|
|
|
IT_NTH =>0, # reposition iterator |
|
77
|
|
|
|
|
|
|
IT_CUR =>1, # what is the current index |
|
78
|
|
|
|
|
|
|
IT_NELEM =>2, # how many elements does it iterate over |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
DATAMODE_NORMAL=>0, |
|
81
|
|
|
|
|
|
|
DATAMODE_SIMPLE=>1, |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
E_READONLY => \'database is read-only', |
|
84
|
|
|
|
|
|
|
E_TWICE => \'can\'t insert the same ID twice', |
|
85
|
|
|
|
|
|
|
E_TRANSACTION => \'there is already an active transaction', |
|
86
|
|
|
|
|
|
|
E_FULL => \'can\'t allocate ID', |
|
87
|
|
|
|
|
|
|
E_DUPLICATE => \'data records cannot be mixed up with subindices', |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
E_OPEN => \'can\'t open file', |
|
90
|
|
|
|
|
|
|
E_READ => \'can\'t read from file', |
|
91
|
|
|
|
|
|
|
E_WRITE => \'can\'t write to file', |
|
92
|
|
|
|
|
|
|
E_CLOSE => \'file could not be closed', |
|
93
|
|
|
|
|
|
|
E_RENAME => \'can\'t rename file', |
|
94
|
|
|
|
|
|
|
E_SEEK => \'can\'t move file pointer', |
|
95
|
|
|
|
|
|
|
E_TRUNCATE => \'can\'t truncate file', |
|
96
|
|
|
|
|
|
|
E_LOCK => \'can\'t (un)lock lockfile', |
|
97
|
|
|
|
|
|
|
E_RANGE => \'attempt move iterator out of its range', |
|
98
|
|
|
|
|
|
|
E_NOT_IMPLEMENTED => \'function not implemented', |
|
99
|
2
|
|
|
2
|
|
12
|
}; |
|
|
2
|
|
|
|
|
14
|
|
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
#sub D { |
|
103
|
|
|
|
|
|
|
# use Data::Dumper; |
|
104
|
|
|
|
|
|
|
# local $Data::Dumper::Useqq=1; |
|
105
|
|
|
|
|
|
|
# warn Dumper @_; |
|
106
|
|
|
|
|
|
|
#} |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub _putdata { |
|
109
|
3343
|
|
|
3343
|
|
8993
|
my ($I, $pos, $fmt, @param)=@_; |
|
110
|
|
|
|
|
|
|
|
|
111
|
3343
|
|
|
|
|
6628
|
my $pstr=pack $fmt, @param; |
|
112
|
3343
|
|
|
|
|
6490
|
my $map=$I->_tmpmap; |
|
113
|
3343
|
100
|
|
|
|
7400
|
if( $pos+length($pstr)>length $$map ) { |
|
114
|
16
|
|
|
|
|
48
|
my $prea=$I->index_prealloc; |
|
115
|
16
|
|
|
|
|
43
|
my $need=$prea*(($pos+length($pstr)+$prea-1)/$prea); |
|
116
|
16
|
|
|
|
|
32
|
eval { |
|
117
|
16
|
|
|
|
|
38
|
my $fh=$I->_tmpfh; |
|
118
|
16
|
50
|
33
|
|
|
1161
|
sysseek $fh, $need, SEEK_SET and |
|
119
|
|
|
|
|
|
|
truncate $fh, $need and |
|
120
|
|
|
|
|
|
|
map_handle $$map, $fh, '+>', 0, $need; |
|
121
|
|
|
|
|
|
|
}; |
|
122
|
16
|
50
|
|
|
|
4028
|
$I->_e(E_OPEN) if $@; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
3343
|
|
|
|
|
7464
|
substr $$map, $pos, length($pstr), $pstr; |
|
125
|
3343
|
|
|
|
|
9511
|
return length($pstr); |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub set_intfmt { |
|
129
|
7
|
|
|
7
|
1
|
18
|
my ($I, $fmt)=@_; |
|
130
|
|
|
|
|
|
|
|
|
131
|
7
|
50
|
|
|
|
19
|
$fmt='N' unless $fmt; |
|
132
|
|
|
|
|
|
|
|
|
133
|
7
|
|
|
|
|
11
|
my %allowed; undef @allowed{qw/L N J Q/}; |
|
|
7
|
|
|
|
|
28
|
|
|
134
|
7
|
50
|
|
|
|
24
|
return unless exists $allowed{$fmt}; |
|
135
|
|
|
|
|
|
|
|
|
136
|
7
|
|
|
|
|
22
|
$I->intfmt=$fmt; |
|
137
|
7
|
|
|
|
|
33
|
$I->_intsize=length pack($fmt, 0); |
|
138
|
|
|
|
|
|
|
|
|
139
|
7
|
100
|
|
|
|
18
|
if( $I->dbformat_in>DBFMT0 ) { |
|
140
|
|
|
|
|
|
|
# new format with utf8 support |
|
141
|
6
|
|
|
|
|
13
|
$I->_stringfmt=$I->intfmt.'/a*C x!'.$I->_intsize; |
|
142
|
|
|
|
|
|
|
} else { |
|
143
|
1
|
|
|
|
|
3
|
$I->_stringfmt=$I->intfmt.'/a* x!'.$I->_intsize; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
7
|
100
|
|
|
|
18
|
if( $I->dbformat_out>DBFMT0 ) { |
|
147
|
|
|
|
|
|
|
# new format with utf8 support |
|
148
|
6
|
|
|
|
|
15
|
$I->_stringfmt_out=$I->intfmt.'/a*C x!'.$I->_intsize; |
|
149
|
|
|
|
|
|
|
} else { |
|
150
|
1
|
|
|
|
|
3
|
$I->_stringfmt_out=$I->intfmt.'/a* x!'.$I->_intsize; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
7
|
|
|
|
|
22
|
return 1; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub new { |
|
157
|
1
|
|
|
1
|
1
|
233
|
my ($parent, @param)=@_; |
|
158
|
1
|
|
|
|
|
2
|
my $I; |
|
159
|
|
|
|
|
|
|
|
|
160
|
1
|
50
|
|
|
|
4
|
if (ref $parent) { |
|
161
|
0
|
|
|
|
|
0
|
$I=bless [@$parent]=>ref($parent); |
|
162
|
0
|
|
|
|
|
0
|
for my $k (qw/_nextid _idmap _tmpfh _tmpname _stringfh _stringmap |
|
163
|
|
|
|
|
|
|
_strpos main_index id_index/) { |
|
164
|
0
|
|
|
|
|
0
|
undef $I->$k; |
|
165
|
|
|
|
|
|
|
} |
|
166
|
0
|
0
|
|
|
|
0
|
if( defined $I->_data ) { |
|
167
|
|
|
|
|
|
|
# parameters: PARENT POS DATAMODE |
|
168
|
0
|
|
|
|
|
0
|
tie %{$I->main_index=+{}}, 'MMapDB::Index', $I, $I->mainidx, 0; |
|
|
0
|
|
|
|
|
0
|
|
|
169
|
0
|
|
|
|
|
0
|
tie %{$I->id_index=+{}}, 'MMapDB::IDIndex', $I, undef, 0; |
|
|
0
|
|
|
|
|
0
|
|
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
} else { |
|
172
|
1
|
|
|
|
|
2
|
$I=bless []=>$parent; |
|
173
|
1
|
|
|
|
|
4
|
$I->set_intfmt('N'); |
|
174
|
1
|
|
|
|
|
3
|
$I->flags=0; |
|
175
|
1
|
|
|
|
|
5
|
$I->dbformat_in=$#dbformats; # use the newest by default |
|
176
|
1
|
|
|
|
|
3
|
$I->dbformat_out=$#dbformats; # use the newest by default |
|
177
|
|
|
|
|
|
|
} |
|
178
|
1
|
|
|
|
|
3
|
$I->stringmap_prealloc=1024*1024*10; # 10MB |
|
179
|
1
|
|
|
|
|
4
|
$I->index_prealloc=1024*1024*10; # 10MB |
|
180
|
|
|
|
|
|
|
|
|
181
|
1
|
50
|
|
|
|
4
|
if( @param==1 ) { |
|
182
|
0
|
|
|
|
|
0
|
$I->filename=$param[0]; |
|
183
|
|
|
|
|
|
|
} else { |
|
184
|
1
|
|
|
|
|
6
|
while( my ($k, $v)=splice @param, 0, 2 ) { |
|
185
|
1
|
50
|
|
|
|
17
|
$I->$k=$v if $k=$I->can($k); |
|
186
|
|
|
|
|
|
|
} |
|
187
|
1
|
50
|
|
|
|
3
|
$I->set_intfmt($I->intfmt) unless $I->intfmt eq 'N'; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
1
|
|
|
|
|
3
|
return $I; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub is_valid { |
|
194
|
0
|
|
|
0
|
1
|
0
|
my ($I)=@_; |
|
195
|
|
|
|
|
|
|
|
|
196
|
0
|
0
|
|
|
|
0
|
return unless $I->_data; |
|
197
|
|
|
|
|
|
|
# the INTFMT field serves 2 ways: |
|
198
|
|
|
|
|
|
|
# 1) it specifies the used integer format |
|
199
|
|
|
|
|
|
|
# 2) it works as VALID flag. commit() write a NULL byte here |
|
200
|
|
|
|
|
|
|
# to mark the old file as invalid. |
|
201
|
|
|
|
|
|
|
# we must reconnect if our cached fmt does not match. |
|
202
|
0
|
|
|
|
|
0
|
return substr( ${$I->_data}, INTFMT, 1 ) eq $I->intfmt; |
|
|
0
|
|
|
|
|
0
|
|
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub start { |
|
206
|
4
|
|
|
4
|
1
|
9
|
my ($I)=@_; |
|
207
|
|
|
|
|
|
|
|
|
208
|
4
|
50
|
|
|
|
13
|
$I->_e(E_TRANSACTION) if defined $I->_tmpfh; |
|
209
|
|
|
|
|
|
|
|
|
210
|
4
|
|
|
|
|
6
|
my $retry=5; |
|
211
|
4
|
50
|
|
|
|
14
|
RETRY: { |
|
212
|
4
|
|
|
|
|
5
|
return unless $retry--; |
|
213
|
2
|
|
|
|
|
5
|
$I->stop if (defined $I->_data and |
|
214
|
4
|
100
|
66
|
|
|
9
|
substr( ${$I->_data}, INTFMT, 1 ) ne $I->intfmt); |
|
215
|
|
|
|
|
|
|
|
|
216
|
4
|
50
|
|
|
|
9
|
unless( $I->_data ) { |
|
217
|
4
|
|
|
|
|
8
|
my ($dummy, $fmt); |
|
218
|
0
|
|
|
|
|
0
|
my $fh; |
|
219
|
4
|
50
|
|
|
|
11
|
if( $I->readonly ) { |
|
220
|
0
|
0
|
|
|
|
0
|
open $fh, '<', $I->filename or return; |
|
221
|
|
|
|
|
|
|
} else { |
|
222
|
4
|
100
|
|
|
|
21
|
open $fh, '+<', $I->filename or return; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Map the main data always read-only. If we are in writable mode |
|
226
|
|
|
|
|
|
|
# map only the header page again writable. |
|
227
|
3
|
|
|
|
|
9
|
eval { |
|
228
|
3
|
|
|
|
|
18
|
map_handle $dummy, $fh, '<'; |
|
229
|
|
|
|
|
|
|
}; |
|
230
|
3
|
|
|
|
|
9821
|
close $fh; |
|
231
|
3
|
50
|
|
|
|
12
|
return if $@; # perhaps throw something here |
|
232
|
3
|
50
|
|
|
|
11
|
return unless length $dummy; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# check magic number |
|
235
|
3
|
50
|
|
|
|
32
|
return unless exists $dbformats{substr($dummy, FORMATVERSION, 4)}; |
|
236
|
3
|
|
|
|
|
27
|
$I->dbformat_out=$I->dbformat_in= |
|
237
|
|
|
|
|
|
|
$dbformats{substr($dummy, FORMATVERSION, 4)}; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# read integer format |
|
240
|
3
|
|
|
|
|
20
|
$fmt=unpack 'x4a', $dummy; |
|
241
|
3
|
50
|
|
|
|
12
|
if( $fmt eq "\0" ) { |
|
242
|
0
|
|
|
|
|
0
|
select undef, undef, undef, 0.1; |
|
243
|
0
|
|
|
|
|
0
|
redo RETRY; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
3
|
50
|
|
|
|
14
|
return unless $I->set_intfmt($fmt); |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# read the byte just after the format character |
|
248
|
3
|
|
|
|
|
15
|
$I->flags=unpack 'x5C', $dummy; |
|
249
|
|
|
|
|
|
|
|
|
250
|
3
|
|
|
|
|
9
|
$I->_data=\$dummy; # now mapped |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# read main index position |
|
253
|
3
|
|
|
|
|
5
|
$I->mainidx=unpack('x'.(BASEOFFSET+MAINIDX*$I->_intsize).$I->intfmt, |
|
254
|
3
|
|
|
|
|
9
|
${$I->_data}); |
|
255
|
3
|
|
|
|
|
8
|
$I->_ididx=unpack('x'.(BASEOFFSET+IDIDX*$I->_intsize).$I->intfmt, |
|
256
|
3
|
|
|
|
|
9
|
${$I->_data}); |
|
257
|
3
|
|
|
|
|
6
|
$I->_stringtbl=unpack('x'.(BASEOFFSET+STRINGTBL*$I->_intsize). |
|
258
|
3
|
|
|
|
|
9
|
$I->intfmt, ${$I->_data}); |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# parameters: PARENT POS DATAMODE |
|
261
|
3
|
|
|
|
|
7
|
tie %{$I->main_index=+{}}, 'MMapDB::Index', $I, $I->mainidx, 0; |
|
|
3
|
|
|
|
|
8
|
|
|
262
|
3
|
|
|
|
|
4
|
tie %{$I->id_index=+{}}, 'MMapDB::IDIndex', $I, undef, 0; |
|
|
3
|
|
|
|
|
8
|
|
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
3
|
|
|
|
|
13937
|
return $I; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub stop { |
|
270
|
4
|
|
|
4
|
1
|
6
|
my ($I)=@_; |
|
271
|
|
|
|
|
|
|
|
|
272
|
4
|
50
|
|
|
|
13
|
$I->_e(E_TRANSACTION) if defined $I->_tmpfh; |
|
273
|
|
|
|
|
|
|
|
|
274
|
4
|
100
|
|
|
|
13
|
return $I unless defined $I->_data; |
|
275
|
|
|
|
|
|
|
|
|
276
|
3
|
|
|
|
|
9
|
for my $k (qw/_data _stringtbl mainidx _ididx/) { |
|
277
|
12
|
|
|
|
|
34
|
undef $I->$k; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
3
|
|
|
|
|
92
|
untie %{$I->main_index}; undef $I->main_index; |
|
|
3
|
|
|
|
|
8
|
|
|
|
3
|
|
|
|
|
11
|
|
|
281
|
3
|
|
|
|
|
8
|
untie %{$I->id_index}; undef $I->id_index; |
|
|
3
|
|
|
|
|
22
|
|
|
|
3
|
|
|
|
|
8
|
|
|
282
|
|
|
|
|
|
|
|
|
283
|
3
|
|
|
|
|
7
|
return $I; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub index_iterator { |
|
287
|
0
|
|
|
0
|
1
|
0
|
my ($I, $pos, $nth)=@_; |
|
288
|
|
|
|
|
|
|
|
|
289
|
0
|
|
|
|
|
0
|
my $data=$I->_data; |
|
290
|
0
|
0
|
0
|
0
|
|
0
|
return sub {} unless $data and defined $pos; |
|
|
0
|
|
|
|
|
0
|
|
|
291
|
0
|
|
|
|
|
0
|
my $fmt=$I->intfmt; |
|
292
|
0
|
|
|
|
|
0
|
my $isz=$I->_intsize; |
|
293
|
0
|
|
|
|
|
0
|
my ($nrecords, $recordlen)=unpack 'x'.$pos.$fmt.'2', $$data; |
|
294
|
0
|
0
|
|
|
|
0
|
die E_RANGE if $nth>$nrecords; |
|
295
|
0
|
|
|
|
|
0
|
$recordlen*=$isz; |
|
296
|
0
|
|
|
|
|
0
|
my ($cur, $end)=($pos+2*$isz+$nth*$recordlen, |
|
297
|
|
|
|
|
|
|
$pos+2*$isz+$nrecords*$recordlen); |
|
298
|
0
|
|
|
|
|
0
|
my $stroff=$I->_stringtbl; |
|
299
|
0
|
|
|
|
|
0
|
my $sfmt=$I->_stringfmt; |
|
300
|
0
|
|
|
|
|
0
|
my $dbfmt=$I->dbformat_in; |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
my $it=MMapDB::Iterator->new |
|
303
|
|
|
|
|
|
|
( sub { |
|
304
|
0
|
0
|
|
0
|
|
0
|
if( @_ ) { |
|
305
|
0
|
|
|
|
|
0
|
for( my $i=0; $i<@_; $i++ ) { |
|
306
|
0
|
0
|
|
|
|
0
|
if( $_[$i]==IT_NTH ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
0
|
my $nth=$_[++$i]; |
|
308
|
0
|
|
|
|
|
0
|
$nth=$pos+2*$isz+$nth*$recordlen; |
|
309
|
0
|
0
|
0
|
|
|
0
|
die E_RANGE unless( $pos+2*$isz<=$nth and $nth<=$end ); |
|
310
|
0
|
|
|
|
|
0
|
$cur=$nth; |
|
311
|
|
|
|
|
|
|
# return in VOID context |
|
312
|
0
|
0
|
|
|
|
0
|
return unless defined wantarray; |
|
313
|
|
|
|
|
|
|
} elsif( $_[$i]==IT_CUR ) { |
|
314
|
0
|
|
|
|
|
0
|
return ($cur-2*$isz-$pos)/$recordlen; |
|
315
|
|
|
|
|
|
|
} elsif( $_[$i]==IT_NELEM ) { |
|
316
|
0
|
|
|
|
|
0
|
return ($end-2*$isz-$pos)/$recordlen; |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
0
|
0
|
|
|
|
0
|
return if $cur>=$end; |
|
321
|
0
|
|
|
|
|
0
|
my ($key, $npos)=unpack 'x'.$cur.$fmt.'2', $$data; |
|
322
|
0
|
|
|
|
|
0
|
my @list=unpack 'x'.($cur+2*$isz).$fmt.$npos, $$data; |
|
323
|
0
|
|
|
|
|
0
|
$cur+=$recordlen; |
|
324
|
0
|
0
|
|
|
|
0
|
if( $dbfmt>DBFMT0 ) { |
|
325
|
0
|
|
|
|
|
0
|
my ($str, $utf8)=unpack('x'.($stroff+$key).$sfmt, $$data); |
|
326
|
0
|
0
|
|
|
|
0
|
Encode::_utf8_on($str) if( $utf8 ); |
|
327
|
0
|
|
|
|
|
0
|
return ($str, @list); |
|
328
|
|
|
|
|
|
|
} else { |
|
329
|
0
|
|
|
|
|
0
|
return (unpack('x'.($stroff+$key).$sfmt, $$data), @list); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
} |
|
332
|
0
|
|
|
|
|
0
|
); |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
0
|
|
|
|
0
|
return wantarray ? ($it, $nrecords) : $it; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub id_index_iterator { |
|
338
|
0
|
|
|
0
|
1
|
0
|
my ($I)=@_; |
|
339
|
|
|
|
|
|
|
|
|
340
|
0
|
|
|
|
|
0
|
my $data=$I->_data; |
|
341
|
0
|
0
|
|
0
|
|
0
|
return sub {} unless $data; |
|
|
0
|
|
|
|
|
0
|
|
|
342
|
0
|
|
|
|
|
0
|
my $pos=$I->_ididx; |
|
343
|
0
|
|
|
|
|
0
|
my ($nrecords)=unpack 'x'.$pos.$I->intfmt, $$data; |
|
344
|
0
|
|
|
|
|
0
|
my $isz=$I->_intsize; |
|
345
|
0
|
|
|
|
|
0
|
my $recordlen=2*$isz; |
|
346
|
0
|
|
|
|
|
0
|
my ($cur, $end)=($pos+$isz, |
|
347
|
|
|
|
|
|
|
$pos+$isz+$nrecords*$recordlen); |
|
348
|
0
|
|
|
|
|
0
|
my $fmt=$I->intfmt.'2'; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
my $it=MMapDB::Iterator->new |
|
351
|
|
|
|
|
|
|
( sub { |
|
352
|
0
|
0
|
|
0
|
|
0
|
if( @_ ) { |
|
353
|
0
|
|
|
|
|
0
|
for( my $i=0; $i<@_; $i++ ) { |
|
354
|
0
|
0
|
|
|
|
0
|
if( $_[$i]==IT_NTH ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
0
|
my $nth=$_[++$i]; |
|
356
|
0
|
|
|
|
|
0
|
$nth=$pos+$isz+$nth*$recordlen; |
|
357
|
0
|
0
|
0
|
|
|
0
|
die E_RANGE unless( $pos+$isz<=$nth and $nth<=$end ); |
|
358
|
0
|
|
|
|
|
0
|
$cur=$nth; |
|
359
|
|
|
|
|
|
|
# return in VOID context |
|
360
|
0
|
0
|
|
|
|
0
|
return unless defined wantarray; |
|
361
|
|
|
|
|
|
|
} elsif( $_[$i]==IT_CUR ) { |
|
362
|
0
|
|
|
|
|
0
|
return ($cur-$isz-$pos)/$recordlen; |
|
363
|
|
|
|
|
|
|
} elsif( $_[$i]==IT_NELEM ) { |
|
364
|
0
|
|
|
|
|
0
|
return ($end-$isz-$pos)/$recordlen; |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
} |
|
368
|
0
|
0
|
|
|
|
0
|
return if $cur>=$end; |
|
369
|
0
|
|
|
|
|
0
|
my @l=unpack 'x'.$cur.$fmt, $$data; |
|
370
|
0
|
|
|
|
|
0
|
$cur+=$recordlen; |
|
371
|
0
|
|
|
|
|
0
|
return @l; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
0
|
|
|
|
|
0
|
); |
|
374
|
|
|
|
|
|
|
|
|
375
|
0
|
0
|
|
|
|
0
|
return wantarray ? ($it, $nrecords) : $it; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub is_datapos { |
|
379
|
0
|
|
|
0
|
1
|
0
|
my ($I, $pos)=@_; |
|
380
|
0
|
|
|
|
|
0
|
return $pos<$I->mainidx; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub datamode : lvalue { |
|
384
|
0
|
|
|
0
|
1
|
0
|
tied(%{$_[0]->main_index})->datamode; |
|
|
0
|
|
|
|
|
0
|
|
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub id_datamode : lvalue { |
|
388
|
0
|
|
|
0
|
1
|
0
|
tied(%{$_[0]->id_index})->datamode; |
|
|
0
|
|
|
|
|
0
|
|
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
0
|
|
|
0
|
|
0
|
sub _e {$_[0]->_rollback; die $_[1]} |
|
|
0
|
|
|
|
|
0
|
|
|
392
|
1520
|
50
|
|
1520
|
|
3104
|
sub _ct {$_[0]->_tmpfh or die E_TRANSACTION} |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub begin { |
|
395
|
3
|
|
|
3
|
1
|
15
|
my ($I, $dbfmt)=@_; |
|
396
|
|
|
|
|
|
|
|
|
397
|
3
|
50
|
|
|
|
9
|
$I->_e(E_TRANSACTION) if defined $I->_tmpfh; |
|
398
|
|
|
|
|
|
|
|
|
399
|
3
|
50
|
|
|
|
7
|
die E_READONLY if $I->readonly; |
|
400
|
|
|
|
|
|
|
|
|
401
|
3
|
50
|
|
|
|
8
|
if( defined $I->lockfile ) { |
|
402
|
|
|
|
|
|
|
# open lockfile |
|
403
|
0
|
0
|
|
|
|
0
|
unless( ref $I->lockfile ) { |
|
404
|
0
|
0
|
|
|
|
0
|
open my $fh, '>', $I->lockfile or die E_OPEN; |
|
405
|
0
|
|
|
|
|
0
|
$I->lockfile=$fh; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
0
|
0
|
|
|
|
0
|
flock $I->lockfile, LOCK_EX or die E_LOCK; |
|
408
|
|
|
|
|
|
|
} |
|
409
|
|
|
|
|
|
|
|
|
410
|
3
|
50
|
|
|
|
14
|
if (defined $dbfmt) { |
|
411
|
0
|
0
|
|
|
|
0
|
$I->dbformat_out=($dbfmt==-1 ? $#dbformats : $dbfmt); |
|
412
|
|
|
|
|
|
|
} |
|
413
|
3
|
|
|
|
|
12
|
$I->set_intfmt($I->intfmt); # adjust string format |
|
414
|
|
|
|
|
|
|
|
|
415
|
3
|
|
|
|
|
9
|
$I->_tmpname=$I->filename.'.'.$$; |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
{ |
|
418
|
|
|
|
|
|
|
# open stringtbl tmpfile |
|
419
|
3
|
50
|
|
|
|
6
|
open my $fh, '+>', $I->_tmpname.'.strings' or die E_OPEN; |
|
|
3
|
|
|
|
|
15
|
|
|
420
|
3
|
|
|
|
|
10
|
$I->_stringfh=$fh; |
|
421
|
3
|
|
|
|
|
8
|
$I->_stringmap=\my $strings; |
|
422
|
3
|
|
|
|
|
5
|
eval { |
|
423
|
3
|
50
|
33
|
|
|
9
|
sysseek $fh, $I->stringmap_prealloc, SEEK_SET and |
|
424
|
|
|
|
|
|
|
truncate $fh, $I->stringmap_prealloc and |
|
425
|
|
|
|
|
|
|
map_handle $strings, $fh, '+>', 0, $I->stringmap_prealloc; |
|
426
|
|
|
|
|
|
|
}; |
|
427
|
3
|
50
|
|
|
|
623
|
die E_OPEN if $@; |
|
428
|
3
|
|
|
|
|
10
|
$I->_stringmap_end=0; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
{ |
|
432
|
|
|
|
|
|
|
# open tmpfile |
|
433
|
3
|
50
|
|
|
|
5
|
open my $fh, '+>', $I->_tmpname or die E_OPEN; |
|
|
3
|
|
|
|
|
14
|
|
|
434
|
3
|
|
|
|
|
8
|
$I->_tmpfh=$fh; # this starts the transaction |
|
435
|
3
|
|
|
|
|
3
|
$I->_tmpmap=\do{my $map=''}; |
|
|
3
|
|
|
|
|
10
|
|
|
436
|
|
|
|
|
|
|
|
|
437
|
3
|
|
|
|
|
15
|
$I->_putdata(0, 'a4aC', $dbformats[$I->dbformat_out], $I->intfmt, |
|
438
|
|
|
|
|
|
|
$I->flags & 0xff); |
|
439
|
3
|
|
|
|
|
10
|
$I->_index_end=BASEOFFSET+DATASTART*$I->_intsize; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# and copy every *valid* entry from the old file |
|
443
|
|
|
|
|
|
|
# create _idmap on the way |
|
444
|
3
|
|
|
|
|
8
|
$I->_idmap={}; |
|
445
|
3
|
|
|
|
|
9
|
$I->_strpos=[]; |
|
446
|
3
|
|
|
|
|
11
|
for( my $it=$I->iterator; my ($pos)=$it->(); ) { |
|
447
|
258
|
|
|
|
|
1508
|
$I->insert($I->data_record($pos)); |
|
448
|
|
|
|
|
|
|
} |
|
449
|
3
|
100
|
|
|
|
6
|
if( $I->_data ) { |
|
450
|
2
|
|
|
|
|
5
|
$I->_nextid=unpack('x'.(BASEOFFSET+NEXTID*$I->_intsize).$I->intfmt, |
|
451
|
2
|
|
|
|
|
5
|
${$I->_data}); |
|
452
|
|
|
|
|
|
|
} else { |
|
453
|
1
|
|
|
|
|
2
|
$I->_nextid=1; |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
|
|
456
|
3
|
|
|
|
|
31
|
return $I; |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# The interator() below hops over the mmapped area. This one works on the file. |
|
460
|
|
|
|
|
|
|
# It can be used only within a begin/commit cycle. |
|
461
|
|
|
|
|
|
|
sub _fiterator { |
|
462
|
3
|
|
|
3
|
|
25
|
my ($I, $end)=@_; |
|
463
|
|
|
|
|
|
|
|
|
464
|
3
|
|
|
|
|
11
|
my $map=$I->_tmpmap; |
|
465
|
3
|
|
|
|
|
11
|
my $pos=BASEOFFSET+$I->_intsize*DATASTART; |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
return sub { |
|
468
|
1261
|
100
|
|
|
|
2386
|
LOOP: { |
|
469
|
1261
|
|
|
1261
|
|
1436
|
return if $pos>=$end; |
|
470
|
1258
|
|
|
|
|
1396
|
my $elpos=$pos; |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# valid id nkeys key1...keyn sort data |
|
473
|
|
|
|
|
|
|
# read (valid, id, nkeys) |
|
474
|
1258
|
|
|
|
|
2899
|
my ($valid, $id, $nkeys)=unpack 'x'.$pos.$I->intfmt.'3', $$map; |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# move iterator position |
|
477
|
|
|
|
|
|
|
# 5: valid, id, nkeys ... sort, data |
|
478
|
1258
|
|
|
|
|
2380
|
$pos+=$I->_intsize*(5+$nkeys); |
|
479
|
1258
|
50
|
|
|
|
2511
|
redo LOOP unless ($valid); |
|
480
|
|
|
|
|
|
|
|
|
481
|
1258
|
|
|
|
|
2249
|
my @l=unpack 'x'.($elpos+3*$I->_intsize).$I->intfmt.($nkeys+2), $$map; |
|
482
|
1258
|
|
|
|
|
1956
|
my $data=pop @l; |
|
483
|
1258
|
|
|
|
|
1512
|
my $sort=pop @l; |
|
484
|
|
|
|
|
|
|
|
|
485
|
1258
|
|
|
|
|
5088
|
return ([\@l, $sort, $data, $id], $elpos); |
|
486
|
|
|
|
|
|
|
} |
|
487
|
3
|
|
|
|
|
31
|
}; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub _really_write_index { |
|
491
|
28
|
|
|
28
|
|
49
|
my ($I, $map, $level)=@_; |
|
492
|
|
|
|
|
|
|
|
|
493
|
28
|
|
|
|
|
36
|
my $recordlen=1; # in ints: (1): for subindexes there is one |
|
494
|
|
|
|
|
|
|
# position to store |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# find the max. number of positions we have to store |
|
497
|
28
|
|
|
|
|
157
|
foreach my $v (values %$map) { |
|
498
|
523
|
100
|
|
|
|
1000
|
if( ref($v) eq 'ARRAY' ) { |
|
499
|
|
|
|
|
|
|
# list of data records |
|
500
|
498
|
100
|
|
|
|
1089
|
$recordlen=@$v if @$v>$recordlen; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
# else: recordlen is initialized with 1. So for subindexes there is |
|
503
|
|
|
|
|
|
|
# nothing to do |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
# each record comes with a header of 2 integers, the key position in the |
|
506
|
|
|
|
|
|
|
# string table and the actual position count of the record. So we have to |
|
507
|
|
|
|
|
|
|
# add 2 to $recordlen. |
|
508
|
28
|
|
|
|
|
53
|
$recordlen+=2; |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# the index itself has a 2 integer header, the recordlen and the number |
|
511
|
|
|
|
|
|
|
# of index records that belong to the index. |
|
512
|
28
|
|
|
|
|
91
|
my $indexsize=(2+$recordlen*keys(%$map))*$I->_intsize; # in bytes |
|
513
|
|
|
|
|
|
|
|
|
514
|
28
|
|
|
|
|
61
|
my $pos=$I->_index_end; |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
# make room |
|
517
|
28
|
|
|
|
|
58
|
$I->_index_end=$pos+$indexsize; |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# and write subindices after this index |
|
520
|
28
|
|
|
|
|
51
|
my $strings=$I->_stringmap; |
|
521
|
28
|
|
|
|
|
60
|
my $sfmt=$I->_stringfmt_out; |
|
522
|
28
|
|
|
|
|
50
|
my $dbfmt=$I->dbformat_out; |
|
523
|
28
|
|
|
|
|
88
|
foreach my $v (values %$map) { |
|
524
|
523
|
100
|
|
|
|
1153
|
if( ref($v) eq 'HASH' ) { |
|
525
|
|
|
|
|
|
|
# convert the subindex into a position list |
|
526
|
25
|
|
|
|
|
85
|
$v=[$I->_really_write_index($v, $level+1)]; |
|
527
|
|
|
|
|
|
|
} else { |
|
528
|
|
|
|
|
|
|
# here we already have a position list but it still contains |
|
529
|
|
|
|
|
|
|
# sorting ids. |
|
530
|
1258
|
|
|
|
|
12234
|
@$v=map { |
|
531
|
965
|
|
|
|
|
1525
|
$_->[1]; |
|
532
|
|
|
|
|
|
|
} sort { |
|
533
|
|
|
|
|
|
|
$a->[0] cmp $b->[0]; |
|
534
|
|
|
|
|
|
|
} map { |
|
535
|
|
|
|
|
|
|
# fetch sort string from string table |
|
536
|
498
|
50
|
|
|
|
764
|
if( $dbfmt>DBFMT0 ) { |
|
|
1258
|
|
|
|
|
2032
|
|
|
537
|
1258
|
|
|
|
|
4878
|
my ($str, $utf8)=unpack('x'.$_->[0].$sfmt, $$strings); |
|
538
|
1258
|
50
|
|
|
|
2659
|
Encode::_utf8_on($str) if $utf8; |
|
539
|
1258
|
|
|
|
|
3983
|
[$str, $_->[1]]; |
|
540
|
|
|
|
|
|
|
} else { |
|
541
|
0
|
|
|
|
|
0
|
[unpack('x'.$_->[0].$sfmt, $$strings), $_->[1]]; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
|
|
|
|
|
|
} @$v; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
} |
|
546
|
|
|
|
|
|
|
|
|
547
|
28
|
|
|
|
|
86
|
my $fmt=$I->intfmt; |
|
548
|
28
|
|
|
|
|
38
|
my $written=$pos; |
|
549
|
28
|
|
|
|
|
126
|
$written+=$I->_putdata($written, $fmt.'2', 0+keys(%$map), $recordlen); |
|
550
|
|
|
|
|
|
|
|
|
551
|
28
|
|
|
|
|
57
|
$fmt.=$recordlen; |
|
552
|
|
|
|
|
|
|
# write the records |
|
553
|
28
|
|
|
|
|
146
|
foreach my $key (map { |
|
|
523
|
|
|
|
|
767
|
|
|
554
|
1683
|
|
|
|
|
2108
|
$_->[0] |
|
555
|
|
|
|
|
|
|
} sort { |
|
556
|
|
|
|
|
|
|
$a->[1] cmp $b->[1]; |
|
557
|
|
|
|
|
|
|
} map { |
|
558
|
523
|
50
|
|
|
|
873
|
if( $dbfmt>DBFMT0 ) { |
|
559
|
523
|
|
|
|
|
1538
|
my ($str, $utf8)=unpack('x'.$_.$sfmt, $$strings); |
|
560
|
523
|
50
|
|
|
|
1062
|
Encode::_utf8_on($str) if $utf8; |
|
561
|
523
|
|
|
|
|
1262
|
[$_, $str]; |
|
562
|
|
|
|
|
|
|
} else { |
|
563
|
0
|
|
|
|
|
0
|
[$_, unpack('x'.$_.$sfmt, $$strings)]; |
|
564
|
|
|
|
|
|
|
} |
|
565
|
|
|
|
|
|
|
} keys %$map) { |
|
566
|
523
|
|
|
|
|
851
|
my $v=$map->{$key}; |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
#D($key, $v); |
|
569
|
|
|
|
|
|
|
#warn "$prefix> idx rec: ".unpack('H*', pack($fmt, $key, 0+@$v, @$v))."\n"; |
|
570
|
|
|
|
|
|
|
|
|
571
|
523
|
|
|
|
|
1254
|
$written+=$I->_putdata($written, $fmt, $key, 0+@$v, @$v); |
|
572
|
|
|
|
|
|
|
} |
|
573
|
|
|
|
|
|
|
|
|
574
|
28
|
|
|
|
|
521
|
return $pos; |
|
575
|
|
|
|
|
|
|
} |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
sub _write_index { |
|
578
|
3
|
|
|
3
|
|
8
|
my ($I)=@_; |
|
579
|
|
|
|
|
|
|
|
|
580
|
3
|
|
|
|
|
5
|
my %map; |
|
581
|
3
|
|
|
|
|
10
|
for( my $it=$I->_fiterator($I->_index_end); my ($el, $pos)=$it->(); ) { |
|
582
|
1258
|
|
|
|
|
9831
|
my $m=\%map; |
|
583
|
1258
|
|
|
|
|
1560
|
my @k=@{$el->[0]}; |
|
|
1258
|
|
|
|
|
4405
|
|
|
584
|
1258
|
|
66
|
|
|
5144
|
while(@k>1 and ref($m) eq 'HASH') { |
|
585
|
1000
|
|
|
|
|
1363
|
my $k=shift @k; |
|
586
|
1000
|
100
|
|
|
|
2231
|
$m->{$k}={} unless exists $m->{$k}; |
|
587
|
1000
|
|
|
|
|
2805
|
$m=$m->{$k}; |
|
588
|
|
|
|
|
|
|
} |
|
589
|
1258
|
50
|
|
|
|
3057
|
$I->_e(E_DUPLICATE) unless ref($m) eq 'HASH'; |
|
590
|
1258
|
100
|
|
|
|
3993
|
$m->{$k[0]}=[] unless defined $m->{$k[0]}; |
|
591
|
1258
|
50
|
|
|
|
12073
|
$I->_e(E_DUPLICATE) unless ref($m->{$k[0]}) eq 'ARRAY'; |
|
592
|
|
|
|
|
|
|
# Actually we want to save only positions but they must be ordered. |
|
593
|
|
|
|
|
|
|
# So either keep the order field together with the position here to |
|
594
|
|
|
|
|
|
|
# sort it later or do sort of ordered insert here. |
|
595
|
|
|
|
|
|
|
# The former is simpler. So it's it. |
|
596
|
1258
|
|
|
|
|
1395
|
push @{$m->{$k[0]}}, [$el->[1], $pos]; |
|
|
1258
|
|
|
|
|
6684
|
|
|
597
|
|
|
|
|
|
|
} |
|
598
|
|
|
|
|
|
|
|
|
599
|
3
|
|
|
|
|
19
|
return $I->_really_write_index(\%map, 0); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
sub _write_id_index { |
|
603
|
3
|
|
|
3
|
|
6
|
my ($I)=@_; |
|
604
|
|
|
|
|
|
|
|
|
605
|
3
|
|
|
|
|
11
|
my $map=$I->_idmap; |
|
606
|
3
|
|
|
|
|
8
|
my $fmt=$I->intfmt; |
|
607
|
|
|
|
|
|
|
|
|
608
|
3
|
|
|
|
|
8
|
my $pos=$I->_index_end; |
|
609
|
3
|
|
|
|
|
4
|
my $written=$pos; |
|
610
|
3
|
|
|
|
|
11
|
$written+=$I->_putdata($written, $fmt, 0+keys(%$map)); |
|
611
|
|
|
|
|
|
|
|
|
612
|
3
|
|
|
|
|
8
|
$fmt.='2'; |
|
613
|
|
|
|
|
|
|
# write the records |
|
614
|
3
|
|
|
|
|
452
|
foreach my $key (sort {$a <=> $b} keys %$map) { |
|
|
10463
|
|
|
|
|
9376
|
|
|
615
|
1258
|
|
|
|
|
2132
|
my $v=$map->{$key}; |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
#warn "id> idx rec: ".unpack('H*', pack($fmt, $key, $v))."\n"; |
|
618
|
|
|
|
|
|
|
|
|
619
|
1258
|
|
|
|
|
2266
|
$written+=$I->_putdata($written, $fmt, $key, $v); |
|
620
|
|
|
|
|
|
|
} |
|
621
|
3
|
|
|
|
|
147
|
$I->_index_end=$written; |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
#warn sprintf "id> index written @ %#x\n", $pos; |
|
624
|
|
|
|
|
|
|
|
|
625
|
3
|
|
|
|
|
8
|
return $pos; |
|
626
|
|
|
|
|
|
|
} |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
sub invalidate { |
|
629
|
3
|
|
|
3
|
1
|
5
|
my ($I)=@_; |
|
630
|
3
|
50
|
|
|
|
12
|
$I->_e(E_READONLY) if $I->readonly; |
|
631
|
3
|
100
|
|
|
|
8
|
return unless defined $I->_data; |
|
632
|
2
|
|
|
|
|
4
|
protect ${$I->_data}, '+<'; |
|
|
2
|
|
|
|
|
6
|
|
|
633
|
2
|
|
|
|
|
4
|
substr( ${$I->_data}, INTFMT, 1, "\0" ); |
|
|
2
|
|
|
|
|
7
|
|
|
634
|
2
|
|
|
|
|
5
|
protect ${$I->_data}, '<'; |
|
|
2
|
|
|
|
|
5
|
|
|
635
|
|
|
|
|
|
|
} |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
sub commit { |
|
638
|
3
|
|
|
3
|
1
|
10
|
my ($I, $dont_invalidate)=@_; |
|
639
|
|
|
|
|
|
|
|
|
640
|
3
|
|
|
|
|
8
|
$I->_ct; |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# write NEXTID |
|
643
|
3
|
|
|
|
|
12
|
$I->_putdata(BASEOFFSET+NEXTID*$I->_intsize, $I->intfmt, $I->_nextid); |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
# write MAINIDX and IDIDX |
|
646
|
3
|
|
|
|
|
13
|
my $mainidx=$I->_write_index; |
|
647
|
3
|
|
|
|
|
29
|
my $ididx=$I->_write_id_index; |
|
648
|
|
|
|
|
|
|
|
|
649
|
3
|
|
|
|
|
38
|
$I->_putdata(BASEOFFSET+MAINIDX*$I->_intsize, $I->intfmt, $mainidx); |
|
650
|
3
|
|
|
|
|
8
|
$I->_putdata(BASEOFFSET+IDIDX*$I->_intsize, $I->intfmt, $ididx); |
|
651
|
3
|
|
|
|
|
10
|
$I->_putdata(BASEOFFSET+STRINGTBL*$I->_intsize, $I->intfmt, $I->_index_end); |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
# now copy the string table |
|
654
|
3
|
|
|
|
|
8
|
my $fh=$I->_tmpfh; |
|
655
|
3
|
|
|
|
|
10
|
my $strings=$I->_stringmap; |
|
656
|
3
|
|
|
|
|
9
|
my $map=$I->_tmpmap; |
|
657
|
3
|
|
|
|
|
10
|
my $need=$I->_index_end+$I->_stringmap_end; |
|
658
|
3
|
100
|
|
|
|
12
|
if( $need>length $$map ) { |
|
659
|
1
|
|
|
|
|
4
|
eval { |
|
660
|
1
|
50
|
33
|
|
|
62
|
sysseek $fh, $need, SEEK_SET and |
|
661
|
|
|
|
|
|
|
truncate $fh, $need and |
|
662
|
|
|
|
|
|
|
map_handle $$map, $fh, '+>', 0, $need; |
|
663
|
|
|
|
|
|
|
}; |
|
664
|
1
|
50
|
|
|
|
198
|
$I->_e(E_OPEN) if $@; |
|
665
|
|
|
|
|
|
|
} |
|
666
|
3
|
|
|
|
|
9
|
substr($$map, $I->_index_end, $I->_stringmap_end, |
|
667
|
|
|
|
|
|
|
substr($$strings, 0, $I->_stringmap_end)); |
|
668
|
3
|
|
|
|
|
218
|
truncate $fh, $need; |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
#warn "mainidx=$mainidx, ididx=$ididx, strtbl=$strtbl\n"; |
|
671
|
|
|
|
|
|
|
|
|
672
|
3
|
|
|
|
|
11
|
undef $I->_idmap; |
|
673
|
3
|
|
|
|
|
384
|
undef $I->_strpos; |
|
674
|
3
|
|
|
|
|
211
|
undef $I->_stringmap; |
|
675
|
|
|
|
|
|
|
|
|
676
|
3
|
50
|
|
|
|
11
|
close $I->_stringfh or $I->_e(E_CLOSE); |
|
677
|
3
|
|
|
|
|
8
|
undef $I->_stringfh; |
|
678
|
3
|
|
|
|
|
19
|
unlink $I->_tmpname.'.strings'; |
|
679
|
|
|
|
|
|
|
|
|
680
|
3
|
|
|
|
|
11
|
undef $I->_tmpmap; |
|
681
|
3
|
50
|
|
|
|
20
|
close $fh or $I->_e(E_CLOSE); |
|
682
|
3
|
|
|
|
|
9
|
undef $I->_tmpfh; |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
# rename is (at least on Linux) an atomic operation |
|
685
|
3
|
50
|
|
|
|
10
|
rename $I->_tmpname, $I->filename or $I->_e(E_RENAME); |
|
686
|
|
|
|
|
|
|
|
|
687
|
3
|
50
|
|
|
|
18
|
$I->invalidate unless $dont_invalidate; |
|
688
|
|
|
|
|
|
|
|
|
689
|
3
|
50
|
|
|
|
9
|
if( $I->lockfile ) { |
|
690
|
0
|
0
|
|
|
|
0
|
flock $I->lockfile, LOCK_UN or die E_LOCK; |
|
691
|
|
|
|
|
|
|
} |
|
692
|
|
|
|
|
|
|
|
|
693
|
3
|
|
|
|
|
14
|
$I->start; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub _rollback { |
|
697
|
0
|
|
|
0
|
|
0
|
my ($I)=@_; |
|
698
|
|
|
|
|
|
|
|
|
699
|
0
|
|
|
|
|
0
|
close $I->_tmpfh; |
|
700
|
0
|
|
|
|
|
0
|
undef $I->_tmpfh; |
|
701
|
0
|
|
|
|
|
0
|
unlink $I->_tmpname; |
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
|
|
|
|
0
|
close $I->_stringfh; |
|
704
|
0
|
|
|
|
|
0
|
undef $I->_stringfh; |
|
705
|
0
|
|
|
|
|
0
|
unlink $I->_tmpname.'.strings'; |
|
706
|
|
|
|
|
|
|
|
|
707
|
0
|
|
|
|
|
0
|
undef $I->_stringmap; |
|
708
|
0
|
|
|
|
|
0
|
undef $I->_strpos; |
|
709
|
0
|
|
|
|
|
0
|
undef $I->_idmap; |
|
710
|
|
|
|
|
|
|
|
|
711
|
0
|
|
|
|
|
0
|
$I->_stringfmt_out=$I->_stringfmt; |
|
712
|
0
|
|
|
|
|
0
|
$I->dbformat_out=$I->dbformat_in; |
|
713
|
|
|
|
|
|
|
|
|
714
|
0
|
0
|
|
|
|
0
|
if( $I->lockfile ) { |
|
715
|
0
|
0
|
|
|
|
0
|
flock $I->lockfile, LOCK_UN or die E_LOCK; |
|
716
|
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
} |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub rollback { |
|
720
|
0
|
|
|
0
|
1
|
0
|
my ($I)=@_; |
|
721
|
|
|
|
|
|
|
|
|
722
|
0
|
0
|
|
|
|
0
|
$I->_e(E_TRANSACTION) unless defined $I->_tmpfh; |
|
723
|
0
|
|
|
|
|
0
|
$I->_rollback; |
|
724
|
|
|
|
|
|
|
} |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
sub DESTROY { |
|
727
|
1
|
|
|
1
|
|
573
|
my ($I)=@_; |
|
728
|
|
|
|
|
|
|
|
|
729
|
1
|
50
|
|
|
|
3
|
$I->_rollback if defined $I->_tmpfh; |
|
730
|
1
|
|
|
|
|
5
|
$I->stop; |
|
731
|
|
|
|
|
|
|
} |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
sub backup { |
|
734
|
0
|
|
|
0
|
1
|
0
|
my ($I, $fn)=@_; |
|
735
|
|
|
|
|
|
|
|
|
736
|
0
|
|
|
|
|
0
|
$I->start; |
|
737
|
|
|
|
|
|
|
|
|
738
|
0
|
0
|
|
|
|
0
|
my $backup=$I->new(filename=>(defined $fn ? $fn : $I->filename.'.BACKUP')); |
|
739
|
|
|
|
|
|
|
|
|
740
|
0
|
|
|
|
|
0
|
$backup->begin; |
|
741
|
0
|
|
|
|
|
0
|
$backup->commit(1); |
|
742
|
|
|
|
|
|
|
} |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub restore { |
|
745
|
0
|
|
|
0
|
1
|
0
|
my ($I, $fn)=@_; |
|
746
|
|
|
|
|
|
|
|
|
747
|
0
|
|
|
|
|
0
|
$I->start; |
|
748
|
0
|
0
|
|
|
|
0
|
$fn=$I->filename.'.BACKUP' unless defined $fn; |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# rename is (at least on Linux) an atomic operation |
|
751
|
0
|
0
|
|
|
|
0
|
rename $fn, $I->filename or die E_RENAME; |
|
752
|
0
|
|
|
|
|
0
|
$I->invalidate; |
|
753
|
|
|
|
|
|
|
|
|
754
|
0
|
|
|
|
|
0
|
return $I->start; |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# Returns the position of $key in the stringtable |
|
758
|
|
|
|
|
|
|
# If $key is not found it is inserted. @{$I->_strpos} is kept ordered. |
|
759
|
|
|
|
|
|
|
# So, we can do a binary search. |
|
760
|
|
|
|
|
|
|
# This implements something very similar to a HASH. So, why not use a HASH? |
|
761
|
|
|
|
|
|
|
# A HASH is kept completely in core and the memory is not returned to the |
|
762
|
|
|
|
|
|
|
# operating system when finished. The number of strings in the database |
|
763
|
|
|
|
|
|
|
# can become quite large. So if a long running process updates the database |
|
764
|
|
|
|
|
|
|
# only once it will consume much memory for nothing. To avoid this we map |
|
765
|
|
|
|
|
|
|
# the string table currently under construction in a separate file that |
|
766
|
|
|
|
|
|
|
# is mmapped into the address space of this process and keep here only |
|
767
|
|
|
|
|
|
|
# a list of pointer into this area. When the transaction is committed the |
|
768
|
|
|
|
|
|
|
# memory is returned to the OS. But on the other hand we need fast access. |
|
769
|
|
|
|
|
|
|
# This is achieved by the binary search. |
|
770
|
|
|
|
|
|
|
sub _string2pos { |
|
771
|
5548
|
|
|
5548
|
|
8186
|
my ($I, $key)=@_; |
|
772
|
|
|
|
|
|
|
|
|
773
|
5548
|
|
|
|
|
10144
|
my $fmt=$I->_stringfmt_out; |
|
774
|
5548
|
|
|
|
|
9681
|
my $dbfmt=$I->dbformat_out; |
|
775
|
|
|
|
|
|
|
|
|
776
|
5548
|
50
|
|
|
|
11360
|
Encode::_utf8_off($key) if $dbfmt==DBFMT0; |
|
777
|
|
|
|
|
|
|
|
|
778
|
5548
|
|
|
|
|
9687
|
my $strings=$I->_stringmap; |
|
779
|
5548
|
|
|
|
|
9670
|
my $poslist=$I->_strpos; |
|
780
|
|
|
|
|
|
|
|
|
781
|
5548
|
|
|
|
|
10641
|
my ($low, $high)=(0, 0+@$poslist); |
|
782
|
|
|
|
|
|
|
#warn "_string2pos($key): low=$low, high=$high\n"; |
|
783
|
|
|
|
|
|
|
|
|
784
|
5548
|
|
|
|
|
5975
|
my ($cur, $rel, $curstr); |
|
785
|
5548
|
|
|
|
|
10906
|
while( $low<$high ) { |
|
786
|
48535
|
|
|
|
|
54001
|
$cur=($high+$low)/2; # "use integer" is active, see above |
|
787
|
48535
|
50
|
|
|
|
74672
|
if( $dbfmt>DBFMT0 ) { |
|
788
|
48535
|
|
|
|
|
175999
|
($curstr, my $utf8)=unpack 'x'.$poslist->[$cur].$fmt, $$strings; |
|
789
|
48535
|
50
|
|
|
|
113766
|
Encode::_utf8_on($curstr) if $utf8; |
|
790
|
|
|
|
|
|
|
} else { |
|
791
|
0
|
|
|
|
|
0
|
$curstr=unpack 'x'.$poslist->[$cur].$fmt, $$strings; |
|
792
|
|
|
|
|
|
|
} |
|
793
|
|
|
|
|
|
|
#warn " --> looking at $curstr: low=$low, high=$high, cur=$cur\n"; |
|
794
|
48535
|
|
|
|
|
66884
|
$rel=($curstr cmp $key); |
|
795
|
48535
|
100
|
|
|
|
81145
|
if( $rel<0 ) { |
|
|
|
100
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
#warn " --> moving low: $low ==> ".($cur+1)."\n"; |
|
797
|
29982
|
|
|
|
|
59137
|
$low=$cur+1; |
|
798
|
|
|
|
|
|
|
} elsif( $rel>0 ) { |
|
799
|
|
|
|
|
|
|
#warn " --> moving high: $high ==> ".($cur)."\n"; |
|
800
|
|
|
|
|
|
|
# don't try to optimize here: $high=$cur-1 will not work in border cases |
|
801
|
16064
|
|
|
|
|
31562
|
$high=$cur; |
|
802
|
|
|
|
|
|
|
} else { |
|
803
|
|
|
|
|
|
|
#warn " --> BINGO\n"; |
|
804
|
2489
|
|
|
|
|
8198
|
return $poslist->[$cur]; |
|
805
|
|
|
|
|
|
|
} |
|
806
|
|
|
|
|
|
|
} |
|
807
|
|
|
|
|
|
|
#warn " --> NOT FOUND\n"; |
|
808
|
3059
|
|
|
|
|
6818
|
my $pos=$I->_stringmap_end; |
|
809
|
3059
|
|
|
|
|
6529
|
splice @$poslist, $low, 0, $pos; |
|
810
|
|
|
|
|
|
|
#warn " --> inserting $pos into poslist at $low ==> @$poslist\n"; |
|
811
|
|
|
|
|
|
|
|
|
812
|
3059
|
|
|
|
|
3251
|
my $newstr; |
|
813
|
3059
|
50
|
|
|
|
6194
|
if( $dbfmt>DBFMT0 ) { |
|
814
|
3059
|
50
|
|
|
|
6924
|
if( Encode::is_utf8($key) ) { |
|
815
|
0
|
|
|
|
|
0
|
$newstr=pack($fmt, Encode::encode_utf8($key), 1); |
|
816
|
|
|
|
|
|
|
} else { |
|
817
|
3059
|
|
|
|
|
9760
|
$newstr=pack($fmt, $key, 0); |
|
818
|
|
|
|
|
|
|
} |
|
819
|
|
|
|
|
|
|
} else { |
|
820
|
0
|
|
|
|
|
0
|
$newstr=pack($fmt, $key); |
|
821
|
|
|
|
|
|
|
} |
|
822
|
|
|
|
|
|
|
|
|
823
|
3059
|
100
|
|
|
|
7144
|
if( $pos+length($newstr)>length $$strings ) { |
|
824
|
|
|
|
|
|
|
# remap |
|
825
|
10
|
|
|
|
|
31
|
my $prea=$I->stringmap_prealloc; |
|
826
|
10
|
|
|
|
|
24
|
my $need=$prea*(($pos+length($newstr)+$prea-1)/$prea); |
|
827
|
10
|
|
|
|
|
20
|
eval { |
|
828
|
10
|
|
|
|
|
28
|
my $fh=$I->_stringfh; |
|
829
|
10
|
50
|
33
|
|
|
1334
|
sysseek $fh, $need, SEEK_SET and |
|
830
|
|
|
|
|
|
|
truncate $fh, $need and |
|
831
|
|
|
|
|
|
|
map_handle $$strings, $fh, '+>', 0, $need; |
|
832
|
|
|
|
|
|
|
}; |
|
833
|
10
|
50
|
|
|
|
2437
|
$I->_e(E_OPEN) if $@; |
|
834
|
|
|
|
|
|
|
} |
|
835
|
|
|
|
|
|
|
|
|
836
|
3059
|
|
|
|
|
7132
|
substr $$strings, $pos, length($newstr), $newstr; |
|
837
|
3059
|
|
|
|
|
6470
|
$I->_stringmap_end=$pos+length($newstr); |
|
838
|
|
|
|
|
|
|
|
|
839
|
3059
|
|
|
|
|
11126
|
return $pos; |
|
840
|
|
|
|
|
|
|
} |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
sub insert { |
|
843
|
1516
|
|
|
1516
|
1
|
23301
|
my ($I, $rec)=@_; |
|
844
|
|
|
|
|
|
|
#my ($I, $key, $sort, $data, $id)=@_; |
|
845
|
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
#use Data::Dumper; $Data::Dumper::Useqq=1; warn Dumper $rec; |
|
847
|
|
|
|
|
|
|
|
|
848
|
1516
|
|
|
|
|
2903
|
$I->_ct; |
|
849
|
|
|
|
|
|
|
|
|
850
|
1516
|
50
|
|
|
|
3976
|
$rec->[0]=[$rec->[0]] unless ref $rec->[0]; |
|
851
|
1516
|
|
|
|
|
1945
|
for my $v (@{$rec}[1,2]) { |
|
|
1516
|
|
|
|
|
3178
|
|
|
852
|
3032
|
50
|
|
|
|
6867
|
$v='' unless defined $v; |
|
853
|
|
|
|
|
|
|
} |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
# create new ID if necessary |
|
856
|
1516
|
|
|
|
|
2733
|
my $id=$rec->[3]; |
|
857
|
1516
|
|
|
|
|
2834
|
my $idmap=$I->_idmap; |
|
858
|
1516
|
100
|
|
|
|
3048
|
if( defined $id ) { |
|
859
|
258
|
50
|
|
|
|
664
|
$I->_e(E_TWICE) if exists $idmap->{$id}; |
|
860
|
|
|
|
|
|
|
} else { |
|
861
|
1258
|
|
|
|
|
2089
|
$id=$I->_nextid; |
|
862
|
1258
|
|
|
|
|
3286
|
undef $idmap->{$id}; # allocate it |
|
863
|
|
|
|
|
|
|
|
|
864
|
2
|
|
|
2
|
|
31
|
my $mask=do{no integer; unpack( $I->intfmt, pack $I->intfmt, -1 )>>1}; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
20
|
|
|
|
1258
|
|
|
|
|
1362
|
|
|
|
1258
|
|
|
|
|
2338
|
|
|
865
|
1258
|
|
|
|
|
1877
|
my $nid=($id+1)&$mask; |
|
866
|
1258
|
50
|
|
|
|
2550
|
$nid=1 if $nid==0; |
|
867
|
1258
|
|
|
|
|
3339
|
while(exists $idmap->{$nid}) { |
|
868
|
0
|
0
|
|
|
|
0
|
$nid=($nid+1)&$mask; $nid=1 if $nid==0; |
|
|
0
|
|
|
|
|
0
|
|
|
869
|
0
|
0
|
|
|
|
0
|
$I->_e(E_FULL) if $nid==$id; |
|
870
|
|
|
|
|
|
|
} |
|
871
|
1258
|
|
|
|
|
2443
|
$I->_nextid=$nid; |
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
|
|
874
|
1516
|
|
|
|
|
3012
|
my $pos=$I->_index_end; |
|
875
|
1516
|
|
|
|
|
5986
|
$I->_index_end+=$I->_putdata($pos, $I->intfmt.'*', 1, $id, 0+@{$rec->[0]}, |
|
|
5548
|
|
|
|
|
10931
|
|
|
876
|
1516
|
|
|
|
|
2819
|
map {$I->_string2pos($_)} |
|
877
|
1516
|
|
|
|
|
2669
|
@{$rec->[0]}, @{$rec}[1,2]); |
|
|
1516
|
|
|
|
|
2610
|
|
|
878
|
|
|
|
|
|
|
|
|
879
|
1516
|
|
|
|
|
3942
|
$idmap->{$id}=$pos; |
|
880
|
|
|
|
|
|
|
|
|
881
|
1516
|
|
|
|
|
6468
|
return ($id, $pos); |
|
882
|
|
|
|
|
|
|
} |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
sub delete_by_id { |
|
885
|
0
|
|
|
0
|
1
|
0
|
my ($I, $id, $return_element)=@_; |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
# warn "delete_by_id($id)\n"; |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
# no such id |
|
890
|
0
|
0
|
|
|
|
0
|
return unless exists $I->_idmap->{$id}; |
|
891
|
|
|
|
|
|
|
|
|
892
|
0
|
|
|
|
|
0
|
my $map=$I->_tmpmap; |
|
893
|
0
|
|
|
|
|
0
|
my $idmap=$I->_idmap; |
|
894
|
0
|
|
|
|
|
0
|
my $pos; |
|
895
|
|
|
|
|
|
|
|
|
896
|
0
|
0
|
|
|
|
0
|
return unless defined($pos=delete $idmap->{$id}); |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
# read VALID, ID, NKEYS |
|
899
|
0
|
|
|
|
|
0
|
my ($valid, $elid, $nkeys)=unpack 'x'.$pos.$I->intfmt.'3', $$map; |
|
900
|
|
|
|
|
|
|
|
|
901
|
0
|
0
|
|
|
|
0
|
return unless $valid; |
|
902
|
0
|
0
|
|
|
|
0
|
return unless $id==$elid; # XXX: should'nt that be an E_CORRUPT |
|
903
|
|
|
|
|
|
|
|
|
904
|
0
|
|
|
|
|
0
|
my $rc=1; |
|
905
|
0
|
0
|
|
|
|
0
|
if( $return_element ) { |
|
906
|
0
|
|
|
|
|
0
|
my $strings=$I->_stringmap; |
|
907
|
0
|
|
|
|
|
0
|
my $sfmt=$I->_stringfmt_out; |
|
908
|
0
|
|
|
|
|
0
|
my $dbfmt=$I->dbformat_out; |
|
909
|
|
|
|
|
|
|
my @l=map { |
|
910
|
0
|
0
|
|
|
|
0
|
if( $dbfmt>DBFMT0 ) { |
|
|
0
|
|
|
|
|
0
|
|
|
911
|
0
|
|
|
|
|
0
|
my ($str, $utf8)=unpack('x'.$_.$sfmt, $$strings); |
|
912
|
0
|
0
|
|
|
|
0
|
Encode::_utf8_on($str) if $utf8; |
|
913
|
0
|
|
|
|
|
0
|
$str; |
|
914
|
|
|
|
|
|
|
} else { |
|
915
|
0
|
|
|
|
|
0
|
unpack('x'.$_.$sfmt, $$strings); |
|
916
|
|
|
|
|
|
|
} |
|
917
|
|
|
|
|
|
|
} unpack('x'.($pos+3*$I->_intsize).$I->intfmt.($nkeys+2), $$map); |
|
918
|
|
|
|
|
|
|
|
|
919
|
0
|
|
|
|
|
0
|
my $rdata=pop @l; |
|
920
|
0
|
|
|
|
|
0
|
my $rsort=pop @l; |
|
921
|
|
|
|
|
|
|
|
|
922
|
0
|
|
|
|
|
0
|
$rc=[\@l, $rsort, $rdata, $id]; |
|
923
|
|
|
|
|
|
|
} |
|
924
|
|
|
|
|
|
|
|
|
925
|
0
|
|
|
|
|
0
|
$I->_putdata($pos, $I->intfmt, 0); # invalidate the record |
|
926
|
|
|
|
|
|
|
|
|
927
|
0
|
|
|
|
|
0
|
return $rc; |
|
928
|
|
|
|
|
|
|
} |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
sub clear { |
|
931
|
1
|
|
|
1
|
1
|
3
|
my ($I)=@_; |
|
932
|
|
|
|
|
|
|
|
|
933
|
1
|
|
|
|
|
3
|
$I->_ct; |
|
934
|
|
|
|
|
|
|
|
|
935
|
1
|
|
|
|
|
3
|
$I->_index_end=BASEOFFSET+DATASTART*$I->_intsize; |
|
936
|
1
|
|
|
|
|
4
|
$I->_stringmap_end=0; |
|
937
|
|
|
|
|
|
|
|
|
938
|
1
|
|
|
|
|
3
|
$I->_idmap={}; |
|
939
|
1
|
|
|
|
|
75
|
$I->_strpos=[]; |
|
940
|
|
|
|
|
|
|
|
|
941
|
1
|
|
|
|
|
41
|
return; |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
# sub xdata_record { |
|
945
|
|
|
|
|
|
|
# my ($I, $pos)=@_; |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
# return unless $pos>0 and $pos<$I->mainidx; |
|
948
|
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
# # valid id nkeys key1...keyn sort data |
|
950
|
|
|
|
|
|
|
# my ($id, $nkeys)=unpack('x'.($pos+$I->_intsize).' '.$I->intfmt.'3', |
|
951
|
|
|
|
|
|
|
# ${$I->_data}); |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
# my $off=$I->_stringtbl; |
|
954
|
|
|
|
|
|
|
# my $data=$I->_data; |
|
955
|
|
|
|
|
|
|
# my $sfmt=$I->_stringfmt; |
|
956
|
|
|
|
|
|
|
# my @l=map { |
|
957
|
|
|
|
|
|
|
# unpack('x'.($off+$_).$sfmt, $$data); |
|
958
|
|
|
|
|
|
|
# } unpack('x'.($pos+3*$I->_intsize).' '.$I->intfmt.($nkeys+2), $$data); |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# my $rdata=pop @l; |
|
961
|
|
|
|
|
|
|
# my $rsort=pop @l; |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# #warn "data_record: keys=[@l], sort=$rsort, data=$rdata, id=$id\n"; |
|
964
|
|
|
|
|
|
|
# return [\@l, $rsort, $rdata, $id]; |
|
965
|
|
|
|
|
|
|
# } |
|
966
|
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
sub iterator { |
|
968
|
3
|
|
|
3
|
1
|
5
|
my ($I, $show_invalid)=@_; |
|
969
|
|
|
|
|
|
|
|
|
970
|
3
|
100
|
|
1
|
|
7
|
return sub {} unless $I->_data; |
|
|
1
|
|
|
|
|
4
|
|
|
971
|
|
|
|
|
|
|
|
|
972
|
2
|
|
|
|
|
6
|
my $pos=BASEOFFSET+DATASTART*$I->_intsize; |
|
973
|
2
|
|
|
|
|
7
|
my $end=$I->mainidx; |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
return MMapDB::Iterator->new |
|
976
|
|
|
|
|
|
|
(sub { |
|
977
|
260
|
50
|
|
260
|
|
521
|
die E_NOT_IMPLEMENTED if @_; |
|
978
|
260
|
100
|
|
|
|
558
|
LOOP: { |
|
979
|
260
|
|
|
|
|
306
|
return if $pos>=$end; |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
# valid id nkeys key1...keyn sort data |
|
982
|
258
|
|
|
|
|
462
|
my ($valid, undef, $nkeys)= |
|
983
|
258
|
|
|
|
|
835
|
unpack 'x'.$pos.' '.$I->intfmt.'3', ${$I->_data}; |
|
984
|
|
|
|
|
|
|
|
|
985
|
258
|
50
|
25
|
|
|
1107
|
if( $valid xor $show_invalid ) { |
|
986
|
258
|
|
|
|
|
367
|
my $rc=$pos; |
|
987
|
258
|
|
|
|
|
482
|
$pos+=$I->_intsize*($nkeys+5); # 5=(valid id nkeys sort data) |
|
988
|
258
|
|
|
|
|
936
|
return $rc; |
|
989
|
|
|
|
|
|
|
} |
|
990
|
0
|
|
|
|
|
0
|
$pos+=$I->_intsize*($nkeys+5); # 5=(valid id nkeys sort data) |
|
991
|
0
|
|
|
|
|
0
|
redo LOOP; |
|
992
|
|
|
|
|
|
|
} |
|
993
|
2
|
|
|
|
|
21
|
}); |
|
994
|
|
|
|
|
|
|
} |
|
995
|
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
package MMapDB::Iterator; |
|
997
|
|
|
|
|
|
|
|
|
998
|
2
|
|
|
2
|
|
2973
|
use strict; |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
542
|
|
|
999
|
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
sub new { |
|
1001
|
2
|
|
|
2
|
|
5
|
my ($class, $func)=@_; |
|
1002
|
2
|
|
33
|
|
|
10
|
$class=ref($class) || $class; |
|
1003
|
2
|
|
|
|
|
16
|
return bless $func=>$class; |
|
1004
|
|
|
|
|
|
|
} |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
sub nth { |
|
1007
|
0
|
|
|
0
|
|
0
|
return $_[0]->(MMapDB::IT_NTH, $_[1]); |
|
1008
|
|
|
|
|
|
|
} |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
sub cur { |
|
1011
|
0
|
|
|
0
|
|
0
|
return $_[0]->(MMapDB::IT_CUR); |
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
sub nelem { |
|
1015
|
0
|
|
|
0
|
|
0
|
return $_[0]->(MMapDB::IT_NELEM); |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
####################################################################### |
|
1019
|
|
|
|
|
|
|
# High Level Accessor Classes |
|
1020
|
|
|
|
|
|
|
####################################################################### |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
{ |
|
1023
|
|
|
|
|
|
|
package |
|
1024
|
|
|
|
|
|
|
MMapDB::_base; |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
2
|
|
|
2
|
|
12
|
use strict; |
|
|
2
|
|
|
|
|
52
|
|
|
|
2
|
|
|
|
|
105
|
|
|
1027
|
2
|
|
|
2
|
|
12
|
use Carp qw/croak/; |
|
|
2
|
|
|
|
|
84
|
|
|
|
2
|
|
|
|
|
155
|
|
|
1028
|
2
|
|
|
2
|
|
11
|
use Scalar::Util (); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
208
|
|
|
1029
|
2
|
|
|
2
|
|
12
|
use Exporter qw/import/; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
100
|
|
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
use constant ({ |
|
1032
|
2
|
|
|
|
|
286
|
PARENT=>0, |
|
1033
|
|
|
|
|
|
|
POS=>1, |
|
1034
|
|
|
|
|
|
|
DATAMODE=>2, |
|
1035
|
|
|
|
|
|
|
ITERATOR=>3, |
|
1036
|
|
|
|
|
|
|
SHADOW=>4, |
|
1037
|
2
|
|
|
2
|
|
13
|
}); |
|
|
2
|
|
|
|
|
10
|
|
|
1038
|
2
|
|
|
2
|
|
1424
|
BEGIN {our @EXPORT=(qw!PARENT POS DATAMODE ITERATOR SHADOW!)}; |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
sub new { |
|
1041
|
6
|
|
|
6
|
|
15
|
my ($class, @param)=@_; |
|
1042
|
6
|
|
33
|
|
|
26
|
$class=ref($class) || $class; |
|
1043
|
6
|
50
|
|
|
|
15
|
$param[DATAMODE]=0 unless defined $param[DATAMODE]; |
|
1044
|
6
|
|
|
|
|
22
|
Scalar::Util::weaken $param[0]; |
|
1045
|
6
|
|
|
|
|
29
|
return bless \@param=>$class; |
|
1046
|
|
|
|
|
|
|
} |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
0
|
|
|
0
|
|
|
sub readonly {croak "Modification of a read-only value attempted";} |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
0
|
|
|
0
|
|
|
sub datamode : lvalue {$_[0]->[DATAMODE]} |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
BEGIN { |
|
1053
|
2
|
|
|
2
|
|
8
|
*TIEHASH=\&new; |
|
1054
|
|
|
|
|
|
|
# STORE must be allowed to support constructs like this (with aliases): |
|
1055
|
|
|
|
|
|
|
# map { |
|
1056
|
|
|
|
|
|
|
# local $_; |
|
1057
|
|
|
|
|
|
|
# } values %{$db->main_index}; |
|
1058
|
|
|
|
|
|
|
# or |
|
1059
|
|
|
|
|
|
|
# for (values %{$db->main_index}) { |
|
1060
|
|
|
|
|
|
|
# local $_; |
|
1061
|
|
|
|
|
|
|
# } |
|
1062
|
|
|
|
|
|
|
*STORE=sub { |
|
1063
|
0
|
|
|
0
|
|
0
|
my ($I, $key, $value)=@_; |
|
1064
|
0
|
|
|
|
|
0
|
my $el; |
|
1065
|
0
|
|
|
|
|
0
|
my $ll=MMapDB::_localizing(); |
|
1066
|
|
|
|
|
|
|
# Carp::cluck "PL_localizing=$ll"; |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
0
|
|
0
|
|
|
0
|
$el=($I->[SHADOW]||={}); |
|
1069
|
0
|
|
|
|
|
0
|
my $sh; |
|
1070
|
0
|
0
|
0
|
|
|
0
|
if( $ll==0 and $sh=$el->{$key} ) { # is already localized |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
# warn " ==> already shadowed"; |
|
1072
|
0
|
|
|
|
|
0
|
$sh->[1]=$value; |
|
1073
|
|
|
|
|
|
|
} elsif( $ll==1 ) { |
|
1074
|
|
|
|
|
|
|
# warn " ==> shadowing"; |
|
1075
|
0
|
|
0
|
|
|
0
|
$sh=($el->{$key}||=[]); |
|
1076
|
0
|
|
|
|
|
0
|
$sh->[0]++; |
|
1077
|
0
|
|
|
|
|
0
|
$sh->[1]=$value; |
|
1078
|
|
|
|
|
|
|
} elsif( $ll==2 ) { |
|
1079
|
0
|
0
|
|
|
|
0
|
if( --$sh->[0] ) { |
|
1080
|
|
|
|
|
|
|
# warn " ==> decremented shadow counter"; |
|
1081
|
0
|
|
|
|
|
0
|
$sh->[1]=$value; |
|
1082
|
|
|
|
|
|
|
} else { |
|
1083
|
|
|
|
|
|
|
# warn " ==> deleting shadow"; |
|
1084
|
0
|
|
|
|
|
0
|
delete $el->{$key}; |
|
1085
|
|
|
|
|
|
|
} |
|
1086
|
|
|
|
|
|
|
} else { |
|
1087
|
|
|
|
|
|
|
# warn " ==> ro"; |
|
1088
|
0
|
|
|
|
|
0
|
goto &readonly; |
|
1089
|
|
|
|
|
|
|
} |
|
1090
|
2
|
|
|
|
|
21
|
}; |
|
1091
|
2
|
|
|
|
|
5
|
*DELETE=\&readonly; |
|
1092
|
2
|
|
|
|
|
6
|
*CLEAR=\&readonly; |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
2
|
|
|
|
|
4
|
*TIEARRAY=\&new; |
|
1095
|
|
|
|
|
|
|
#*STORE=sub {}; |
|
1096
|
2
|
|
|
|
|
4
|
*STORESIZE=\&readonly; |
|
1097
|
2
|
|
|
|
|
4
|
*EXTEND=\&readonly; |
|
1098
|
|
|
|
|
|
|
#*DELETE=\&readonly; |
|
1099
|
|
|
|
|
|
|
#*CLEAR=\&readonly; |
|
1100
|
2
|
|
|
|
|
4
|
*PUSH=\&readonly; |
|
1101
|
2
|
|
|
|
|
4
|
*UNSHIFT=\&readonly; |
|
1102
|
2
|
|
|
|
|
4
|
*POP=\&readonly; |
|
1103
|
2
|
|
|
|
|
4
|
*SHIFT=\&readonly; |
|
1104
|
2
|
|
|
|
|
87
|
*SPLICE=\&readonly; |
|
1105
|
|
|
|
|
|
|
} |
|
1106
|
|
|
|
|
|
|
} |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
####################################################################### |
|
1109
|
|
|
|
|
|
|
# Normal Index Accessor |
|
1110
|
|
|
|
|
|
|
####################################################################### |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
{ |
|
1113
|
|
|
|
|
|
|
package MMapDB::Index; |
|
1114
|
|
|
|
|
|
|
|
|
1115
|
2
|
|
|
2
|
|
13
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
172
|
|
|
1116
|
2
|
|
|
2
|
|
1723
|
BEGIN {MMapDB::_base->import} |
|
1117
|
|
|
|
|
|
|
{our @ISA=qw/MMapDB::_base/} |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
sub FETCH { |
|
1120
|
0
|
|
|
0
|
|
|
my ($I, $key)=@_; |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
{ |
|
1123
|
0
|
|
|
|
|
|
my $shel; |
|
|
0
|
|
|
|
|
|
|
|
1124
|
0
|
0
|
0
|
|
|
|
$shel=$I->[SHADOW] and |
|
|
|
|
0
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
keys %$shel and |
|
1126
|
|
|
|
|
|
|
$shel=$shel->{$key} and |
|
1127
|
|
|
|
|
|
|
return $shel->[1]; |
|
1128
|
|
|
|
|
|
|
} |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
0
|
|
|
|
|
|
my @el=$I->[PARENT]->index_lookup($I->[POS], $key); |
|
1131
|
|
|
|
|
|
|
|
|
1132
|
0
|
0
|
|
|
|
|
return unless @el; |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
0
|
|
|
|
|
|
my $rc; |
|
1135
|
|
|
|
|
|
|
|
|
1136
|
0
|
0
|
0
|
|
|
|
if( @el==1 and $el[0]>=$I->[PARENT]->mainidx ) { |
|
1137
|
|
|
|
|
|
|
# another index |
|
1138
|
0
|
|
|
|
|
|
tie %{$rc={}}, ref($I), $I->[PARENT], $el[0], $I->[DATAMODE]; |
|
|
0
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
} else { |
|
1140
|
0
|
|
|
|
|
|
tie @{$rc=[]}, 'MMapDB::Data', $I->[PARENT], \@el, $I->[DATAMODE]; |
|
|
0
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
} |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
0
|
|
|
|
|
|
return $rc; |
|
1144
|
|
|
|
|
|
|
} |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
sub EXISTS { |
|
1147
|
0
|
|
|
0
|
|
|
my ($I, $key)=@_; |
|
1148
|
0
|
0
|
|
|
|
|
return $I->[PARENT]->index_lookup($I->[POS], $key) ? 1 : undef; |
|
1149
|
|
|
|
|
|
|
} |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
sub FIRSTKEY { |
|
1152
|
0
|
|
|
0
|
|
|
my ($I)=@_; |
|
1153
|
0
|
|
|
|
|
|
my @el=($I->[ITERATOR]=$I->[PARENT]->index_iterator($I->[POS]))->(); |
|
1154
|
0
|
0
|
|
|
|
|
return @el ? $el[0] : (); |
|
1155
|
|
|
|
|
|
|
} |
|
1156
|
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
sub NEXTKEY { |
|
1158
|
0
|
|
|
0
|
|
|
my ($I)=@_; |
|
1159
|
0
|
|
|
|
|
|
my @el=$I->[ITERATOR]->(); |
|
1160
|
0
|
0
|
|
|
|
|
return @el ? $el[0] : (); |
|
1161
|
|
|
|
|
|
|
} |
|
1162
|
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
sub SCALAR { |
|
1164
|
0
|
|
|
0
|
|
|
my ($I)=@_; |
|
1165
|
0
|
0
|
|
|
|
|
my $pos=defined $I->[POS] ? $I->[POS] : $I->[PARENT]->_ididx; |
|
1166
|
0
|
|
|
|
|
|
my $n=unpack 'x'.$pos.$I->[PARENT]->intfmt,${$I->[PARENT]->_data}; |
|
|
0
|
|
|
|
|
|
|
|
1167
|
0
|
0
|
|
|
|
|
return $n==0 ? $n : "$n/$n"; |
|
1168
|
|
|
|
|
|
|
} |
|
1169
|
|
|
|
|
|
|
} |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
####################################################################### |
|
1172
|
|
|
|
|
|
|
# ID Index Accessor |
|
1173
|
|
|
|
|
|
|
####################################################################### |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
{ |
|
1176
|
|
|
|
|
|
|
package MMapDB::IDIndex; |
|
1177
|
|
|
|
|
|
|
|
|
1178
|
2
|
|
|
2
|
|
11
|
use strict; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
137
|
|
|
1179
|
2
|
|
|
2
|
|
1423
|
BEGIN {MMapDB::_base->import} |
|
1180
|
|
|
|
|
|
|
{our @ISA=qw/MMapDB::Index/} |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
sub FETCH { |
|
1183
|
|
|
|
|
|
|
{ |
|
1184
|
0
|
|
|
0
|
|
|
my $shel; |
|
|
0
|
|
|
|
|
|
|
|
1185
|
0
|
0
|
0
|
|
|
|
$shel=$_[0]->[SHADOW] and |
|
|
|
|
0
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
keys %$shel and |
|
1187
|
|
|
|
|
|
|
$shel=$shel->{$_[1]} and |
|
1188
|
|
|
|
|
|
|
return $shel->[1]; |
|
1189
|
|
|
|
|
|
|
} |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
0
|
0
|
|
|
|
|
if( $_[0]->[DATAMODE]==MMapDB::DATAMODE_SIMPLE ) { |
|
1192
|
0
|
|
|
|
|
|
$_[0]->[PARENT]->data_value($_[0]->[PARENT]->id_index_lookup($_[1])); |
|
1193
|
|
|
|
|
|
|
} else { |
|
1194
|
0
|
|
|
|
|
|
$_[0]->[PARENT]->data_record($_[0]->[PARENT]->id_index_lookup($_[1])); |
|
1195
|
|
|
|
|
|
|
} |
|
1196
|
|
|
|
|
|
|
} |
|
1197
|
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
sub EXISTS { |
|
1199
|
0
|
|
|
0
|
|
|
my ($I, $key)=@_; |
|
1200
|
0
|
0
|
|
|
|
|
return $I->[PARENT]->id_index_lookup($key) ? 1 : undef; |
|
1201
|
|
|
|
|
|
|
} |
|
1202
|
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
sub FIRSTKEY { |
|
1204
|
0
|
|
|
0
|
|
|
my ($I)=@_; |
|
1205
|
0
|
|
|
|
|
|
my @el=($I->[ITERATOR]=$I->[PARENT]->id_index_iterator)->(); |
|
1206
|
0
|
0
|
|
|
|
|
return @el ? $el[0] : (); |
|
1207
|
|
|
|
|
|
|
} |
|
1208
|
|
|
|
|
|
|
} |
|
1209
|
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
####################################################################### |
|
1211
|
|
|
|
|
|
|
# Data Accessor |
|
1212
|
|
|
|
|
|
|
####################################################################### |
|
1213
|
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
{ |
|
1215
|
|
|
|
|
|
|
package MMapDB::Data; |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
2
|
|
|
2
|
|
12
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
136
|
|
|
1218
|
2
|
|
|
2
|
|
1040
|
BEGIN {MMapDB::_base->import} |
|
1219
|
|
|
|
|
|
|
{our @ISA=qw/MMapDB::_base/} |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
sub FETCH { |
|
1222
|
0
|
|
|
0
|
|
|
my ($I, $idx)=@_; |
|
1223
|
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
{ |
|
1225
|
0
|
|
|
|
|
|
my $shel; |
|
|
0
|
|
|
|
|
|
|
|
1226
|
0
|
0
|
0
|
|
|
|
$shel=$I->[SHADOW] and |
|
|
|
|
0
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
keys %$shel and |
|
1228
|
|
|
|
|
|
|
$shel=$shel->{$idx} and |
|
1229
|
|
|
|
|
|
|
return $shel->[1]; |
|
1230
|
|
|
|
|
|
|
} |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
0
|
0
|
|
|
|
|
return unless @{$I->[POS]}>$idx; |
|
|
0
|
|
|
|
|
|
|
|
1233
|
0
|
0
|
|
|
|
|
if( $I->[DATAMODE]==MMapDB::DATAMODE_SIMPLE ) { |
|
1234
|
0
|
|
|
|
|
|
return $I->[PARENT]->data_value($I->[POS]->[$idx]); |
|
1235
|
|
|
|
|
|
|
} else { |
|
1236
|
0
|
|
|
|
|
|
return $I->[PARENT]->data_record($I->[POS]->[$idx]); |
|
1237
|
|
|
|
|
|
|
} |
|
1238
|
|
|
|
|
|
|
} |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
0
|
|
|
0
|
|
|
sub FETCHSIZE {scalar @{$_[0]->[POS]}} |
|
|
0
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
|
|
1242
|
0
|
|
|
0
|
|
|
sub EXISTS {@{$_[0]->[POS]}>$_[1]} |
|
|
0
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
} |
|
1244
|
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
1; |
|
1246
|
|
|
|
|
|
|
__END__ |