Hash-SafeKeys-0.04/0000755003443400001440000000000012733024740014111 5ustar mobrien112generalHash-SafeKeys-0.04/lib/0000755003443400001440000000000012733024740014657 5ustar mobrien112generalHash-SafeKeys-0.04/lib/Hash/0000755003443400001440000000000012733024740015542 5ustar mobrien112generalHash-SafeKeys-0.04/lib/Hash/SafeKeys.pm0000644003443400001440000002512612733024670017622 0ustar mobrien112general=head1 NAME Hash::SafeKeys - get hash contents without resetting each iterator =head1 VERSION Version 0.04 =cut =head1 SYNOPSIS use Hash::SafeKeys; while (my ($k,$v) = each %hash) { if (something_interesting_happens()) { # get keys, values of %hash without resetting # the 'each' iterator above my @k = safekeys %hash; my @v = safevalues %hash; my %copy = safecopy %hash; } } =head1 DESCRIPTION Every hash variable in Perl has its own internal iterator, accessed by the builtin C, C, and C functions. The iterator is also implicitly used whenever the hash is evaluated in list context. The iterator is "reset" whenever C or C is called on a hash, including the implicit calls when the hash is evaluated in list context. That makes it dangerous to do certain hash operations inside a C loop: while (my($k,$v) = each %hash) { ... @k = sort keys %hash; # Infinite loop! @v = grep { /foo/ }, values %hash; # Ack! print join ' ', %hash; # Run away! } C provides alternate functions to access the keys, values, or entire contents of a hash in a way that does not reset the iterator, making them safe to use in such contexts: while (my($k,$v) = each %hash) { ... @k = sort safekeys %hash; # Can do @v = grep { /foo/ }, safevalues %hash; # No problem print join ' ', safecopy %hash; # Right away, sir } =head1 FUNCTIONS =head2 safekeys =head2 LIST = safekeys HASH Like the builtin L function, returns a list consisting of all the keys of the named hash, in the same order that the builtin function would return them in. Unlike C, calling C does not reset the HASH's internal iterator (see L). =head2 safevalues =head2 LIST = safevalues HASH Like the builtin L function, returns a list consisting of all the values of the named hash, in the same order that the builtin function would return them in. Unlike C, calling C does not reset the HASH's internal iterator (see L). =head2 safecopy =head2 LIST = safecopy HASH In list context, returns a shallow copy of the named HASH without resetting the HASH's internal iterator. Usually, evaluating a HASH in list context implicitly uses the internal iterator, resetting any existing state =head2 save_iterator_state =head2 restore_iterator_state =head2 HANDLE = save_iterator_state($hashref) =head2 restore_iterator_state($hashref, HANDLE) Low-level functions to manipulate the iterator of a hash reference. The use cases for directly using these functions are =over 4 =item 1. Performance The absolute fastest way to I access the keys of a hash is: $handle = Hash::Safekeys::save_iterator_state( \%hash ); @keys = keys %hash; Hash::Safekeys::restore_iterator_state( \%hash, $handle ); This is an improvement over C<@keys = safekeys %hash> because it eliminates the O(n) list copy operation on return from the C function. =item 2. Access to aliased values The builtin C function returns aliases to the internal hash values, allowing you to modify the contents of the hash with constructions like s/foo/bar/g for values %hash As C returns a copy of the hash values, C will B modify the contents of the hash. To I modify the values of the hash, a workaround with the low-level iterator functions is $handle = Hash::SafeKeys::save_iterator_state( \%hash ); for (values %hash) { ... modify($_) ... } Hash::SafeKeys::restore_iterator_state( \%hash, $handle ); =item 3. Nested each calls on the same hash This construction will not work if C<$hash1> and C<$hash2> refer to the same hash: while (($key1,$val1) = each %$hash1) { while (($key2,$val2) = each %$hash2) { ... } } but this construction is I: while (($key1,$val1) = each %$hash1) { $handle = Hash::SafeKeys::save_iterator_state($hash2); while (($key2,$val2) = each %$hash2) { ... } Hash::SafeKeys::restore_iterator_state($hash2, $handle); } The HANDLE that is returned by C and used as an input to C is currently implemented as an integer that can be mapped internally to an original hash iterator. This implementation is subject to change in future releases and you should not rely on this value being an integer. It is a grave error to provide a different hash reference with the handle to the C call than you provided to the C call that created the handle. Calling C without later calling C will leak memory. =back =head1 EXPORT L<"safekeys">, L<"safevalues">, and L<"safecopy"> are all exported by default. Invoke L with the empty arg list use Hash::SafeKeys (); if you don't want these functions to be imported into the calling package. The low-level iterator functions L<"save_iterator_state"> and L<"restore_iterator_state"> may also be exported by including them in the C call or by using the tag C<:all> use Hash::SafeKeys ':all'; # also exports low-level iterator funcs =head1 AUTHOR Marty O'Brien, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Hash::SafeKeys You can also look for information at: =over 4 =item * RT: CPAN's request tracker (report bugs here) L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 ACKNOWLEDGEMENTS The C method in the L module demonstrated how to save and restore internal hash iterator state. This module is indebted to the authors of this module and to L<< user C at stackoverflow.com|http://stackoverflow.com/a/10921567/168857 >> for directing me to it. A helpful comment by L<> let to further improvements. =head1 LICENSE AND COPYRIGHT Copyright 2012-2016 Marty O'Brien. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information. =cut package Hash::SafeKeys; use strict; use warnings; use base qw(Exporter); our @EXPORT = qw(safekeys safevalues safecopy); our @EXPORT_OK = qw(save_iterator_state restore_iterator_state); our %EXPORT_TAGS = ('all' => [@EXPORT, @EXPORT_OK]); our $VERSION = '0.04'; ## crutch for creating the XS code ... #use Inline (Config => CLEAN_AFTER_BUILD => 0, FORCE_BUILD => 1, # BUILD_NOISY => 1); #use Inline 'C'; # crutches off use base qw(DynaLoader); bootstrap Hash::SafeKeys $VERSION; sub safekeys (\%) { my $hash = shift; my $state = save_iterator_state($hash); if (wantarray) { my @keys = keys %$hash; restore_iterator_state($hash,$state); return @keys; } else { my $nkeys = keys %$hash; restore_iterator_state($hash,$state); return $nkeys; } } sub safevalues (\%) { my $hash = shift; my $state = save_iterator_state($hash); if (wantarray) { my @vals = values %$hash; restore_iterator_state($hash,$state); return @vals; } else { my $nvals = values %$hash; restore_iterator_state($hash,$state); return $nvals; } } sub safecopy (\%) { my $hash = shift; return scalar %$hash if !wantarray; # scalar(%HASH) does not reset iter my $state = save_iterator_state($hash); my @copy = %$hash; restore_iterator_state($hash,$state); return @copy; } 1; __DATA__ __C__ #define STATES_INITIAL_SIZE 10 struct _iterator_state { I32 riter; HE* eiter; }; typedef struct _iterator_state iterator_state; static int module_initialized = 0; iterator_state **STATES; int STATES_size; void _initialize() { int i; if (module_initialized) return; STATES = malloc(STATES_INITIAL_SIZE*sizeof(iterator_state *)); STATES_size = STATES_INITIAL_SIZE; for (i=0; i= STATES_size) { i = STATES_size; _resize_STATES(); } state->riter = HvRITER(hv); state->eiter = HvEITER(hv); STATES[i] = state; hv_iterinit(hv); return i; } int restore_iterator_state(SV* hvref, int i) { if (hvref == (SV*) 0) { warn("Hash::SafeKeys::restore_iterator_state: null input"); return 0; } HV* hv = (HV*) SvRV(hvref); if (hv == (HV*) 0) { warn("Hash::SafeKeys::restore_iterator_state: null input"); return 0; } _initialize(); if (i < 0 || i >= STATES_size) { warn("Hash::SafeKeys::restore_iterator_state: invalid restore key %d", i); return 0; } iterator_state *state = STATES[i]; if (state != (iterator_state*) 0) { HvRITER(hv) = state->riter; HvEITER(hv) = state->eiter; free(state); STATES[i] = (iterator_state*) 0; return 1; } warn("Hash::SafeKeys::restore_iterator_state: operation failed for key %d", i); STATES[i] = (iterator_state*) 0; return 0; } __END__ Hash-SafeKeys-0.04/META.yml0000664003443400001440000000107612733024740015370 0ustar mobrien112general--- abstract: 'get hash contents without resetting each iterator' author: - "Marty O'Brien " build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150005' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Hash-SafeKeys no_index: directory: - t - inc requires: Test::More: '0' version: '0.04' x_serialization_backend: 'CPAN::Meta::YAML version 0.016' Hash-SafeKeys-0.04/Changes0000644003443400001440000000075212733023000015374 0ustar mobrien112generalRevision history for Hash-SafeKeys 0.04 2016-06-23 improve performance for calling functions in scalar context expose,test,doc save_iterator_state, restore_iterator_state functions 0.03 2012-12-02 remove test that depends on two identical hashes returning keys in the same order. That feature will break in 5.18. No code changes. 0.02 2012-06-12 Backport to Perl v5.6. Added warning messages that no one should ever see. 0.01 2012-06-07 safekeys, safevalues, safecopy functions Hash-SafeKeys-0.04/t/0000755003443400001440000000000012733024740014354 5ustar mobrien112generalHash-SafeKeys-0.04/t/00-load.t0000644003443400001440000000026311765700033015676 0ustar mobrien112general#!perl -T use Test::More tests => 1; BEGIN { use_ok( 'Hash::SafeKeys' ) || print "Bail out!\n"; } diag( "Testing Hash::SafeKeys $Hash::SafeKeys::VERSION, Perl $], $^X" ); Hash-SafeKeys-0.04/t/manifest.t0000644003443400001440000000042011763762437016361 0ustar mobrien112general#!perl -T use strict; use warnings; use Test::More; unless ( $ENV{RELEASE_TESTING} ) { plan( skip_all => "Author tests not required for installation" ); } eval "use Test::CheckManifest 0.9"; plan skip_all => "Test::CheckManifest 0.9 required" if $@; ok_manifest(); Hash-SafeKeys-0.04/t/benchmark.pl0000644003443400001440000001011112733007442016635 0ustar mobrien112generaluse Benchmark ':all'; use lib 'blib/lib', 'blib/arch'; use Hash::SafeKeys; use Hash::StoredIterator qw(hash_get_iterator hash_set_iterator hkeys); use Time::HiRes; use strict; use warnings; # http://stackoverflow.com/questions/10921221/ # can-i-copy-a-hash-without-resetting-its-each-iterator # ?noredirect=1#comment63376080_10921221 # proposes Hash::StoredIterator as faster than Hash::SafeKeys # # and how does Hash::SafeKeys perform with large hashes ? my $t0 = Time::HiRes::time; my %hash = ( "aaaaa" .. "aczzz", foo => 123, bar => "456", baz => [ 3, 17, "Alpha", { "Bravo" => "Charlie", "Delta" => "Echo" }, [ "Foxtrot", "Golf", "Hotel" ], *STDERR, sub { my($i,$j,$k) = @_; return 42*$i+$j/$k; } ], quux => { "Lima" => "Mike", "November" => *Oscar, "Papa" => sub { "Quebec" }, "Romeo" => [ qw(Sierra Tango Uniform) ], "Victor" => { "Whiskey" => { "X-ray" => "Yankee" } }, "Zulu" => undef } ); close *Oscar if 0; # suppress "used only once" warning my $t1 = Time::HiRes::time; print "Size of test hash is ", scalar keys %hash, "\n"; printf "Setup time was %.3fs\n", $t1-$t0; my $rlist = timethese(1000, { builtin => sub { my @x = keys %hash; }, safekeys => sub { my @x = safekeys %hash; }, storediter1 => sub { my $hi = hash_get_iterator(\%hash); my @x = keys %hash; hash_set_iterator(\%hash,$hi); }, storediter2 => sub { my @x = hkeys %hash; }, saverestore => sub { my $hi = Hash::SafeKeys::save_iterator_state(\%hash); my @x = keys %hash; Hash::SafeKeys::restore_iterator_state(\%hash, $hi); }, } ); # scalar context my $rscalar = timethese(1000, { builtinSC => sub { my $x = keys %hash; }, storediter1SC => sub { my $hi = hash_get_iterator(\%hash); my $x = keys %hash; hash_set_iterator(\%hash,$hi); }, safekeysSC => sub { my $x = safekeys %hash; }, storediter2SC => sub { my $x = hkeys %hash }, saverestoreSC => sub { my $hi = Hash::SafeKeys::save_iterator_state(\%hash); my $x = keys %hash; Hash::SafeKeys::restore_iterator_state(\%hash, $hi); }, }); cmpthese( $rscalar ); cmpthese( $rlist ); exit; __END__ Results: Hash::SafeKeys v0.03 - Hash::StoredIterator v0.007, 26_368 keys SCALAR Rate safekeysSC storediter2SC builtinSC storediter1SC safekeysSC 50.9/s -- -1% -100% -100% storediter2SC 51.5/s 1% -- -100% -100% builtinSC Inf/s Inf% Inf% -- 0% storediter1SC Inf/s Inf% Inf% 0% -- LIST Rate safekeys storediter2 builtin storediter1 safekeys 28.0/s -- -2% -62% -63% storediter2 28.5/s 2% -- -62% -62% builtin 74.4/s 165% 161% -- -1% storediter1 75.3/s 169% 164% 1% -- TL;DR: custom keys method is 3 times costlier than native stored iterator + native keys does not have a performance drop-off scalar call on custom keys method is expensive ----- Hash::SafeKeys v0.04 - Hash::StoredIterator v0.007, 26_368 keys SCALAR Rate storediter2SC safekeysSC builtinSC storediter1SC storediter2SC 62.0/s -- -100% -100% -100% safekeysSC Inf/s Inf% -- 0% 0% builtinSC Inf/s Inf% 0% -- 0% storediter1SC Inf/s Inf% 0% 0% -- LIST Rate safekeys storediter2 storediter1 builtin safekeys 33.4/s -- -1% -62% -63% storediter2 33.7/s 1% -- -62% -63% storediter1 88.0/s 164% 161% -- -3% builtin 90.6/s 171% 169% 3% -- TL;DR: scalar custom keys call is now cheap in Hash::SafeKeys Hash-SafeKeys-0.04/t/01-safekeys.t0000644003443400001440000001031412733011154016563 0ustar mobrien112generaluse Hash::SafeKeys; use Test::More tests => 25; use strict; use warnings; my %hash = ( foo => 123, bar => "456", baz => [ 3, 17, "Alpha", { "Bravo" => "Charlie", "Delta" => "Echo" }, [ "Foxtrot", "Golf", "Hotel" ], *STDERR, sub { my($i,$j,$k) = @_; return 42*$i+$j/$k; } ], quux => { "Lima" => "Mike", "November" => *Oscar, "Papa" => sub { "Quebec" }, "Romeo" => [ qw(Sierra Tango Uniform) ], "Victor" => { "Whiskey" => { "X-ray" => "Yankee" } }, "Zulu" => undef } ); close *Oscar if 0; # suppress "used only once" warning ############################################################################# # do builtins reset the iterator? my ($k1,$v1) = each %hash; my ($k2,$v2) = each %hash; ok($k1 ne $k2, "each ok"); keys %hash; my ($k3,$v3) = each %hash; ok($k1 eq $k3 && $v1 eq $v3, "builtin keys resets iterator"); values %hash; my ($k4,$v4) = each %hash; ok($k1 eq $k4 && $v1 eq $v4, "builtin values resets iterator"); my %copy = %hash; scalar each %hash; my ($k5,$v5) = each %hash; ok($k2 eq $k5 && $v2 eq $v5, "list eval of hash resets iterator"); # builtins in scalar context keys %hash; ($k1,$v1) = each %hash; ($k2,$v2) = each %hash; my $n = keys %hash; ($k3,$v3) = each %hash; ok($k1 eq $k3 && $v1 eq $v3, 'scalar keys resets iterator'); $n = values %hash; ($k4,$v4) = each %hash; ok($k1 eq $k4 && $v1 eq $v4, 'scalar values reset iterator'); $n = %hash; ($k5,$v5) = each %hash; ok($k1 ne $k5, 'scalar %HASH does not reset iterator'); # is return for safekeys and safevalues same as keys and values? my @k1 = keys %hash; my @k2 = safekeys %hash; ok( @k1 > 0 && @k1 == @k2, 'safekeys returns data' ); ok( "@k1" eq "@k2" , 'safekeys returns same order as keys' ); my $nk1 = keys %hash; my $nk2 = safekeys %hash; ok($nk1 == $nk2, 'scalar safekeys returns same value as keys'); my @v1 = values %hash; my @v2 = safevalues %hash; ok( @v1 > 0 && @v1 == @v2, 'safevalues returns data' ); ok( join(q/;/,@v1) eq join(q/;/,@v2), 'safevalues returns same order as values' ); my $nv1 = values %hash; my $nv2 = safevalues %hash; ok($nv1==$nv2, 'scalar safevalues returns same value as values'); my $ns1 = %hash; my $ns2 = safecopy %hash; ok($ns1 eq $ns2, 'scalar safecopy returns same value as scalar HASH'); # is return for safekeys and safevalues the same inside each iterator ? # do safekeys/safevalues protect the iterator? keys %hash; # reset while ( my ($k6,$v6) = each %hash ) { ok( $k6 eq $k1 && $v6 eq $v1, 'iterator reset before inside each test' ); my @k3 = safekeys %hash; my @v3 = safevalues %hash; my ($k7,$v7) = each %hash; ok( "@k3" eq "@k1" , 'safekeys returns correct data, correct order after each' ); ok( join(q/;;/,@v3) eq join(q/;;/,@v1), 'safevalues returns correct data, correct order after each' ); ok( $k6 ne $k7 && $v6 ne $v7, 'each iterator was not reset after safekeys/safevalues' ); ok( $k7 eq $k2 && $v7 eq $v2, '2nd each call returns 2nd key/val pair' ); last; # ok, it's not much of a while loop } # the infinite loop tests my $count = 0; my %foo = (abc => 123, def => 456); while (each %foo) { last if $count++ > 100; keys %foo; values %foo; () = sort %foo; } ok($count >= 100, 'builtins inside each create infinite loop' ); keys %foo; $count = 0; while (each %foo) { last if $count++ > 100; safekeys %foo; safevalues %foo; my %foo2 = safecopy %foo; } ok($count < 3, 'safe versions do not create infinite loop' ); no warnings 'uninitialized'; keys %hash; my @kk0 = keys %{$hash{quux}}; my $vv0 = join q/--/, values %{$hash{quux}}; # with 5.17.6, 5.18, two identical hashes can return kv's in different order my $hh0 = join q/==/,sort %{$hash{quux}}; while (my($k,$v) = each %hash) { if (ref($v) eq 'HASH') { my $count = 0; my (@kk,@vv,%hh); while (my($kk,$vv) = each %$v) { last if ++$count > 100; @kk = safekeys %$v; @vv = safevalues %$v; %hh = safecopy %$v; } ok( $count < 10, 'second level each not an infinite loop' ); ok( "@kk0" eq "@kk", 'second level safekeys have correct data,order' ); ok( $vv0 eq join(q/--/,@vv), 'second level safevals have correct data,order' ); ok( $hh0 eq join(q/==/,sort %hh), 'second level safecopy has correct data' ); } } Hash-SafeKeys-0.04/t/02-lowlevel.t0000644003443400001440000000737512733022710016620 0ustar mobrien112generaluse Hash::SafeKeys ':all'; use Test::More tests => 18; use strict; use warnings; # exercise the low-level iterator functions save_iterator_state and # restore_iterator_state my %hash = ( foo => 123, bar => "456", baz => [ 3, 17, "Alpha", { "Bravo" => "Charlie", "Delta" => "Echo" }, [ "Foxtrot", "Golf", "Hotel" ], *STDERR, sub { my($i,$j,$k) = @_; return 42*$i+$j/$k; } ], quux => { "Lima" => "Mike", "November" => *Oscar, "Papa" => sub { "Quebec" }, "Romeo" => [ qw(Sierra Tango Uniform) ], "Victor" => { "Whiskey" => { "X-ray" => "Yankee" } }, "Zulu" => undef } ); close *Oscar if 0; # suppress "used only once" warning ############################################################################# my @k1 = keys %hash; my @v1 = values %hash; my @k2 = safekeys %hash; my @v2 = safevalues %hash; my $it = save_iterator_state(\%hash); my @k3 = keys %hash; my @v3 = values %hash; restore_iterator_state(\%hash, $it); ok("@k1" eq "@k3", 'keys from new iterator match keys'); ok("@k2" eq "@k3", 'keys from new iterator match safekeys'); ok("@v1" eq "@v3", 'keys from new iterator match values'); ok("@v2" eq "@v3", 'keys from new iterator match safevalues'); my @it = map { save_iterator_state(\%hash) } 0 .. 100; ok(1, 'multiple calls to save_iterator_state does not crash'); my $z = 0; $z += restore_iterator_state(\%hash, $_) for reverse @it; ok($z == @it, 'all restore calls successful'); ok(!restore_iterator_state(\%hash, $it[20]), 'extraneous restore_iterator_state is returns false'); # save way to call keys inside each my $c = 0; my %foo = (abc => 123, def => 456); while (each %foo) { last if $c++ > 100; keys %foo; } ok($c >= 100, 'builtin keys inside each makes infinite loop'); keys %foo; $c = 0; while (each %foo) { last if $c++ > 100; my $it = save_iterator_state(\%foo); keys %foo; restore_iterator_state(\%foo,$it); } ok($c < 100, 'builtin keys with iterator_state guard safe inside each'); # safely modify values my %bar = ('aa' .. 'zz'); $c = 0; while (each %bar) { foreach (values %bar) { s/ez/EZ/g; } last if $c++ > 1000; } ok($c >= 1000, 'builtin values inside each makes infinite loop'); ok($bar{"ey"} eq 'EZ', 'values modified'); $c = 0; keys %bar; while (each %bar) { foreach (safevalues %bar) { s/in/IN/; } last if $c++ > 1000; } ok($c < 1000, 'safevalues inside each is safe'); ok($bar{"im"} eq "in" && $bar{"im"} ne "IN", 'safevalues not modified'); keys %bar; $c = 0; while (each %bar) { my $it = save_iterator_state(\%bar); foreach (values %bar) { s/xz/XZ/; } restore_iterator_state(\%bar,$it); last if $c++ > 1000; } ok($c < 1000, 'values inside each is safe with iterator_state guard'); ok($bar{"xy"} eq 'XZ', 'values modified'); # nested each my $hash2 = { 1 .. 10 }; my $hash3 = { 1 .. 10 }; my $count = 0; my %r; EACH1: while (my ($k2,$v2) = each %$hash2) { while (my ($k3,$v3) = each %$hash3) { $count++; last EACH1 if $count > 100; $r{"$k2:$k3"}++; } } ok($count < 100, 'nested each ok for different hash'); keys %$hash2; $count = 0; my $hash4 = $hash2; EACH2: while (my ($k2,$v2) = each %$hash2) { while (my ($k4,$v4) = each %$hash4) { $count++; last EACH2 if $count > 100; $r{"$k2:$k4"}++; } } ok($count >= 100, 'nested each not ok for same hash'); keys %$hash2; $count = 0; EACH3: while (my ($k2,$v2) = each %$hash2) { my $it = Hash::SafeKeys::save_iterator_state($hash4); while (my ($k4,$v4) = each %$hash4) { $count++; last EACH3 if $count > 100; $r{"$k2:$k4"}++; } Hash::SafeKeys::restore_iterator_state($hash4, $it); } ok($count < 100, 'safe nested hash ok for same hash'); keys %$hash2; Hash-SafeKeys-0.04/MANIFEST0000644003443400001440000000051212733006066015241 0ustar mobrien112generalChanges lib/Hash/SafeKeys.pm Makefile.PL MANIFEST This list of files README SafeKeys.xs t/00-load.t t/01-safekeys.t t/02-lowlevel.t t/benchmark.pl t/manifest.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Hash-SafeKeys-0.04/META.json0000664003443400001440000000167112733024740015541 0ustar mobrien112general{ "abstract" : "get hash contents without resetting each iterator", "author" : [ "Marty O'Brien " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150005", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Hash-SafeKeys", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "version" : "0.04", "x_serialization_backend" : "JSON::PP version 2.27203" } Hash-SafeKeys-0.04/Makefile.PL0000644003443400001440000000112611764161014016062 0ustar mobrien112generaluse 5.006; use strict; use warnings; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Hash::SafeKeys', AUTHOR => q{Marty O'Brien }, VERSION_FROM => 'lib/Hash/SafeKeys.pm', ABSTRACT_FROM => 'lib/Hash/SafeKeys.pm', ($ExtUtils::MakeMaker::VERSION >= 6.3002 ? ('LICENSE'=> 'perl') : ()), PL_FILES => {}, PREREQ_PM => { 'Test::More' => 0, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'Hash-SafeKeys-* _Inline' }, ); Hash-SafeKeys-0.04/README0000644003443400001440000000403112732513752014774 0ustar mobrien112generalHash-SafeKeys Every hash variable in Perl has its own internal iterator, accessed by the builtin C, C, and C functions. The iterator is also implicitly used whenever the hash is evaluated in list context. The iterator is "reset" whenever C or C is called on a hash, including the implicit calls when the hash is evaluated in list context. That makes it dangerous to do certain hash operations inside a C loop: while (my($k,$v) = each %hash) { ... @k = sort keys %hash; # Infinite loop! @v = grep { /foo/ }, values %hash; # Ack! print join ' ', %hash; # Run away! } C provides alternate functions to access the keys, values, or entire contents of a hash in a way that does not reset the iterator, making them safe to use in such contexts: while (my($k,$v) = each %hash) { ... @k = sort safekeys %hash; # Can do @v = grep { /foo/ }, safevalues %hash; # No problem print join ' ', safecopy %hash; # Right away, sir } INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc Hash::SafeKeys You can also look for information at: RT, CPAN's request tracker (report bugs here) http://rt.cpan.org/NoAuth/Bugs.html?Dist=Hash-SafeKeys AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/Hash-SafeKeys CPAN Ratings http://cpanratings.perl.org/d/Hash-SafeKeys Search CPAN http://search.cpan.org/dist/Hash-SafeKeys/ LICENSE AND COPYRIGHT Copyright (C) 2012-2016 Marty O'Brien This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information. Hash-SafeKeys-0.04/SafeKeys.xs0000644003443400001440000000505612733024122016177 0ustar mobrien112general#include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* #include "INLINE.h" */ #define STATES_INITIAL_SIZE 10 struct _iterator_state { I32 riter; HE* eiter; }; typedef struct _iterator_state iterator_state; static int module_initialized = 0; iterator_state **STATES; int STATES_size; void _initialize() { int i; if (module_initialized) return; STATES = malloc(STATES_INITIAL_SIZE*sizeof(iterator_state *)); STATES_size = STATES_INITIAL_SIZE; for (i=0; i= STATES_size) { i = STATES_size; _resize_STATES(); } state->riter = HvRITER(hv); state->eiter = HvEITER(hv); STATES[i] = state; hv_iterinit(hv); return i; } int restore_iterator_state(SV* hvref, int i) { if (hvref == (SV*) 0) { warn("Hash::SafeKeys::restore_iterator_state: null input"); return 0; } HV* hv = (HV*) SvRV(hvref); if (hv == (HV*) 0) { warn("Hash::SafeKeys::restore_iterator_state: null input"); return 0; } _initialize(); if (i < 0 || i >= STATES_size) { warn("Hash::SafeKeys::restore_iterator_state: " "invalid restore key %d", i); return 0; } iterator_state *state = STATES[i]; if (state != (iterator_state*) 0) { HvRITER(hv) = state->riter; HvEITER(hv) = state->eiter; free(state); STATES[i] = (iterator_state*) 0; return 1; } warn("Hash::SafeKeys::restore_iterator_state: " "operation failed for key %d", i); STATES[i] = (iterator_state*) 0; return 0; } MODULE = Hash::SafeKeys PACKAGE = Hash::SafeKeys PROTOTYPES: DISABLE int save_iterator_state (hvref) SV * hvref int restore_iterator_state (hvref, i) SV * hvref int i