File Coverage

blib/lib/Tie/RefHash.pm
Criterion Covered Total %
statement 83 90 92.2
branch 21 28 75.0
condition 5 12 41.6
subroutine 18 20 90.0
pod 0 2 0.0
total 127 152 83.5


line stmt bran cond sub pod time code
1             package Tie::RefHash; # git description: Tie-RefHash-1.39-10-g2cfa4bd
2             # ABSTRACT: Use references as hash keys
3              
4             our $VERSION = '1.40';
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   14393 use Tie::Hash;
  3         2914  
  3         127  
75             our @ISA = qw(Tie::Hash);
76 3     3   19 use strict;
  3         6  
  3         58  
77 3     3   14 use Carp ();
  3         6  
  3         65  
78              
79             BEGIN {
80 3     3   11 local $@;
81             # determine whether we need to take care of threads
82 3     3   13 use Config ();
  3         6  
  3         449  
83 3         285 my $usethreads = $Config::Config{usethreads}; # && exists $INC{"threads.pm"}
84 3 50       20 *_HAS_THREADS = $usethreads ? sub () { 1 } : sub () { 0 };
85 3 50       7 *_HAS_SCALAR_UTIL = eval { require Scalar::Util; 1 } ? sub () { 1 } : sub () { 0 };
  3         17  
  3         11  
86 3 50       507 *_HAS_WEAKEN = defined(&Scalar::Util::weaken) ? sub () { 1 } : sub () { 0 };
87             }
88              
89             BEGIN {
90             # create a refaddr function
91              
92 3     3   11 local $@;
93              
94 3         5 if ( _HAS_SCALAR_UTIL ) {
95 0     0   0 *refaddr = sub { goto \&Scalar::Util::refaddr }
96 3         3040 } else {
97             require overload;
98              
99             *refaddr = sub {
100             if ( overload::StrVal($_[0]) =~ /\( 0x ([a-zA-Z0-9]+) \)$/x) {
101             return $1;
102             } else {
103             die "couldn't parse StrVal: " . overload::StrVal($_[0]);
104             }
105             };
106             }
107             }
108              
109             my (@thread_object_registry, $count); # used by the CLONE method to rehash the keys after their refaddr changed
110              
111             sub TIEHASH {
112 9     9   79549 my $c = shift;
113 9         20 my $s = [];
114 9         20 bless $s, $c;
115 9         36 while (@_) {
116 0         0 $s->STORE(shift, shift);
117             }
118              
119 9         13 if (_HAS_THREADS ) {
120              
121             if ( _HAS_WEAKEN ) {
122             # remember the object so that we can rekey it on CLONE
123             push @thread_object_registry, $s;
124             # but make this a weak reference, so that there are no leaks
125             Scalar::Util::weaken( $thread_object_registry[-1] );
126              
127             if ( ++$count > 1000 ) {
128             # this ensures we don't fill up with a huge array dead weakrefs
129             @thread_object_registry = grep defined, @thread_object_registry;
130             $count = 0;
131             }
132             } else {
133             $count++; # used in the warning
134             }
135             }
136              
137 9         29 return $s;
138             }
139              
140             my $storable_format_version = join("/", __PACKAGE__, "0.01");
141              
142             sub STORABLE_freeze {
143 4     4 0 63 my ( $self, $is_cloning ) = @_;
144 4         8 my ( $refs, $reg ) = @$self;
145 4   100     142 return ( $storable_format_version, [ values %$refs ], $reg || {} );
146             }
147              
148             sub STORABLE_thaw {
149 4     4 0 59 my ( $self, $is_cloning, $version, $refs, $reg ) = @_;
150 4 50       11 Carp::croak "incompatible versions of Tie::RefHash between freeze and thaw"
151             unless $version eq $storable_format_version;
152              
153 4         10 @$self = ( {}, $reg );
154 4         9 $self->_reindex_keys( $refs );
155             }
156              
157             sub CLONE {
158 0     0   0 my $pkg = shift;
159              
160 0 0 0     0 if ( $count and not _HAS_WEAKEN ) {
161 0         0 warn "Tie::RefHash is not threadsafe without Scalar::Util::weaken";
162             }
163              
164             # when the thread has been cloned all the objects need to be updated.
165             # dead weakrefs are undefined, so we filter them out
166 0   0     0 @thread_object_registry = grep defined && do { $_->_reindex_keys; 1 }, @thread_object_registry;
167 0         0 $count = 0; # we just cleaned up
168             }
169              
170             sub _reindex_keys {
171 4     4   8 my ( $self, $extra_keys ) = @_;
172             # rehash all the ref keys based on their new StrVal
173 4 50       6 %{ $self->[0] } = map +(Scalar::Util::refaddr($_->[0]) => $_), (values(%{ $self->[0] }), @{ $extra_keys || [] });
  4         32  
  4         9  
  4         19  
174             }
175              
176             sub FETCH {
177 625     625   33259 my($s, $k) = @_;
178 625 100       1185 if (ref $k) {
179 22         46 my $kstr = Scalar::Util::refaddr($k);
180 22 100       45 if (defined $s->[0]{$kstr}) {
181 20         74 $s->[0]{$kstr}[1];
182             }
183             else {
184 2         8 undef;
185             }
186             }
187             else {
188 603         4442 $s->[1]{$k};
189             }
190             }
191              
192             sub STORE {
193 95     95   5548 my($s, $k, $v) = @_;
194 95 100       201 if (ref $k) {
195 8         57 $s->[0]{Scalar::Util::refaddr($k)} = [$k, $v];
196             }
197             else {
198 87         194 $s->[1]{$k} = $v;
199             }
200 95         383 $v;
201             }
202              
203             sub DELETE {
204 64     64   4425 my($s, $k) = @_;
205             (ref $k)
206             ? (delete($s->[0]{Scalar::Util::refaddr($k)}) || [])->[1]
207 64 100 50     482 : delete($s->[1]{$k});
208             }
209              
210             sub EXISTS {
211 132     132   9093 my($s, $k) = @_;
212 132 100       1004 (ref $k) ? exists($s->[0]{Scalar::Util::refaddr($k)}) : exists($s->[1]{$k});
213             }
214              
215             sub FIRSTKEY {
216 594     594   72382 my $s = shift;
217 594         827 keys %{$s->[0]}; # reset iterator
  594         1182  
218 594         839 keys %{$s->[1]}; # reset iterator
  594         799  
219 594         914 $s->[2] = 0; # flag for iteration, see NEXTKEY
220 594         1104 $s->NEXTKEY;
221             }
222              
223             sub NEXTKEY {
224 1173     1173   3286 my $s = shift;
225 1173         1562 my ($k, $v);
226 1173 100       2267 if (!$s->[2]) {
227 610 100       847 if (($k, $v) = each %{$s->[0]}) {
  610         1512  
228 16         52 return $v->[0];
229             }
230             else {
231 594         805 $s->[2] = 1;
232             }
233             }
234 1157         1438 return each %{$s->[1]};
  1157         8760  
235             }
236              
237             sub CLEAR {
238 2     2   2029 my $s = shift;
239 2         7 $s->[2] = 0;
240 2         6 %{$s->[0]} = ();
  2         6  
241 2         3 %{$s->[1]} = ();
  2         20  
242             }
243              
244             package # hide from PAUSE
245             Tie::RefHash::Nestable;
246             our @ISA = 'Tie::RefHash';
247              
248             sub STORE {
249 45     45   5508 my($s, $k, $v) = @_;
250 45 100 66     154 if (ref($v) eq 'HASH' and not tied %$v) {
251 1         4 my @elems = %$v;
252 1         5 tie %$v, ref($s), @elems;
253             }
254 45         109 $s->SUPER::STORE($k, $v);
255             }
256              
257             1;
258              
259             __END__