File Coverage

blib/lib/Hash/SafeKeys.pm
Criterion Covered Total %
statement 36 36 100.0
branch 6 6 100.0
condition n/a
subroutine 7 7 100.0
pod 3 3 100.0
total 52 52 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Hash::SafeKeys - get hash contents without resetting each iterator
4              
5             =head1 VERSION
6              
7             Version 0.04
8              
9             =cut
10              
11             =head1 SYNOPSIS
12              
13             use Hash::SafeKeys;
14             while (my ($k,$v) = each %hash) {
15             if (something_interesting_happens()) {
16             # get keys, values of %hash without resetting
17             # the 'each' iterator above
18             my @k = safekeys %hash;
19             my @v = safevalues %hash;
20             my %copy = safecopy %hash;
21             }
22             }
23              
24             =head1 DESCRIPTION
25              
26             Every hash variable in Perl has its own internal iterator,
27             accessed by the builtin C, C, and C
28             functions. The iterator is also implicitly used whenever
29             the hash is evaluated in list context. The iterator is
30             "reset" whenever C or C is called on a hash,
31             including the implicit calls when the hash is evaluated in
32             list context. That makes it dangerous to do certain hash
33             operations inside a C loop:
34              
35             while (my($k,$v) = each %hash) {
36             ...
37             @k = sort keys %hash; # Infinite loop!
38             @v = grep { /foo/ }, values %hash; # Ack!
39             print join ' ', %hash; # Run away!
40             }
41              
42             C provides alternate functions to access
43             the keys, values, or entire contents of a hash in a way
44             that does not reset the iterator, making them safe to use
45             in such contexts:
46              
47             while (my($k,$v) = each %hash) {
48             ...
49             @k = sort safekeys %hash; # Can do
50             @v = grep { /foo/ }, safevalues %hash; # No problem
51             print join ' ', safecopy %hash; # Right away, sir
52             }
53              
54             =head1 FUNCTIONS
55              
56             =head2 safekeys
57              
58             =head2 LIST = safekeys HASH
59              
60             Like the builtin L function, returns a list
61             consisting of all the keys of the named hash, in the same order
62             that the builtin function would return them in. Unlike C,
63             calling C does not reset the HASH's internal iterator
64             (see L).
65              
66             =head2 safevalues
67              
68             =head2 LIST = safevalues HASH
69              
70             Like the builtin L function, returns a list
71             consisting of all the values of the named hash, in the same order
72             that the builtin function would return them in. Unlike C,
73             calling C does not reset the HASH's internal iterator
74             (see L).
75              
76             =head2 safecopy
77              
78             =head2 LIST = safecopy HASH
79              
80             In list context, returns a shallow copy of the named HASH without
81             resetting the HASH's internal iterator. Usually, evaluating a HASH
82             in list context implicitly uses the internal iterator, resetting
83             any existing state
84              
85             =head2 save_iterator_state
86              
87             =head2 restore_iterator_state
88              
89             =head2 HANDLE = save_iterator_state($hashref)
90              
91             =head2 restore_iterator_state($hashref, HANDLE)
92              
93             Low-level functions to manipulate the iterator of a hash reference.
94             The use cases for directly using these functions are
95              
96             =over 4
97              
98             =item 1. Performance
99              
100             The absolute fastest way to I access the keys of a hash is:
101              
102             $handle = Hash::Safekeys::save_iterator_state( \%hash );
103             @keys = keys %hash;
104             Hash::Safekeys::restore_iterator_state( \%hash, $handle );
105              
106             This is an improvement over C<@keys = safekeys %hash> because it
107             eliminates the O(n) list copy operation on return from the
108             C function.
109              
110             =item 2. Access to aliased values
111              
112             The builtin C function returns aliases to the internal
113             hash values, allowing you to modify the contents of the hash
114             with constructions like
115              
116             s/foo/bar/g for values %hash
117              
118             As C returns a copy of the hash values,
119             C will B modify the contents
120             of the hash.
121              
122             To I modify the values of the hash, a workaround with the
123             low-level iterator functions is
124              
125             $handle = Hash::SafeKeys::save_iterator_state( \%hash );
126             for (values %hash) { ... modify($_) ... }
127             Hash::SafeKeys::restore_iterator_state( \%hash, $handle );
128              
129             =item 3. Nested each calls on the same hash
130              
131             This construction will not work if C<$hash1> and C<$hash2> refer
132             to the same hash:
133              
134             while (($key1,$val1) = each %$hash1) {
135             while (($key2,$val2) = each %$hash2) { ... }
136             }
137              
138             but this construction is I:
139              
140             while (($key1,$val1) = each %$hash1) {
141             $handle = Hash::SafeKeys::save_iterator_state($hash2);
142             while (($key2,$val2) = each %$hash2) { ... }
143             Hash::SafeKeys::restore_iterator_state($hash2, $handle);
144             }
145              
146             The HANDLE that is returned by C and used
147             as an input to C is currently implemented
148             as an integer that can be mapped internally to an original
149             hash iterator. This implementation is subject to change in future
150             releases and you should not rely on this value being an integer.
151              
152             It is a grave error to provide a different hash reference with the
153             handle to the C call than you provided to the
154             C call that created the handle.
155              
156             Calling C without later calling
157             C will leak memory.
158              
159             =back
160              
161             =head1 EXPORT
162              
163             L<"safekeys">, L<"safevalues">, and L<"safecopy"> are all
164             exported by default. Invoke L with the empty arg list
165              
166             use Hash::SafeKeys ();
167              
168             if you don't want these functions to be imported into the calling
169             package.
170              
171             The low-level iterator functions L<"save_iterator_state"> and
172             L<"restore_iterator_state"> may also be exported by including them
173             in the C call or by using the tag C<:all>
174              
175             use Hash::SafeKeys ':all'; # also exports low-level iterator funcs
176              
177             =head1 AUTHOR
178              
179             Marty O'Brien, C<< >>
180              
181             =head1 BUGS
182              
183             Please report any bugs or feature requests to
184             C, or through the web interface at
185             L.
186             I will be notified, and then you'll
187             automatically be notified of progress on your bug as I make changes.
188              
189             =head1 SUPPORT
190              
191             You can find documentation for this module with the perldoc command.
192              
193             perldoc Hash::SafeKeys
194              
195              
196             You can also look for information at:
197              
198             =over 4
199              
200             =item * RT: CPAN's request tracker (report bugs here)
201              
202             L
203              
204             =item * AnnoCPAN: Annotated CPAN documentation
205              
206             L
207              
208             =item * CPAN Ratings
209              
210             L
211              
212             =item * Search CPAN
213              
214             L
215              
216             =back
217              
218              
219             =head1 ACKNOWLEDGEMENTS
220              
221             The C method in the L module demonstrated how
222             to save and restore internal hash iterator state.
223             This module is indebted to the authors of this module and to
224             L<< user C at stackoverflow.com|http://stackoverflow.com/a/10921567/168857 >>
225             for directing me to it.
226              
227             A helpful comment by
228             L<>
229             let to further improvements.
230              
231             =head1 LICENSE AND COPYRIGHT
232              
233             Copyright 2012-2016 Marty O'Brien.
234              
235             This program is free software; you can redistribute it and/or modify it
236             under the terms of either: the GNU General Public License as published
237             by the Free Software Foundation; or the Artistic License.
238              
239             See http://dev.perl.org/licenses/ for more information.
240              
241             =cut
242              
243             package Hash::SafeKeys;
244              
245 3     3   14671 use strict;
  3         4  
  3         66  
246 3     3   9 use warnings;
  3         2  
  3         68  
247 3     3   8 use base qw(Exporter);
  3         7  
  3         378  
248             our @EXPORT = qw(safekeys safevalues safecopy);
249             our @EXPORT_OK = qw(save_iterator_state restore_iterator_state);
250             our %EXPORT_TAGS = ('all' => [@EXPORT, @EXPORT_OK]);
251             our $VERSION = '0.04';
252              
253             ## crutch for creating the XS code ...
254             #use Inline (Config => CLEAN_AFTER_BUILD => 0, FORCE_BUILD => 1,
255             # BUILD_NOISY => 1);
256             #use Inline 'C';
257              
258             # crutches off
259 3     3   10 use base qw(DynaLoader); bootstrap Hash::SafeKeys $VERSION;
  3         3  
  3         750  
260              
261             sub safekeys (\%) {
262 12     12 1 5142 my $hash = shift;
263 12         37 my $state = save_iterator_state($hash);
264 12 100       22 if (wantarray) {
265 9         31 my @keys = keys %$hash;
266 9         21 restore_iterator_state($hash,$state);
267 9         39 return @keys;
268             } else {
269 3         4 my $nkeys = keys %$hash;
270 3         8 restore_iterator_state($hash,$state);
271 3         6 return $nkeys;
272             }
273             }
274              
275             sub safevalues (\%) {
276 350     350 1 242401 my $hash = shift;
277 350         399 my $state = save_iterator_state($hash);
278 350 100       358 if (wantarray) {
279 347         11513 my @vals = values %$hash;
280 347         379 restore_iterator_state($hash,$state);
281 347         12009 return @vals;
282             } else {
283 3         5 my $nvals = values %$hash;
284 3         5 restore_iterator_state($hash,$state);
285 3         6 return $nvals;
286             }
287             }
288              
289             sub safecopy (\%) {
290 9     9 1 280 my $hash = shift;
291 9 100       24 return scalar %$hash if !wantarray; # scalar(%HASH) does not reset iter
292 8         25 my $state = save_iterator_state($hash);
293 8         30 my @copy = %$hash;
294 8         17 restore_iterator_state($hash,$state);
295 8         72 return @copy;
296             }
297              
298             1;
299              
300             __DATA__