added key to _get_subloc after figuring out the correct unpack magic
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Array.pm
CommitLineData
6fe26b29 1package DBM::Deep::Array;
2
3use strict;
4
8fec41b9 5# This is to allow DBM::Deep::Array to handle negative indices on
6# its own. Otherwise, Perl would intercept the call to negative
7# indices for us. This was causing bugs for negative index handling.
7910cf68 8use vars qw( $NEGATIVE_INDICES );
9$NEGATIVE_INDICES = 1;
10
6fe26b29 11use base 'DBM::Deep';
12
e1b265cc 13use Scalar::Util ();
14
596e9574 15sub _get_self {
75be6413 16 #eval { local $SIG{'__DIE__'}; tied( @{$_[0]} ) } || $_[0]
17 eval { tied( @{$_[0]} ) } || $_[0]
596e9574 18}
19
6fe26b29 20sub TIEARRAY {
21##
22# Tied array constructor method, called by Perl's tie() function.
23##
24 my $class = shift;
0ca7ea98 25 my $args = $class->_get_args( @_ );
6fe26b29 26
27 $args->{type} = $class->TYPE_ARRAY;
28
29 return $class->_init($args);
30}
31
7f441181 32sub FETCH {
33 my $self = $_[0]->_get_self;
34 my $key = $_[1];
35
9281d66b 36 $self->lock( $self->LOCK_SH );
37
7f441181 38 if ( $key =~ /^-?\d+$/ ) {
39 if ( $key < 0 ) {
40 $key += $self->FETCHSIZE;
9281d66b 41 unless ( $key >= 0 ) {
42 $self->unlock;
43 return;
44 }
7f441181 45 }
46
612969fb 47 $key = pack($self->{engine}{long_pack}, $key);
7f441181 48 }
49
9281d66b 50 my $rv = $self->SUPER::FETCH( $key );
51
52 $self->unlock;
53
54 return $rv;
7f441181 55}
56
cb79ec85 57sub STORE {
58 my $self = shift->_get_self;
59 my ($key, $value) = @_;
60
9281d66b 61 $self->lock( $self->LOCK_EX );
62
baa27ab6 63 my $orig = $key;
cb79ec85 64
9ab67b8c 65 my $size;
cb79ec85 66 my $numeric_idx;
9ab67b8c 67 if ( $key =~ /^\-?\d+$/ ) {
cb79ec85 68 $numeric_idx = 1;
69 if ( $key < 0 ) {
9ab67b8c 70 $size = $self->FETCHSIZE;
cb79ec85 71 $key += $size;
baa27ab6 72 if ( $key < 0 ) {
73 die( "Modification of non-creatable array value attempted, subscript $orig" );
74 }
cb79ec85 75 }
76
612969fb 77 $key = pack($self->{engine}{long_pack}, $key);
cb79ec85 78 }
79
80 my $rv = $self->SUPER::STORE( $key, $value );
81
9ab67b8c 82 if ( $numeric_idx && $rv == 2 ) {
83 $size = $self->FETCHSIZE unless defined $size;
84 if ( $orig >= $size ) {
85 $self->STORESIZE( $orig + 1 );
86 }
cb79ec85 87 }
88
9281d66b 89 $self->unlock;
90
cb79ec85 91 return $rv;
92}
93
baa27ab6 94sub EXISTS {
95 my $self = $_[0]->_get_self;
96 my $key = $_[1];
97
9281d66b 98 $self->lock( $self->LOCK_SH );
99
9ab67b8c 100 if ( $key =~ /^\-?\d+$/ ) {
baa27ab6 101 if ( $key < 0 ) {
102 $key += $self->FETCHSIZE;
9281d66b 103 unless ( $key >= 0 ) {
104 $self->unlock;
105 return;
106 }
107 }
108
612969fb 109 $key = pack($self->{engine}{long_pack}, $key);
9281d66b 110 }
111
112 my $rv = $self->SUPER::EXISTS( $key );
113
114 $self->unlock;
115
116 return $rv;
117}
118
119sub DELETE {
120 my $self = $_[0]->_get_self;
121 my $key = $_[1];
122
123 my $unpacked_key = $key;
124
125 $self->lock( $self->LOCK_EX );
126
127 my $size = $self->FETCHSIZE;
128 if ( $key =~ /^-?\d+$/ ) {
129 if ( $key < 0 ) {
130 $key += $size;
131 unless ( $key >= 0 ) {
132 $self->unlock;
133 return;
134 }
baa27ab6 135 }
136
612969fb 137 $key = pack($self->{engine}{long_pack}, $key);
baa27ab6 138 }
139
9281d66b 140 my $rv = $self->SUPER::DELETE( $key );
141
142 if ($rv && $unpacked_key == $size - 1) {
143 $self->STORESIZE( $unpacked_key );
144 }
145
146 $self->unlock;
147
148 return $rv;
baa27ab6 149}
150
6fe26b29 151sub FETCHSIZE {
152 ##
153 # Return the length of the array
154 ##
9281d66b 155 my $self = shift->_get_self;
156
157 $self->lock( $self->LOCK_SH );
158
4d35d856 159 my $SAVE_FILTER = $self->_root->{filter_fetch_value};
160 $self->_root->{filter_fetch_value} = undef;
6fe26b29 161
162 my $packed_size = $self->FETCH('length');
163
4d35d856 164 $self->_root->{filter_fetch_value} = $SAVE_FILTER;
6fe26b29 165
9281d66b 166 $self->unlock;
167
7f441181 168 if ($packed_size) {
612969fb 169 return int(unpack($self->{engine}{long_pack}, $packed_size));
7f441181 170 }
cb79ec85 171
172 return 0;
6fe26b29 173}
174
175sub STORESIZE {
176 ##
177 # Set the length of the array
178 ##
2ac02042 179 my $self = $_[0]->_get_self;
6fe26b29 180 my $new_length = $_[1];
181
9281d66b 182 $self->lock( $self->LOCK_EX );
183
4d35d856 184 my $SAVE_FILTER = $self->_root->{filter_store_value};
185 $self->_root->{filter_store_value} = undef;
6fe26b29 186
612969fb 187 my $result = $self->STORE('length', pack($self->{engine}{long_pack}, $new_length));
6fe26b29 188
4d35d856 189 $self->_root->{filter_store_value} = $SAVE_FILTER;
6fe26b29 190
9281d66b 191 $self->unlock;
192
6fe26b29 193 return $result;
194}
195
196sub POP {
197 ##
198 # Remove and return the last element on the array
199 ##
2ac02042 200 my $self = $_[0]->_get_self;
9281d66b 201
202 $self->lock( $self->LOCK_EX );
203
6fe26b29 204 my $length = $self->FETCHSIZE();
205
206 if ($length) {
207 my $content = $self->FETCH( $length - 1 );
208 $self->DELETE( $length - 1 );
9281d66b 209
210 $self->unlock;
211
6fe26b29 212 return $content;
213 }
214 else {
9281d66b 215 $self->unlock;
6fe26b29 216 return;
217 }
218}
219
220sub PUSH {
221 ##
222 # Add new element(s) to the end of the array
223 ##
2ac02042 224 my $self = shift->_get_self;
6fe26b29 225
9281d66b 226 $self->lock( $self->LOCK_EX );
227
228 my $length = $self->FETCHSIZE();
229
6fe26b29 230 while (my $content = shift @_) {
231 $self->STORE( $length, $content );
232 $length++;
233 }
8f6d6ed0 234
9281d66b 235 $self->unlock;
236
8f6d6ed0 237 return $length;
6fe26b29 238}
239
240sub SHIFT {
241 ##
242 # Remove and return first element on the array.
243 # Shift over remaining elements to take up space.
244 ##
2ac02042 245 my $self = $_[0]->_get_self;
9281d66b 246
247 $self->lock( $self->LOCK_EX );
248
6fe26b29 249 my $length = $self->FETCHSIZE();
250
251 if ($length) {
252 my $content = $self->FETCH( 0 );
253
254 ##
255 # Shift elements over and remove last one.
256 ##
257 for (my $i = 0; $i < $length - 1; $i++) {
258 $self->STORE( $i, $self->FETCH($i + 1) );
259 }
260 $self->DELETE( $length - 1 );
9281d66b 261
262 $self->unlock;
6fe26b29 263
264 return $content;
265 }
266 else {
9281d66b 267 $self->unlock;
6fe26b29 268 return;
269 }
270}
271
272sub UNSHIFT {
273 ##
274 # Insert new element(s) at beginning of array.
275 # Shift over other elements to make space.
276 ##
2ac02042 277 my $self = shift->_get_self;
6fe26b29 278 my @new_elements = @_;
9281d66b 279
280 $self->lock( $self->LOCK_EX );
281
6fe26b29 282 my $length = $self->FETCHSIZE();
283 my $new_size = scalar @new_elements;
284
285 if ($length) {
286 for (my $i = $length - 1; $i >= 0; $i--) {
287 $self->STORE( $i + $new_size, $self->FETCH($i) );
288 }
289 }
290
291 for (my $i = 0; $i < $new_size; $i++) {
292 $self->STORE( $i, $new_elements[$i] );
293 }
8f6d6ed0 294
9281d66b 295 $self->unlock;
296
8f6d6ed0 297 return $length + $new_size;
6fe26b29 298}
299
300sub SPLICE {
301 ##
302 # Splices section of array with optional new section.
303 # Returns deleted section, or last element deleted in scalar context.
304 ##
2ac02042 305 my $self = shift->_get_self;
9281d66b 306
307 $self->lock( $self->LOCK_EX );
308
6fe26b29 309 my $length = $self->FETCHSIZE();
310
311 ##
312 # Calculate offset and length of splice
313 ##
714618f0 314 my $offset = shift;
315 $offset = 0 unless defined $offset;
6fe26b29 316 if ($offset < 0) { $offset += $length; }
317
318 my $splice_length;
319 if (scalar @_) { $splice_length = shift; }
320 else { $splice_length = $length - $offset; }
321 if ($splice_length < 0) { $splice_length += ($length - $offset); }
322
323 ##
324 # Setup array with new elements, and copy out old elements for return
325 ##
326 my @new_elements = @_;
327 my $new_size = scalar @new_elements;
328
df3c5701 329 my @old_elements = map {
330 $self->FETCH( $_ )
331 } $offset .. ($offset + $splice_length - 1);
6fe26b29 332
333 ##
334 # Adjust array length, and shift elements to accomodate new section.
335 ##
336 if ( $new_size != $splice_length ) {
337 if ($new_size > $splice_length) {
338 for (my $i = $length - 1; $i >= $offset + $splice_length; $i--) {
339 $self->STORE( $i + ($new_size - $splice_length), $self->FETCH($i) );
340 }
341 }
342 else {
343 for (my $i = $offset + $splice_length; $i < $length; $i++) {
344 $self->STORE( $i + ($new_size - $splice_length), $self->FETCH($i) );
345 }
346 for (my $i = 0; $i < $splice_length - $new_size; $i++) {
347 $self->DELETE( $length - 1 );
348 $length--;
349 }
350 }
351 }
352
353 ##
354 # Insert new elements into array
355 ##
356 for (my $i = $offset; $i < $offset + $new_size; $i++) {
357 $self->STORE( $i, shift @new_elements );
358 }
359
9281d66b 360 $self->unlock;
361
6fe26b29 362 ##
363 # Return deleted section, or last element in scalar context.
364 ##
365 return wantarray ? @old_elements : $old_elements[-1];
366}
367
9281d66b 368#XXX We don't need to define it, yet.
6fe26b29 369#XXX It will be useful, though, when we split out HASH and ARRAY
370#sub EXTEND {
371 ##
372 # Perl will call EXTEND() when the array is likely to grow.
373 # We don't care, but include it for compatibility.
374 ##
375#}
376
377##
378# Public method aliases
379##
f75b719e 380sub length { (CORE::shift)->FETCHSIZE(@_) }
381sub pop { (CORE::shift)->POP(@_) }
382sub push { (CORE::shift)->PUSH(@_) }
383sub shift { (CORE::shift)->SHIFT(@_) }
384sub unshift { (CORE::shift)->UNSHIFT(@_) }
385sub splice { (CORE::shift)->SPLICE(@_) }
6fe26b29 386
3871;
388__END__