File Coverage

inc/Tie/IxHash.pm
Criterion Covered Total %
statement 27 212 12.7
branch 5 58 8.6
condition 0 6 0.0
subroutine 6 28 21.4
pod 14 19 73.6
total 52 323 16.1


line stmt bran cond sub pod time code
1             #line 1
2             #
3             # Tie/IxHash.pm
4             #
5             # Indexed hash implementation for Perl
6             #
7             # See below for documentation.
8             #
9              
10             require 5.003;
11              
12 1     1   1737 package Tie::IxHash;
  1         11  
  1         5  
13             use integer;
14             require Tie::Hash;
15             @ISA = qw(Tie::Hash);
16              
17             $VERSION = $VERSION = '1.21';
18              
19             #
20             # standard tie functions
21             #
22              
23 9     9   58 sub TIEHASH {
24 9         15 my($c) = shift;
25 9         20 my($s) = [];
26 9         13 $s->[0] = {}; # hashkey index
27 9         11 $s->[1] = []; # array of keys
28 9         16 $s->[2] = []; # array of data
29             $s->[3] = 0; # iter count
30 9         14  
31             bless $s, $c;
32 9 50       24  
33             $s->Push(@_) if @_;
34 9         26  
35             return $s;
36             }
37              
38             #sub DESTROY {} # costly if there's nothing to do
39              
40 13     13   28 sub FETCH {
41 13 50       71 my($s, $k) = (shift, shift);
42             return exists( $s->[0]{$k} ) ? $s->[2][ $s->[0]{$k} ] : undef;
43             }
44              
45 13     13   95 sub STORE {
46             my($s, $k, $v) = (shift, shift, shift);
47 13 50       28
48 0         0 if (exists $s->[0]{$k}) {
49 0         0 my($i) = $s->[0]{$k};
50 0         0 $s->[1][$i] = $k;
51 0         0 $s->[2][$i] = $v;
52             $s->[0]{$k} = $i;
53             }
54 13         14 else {
  13         28  
55 13         17 push(@{$s->[1]}, $k);
  13         22  
56 13         17 push(@{$s->[2]}, $v);
  13         62  
57             $s->[0]{$k} = $#{$s->[1]};
58             }
59             }
60              
61 0     0   0 sub DELETE {
62             my($s, $k) = (shift, shift);
63 0 0       0  
64 0         0 if (exists $s->[0]{$k}) {
65 0         0 my($i) = $s->[0]{$k};
  0         0  
66 0         0 for ($i+1..$#{$s->[1]}) { # reset higher elt indexes
67             $s->[0]{$s->[1][$_]}--; # timeconsuming, is there is better way?
68 0         0 }
69 0         0 delete $s->[0]{$k};
  0         0  
70 0         0 splice @{$s->[1]}, $i, 1;
  0         0  
71             return (splice(@{$s->[2]}, $i, 1))[0];
72 0         0 }
73             return undef;
74             }
75              
76 0     0   0 sub EXISTS {
77             exists $_[0]->[0]{ $_[1] };
78             }
79              
80 18     18   141 sub FIRSTKEY {
81 18         33 $_[0][3] = 0;
82             &NEXTKEY;
83             }
84              
85 31 100   31   40 sub NEXTKEY {
  31         124  
86 18         54 return $_[0][1][$_[0][3]++] if ($_[0][3] <= $#{$_[0][1]});
87             return undef;
88             }
89              
90              
91              
92             #
93             #
94             # class functions that provide additional capabilities
95             #
96             #
97 0     0 0    
98             sub new { TIEHASH(@_) }
99              
100             #
101             # add pairs to end of indexed hash
102             # note that if a supplied key exists, it will not be reordered
103             #
104 0     0 1   sub Push {
105 0           my($s) = shift;
106 0           while (@_) {
107             $s->STORE(shift, shift);
108 0           }
  0            
109             return scalar(@{$s->[1]});
110             }
111              
112 0     0 0   sub Push2 {
113 0           my($s) = shift;
  0            
114 0           $s->Splice($#{$s->[1]}+1, 0, @_);
  0            
115             return scalar(@{$s->[1]});
116             }
117              
118             #
119             # pop last k-v pair
120             #
121 0     0 1   sub Pop {
122 0           my($s) = shift;
123 0           my($k, $v, $i);
  0            
124 0           $k = pop(@{$s->[1]});
  0            
125 0 0         $v = pop(@{$s->[2]});
126 0           if (defined $k) {
127 0           delete $s->[0]{$k};
128             return ($k, $v);
129 0           }
130             return undef;
131             }
132              
133 0     0 0   sub Pop2 {
134             return $_[0]->Splice(-1);
135             }
136              
137             #
138             # shift
139             #
140 0     0 1   sub Shift {
141 0           my($s) = shift;
142 0           my($k, $v, $i);
  0            
143 0           $k = shift(@{$s->[1]});
  0            
144 0 0         $v = shift(@{$s->[2]});
145 0           if (defined $k) {
146 0           delete $s->[0]{$k};
  0            
147 0           for (keys %{$s->[0]}) {
148             $s->[0]{$_}--;
149 0           }
150             return ($k, $v);
151 0           }
152             return undef;
153             }
154              
155 0     0 0   sub Shift2 {
156             return $_[0]->Splice(0, 1);
157             }
158              
159             #
160             # unshift
161             # if a supplied key exists, it will not be reordered
162             #
163 0     0 1   sub Unshift {
164 0           my($s) = shift;
165             my($k, $v, @k, @v, $len, $i);
166 0            
167 0           while (@_) {
168 0 0         ($k, $v) = (shift, shift);
169 0           if (exists $s->[0]{$k}) {
170 0           $i = $s->[0]{$k};
171 0           $s->[1][$i] = $k;
172 0           $s->[2][$i] = $v;
173             $s->[0]{$k} = $i;
174             }
175 0           else {
176 0           push(@k, $k);
177 0           push(@v, $v);
178             $len++;
179             }
180 0 0         }
181 0           if (defined $len) {
  0            
182 0           for (keys %{$s->[0]}) {
183             $s->[0]{$_} += $len;
184 0           }
185 0           $i = 0;
186 0           for (@k) {
187             $s->[0]{$_} = $i++;
188 0           }
  0            
189 0           unshift(@{$s->[1]}, @k);
  0            
190             return unshift(@{$s->[2]}, @v);
191 0           }
  0            
192             return scalar(@{$s->[1]});
193             }
194              
195 0     0 0   sub Unshift2 {
196 0           my($s) = shift;
197 0           $s->Splice(0,0,@_);
  0            
198             return scalar(@{$s->[1]});
199             }
200              
201             #
202             # splice
203             #
204             # any existing hash key order is preserved. the value is replaced for
205             # such keys, and the new keys are spliced in the regular fashion.
206             #
207             # supports -ve offsets but only +ve lengths
208             #
209             # always assumes a 0 start offset
210             #
211 0     0 1   sub Splice {
212 0           my($s, $start, $len) = (shift, shift, shift);
213 0           my($k, $v, @k, @v, @r, $i, $siz);
214             my($end); # inclusive
215              
216 0           # XXX inline this
217             ($start, $end, $len) = $s->_lrange($start, $len);
218 0 0          
219 0 0         if (defined $start) {
220 0           if ($len > 0) {
  0            
221 0           my(@k) = splice(@{$s->[1]}, $start, $len);
  0            
222 0           my(@v) = splice(@{$s->[2]}, $start, $len);
223 0           while (@k) {
224 0           $k = shift(@k);
225 0           delete $s->[0]{$k};
226             push(@r, $k, shift(@v));
227 0           }
  0            
228 0           for ($start..$#{$s->[1]}) {
229             $s->[0]{$s->[1][$_]} -= $len;
230             }
231 0           }
232 0           while (@_) {
233 0 0         ($k, $v) = (shift, shift);
234             if (exists $s->[0]{$k}) {
235 0           # $s->STORE($k, $v);
236 0           $i = $s->[0]{$k};
237 0           $s->[1][$i] = $k;
238 0           $s->[2][$i] = $v;
239             $s->[0]{$k} = $i;
240             }
241 0           else {
242 0           push(@k, $k);
243 0           push(@v, $v);
244             $siz++;
245             }
246 0 0         }
247 0           if (defined $siz) {
  0            
248 0           for ($start..$#{$s->[1]}) {
249             $s->[0]{$s->[1][$_]} += $siz;
250 0           }
251 0           $i = $start;
252 0           for (@k) {
253             $s->[0]{$_} = $i++;
254 0           }
  0            
255 0           splice(@{$s->[1]}, $start, 0, @k);
  0            
256             splice(@{$s->[2]}, $start, 0, @v);
257             }
258 0           }
259             return @r;
260             }
261              
262             #
263             # delete elements specified by key
264             # other elements higher than the one deleted "slide" down
265             #
266 0     0 1   sub Delete {
267             my($s) = shift;
268 0            
269             for (@_) {
270             #
271             # XXX potential optimization: could do $s->DELETE only if $#_ < 4.
272             # otherwise, should reset all the hash indices in one loop
273 0           #
274             $s->DELETE($_);
275             }
276             }
277              
278             #
279             # replace hash element at specified index
280             #
281             # if the optional key is not supplied the value at index will simply be
282             # replaced without affecting the order.
283             #
284             # if an element with the supplied key already exists, it will be deleted first.
285             #
286             # returns the key of replaced value if it succeeds.
287             #
288 0     0 1   sub Replace {
289 0           my($s) = shift;
290 0 0 0       my($i, $v, $k) = (shift, shift, shift);
  0   0        
291 0 0         if (defined $i and $i <= $#{$s->[1]} and $i >= 0) {
292 0           if (defined $k) {
293 0           delete $s->[0]{ $s->[1][$i] };
294 0           $s->DELETE($k) ; #if exists $s->[0]{$k};
295 0           $s->[1][$i] = $k;
296 0           $s->[2][$i] = $v;
297 0           $s->[0]{$k} = $i;
298             return $k;
299             }
300 0           else {
301 0           $s->[2][$i] = $v;
302             return $s->[1][$i];
303             }
304 0           }
305             return undef;
306             }
307              
308             #
309             # Given an $start and $len, returns a legal start and end (where start <= end)
310             # for the current hash.
311             # Legal range is defined as 0 to $#s+1
312             # $len defaults to number of elts upto end of list
313             #
314             # 0 1 2 ...
315             # | X | X | X ... X | X | X |
316             # -2 -1 (no -0 alas)
317             # X's above are the elements
318             #
319 0     0     sub _lrange {
320 0           my($s) = shift;
321 0           my($offset, $len) = @_;
322 0           my($start, $end); # both inclusive
  0            
323             my($size) = $#{$s->[1]}+1;
324 0 0          
325 0 0         return undef unless defined $offset;
326 0           if($offset < 0) {
327 0 0         $start = $offset + $size;
328             $start = 0 if $start < 0;
329             }
330 0 0         else {
331             ($offset > $size) ? ($start = $size) : ($start = $offset);
332             }
333 0 0          
334 0 0         if (defined $len) {
335 0 0         $len = -$len if $len < 0;
336             $len = $size - $start if $len > $size - $start;
337             }
338 0           else {
339             $len = $size - $start;
340 0           }
341             $end = $start + $len - 1;
342 0            
343             return ($start, $end, $len);
344             }
345              
346             #
347             # Return keys at supplied indices
348             # Returns all keys if no args.
349             #
350 0     0 1   sub Keys {
351 0           my($s) = shift;
352             return ( @_ == 1
353             ? $s->[1][$_[0]]
354 0           : ( @_
355 0 0         ? @{$s->[1]}[@_]
    0          
356             : @{$s->[1]} ) );
357             }
358              
359             #
360             # Returns values at supplied indices
361             # Returns all values if no args.
362             #
363 0     0 1   sub Values {
364 0           my($s) = shift;
365             return ( @_ == 1
366             ? $s->[2][$_[0]]
367 0           : ( @_
368 0 0         ? @{$s->[2]}[@_]
    0          
369             : @{$s->[2]} ) );
370             }
371              
372             #
373             # get indices of specified hash keys
374             #
375 0     0 1   sub Indices {
376 0 0         my($s) = shift;
  0            
377             return ( @_ == 1 ? $s->[0]{$_[0]} : @{$s->[0]}{@_} );
378             }
379              
380             #
381             # number of k-v pairs in the ixhash
382             # note that this does not equal the highest index
383             # owing to preextended arrays
384             #
385 0     0 1   sub Length {
  0            
386             return scalar @{$_[0]->[1]};
387             }
388              
389             #
390             # Reorder the hash in the supplied key order
391             #
392             # warning: any unsupplied keys will be lost from the hash
393             # any supplied keys that dont exist in the hash will be ignored
394             #
395 0     0 1   sub Reorder {
396 0           my($s) = shift;
397 0 0         my(@k, @v, %x, $i);
398             return unless @_;
399 0            
400 0           $i = 0;
401 0 0         for (@_) {
402 0           if (exists $s->[0]{$_}) {
403 0           push(@k, $_);
404 0           push(@v, $s->[2][ $s->[0]{$_} ] );
405             $x{$_} = $i++;
406             }
407 0           }
408 0           $s->[1] = \@k;
409 0           $s->[2] = \@v;
410 0           $s->[0] = \%x;
411             return $s;
412             }
413              
414 0     0 1   sub SortByKey {
415 0           my($s) = shift;
416             $s->Reorder(sort $s->Keys);
417             }
418              
419 0     0 1   sub SortByValue {
420 0           my($s) = shift;
  0            
421             $s->Reorder(sort { $s->FETCH($a) cmp $s->FETCH($b) } $s->Keys)
422             }
423              
424             1;
425             __END__