File Coverage

blib/lib/Tie/Array/BoundedIndex.pm
Criterion Covered Total %
statement 61 66 92.4
branch 13 20 65.0
condition 8 11 72.7
subroutine 14 16 87.5
pod n/a
total 96 113 84.9


line stmt bran cond sub pod time code
1             package Tie::Array::BoundedIndex;
2 5     5   148404 use strict;
  5         11  
  5         199  
3 5     5   28 use warnings;
  5         9  
  5         169  
4 5     5   31 use Carp;
  5         15  
  5         517  
5              
6 5     5   4716 use Tie::Array;
  5         6524  
  5         427  
7              
8             our $VERSION = '0.06';
9              
10             BEGIN
11             {
12 5     5   11 $DB::single = 1;
13 5         322 eval "require Attribute::Handlers";
14 5 50       43563 return if $@;
15 5         47 Attribute::Handlers->import(autotie => { '__CALLER__::Bounded'
16             => __PACKAGE__ });
17             }
18              
19             # The underlying object contains the bounds and also an
20             # inner object that is the result of tying an array to
21             # Tie::StdArray. When the user performs an operation on
22             # the array tied to this class, it is passed on to the
23             # inner array after bounds checking and shifting the
24             # indices so that the inner array's indices start at 0
25             # and go up to -
26             sub TIEARRAY
27             {
28 4     4   2358 my ($class, %arg) = @_;
29 4         16 my ($upper, $lower) = delete @arg{qw(upper lower)};
30 4 50       14 croak "Illegal arguments in tie" if %arg;
31 4 50       18 croak "No upper bound for array" unless defined $upper;
32              
33 4   100     22 $lower ||= 0;
34              
35 4   33     26 /\D/ and croak "Array bound '$_' must be integer" for ($upper, $lower);
36              
37 4 50       11 croak "Upper bound < lower bound" if $upper < $lower;
38              
39 4         5 my @array;
40 4         23 my $inner = tie @array, 'Tie::StdArray';
41              
42 4         80 return bless { upper => $upper,
43             lower => $lower,
44             inner => $inner
45             }, $class;
46             }
47              
48             # Delegate anything we haven't overridden to the inner array,
49             # which, being tied to Tie::StdArray, knows what to do.
50             # In this class we only need to implement methods that have
51             # to adjust an array index.
52             sub AUTOLOAD
53             {
54 30     30   4388 (my $method = our $AUTOLOAD) =~ s/.*://;
55 30         47 my $self = shift;
56 30         663 $self->{inner}->$method(@_);
57             }
58              
59 0     0   0 sub DESTROY { }
60              
61             sub _bound_check
62             {
63 37     37   44 my ($self, $index) = @_;
64 37         39 my ($upper, $lower) = @{$self}{qw(upper lower)};
  37         72  
65              
66 37 100 100     1097 croak "Index $index out of range [$lower, $upper]"
67             if $index < $lower || $index > $upper;
68              
69 29         55 return $lower; # Convenience for several callers
70             }
71              
72             # Only need to implement methods that can increase the
73             # size of the array or store outside the bounds.
74              
75             sub STORE
76             {
77 10     10   806 my ($self, $index, $value) = @_;
78 10         21 my $lower = $self->_bound_check($index);
79 7         25 $self->{inner}->STORE($index - $lower, $value);
80             }
81              
82             sub FETCH
83             {
84 16     16   1669 my ($self, $index) = @_;
85 16         30 my $lower = $self->_bound_check($index);
86 16         53 $self->{inner}->FETCH($index - $lower);
87             }
88              
89             sub STORESIZE
90             {
91 0     0   0 my ($self, $size) = @_;
92 0         0 $self->_bound_check($size-1);
93 0         0 $self->{inner}->STORESIZE($size);
94             }
95              
96             sub EXTEND
97             {
98 8     8   33 my ($self, $newsize) = @_;
99              
100             # We may get called with a new size of 0, indicating that
101             # the caller doesn't want to extend the array at all.
102             # But since that would result in a bound check on
103             # - 1, we return at that point since otherwise
104             # we would generate an exception. Our arrays are
105             # guaranteed to have at least one elenment in them.
106              
107 8 100       18 return unless $newsize;
108 7         11 my $lower = $self->{lower};
109 7         16 $self->_bound_check($lower+$newsize-1);
110             }
111              
112             sub PUSH
113             {
114 4     4   1386 my ($self, @new) = @_;
115 4         17 $self->EXTEND($self->FETCHSIZE + @new);
116 2         9 $self->{inner}->PUSH(@new);
117             }
118              
119             sub UNSHIFT
120             {
121 1     1   67 my ($self, @new) = @_;
122 1         9 $self->EXTEND($self->FETCHSIZE + @new);
123 0         0 $self->{inner}->UNSHIFT(@new);
124             }
125              
126             sub SPLICE
127             {
128 4     4   2098 my $self = shift;
129 4         8 my $lower = $self->{lower};
130              
131 4         6 my $offset = shift;
132 4 50       14 defined($offset) or $offset = $lower;
133              
134 4         16 my $size = $self->FETCHSIZE;
135              
136 4 50       24 $offset < 0 and $offset = $size + $lower - $offset;
137 4         9 $self->_bound_check($offset);
138              
139 3   66     12 my $length = shift || $size - $offset + $lower;
140 3 50       24 $length < 0 and $length = $lower + $size - $offset + $length;
141 3 100       8 $length > $lower + $size - $offset and $length = $lower + $size - $offset;
142              
143 3         8 $self->EXTEND($size + @_ - $length);
144 2         9 $self->{inner}->SPLICE($offset - $lower, $length, @_)
145             }
146              
147              
148             1;
149              
150             __END__