line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::SkipList;
|
2
|
|
|
|
|
|
|
|
3
|
13
|
|
|
13
|
|
202217
|
use 5.006;
|
|
13
|
|
|
|
|
40
|
|
|
13
|
|
|
|
|
452
|
|
4
|
13
|
|
|
13
|
|
70
|
use strict;
|
|
13
|
|
|
|
|
30
|
|
|
13
|
|
|
|
|
451
|
|
5
|
13
|
|
|
13
|
|
67
|
use warnings::register __PACKAGE__;
|
|
13
|
|
|
|
|
23
|
|
|
13
|
|
|
|
|
3961
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '1.02';
|
8
|
|
|
|
|
|
|
# $VERSION = eval $VERSION;
|
9
|
|
|
|
|
|
|
|
10
|
13
|
|
|
13
|
|
173521
|
use AutoLoader qw( AUTOLOAD );
|
|
13
|
|
|
|
|
20519
|
|
|
13
|
|
|
|
|
76
|
|
11
|
13
|
|
|
13
|
|
544
|
use Carp qw( carp croak );
|
|
13
|
|
|
|
|
25
|
|
|
13
|
|
|
|
|
1329
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Algorithm::SkipList::Node;
|
14
|
|
|
|
|
|
|
require Algorithm::SkipList::Header;
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# Future versions should check Config module to determine if it is
|
17
|
|
|
|
|
|
|
# being run on a 64-bit processor, and set MAX_LEVEL to 64.
|
18
|
|
|
|
|
|
|
|
19
|
13
|
|
|
13
|
|
75
|
use constant MIN_LEVEL => 2;
|
|
13
|
|
|
|
|
24
|
|
|
13
|
|
|
|
|
925
|
|
20
|
13
|
|
|
13
|
|
69
|
use constant MAX_LEVEL => 32;
|
|
13
|
|
|
|
|
23
|
|
|
13
|
|
|
|
|
487
|
|
21
|
13
|
|
|
13
|
|
61
|
use constant DEF_P => 0.25;
|
|
13
|
|
|
|
|
22
|
|
|
13
|
|
|
|
|
501
|
|
22
|
13
|
|
|
13
|
|
66
|
use constant DEF_K => 0;
|
|
13
|
|
|
|
|
21
|
|
|
13
|
|
|
|
|
579
|
|
23
|
|
|
|
|
|
|
|
24
|
13
|
|
|
13
|
|
66
|
use constant BASE_NODE_CLASS => 'Algorithm::SkipList::Node';
|
|
13
|
|
|
|
|
22
|
|
|
13
|
|
|
|
|
1540
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# We use Exporter instead of something like Exporter::Lite because
|
27
|
|
|
|
|
|
|
# Carp uses it.
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
require Exporter;
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our @EXPORT = ( );
|
32
|
|
|
|
|
|
|
our @EXPORT_OK = ( );
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub new {
|
35
|
13
|
|
|
13
|
|
13078
|
no integer;
|
|
13
|
|
|
|
|
127
|
|
|
13
|
|
|
|
|
62
|
|
36
|
|
|
|
|
|
|
|
37
|
103
|
|
|
103
|
1
|
49984
|
my $class = shift;
|
38
|
|
|
|
|
|
|
|
39
|
103
|
|
|
|
|
1227
|
my $self = {
|
40
|
|
|
|
|
|
|
NODECLASS => BASE_NODE_CLASS, # node class used by list
|
41
|
|
|
|
|
|
|
LIST => undef, # pointer to the header node
|
42
|
|
|
|
|
|
|
SIZE => undef, # size of list
|
43
|
|
|
|
|
|
|
SIZE_THRESHOLD => undef, # size at which SIZE_LEVEL increased
|
44
|
|
|
|
|
|
|
LAST_SIZE_TH => undef, # previous SIZE_THRESHOLD
|
45
|
|
|
|
|
|
|
SIZE_LEVEL => undef, # maximum level random_level
|
46
|
|
|
|
|
|
|
MAXLEVEL => MAX_LEVEL, # absolute maximum level
|
47
|
|
|
|
|
|
|
P => 0, # probability for each level
|
48
|
|
|
|
|
|
|
K => 0, # minimum power of P
|
49
|
|
|
|
|
|
|
P_LEVELS => [ ], # array used by random_level
|
50
|
|
|
|
|
|
|
LIST_END => undef, # node with greatest key
|
51
|
|
|
|
|
|
|
LASTKEY => undef, # last key used by next_key
|
52
|
|
|
|
|
|
|
LASTINSRT => undef, # cached insertion fingers
|
53
|
|
|
|
|
|
|
DUPLICATES => 0, # allow duplicates?
|
54
|
|
|
|
|
|
|
};
|
55
|
|
|
|
|
|
|
|
56
|
103
|
|
|
|
|
281
|
bless $self, $class;
|
57
|
|
|
|
|
|
|
|
58
|
103
|
|
|
|
|
309
|
$self->_set_p( DEF_P ); # initializes P_LEVELS
|
59
|
103
|
|
|
|
|
338
|
$self->_set_k( DEF_K );
|
60
|
|
|
|
|
|
|
|
61
|
103
|
100
|
|
|
|
369
|
if (@_) {
|
62
|
95
|
|
|
|
|
316
|
my %args = @_;
|
63
|
95
|
|
|
|
|
231
|
foreach my $arg_name (CORE::keys %args) {
|
64
|
163
|
|
|
|
|
301
|
my $method = "_set_" . $arg_name;
|
65
|
163
|
50
|
|
|
|
714
|
if ($self->can($method)) {
|
66
|
163
|
|
|
|
|
394
|
$self->$method( $args{ $arg_name } );
|
67
|
|
|
|
|
|
|
} else {
|
68
|
0
|
|
|
|
|
0
|
croak "Invalid parameter name: ``$arg_name\'\'";
|
69
|
|
|
|
|
|
|
}
|
70
|
|
|
|
|
|
|
}
|
71
|
|
|
|
|
|
|
}
|
72
|
|
|
|
|
|
|
|
73
|
96
|
|
|
|
|
308
|
$self->clear;
|
74
|
|
|
|
|
|
|
|
75
|
96
|
|
|
|
|
1161
|
return $self;
|
76
|
|
|
|
|
|
|
}
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub _set_duplicates {
|
79
|
2
|
|
|
2
|
|
3
|
my ($self, $dup) = @_;
|
80
|
2
|
|
100
|
|
|
12
|
$self->{DUPLICATES} = $dup || 0;
|
81
|
|
|
|
|
|
|
}
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub _set_node_class {
|
84
|
47
|
|
|
47
|
|
65
|
my ($self, $node_class) = @_;
|
85
|
47
|
|
|
|
|
145
|
$self->{NODECLASS} = $node_class;
|
86
|
|
|
|
|
|
|
}
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub _node_class {
|
89
|
24990
|
|
|
24990
|
|
27350
|
my ($self) = @_;
|
90
|
24990
|
|
|
|
|
84784
|
$self->{NODECLASS};
|
91
|
|
|
|
|
|
|
}
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub reset {
|
94
|
156
|
|
|
156
|
1
|
448
|
my ($self) = @_;
|
95
|
156
|
|
|
|
|
304
|
$self->{LASTKEY} = undef;
|
96
|
|
|
|
|
|
|
}
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub clear {
|
99
|
99
|
|
|
99
|
1
|
1042
|
my ($self) = @_;
|
100
|
|
|
|
|
|
|
|
101
|
99
|
|
|
|
|
183
|
$self->{SIZE} = 0;
|
102
|
99
|
|
|
|
|
140
|
$self->{SIZE_THRESHOLD} = 2;
|
103
|
99
|
|
|
|
|
139
|
$self->{LAST_SIZE_TH} = 0;
|
104
|
99
|
|
|
|
|
122
|
$self->{SIZE_LEVEL} = MIN_LEVEL;
|
105
|
|
|
|
|
|
|
|
106
|
99
|
|
|
|
|
261
|
my $hdr = [ (undef) x $self->{SIZE_LEVEL} ];
|
107
|
|
|
|
|
|
|
|
108
|
99
|
|
|
|
|
228
|
CORE::delete $self->{LIST};
|
109
|
99
|
|
|
|
|
521
|
$self->{LIST} = new Algorithm::SkipList::Header( undef, undef, $hdr );
|
110
|
|
|
|
|
|
|
|
111
|
99
|
|
|
|
|
163
|
$self->{LIST_END} = undef;
|
112
|
99
|
|
|
|
|
130
|
$self->{LASTINSRT} = undef;
|
113
|
|
|
|
|
|
|
|
114
|
99
|
|
|
|
|
284
|
$self->reset;
|
115
|
|
|
|
|
|
|
}
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub _set_max_level {
|
118
|
161
|
|
|
161
|
|
241
|
my ($self, $level) = @_;
|
119
|
161
|
100
|
100
|
|
|
626
|
if ($level > MAX_LEVEL) {
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
120
|
1
|
|
|
|
|
198
|
croak "Cannot set max_level greater than ", MAX_LEVEL;
|
121
|
|
|
|
|
|
|
} elsif ($level < MIN_LEVEL) {
|
122
|
5
|
|
|
|
|
657
|
croak "Cannot set max_level less than ", MIN_LEVEL;
|
123
|
|
|
|
|
|
|
} elsif ((defined $self->list) && ($level < $self->list->level)) {
|
124
|
30
|
|
|
|
|
5440
|
croak "Current level exceeds specified level";
|
125
|
|
|
|
|
|
|
}
|
126
|
125
|
|
|
|
|
400
|
$self->{MAXLEVEL} = $level;
|
127
|
|
|
|
|
|
|
}
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub max_level {
|
130
|
280
|
|
|
280
|
1
|
13936
|
my ($self, $level) = @_;
|
131
|
|
|
|
|
|
|
|
132
|
280
|
100
|
|
|
|
550
|
if (defined $level) {
|
133
|
92
|
|
|
|
|
210
|
$self->_set_max_level($level);
|
134
|
|
|
|
|
|
|
} else {
|
135
|
188
|
|
|
|
|
830
|
$self->{MAXLEVEL};
|
136
|
|
|
|
|
|
|
}
|
137
|
|
|
|
|
|
|
}
|
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# We use the formula from Pugh's "Skip List Cookbook" paper. We
|
140
|
|
|
|
|
|
|
# generate a reverse-sorted array of values based on p and k. In
|
141
|
|
|
|
|
|
|
# _new_node_level() we look for the highest value in the array that is
|
142
|
|
|
|
|
|
|
# less than a random number n (0
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _build_distribution {
|
145
|
13
|
|
|
13
|
|
9592
|
no integer;
|
|
13
|
|
|
|
|
22
|
|
|
13
|
|
|
|
|
52
|
|
146
|
|
|
|
|
|
|
|
147
|
256
|
|
|
256
|
|
373
|
my ($self) = @_;
|
148
|
|
|
|
|
|
|
|
149
|
256
|
|
|
|
|
681
|
my $p = $self->p;
|
150
|
256
|
|
|
|
|
551
|
my $k = $self->k;
|
151
|
|
|
|
|
|
|
|
152
|
256
|
|
|
|
|
1453
|
$self->{P_LEVELS} = [ (0) x MAX_LEVEL ];
|
153
|
256
|
|
|
|
|
791
|
for my $i (0..MAX_LEVEL) {
|
154
|
8448
|
|
|
|
|
20249
|
$self->{P_LEVELS}->[$i] = $p**($i+$k);
|
155
|
|
|
|
|
|
|
}
|
156
|
|
|
|
|
|
|
}
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _set_p {
|
159
|
13
|
|
|
13
|
|
1703
|
no integer;
|
|
13
|
|
|
|
|
26
|
|
|
13
|
|
|
|
|
45
|
|
160
|
|
|
|
|
|
|
|
161
|
156
|
|
|
156
|
|
265
|
my ($self, $p) = @_;
|
162
|
|
|
|
|
|
|
|
163
|
156
|
100
|
100
|
|
|
1120
|
unless ( ($p>0) && ($p<1) ) {
|
164
|
3
|
|
|
|
|
491
|
croak "Unvalid value for P (must be between 0 and 1)";
|
165
|
|
|
|
|
|
|
}
|
166
|
|
|
|
|
|
|
|
167
|
153
|
|
|
|
|
308
|
$self->{P} = $p;
|
168
|
153
|
|
|
|
|
385
|
$self->_build_distribution;
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
}
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub p {
|
173
|
13
|
|
|
13
|
|
1586
|
no integer;
|
|
13
|
|
|
|
|
42
|
|
|
13
|
|
|
|
|
65
|
|
174
|
|
|
|
|
|
|
|
175
|
314
|
|
|
314
|
1
|
453
|
my ($self, $p) = @_;
|
176
|
|
|
|
|
|
|
|
177
|
314
|
100
|
|
|
|
634
|
if (defined $p) {
|
178
|
8
|
|
|
|
|
16
|
$self->_set_p($p);
|
179
|
|
|
|
|
|
|
} else {
|
180
|
306
|
|
|
|
|
763
|
$self->{P};
|
181
|
|
|
|
|
|
|
}
|
182
|
|
|
|
|
|
|
}
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub _set_k {
|
185
|
103
|
|
|
103
|
|
153
|
my ($self, $k) = @_;
|
186
|
|
|
|
|
|
|
|
187
|
103
|
50
|
|
|
|
300
|
unless ( $k>=0 ) {
|
188
|
0
|
|
|
|
|
0
|
croak "Unvalid value for K (must be at least 0)";
|
189
|
|
|
|
|
|
|
}
|
190
|
|
|
|
|
|
|
|
191
|
103
|
|
|
|
|
198
|
$self->{K} = $k;
|
192
|
103
|
|
|
|
|
215
|
$self->_build_distribution;
|
193
|
|
|
|
|
|
|
}
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub k {
|
196
|
256
|
|
|
256
|
1
|
336
|
my ($self, $k) = @_;
|
197
|
|
|
|
|
|
|
|
198
|
256
|
50
|
|
|
|
436
|
if (defined $k) {
|
199
|
0
|
|
|
|
|
0
|
$self->_set_k($k);
|
200
|
|
|
|
|
|
|
} else {
|
201
|
256
|
|
|
|
|
757
|
$self->{K};
|
202
|
|
|
|
|
|
|
}
|
203
|
|
|
|
|
|
|
}
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub size {
|
206
|
490
|
|
|
490
|
1
|
25949
|
my ($self) = @_;
|
207
|
490
|
|
|
|
|
2944
|
$self->{SIZE};
|
208
|
|
|
|
|
|
|
}
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub list {
|
211
|
51387
|
|
|
51387
|
1
|
77402
|
my ($self) = @_;
|
212
|
51387
|
|
|
|
|
82915
|
$self->{LIST};
|
213
|
|
|
|
|
|
|
}
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub _adjust_level_threshold {
|
217
|
13
|
|
|
13
|
|
2798
|
use integer;
|
|
13
|
|
|
|
|
37
|
|
|
13
|
|
|
|
|
51
|
|
218
|
|
|
|
|
|
|
|
219
|
24984
|
|
|
24984
|
|
27602
|
my ($self) = @_;
|
220
|
|
|
|
|
|
|
|
221
|
24984
|
100
|
|
|
|
99392
|
if ($self->{SIZE} >= $self->{SIZE_THRESHOLD}) {
|
|
|
100
|
|
|
|
|
|
222
|
386
|
|
|
|
|
615
|
$self->{LAST_SIZE_TH} = $self->{SIZE_THRESHOLD};
|
223
|
386
|
|
|
|
|
571
|
$self->{SIZE_THRESHOLD} += $self->{SIZE_THRESHOLD};
|
224
|
386
|
100
|
|
|
|
1633
|
$self->{SIZE_LEVEL}++, if ($self->{SIZE_LEVEL} < $self->{MAXLEVEL});
|
225
|
|
|
|
|
|
|
} elsif ($self->{SIZE} < $self->{LAST_SIZE_TH}) {
|
226
|
22
|
|
|
|
|
40
|
$self->{SIZE_THRESHOLD} = $self->{LAST_SIZE_TH};
|
227
|
22
|
|
|
|
|
46
|
$self->{LAST_SIZE_TH} = $self->{LAST_SIZE_TH} / 2;
|
228
|
22
|
100
|
|
|
|
236
|
$self->{SIZE_LEVEL}--, if ($self->{SIZE_LEVEL} > MIN_LEVEL);
|
229
|
|
|
|
|
|
|
}
|
230
|
|
|
|
|
|
|
}
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub _new_node_level { # previously _random_level
|
233
|
13
|
|
|
13
|
|
1745
|
no integer;
|
|
13
|
|
|
|
|
520
|
|
|
13
|
|
|
|
|
48
|
|
234
|
|
|
|
|
|
|
|
235
|
24906
|
|
|
24906
|
|
25936
|
my ($self) = @_;
|
236
|
|
|
|
|
|
|
|
237
|
24906
|
|
|
|
|
29508
|
my $n = CORE::rand();
|
238
|
24906
|
|
|
|
|
24376
|
my $level = 1;
|
239
|
|
|
|
|
|
|
|
240
|
24906
|
|
100
|
|
|
91856
|
while (($n < $self->{P_LEVELS}->[$level]) &&
|
241
|
|
|
|
|
|
|
($level < $self->{SIZE_LEVEL})) {
|
242
|
8161
|
|
|
|
|
27384
|
$level++;
|
243
|
|
|
|
|
|
|
}
|
244
|
|
|
|
|
|
|
|
245
|
24906
|
|
|
|
|
38064
|
$level;
|
246
|
|
|
|
|
|
|
}
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub _search_with_finger {
|
249
|
25202
|
|
|
25202
|
|
29231
|
my ($self, $key, $finger) = @_;
|
250
|
|
|
|
|
|
|
|
251
|
13
|
|
|
13
|
|
1278
|
use integer;
|
|
13
|
|
|
|
|
20
|
|
|
13
|
|
|
|
|
44
|
|
252
|
|
|
|
|
|
|
|
253
|
25202
|
|
|
|
|
39099
|
my $list = $self->list;
|
254
|
25202
|
|
|
|
|
61397
|
my $level = $list->level-1;
|
255
|
|
|
|
|
|
|
|
256
|
25202
|
|
66
|
|
|
63090
|
my $node = $finger->[ $level ] || $list;
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Iteresting Perl syntax quirk:
|
259
|
|
|
|
|
|
|
# do { my $x = ... } while ($x)
|
260
|
|
|
|
|
|
|
# doesn't work because it considers $x out of scope.
|
261
|
|
|
|
|
|
|
#
|
262
|
|
|
|
|
|
|
# However, benchmarking shows that it's faster to use
|
263
|
|
|
|
|
|
|
# my $x; do { $x = ... } while ($x)
|
264
|
|
|
|
|
|
|
#
|
265
|
|
|
|
|
|
|
|
266
|
25202
|
|
|
|
|
23613
|
my $fwd;
|
267
|
25202
|
|
|
|
|
24122
|
my $cmp = -1;
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# This version of the search algorithm is based on Schneier, 1994.
|
270
|
|
|
|
|
|
|
|
271
|
25202
|
|
|
|
|
24577
|
do {
|
272
|
127777
|
|
100
|
|
|
293822
|
while ( ($fwd = $node->header()->[$level]) &&
|
273
|
|
|
|
|
|
|
($cmp = $fwd->key_cmp($key)) < 0) {
|
274
|
249632
|
|
|
|
|
633405
|
$node = $fwd;
|
275
|
|
|
|
|
|
|
}
|
276
|
127777
|
|
|
|
|
335137
|
$finger->[$level] = $node;
|
277
|
|
|
|
|
|
|
} while ((--$level>=0)); # && ($cmp));
|
278
|
|
|
|
|
|
|
|
279
|
25202
|
100
|
|
|
|
47475
|
$node = $fwd, unless ($cmp);
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Ideally we could stop when $cmp == 0, but the update vector would
|
282
|
|
|
|
|
|
|
# not be complete for levels below $level. insert still works, but
|
283
|
|
|
|
|
|
|
# delete and truncate have problems and need kluges to make up for
|
284
|
|
|
|
|
|
|
# that.
|
285
|
|
|
|
|
|
|
|
286
|
25202
|
|
|
|
|
69271
|
($node, $finger, $cmp);
|
287
|
|
|
|
|
|
|
}
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub _search {
|
290
|
366
|
|
|
366
|
|
9373
|
my ($self, $key, $finger) = @_;
|
291
|
|
|
|
|
|
|
|
292
|
13
|
|
|
13
|
|
1977
|
use integer;
|
|
13
|
|
|
|
|
45
|
|
|
13
|
|
|
|
|
64
|
|
293
|
|
|
|
|
|
|
|
294
|
366
|
|
|
|
|
720
|
my $list = $self->list;
|
295
|
366
|
|
|
|
|
1151
|
my $level = $list->level-1;
|
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# $finger ||= [ ];
|
298
|
|
|
|
|
|
|
|
299
|
366
|
|
66
|
|
|
1539
|
my $node = $finger->[ $level ] || $list;
|
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# This version of the search algorithm is based on Schneier, 1994.
|
302
|
|
|
|
|
|
|
|
303
|
366
|
|
|
|
|
380
|
my $fwd;
|
304
|
366
|
|
|
|
|
432
|
my $cmp = -1;
|
305
|
|
|
|
|
|
|
|
306
|
366
|
|
100
|
|
|
368
|
do {
|
307
|
707
|
|
100
|
|
|
2306
|
while ( ($fwd = $node->header()->[$level]) &&
|
308
|
|
|
|
|
|
|
($cmp = $fwd->key_cmp($key)) < 0) {
|
309
|
809
|
|
|
|
|
4702
|
$node = $fwd;
|
310
|
|
|
|
|
|
|
}
|
311
|
|
|
|
|
|
|
} while ((--$level>=0) && ($cmp));
|
312
|
|
|
|
|
|
|
|
313
|
366
|
|
|
|
|
1533
|
$node = $fwd; # , unless ($cmp); # Devel::Cover says it's never false
|
314
|
|
|
|
|
|
|
|
315
|
366
|
|
|
|
|
2760
|
($node, $finger, $cmp);
|
316
|
|
|
|
|
|
|
}
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub insert {
|
319
|
24954
|
|
|
24954
|
1
|
89247
|
my ($self, $key, $value, $finger) = @_;
|
320
|
|
|
|
|
|
|
|
321
|
13
|
|
|
13
|
|
1771
|
use integer;
|
|
13
|
|
|
|
|
44
|
|
|
13
|
|
|
|
|
48
|
|
322
|
|
|
|
|
|
|
|
323
|
24954
|
|
|
|
|
40676
|
my $list = $self->list;
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# We save the node and finger of the last insertion. If the next key
|
326
|
|
|
|
|
|
|
# is larger, then we can use the "finger" to speed up insertions.
|
327
|
|
|
|
|
|
|
|
328
|
24954
|
|
|
|
|
28558
|
my ($node, $cmp);
|
329
|
|
|
|
|
|
|
|
330
|
24954
|
100
|
|
|
|
50269
|
unless ($finger) {
|
331
|
24812
|
100
|
|
|
|
60789
|
$node = $self->{LASTINSRT}->[0] and do {
|
332
|
24736
|
100
|
|
|
|
66836
|
$finger = $self->{LASTINSRT}->[1],
|
333
|
|
|
|
|
|
|
if ($node->key_cmp($key) <= 0);
|
334
|
|
|
|
|
|
|
};
|
335
|
|
|
|
|
|
|
}
|
336
|
|
|
|
|
|
|
|
337
|
24954
|
|
|
|
|
51378
|
($node, $finger, $cmp) = $self->_search_with_finger($key, $finger);
|
338
|
|
|
|
|
|
|
|
339
|
24954
|
100
|
100
|
|
|
65040
|
if ($cmp || $self->{DUPLICATES}) {
|
340
|
|
|
|
|
|
|
|
341
|
24906
|
|
|
|
|
42677
|
my $new_level = $self->_new_node_level;
|
342
|
|
|
|
|
|
|
|
343
|
24906
|
|
|
|
|
37345
|
my $node_hdr = [ ];
|
344
|
24906
|
|
|
|
|
24584
|
my $fing_hdr;
|
345
|
|
|
|
|
|
|
|
346
|
24906
|
|
|
|
|
41856
|
$node = $self->_node_class->new( $key, $value, $node_hdr );
|
347
|
|
|
|
|
|
|
|
348
|
24906
|
|
|
|
|
64235
|
for (my $i=0;$i<$new_level;$i++) {
|
349
|
33067
|
|
66
|
|
|
112480
|
$fing_hdr = ($finger->[$i]||$list)->header();
|
350
|
33067
|
|
|
|
|
61719
|
$node_hdr->[$i] = $fing_hdr->[$i];
|
351
|
33067
|
|
|
|
|
87630
|
$fing_hdr->[$i] = $node;
|
352
|
|
|
|
|
|
|
}
|
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# We no longer set the LIST_END value, since it is the job of the
|
356
|
|
|
|
|
|
|
# _greatest_node method to find it, as needed.
|
357
|
|
|
|
|
|
|
|
358
|
24906
|
|
|
|
|
29210
|
$self->{SIZE}++;
|
359
|
24906
|
|
|
|
|
41642
|
$self->_adjust_level_threshold;
|
360
|
|
|
|
|
|
|
} else {
|
361
|
48
|
|
|
|
|
124
|
$node->value($value);
|
362
|
|
|
|
|
|
|
}
|
363
|
24954
|
|
|
|
|
43333
|
$self->{LASTINSRT}->[0] = $node;
|
364
|
24954
|
|
|
|
|
70262
|
$self->{LASTINSRT}->[1] = $finger;
|
365
|
|
|
|
|
|
|
}
|
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub delete {
|
368
|
|
|
|
|
|
|
|
369
|
75
|
|
|
75
|
1
|
17439
|
my ($self, $key, $finger) = @_;
|
370
|
|
|
|
|
|
|
|
371
|
13
|
|
|
13
|
|
3298
|
use integer;
|
|
13
|
|
|
|
|
22
|
|
|
13
|
|
|
|
|
57
|
|
372
|
|
|
|
|
|
|
|
373
|
75
|
|
|
|
|
136
|
my $list = $self->list;
|
374
|
|
|
|
|
|
|
|
375
|
75
|
|
|
|
|
168
|
my ($node, $update_ref, $cmp) = $self->_search_with_finger($key, $finger);
|
376
|
|
|
|
|
|
|
|
377
|
75
|
100
|
|
|
|
170
|
if ($cmp == 0) {
|
378
|
55
|
|
|
|
|
134
|
my $value = $node->value;
|
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Note: it might make better sense to set $self->{LIST_END} = undef, and
|
381
|
|
|
|
|
|
|
# let the _greatest_node method search for it if it's needed again.
|
382
|
|
|
|
|
|
|
|
383
|
55
|
100
|
100
|
|
|
217
|
if (($self->{LIST_END}) && ($node == $self->{LIST_END})) {
|
384
|
6
|
|
|
|
|
11
|
$self->{LIST_END} = $update_ref->[0];
|
385
|
|
|
|
|
|
|
}
|
386
|
|
|
|
|
|
|
|
387
|
55
|
|
|
|
|
121
|
my $level = $node->level;
|
388
|
|
|
|
|
|
|
|
389
|
55
|
|
|
|
|
125
|
for (my $i=0; $i<$level; $i++) {
|
390
|
72
|
|
|
|
|
160
|
$update_ref->[$i]->header()->[$i] = $node->header()->[$i];
|
391
|
|
|
|
|
|
|
}
|
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# There's probably a smarter way to handle the last insert and
|
394
|
|
|
|
|
|
|
# last key values, but this is the fastest, easiest, safest and
|
395
|
|
|
|
|
|
|
# most consistent way.
|
396
|
|
|
|
|
|
|
|
397
|
55
|
|
|
|
|
80
|
$self->{LASTINSRT} = undef;
|
398
|
55
|
|
|
|
|
137
|
$self->reset;
|
399
|
|
|
|
|
|
|
|
400
|
55
|
|
|
|
|
80
|
$self->{SIZE} --;
|
401
|
55
|
|
|
|
|
129
|
$self->_adjust_level_threshold;
|
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# We shouldn't need to "undef $node" here. The Garbage Collector
|
404
|
|
|
|
|
|
|
# should hanldle that (especially if there's a finger that points
|
405
|
|
|
|
|
|
|
# to it somewhere).
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Note: It doesn't seem to be a wise idea to return a search
|
408
|
|
|
|
|
|
|
# finger for deletions without further analysis
|
409
|
|
|
|
|
|
|
|
410
|
55
|
|
|
|
|
275
|
$value;
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
} else {
|
413
|
20
|
50
|
|
|
|
370
|
carp "key not found", if (warnings::enabled);
|
414
|
20
|
|
|
|
|
1851
|
return;
|
415
|
|
|
|
|
|
|
}
|
416
|
|
|
|
|
|
|
}
|
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
sub exists {
|
419
|
|
|
|
|
|
|
|
420
|
42
|
|
|
42
|
1
|
115
|
my ($self, $key, $finger) = @_;
|
421
|
|
|
|
|
|
|
|
422
|
42
|
|
|
|
|
137
|
(($self->_search($key, $finger))[2] == 0);
|
423
|
|
|
|
|
|
|
}
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub find_with_finger {
|
426
|
120
|
|
|
120
|
1
|
47262
|
my ($self, $key, $finger) = @_;
|
427
|
|
|
|
|
|
|
|
428
|
120
|
|
|
|
|
485
|
my ($x, $update_ref, $cmp) = $self->_search_with_finger($key, $finger);
|
429
|
|
|
|
|
|
|
|
430
|
120
|
100
|
|
|
|
734
|
($cmp == 0) ? (
|
|
|
100
|
|
|
|
|
|
431
|
|
|
|
|
|
|
(wantarray) ? ($x->value, $update_ref) : $x->value
|
432
|
|
|
|
|
|
|
) : undef;
|
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
}
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub find {
|
437
|
235
|
|
|
235
|
1
|
72030
|
my ($self, $key, $finger) = @_;
|
438
|
|
|
|
|
|
|
|
439
|
235
|
|
|
|
|
586
|
my ($node, $update_ref, $cmp) = $self->_search($key, $finger);
|
440
|
|
|
|
|
|
|
|
441
|
235
|
100
|
|
|
|
1039
|
($cmp == 0) ? $node->value : undef;
|
442
|
|
|
|
|
|
|
}
|
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
sub _first_node { # actually this is the second node
|
446
|
134
|
|
|
134
|
|
191
|
my $self = shift;
|
447
|
|
|
|
|
|
|
|
448
|
134
|
|
|
|
|
267
|
my $list = $self->list;
|
449
|
134
|
|
|
|
|
405
|
my $node = $list->header()->[0];
|
450
|
|
|
|
|
|
|
}
|
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub last_key {
|
454
|
154
|
|
|
154
|
1
|
228
|
my ($self, $node, $index) = @_;
|
455
|
|
|
|
|
|
|
|
456
|
154
|
100
|
|
|
|
586
|
if (@_ > 1) {
|
457
|
145
|
|
|
|
|
347
|
$self->{LASTKEY} = [ $node, $index ];
|
458
|
145
|
|
100
|
|
|
461
|
my $check = $index || 0;
|
459
|
145
|
50
|
33
|
|
|
514
|
if (($check < 0) || ($check >= $self->size)) {
|
460
|
0
|
0
|
|
|
|
0
|
carp "index out of bounds", if (warnings::enabled);
|
461
|
|
|
|
|
|
|
}
|
462
|
|
|
|
|
|
|
}
|
463
|
|
|
|
|
|
|
else {
|
464
|
9
|
50
|
|
|
|
29
|
unless ($self->{LASTKEY}) {
|
465
|
0
|
|
|
|
|
0
|
$self->{LASTKEY} = [ $self->_first_node, 0 ];
|
466
|
|
|
|
|
|
|
}
|
467
|
9
|
|
|
|
|
8
|
($node, $index) = @{ $self->{LASTKEY} };
|
|
9
|
|
|
|
|
21
|
|
468
|
|
|
|
|
|
|
}
|
469
|
|
|
|
|
|
|
|
470
|
154
|
100
|
|
|
|
307
|
if ($node) {
|
471
|
152
|
100
|
|
|
|
645
|
return (wantarray) ?
|
472
|
|
|
|
|
|
|
( $node->key, [ $node ], $node->value, $index ) : $node->key;
|
473
|
|
|
|
|
|
|
} else {
|
474
|
2
|
|
|
|
|
30
|
return;
|
475
|
|
|
|
|
|
|
}
|
476
|
|
|
|
|
|
|
}
|
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub first_key {
|
479
|
50
|
|
|
50
|
1
|
11771
|
my $self = shift;
|
480
|
|
|
|
|
|
|
|
481
|
50
|
|
|
|
|
103
|
my $node = $self->_first_node;
|
482
|
|
|
|
|
|
|
|
483
|
50
|
100
|
|
|
|
103
|
if ($node) {
|
484
|
47
|
|
|
|
|
114
|
return $self->last_key( $node, 0);
|
485
|
|
|
|
|
|
|
}
|
486
|
|
|
|
|
|
|
else {
|
487
|
3
|
50
|
|
|
|
195
|
carp "no _first_node", if (warnings::enabled);
|
488
|
3
|
|
|
|
|
243
|
return;
|
489
|
|
|
|
|
|
|
}
|
490
|
|
|
|
|
|
|
}
|
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
sub next_key {
|
493
|
86
|
|
|
86
|
1
|
15009
|
my ($self, $last_key, $finger) = @_;
|
494
|
|
|
|
|
|
|
|
495
|
86
|
|
|
|
|
116
|
my ($node, $cmp, $value, $index);
|
496
|
|
|
|
|
|
|
|
497
|
86
|
50
|
|
|
|
178
|
if (defined $last_key) {
|
498
|
0
|
|
|
|
|
0
|
($node, $finger, $cmp) = $self->_search_with_finger($last_key, $finger);
|
499
|
|
|
|
|
|
|
|
500
|
0
|
0
|
|
|
|
0
|
if ($cmp) {
|
501
|
0
|
0
|
|
|
|
0
|
carp "cannot find last_key", if (warnings::enabled);
|
502
|
0
|
|
|
|
|
0
|
return;
|
503
|
|
|
|
|
|
|
}
|
504
|
|
|
|
|
|
|
}
|
505
|
|
|
|
|
|
|
else {
|
506
|
86
|
100
|
|
|
|
119
|
($node, $index) = @{ $self->{LASTKEY} || [ ] };
|
|
86
|
|
|
|
|
367
|
|
507
|
86
|
100
|
|
|
|
210
|
unless ($node) {
|
508
|
25
|
|
|
|
|
50
|
return $self->first_key;
|
509
|
|
|
|
|
|
|
}
|
510
|
|
|
|
|
|
|
}
|
511
|
|
|
|
|
|
|
|
512
|
61
|
50
|
|
|
|
122
|
if ($node) {
|
513
|
61
|
|
|
|
|
189
|
$node = $node->header()->[0];
|
514
|
61
|
100
|
66
|
|
|
397
|
return $self->last_key(
|
515
|
|
|
|
|
|
|
$node,
|
516
|
|
|
|
|
|
|
(($node && (defined $index)) ? ($index+1) : undef )
|
517
|
|
|
|
|
|
|
);
|
518
|
|
|
|
|
|
|
}
|
519
|
|
|
|
|
|
|
else {
|
520
|
0
|
|
|
|
|
0
|
return $self->reset;
|
521
|
|
|
|
|
|
|
}
|
522
|
|
|
|
|
|
|
}
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
BEGIN
|
526
|
|
|
|
|
|
|
{
|
527
|
|
|
|
|
|
|
# make aliases to methods...
|
528
|
13
|
|
|
13
|
|
21786
|
no strict;
|
|
13
|
|
|
|
|
23
|
|
|
13
|
|
|
|
|
4358
|
|
529
|
13
|
|
|
13
|
|
44
|
*TIEHASH = \&new;
|
530
|
13
|
|
|
|
|
29
|
*STORE = \&insert;
|
531
|
13
|
|
|
|
|
24
|
*FETCH = \&find;
|
532
|
13
|
|
|
|
|
31
|
*EXISTS = \&exists;
|
533
|
13
|
|
|
|
|
31
|
*CLEAR = \&clear;
|
534
|
13
|
|
|
|
|
22
|
*DELETE = \&delete;
|
535
|
13
|
|
|
|
|
28
|
*FIRSTKEY = \&first_key;
|
536
|
13
|
|
|
|
|
31
|
*NEXTKEY = \&next_key;
|
537
|
|
|
|
|
|
|
|
538
|
13
|
|
|
|
|
450
|
*search = \&find;
|
539
|
|
|
|
|
|
|
}
|
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
1;
|
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
__END__
|