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