File Coverage

blib/lib/Tie/RangeHash.pm
Criterion Covered Total %
statement 175 182 96.1
branch 111 124 89.5
condition 48 72 66.6
subroutine 33 33 100.0
pod 10 12 83.3
total 377 423 89.1


line stmt bran cond sub pod time code
1             package Algorithm::SkipList::StringRangeNode;
2            
3             require 5.006;
4            
5 2     2   151485 use strict;
  2         4  
  2         87  
6 2     2   9 use warnings; ## ::register __PACKAGE__;
  2         5  
  2         74  
7            
8 2     2   11 use Carp;
  2         8  
  2         174  
9             # no Carp::Assert;
10            
11 2     2   11 use base 'Algorithm::SkipList::Node';
  2         3  
  2         2085  
12            
13             sub key_cmp {
14 198     198   3092 my $self = shift;
15             # assert( UNIVERSAL::isa($self, __PACKAGE__) ), if DEBUG;
16            
17 198         455 my $left = $self->key;
18 198         810 my $right = shift;
19            
20 198 50       415 unless (defined $left) { return -1; }
  0         0  
21            
22 198 100       484 if ($right =~ /,/) {
23            
24 104 100       226 my ($lo, $hi) = map { $_ || "" } split /,/, $left;
  203         648  
25 104 100       281 $hi = "", unless (defined $hi);
26 104 100       181 my ($lr, $hr) = map { $_ || "" } split /,/, $right;
  190         457  
27 104 100       206 $hr = "", unless (defined $hr);
28            
29 104 100       260 my $lo_cmp = ($hr eq "") ?
    100          
    100          
30             (($lo ne "") ? -1 : -1 ) : # ?
31             (($lo ne "") ? ($lo cmp $hr) : -1);
32 104 100       230 my $lr_cmp = ($lr eq "") ?
    100          
    100          
33             (($lo ne "") ? 1 : 0 ) :
34             (($lo ne "") ? ($lo cmp $lr) : -1);
35 104 50       288 my $hi_cmp = ($lr eq "") ?
    100          
    100          
36             (($hi ne "") ? 1 : 1 ) :
37             (($hi ne "") ? ($hi cmp $lr) : 1);
38 104 100       240 my $hr_cmp = ($hr eq "") ?
    100          
    100          
39             (($hi ne "") ? -1 : 0 ) :
40             (($hi ne "") ? ($hi cmp $hr) : 1);
41            
42 104 100 100     1944 if ( (($lo_cmp==-1) && ($hi_cmp==1) && (!$lr_cmp) && (!$hr_cmp)) ||
    100 100        
    100 100        
      0        
      66        
      66        
      66        
      33        
      100        
      66        
      66        
43             ((!$lo_cmp) && (!$hi_cmp) && (!$lr_cmp) && (!$hr_cmp)) ){
44 36         101 return 0;
45             } elsif (($lo_cmp==1) && ($hi_cmp==1) &&
46             ($lr_cmp==1) && ($hr_cmp==1)) {
47 16         93 return 1;
48             } elsif (($lo_cmp==-1) && ($hi_cmp==-1) &&
49             ($lr_cmp==-1) && ($hr_cmp==-1)) {
50 46         135 return -1;
51             } else {
52 6         1029 confess "Overlapping ranges";
53             }
54            
55             } else {
56            
57 94         217 my ($lo, $hi) = split /,/, $left;
58            
59 94 100       206 my $lo_cmp = ($lo ne "") ? ($lo cmp $right) : -1;
60 94 100       162 my $hi_cmp = ($hi ne "") ? ($hi cmp $right) : 1;
61            
62             # assert( $hi_cmp >= $lo_cmp ), if DEBUG;
63            
64 94 100 100     391 if (($lo_cmp <= 0) && ($hi_cmp >= 0)) {
    100          
    50          
65 45         170 return 0;
66             } elsif ($hi_cmp < 0) {
67 43         150 return -1;
68             } elsif ($lo_cmp > 0) {
69 6         21 return 1;
70             }
71             }
72             }
73            
74            
75             1;
76            
77             package Algorithm::SkipList::NumericRangeNode;
78            
79             require 5.006;
80            
81 2     2   9308 use strict;
  2         6  
  2         138  
82 2     2   13 use warnings; # ::register __PACKAGE__;
  2         4  
  2         101  
83            
84 2     2   15 use Carp;
  2         4  
  2         140  
85             # no Carp::Assert;
86            
87 2     2   12 use base 'Algorithm::SkipList::Node';
  2         4  
  2         1063  
88            
89             sub key_cmp {
90 198     198   5082 my $self = shift;
91             # assert( UNIVERSAL::isa($self, __PACKAGE__) ), if DEBUG;
92            
93 198         407 my $left = $self->key;
94 198         791 my $right = shift;
95            
96 198 50       387 unless (defined $left) { return -1; }
  0         0  
97            
98 198 100       390 if ($right =~ /,/) {
99            
100 90 100       199 my ($lo, $hi) = map { $_ || "" } split /,/, $left;
  175         562  
101 90 100       191 $hi = "", unless (defined $hi);
102 90 100       237 my ($lr, $hr) = map { $_ || "" } split /,/, $right;
  158         443  
103 90 100       182 $hr = "", unless (defined $hr);
104            
105 90 100       237 my $lo_cmp = ($hr eq "") ?
    100          
    100          
106             (($lo ne "") ? -1 : -1 ) : # ?
107             (($lo ne "") ? ($lo <=> $hr) : -1);
108 90 100       212 my $lr_cmp = ($lr eq "") ?
    100          
    100          
109             (($lo ne "") ? 1 : 0 ) :
110             (($lo ne "") ? ($lo <=> $lr) : -1);
111 90 50       298 my $hi_cmp = ($lr eq "") ?
    100          
    100          
112             (($hi ne "") ? 1 : 1 ) :
113             (($hi ne "") ? ($hi <=> $lr) : 1);
114 90 100       587 my $hr_cmp = ($hr eq "") ?
    100          
    100          
115             (($hi ne "") ? -1 : 0 ) :
116             (($hi ne "") ? ($hi <=> $hr) : 1);
117            
118             # print join(" ", $hi, $hr, $lo_cmp, $lr_cmp, $hi_cmp, $hr_cmp), "\n";
119            
120 90 100 100     1461 if ( (($lo_cmp==-1) && ($hi_cmp==1) && (!$lr_cmp) && (!$hr_cmp)) ||
    100 66        
    100 66        
      0        
      66        
      66        
      66        
      33        
      66        
      66        
      33        
121             ((!$lo_cmp) && (!$hi_cmp) && (!$lr_cmp) && (!$hr_cmp)) ){
122 32         95 return 0;
123             } elsif (($lo_cmp==1) && ($hi_cmp==1) &&
124             ($lr_cmp==1) && ($hr_cmp==1)) {
125 6         20 return 1;
126             } elsif (($lo_cmp==-1) && ($hi_cmp==-1) &&
127             ($lr_cmp==-1) && ($hr_cmp==-1)) {
128 50         231 return -1;
129             } else {
130 2         436 confess "Overlapping ranges";
131             }
132            
133             } else {
134            
135 108         276 my ($lo, $hi) = split /,/, $left;
136            
137 108 50       191 $lo = "", unless (defined $lo);
138 108 100       257 $hi = "", unless (defined $hi);
139 108 50       161 $right = "", unless (defined $right);
140            
141 108 100       205 my $lo_cmp = ($lo ne "") ? ($lo <=> $right) : -1;
142 108 100       224 my $hi_cmp = ($hi ne "") ? ($hi <=> $right) : 1;
143            
144             # assert( $hi_cmp >= $lo_cmp ), if DEBUG;
145            
146 108 100 100     495 if (($lo_cmp <= 0) && ($hi_cmp >= 0)) {
    100          
    50          
147 55         549 return 0;
148             } elsif ($hi_cmp < 0) {
149 44         543 return -1;
150             } elsif ($lo_cmp > 0) {
151 9         189 return 1;
152             }
153             }
154             }
155            
156            
157             1;
158            
159             package Tie::RangeHash::TYPE_STRING;
160            
161 2     2   11 use base 'Algorithm::SkipList::StringRangeNode';
  2         3  
  2         1154  
162            
163             1;
164            
165             package Tie::RangeHash::TYPE_NUMBER;
166            
167 2     2   14 use base 'Algorithm::SkipList::NumericRangeNode';
  2         3  
  2         1002  
168            
169             1;
170            
171             package Tie::RangeHash;
172            
173             require 5.006;
174            
175 2     2   10 use strict;
  2         3  
  2         57  
176 2     2   9 use warnings; # ::register __PACKAGE__;
  2         3  
  2         76  
177            
178 2     2   9 use Carp;
  2         4  
  2         101  
179             # no Carp::Assert;
180 2     2   2342 use Algorithm::SkipList 1.02;
  2         67421  
  2         88  
181            
182             our $VERSION = '1.05';
183             # $VERSION = eval $VERSION;
184            
185 2     2   16 use constant TYPE_STRING => 'Algorithm::SkipList::StringRangeNode';
  2         4  
  2         111  
186 2     2   9 use constant TYPE_NUMBER => 'Algorithm::SkipList::NumericRangeNode';
  2         4  
  2         2472  
187            
188             # we use the full Exporter rather than something like Exporter::Lite
189             # because Algorithm::SkipList uses the full exporter.
190            
191             require Exporter;
192            
193             our @ISA = qw(Exporter);
194            
195             our @EXPORT = ();
196             our @EXPORT_OK = qw( TYPE_STRING TYPE_NUMBER );
197            
198             sub new {
199 5     5 1 5986 my $class = shift;
200            
201 5         81 my $self = {
202             SKIPLIST => undef,
203             NODECLASS => TYPE_STRING,
204             };
205            
206 5         13 bless $self, $class;
207            
208             {
209 5         7 my %ARGLIST = ( map { $_ => 1 } qw( Type ) );
  5         11  
  5         21  
210 5         7 my %args;
211            
212 5 50       15 if (ref($_[0]) eq "HASH") {
213 0         0 %args = %{$_[0]};
  0         0  
214             } else {
215 5         8 %args = @_;
216             }
217            
218 5         18 foreach my $arg_name (keys %args) {
219 2 50       6 if ($ARGLIST{$arg_name}) {
220 2         4 my $method = "_set_" . $arg_name;
221 2         11 $self->$method( $args{ $arg_name } );
222             } else {
223 0         0 croak "Invalid parameter name: ``$arg_name\'\'";
224             }
225             }
226             }
227            
228 5         53 $self->{SKIPLIST} = new Algorithm::SkipList(
229             node_class => $self->{NODECLASS},
230             );
231            
232 5         977 return $self;
233             }
234            
235             sub _set_Type {
236 2     2   3 my $self = shift;
237             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
238            
239 2         3 my $node_class = shift;
240 2         7 my $node = new $node_class;
241             # assert( UNIVERSAL::isa($node, "Algorithm::SkipList::Node" ) ), if DEBUG;
242            
243 2         68 $self->{NODECLASS} = $node_class;
244             }
245            
246             sub fetch {
247 70     70 1 24563 my $self = shift;
248             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
249 70         141 my $key = shift;
250 70         224 return $self->{SKIPLIST}->find( $key );
251             }
252            
253             sub fetch_key {
254 30     30 1 9998 my $self = shift;
255             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
256            
257 30         37 my $key = shift;
258 30         97 my ($x, $update_ref) = $self->{SKIPLIST}->_search($key);
259 30 50       201 if ($x->key_cmp($key) == 0) {
260 30 50       90 return (wantarray) ? ($x->key => $x->value) : $x->key;
261             } else {
262 0         0 return;
263             }
264             }
265            
266             sub fetch_overlap {
267 7     7 1 3573 my $self = shift;
268             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
269            
270 7         11 my $key = shift;
271 7         20 my ($low, $high) = split /,/, $key;
272            
273 7         210 my @nodes = $self->{SKIPLIST}->_search_nodes($low, undef, $high);
274 7 100       200 unless (@nodes) {
275 3         93 @nodes = $self->{SKIPLIST}->_search_nodes(undef, undef, $high);
276             }
277            
278 7 50       90 if (@nodes) {
279 7         12 return map { $_->value } @nodes;
  14         56  
280             }
281             else {
282 0         0 return;
283             }
284             }
285            
286             sub key_exists {
287 17     17 1 4926 my $self = shift;
288             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
289 17         29 my $key = shift;
290 17         62 $self->{SKIPLIST}->exists($key);
291             }
292            
293             sub add {
294 30     30 1 11388 my $self = shift;
295             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
296 30         57 my ($key, $value) = @_;
297 30         121 $self->{SKIPLIST}->insert($key, $value);
298             }
299            
300             sub clear {
301 3     3 1 870 my $self = shift;
302             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
303 3         15 $self->{SKIPLIST}->clear;
304             }
305            
306             sub remove {
307 14     14 1 4198 my $self = shift;
308             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
309 14         21 my $key = shift;
310            
311             # We could simply call $self->{SKIPLIST}->delete( $key ), but we
312             # want to make sure that the user has specified the exact key that
313             # is used (to keep compatability with previous versions)
314            
315 14         47 my ($x, $update_ref) = $self->{SKIPLIST}->_search($key);
316 13 100       93 if ($x->key eq $key) {
317 12         86 return $self->{SKIPLIST}->delete( $key );
318             } else {
319 1         10 return;
320             }
321             }
322            
323             sub first_key {
324 2     2 1 67 my $self = shift;
325             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
326 2         7 return $self->{SKIPLIST}->first_key;
327             }
328            
329             sub next_key {
330 14     14 1 7071 my $self = shift;
331             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
332 14         17 my $last_key = shift;
333 14         51 return $self->{SKIPLIST}->next_key( $last_key );
334             }
335            
336             sub reset {
337 2     2 0 57 my $self = shift;
338             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
339 2         10 $self->{SKIPLIST}->reset;
340             }
341            
342             sub size {
343 24     24 0 5327 my $self = shift;
344             # assert( UNIVERSAL::isa($self, "Tie::RangeHash") ), if DEBUG;
345 24         70 return $self->{SKIPLIST}->size;
346             }
347            
348            
349             BEGIN
350             {
351             # make aliases to methods...
352 2     2   13 no strict;
  2         3  
  2         257  
353 2     2   7 *TIEHASH = \&new;
354 2         5 *STORE = \&add;
355 2         4 *FETCH = \&fetch;
356 2         4 *EXISTS = \&key_exists;
357 2         6 *CLEAR = \*clear;
358 2         3 *DELETE = \*remove;
359 2         5 *FIRSTKEY = \*first_key;
360 2         80 *NEXTKEY = \*next_key;
361             }
362            
363             1;
364            
365             __END__