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