File Coverage

blib/lib/Ref/Store.pm
Criterion Covered Total %
statement 279 426 65.4
branch 68 126 53.9
condition 13 27 48.1
subroutine 43 55 78.1
pod 15 32 46.8
total 418 666 62.7


line stmt bran cond sub pod time code
1             package Ref::Store;
2 2     2   37018 use strict;
  2         5  
  2         50  
3 2     2   9 use warnings;
  2         4  
  2         91  
4              
5             our $VERSION = '0.15_0';
6              
7 2     2   13 use Scalar::Util qw(weaken);
  2         5  
  2         109  
8 2     2   736 use Carp::Heavy;
  2         217  
  2         46  
9 2     2   11 use Ref::Store::Common;
  2         3  
  2         226  
10 2     2   658 use Ref::Store::Attribute;
  2         5  
  2         60  
11 2     2   607 use Ref::Store::Dumper;
  2         5  
  2         50  
12 2     2   12 use Scalar::Util qw(weaken isweak);
  2         4  
  2         70  
13 2     2   10 use Devel::GlobalDestruction;
  2         4  
  2         9  
14 2     2   86 use Data::Dumper;
  2         3  
  2         63  
15 2     2   9 use Log::Fu { level => "debug" };
  2         4  
  2         9  
16 2     2   98 use Carp qw(confess cluck);
  2         5  
  2         96  
17 2     2   827 use Devel::FindRef qw(ptr2ref);
  2         2534  
  2         54  
18              
19 2     2   12 use base qw(Ref::Store::Feature::KeyTyped Exporter);
  2         4  
  2         707  
20             our (@EXPORT,@EXPORT_OK,%EXPORT_TAGS);
21              
22 2         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 2     2   13 -export_tags => 1;
  2         4  
32              
33             use Class::XSAccessor::Array
34 2         28 accessors => {
35             %Ref::Store::Common::LookupNames
36 2     2   780 };
  2         18  
37              
38             my %Tables; #Global hash of tables
39              
40             ################################################################################
41             ################################################################################
42             ################################################################################
43             ### GENERIC FUNCTIONS ###
44             ################################################################################
45             ################################################################################
46             ################################################################################
47             sub _keyfunc_defl {
48 79     79   126 my $k = shift;
49 79 100       174 if(ref $k) {
50 32         78 return $k + 0;
51             }
52 47         75 return $k;
53             }
54              
55             our $SelectedImpl;
56              
57             sub new {
58 31     31 1 26810 my ($cls,%options) = @_;
59            
60 31 50       95 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 31   50     172 $options{keyfunc} ||= \&_keyfunc_defl;
78 31   50 0   183 $options{unkeyfunc} ||= sub { $_[0] };
  0         0  
79            
80 31         57 my $self = [];
81 31         50 bless $self, $cls;
82            
83 31         165 $self->[$_] = {} for
84             (HR_TIDX_FLOOKUP,
85             HR_TIDX_RLOOKUP,
86             HR_TIDX_SLOOKUP,
87             HR_TIDX_ALOOKUP,
88             HR_TIDX_KEYTYPES);
89            
90 31         61 $self->[HR_TIDX_KEYFUNC] = $options{keyfunc};
91 31         49 $self->[HR_TIDX_UNKEYFUNC] = $options{unkeyfunc};
92            
93 31 100       118 if($self->can('table_init')) {
94 17         101 $self->table_init();
95             }
96            
97 31         160 weaken($Tables{$self+0} = $self);
98 31         91 return $self;
99             }
100              
101             sub purge {
102 54     54 1 1310 my ($self,$value) = @_;
103 54 50       120 return unless defined $value;
104 54         86 my $vstring = $value + 0;
105            
106 54         82 foreach my $ko (values %{ $self->reverse->{$vstring} }) {
  54         182  
107 81 50       165 if(!defined $ko) {
108 0         0 die "Found stale key object!";
109             }
110 81         273 $ko->unlink_value($value);
111             }
112            
113 54         182 $self->dref_del_ptr($value, $self->reverse, $value + 0);
114 54         236 delete $self->reverse->{$vstring};
115 54         105 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 27     27 1 233 my ($self,$kt,$id_prefix) = @_;
140 27 50       90 if(!$self->keytypes) {
141 0         0 $self->keytypes({});
142             }
143 27   33     118 $id_prefix ||= $kt;
144 27 50       77 if(!exists $self->keytypes->{$kt}) {
145             #log_info("Registering CONST=$kt PREFIX=$id_prefix");
146 27         88 $self->keytypes->{$kt} = $id_prefix;
147             }
148             }
149              
150             sub maybe_cleanup_value {
151 4     4 0 8 my ($self,$value) = @_;
152 4         10 my $v_rhash = $self->reverse->{$value+0};
153 4 50       9 if(!scalar %$v_rhash) {
154 4         9 delete $self->reverse->{$value+0};
155 4         13 $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 2     2 0 57 my ($self,$key) = @_;
170 2 50       9 $key = ref $key ? $key + 0 : $key;
171 2   33     27 return (exists $self->forward->{$key} || exists $self->scalar_lookup->{$key});
172             }
173              
174             *lexists = \&has_key;
175              
176             sub has_value {
177 43     43 0 2729 my ($self,$value) = @_;
178 43 100       100 return 0 if !defined $value;
179 41         67 $value = $value + 0;
180 41         156 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 8     8 0 403 my ($self,$attr,$t) = @_;
199 8         36 $self->attr_get($attr, $t);
200             }
201              
202             sub is_empty {
203 13     13 1 1070 my $self = shift;
204 13         48 %{$self->scalar_lookup} == 0
205 13         52 && %{$self->reverse} == 0
206 13         110 && %{$self->forward} == 0
207 13 50 33     22 && %{$self->attr_lookup} == 0;
  13   33     75  
208             }
209              
210             sub vlist {
211 2     2 1 94 my $self = shift;
212 2         4 return map { Devel::FindRef::ptr2ref $_+0 } keys %{ $self->reverse };
  22         57  
  2         9  
213             }
214              
215             sub _mk_keyspec {
216 3     3   9 my $lookup = shift;
217 3         3 my $prefix;
218 3         15 my $kstring = $lookup->kstring;
219 3         10 my $ukey = $lookup->ukey;
220 3 100       9 $ukey = $kstring unless defined $ukey;
221 3 100       10 if($lookup->prefix_len) {
222 2         7 $prefix = substr($kstring, 0, $lookup->prefix_len);
223 2 100       7 if(!ref $ukey) {
224 1         3 $ukey = substr($kstring, $lookup->prefix_len+1);
225             }
226             } else {
227 1         2 $prefix = "";
228             }
229 3         14 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 2         4368 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 2     2   2785 };
  2         4  
260             sub iterinit {
261 1     1 1 92 my ($self,%options) = @_;
262            
263 1 50       7 warn("Resetting existing non-null iterator") if defined $self->_iter;
264            
265 1         2 keys %{$self->scalar_lookup};
  1         3  
266 1         2 keys %{$self->attr_lookup};
  1         3  
267 1         2 my $iter = [];
268 1         3 $iter->[ITER_FLD_CUR] = 0;
269            
270 1         3 $iter->[ITER_FLD_HASH]->[ITER_CUR_KEYS] = $self->scalar_lookup;
271 1         4 $iter->[ITER_FLD_HASH]->[ITER_CUR_ATTR] = $self->attr_lookup;
272            
273 1 50       4 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         3 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 285 my $self = $_[0];
290 5         12 my $iter = $self->_iter;
291 5 50       12 return unless $iter;
292 5         8 my @ret;
293             #print Dumper($iter);
294 5         6 my $nextk = each %{$iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR] ] };
  5         10  
295 5 100       17 goto GT_EMPTY unless defined $nextk;
296            
297 3         7 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       7 if($iter->[ITER_FLD_CUR] == ITER_CUR_KEYS) {
304             @ret = (REF_STORE_KEY,
305             _mk_keyspec($lookup),
306 2         5 $self->forward->{$lookup->kstring});
307             } else {
308             #Attribute
309             @ret = (REF_STORE_ATTRIBUTE,
310             _mk_keyspec($lookup),
311 1         3 [values %{$lookup->get_hash}]);
  1         8  
312             }
313 3         13 return @ret;
314            
315             GT_EMPTY:
316 2         7 while($iter->[ITER_FLD_CUR]++ < ITER_CUR_END) {
317 2 100       7 if($iter->[ITER_FLD_HASH]->[ $iter->[ITER_FLD_CUR ] ]) {
318 1         5 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 79     79 0 168 my ($self, $ukey, %options) = @_;
349            
350 79         206 my $ustr = $self->keyfunc->($ukey);
351 79         137 my $expected = delete $options{O_EXCL};
352 79         112 my $create_if_needed = delete $options{Create};
353            
354             #log_info($ustr);
355 79         146 my $o = $self->scalar_lookup->{$ustr};
356 79 100 100     299 if($expected && $o) {
357 1         5 my $existing = $self->forward->{$o->kstring};
358 1 50 33     7 if($existing && $expected != $existing) {
359 1         2 die "Request O_EXCL for new key ${\$o->kstring} => $expected but key ".
  1         3  
360             "is already tied to $existing";
361             }
362             }
363            
364 78 100 100     261 if(!$o && $create_if_needed) {
365 43         106 $o = $self->new_key($ukey);
366 43 100       94 if(!$options{StrongKey}) {
367 31         80 $o->weaken_encapsulated();
368             }
369             }
370            
371 78         149 return $o;
372             }
373              
374             sub store_sk {
375 44     44 0 694 my ($self,$ukey,$value,%options) = @_;
376 44         113 my $o = $self->ukey2ikey($ukey,
377             Create => 1,
378             O_EXCL => $value,
379             %options
380             );
381 43         68 my $vstring = $value+0;
382 43         107 my $kstring = $o->kstring;
383 43         109 $self->reverse->{$vstring}->{$kstring} = $o;
384 43         80 $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 43         109 $self->dref_add_ptr($value, $self->reverse);
389 43         136 $o->link_value($value);
390            
391 43 100       93 if(!$options{StrongValue}) {
392 16         40 weaken($self->forward->{$kstring});
393             }
394 43         103 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 29     29 0 93 my ($self,$ukey) = @_;
406             #log_info("called..");
407 29         55 my $o = $self->ukey2ikey($ukey);
408 29 100       70 return unless $o;
409 25         70 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 6     6 0 2254 my ($self,$ukey) = @_;
416            
417 6         26 my $ko = $self->ukey2ikey($ukey);
418 6 50       18 return unless $ko;
419 6         31 my $value = $self->forward->{$ko->kstring};
420 6 50       18 die "Found orphaned key $ko" unless defined $value;
421            
422 6         13 my $vstr = $value + 0;
423 6         18 my $kstr = $ko->kstring;
424            
425 6         14 my $vhash = $self->reverse->{$vstr};
426            
427 6 50       17 die "Can't locate vhash" unless defined $vhash;
428 6         15 delete $vhash->{$kstr};
429            
430 6         23 $ko->unlink_value($value);
431            
432 6 50       12 if(!%{$self->reverse->{$vstr}}) {
  6         44  
433 0         0 delete $self->reverse->{$vstr};
434 0         0 $self->dref_del_ptr($value, $self->reverse, $vstr);
435            
436             }
437            
438 6         45 return $value;
439             }
440             *unlink = \&unlink_sk;
441              
442             sub purgeby_sk {
443 26     26 0 3256 my ($self,$kspec) = @_;
444 26         92 my $value = $self->fetch($kspec);
445 26 50       73 return unless $value;
446 26         80 $self->purge($value);
447 26         231 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 13     13 0 27 my ($self,$astr,$attr) = @_;
463 13 100       29 my $cls = ref $attr ? 'Ref::Store::Attribute::Encapsulating' :
464             'Ref::Store::Attribute';
465 13         52 $cls->new($astr,$attr,$self);
466             }
467              
468             sub attr_get {
469 55     55 0 114 my ($self,$attr,$t,%options) = @_;
470            
471 55 50       167 my $ustr = $self->keytypes->{$t} or die "Couldn't find attribtue type!";
472 55         88 $ustr .= "#";
473 55 100       101 if(ref $attr) {
474 12         26 $ustr .= $attr+0;
475             } else {
476 43 50       82 die unless $attr;
477 43         67 $ustr .= $attr;
478             }
479 55         88 my $aobj = $self->attr_lookup->{$ustr};
480 55 100       144 return $aobj if $aobj;
481            
482 17 100       36 if(!$options{Create}) {
483 4         12 return;
484             }
485            
486 13         30 $aobj = $self->new_attr($ustr, $attr, $self);
487 13         57 weaken($self->attr_lookup->{$ustr} = $aobj);
488            
489 13 100       32 if(!$options{StrongAttr}) {
490 12         29 $aobj->weaken_encapsulated();
491             }
492 13         27 return $aobj;
493             }
494              
495             sub store_a {
496 37     37 1 377 my ($self,$attr,$t,$value,%options) = @_;
497            
498 37         89 my $aobj = $self->attr_get($attr, $t, Create => 1, %options);
499 37 50       76 if(!$value) {
500 0         0 log_err(@_);
501 0         0 die "NULL Value!";
502             }
503              
504 37         56 my $vaddr = $value + 0;
505              
506 37         111 $self->reverse->{$vaddr}->{$aobj+0} = $aobj;
507            
508 37 100       73 if(!$options{StrongValue}) {
509 36         86 $aobj->store_weak($vaddr, $value);
510             } else {
511 1         4 $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 37         86 $self->dref_add_ptr($value, $aobj->get_hash);
518 37         101 $self->dref_add_ptr($value, $self->reverse);
519 37         101 $aobj->link_value($value);
520            
521 37         88 return $value;
522             }
523              
524             #sub tag {
525             # my ($self,$tag,$value, %options) = @_;
526             # $self->store_a(1, $tag, $value, %options);
527             #}
528             #
529             #sub untag {
530             # my ($self,$tag,$value,%options) = @_;
531             # $self->dissoc_a(1, $tag, $value);
532             #}
533              
534              
535              
536             sub fetch_a {
537 11     11 1 49 my ($self,$attr,$t) = @_;
538 11         26 my $aobj = $self->attr_get($attr, $t);
539 11 50       25 if(!$aobj) {
540             #log_err("Can't find attribute object! ($attr:$t)");
541             #print Dumper($self->attr_lookup);
542 0         0 return;
543             }
544 11         16 my @ret;
545 11 50       22 return @ret unless $aobj;
546 11         15 @ret = values %{$aobj->get_hash};
  11         28  
547 11         42 return @ret;
548             }
549              
550             sub purgeby_a {
551 2     2 0 14 my ($self,$attr,$t) = @_;
552 2         11 my @values = $self->fetch_a($attr, $t);
553 2         10 $self->purge($_) foreach @values;
554 2         6 return @values;
555             }
556              
557             sub dissoc_a {
558 2     2 1 6 my ($self,$attr,$t,$value) = @_;
559 2         5 my $aobj = $self->attr_get($attr, $t);
560 2 50       7 if(!$aobj) {
561 0         0 log_err("Can't find attribute for $t$attr");
562 0         0 return;
563             }
564 2         6 my $attrhash = $aobj->get_hash;
565 2         8 delete $attrhash->{$value+0};
566 2         6 delete $self->reverse->{$value+0}->{$aobj+0};
567 2         7 $self->dref_del_ptr($value, $attrhash, $value+0);
568              
569 2         10 $aobj->unlink_value($value);
570 2         8 $self->maybe_cleanup_value($value);
571             }
572              
573             sub unlink_a {
574 1     1 1 3 my ($self,$attr,$t) = @_;
575 1         3 my $aobj = $self->attr_get($attr, $t);
576 1         3 my $attrhash = $aobj->get_hash;
577 1 50       9 return unless $attrhash;
578            
579            
580 1         5 while (my ($k,$v) = each %$attrhash) {
581 2         7 $self->dref_del_ptr($v, $attrhash, $v+0);
582 2         5 delete $attrhash->{$k};
583 2         7 delete $self->reverse->{$v+0}->{$aobj+0};
584 2         5 $aobj->unlink_value($v);
585 2         4 $self->maybe_cleanup_value($v);
586             }
587             }
588              
589              
590             *lexists_a = \&has_attr;
591              
592             sub Dumperized {
593 0     0 0 0 my $self = shift;
594             return {
595 0         0 "This is the Toaster method" => "NOTICE!",
596            
597             'Reverse Lookups' => $self->reverse,
598             'Forward Lookups' => $self->forward,
599             'Scalar Lookups' => $self->scalar_lookup,
600             'Attribute Lookups' => $self->attr_lookup
601             };
602             }
603              
604             sub DESTROY {
605 31 50   31   8107 return if in_global_destruction;
606 31         204 my $self = shift;
607 31         45 my @values;
608 31         51 foreach my $attr (values %{$self->attr_lookup}) {
  31         112  
609 0         0 foreach my $v (values %{$attr->get_hash}) {
  0         0  
610 0 0       0 next unless defined $v;
611 0 0       0 if($attr->can('unlink_value')) {
612 0         0 $attr->unlink_value($v);
613             }
614 0         0 push @values, $v;
615             }
616             }
617            
618 31         53 foreach my $kobj (values %{$self->scalar_lookup}) {
  31         77  
619 1         7 my $v = $self->forward->{$kobj->kstring};
620 1         3 push @values, $v;
621 1 50       9 if($kobj->can("unlink_value")) {
622 1         4 $kobj->unlink_value($v);
623             }
624 1         5 delete $self->scalar_lookup->{$kobj->kstring};
625 1         4 delete $self->forward->{$kobj->kstring};
626             }
627            
628 31         58 foreach my $value (@values) {
629 1         7 delete $self->reverse->{$value+0};
630 1         7 $self->dref_del_ptr($value, $self->reverse, $value + 0);
631             }
632 31         51 undef @values;
633            
634 31         228 delete $Tables{$self+0};
635             }
636              
637             ################################################################################
638             ################################################################################
639             ### Thread Cloning ###
640             ################################################################################
641             ################################################################################
642              
643             #This maps addresses to (weak) object references
644             our %CloneAddrs;
645              
646             sub ithread_predup {
647 0     0 0   my $self = shift;
648            
649 0           $self->ithread_store_lookup_info(\%CloneAddrs);
650            
651             #Key lookups
652 0           foreach my $val (values %{$self->forward}) {
  0            
653 0           weaken($CloneAddrs{$val+0} = $val);
654             }
655            
656 0           foreach my $kobj (values %{$self->scalar_lookup}) {
  0            
657 0           weaken($CloneAddrs{$kobj+0} = $kobj);
658            
659 0           my $v = $self->forward->{$kobj->kstring};
660 0           $kobj->ithread_predup($self, \%CloneAddrs, $v);
661             }
662            
663 0           foreach my $attr (values %{$self->attr_lookup}) {
  0            
664 0           weaken($CloneAddrs{$attr+0} = $attr);
665 0           $attr->ithread_predup($self, \%CloneAddrs);
666 0           foreach my $v (values %{$attr->get_hash}) {
  0            
667 0           weaken($CloneAddrs{$v+0} = $v);
668             }
669             }
670            
671 0           foreach my $vhash (values %{$self->reverse}) {
  0            
672 0           weaken($CloneAddrs{$vhash+0} = $vhash);
673             }
674             }
675              
676             sub ithread_postdup {
677 0     0 0   my ($self,$old_table) = @_;
678            
679 0           my @oldkeys = keys %{$self->reverse};
  0            
680 0           foreach my $oldaddr (@oldkeys) {
681 0           my $vhash = $self->reverse->{$oldaddr};
682 0           my $vobj = $CloneAddrs{$oldaddr};
683 0 0         if(!defined $vobj) {
684 0           print Dumper(\%CloneAddrs);
685 0           die("KEY=$oldaddr");
686             }
687 0           my $newaddr = $vobj + 0;
688 0           $self->reverse->{$newaddr} = $vhash;
689 0           delete $self->reverse->{$oldaddr};
690 0           $self->dref_add_ptr($vobj, $self->reverse, $newaddr);
691             }
692            
693 0           @oldkeys = keys %{$self->scalar_lookup};
  0            
694 0           foreach my $kstring (@oldkeys) {
695 0           my $kobj = $self->scalar_lookup->{$kstring};
696 0           $kobj->ithread_postdup($self, \%CloneAddrs, $old_table);
697 0           my $new_kstring = $kobj->kstring;
698            
699 0 0         next unless $new_kstring ne $kstring;
700 0           my $weak_key = isweak($self->scalar_lookup->{$kstring});
701 0           my $weak_val = isweak($self->forward->{$kstring});
702            
703 0           delete $self->scalar_lookup->{$kstring};
704 0           my $v = delete $self->forward->{$kstring};
705            
706 0           $self->scalar_lookup->{$new_kstring} = $kobj;
707 0           $self->forward->{$new_kstring} = $v;
708            
709 0 0         if($weak_key) {
710 0           weaken($self->scalar_lookup->{$new_kstring});
711             }
712 0 0         if($weak_val) {
713 0           weaken($self->forward->{$new_kstring});
714             }
715             }
716            
717 0           @oldkeys = keys %{$self->attr_lookup};
  0            
718 0           foreach my $astring (@oldkeys) {
719 0           my $aobj = $self->attr_lookup->{$astring};
720 0           $aobj->ithread_postdup($self, \%CloneAddrs);
721 0           my $new_astring = $aobj->kstring;
722            
723 0 0         next unless $new_astring ne $astring;
724            
725 0           delete $self->attr_lookup->{$astring};
726 0           weaken($self->attr_lookup->{$new_astring} = $aobj);
727             }
728             }
729              
730             $SIG{__DIE__}=\&confess;
731             sub CLONE_SKIP {
732 0     0     my $pkg = shift;
733 0 0         return 0 if $pkg ne __PACKAGE__;
734 0           %CloneAddrs = ();
735            
736 0           while ( my ($addr,$obj) = each %Tables ) {
737 0 0         if(!defined $obj) {
738 0           log_err("Found undefined reference T=$addr");
739             #die("Found undef table in hash");
740 0           delete $Tables{$addr};
741 0           next;
742             }
743 0           $obj->ithread_predup();
744             }
745            
746 0           return 0;
747             }
748              
749             sub CLONE {
750 0     0     my $pkg = shift;
751 0 0         return if $pkg ne __PACKAGE__;
752            
753 0           my @tkeys = keys %Tables;
754 0           my @new_tables;
755 0           foreach my $old_taddr (@tkeys) {
756 0           my $table = delete $Tables{$old_taddr};
757             #log_info("Calling ithread_postdup on table");
758 0           $table->ithread_postdup($old_taddr);
759             #log_info("Done");
760 0           weaken($Tables{$table+0} = $table);
761             }
762            
763 0           %CloneAddrs = ();
764             }
765             1;
766              
767             __END__