File Coverage

blib/lib/Ref/Store.pm
Criterion Covered Total %
statement 198 441 44.9
branch 38 130 29.2
condition 8 27 29.6
subroutine 35 56 62.5
pod 15 32 46.8
total 294 686 42.8


line stmt bran cond sub pod time code
1             package Ref::Store;
2 1     1   81617 use strict;
  1         3  
  1         44  
3 1     1   7 use warnings;
  1         1  
  1         54  
4              
5             our $VERSION = '0.20';
6              
7 1     1   7 use Scalar::Util qw(weaken);
  1         2  
  1         78  
8 1     1   1567 use Carp::Heavy;
  1         130  
  1         29  
9 1     1   5 use Ref::Store::Common;
  1         2  
  1         175  
10 1     1   548 use Ref::Store::Attribute;
  1         4  
  1         35  
11 1     1   649 use Ref::Store::Dumper;
  1         3  
  1         35  
12 1     1   7 use Scalar::Util qw(weaken isweak);
  1         2  
  1         50  
13 1     1   6 use Devel::GlobalDestruction;
  1         2  
  1         7  
14 1     1   61 use Data::Dumper;
  1         2  
  1         47  
15 1     1   5 use Log::Fu { level => "debug" };
  1         2  
  1         7  
16 1     1   46 use Carp qw(confess cluck);
  1         3  
  1         61  
17 1     1   910 use Devel::FindRef qw(ptr2ref);
  1         2486  
  1         37  
18              
19 1     1   7 use base qw(Ref::Store::Feature::KeyTyped Exporter);
  1         3  
  1         686  
20             our (@EXPORT,@EXPORT_OK,%EXPORT_TAGS);
21              
22 1         20 use Constant::Generate [qw(
23             REF_STORE_FALSE
24             REF_STORE_TRUE
25            
26             REF_STORE_KEY
27             REF_STORE_ATTRIBUTE
28            
29             )], -tag => 'ref_store_constants',
30             -export => 1,
31 1     1   7 -export_tags => 1;
  1         2  
32              
33             use Class::XSAccessor::Array
34 1         17 accessors => {
35             %Ref::Store::Common::LookupNames
36 1     1   353 };
  1         3  
37              
38             my %Tables; #Global hash of tables
39              
40             ################################################################################
41             ################################################################################
42             ################################################################################
43             ### GENERIC FUNCTIONS ###
44             ################################################################################
45             ################################################################################
46             ################################################################################
47             sub _keyfunc_defl {
48 3     3   5 my $k = shift;
49 3 100       11 if(ref $k) {
50 1         4 return $k + 0;
51             }
52 2         5 return $k;
53             }
54              
55             our $SelectedImpl;
56              
57             sub new {
58 17     17 1 25307 my ($cls,%options) = @_;
59            
60 17 50       61 if($cls eq __PACKAGE__) {
61 0 0       0 if(!defined $SelectedImpl) {
62 0         0 log_debug("Will try to select best implementation");
63 0         0 foreach (qw(XS PP Sweeping)) {
64 0         0 my $impl = $cls . "::$_";
65 0         0 eval "require $impl";
66 0 0       0 if(!$@) {
67 0         0 $SelectedImpl = $impl;
68 0         0 last;
69             }
70             }
71             }
72 0 0       0 die "Can't load any implmented" unless $SelectedImpl;
73 0         0 $cls = $SelectedImpl;
74 0         0 log_debug("Using $SelectedImpl");
75             }
76            
77 17   50     185 $options{keyfunc} ||= \&_keyfunc_defl;
78 17   50 0   126 $options{unkeyfunc} ||= sub { $_[0] };
  0         0  
79            
80 17         31 my $self = [];
81 17         49 bless $self, $cls;
82            
83 17         134 $self->[$_] = {} for
84             (HR_TIDX_FLOOKUP,
85             HR_TIDX_RLOOKUP,
86             HR_TIDX_SLOOKUP,
87             HR_TIDX_ALOOKUP,
88             HR_TIDX_KEYTYPES);
89            
90 17         50 $self->[HR_TIDX_KEYFUNC] = $options{keyfunc};
91 17         34 $self->[HR_TIDX_UNKEYFUNC] = $options{unkeyfunc};
92            
93 17 50       91 if($self->can('table_init')) {
94 17         216 $self->table_init();
95             }
96            
97 17         96 weaken($Tables{$self+0} = $self);
98 17         63 return $self;
99             }
100              
101             sub purge {
102 28     28 1 1630 my ($self,$value) = @_;
103 28 50       65 return unless defined $value;
104 28         46 my $vstring = $value + 0;
105            
106 28         41 foreach my $ko (values %{ $self->reverse->{$vstring} }) {
  28         144  
107 43 50       80 if(!defined $ko) {
108 0         0 die "Found stale key object!";
109             }
110 43         229 $ko->unlink_value($value);
111             }
112            
113 28         134 $self->dref_del_ptr($value, $self->reverse, $value + 0);
114 28         199 delete $self->reverse->{$vstring};
115 28         59 return $value;
116             }
117              
118             #Not fully implemented
119             sub exchange_value {
120 0     0 0 0 my ($self,$old,$new) = @_;
121 0         0 my $olds = $old+0;
122 0         0 my $news = $new + 0;
123 0 0       0 die "Can't switch to existing value!" if exists $self->reverse->{$news};
124            
125 0 0       0 return unless exists $self->reverse->{$olds};
126            
127 0         0 my $newh = {};
128 0         0 my $oldh = $self->reverse->{$olds};
129 0         0 $self->reverse->{$news} = $newh;
130            
131 0         0 while (my ($kaddr,$kobj) = each %$oldh) {
132 0         0 $newh->{$kaddr} = $kobj;
133 0         0 $kobj->exchange_value($old,$new);
134 0         0 delete $oldh->{$kaddr};
135             }
136             }
137              
138             sub register_kt {
139 15     15 1 181 my ($self,$kt,$id_prefix) = @_;
140 15 50       64 if(!$self->keytypes) {
141 0         0 $self->keytypes({});
142             }
143 15   33     56 $id_prefix ||= $kt;
144 15 50       56 if(!exists $self->keytypes->{$kt}) {
145             #log_info("Registering CONST=$kt PREFIX=$id_prefix");
146 15         70 $self->keytypes->{$kt} = $id_prefix;
147             }
148             }
149              
150             sub maybe_cleanup_value {
151 0     0 0 0 my ($self,$value) = @_;
152 0         0 my $v_rhash = $self->reverse->{$value+0};
153 0 0       0 if(!scalar %$v_rhash) {
154 0         0 delete $self->reverse->{$value+0};
155 0         0 $self->dref_del_ptr($value, $self->reverse, $value + 0);
156             } else {
157             #log_warn(scalar %$v_rhash);
158             }
159             }
160              
161             ################################################################################
162             ################################################################################
163             ################################################################################
164             ### INFORMATIONAL FUNCTIONS ###
165             ################################################################################
166             ################################################################################
167             ################################################################################
168             sub has_key {
169 1     1 0 53 my ($self,$key) = @_;
170 1 50       5 $key = ref $key ? $key + 0 : $key;
171 1   33     15 return (exists $self->forward->{$key} || exists $self->scalar_lookup->{$key});
172             }
173              
174             *lexists = \&has_key;
175              
176             sub has_value {
177 23     23 0 3772 my ($self,$value) = @_;
178 23 100       61 return 0 if !defined $value;
179 22         34 $value = $value + 0;
180 22         116 return exists $self->reverse->{$value};
181             }
182              
183             sub vlookups {
184 0     0 1 0 my ($self,$value) = @_;
185 0         0 my @ret;
186 0         0 $value = $value + 0;
187 0         0 my $vhash = $self->reverse->{$value};
188 0   0     0 $vhash ||= {};
189 0         0 foreach my $ko (values %$vhash) {
190 0         0 push @ret, $ko->kstring;
191             }
192 0         0 return @ret;
193             }
194              
195             *vexists = \&has_value;
196              
197             sub has_attr {
198 4     4 0 489 my ($self,$attr,$t) = @_;
199 4         34 $self->attr_get($attr, $t);
200             }
201              
202             sub is_empty {
203 7     7 1 1342 my $self = shift;
204 7         34 %{$self->scalar_lookup} == 0
  7         39  
205 7         36 && %{$self->reverse} == 0
206 7         44 && %{$self->forward} == 0
207 7 50 33     11 && %{$self->attr_lookup} == 0;
      33        
208             }
209              
210             sub vlist {
211 1     1 1 136 my $self = shift;
212 1         3 return map { Devel::FindRef::ptr2ref $_+0 } keys %{ $self->reverse };
  11         33  
  1         9  
213             }
214              
215             sub _mk_keyspec {
216 3     3   5 my $lookup = shift;
217 3         4 my $prefix;
218 3         20 my $kstring = $lookup->kstring;
219 3         11 my $ukey = $lookup->ukey;
220 3 100       10 $ukey = $kstring unless defined $ukey;
221 3 100       17 if($lookup->prefix_len) {
222 2         7 $prefix = substr($kstring, 0, $lookup->prefix_len);
223 2 100       7 if(!ref $ukey) {
224 1         6 $ukey = substr($kstring, $lookup->prefix_len+1);
225             }
226             } else {
227 1         3 $prefix = "";
228             }
229 3         19 return ($prefix, $ukey);
230             }
231              
232             sub klist {
233 0     0 1 0 my ($self,%options) = @_;
234 0         0 my @ret;
235 0         0 foreach my $kobj (values %{$self->forward}) {
  0         0  
236 0         0 push @ret, [REF_STORE_KEY, _mk_keyspec($kobj)];
237             }
238 0         0 foreach my $aobj (values %{$self->attr_lookup}) {
  0         0  
239 0         0 push @ret, [REF_STORE_ATTRIBUTE, _mk_keyspec($aobj)];
240             }
241 0         0 return @ret;
242             }
243              
244              
245             #This is the iteration mechanism. An 'iterator' is an internal structure
246             #which keeps track of the items we wish to iterate over. the CUR field
247             #is a simple integer. the HASH field is an array of hashrefs, with the
248             #current active hash specified with the CUR field; thus the currently
249             #iterated-over hash is $iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR] ];
250             # When the CUR field reaches ITER_CUR_END, it means there are no more
251             #hashes to iterate over.
252             use constant {
253 1         8122 ITER_FLD_HASH => 0,
254             ITER_FLD_CUR => 1,
255            
256             ITER_CUR_KEYS => 0,
257             ITER_CUR_ATTR => 1,
258             ITER_CUR_END => 2
259 1     1   3134 };
  1         2  
260             sub iterinit {
261 1     1 1 183 my ($self,%options) = @_;
262            
263 1 50       10 warn("Resetting existing non-null iterator") if defined $self->_iter;
264            
265 1         2 keys %{$self->scalar_lookup};
  1         5  
266 1         2 keys %{$self->attr_lookup};
  1         4  
267 1         2 my $iter = [];
268 1         3 $iter->[ITER_FLD_CUR] = 0;
269            
270 1         4 $iter->[ITER_FLD_HASH]->[ITER_CUR_KEYS] = $self->scalar_lookup;
271 1         5 $iter->[ITER_FLD_HASH]->[ITER_CUR_ATTR] = $self->attr_lookup;
272            
273 1 50       6 if($options{OnlyKeys}) {
    50          
274 0         0 delete $iter->[ITER_FLD_HASH]->[ITER_CUR_ATTR];
275             } elsif ($options{OnlyAttrs}) {
276 0         0 delete $iter->[ITER_FLD_HASH]->[ITER_CUR_KEYS];
277 0         0 $iter->[ITER_FLD_CUR]++;
278             }
279 1         4 $self->_iter($iter);
280 1         4 return;
281             }
282              
283             sub iterdone {
284 0     0 1 0 my $self = shift;
285 0         0 $self->_iter(undef);
286             }
287              
288             sub iter {
289 5     5 1 594 my $self = $_[0];
290 5         12 my $iter = $self->_iter;
291 5 50       9 return unless $iter;
292 5         7 my @ret;
293             #print Dumper($iter);
294 5         5 my $nextk = each %{$iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR] ] };
  5         13  
295 5 100       16 goto GT_EMPTY unless defined $nextk;
296            
297 3         6 my $lookup = $iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR] ]->{$nextk};
298            
299 3 50       7 goto GT_EMPTY unless defined $lookup;
300            
301            
302            
303 3 100       8 if($iter->[ITER_FLD_CUR] == ITER_CUR_KEYS) {
304 2         7 @ret = (REF_STORE_KEY,
305             _mk_keyspec($lookup),
306             $self->forward->{$lookup->kstring});
307             } else {
308             #Attribute
309 1         10 @ret = (REF_STORE_ATTRIBUTE,
310             _mk_keyspec($lookup),
311 1         3 [values %{$lookup->get_hash}]);
312             }
313 3         16 return @ret;
314            
315             GT_EMPTY:
316 2         9 while($iter->[ITER_FLD_CUR]++ < ITER_CUR_END) {
317 2 100       10 if($iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR ] ]) {
318 1         6 goto &iter;
319             }
320             }
321             #End!
322 1         3 $self->_iter(undef);
323 1         4 return ();
324             }
325              
326             sub dump {
327 0     0 1 0 my $self = shift;
328 0         0 my $dcls = "Ref::Store::Dumper";
329 0         0 my $hrd = $dcls->new();
330             #my $hrd = Ref::Store::Dumper->new();
331             #log_err($hrd);
332 0         0 $hrd->dump($self);
333 0         0 $hrd->flush();
334             #print Dumper($self);
335             }
336             ################################################################################
337             ################################################################################
338             ################################################################################
339             ### KEY FUNCTIONS ###
340             ################################################################################
341             ################################################################################
342             ################################################################################
343             sub new_key {
344 0     0 0 0 die "new_key not implemented!";
345             }
346              
347             sub ukey2ikey {
348 3     3 0 8 my ($self, $ukey, %options) = @_;
349            
350 3         16 my $ustr = $self->keyfunc->($ukey);
351 3         8 my $expected = delete $options{O_EXCL};
352 3         6 my $create_if_needed = delete $options{Create};
353            
354             #log_info($ustr);
355 3         10 my $o = $self->scalar_lookup->{$ustr};
356 3 50 33     12 if($expected && $o) {
357 0         0 my $existing = $self->forward->{$o->kstring};
358 0 0 0     0 if($existing && $expected != $existing) {
359 0         0 die "Request O_EXCL for new key ${\$o->kstring} => $expected but key ".
  0         0  
360             "is already tied to $existing";
361             }
362             }
363            
364 3 50 33     17 if(!$o && $create_if_needed) {
365 0         0 $o = $self->new_key($ukey);
366 0 0       0 if(!$options{StrongKey}) {
367 0         0 $o->weaken_encapsulated();
368             }
369             }
370            
371 3         8 return $o;
372             }
373              
374             sub store_sk {
375 0     0 0 0 my ($self,$ukey,$value,%options) = @_;
376 0         0 my $o = $self->ukey2ikey($ukey,
377             Create => 1,
378             O_EXCL => $value,
379             %options
380             );
381 0         0 my $vstring = $value+0;
382 0         0 my $kstring = $o->kstring;
383 0         0 $self->reverse->{$vstring}->{$kstring} = $o;
384 0         0 $self->forward->{$kstring} = $value;
385            
386             #Add a back-delete to the reverse entry. The forward
387             #entry for keys are handled by the keys themselves.
388 0         0 $self->dref_add_ptr($value, $self->reverse);
389 0         0 $o->link_value($value);
390            
391 0 0       0 if(!$options{StrongValue}) {
392 0         0 weaken($self->forward->{$kstring});
393             }
394 0         0 return $value;
395             }
396             *store = \&store_sk;
397              
398              
399             #sub store_kt {
400             # my ($self,$ukey,$prefix,$value,%options) = @_;
401             #
402             #}
403              
404             sub fetch_sk {
405 0     0 0 0 my ($self,$ukey) = @_;
406             #log_info("called..");
407 0         0 my $o = $self->ukey2ikey($ukey);
408 0 0       0 return unless $o;
409 0         0 return $self->forward->{$o->kstring};
410             }
411             *fetch = \&fetch_sk;
412              
413             #This dissociates a value from a single key
414             sub unlink_sk {
415 3     3 0 1969 my ($self,$ukey) = @_;
416            
417 3         21 my $ko = $self->ukey2ikey($ukey);
418 3 50       7 return unless $ko;
419 3         23 my $value = $self->forward->{$ko->kstring};
420 3 50       8 die "Found orphaned key $ko" unless defined $value;
421            
422 3         7 my $vstr = $value + 0;
423 3         9 my $kstr = $ko->kstring;
424            
425 3         9 my $vhash = $self->reverse->{$vstr};
426            
427 3 50       7 die "Can't locate vhash" unless defined $vhash;
428 3         7 delete $vhash->{$kstr};
429            
430 3         13 $ko->unlink_value($value);
431            
432 3 50       3 if(!%{$self->reverse->{$vstr}}) {
  3         19  
433 0         0 delete $self->reverse->{$vstr};
434 0         0 $self->dref_del_ptr($value, $self->reverse, $vstr);
435            
436             }
437            
438 3         39 return $value;
439             }
440             *unlink = \&unlink_sk;
441              
442             sub purgeby_sk {
443 13     13 0 6851 my ($self,$kspec) = @_;
444 13         85 my $value = $self->fetch($kspec);
445 13 50       36 return unless $value;
446 13         33 $self->purge($value);
447 13         144 return $value;
448             }
449              
450             *purgeby = \&purgeby_sk;
451              
452             *lexists_sk = \&lexists;
453              
454             ################################################################################
455             ################################################################################
456             ################################################################################
457             ### ATTRIBUTE FUNCTIONS ###
458             ################################################################################
459             ################################################################################
460             ################################################################################
461             sub new_attr {
462 0     0 0 0 my ($self,$astr,$attr) = @_;
463 0 0       0 my $cls = ref $attr ? 'Ref::Store::Attribute::Encapsulating' :
464             'Ref::Store::Attribute';
465 0         0 $cls->new($astr,$attr,$self);
466             }
467              
468             sub attr_get {
469 0     0 0 0 my ($self,$attr,$t,%options) = @_;
470            
471 0 0       0 my $ustr = $self->keytypes->{$t} or die "Couldn't find attribtue type!";
472 0         0 $ustr .= "#";
473 0 0       0 if(ref $attr) {
474 0         0 $ustr .= $attr+0;
475             } else {
476 0 0       0 die unless $attr;
477 0         0 $ustr .= $attr;
478             }
479 0         0 my $aobj = $self->attr_lookup->{$ustr};
480 0 0       0 return $aobj if $aobj;
481            
482 0 0       0 if(!$options{Create}) {
483 0         0 return;
484             }
485            
486 0         0 $aobj = $self->new_attr($ustr, $attr, $self);
487 0         0 weaken($self->attr_lookup->{$ustr} = $aobj);
488            
489 0 0       0 if(!$options{StrongAttr}) {
490 0         0 $aobj->weaken_encapsulated();
491             }
492 0         0 return $aobj;
493             }
494              
495             sub store_a {
496 0     0 1 0 my ($self,$attr,$t,$value,%options) = @_;
497            
498 0         0 my $aobj = $self->attr_get($attr, $t, Create => 1, %options);
499 0 0       0 if(!$value) {
500 0         0 log_err(@_);
501 0         0 die "NULL Value!";
502             }
503              
504 0         0 my $vaddr = $value + 0;
505              
506 0         0 $self->reverse->{$vaddr}->{$aobj+0} = $aobj;
507            
508 0 0       0 if(!$options{StrongValue}) {
509 0         0 $aobj->store_weak($vaddr, $value);
510             } else {
511 0         0 $aobj->store_strong($vaddr, $value);
512             }
513              
514             #add back-delete references to both the private
515             #attribute hash as well as the reverse entry.
516            
517 0         0 $self->dref_add_ptr($value, $aobj->get_hash);
518 0         0 $self->dref_add_ptr($value, $self->reverse);
519 0         0 $aobj->link_value($value);
520            
521 0         0 return $value;
522             }
523              
524              
525             sub fetch_a {
526 0     0 1 0 my ($self,$attr,$t) = @_;
527 0         0 my $aobj = $self->attr_get($attr, $t);
528 0 0       0 if(!$aobj) {
529             #log_err("Can't find attribute object! ($attr:$t)");
530             #print Dumper($self->attr_lookup);
531 0         0 return;
532             }
533 0         0 my @ret;
534 0 0       0 return @ret unless $aobj;
535 0         0 @ret = values %{$aobj->get_hash};
  0         0  
536 0         0 return @ret;
537             }
538              
539             sub purgeby_a {
540 1     1 0 12 my ($self,$attr,$t) = @_;
541 1         10 my @values = $self->fetch_a($attr, $t);
542 1         5 $self->purge($_) foreach @values;
543 1         4 return @values;
544             }
545              
546             sub dissoc_a {
547 0     0 1 0 my ($self,$attr,$t,$value) = @_;
548 0         0 my $aobj = $self->attr_get($attr, $t);
549 0 0       0 if(!$aobj) {
550 0         0 log_err("Can't find attribute for $t$attr");
551 0         0 return;
552             }
553 0         0 my $attrhash = $aobj->get_hash;
554 0         0 delete $attrhash->{$value+0};
555 0         0 delete $self->reverse->{$value+0}->{$aobj+0};
556 0         0 $self->dref_del_ptr($value, $attrhash, $value+0);
557              
558 0         0 $aobj->unlink_value($value);
559 0         0 $self->maybe_cleanup_value($value);
560             }
561              
562             sub unlink_a {
563 0     0 1 0 my ($self,$attr,$t) = @_;
564 0         0 my $aobj = $self->attr_get($attr, $t);
565 0         0 my $attrhash = $aobj->get_hash;
566 0 0       0 return unless $attrhash;
567            
568            
569 0         0 while (my ($k,$v) = each %$attrhash) {
570 0         0 $self->dref_del_ptr($v, $attrhash, $v+0);
571 0         0 delete $attrhash->{$k};
572 0         0 delete $self->reverse->{$v+0}->{$aobj+0};
573 0         0 $aobj->unlink_value($v);
574 0         0 $self->maybe_cleanup_value($v);
575             }
576             }
577              
578              
579             *lexists_a = \&has_attr;
580              
581             sub Dumperized {
582 0     0 0 0 my $self = shift;
583             return {
584 0         0 'Reverse Lookups' => $self->reverse,
585             'Forward Lookups' => $self->forward,
586             'Scalar Lookups' => $self->scalar_lookup,
587             'Attribute Lookups' => $self->attr_lookup
588             };
589             }
590              
591             sub DESTROY {
592 17 50   17   8879 return if in_global_destruction;
593 17         135 my $self = shift;
594             #log_err("Destroying $self...");
595 17         29 my @values;
596 17         25 foreach my $attr (values %{$self->attr_lookup}) {
  17         96  
597             #log_warn("Attr: $attr");
598 0         0 my $attrhash = $attr->get_hash();
599 0 0       0 next unless ref $attrhash;
600 0 0       0 if(ref $attrhash ne 'HASH') {
601 1     1   16 use Devel::Peek;
  1         3  
  1         10  
602 0         0 Devel::Peek::Dump($attrhash);
603             }
604 0         0 foreach my $v (values %$attrhash) {
605 0 0       0 next unless defined $v;
606 0 0       0 if($attr->can('unlink_value')) {
607 0         0 $attr->unlink_value($v);
608             }
609 0         0 push @values, $v;
610             }
611             }
612             #log_warn("Attribute deletion done");
613            
614 17         31 foreach my $kobj (values %{$self->scalar_lookup}) {
  17         72  
615 1         10 my $v = $self->forward->{$kobj->kstring};
616 1         2 push @values, $v;
617 1 50       14 if($kobj->can("unlink_value")) {
618 1         6 $kobj->unlink_value($v);
619             }
620 1         7 delete $self->scalar_lookup->{$kobj->kstring};
621 1         9 delete $self->forward->{$kobj->kstring};
622             }
623            
624             #log_warn("Key deletion done");
625            
626 17         45 foreach my $value (@values) {
627 1         3 my $vaddr = $value + 0 ;
628 1         5 my $vhash = delete $self->reverse->{$vaddr};
629             #log_warn($vhash);
630 1         8 $self->dref_del_ptr($value, $self->reverse, $vaddr);
631             }
632             #log_warn("Will clear temporary value list");
633 17         33 undef @values;
634            
635 17         169 delete $Tables{$self+0};
636             #log_err("Destroy $self done");
637             }
638              
639             ################################################################################
640             ################################################################################
641             ### Thread Cloning ###
642             ################################################################################
643             ################################################################################
644              
645             #This maps addresses to (weak) object references
646             our %CloneAddrs;
647              
648             sub ithread_predup {
649 0     0 0   my $self = shift;
650            
651 0           $self->ithread_store_lookup_info(\%CloneAddrs);
652            
653             #Key lookups
654 0           foreach my $val (values %{$self->forward}) {
  0            
655 0           weaken($CloneAddrs{$val+0} = $val);
656             }
657            
658 0           foreach my $kobj (values %{$self->scalar_lookup}) {
  0            
659 0           weaken($CloneAddrs{$kobj+0} = $kobj);
660            
661 0           my $v = $self->forward->{$kobj->kstring};
662 0           $kobj->ithread_predup($self, \%CloneAddrs, $v);
663             }
664            
665 0           foreach my $attr (values %{$self->attr_lookup}) {
  0            
666 0           weaken($CloneAddrs{$attr+0} = $attr);
667 0           $attr->ithread_predup($self, \%CloneAddrs);
668 0           my $attrhash = $attr->get_hash;
669             #log_warn("ATTRHASH", $attrhash);
670 0           foreach my $v (values %$attrhash) {
671 0           weaken($CloneAddrs{$v+0} = $v);
672             }
673             }
674            
675 0           foreach my $vhash (values %{$self->reverse}) {
  0            
676             #log_warn($vhash);
677 0           weaken($CloneAddrs{$vhash+0} = $vhash);
678             }
679             #foreach (qw(attr_lookup scalar_lookup forward reverse)) {
680             # log_warn($_, $self->can($_)->($self));
681             #}
682             }
683              
684             sub ithread_postdup {
685 0     0 0   my ($self,$old_table) = @_;
686            
687 0           my @oldkeys = keys %{$self->reverse};
  0            
688 0           foreach my $oldaddr (@oldkeys) {
689 0           my $vhash = $self->reverse->{$oldaddr};
690 0           my $vobj = $CloneAddrs{$oldaddr};
691 0 0         if(!defined $vobj) {
692 0           print Dumper(\%CloneAddrs);
693 0           die("KEY=$oldaddr");
694             }
695 0           my $newaddr = $vobj + 0;
696 0           $self->reverse->{$newaddr} = $vhash;
697 0           delete $self->reverse->{$oldaddr};
698 0           $self->dref_add_ptr($vobj, $self->reverse, $newaddr);
699             }
700            
701 0           @oldkeys = keys %{$self->scalar_lookup};
  0            
702 0           foreach my $kstring (@oldkeys) {
703 0           my $kobj = $self->scalar_lookup->{$kstring};
704 0           $kobj->ithread_postdup($self, \%CloneAddrs, $old_table);
705 0           my $new_kstring = $kobj->kstring;
706            
707 0 0         next unless $new_kstring ne $kstring;
708 0           my $weak_key = isweak($self->scalar_lookup->{$kstring});
709 0           my $weak_val = isweak($self->forward->{$kstring});
710            
711 0           delete $self->scalar_lookup->{$kstring};
712 0           my $v = delete $self->forward->{$kstring};
713            
714 0           $self->scalar_lookup->{$new_kstring} = $kobj;
715 0           $self->forward->{$new_kstring} = $v;
716            
717 0 0         if($weak_key) {
718 0           weaken($self->scalar_lookup->{$new_kstring});
719             }
720 0 0         if($weak_val) {
721 0           weaken($self->forward->{$new_kstring});
722             }
723             }
724            
725 0           @oldkeys = keys %{$self->attr_lookup};
  0            
726 0           foreach my $astring (@oldkeys) {
727 0           my $aobj = $self->attr_lookup->{$astring};
728 0           $aobj->ithread_postdup($self, \%CloneAddrs);
729 0           my $new_astring = $aobj->kstring;
730            
731 0 0         next unless $new_astring ne $astring;
732            
733 0           delete $self->attr_lookup->{$astring};
734 0           weaken($self->attr_lookup->{$new_astring} = $aobj);
735             }
736            
737             #foreach (qw(attr_lookup scalar_lookup forward reverse)) {
738             # log_warn($_, $self->can($_)->($self));
739             #}
740            
741 0           foreach my $vhash (values %{$self->reverse}) {
  0            
742 0           my @vhkeys = keys %$vhash;
743 0           foreach my $lkey (@vhkeys) {
744 0           my $lobj = delete $vhash->{$lkey};
745 0           $vhash->{$lobj->kstring} = $lobj;
746             }
747             }
748             }
749              
750             $SIG{__DIE__}=\&confess;
751             sub CLONE_SKIP {
752 0     0     my $pkg = shift;
753 0 0         return 0 if $pkg ne __PACKAGE__;
754 0           $Log::Fu::LINE_PREFIX = 'PARENT: ';
755 0           %CloneAddrs = ();
756            
757 0           while ( my ($addr,$obj) = each %Tables ) {
758 0 0         if(!defined $obj) {
759 0           log_err("Found undefined reference T=$addr");
760             #die("Found undef table in hash");
761 0           delete $Tables{$addr};
762 0           next;
763             }
764 0           $obj->ithread_predup();
765             }
766            
767 0           return 0;
768             }
769              
770             sub CLONE {
771 0     0     my $pkg = shift;
772 0 0         return if $pkg ne __PACKAGE__;
773 0           $Log::Fu::LINE_PREFIX = 'CHILD: ';
774 0           my @tkeys = keys %Tables;
775 0           my @new_tables;
776 0           foreach my $old_taddr (@tkeys) {
777 0           my $table = delete $Tables{$old_taddr};
778             #log_info("Calling ithread_postdup on table");
779 0           $table->ithread_postdup($old_taddr);
780             #log_info("Done");
781 0           weaken($Tables{$table+0} = $table);
782             }
783            
784 0           %CloneAddrs = ();
785             }
786             1;
787              
788             __END__