143003dfeSmillert#!perl -w 243003dfeSmillert 343003dfeSmillertuse strict; 443003dfeSmillertuse utf8; 543003dfeSmillertuse Tie::Hash; 6898184e3Ssthenuse Test::More; 743003dfeSmillert 843003dfeSmillertBEGIN {use_ok('XS::APItest')}; 943003dfeSmillert 1043003dfeSmillertsub preform_test; 1143003dfeSmillertsub test_present; 1243003dfeSmillertsub test_absent; 1343003dfeSmillertsub test_delete_present; 1443003dfeSmillertsub test_delete_absent; 1543003dfeSmillertsub brute_force_exists; 1643003dfeSmillertsub test_store; 1743003dfeSmillertsub test_fetch_present; 1843003dfeSmillertsub test_fetch_absent; 1943003dfeSmillert 2043003dfeSmillertmy $utf8_for_258 = chr 258; 2143003dfeSmillertutf8::encode $utf8_for_258; 2243003dfeSmillert 23b8851fccSafresh1my @testkeys = ('N', chr utf8::unicode_to_native(198), chr 256); 2443003dfeSmillertmy @keys = (@testkeys, $utf8_for_258); 2543003dfeSmillert 2643003dfeSmillertforeach (@keys) { 2743003dfeSmillert utf8::downgrade $_, 1; 2843003dfeSmillert} 2943003dfeSmillertmain_tests (\@keys, \@testkeys, ''); 3043003dfeSmillert 3143003dfeSmillertforeach (@keys) { 3243003dfeSmillert utf8::upgrade $_; 3343003dfeSmillert} 3443003dfeSmillertmain_tests (\@keys, \@testkeys, ' [utf8 hash]'); 3543003dfeSmillert 3643003dfeSmillert{ 3743003dfeSmillert my %h = (a=>'cheat'); 3843003dfeSmillert tie %h, 'Tie::StdHash'; 3943003dfeSmillert # is bug 36327 fixed? 4043003dfeSmillert my $result = ($] > 5.009) ? undef : 1; 4143003dfeSmillert 4243003dfeSmillert is (XS::APItest::Hash::store(\%h, chr 258, 1), $result); 4343003dfeSmillert 4443003dfeSmillert ok (!exists $h{$utf8_for_258}, 4543003dfeSmillert "hv_store doesn't insert a key with the raw utf8 on a tied hash"); 4643003dfeSmillert} 4743003dfeSmillert 48898184e3Ssthen{ 4943003dfeSmillert my $strtab = strtab(); 5043003dfeSmillert is (ref $strtab, 'HASH', "The shared string table quacks like a hash"); 5143003dfeSmillert my $wibble = "\0"; 5243003dfeSmillert eval { 5343003dfeSmillert $strtab->{$wibble}++; 5443003dfeSmillert }; 5543003dfeSmillert my $prefix = "Cannot modify shared string table in hv_"; 5643003dfeSmillert my $what = $prefix . 'fetch'; 5743003dfeSmillert like ($@, qr/^$what/,$what); 5843003dfeSmillert eval { 5943003dfeSmillert XS::APItest::Hash::store($strtab, 'Boom!', 1) 6043003dfeSmillert }; 6143003dfeSmillert $what = $prefix . 'store'; 6243003dfeSmillert like ($@, qr/^$what/, $what); 6343003dfeSmillert if (0) { 6443003dfeSmillert A::B->method(); 6543003dfeSmillert } 6643003dfeSmillert # DESTROY should be in there. 6743003dfeSmillert eval { 6843003dfeSmillert delete $strtab->{DESTROY}; 6943003dfeSmillert }; 7043003dfeSmillert $what = $prefix . 'delete'; 7143003dfeSmillert like ($@, qr/^$what/, $what); 7243003dfeSmillert # I can't work out how to get to the code that flips the wasutf8 flag on 7343003dfeSmillert # the hash key without some ikcy XS 7443003dfeSmillert} 7543003dfeSmillert 7643003dfeSmillert{ 7743003dfeSmillert is_deeply([&XS::APItest::Hash::test_hv_free_ent], [2,2,1,1], 7843003dfeSmillert "hv_free_ent frees the value immediately"); 7943003dfeSmillert is_deeply([&XS::APItest::Hash::test_hv_delayfree_ent], [2,2,2,1], 8043003dfeSmillert "hv_delayfree_ent keeps the value around until FREETMPS"); 8143003dfeSmillert} 8243003dfeSmillert 8343003dfeSmillertforeach my $in ("", "N", "a\0b") { 8443003dfeSmillert my $got = XS::APItest::Hash::test_share_unshare_pvn($in); 8543003dfeSmillert is ($got, $in, "test_share_unshare_pvn"); 8643003dfeSmillert} 8743003dfeSmillert 88898184e3Ssthen{ 8943003dfeSmillert foreach ([\&XS::APItest::Hash::rot13_hash, \&rot13, "rot 13"], 9043003dfeSmillert [\&XS::APItest::Hash::bitflip_hash, \&bitflip, "bitflip"], 9143003dfeSmillert ) { 9243003dfeSmillert my ($setup, $mapping, $name) = @$_; 9343003dfeSmillert my %hash; 9443003dfeSmillert my %placebo = (a => 1, p => 2, i => 4, e => 8); 9543003dfeSmillert $setup->(\%hash); 9643003dfeSmillert $hash{a}++; @hash{qw(p i e)} = (2, 4, 8); 9743003dfeSmillert 9843003dfeSmillert test_U_hash(\%hash, \%placebo, [f => 9, g => 10, h => 11], $mapping, 9943003dfeSmillert $name); 10043003dfeSmillert } 10143003dfeSmillert foreach my $upgrade_o (0, 1) { 10243003dfeSmillert foreach my $upgrade_n (0, 1) { 10343003dfeSmillert my (%hash, %placebo); 10443003dfeSmillert XS::APItest::Hash::bitflip_hash(\%hash); 105b8851fccSafresh1 foreach my $new (["7", utf8::unicode_to_native(65), 106b8851fccSafresh1 utf8::unicode_to_native(67), 107b8851fccSafresh1 utf8::unicode_to_native(80) 108b8851fccSafresh1 ], 109b8851fccSafresh1 ["8", utf8::unicode_to_native(163), 110b8851fccSafresh1 utf8::unicode_to_native(171), 111b8851fccSafresh1 utf8::unicode_to_native(215) 112b8851fccSafresh1 ], 11343003dfeSmillert ["U", 2603, 2604, 2604], 11443003dfeSmillert ) { 115b8851fccSafresh1 foreach my $code (utf8::unicode_to_native(78), 116b8851fccSafresh1 utf8::unicode_to_native(240), 117b8851fccSafresh1 256, 118b8851fccSafresh1 1336 119b8851fccSafresh1 ) { 12043003dfeSmillert my $key = chr $code; 12143003dfeSmillert # This is the UTF-8 byte sequence for the key. 12243003dfeSmillert my $key_utf8 = $key; 12343003dfeSmillert utf8::encode($key_utf8); 12443003dfeSmillert if ($upgrade_o) { 12543003dfeSmillert $key .= chr 256; 12643003dfeSmillert chop $key; 12743003dfeSmillert } 12843003dfeSmillert $hash{$key} = $placebo{$key} = $code; 12943003dfeSmillert $hash{$key_utf8} = $placebo{$key_utf8} = "$code as UTF-8"; 13043003dfeSmillert } 13143003dfeSmillert my $name = 'bitflip ' . shift @$new; 13243003dfeSmillert my @new_kv; 13343003dfeSmillert foreach my $code (@$new) { 13443003dfeSmillert my $key = chr $code; 13543003dfeSmillert if ($upgrade_n) { 13643003dfeSmillert $key .= chr 256; 13743003dfeSmillert chop $key; 13843003dfeSmillert } 13943003dfeSmillert push @new_kv, $key, $_; 14043003dfeSmillert } 14143003dfeSmillert 14243003dfeSmillert $name .= ' upgraded(orig) ' if $upgrade_o; 14343003dfeSmillert $name .= ' upgraded(new) ' if $upgrade_n; 14443003dfeSmillert test_U_hash(\%hash, \%placebo, \@new_kv, \&bitflip, $name); 14543003dfeSmillert } 14643003dfeSmillert } 14743003dfeSmillert } 14843003dfeSmillert} 14943003dfeSmillert 15043003dfeSmillertsub test_precomputed_hashes { 15143003dfeSmillert my $what = shift; 15243003dfeSmillert my $hash_it = shift; 15343003dfeSmillert my $ord = shift; 15443003dfeSmillert my $key_copy = $_[0]; 15543003dfeSmillert $key_copy .= ''; 15643003dfeSmillert 15743003dfeSmillert my %hash; 15843003dfeSmillert is (XS::APItest::Hash::common({hv => \%hash, 15943003dfeSmillert "key$what" => $_[0], 16043003dfeSmillert val => $ord, 16143003dfeSmillert "hash_$what" => $hash_it, 16243003dfeSmillert action => XS::APItest::HV_FETCH_ISSTORE}), 16343003dfeSmillert $ord, "store $ord with $what \$hash_it = $hash_it"); 16443003dfeSmillert is_deeply ([each %hash], [$_[0], $ord], "First key read is good"); 16543003dfeSmillert is_deeply ([each %hash], [], "No second key good"); 16643003dfeSmillert 16743003dfeSmillert is ($hash{$_[0]}, $ord, "Direct hash read finds $ord"); 16843003dfeSmillert 16943003dfeSmillert is_deeply ([each %hash], [$key_copy, $ord], 17043003dfeSmillert "First key read is good with a copy"); 17143003dfeSmillert is_deeply ([each %hash], [], "No second key good"); 17243003dfeSmillert 17343003dfeSmillert is ($hash{$key_copy}, $ord, "Direct hash read finds $ord"); 17443003dfeSmillert} 17543003dfeSmillert 17643003dfeSmillert{ 17743003dfeSmillert my $as_utf8 = "\241" . chr 256; 17843003dfeSmillert chop $as_utf8; 17943003dfeSmillert my $as_bytes = "\243"; 18043003dfeSmillert foreach my $key ('N', $as_bytes, $as_utf8, "\x{2623}") { 18143003dfeSmillert my $ord = ord $key; 18243003dfeSmillert foreach my $hash_it (0, 1) { 18343003dfeSmillert foreach my $what (qw(pv sv)) { 18443003dfeSmillert test_precomputed_hashes($what, $hash_it, $ord, $key); 18543003dfeSmillert } 18643003dfeSmillert # Generate a shared hash key scalar 18743003dfeSmillert my %h = ($key => 1); 18843003dfeSmillert test_precomputed_hashes('sv', $hash_it, $ord, (keys %h)[0]); 18943003dfeSmillert } 19043003dfeSmillert } 19143003dfeSmillert} 19243003dfeSmillert 193898184e3Ssthen{ 194*eac174f2Safresh1 no warnings 'experimental::builtin'; 195*eac174f2Safresh1 use builtin 'weaken'; 196898184e3Ssthen my %h; 197898184e3Ssthen fill_hash_with_nulls(\%h); 198898184e3Ssthen my @objs; 199898184e3Ssthen for("a".."z","A".."Z") { 200898184e3Ssthen weaken($objs[@objs] = $h{$_} = []); 201898184e3Ssthen } 202898184e3Ssthen undef %h; 203898184e3Ssthen no warnings 'uninitialized'; 204898184e3Ssthen local $" = ""; 205898184e3Ssthen is "@objs", "", 206898184e3Ssthen 'explicitly undeffing a hash with nulls frees all entries'; 207898184e3Ssthen 208898184e3Ssthen my $h = {}; 209898184e3Ssthen fill_hash_with_nulls($h); 210898184e3Ssthen @objs = (); 211898184e3Ssthen for("a".."z","A".."Z") { 212898184e3Ssthen weaken($objs[@objs] = $$h{$_} = []); 213898184e3Ssthen } 214898184e3Ssthen undef $h; 215898184e3Ssthen is "@objs", "", 'freeing a hash with nulls frees all entries'; 216898184e3Ssthen} 217898184e3Ssthen 218898184e3Ssthen# Tests for HvENAME and UTF8 219898184e3Ssthen{ 220898184e3Ssthen no strict; 221898184e3Ssthen no warnings 'void'; 222898184e3Ssthen my $hvref; 223898184e3Ssthen 224898184e3Ssthen *{"\xff::bar"}; # autovivify %ÿ:: without UTF8 225898184e3Ssthen *{"\xff::bαr::"} = $hvref = \%foo::; 226898184e3Ssthen undef *foo::; 227898184e3Ssthen is HvENAME($hvref), "\xff::bαr", 228898184e3Ssthen 'stash alias (utf8 inside bytes) does not create malformed UTF8'; 229898184e3Ssthen 230898184e3Ssthen *{"é::foo"}; # autovivify %é:: with UTF8 231898184e3Ssthen *{"\xe9::\xe9::"} = $hvref = \%bar::; 232898184e3Ssthen undef *bar::; 233898184e3Ssthen is HvENAME($hvref), "\xe9::\xe9", 234898184e3Ssthen 'stash alias (bytes inside utf8) does not create malformed UTF8'; 235898184e3Ssthen 236898184e3Ssthen *{"\xfe::bar"}; *{"\xfd::bar"}; 237898184e3Ssthen *{"\xfe::bαr::"} = \%goo::; 238898184e3Ssthen *{"\xfd::bαr::"} = $hvref = \%goo::; 239898184e3Ssthen undef *goo::; 240898184e3Ssthen like HvENAME($hvref), qr/^[\xfe\xfd]::bαr\z/, 241898184e3Ssthen 'multiple stash aliases (utf8 inside bytes) do not cause bad UTF8'; 242898184e3Ssthen 243898184e3Ssthen *{"è::foo"}; *{"ë::foo"}; 244898184e3Ssthen *{"\xe8::\xe9::"} = $hvref = \%bear::; 245898184e3Ssthen *{"\xeb::\xe9::"} = \%bear::; 246898184e3Ssthen undef *bear::; 247898184e3Ssthen like HvENAME($hvref), qr"^[\xe8\xeb]::\xe9\z", 248898184e3Ssthen 'multiple stash aliases (bytes inside utf8) do not cause bad UTF8'; 249898184e3Ssthen} 250898184e3Ssthen 251898184e3Ssthen{ # newHVhv 252898184e3Ssthen use Tie::Hash; 253898184e3Ssthen tie my %h, 'Tie::StdHash'; 254898184e3Ssthen %h = 1..10; 255898184e3Ssthen is join(' ', sort %{newHVhv \%h}), '1 10 2 3 4 5 6 7 8 9', 256898184e3Ssthen 'newHVhv on tied hash'; 257898184e3Ssthen} 258898184e3Ssthen 259898184e3Ssthen# helem and hslice on entry with null value 260898184e3Ssthen# This is actually a test for a Perl operator, not an XS API test. But it 261898184e3Ssthen# requires a hash that can only be produced by XS (although recently it 262898184e3Ssthen# could be encountered when tying hint hashes). 263898184e3Ssthen{ 264898184e3Ssthen my %h; 265898184e3Ssthen fill_hash_with_nulls(\%h); 266898184e3Ssthen eval{ $h{84} = 1 }; 267898184e3Ssthen pass 'no crash when writing to hash elem with null value'; 268898184e3Ssthen eval{ no # silly 269898184e3Ssthen warnings; # thank you! 270898184e3Ssthen @h{85} = 1 }; 271898184e3Ssthen pass 'no crash when writing to hash elem with null value via slice'; 27291f110e0Safresh1 eval { delete local $h{86} }; 27391f110e0Safresh1 pass 'no crash during local deletion of hash elem with null value'; 27491f110e0Safresh1 eval { delete local @h{87,88} }; 27591f110e0Safresh1 pass 'no crash during local deletion of hash slice with null values'; 276898184e3Ssthen} 277898184e3Ssthen 27891f110e0Safresh1# [perl #111000] Bug number eleventy-one thousand: 27991f110e0Safresh1# hv_store should work on hint hashes 28091f110e0Safresh1eval q{ 28191f110e0Safresh1 BEGIN { 28291f110e0Safresh1 XS::APItest::Hash::store \%^H, "XS::APItest/hash.t", undef; 28391f110e0Safresh1 delete $^H{"XS::APItest/hash.t"}; 28491f110e0Safresh1 } 28591f110e0Safresh1}; 28691f110e0Safresh1pass("hv_store works on the hint hash"); 28791f110e0Safresh1 2886fb12b70Safresh1{ 2896fb12b70Safresh1 # [perl #79074] HeSVKEY_force loses UTF8ness 2906fb12b70Safresh1 my %hash = ( "\xff" => 1, "\x{100}" => 1 ); 2916fb12b70Safresh1 my @keys = sort ( XS::APItest::Hash::test_force_keys(\%hash) ); 2926fb12b70Safresh1 is_deeply(\@keys, [ sort keys %hash ], "check HeSVKEY_force()"); 2936fb12b70Safresh1} 2946fb12b70Safresh1 295*eac174f2Safresh1# Test that mg_copy is called when expected (and not called when not) 296*eac174f2Safresh1# No (other) tests in core will fail if the implementation of `keys %tied_hash` 297*eac174f2Safresh1# is (accidentally) changed to also call hv_iterval() and trigger mg_copy. 298*eac174f2Safresh1# However, this behaviour is visible, and tested by Variable::Magic on CPAN. 299*eac174f2Safresh1 300*eac174f2Safresh1{ 301*eac174f2Safresh1 my %h; 302*eac174f2Safresh1 my $obj = tie %h, 'Tie::StdHash'; 303*eac174f2Safresh1 sv_magic_mycopy(\%h); 304*eac174f2Safresh1 305*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 0); 306*eac174f2Safresh1 307*eac174f2Safresh1 $h{perl} = "rules"; 308*eac174f2Safresh1 309*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 1); 310*eac174f2Safresh1 311*eac174f2Safresh1 is($h{perl}, "rules", "found key"); 312*eac174f2Safresh1 313*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 2); 314*eac174f2Safresh1 315*eac174f2Safresh1 # keys *doesn't* trigger copy magic, so the count is still 2 316*eac174f2Safresh1 my @flat = keys %h; 317*eac174f2Safresh1 318*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 2); 319*eac174f2Safresh1 320*eac174f2Safresh1 @flat = values %h; 321*eac174f2Safresh1 322*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 3); 323*eac174f2Safresh1 324*eac174f2Safresh1 @flat = each %h; 325*eac174f2Safresh1 326*eac174f2Safresh1 is(sv_magic_mycopy_count(\%h), 4); 327*eac174f2Safresh1} 328*eac174f2Safresh1 329*eac174f2Safresh1{ 330*eac174f2Safresh1 # There are two API variants - hv_delete and hv_delete_ent. The Perl 331*eac174f2Safresh1 # interpreter exclusively uses hv_delete_ent. Only XS code uses hv_delete. 332*eac174f2Safresh1 # Hence the problem case could only be triggered by XS code called on 333*eac174f2Safresh1 # symbol tables, and with particular non-ASCII keys: 334*eac174f2Safresh1 335*eac174f2Safresh1 # Deleting a key with WASUTF from a stash used to trigger a use-after free: 336*eac174f2Safresh1 my $key = "\xFF\x{100}"; 337*eac174f2Safresh1 chop $key; 338*eac174f2Safresh1 ++$main::{$key}; 339*eac174f2Safresh1 is(XS::APItest::Hash::delete(\%main::, $key), 1, 340*eac174f2Safresh1 "hv_delete doesn't trigger a use-after free"); 341*eac174f2Safresh1 342*eac174f2Safresh1 # Perl code has always used this API, which never had the problem: 343*eac174f2Safresh1 ++$main::{$key}; 344*eac174f2Safresh1 is(XS::APItest::Hash::delete_ent(\%main::, $key), 1, 345*eac174f2Safresh1 "hv_delete_ent never triggered a use-after free, but test it anyway"); 346*eac174f2Safresh1} 347*eac174f2Safresh1 348898184e3Ssthendone_testing; 34943003dfeSmillertexit; 35043003dfeSmillert 35143003dfeSmillert################################ The End ################################ 35243003dfeSmillert 35343003dfeSmillertsub test_U_hash { 35443003dfeSmillert my ($hash, $placebo, $new, $mapping, $message) = @_; 35543003dfeSmillert my @hitlist = keys %$placebo; 35643003dfeSmillert print "# $message\n"; 35743003dfeSmillert 35843003dfeSmillert my @keys = sort keys %$hash; 35943003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo))), 36043003dfeSmillert "uvar magic called exactly once on store"); 36143003dfeSmillert 36243003dfeSmillert is (keys %$hash, keys %$placebo); 36343003dfeSmillert 36443003dfeSmillert my $victim = shift @hitlist; 36543003dfeSmillert is (delete $hash->{$victim}, delete $placebo->{$victim}); 36643003dfeSmillert 36743003dfeSmillert is (keys %$hash, keys %$placebo); 36843003dfeSmillert @keys = sort keys %$hash; 36943003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 37043003dfeSmillert 37143003dfeSmillert $victim = shift @hitlist; 37243003dfeSmillert is (XS::APItest::Hash::delete_ent ($hash, $victim, 37343003dfeSmillert XS::APItest::HV_DISABLE_UVAR_XKEY), 37443003dfeSmillert undef, "Deleting a known key with conversion disabled fails (ent)"); 37543003dfeSmillert is (keys %$hash, keys %$placebo); 37643003dfeSmillert 37743003dfeSmillert is (XS::APItest::Hash::delete_ent ($hash, $victim, 0), 37843003dfeSmillert delete $placebo->{$victim}, 37943003dfeSmillert "Deleting a known key with conversion enabled works (ent)"); 38043003dfeSmillert is (keys %$hash, keys %$placebo); 38143003dfeSmillert @keys = sort keys %$hash; 38243003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 38343003dfeSmillert 38443003dfeSmillert $victim = shift @hitlist; 38543003dfeSmillert is (XS::APItest::Hash::delete ($hash, $victim, 38643003dfeSmillert XS::APItest::HV_DISABLE_UVAR_XKEY), 38743003dfeSmillert undef, "Deleting a known key with conversion disabled fails"); 38843003dfeSmillert is (keys %$hash, keys %$placebo); 38943003dfeSmillert 39043003dfeSmillert is (XS::APItest::Hash::delete ($hash, $victim, 0), 39143003dfeSmillert delete $placebo->{$victim}, 39243003dfeSmillert "Deleting a known key with conversion enabled works"); 39343003dfeSmillert is (keys %$hash, keys %$placebo); 39443003dfeSmillert @keys = sort keys %$hash; 39543003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 39643003dfeSmillert 39743003dfeSmillert my ($k, $v) = splice @$new, 0, 2; 39843003dfeSmillert $hash->{$k} = $v; 39943003dfeSmillert $placebo->{$k} = $v; 40043003dfeSmillert is (keys %$hash, keys %$placebo); 40143003dfeSmillert @keys = sort keys %$hash; 40243003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 40343003dfeSmillert 40443003dfeSmillert ($k, $v) = splice @$new, 0, 2; 40543003dfeSmillert is (XS::APItest::Hash::store_ent($hash, $k, $v), $v, "store_ent"); 40643003dfeSmillert $placebo->{$k} = $v; 40743003dfeSmillert is (keys %$hash, keys %$placebo); 40843003dfeSmillert @keys = sort keys %$hash; 40943003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 41043003dfeSmillert 41143003dfeSmillert ($k, $v) = splice @$new, 0, 2; 41243003dfeSmillert is (XS::APItest::Hash::store($hash, $k, $v), $v, "store"); 41343003dfeSmillert $placebo->{$k} = $v; 41443003dfeSmillert is (keys %$hash, keys %$placebo); 41543003dfeSmillert @keys = sort keys %$hash; 41643003dfeSmillert is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); 41743003dfeSmillert 41843003dfeSmillert @hitlist = keys %$placebo; 41943003dfeSmillert $victim = shift @hitlist; 42043003dfeSmillert is (XS::APItest::Hash::fetch_ent($hash, $victim), $placebo->{$victim}, 42143003dfeSmillert "fetch_ent"); 42243003dfeSmillert is (XS::APItest::Hash::fetch_ent($hash, $mapping->($victim)), undef, 42343003dfeSmillert "fetch_ent (missing)"); 42443003dfeSmillert 42543003dfeSmillert $victim = shift @hitlist; 42643003dfeSmillert is (XS::APItest::Hash::fetch($hash, $victim), $placebo->{$victim}, 42743003dfeSmillert "fetch"); 42843003dfeSmillert is (XS::APItest::Hash::fetch($hash, $mapping->($victim)), undef, 42943003dfeSmillert "fetch (missing)"); 43043003dfeSmillert 43143003dfeSmillert $victim = shift @hitlist; 43243003dfeSmillert ok (XS::APItest::Hash::exists_ent($hash, $victim), "exists_ent"); 43343003dfeSmillert ok (!XS::APItest::Hash::exists_ent($hash, $mapping->($victim)), 43443003dfeSmillert "exists_ent (missing)"); 43543003dfeSmillert 43643003dfeSmillert $victim = shift @hitlist; 43743003dfeSmillert die "Need a victim" unless defined $victim; 43843003dfeSmillert ok (XS::APItest::Hash::exists($hash, $victim), "exists"); 43943003dfeSmillert ok (!XS::APItest::Hash::exists($hash, $mapping->($victim)), 44043003dfeSmillert "exists (missing)"); 44143003dfeSmillert 44243003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keysv => $victim}), 44343003dfeSmillert $placebo->{$victim}, "common (fetch)"); 44443003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keypv => $victim}), 44543003dfeSmillert $placebo->{$victim}, "common (fetch pv)"); 44643003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keysv => $victim, 44743003dfeSmillert action => XS::APItest::HV_DISABLE_UVAR_XKEY}), 44843003dfeSmillert undef, "common (fetch) missing"); 44943003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keypv => $victim, 45043003dfeSmillert action => XS::APItest::HV_DISABLE_UVAR_XKEY}), 45143003dfeSmillert undef, "common (fetch pv) missing"); 45243003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keysv => $mapping->($victim), 45343003dfeSmillert action => XS::APItest::HV_DISABLE_UVAR_XKEY}), 45443003dfeSmillert $placebo->{$victim}, "common (fetch) missing mapped"); 45543003dfeSmillert is (XS::APItest::Hash::common({hv => $hash, keypv => $mapping->($victim), 45643003dfeSmillert action => XS::APItest::HV_DISABLE_UVAR_XKEY}), 45743003dfeSmillert $placebo->{$victim}, "common (fetch pv) missing mapped"); 45843003dfeSmillert} 45943003dfeSmillert 46043003dfeSmillertsub main_tests { 46143003dfeSmillert my ($keys, $testkeys, $description) = @_; 46243003dfeSmillert foreach my $key (@$testkeys) { 463b8851fccSafresh1 my $lckey = ($key eq chr utf8::unicode_to_native(198)) ? chr utf8::unicode_to_native(230) : lc $key; 46443003dfeSmillert my $unikey = $key; 46543003dfeSmillert utf8::encode $unikey; 46643003dfeSmillert 46743003dfeSmillert utf8::downgrade $key, 1; 46843003dfeSmillert utf8::downgrade $lckey, 1; 46943003dfeSmillert utf8::downgrade $unikey, 1; 47043003dfeSmillert main_test_inner ($key, $lckey, $unikey, $keys, $description); 47143003dfeSmillert 47243003dfeSmillert utf8::upgrade $key; 47343003dfeSmillert utf8::upgrade $lckey; 47443003dfeSmillert utf8::upgrade $unikey; 47543003dfeSmillert main_test_inner ($key, $lckey, $unikey, $keys, 47643003dfeSmillert $description . ' [key utf8 on]'); 47743003dfeSmillert } 47843003dfeSmillert 47943003dfeSmillert # hv_exists was buggy for tied hashes, in that the raw utf8 key was being 48043003dfeSmillert # used - the utf8 flag was being lost. 48143003dfeSmillert perform_test (\&test_absent, (chr 258), $keys, ''); 48243003dfeSmillert 48343003dfeSmillert perform_test (\&test_fetch_absent, (chr 258), $keys, ''); 48443003dfeSmillert perform_test (\&test_delete_absent, (chr 258), $keys, ''); 48543003dfeSmillert} 48643003dfeSmillert 48743003dfeSmillertsub main_test_inner { 48843003dfeSmillert my ($key, $lckey, $unikey, $keys, $description) = @_; 48943003dfeSmillert perform_test (\&test_present, $key, $keys, $description); 49043003dfeSmillert perform_test (\&test_fetch_present, $key, $keys, $description); 49143003dfeSmillert perform_test (\&test_delete_present, $key, $keys, $description); 49243003dfeSmillert 49343003dfeSmillert perform_test (\&test_store, $key, $keys, $description, [a=>'cheat']); 49443003dfeSmillert perform_test (\&test_store, $key, $keys, $description, []); 49543003dfeSmillert 49643003dfeSmillert perform_test (\&test_absent, $lckey, $keys, $description); 49743003dfeSmillert perform_test (\&test_fetch_absent, $lckey, $keys, $description); 49843003dfeSmillert perform_test (\&test_delete_absent, $lckey, $keys, $description); 49943003dfeSmillert 50043003dfeSmillert return if $unikey eq $key; 50143003dfeSmillert 50243003dfeSmillert perform_test (\&test_absent, $unikey, $keys, $description); 50343003dfeSmillert perform_test (\&test_fetch_absent, $unikey, $keys, $description); 50443003dfeSmillert perform_test (\&test_delete_absent, $unikey, $keys, $description); 50543003dfeSmillert} 50643003dfeSmillert 50743003dfeSmillertsub perform_test { 50843003dfeSmillert my ($test_sub, $key, $keys, $message, @other) = @_; 50943003dfeSmillert my $printable = join ',', map {ord} split //, $key; 51043003dfeSmillert 51143003dfeSmillert my (%hash, %tiehash); 51243003dfeSmillert tie %tiehash, 'Tie::StdHash'; 51343003dfeSmillert 51443003dfeSmillert @hash{@$keys} = @$keys; 51543003dfeSmillert @tiehash{@$keys} = @$keys; 51643003dfeSmillert 51743003dfeSmillert &$test_sub (\%hash, $key, $printable, $message, @other); 51843003dfeSmillert &$test_sub (\%tiehash, $key, $printable, "$message tie", @other); 51943003dfeSmillert} 52043003dfeSmillert 52143003dfeSmillertsub test_present { 52243003dfeSmillert my ($hash, $key, $printable, $message) = @_; 52343003dfeSmillert 52443003dfeSmillert ok (exists $hash->{$key}, "hv_exists_ent present$message $printable"); 52543003dfeSmillert ok (XS::APItest::Hash::exists ($hash, $key), 52643003dfeSmillert "hv_exists present$message $printable"); 52743003dfeSmillert} 52843003dfeSmillert 52943003dfeSmillertsub test_absent { 53043003dfeSmillert my ($hash, $key, $printable, $message) = @_; 53143003dfeSmillert 53243003dfeSmillert ok (!exists $hash->{$key}, "hv_exists_ent absent$message $printable"); 53343003dfeSmillert ok (!XS::APItest::Hash::exists ($hash, $key), 53443003dfeSmillert "hv_exists absent$message $printable"); 53543003dfeSmillert} 53643003dfeSmillert 53743003dfeSmillertsub test_delete_present { 53843003dfeSmillert my ($hash, $key, $printable, $message) = @_; 53943003dfeSmillert 54043003dfeSmillert my $copy = {}; 54143003dfeSmillert my $class = tied %$hash; 54243003dfeSmillert if (defined $class) { 54343003dfeSmillert tie %$copy, ref $class; 54443003dfeSmillert } 54543003dfeSmillert $copy = {%$hash}; 54643003dfeSmillert ok (brute_force_exists ($copy, $key), 54743003dfeSmillert "hv_delete_ent present$message $printable"); 54843003dfeSmillert is (delete $copy->{$key}, $key, "hv_delete_ent present$message $printable"); 54943003dfeSmillert ok (!brute_force_exists ($copy, $key), 55043003dfeSmillert "hv_delete_ent present$message $printable"); 55143003dfeSmillert $copy = {%$hash}; 55243003dfeSmillert ok (brute_force_exists ($copy, $key), 55343003dfeSmillert "hv_delete present$message $printable"); 55443003dfeSmillert is (XS::APItest::Hash::delete ($copy, $key), $key, 55543003dfeSmillert "hv_delete present$message $printable"); 55643003dfeSmillert ok (!brute_force_exists ($copy, $key), 55743003dfeSmillert "hv_delete present$message $printable"); 55843003dfeSmillert} 55943003dfeSmillert 56043003dfeSmillertsub test_delete_absent { 56143003dfeSmillert my ($hash, $key, $printable, $message) = @_; 56243003dfeSmillert 56343003dfeSmillert my $copy = {}; 56443003dfeSmillert my $class = tied %$hash; 56543003dfeSmillert if (defined $class) { 56643003dfeSmillert tie %$copy, ref $class; 56743003dfeSmillert } 56843003dfeSmillert $copy = {%$hash}; 56943003dfeSmillert is (delete $copy->{$key}, undef, "hv_delete_ent absent$message $printable"); 57043003dfeSmillert $copy = {%$hash}; 57143003dfeSmillert is (XS::APItest::Hash::delete ($copy, $key), undef, 57243003dfeSmillert "hv_delete absent$message $printable"); 57343003dfeSmillert} 57443003dfeSmillert 57543003dfeSmillertsub test_store { 57643003dfeSmillert my ($hash, $key, $printable, $message, $defaults) = @_; 57743003dfeSmillert my $HV_STORE_IS_CRAZY = 1; 57843003dfeSmillert 57943003dfeSmillert # We are cheating - hv_store returns NULL for a store into an empty 58043003dfeSmillert # tied hash. This isn't helpful here. 58143003dfeSmillert 58243003dfeSmillert my $class = tied %$hash; 58343003dfeSmillert 58443003dfeSmillert # It's important to do this with nice new hashes created each time round 58543003dfeSmillert # the loop, rather than hashes in the pad, which get recycled, and may have 58643003dfeSmillert # xhv_array non-NULL 58743003dfeSmillert my $h1 = {@$defaults}; 58843003dfeSmillert my $h2 = {@$defaults}; 58943003dfeSmillert if (defined $class) { 59043003dfeSmillert tie %$h1, ref $class; 59143003dfeSmillert tie %$h2, ref $class; 59243003dfeSmillert if ($] > 5.009) { 59343003dfeSmillert # bug 36327 is fixed 59443003dfeSmillert $HV_STORE_IS_CRAZY = undef; 59543003dfeSmillert } else { 59643003dfeSmillert # HV store_ent returns 1 if there was already underlying hash storage 59743003dfeSmillert $HV_STORE_IS_CRAZY = undef unless @$defaults; 59843003dfeSmillert } 59943003dfeSmillert } 60043003dfeSmillert is (XS::APItest::Hash::store_ent($h1, $key, 1), $HV_STORE_IS_CRAZY, 60143003dfeSmillert "hv_store_ent$message $printable"); 60243003dfeSmillert ok (brute_force_exists ($h1, $key), "hv_store_ent$message $printable"); 60343003dfeSmillert is (XS::APItest::Hash::store($h2, $key, 1), $HV_STORE_IS_CRAZY, 60443003dfeSmillert "hv_store$message $printable"); 60543003dfeSmillert ok (brute_force_exists ($h2, $key), "hv_store$message $printable"); 60643003dfeSmillert} 60743003dfeSmillert 60843003dfeSmillertsub test_fetch_present { 60943003dfeSmillert my ($hash, $key, $printable, $message) = @_; 61043003dfeSmillert 61143003dfeSmillert is ($hash->{$key}, $key, "hv_fetch_ent present$message $printable"); 61243003dfeSmillert is (XS::APItest::Hash::fetch ($hash, $key), $key, 61343003dfeSmillert "hv_fetch present$message $printable"); 61443003dfeSmillert} 61543003dfeSmillert 61643003dfeSmillertsub test_fetch_absent { 61743003dfeSmillert my ($hash, $key, $printable, $message) = @_; 61843003dfeSmillert 61943003dfeSmillert is ($hash->{$key}, undef, "hv_fetch_ent absent$message $printable"); 62043003dfeSmillert is (XS::APItest::Hash::fetch ($hash, $key), undef, 62143003dfeSmillert "hv_fetch absent$message $printable"); 62243003dfeSmillert} 62343003dfeSmillert 62443003dfeSmillertsub brute_force_exists { 62543003dfeSmillert my ($hash, $key) = @_; 62643003dfeSmillert foreach (keys %$hash) { 62743003dfeSmillert return 1 if $key eq $_; 62843003dfeSmillert } 62943003dfeSmillert return 0; 63043003dfeSmillert} 63143003dfeSmillert 63243003dfeSmillertsub rot13 { 63343003dfeSmillert my @results = map {my $a = $_; $a =~ tr/A-Za-z/N-ZA-Mn-za-m/; $a} @_; 63443003dfeSmillert wantarray ? @results : $results[0]; 63543003dfeSmillert} 63643003dfeSmillert 63743003dfeSmillertsub bitflip { 638b8851fccSafresh1 my $flip_bit = ord("A") ^ ord("a"); 639b8851fccSafresh1 my @results = map {join '', map {chr($flip_bit ^ ord $_)} split '', $_} @_; 64043003dfeSmillert wantarray ? @results : $results[0]; 64143003dfeSmillert} 642