Assignment to a negative value within the bounds of the array works
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Array.pm
CommitLineData
6fe26b29 1package DBM::Deep::Array;
2
7f441181 3$NEGATIVE_INDICES = 1;
4
6fe26b29 5use strict;
6
7use base 'DBM::Deep';
8
e1b265cc 9use Scalar::Util ();
10
596e9574 11sub _get_self {
12 eval { tied( @{$_[0]} ) } || $_[0]
13}
14
6fe26b29 15sub TIEARRAY {
16##
17# Tied array constructor method, called by Perl's tie() function.
18##
19 my $class = shift;
20 my $args;
217fef02 21 if (scalar(@_) > 1) {
22 if ( @_ % 2 ) {
23 $class->_throw_error( "Odd number of parameters to TIEARRAY" );
24 }
25 $args = {@_};
26 }
e1b265cc 27 elsif ( my $type = Scalar::Util::reftype($_[0]) ) {
28 if ( $type ne 'HASH' ) {
29 $class->_throw_error( "Not a hashref in TIEARRAY" );
30 }
31 $args = $_[0];
32 }
33 else {
34 $args = { file => shift };
35 }
6fe26b29 36
37 $args->{type} = $class->TYPE_ARRAY;
38
39 return $class->_init($args);
40}
41
7f441181 42sub FETCH {
43 my $self = $_[0]->_get_self;
44 my $key = $_[1];
45
46 if ( $key =~ /^-?\d+$/ ) {
47 if ( $key < 0 ) {
48 $key += $self->FETCHSIZE;
49 return unless $key >= 0;
50 }
51
52 $key = pack($DBM::Deep::LONG_PACK, $key);
53 }
54
55 return $self->SUPER::FETCH( $key );
56}
57
cb79ec85 58sub STORE {
59 my $self = shift->_get_self;
60 my ($key, $value) = @_;
61
62 my $unpacked_key = $key;
63 my $size = $self->FETCHSIZE;
64
65 my $numeric_idx;
66 if ( $key =~ /^-?\d+$/ ) {
67 $numeric_idx = 1;
68 if ( $key < 0 ) {
69 $key += $size;
70 #XXX What to do here?
71# return unless $key >= 0;
72 }
73
74 $key = pack($DBM::Deep::LONG_PACK, $key);
75 }
76
77 my $rv = $self->SUPER::STORE( $key, $value );
78
79 if ( $numeric_idx && $rv == 2 && $unpacked_key >= $size ) {
80 $self->STORESIZE( $unpacked_key + 1 );
81 }
82
83 return $rv;
84}
85
6fe26b29 86sub FETCHSIZE {
87 ##
88 # Return the length of the array
89 ##
2ac02042 90 my $self = $_[0]->_get_self;
6fe26b29 91
92 my $SAVE_FILTER = $self->root->{filter_fetch_value};
93 $self->root->{filter_fetch_value} = undef;
94
95 my $packed_size = $self->FETCH('length');
96
97 $self->root->{filter_fetch_value} = $SAVE_FILTER;
98
7f441181 99 if ($packed_size) {
100 return int(unpack($DBM::Deep::LONG_PACK, $packed_size));
101 }
cb79ec85 102
103 return 0;
6fe26b29 104}
105
106sub STORESIZE {
107 ##
108 # Set the length of the array
109 ##
2ac02042 110 my $self = $_[0]->_get_self;
6fe26b29 111 my $new_length = $_[1];
112
113 my $SAVE_FILTER = $self->root->{filter_store_value};
114 $self->root->{filter_store_value} = undef;
115
116 my $result = $self->STORE('length', pack($DBM::Deep::LONG_PACK, $new_length));
117
118 $self->root->{filter_store_value} = $SAVE_FILTER;
119
120 return $result;
121}
122
123sub POP {
124 ##
125 # Remove and return the last element on the array
126 ##
2ac02042 127 my $self = $_[0]->_get_self;
6fe26b29 128 my $length = $self->FETCHSIZE();
129
130 if ($length) {
131 my $content = $self->FETCH( $length - 1 );
132 $self->DELETE( $length - 1 );
133 return $content;
134 }
135 else {
136 return;
137 }
138}
139
140sub PUSH {
141 ##
142 # Add new element(s) to the end of the array
143 ##
2ac02042 144 my $self = shift->_get_self;
6fe26b29 145 my $length = $self->FETCHSIZE();
146
147 while (my $content = shift @_) {
148 $self->STORE( $length, $content );
149 $length++;
150 }
8f6d6ed0 151
152 return $length;
6fe26b29 153}
154
155sub SHIFT {
156 ##
157 # Remove and return first element on the array.
158 # Shift over remaining elements to take up space.
159 ##
2ac02042 160 my $self = $_[0]->_get_self;
6fe26b29 161 my $length = $self->FETCHSIZE();
162
163 if ($length) {
164 my $content = $self->FETCH( 0 );
165
166 ##
167 # Shift elements over and remove last one.
168 ##
169 for (my $i = 0; $i < $length - 1; $i++) {
170 $self->STORE( $i, $self->FETCH($i + 1) );
171 }
172 $self->DELETE( $length - 1 );
173
174 return $content;
175 }
176 else {
177 return;
178 }
179}
180
181sub UNSHIFT {
182 ##
183 # Insert new element(s) at beginning of array.
184 # Shift over other elements to make space.
185 ##
2ac02042 186 my $self = shift->_get_self;
6fe26b29 187 my @new_elements = @_;
188 my $length = $self->FETCHSIZE();
189 my $new_size = scalar @new_elements;
190
191 if ($length) {
192 for (my $i = $length - 1; $i >= 0; $i--) {
193 $self->STORE( $i + $new_size, $self->FETCH($i) );
194 }
195 }
196
197 for (my $i = 0; $i < $new_size; $i++) {
198 $self->STORE( $i, $new_elements[$i] );
199 }
8f6d6ed0 200
201 return $length + $new_size;
6fe26b29 202}
203
204sub SPLICE {
205 ##
206 # Splices section of array with optional new section.
207 # Returns deleted section, or last element deleted in scalar context.
208 ##
2ac02042 209 my $self = shift->_get_self;
6fe26b29 210 my $length = $self->FETCHSIZE();
211
212 ##
213 # Calculate offset and length of splice
214 ##
215 my $offset = shift || 0;
216 if ($offset < 0) { $offset += $length; }
217
218 my $splice_length;
219 if (scalar @_) { $splice_length = shift; }
220 else { $splice_length = $length - $offset; }
221 if ($splice_length < 0) { $splice_length += ($length - $offset); }
222
223 ##
224 # Setup array with new elements, and copy out old elements for return
225 ##
226 my @new_elements = @_;
227 my $new_size = scalar @new_elements;
228
229 my @old_elements = ();
230 for (my $i = $offset; $i < $offset + $splice_length; $i++) {
231 push @old_elements, $self->FETCH( $i );
232 }
233
234 ##
235 # Adjust array length, and shift elements to accomodate new section.
236 ##
237 if ( $new_size != $splice_length ) {
238 if ($new_size > $splice_length) {
239 for (my $i = $length - 1; $i >= $offset + $splice_length; $i--) {
240 $self->STORE( $i + ($new_size - $splice_length), $self->FETCH($i) );
241 }
242 }
243 else {
244 for (my $i = $offset + $splice_length; $i < $length; $i++) {
245 $self->STORE( $i + ($new_size - $splice_length), $self->FETCH($i) );
246 }
247 for (my $i = 0; $i < $splice_length - $new_size; $i++) {
248 $self->DELETE( $length - 1 );
249 $length--;
250 }
251 }
252 }
253
254 ##
255 # Insert new elements into array
256 ##
257 for (my $i = $offset; $i < $offset + $new_size; $i++) {
258 $self->STORE( $i, shift @new_elements );
259 }
260
261 ##
262 # Return deleted section, or last element in scalar context.
263 ##
264 return wantarray ? @old_elements : $old_elements[-1];
265}
266
267#XXX We don't need to define it.
268#XXX It will be useful, though, when we split out HASH and ARRAY
269#sub EXTEND {
270 ##
271 # Perl will call EXTEND() when the array is likely to grow.
272 # We don't care, but include it for compatibility.
273 ##
274#}
275
276##
277# Public method aliases
278##
279*length = *FETCHSIZE;
280*pop = *POP;
281*push = *PUSH;
282*shift = *SHIFT;
283*unshift = *UNSHIFT;
284*splice = *SPLICE;
285
2861;
287__END__