Fixes for 1.0007
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Array.pm
CommitLineData
6fe26b29 1package DBM::Deep::Array;
2
b48ae6ec 3use 5.006_000;
460b1067 4
6fe26b29 5use strict;
460b1067 6use warnings;
6fe26b29 7
f19b7e37 8our $VERSION = q(1.0007);
86867f3a 9
8fec41b9 10# This is to allow DBM::Deep::Array to handle negative indices on
11# its own. Otherwise, Perl would intercept the call to negative
12# indices for us. This was causing bugs for negative index handling.
460b1067 13our $NEGATIVE_INDICES = 1;
7910cf68 14
6fe26b29 15use base 'DBM::Deep';
16
e1b265cc 17use Scalar::Util ();
18
596e9574 19sub _get_self {
8db25060 20 eval { local $SIG{'__DIE__'}; tied( @{$_[0]} ) } || $_[0]
596e9574 21}
22
4301e879 23sub _repr { [] }
9a63e1f2 24
6fe26b29 25sub TIEARRAY {
6fe26b29 26 my $class = shift;
0ca7ea98 27 my $args = $class->_get_args( @_ );
504185fb 28
29 $args->{type} = $class->TYPE_ARRAY;
30
31 return $class->_init($args);
6fe26b29 32}
33
7f441181 34sub FETCH {
eea0d863 35 my $self = shift->_get_self;
36 my ($key) = @_;
7f441181 37
504185fb 38 $self->lock( $self->LOCK_SH );
9a187d8c 39
9a63e1f2 40 if ( !defined $key ) {
3300d0b3 41 $self->unlock;
9a63e1f2 42 DBM::Deep->_throw_error( "Cannot use an undefined array index." );
43 }
44 elsif ( $key =~ /^-?\d+$/ ) {
7f441181 45 if ( $key < 0 ) {
46 $key += $self->FETCHSIZE;
9281d66b 47 unless ( $key >= 0 ) {
48 $self->unlock;
49 return;
50 }
7f441181 51 }
c3aafc14 52 }
9a63e1f2 53 elsif ( $key ne 'length' ) {
54 $self->unlock;
55 DBM::Deep->_throw_error( "Cannot use '$key' as an array index." );
7f441181 56 }
57
9a63e1f2 58 my $rv = $self->SUPER::FETCH( $key );
9281d66b 59
60 $self->unlock;
61
62 return $rv;
7f441181 63}
64
cb79ec85 65sub STORE {
66 my $self = shift->_get_self;
67 my ($key, $value) = @_;
68
9281d66b 69 $self->lock( $self->LOCK_EX );
70
9ab67b8c 71 my $size;
c3aafc14 72 my $idx_is_numeric;
9a63e1f2 73 if ( !defined $key ) {
3300d0b3 74 $self->unlock;
9a63e1f2 75 DBM::Deep->_throw_error( "Cannot use an undefined array index." );
76 }
77 elsif ( $key =~ /^-?\d+$/ ) {
c3aafc14 78 $idx_is_numeric = 1;
cb79ec85 79 if ( $key < 0 ) {
9ab67b8c 80 $size = $self->FETCHSIZE;
c3aafc14 81 if ( $key + $size < 0 ) {
82 die( "Modification of non-creatable array value attempted, subscript $key" );
baa27ab6 83 }
c3aafc14 84 $key += $size
cb79ec85 85 }
cb79ec85 86 }
9a63e1f2 87 elsif ( $key ne 'length' ) {
88 $self->unlock;
89 DBM::Deep->_throw_error( "Cannot use '$key' as an array index." );
90 }
cb79ec85 91
9a63e1f2 92 my $rv = $self->SUPER::STORE( $key, $value );
cb79ec85 93
c3aafc14 94 if ( $idx_is_numeric ) {
9ab67b8c 95 $size = $self->FETCHSIZE unless defined $size;
c3aafc14 96 if ( $key >= $size ) {
97 $self->STORESIZE( $key + 1 );
9ab67b8c 98 }
cb79ec85 99 }
100
9281d66b 101 $self->unlock;
102
cb79ec85 103 return $rv;
104}
105
baa27ab6 106sub EXISTS {
eea0d863 107 my $self = shift->_get_self;
108 my ($key) = @_;
baa27ab6 109
504185fb 110 $self->lock( $self->LOCK_SH );
9281d66b 111
9a63e1f2 112 if ( !defined $key ) {
3300d0b3 113 $self->unlock;
9a63e1f2 114 DBM::Deep->_throw_error( "Cannot use an undefined array index." );
115 }
116 elsif ( $key =~ /^-?\d+$/ ) {
baa27ab6 117 if ( $key < 0 ) {
118 $key += $self->FETCHSIZE;
9281d66b 119 unless ( $key >= 0 ) {
120 $self->unlock;
121 return;
122 }
123 }
9281d66b 124 }
9a63e1f2 125 elsif ( $key ne 'length' ) {
126 $self->unlock;
127 DBM::Deep->_throw_error( "Cannot use '$key' as an array index." );
128 }
9281d66b 129
130 my $rv = $self->SUPER::EXISTS( $key );
131
132 $self->unlock;
133
134 return $rv;
135}
136
137sub DELETE {
eea0d863 138 my $self = shift->_get_self;
139 my ($key) = @_;
9281d66b 140
9281d66b 141 $self->lock( $self->LOCK_EX );
142
143 my $size = $self->FETCHSIZE;
9a63e1f2 144 if ( !defined $key ) {
3300d0b3 145 $self->unlock;
9a63e1f2 146 DBM::Deep->_throw_error( "Cannot use an undefined array index." );
147 }
148 elsif ( $key =~ /^-?\d+$/ ) {
9281d66b 149 if ( $key < 0 ) {
150 $key += $size;
151 unless ( $key >= 0 ) {
152 $self->unlock;
153 return;
154 }
baa27ab6 155 }
baa27ab6 156 }
9a63e1f2 157 elsif ( $key ne 'length' ) {
158 $self->unlock;
159 DBM::Deep->_throw_error( "Cannot use '$key' as an array index." );
160 }
baa27ab6 161
c3aafc14 162 my $rv = $self->SUPER::DELETE( $key );
9281d66b 163
c3aafc14 164 if ($rv && $key == $size - 1) {
9a63e1f2 165 $self->STORESIZE( $key );
504185fb 166 }
9281d66b 167
168 $self->unlock;
169
170 return $rv;
baa27ab6 171}
172
9a63e1f2 173# Now that we have a real Reference sector, we should store arrayzize there. However,
174# arraysize needs to be transactionally-aware, so a simple location to store it isn't
175# going to work.
6fe26b29 176sub FETCHSIZE {
9281d66b 177 my $self = shift->_get_self;
178
179 $self->lock( $self->LOCK_SH );
180
83371fe3 181 my $SAVE_FILTER = $self->_storage->{filter_fetch_value};
182 $self->_storage->{filter_fetch_value} = undef;
504185fb 183
9a63e1f2 184 my $size = $self->FETCH('length') || 0;
504185fb 185
83371fe3 186 $self->_storage->{filter_fetch_value} = $SAVE_FILTER;
504185fb 187
9281d66b 188 $self->unlock;
189
9a63e1f2 190 return $size;
6fe26b29 191}
192
193sub STORESIZE {
eea0d863 194 my $self = shift->_get_self;
504185fb 195 my ($new_length) = @_;
196
9281d66b 197 $self->lock( $self->LOCK_EX );
198
83371fe3 199 my $SAVE_FILTER = $self->_storage->{filter_store_value};
200 $self->_storage->{filter_store_value} = undef;
504185fb 201
9a63e1f2 202 my $result = $self->STORE('length', $new_length, 'length');
504185fb 203
83371fe3 204 $self->_storage->{filter_store_value} = $SAVE_FILTER;
504185fb 205
9281d66b 206 $self->unlock;
207
504185fb 208 return $result;
6fe26b29 209}
210
211sub POP {
eea0d863 212 my $self = shift->_get_self;
9281d66b 213
214 $self->lock( $self->LOCK_EX );
215
504185fb 216 my $length = $self->FETCHSIZE();
217
218 if ($length) {
219 my $content = $self->FETCH( $length - 1 );
220 $self->DELETE( $length - 1 );
9281d66b 221
222 $self->unlock;
223
504185fb 224 return $content;
225 }
226 else {
9281d66b 227 $self->unlock;
504185fb 228 return;
229 }
6fe26b29 230}
231
232sub PUSH {
2ac02042 233 my $self = shift->_get_self;
504185fb 234
9281d66b 235 $self->lock( $self->LOCK_EX );
236
504185fb 237 my $length = $self->FETCHSIZE();
9281d66b 238
504185fb 239 while (my $content = shift @_) {
240 $self->STORE( $length, $content );
241 $length++;
242 }
8f6d6ed0 243
9281d66b 244 $self->unlock;
245
8f6d6ed0 246 return $length;
6fe26b29 247}
248
67c27827 249# XXX This really needs to be something more direct within the file, not a
250# fetch and re-store. -RobK, 2007-09-20
9bb8d2fd 251sub _move_value {
252 my $self = shift;
253 my ($old_key, $new_key) = @_;
254
e137c258 255 return $self->_engine->make_reference( $self, $old_key, $new_key );
9bb8d2fd 256}
257
6fe26b29 258sub SHIFT {
eea0d863 259 my $self = shift->_get_self;
9281d66b 260
261 $self->lock( $self->LOCK_EX );
262
504185fb 263 my $length = $self->FETCHSIZE();
264
e137c258 265 if ( !$length ) {
9281d66b 266 $self->unlock;
504185fb 267 return;
268 }
e137c258 269
270 my $content = $self->FETCH( 0 );
271
272 for (my $i = 0; $i < $length - 1; $i++) {
273 $self->_move_value( $i+1, $i );
274 }
275 $self->DELETE( $length - 1 );
276
277 $self->unlock;
278
279 return $content;
6fe26b29 280}
281
282sub UNSHIFT {
2ac02042 283 my $self = shift->_get_self;
504185fb 284 my @new_elements = @_;
9281d66b 285
286 $self->lock( $self->LOCK_EX );
287
504185fb 288 my $length = $self->FETCHSIZE();
289 my $new_size = scalar @new_elements;
290
291 if ($length) {
292 for (my $i = $length - 1; $i >= 0; $i--) {
9bb8d2fd 293 $self->_move_value( $i, $i+$new_size );
504185fb 294 }
e137c258 295
296 $self->STORESIZE( $length + $new_size );
504185fb 297 }
298
299 for (my $i = 0; $i < $new_size; $i++) {
300 $self->STORE( $i, $new_elements[$i] );
301 }
8f6d6ed0 302
9281d66b 303 $self->unlock;
304
8f6d6ed0 305 return $length + $new_size;
6fe26b29 306}
307
308sub SPLICE {
2ac02042 309 my $self = shift->_get_self;
9281d66b 310
311 $self->lock( $self->LOCK_EX );
312
504185fb 313 my $length = $self->FETCHSIZE();
314
315 ##
316 # Calculate offset and length of splice
317 ##
318 my $offset = shift;
714618f0 319 $offset = 0 unless defined $offset;
504185fb 320 if ($offset < 0) { $offset += $length; }
321
322 my $splice_length;
323 if (scalar @_) { $splice_length = shift; }
324 else { $splice_length = $length - $offset; }
325 if ($splice_length < 0) { $splice_length += ($length - $offset); }
326
327 ##
328 # Setup array with new elements, and copy out old elements for return
329 ##
330 my @new_elements = @_;
331 my $new_size = scalar @new_elements;
332
df3c5701 333 my @old_elements = map {
334 $self->FETCH( $_ )
335 } $offset .. ($offset + $splice_length - 1);
504185fb 336
337 ##
338 # Adjust array length, and shift elements to accomodate new section.
339 ##
6fe26b29 340 if ( $new_size != $splice_length ) {
341 if ($new_size > $splice_length) {
342 for (my $i = $length - 1; $i >= $offset + $splice_length; $i--) {
9bb8d2fd 343 $self->_move_value( $i, $i + ($new_size - $splice_length) );
6fe26b29 344 }
e137c258 345 $self->STORESIZE( $length + $new_size - $splice_length );
6fe26b29 346 }
347 else {
348 for (my $i = $offset + $splice_length; $i < $length; $i++) {
9bb8d2fd 349 $self->_move_value( $i, $i + ($new_size - $splice_length) );
6fe26b29 350 }
351 for (my $i = 0; $i < $splice_length - $new_size; $i++) {
352 $self->DELETE( $length - 1 );
353 $length--;
354 }
355 }
504185fb 356 }
357
358 ##
359 # Insert new elements into array
360 ##
361 for (my $i = $offset; $i < $offset + $new_size; $i++) {
362 $self->STORE( $i, shift @new_elements );
363 }
364
9281d66b 365 $self->unlock;
366
504185fb 367 ##
368 # Return deleted section, or last element in scalar context.
369 ##
370 return wantarray ? @old_elements : $old_elements[-1];
6fe26b29 371}
372
9a63e1f2 373# We don't need to populate it, yet.
460b1067 374# It will be useful, though, when we split out HASH and ARRAY
685e40f1 375sub EXTEND {
504185fb 376 ##
377 # Perl will call EXTEND() when the array is likely to grow.
378 # We don't care, but include it because it gets called at times.
379 ##
685e40f1 380}
6fe26b29 381
f9c33187 382sub _copy_node {
898fd1fd 383 my $self = shift;
f9c33187 384 my ($db_temp) = @_;
385
386 my $length = $self->length();
387 for (my $index = 0; $index < $length; $index++) {
388 my $value = $self->get($index);
389 $self->_copy_value( \$db_temp->[$index], $value );
390 }
391
392 return 1;
393}
394
6fe26b29 395##
396# Public method aliases
397##
f9c33187 398sub length { (shift)->FETCHSIZE(@_) }
399sub pop { (shift)->POP(@_) }
400sub push { (shift)->PUSH(@_) }
401sub unshift { (shift)->UNSHIFT(@_) }
402sub splice { (shift)->SPLICE(@_) }
403
404# This must be last otherwise we have to qualify all other calls to shift
405# as calls to CORE::shift
f75b719e 406sub shift { (CORE::shift)->SHIFT(@_) }
6fe26b29 407
4081;
409__END__