File Coverage

blib/lib/Tie/RefHash.pm
Criterion Covered Total %
statement 79 86 91.8
branch 20 26 76.9
condition 5 12 41.6
subroutine 18 19 94.7
pod 0 2 0.0
total 122 145 84.1


line stmt bran cond sub pod time code
1             package Tie::RefHash; # git description: v1.40-9-g23812d9
2             # ABSTRACT: Use references as hash keys
3              
4             our $VERSION = '1.41';
5              
6             #pod =head1 SYNOPSIS
7             #pod
8             #pod require 5.004;
9             #pod use Tie::RefHash;
10             #pod tie HASHVARIABLE, 'Tie::RefHash', LIST;
11             #pod tie HASHVARIABLE, 'Tie::RefHash::Nestable', LIST;
12             #pod
13             #pod untie HASHVARIABLE;
14             #pod
15             #pod =head1 DESCRIPTION
16             #pod
17             #pod This module provides the ability to use references as hash keys if you
18             #pod first C the hash variable to this module. Normally, only the
19             #pod keys of the tied hash itself are preserved as references; to use
20             #pod references as keys in hashes-of-hashes, use Tie::RefHash::Nestable,
21             #pod included as part of Tie::RefHash.
22             #pod
23             #pod It is implemented using the standard perl TIEHASH interface. Please
24             #pod see the C entry in perlfunc(1) and perltie(1) for more information.
25             #pod
26             #pod The Nestable version works by looking for hash references being stored
27             #pod and converting them to tied hashes so that they too can have
28             #pod references as keys. This will happen without warning whenever you
29             #pod store a reference to one of your own hashes in the tied hash.
30             #pod
31             #pod =head1 EXAMPLE
32             #pod
33             #pod use Tie::RefHash;
34             #pod tie %h, 'Tie::RefHash';
35             #pod $a = [];
36             #pod $b = {};
37             #pod $c = \*main;
38             #pod $d = \"gunk";
39             #pod $e = sub { 'foo' };
40             #pod %h = ($a => 1, $b => 2, $c => 3, $d => 4, $e => 5);
41             #pod $a->[0] = 'foo';
42             #pod $b->{foo} = 'bar';
43             #pod for (keys %h) {
44             #pod print ref($_), "\n";
45             #pod }
46             #pod
47             #pod tie %h, 'Tie::RefHash::Nestable';
48             #pod $h{$a}->{$b} = 1;
49             #pod for (keys %h, keys %{$h{$a}}) {
50             #pod print ref($_), "\n";
51             #pod }
52             #pod
53             #pod =head1 THREAD SUPPORT
54             #pod
55             #pod L fully supports threading using the C method.
56             #pod
57             #pod =head1 STORABLE SUPPORT
58             #pod
59             #pod L hooks are provided for semantically correct serialization and
60             #pod cloning of tied refhashes.
61             #pod
62             #pod =head1 AUTHORS
63             #pod
64             #pod Gurusamy Sarathy
65             #pod
66             #pod Tie::RefHash::Nestable by Ed Avis
67             #pod
68             #pod =head1 SEE ALSO
69             #pod
70             #pod perl(1), perlfunc(1), perltie(1)
71             #pod
72             #pod =cut
73              
74 3     3   31629 use Tie::Hash;
  3         2819  
  3         143  
75             our @ISA = qw(Tie::Hash);
76 3     3   19 use strict;
  3         4  
  3         65  
77 3     3   12 use Carp ();
  3         7  
  3         57  
78              
79             # Tie::RefHash::Weak (until at least 0.09) assumes we define a refaddr()
80             # function, so just import the one from Scalar::Util
81 3     3   12 use Scalar::Util qw(refaddr);
  3         7  
  3         241  
82              
83             BEGIN {
84             # determine whether we need to take care of threads
85 3     3   15 use Config ();
  3         8  
  3         336  
86 3     3   307 my $usethreads = $Config::Config{usethreads}; # && exists $INC{"threads.pm"}
87 3 50       19 *_HAS_THREADS = $usethreads ? sub () { 1 } : sub () { 0 };
88 3 50       3701 *_HAS_WEAKEN = defined(&Scalar::Util::weaken) ? sub () { 1 } : sub () { 0 };
89             }
90              
91             my (@thread_object_registry, $count); # used by the CLONE method to rehash the keys after their refaddr changed
92              
93             sub TIEHASH {
94 9     9   2072940 my $c = shift;
95 9         23 my $s = [];
96 9         26 bless $s, $c;
97 9         38 while (@_) {
98 0         0 $s->STORE(shift, shift);
99             }
100              
101 9         16 if (_HAS_THREADS ) {
102              
103             if ( _HAS_WEAKEN ) {
104             # remember the object so that we can rekey it on CLONE
105             push @thread_object_registry, $s;
106             # but make this a weak reference, so that there are no leaks
107             Scalar::Util::weaken( $thread_object_registry[-1] );
108              
109             if ( ++$count > 1000 ) {
110             # this ensures we don't fill up with a huge array dead weakrefs
111             @thread_object_registry = grep defined, @thread_object_registry;
112             Scalar::Util::weaken( $_ ) for @thread_object_registry;
113             $count = 0;
114             }
115             } else {
116             $count++; # used in the warning
117             }
118             }
119              
120 9         36 return $s;
121             }
122              
123             my $storable_format_version = join("/", __PACKAGE__, "0.01");
124              
125             sub STORABLE_freeze {
126 4     4 0 127 my ( $self, $is_cloning ) = @_;
127 4         10 my ( $refs, $reg ) = @$self;
128 4   100     324 return ( $storable_format_version, [ values %$refs ], $reg || {} );
129             }
130              
131             sub STORABLE_thaw {
132 4     4 0 86 my ( $self, $is_cloning, $version, $refs, $reg ) = @_;
133 4 50       14 Carp::croak "incompatible versions of Tie::RefHash between freeze and thaw"
134             unless $version eq $storable_format_version;
135              
136 4         11 @$self = ( {}, $reg );
137 4         14 $self->_reindex_keys( $refs );
138             }
139              
140             sub CLONE {
141 0     0   0 my $pkg = shift;
142              
143 0 0 0     0 if ( $count and not _HAS_WEAKEN ) {
144 0         0 warn "Tie::RefHash is not threadsafe without Scalar::Util::weaken";
145             }
146              
147             # when the thread has been cloned all the objects need to be updated.
148             # dead weakrefs are undefined, so we filter them out
149 0   0     0 @thread_object_registry = grep defined && do { $_->_reindex_keys; 1 }, @thread_object_registry;
150 0         0 Scalar::Util::weaken( $_ ) for @thread_object_registry;
151 0         0 $count = 0; # we just cleaned up
152             }
153              
154             sub _reindex_keys {
155 4     4   16 my ( $self, $extra_keys ) = @_;
156             # rehash all the ref keys based on their new StrVal
157 4 50       7 %{ $self->[0] } = map +(refaddr($_->[0]) => $_), (values(%{ $self->[0] }), @{ $extra_keys || [] });
  4         52  
  4         11  
  4         21  
158             }
159              
160             sub FETCH {
161 625     625   35242 my($s, $k) = @_;
162 625 100       1281 if (ref $k) {
163 22         40 my $kstr = refaddr($k);
164 22 100       67 if (defined $s->[0]{$kstr}) {
165 20         88 $s->[0]{$kstr}[1];
166             }
167             else {
168 2         10 undef;
169             }
170             }
171             else {
172 603         4983 $s->[1]{$k};
173             }
174             }
175              
176             sub STORE {
177 95     95   5443 my($s, $k, $v) = @_;
178 95 100       241 if (ref $k) {
179 8         55 $s->[0]{refaddr($k)} = [$k, $v];
180             }
181             else {
182 87         200 $s->[1]{$k} = $v;
183             }
184 95         408 $v;
185             }
186              
187             sub DELETE {
188 64     64   4370 my($s, $k) = @_;
189             (ref $k)
190             ? (delete($s->[0]{refaddr($k)}) || [])->[1]
191 64 100 50     498 : delete($s->[1]{$k});
192             }
193              
194             sub EXISTS {
195 132     132   9361 my($s, $k) = @_;
196 132 100       1025 (ref $k) ? exists($s->[0]{refaddr($k)}) : exists($s->[1]{$k});
197             }
198              
199             sub FIRSTKEY {
200 594     594   81052 my $s = shift;
201 594         912 keys %{$s->[0]}; # reset iterator
  594         1208  
202 594         924 keys %{$s->[1]}; # reset iterator
  594         981  
203 594         1007 $s->[2] = 0; # flag for iteration, see NEXTKEY
204 594         1207 $s->NEXTKEY;
205             }
206              
207             sub NEXTKEY {
208 1173     1173   3524 my $s = shift;
209 1173         1801 my ($k, $v);
210 1173 100       2739 if (!$s->[2]) {
211 610 100       815 if (($k, $v) = each %{$s->[0]}) {
  610         1595  
212 16         75 return $v->[0];
213             }
214             else {
215 594         977 $s->[2] = 1;
216             }
217             }
218 1157         1595 return each %{$s->[1]};
  1157         10999  
219             }
220              
221             sub CLEAR {
222 2     2   2663 my $s = shift;
223 2         6 $s->[2] = 0;
224 2         4 %{$s->[0]} = ();
  2         8  
225 2         5 %{$s->[1]} = ();
  2         27  
226             }
227              
228             package # hide from PAUSE
229             Tie::RefHash::Nestable;
230             our @ISA = 'Tie::RefHash';
231              
232             sub STORE {
233 45     45   7161 my($s, $k, $v) = @_;
234 45 100 66     202 if (ref($v) eq 'HASH' and not tied %$v) {
235 1         4 my @elems = %$v;
236 1         5 tie %$v, ref($s), @elems;
237             }
238 45         143 $s->SUPER::STORE($k, $v);
239             }
240              
241             1;
242              
243             __END__