| 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__ |