File Coverage

blib/lib/Tie/Hash/Array.pm
Criterion Covered Total %
statement 55 59 93.2
branch 21 26 80.7
condition 5 6 83.3
subroutine 14 16 87.5
pod 1 4 25.0
total 96 111 86.4


line stmt bran cond sub pod time code
1             package Tie::Hash::Array;
2              
3             =head1 NAME
4              
5             Tie::Hash::Array - a hash which is internally implemented as a sorted array
6              
7             =head1 SYNOPSIS
8              
9             use Tie::Hash::Array;
10              
11             tie my %hash, 'Tie::Hash::Array';
12             $hash{foo} = 'bar';
13              
14             my $object = new Foo;
15             $hash{$object} = 'You can also use objects as keys.';
16              
17             while ( my($key, $value) = each %hash ) {
18             $key->dwim($value) if ref $key && $key->can('dwim');
19             }
20              
21             =head1 DESCRIPTION
22              
23             Hashes tied to this class will interally be stored as an array alternately
24             containing keys and values, with its keys sorted in standard string comparison
25             order, that is, as L|perlop/"Equality Operators"> does.
26              
27             While the main purpose of this module is serving as a base class for
28             L, some of its side effects may also be useful by themselves:
29              
30             =over 4
31              
32             =item *
33              
34             L will return the contents in sorted order.
35              
36             =item *
37              
38             You can use objects as keys.
39             (Please note, however, that in this case the string representations of these
40             objects should stay constant, or to be exact, their string sorting order should
41             maintain stable, or else you might get undesired results.)
42              
43             =back
44              
45             =cut
46              
47 6     6   229243 use strict;
  6         15  
  6         264  
48 6     6   32 use vars '$VERSION';
  6         12  
  6         5131  
49              
50             $VERSION = 0.10;
51              
52             sub TIEHASH {
53 6     6   91 my $package = shift;
54 6 50       30 $package = ref $package if length ref $package;
55 6         39 bless [], $package;
56             }
57              
58             sub FETCH {
59 2948     2948   11319 my ( $self, $key ) = @_;
60 2948 100       6464 if ( defined $self->valid( $key, my $pos = $self->pos($key) ) ) {
61 2919         21594 $self->[ $pos + 1 ];
62             }
63 29         142 else { undef }
64             }
65              
66             sub STORE {
67 1525     1525   10893 my ( $self, $key, $value ) = @_;
68 1525 100       2839 if ( defined $self->exact( $key, my $pos = $self->pos($key) ) ) {
69 2         7 $self->[ $pos + 1 ] = $value;
70             }
71 1523         3001 else { $self->splice( $pos, 0, $key, $value ) }
72             }
73              
74             sub EXISTS {
75 1514     1514   78322 my ( $self, $key ) = @_;
76 1514 100       3073 defined( my $pos2 = $self->valid( $key, my $pos = $self->pos($key) ) )
77             or return '';
78 1485         4975 ( 2 + $pos2 - $pos ) >> 1;
79             }
80              
81             my %i;
82              
83             sub DELETE {
84 1460     1460   2069203 my ( $self, $key ) = @_;
85 1460         4750 my $pos = $self->pos($key);
86 1460 100       4988 if ( defined $self->exact( $key, $pos ) ) {
87 1436         4519 ( undef, my $value ) = $self->splice( $pos, 2 );
88 1436         20240 $value;
89             }
90 24         93 else { undef }
91             }
92              
93             sub CLEAR {
94 3     3   4314 my ($self) = @_;
95 3         21 delete $i{$self};
96 3         32 @$self = ();
97             }
98              
99             sub FIRSTKEY {
100 21     21   11635 my ($self) = @_;
101 21 100       82 return undef unless @$self;
102 20         238 $self->[ $i{$self} = 0 ];
103             }
104              
105             sub NEXTKEY {
106 4623     4623   984401 my ($self) = @_;
107 4623 100       20960 if ( ( my $i = $i{$self} += 2 ) < $#$self ) { $self->[$i] }
  4604         32662  
108             else {
109 19         55 delete $i{$self};
110 19         1350 undef;
111             }
112             }
113              
114 0     0   0 sub UNTIE { }
115              
116 6     6   4362 sub DESTROY { delete $i{+shift} }
117              
118             sub exact {
119 7359     7359 0 14069 my ( $self, $key, $pos ) = @_;
120 7359 100 100     35980 if ( $pos <= $#$self && $self->[$pos] eq $key ) { $pos }
  5796         17745  
121 1563         3644 else { undef }
122             }
123              
124             sub pos {
125 7470     7470 0 12236 my ( $self, $key ) = @_;
126 7470         9774 my $a = 0;
127 7470         9708 my $b = @$self;
128 7470   66     43933 while ( $a < $b && $a < $#$self ) { # perform a binary search
129 72015 100       202853 if ( $self->[ my $c = ( $a + $b >> 1 ) & ~1 ] lt $key ) { $a = $c + 2 }
  21165         91492  
130 50850         269168 else { $b = $c }
131             }
132 7470         23073 $a;
133             }
134              
135             sub splice {
136 2982     2982 0 7193 my ( $self, $pos, $length, @values ) = @_;
137 2982 100       10235 if ( defined $i{$self} ) {
138 1431 50       6197 $i{$self} -= $length if $pos <= $i{$self};
139 1431 50       5488 $i{$self} += @values if $pos < $i{$self};
140             }
141 2982         18616 splice @$self, $pos, $length, @values;
142             }
143              
144             *valid = \&exact;
145              
146             =head1 ADDITIONAL METHODS
147              
148             =head2 split_at
149              
150             my %smaller = tied(%hash)->split_at('foo');
151              
152             will delete all keys from C<%hash> which are asciibetically smaller than "foo"
153             (which needs not exist as a key itself) and return a list of the deleted keys
154             and values.
155              
156             =cut
157              
158             sub split_at {
159 0     0 1   my ( $self, $key ) = @_;
160 0 0         defined( my $pos = delete $i{$self} ) or return;
161 0           $self->splice( 0, $self->pos($key) );
162             }
163              
164             =head1 SUBCLASSING
165              
166             Please do not rely on the implementation details of this class for now,
167             since they may still be subject to change.
168              
169             If you'd like to subclass this module, please let me know;
170             perhaps we can agree on some standards then.
171              
172             =head1 AUTHOR
173              
174             Martin H. Sluka
175             mailto:perl@sluka.de
176             http://martin.sluka.de/
177              
178             =head1 BUGS
179              
180             None known so far.
181              
182             =head1 SUPPORT
183              
184             You can find documentation for this module with the perldoc command.
185              
186             perldoc Tie::Hash::Array
187              
188             You can also look for information at:
189              
190             =over 4
191              
192             =item * RT: CPAN's request tracker
193              
194             L
195              
196             =item * AnnoCPAN: Annotated CPAN documentation
197              
198             L
199              
200             =item * CPAN Ratings
201              
202             L
203              
204             =item * Search CPAN
205              
206             L
207              
208             =back
209              
210             =head1 COPYRIGHT & LICENCE
211              
212             This program is free software; you can redistribute
213             it and/or modify it under the same terms as Perl itself.
214              
215             The full text of the license can be found in the
216             LICENSE file included with this module.
217              
218             =head1 SEE ALSO
219              
220             L
221              
222             =cut
223              
224             1