File Coverage

blib/lib/Tie/Array/Unique.pm
Criterion Covered Total %
statement 65 70 92.8
branch 32 44 72.7
condition 14 15 93.3
subroutine 14 17 82.3
pod 0 1 0.0
total 125 147 85.0


line stmt bran cond sub pod time code
1             package Tie::Array::Unique;
2              
3 1     1   30270 use constant DIFF => 2;
  1         2  
  1         76  
4 1     1   5 use Carp;
  1         1  
  1         1178  
5              
6             $VERSION = '0.01';
7              
8              
9             sub TIEARRAY {
10 2     2   18 my $class = shift;
11 2         9 my $self = bless [ {}, 0 ], $class;
12              
13 2 100       13 $self->[1] = shift @_
14             if UNIVERSAL::isa($_[0], 'Tie::Array::Unique::How');
15              
16             $self->[0]{ $self->[1] ? $self->[1]->($_) : $_ }++ or push @$self, $_
17 2 50 100     52 for @_;
18              
19 2         6 return $self;
20             }
21              
22              
23             sub CLEAR {
24 3     3   8 my ($self) = @_;
25 3         8 splice @$self, 2;
26 3         15 $self->[0] = {};
27             }
28              
29              
30             sub FETCHSIZE {
31 14     14   66 my ($self) = @_;
32 14         83 return @$self - DIFF;
33             }
34              
35              
36             sub STORESIZE {
37 0     0   0 croak "STORESIZE not implemented on unique arrays";
38             }
39              
40              
41 3     3   13 sub EXTEND { }
42              
43              
44             sub FETCH {
45 84     84   108 my ($self, $idx) = @_;
46 84         310 return $self->[$idx + DIFF];
47             }
48              
49              
50             sub STORE {
51 14     14   24 my ($self, $idx, $value) = @_;
52              
53             # $old is true if this is not a new index to the array
54 14         23 my $old = $idx < @$self - DIFF;
55              
56             # $dup is true if this value will be a duplicate
57 14 100       46 my $dup = $self->[0]{ $self->[1] ? $self->[1]->($value) : $value };
58              
59             # if we're adding a NEW element and it's a duplicate, don't bother
60 14 100 100     70 return $value if $dup and not $old;
61              
62 11         22 my $r = \$self->[$idx + DIFF];
63              
64             # remove old element
65 11 100       33 delete $self->[0]{ $self->[1] ? $self->[1]->($$r) : $$r } if $old;
    100          
66              
67 11         21 $$r = $value;
68              
69             # add new one
70 11 100       30 $self->[0]{ $self->[1] ? $self->[1]->($$r) : $$r } = 1;
71              
72             # if it was a duplicate, get rid of the older value
73 11 100       32 if ($dup) {
74 3         10 for (my $i = 2; $i < @$self; ++$i) {
75 9 100 100     51 if (($self->[1] ? $self->[1]->($self->[$i]) : $self->[$i]) eq ($self->[1] ? $self->[1]->($value) : $value) and $i != DIFF + $idx) {
    100          
    100          
76 2         3 splice @$self, $i, 1;
77 2         24 last;
78             }
79             }
80             }
81              
82 11         61 return $value;
83             }
84              
85              
86             sub PUSH {
87 2     2   5 my $self = shift;
88              
89 2 50 100     34 $self->[0]{ $self->[1] ? $self->[1]->($_) : $_ }++ or push @$self, $_ for @_;
90 2         8 return @$self - DIFF;
91             }
92              
93              
94             sub POP {
95 1     1   3 my ($self) = @_;
96 1 50       4 return if @$self == DIFF;
97              
98             # remove element
99 1 50       6 delete $self->[0]{ $self->[1] ? $self->[1]->($self->[-1]) : $self->[-1] };
100 1         4 return pop @$self;
101             }
102              
103              
104             sub UNSHIFT {
105 1     1   3 my $self = shift;
106             $self->[0]{ $self->[1] ? $self->[1]->($_) : $_ }++
107 1 50 100     30 or splice @$self, DIFF, 0, $_ for reverse @_;
108 1         4 return @$self - DIFF;
109             }
110              
111              
112             sub SHIFT {
113 1     1   2 my ($self) = @_;
114 1 50       27 return if @$self == DIFF;
115              
116             # remove element
117 1 50       6 delete $self->[0]{ $self->[1] ? $self->[1]->($self->[DIFF]) : $self->[DIFF] };
118 1         6 return splice @$self, DIFF, 1;
119             }
120              
121              
122             sub SPLICE {
123 1     1   3 my $self = shift;
124 1         2 my $idx = DIFF + shift;
125 1         2 my $len = shift;
126              
127 1 50       6 my @removed = defined($len) ?
128             splice(@$self, $idx, $len) :
129             splice(@$self, $idx);
130 1 50       3 delete @{$self->[0]}{map { $self->[1] ? $self->[1]->($_) : $_ } @removed};
  1         3  
  2         10  
131              
132 1 50       12 if (@_) {
133 1         3 my %seen;
134 1   66     33 my @replace = grep !$seen{ $self->[1] ? $self->[1]->($_) : $_ }++ && !$self->[0]{ $self->[1] ? $self->[1]->($_) : $_ }, @_;
135 1         3 splice @$self, $idx, 0, @replace;
136 1 50       5 @{$self->[0]}{map { $self->[1] ? $self->[1]->($_) : $_ } @replace} = (1) x @replace;
  1         5  
  2         7  
137             }
138              
139 1         4 return @removed;
140             }
141              
142              
143             sub DEFINED {
144 0     0 0 0 my ($self, $idx) = @_;
145 0         0 return defined $self->[$idx + DIFF];
146             }
147              
148              
149             sub EXISTS {
150 0     0   0 my ($self, $idx) = @_;
151 0         0 return exists $self->[$idx + DIFF];
152             }
153              
154              
155              
156             package Tie::Array::Unique::How;
157              
158             sub new {
159 1     1   2 my $class = shift;
160 1 50       6 Carp::croak("How->new() argument must be code ref")
161             unless UNIVERSAL::isa($_[0], "CODE");
162 1         6 bless $_[0], $class;
163             }
164            
165              
166             1;
167              
168             __END__