File Coverage

blib/lib/Tie/RefHash.pm
Criterion Covered Total %
statement 96 105 91.4
branch 21 30 70.0
condition 5 9 55.5
subroutine 22 23 95.6
pod 0 2 0.0
total 144 169 85.2


line stmt bran cond sub pod time code
1             package Tie::RefHash;
2              
3 3     3   43866 use vars qw/$VERSION/;
  3         7  
  3         220  
4              
5             $VERSION = "1.39";
6              
7 3     3   237 use 5.005;
  3         11  
  3         170  
8              
9             =head1 NAME
10              
11             Tie::RefHash - use references as hash keys
12              
13             =head1 SYNOPSIS
14              
15             require 5.004;
16             use Tie::RefHash;
17             tie HASHVARIABLE, 'Tie::RefHash', LIST;
18             tie HASHVARIABLE, 'Tie::RefHash::Nestable', LIST;
19              
20             untie HASHVARIABLE;
21              
22             =head1 DESCRIPTION
23              
24             This module provides the ability to use references as hash keys if you
25             first C the hash variable to this module. Normally, only the
26             keys of the tied hash itself are preserved as references; to use
27             references as keys in hashes-of-hashes, use Tie::RefHash::Nestable,
28             included as part of Tie::RefHash.
29              
30             It is implemented using the standard perl TIEHASH interface. Please
31             see the C entry in perlfunc(1) and perltie(1) for more information.
32              
33             The Nestable version works by looking for hash references being stored
34             and converting them to tied hashes so that they too can have
35             references as keys. This will happen without warning whenever you
36             store a reference to one of your own hashes in the tied hash.
37              
38             =head1 EXAMPLE
39              
40             use Tie::RefHash;
41             tie %h, 'Tie::RefHash';
42             $a = [];
43             $b = {};
44             $c = \*main;
45             $d = \"gunk";
46             $e = sub { 'foo' };
47             %h = ($a => 1, $b => 2, $c => 3, $d => 4, $e => 5);
48             $a->[0] = 'foo';
49             $b->{foo} = 'bar';
50             for (keys %h) {
51             print ref($_), "\n";
52             }
53              
54             tie %h, 'Tie::RefHash::Nestable';
55             $h{$a}->{$b} = 1;
56             for (keys %h, keys %{$h{$a}}) {
57             print ref($_), "\n";
58             }
59              
60             =head1 THREAD SUPPORT
61              
62             L fully supports threading using the C method.
63              
64             =head1 STORABLE SUPPORT
65              
66             L hooks are provided for semantically correct serialization and
67             cloning of tied refhashes.
68              
69             =head1 RELIC SUPPORT
70              
71             This version of Tie::RefHash seems to no longer work with 5.004. This has not
72             been throughly investigated. Patches welcome ;-)
73              
74             =head1 LICENSE
75              
76             This program is free software; you can redistribute it and/or modify it under
77             the same terms as Perl itself
78              
79             =head1 MAINTAINER
80              
81             Yuval Kogman Enothingmuch@woobling.orgE
82              
83             =head1 AUTHOR
84              
85             Gurusamy Sarathy gsar@activestate.com
86              
87             'Nestable' by Ed Avis ed@membled.com
88              
89             =head1 SEE ALSO
90              
91             perl(1), perlfunc(1), perltie(1)
92              
93             =cut
94              
95 3     3   2992 use Tie::Hash;
  3         3262  
  3         89  
96 3     3   22 use vars '@ISA';
  3         7  
  3         145  
97             @ISA = qw(Tie::Hash);
98 3     3   14 use strict;
  3         6  
  3         109  
99 3     3   14 use Carp qw/croak/;
  3         5  
  3         160  
100              
101             BEGIN {
102 3     3   6 local $@;
103             # determine whether we need to take care of threads
104 3     3   18 use Config ();
  3         5  
  3         471  
105 3         2676 my $usethreads = $Config::Config{usethreads}; # && exists $INC{"threads.pm"}
106 3 50       16599 *_HAS_THREADS = $usethreads ? sub () { 1 } : sub () { 0 };
107 3 50       9 *_HAS_SCALAR_UTIL = eval { require Scalar::Util; 1 } ? sub () { 1 } : sub () { 0 };
  3         25  
  3         18  
108 3 50       488 *_HAS_WEAKEN = defined(&Scalar::Util::weaken) ? sub () { 1 } : sub () { 0 };
109             }
110              
111             BEGIN {
112             # create a refaddr function
113              
114 3     3   10 local $@;
115              
116 3         6 if ( _HAS_SCALAR_UTIL ) {
117 3         5367 Scalar::Util->import("refaddr");
118             } else {
119             require overload;
120              
121             *refaddr = sub {
122             if ( overload::StrVal($_[0]) =~ /\( 0x ([a-zA-Z0-9]+) \)$/x) {
123             return $1;
124             } else {
125             die "couldn't parse StrVal: " . overload::StrVal($_[0]);
126             }
127             };
128             }
129             }
130              
131             my (@thread_object_registry, $count); # used by the CLONE method to rehash the keys after their refaddr changed
132              
133             sub TIEHASH {
134 9     9   123928 my $c = shift;
135 9         25 my $s = [];
136 9         25 bless $s, $c;
137 9         37 while (@_) {
138 0         0 $s->STORE(shift, shift);
139             }
140              
141 9         13 if (_HAS_THREADS ) {
142              
143             if ( _HAS_WEAKEN ) {
144             # remember the object so that we can rekey it on CLONE
145             push @thread_object_registry, $s;
146             # but make this a weak reference, so that there are no leaks
147             Scalar::Util::weaken( $thread_object_registry[-1] );
148              
149             if ( ++$count > 1000 ) {
150             # this ensures we don't fill up with a huge array dead weakrefs
151             @thread_object_registry = grep { defined } @thread_object_registry;
152             $count = 0;
153             }
154             } else {
155             $count++; # used in the warning
156             }
157             }
158              
159 9         38 return $s;
160             }
161              
162             my $storable_format_version = join("/", __PACKAGE__, "0.01");
163              
164             sub STORABLE_freeze {
165 4     4 0 91 my ( $self, $is_cloning ) = @_;
166 4         5 my ( $refs, $reg ) = @$self;
167 4   100     260 return ( $storable_format_version, [ values %$refs ], $reg || {} );
168             }
169              
170             sub STORABLE_thaw {
171 4     4 0 42 my ( $self, $is_cloning, $version, $refs, $reg ) = @_;
172 4 50       11 croak "incompatible versions of Tie::RefHash between freeze and thaw"
173             unless $version eq $storable_format_version;
174              
175 4         10 @$self = ( {}, $reg );
176 4         10 $self->_reindex_keys( $refs );
177             }
178              
179             sub CLONE {
180 0     0   0 my $pkg = shift;
181              
182 0 0 0     0 if ( $count and not _HAS_WEAKEN ) {
183 0         0 warn "Tie::RefHash is not threadsafe without Scalar::Util::weaken";
184             }
185              
186             # when the thread has been cloned all the objects need to be updated.
187             # dead weakrefs are undefined, so we filter them out
188 0 0       0 @thread_object_registry = grep { defined && do { $_->_reindex_keys; 1 } } @thread_object_registry;
  0         0  
  0         0  
  0         0  
189 0         0 $count = 0; # we just cleaned up
190             }
191              
192             sub _reindex_keys {
193 4     4   21 my ( $self, $extra_keys ) = @_;
194             # rehash all the ref keys based on their new StrVal
195 4 50       7 %{ $self->[0] } = map { refaddr($_->[0]) => $_ } (values(%{ $self->[0] }), @{ $extra_keys || [] });
  4         37  
  4         11  
  4         8  
  4         18  
196             }
197              
198             sub FETCH {
199 625     625   42641 my($s, $k) = @_;
200 625 100       1579 if (ref $k) {
201 22         48 my $kstr = refaddr($k);
202 22 100       237 if (defined $s->[0]{$kstr}) {
203 20         109 $s->[0]{$kstr}[1];
204             }
205             else {
206 2         12 undef;
207             }
208             }
209             else {
210 603         7020 $s->[1]{$k};
211             }
212             }
213              
214             sub STORE {
215 95     95   7278 my($s, $k, $v) = @_;
216 95 100       204 if (ref $k) {
217 8         66 $s->[0]{refaddr($k)} = [$k, $v];
218             }
219             else {
220 87         246 $s->[1]{$k} = $v;
221             }
222 95         443 $v;
223             }
224              
225             sub DELETE {
226 64     64   5470 my($s, $k) = @_;
227 64 100 50     855 (ref $k)
228             ? (delete($s->[0]{refaddr($k)}) || [])->[1]
229             : delete($s->[1]{$k});
230             }
231              
232             sub EXISTS {
233 132     132   10638 my($s, $k) = @_;
234 132 100       1268 (ref $k) ? exists($s->[0]{refaddr($k)}) : exists($s->[1]{$k});
235             }
236              
237             sub FIRSTKEY {
238 594     594   98109 my $s = shift;
239 594         847 keys %{$s->[0]}; # reset iterator
  594         1176  
240 594         744 keys %{$s->[1]}; # reset iterator
  594         1005  
241 594         922 $s->[2] = 0; # flag for iteration, see NEXTKEY
242 594         1293 $s->NEXTKEY;
243             }
244              
245             sub NEXTKEY {
246 1173     1173   3863 my $s = shift;
247 1173         1532 my ($k, $v);
248 1173 100       2940 if (!$s->[2]) {
249 610 100       5457 if (($k, $v) = each %{$s->[0]}) {
  610         2064  
250 16         241 return $v->[0];
251             }
252             else {
253 594         1702 $s->[2] = 1;
254             }
255             }
256 1157         1329 return each %{$s->[1]};
  1157         13293  
257             }
258              
259             sub CLEAR {
260 2     2   3096 my $s = shift;
261 2         7 $s->[2] = 0;
262 2         4 %{$s->[0]} = ();
  2         7  
263 2         4 %{$s->[1]} = ();
  2         61  
264             }
265              
266             package Tie::RefHash::Nestable;
267 3     3   31 use vars '@ISA';
  3         6  
  3         483  
268             @ISA = 'Tie::RefHash';
269              
270             sub STORE {
271 45     45   6618 my($s, $k, $v) = @_;
272 45 100 66     169 if (ref($v) eq 'HASH' and not tied %$v) {
273 1         5 my @elems = %$v;
274 1         5 tie %$v, ref($s), @elems;
275             }
276 45         148 $s->SUPER::STORE($k, $v);
277             }
278              
279             1;