1package Hash::Util; 2 3require 5.007003; 4use strict; 5use Carp; 6use warnings; 7no warnings 'uninitialized'; 8use warnings::register; 9use Scalar::Util qw(reftype); 10 11require Exporter; 12our @ISA = qw(Exporter); 13our @EXPORT_OK = qw( 14 fieldhash fieldhashes 15 16 all_keys 17 lock_keys unlock_keys 18 lock_value unlock_value 19 lock_hash unlock_hash 20 lock_keys_plus 21 hash_locked hash_unlocked 22 hashref_locked hashref_unlocked 23 hidden_keys legal_keys 24 25 lock_ref_keys unlock_ref_keys 26 lock_ref_value unlock_ref_value 27 lock_hashref unlock_hashref 28 lock_ref_keys_plus 29 hidden_ref_keys legal_ref_keys 30 31 hash_seed hash_value hv_store 32 bucket_stats bucket_stats_formatted bucket_info bucket_array 33 lock_hash_recurse unlock_hash_recurse 34 lock_hashref_recurse unlock_hashref_recurse 35 36 hash_traversal_mask 37 ); 38our $VERSION = '0.19'; 39require XSLoader; 40XSLoader::load(); 41 42sub import { 43 my $class = shift; 44 if ( grep /fieldhash/, @_ ) { 45 require Hash::Util::FieldHash; 46 Hash::Util::FieldHash->import(':all'); # for re-export 47 } 48 unshift @_, $class; 49 goto &Exporter::import; 50} 51 52 53=head1 NAME 54 55Hash::Util - A selection of general-utility hash subroutines 56 57=head1 SYNOPSIS 58 59 # Restricted hashes 60 61 use Hash::Util qw( 62 fieldhash fieldhashes 63 64 all_keys 65 lock_keys unlock_keys 66 lock_value unlock_value 67 lock_hash unlock_hash 68 lock_keys_plus 69 hash_locked hash_unlocked 70 hashref_locked hashref_unlocked 71 hidden_keys legal_keys 72 73 lock_ref_keys unlock_ref_keys 74 lock_ref_value unlock_ref_value 75 lock_hashref unlock_hashref 76 lock_ref_keys_plus 77 hidden_ref_keys legal_ref_keys 78 79 hash_seed hash_value hv_store 80 bucket_stats bucket_info bucket_array 81 lock_hash_recurse unlock_hash_recurse 82 lock_hashref_recurse unlock_hashref_recurse 83 84 hash_traversal_mask 85 ); 86 87 %hash = (foo => 42, bar => 23); 88 # Ways to restrict a hash 89 lock_keys(%hash); 90 lock_keys(%hash, @keyset); 91 lock_keys_plus(%hash, @additional_keys); 92 93 # Ways to inspect the properties of a restricted hash 94 my @legal = legal_keys(%hash); 95 my @hidden = hidden_keys(%hash); 96 my $ref = all_keys(%hash,@keys,@hidden); 97 my $is_locked = hash_locked(%hash); 98 99 # Remove restrictions on the hash 100 unlock_keys(%hash); 101 102 # Lock individual values in a hash 103 lock_value (%hash, 'foo'); 104 unlock_value(%hash, 'foo'); 105 106 # Ways to change the restrictions on both keys and values 107 lock_hash (%hash); 108 unlock_hash(%hash); 109 110 my $hashes_are_randomised = hash_seed() != 0; 111 112 my $int_hash_value = hash_value( 'string' ); 113 114 my $mask= hash_traversal_mask(%hash); 115 116 hash_traversal_mask(%hash,1234); 117 118=head1 DESCRIPTION 119 120C<Hash::Util> and C<Hash::Util::FieldHash> contain special functions 121for manipulating hashes that don't really warrant a keyword. 122 123C<Hash::Util> contains a set of functions that support 124L<restricted hashes|/"Restricted hashes">. These are described in 125this document. C<Hash::Util::FieldHash> contains an (unrelated) 126set of functions that support the use of hashes in 127I<inside-out classes>, described in L<Hash::Util::FieldHash>. 128 129By default C<Hash::Util> does not export anything. 130 131=head2 Restricted hashes 132 1335.8.0 introduces the ability to restrict a hash to a certain set of 134keys. No keys outside of this set can be added. It also introduces 135the ability to lock an individual key so it cannot be deleted and the 136ability to ensure that an individual value cannot be changed. 137 138This is intended to largely replace the deprecated pseudo-hashes. 139 140=over 4 141 142=item B<lock_keys> 143 144=item B<unlock_keys> 145 146 lock_keys(%hash); 147 lock_keys(%hash, @keys); 148 149Restricts the given %hash's set of keys to @keys. If @keys is not 150given it restricts it to its current keyset. No more keys can be 151added. delete() and exists() will still work, but will not alter 152the set of allowed keys. B<Note>: the current implementation prevents 153the hash from being bless()ed while it is in a locked state. Any attempt 154to do so will raise an exception. Of course you can still bless() 155the hash before you call lock_keys() so this shouldn't be a problem. 156 157 unlock_keys(%hash); 158 159Removes the restriction on the %hash's keyset. 160 161B<Note> that if any of the values of the hash have been locked they will not 162be unlocked after this sub executes. 163 164Both routines return a reference to the hash operated on. 165 166=cut 167 168sub lock_ref_keys { 169 my($hash, @keys) = @_; 170 171 Internals::hv_clear_placeholders %$hash; 172 if( @keys ) { 173 my %keys = map { ($_ => 1) } @keys; 174 my %original_keys = map { ($_ => 1) } keys %$hash; 175 foreach my $k (keys %original_keys) { 176 croak "Hash has key '$k' which is not in the new key set" 177 unless $keys{$k}; 178 } 179 180 foreach my $k (@keys) { 181 $hash->{$k} = undef unless exists $hash->{$k}; 182 } 183 Internals::SvREADONLY %$hash, 1; 184 185 foreach my $k (@keys) { 186 delete $hash->{$k} unless $original_keys{$k}; 187 } 188 } 189 else { 190 Internals::SvREADONLY %$hash, 1; 191 } 192 193 return $hash; 194} 195 196sub unlock_ref_keys { 197 my $hash = shift; 198 199 Internals::SvREADONLY %$hash, 0; 200 return $hash; 201} 202 203sub lock_keys (\%;@) { lock_ref_keys(@_) } 204sub unlock_keys (\%) { unlock_ref_keys(@_) } 205 206=item B<lock_keys_plus> 207 208 lock_keys_plus(%hash,@additional_keys) 209 210Similar to C<lock_keys()>, with the difference being that the optional key list 211specifies keys that may or may not be already in the hash. Essentially this is 212an easier way to say 213 214 lock_keys(%hash,@additional_keys,keys %hash); 215 216Returns a reference to %hash 217 218=cut 219 220 221sub lock_ref_keys_plus { 222 my ($hash,@keys) = @_; 223 my @delete; 224 Internals::hv_clear_placeholders(%$hash); 225 foreach my $key (@keys) { 226 unless (exists($hash->{$key})) { 227 $hash->{$key}=undef; 228 push @delete,$key; 229 } 230 } 231 Internals::SvREADONLY(%$hash,1); 232 delete @{$hash}{@delete}; 233 return $hash 234} 235 236sub lock_keys_plus(\%;@) { lock_ref_keys_plus(@_) } 237 238 239=item B<lock_value> 240 241=item B<unlock_value> 242 243 lock_value (%hash, $key); 244 unlock_value(%hash, $key); 245 246Locks and unlocks the value for an individual key of a hash. The value of a 247locked key cannot be changed. 248 249Unless %hash has already been locked the key/value could be deleted 250regardless of this setting. 251 252Returns a reference to the %hash. 253 254=cut 255 256sub lock_ref_value { 257 my($hash, $key) = @_; 258 # I'm doubtful about this warning, as it seems not to be true. 259 # Marking a value in the hash as RO is useful, regardless 260 # of the status of the hash itself. 261 carp "Cannot usefully lock values in an unlocked hash" 262 if !Internals::SvREADONLY(%$hash) && warnings::enabled; 263 Internals::SvREADONLY $hash->{$key}, 1; 264 return $hash 265} 266 267sub unlock_ref_value { 268 my($hash, $key) = @_; 269 Internals::SvREADONLY $hash->{$key}, 0; 270 return $hash 271} 272 273sub lock_value (\%$) { lock_ref_value(@_) } 274sub unlock_value (\%$) { unlock_ref_value(@_) } 275 276 277=item B<lock_hash> 278 279=item B<unlock_hash> 280 281 lock_hash(%hash); 282 283lock_hash() locks an entire hash, making all keys and values read-only. 284No value can be changed, no keys can be added or deleted. 285 286 unlock_hash(%hash); 287 288unlock_hash() does the opposite of lock_hash(). All keys and values 289are made writable. All values can be changed and keys can be added 290and deleted. 291 292Returns a reference to the %hash. 293 294=cut 295 296sub lock_hashref { 297 my $hash = shift; 298 299 lock_ref_keys($hash); 300 301 foreach my $value (values %$hash) { 302 Internals::SvREADONLY($value,1); 303 } 304 305 return $hash; 306} 307 308sub unlock_hashref { 309 my $hash = shift; 310 311 foreach my $value (values %$hash) { 312 Internals::SvREADONLY($value, 0); 313 } 314 315 unlock_ref_keys($hash); 316 317 return $hash; 318} 319 320sub lock_hash (\%) { lock_hashref(@_) } 321sub unlock_hash (\%) { unlock_hashref(@_) } 322 323=item B<lock_hash_recurse> 324 325=item B<unlock_hash_recurse> 326 327 lock_hash_recurse(%hash); 328 329lock_hash() locks an entire hash and any hashes it references recursively, 330making all keys and values read-only. No value can be changed, no keys can 331be added or deleted. 332 333This method B<only> recurses into hashes that are referenced by another hash. 334Thus a Hash of Hashes (HoH) will all be restricted, but a Hash of Arrays of 335Hashes (HoAoH) will only have the top hash restricted. 336 337 unlock_hash_recurse(%hash); 338 339unlock_hash_recurse() does the opposite of lock_hash_recurse(). All keys and 340values are made writable. All values can be changed and keys can be added 341and deleted. Identical recursion restrictions apply as to lock_hash_recurse(). 342 343Returns a reference to the %hash. 344 345=cut 346 347sub lock_hashref_recurse { 348 my $hash = shift; 349 350 lock_ref_keys($hash); 351 foreach my $value (values %$hash) { 352 my $type = reftype($value); 353 if (defined($type) and $type eq 'HASH') { 354 lock_hashref_recurse($value); 355 } 356 Internals::SvREADONLY($value,1); 357 } 358 return $hash 359} 360 361sub unlock_hashref_recurse { 362 my $hash = shift; 363 364 foreach my $value (values %$hash) { 365 my $type = reftype($value); 366 if (defined($type) and $type eq 'HASH') { 367 unlock_hashref_recurse($value); 368 } 369 Internals::SvREADONLY($value,0); 370 } 371 unlock_ref_keys($hash); 372 return $hash; 373} 374 375sub lock_hash_recurse (\%) { lock_hashref_recurse(@_) } 376sub unlock_hash_recurse (\%) { unlock_hashref_recurse(@_) } 377 378=item B<hashref_locked> 379 380=item B<hash_locked> 381 382 hashref_locked(\%hash) and print "Hash is locked!\n"; 383 hash_locked(%hash) and print "Hash is locked!\n"; 384 385Returns true if the hash and its keys are locked. 386 387=cut 388 389sub hashref_locked { 390 my $hash=shift; 391 Internals::SvREADONLY(%$hash); 392} 393 394sub hash_locked(\%) { hashref_locked(@_) } 395 396=item B<hashref_unlocked> 397 398=item B<hash_unlocked> 399 400 hashref_unlocked(\%hash) and print "Hash is unlocked!\n"; 401 hash_unlocked(%hash) and print "Hash is unlocked!\n"; 402 403Returns true if the hash and its keys are unlocked. 404 405=cut 406 407sub hashref_unlocked { 408 my $hash=shift; 409 !Internals::SvREADONLY(%$hash); 410} 411 412sub hash_unlocked(\%) { hashref_unlocked(@_) } 413 414=for demerphqs_editor 415sub legal_ref_keys{} 416sub hidden_ref_keys{} 417sub all_keys{} 418 419=cut 420 421sub legal_keys(\%) { legal_ref_keys(@_) } 422sub hidden_keys(\%){ hidden_ref_keys(@_) } 423 424=item B<legal_keys> 425 426 my @keys = legal_keys(%hash); 427 428Returns the list of the keys that are legal in a restricted hash. 429In the case of an unrestricted hash this is identical to calling 430keys(%hash). 431 432=item B<hidden_keys> 433 434 my @keys = hidden_keys(%hash); 435 436Returns the list of the keys that are legal in a restricted hash but 437do not have a value associated to them. Thus if 'foo' is a 438"hidden" key of the %hash it will return false for both C<defined> 439and C<exists> tests. 440 441In the case of an unrestricted hash this will return an empty list. 442 443B<NOTE> this is an experimental feature that is heavily dependent 444on the current implementation of restricted hashes. Should the 445implementation change, this routine may become meaningless, in which 446case it will return an empty list. 447 448=item B<all_keys> 449 450 all_keys(%hash,@keys,@hidden); 451 452Populates the arrays @keys with the all the keys that would pass 453an C<exists> tests, and populates @hidden with the remaining legal 454keys that have not been utilized. 455 456Returns a reference to the hash. 457 458In the case of an unrestricted hash this will be equivalent to 459 460 $ref = do { 461 @keys = keys %hash; 462 @hidden = (); 463 \%hash 464 }; 465 466B<NOTE> this is an experimental feature that is heavily dependent 467on the current implementation of restricted hashes. Should the 468implementation change this routine may become meaningless in which 469case it will behave identically to how it would behave on an 470unrestricted hash. 471 472=item B<hash_seed> 473 474 my $hash_seed = hash_seed(); 475 476hash_seed() returns the seed bytes used to randomise hash ordering. 477 478B<Note that the hash seed is sensitive information>: by knowing it one 479can craft a denial-of-service attack against Perl code, even remotely, 480see L<perlsec/"Algorithmic Complexity Attacks"> for more information. 481B<Do not disclose the hash seed> to people who don't need to know it. 482See also L<perlrun/PERL_HASH_SEED_DEBUG>. 483 484Prior to Perl 5.17.6 this function returned a UV, it now returns a string, 485which may be of nearly any size as determined by the hash function your 486Perl has been built with. Possible sizes may be but are not limited to 4874 bytes (for most hash algorithms) and 16 bytes (for siphash). 488 489=item B<hash_value> 490 491 my $hash_value = hash_value($string); 492 493hash_value() returns the current perl's internal hash value for a given 494string. 495 496Returns a 32 bit integer representing the hash value of the string passed 497in. This value is only reliable for the lifetime of the process. It may 498be different depending on invocation, environment variables, perl version, 499architectures, and build options. 500 501B<Note that the hash value of a given string is sensitive information>: 502by knowing it one can deduce the hash seed which in turn can allow one to 503craft a denial-of-service attack against Perl code, even remotely, 504see L<perlsec/"Algorithmic Complexity Attacks"> for more information. 505B<Do not disclose the hash value of a string> to people who don't need to 506know it. See also L<perlrun/PERL_HASH_SEED_DEBUG>. 507 508=item B<bucket_info> 509 510Return a set of basic information about a hash. 511 512 my ($keys, $buckets, $used, @length_counts)= bucket_info($hash); 513 514Fields are as follows: 515 516 0: Number of keys in the hash 517 1: Number of buckets in the hash 518 2: Number of used buckets in the hash 519 rest : list of counts, Kth element is the number of buckets 520 with K keys in it. 521 522See also bucket_stats() and bucket_array(). 523 524=item B<bucket_stats> 525 526Returns a list of statistics about a hash. 527 528 my ($keys, $buckets, $used, $quality, $utilization_ratio, 529 $collision_pct, $mean, $stddev, @length_counts) 530 = bucket_stats($hashref); 531 532Fields are as follows: 533 534 0: Number of keys in the hash 535 1: Number of buckets in the hash 536 2: Number of used buckets in the hash 537 3: Hash Quality Score 538 4: Percent of buckets used 539 5: Percent of keys which are in collision 540 6: Mean bucket length of occupied buckets 541 7: Standard Deviation of bucket lengths of occupied buckets 542 rest : list of counts, Kth element is the number of buckets 543 with K keys in it. 544 545See also bucket_info() and bucket_array(). 546 547Note that Hash Quality Score would be 1 for an ideal hash, numbers 548close to and below 1 indicate good hashing, and number significantly 549above indicate a poor score. In practice it should be around 0.95 to 1.05. 550It is defined as: 551 552 $score= sum( $count[$length] * ($length * ($length + 1) / 2) ) 553 / 554 ( ( $keys / 2 * $buckets ) * 555 ( $keys + ( 2 * $buckets ) - 1 ) ) 556 557The formula is from the Red Dragon book (reformulated to use the data available) 558and is documented at L<http://www.strchr.com/hash_functions> 559 560=item B<bucket_array> 561 562 my $array= bucket_array(\%hash); 563 564Returns a packed representation of the bucket array associated with a hash. Each element 565of the array is either an integer K, in which case it represents K empty buckets, or 566a reference to another array which contains the keys that are in that bucket. 567 568B<Note that the information returned by bucket_array is sensitive information>: 569by knowing it one can directly attack perl's hash function which in turn may allow 570one to craft a denial-of-service attack against Perl code, even remotely, 571see L<perlsec/"Algorithmic Complexity Attacks"> for more information. 572B<Do not disclose the output of this function> to people who don't need to 573know it. See also L<perlrun/PERL_HASH_SEED_DEBUG>. This function is provided strictly 574for debugging and diagnostics purposes only, it is hard to imagine a reason why it 575would be used in production code. 576 577=cut 578 579 580sub bucket_stats { 581 my ($hash) = @_; 582 my ($keys, $buckets, $used, @length_counts) = bucket_info($hash); 583 my $sum; 584 my $score; 585 for (1 .. $#length_counts) { 586 $sum += ($length_counts[$_] * $_); 587 $score += $length_counts[$_] * ( $_ * ($_ + 1 ) / 2 ); 588 } 589 $score = $score / 590 (( $keys / (2 * $buckets )) * ( $keys + ( 2 * $buckets ) - 1 )) 591 if $keys; 592 my ($mean, $stddev)= (0, 0); 593 if ($used) { 594 $mean= $sum / $used; 595 $sum= 0; 596 $sum += ($length_counts[$_] * (($_-$mean)**2)) for 1 .. $#length_counts; 597 598 $stddev= sqrt($sum/$used); 599 } 600 return $keys, $buckets, $used, $keys ? ($score, $used/$buckets, ($keys-$used)/$keys, $mean, $stddev, @length_counts) : (); 601} 602 603=item B<bucket_stats_formatted> 604 605 print bucket_stats_formatted($hashref); 606 607Return a formatted report of the information returned by bucket_stats(). 608An example report looks like this: 609 610 Keys: 50 Buckets: 33/64 Quality-Score: 1.01 (Good) 611 Utilized Buckets: 51.56% Optimal: 78.12% Keys In Collision: 34.00% 612 Chain Length - mean: 1.52 stddev: 0.66 613 Buckets 64 [0000000000000000000000000000000111111111111111111122222222222333] 614 Len 0 Pct: 48.44 [###############################] 615 Len 1 Pct: 29.69 [###################] 616 Len 2 Pct: 17.19 [###########] 617 Len 3 Pct: 4.69 [###] 618 Keys 50 [11111111111111111111111111111111122222222222222333] 619 Pos 1 Pct: 66.00 [#################################] 620 Pos 2 Pct: 28.00 [##############] 621 Pos 3 Pct: 6.00 [###] 622 623The first set of stats gives some summary statistical information, 624including the quality score translated into "Good", "Poor" and "Bad", 625(score<=1.05, score<=1.2, score>1.2). See the documentation in 626bucket_stats() for more details. 627 628The two sets of barcharts give stats and a visual indication of performance 629of the hash. 630 631The first gives data on bucket chain lengths and provides insight on how 632much work a fetch *miss* will take. In this case we have to inspect every item 633in a bucket before we can be sure the item is not in the list. The performance 634for an insert is equivalent to this case, as is a delete where the item 635is not in the hash. 636 637The second gives data on how many keys are at each depth in the chain, and 638gives an idea of how much work a fetch *hit* will take. The performance for 639an update or delete of an item in the hash is equivalent to this case. 640 641Note that these statistics are summary only. Actual performance will depend 642on real hit/miss ratios accessing the hash. If you are concerned by hit ratios 643you are recommended to "oversize" your hash by using something like: 644 645 keys(%hash)= keys(%hash) << $k; 646 647With $k chosen carefully, and likely to be a small number like 1 or 2. In 648theory the larger the bucket array the less chance of collision. 649 650=cut 651 652 653sub _bucket_stats_formatted_bars { 654 my ($total, $ary, $start_idx, $title, $row_title)= @_; 655 656 my $return = ""; 657 my $max_width= $total > 64 ? 64 : $total; 658 my $bar_width= $max_width / $total; 659 660 my $str= ""; 661 if ( @$ary < 10) { 662 for my $idx ($start_idx .. $#$ary) { 663 $str .= $idx x sprintf("%.0f", ($ary->[$idx] * $bar_width)); 664 } 665 } else { 666 $str= "-" x $max_width; 667 } 668 $return .= sprintf "%-7s %6d [%s]\n",$title, $total, $str; 669 670 foreach my $idx ($start_idx .. $#$ary) { 671 $return .= sprintf "%-.3s %3d %6.2f%% %6d [%s]\n", 672 $row_title, 673 $idx, 674 $ary->[$idx] / $total * 100, 675 $ary->[$idx], 676 "#" x sprintf("%.0f", ($ary->[$idx] * $bar_width)), 677 ; 678 } 679 return $return; 680} 681 682sub bucket_stats_formatted { 683 my ($hashref)= @_; 684 my ($keys, $buckets, $used, $score, $utilization_ratio, $collision_pct, 685 $mean, $stddev, @length_counts) = bucket_stats($hashref); 686 687 my $return= sprintf "Keys: %d Buckets: %d/%d Quality-Score: %.2f (%s)\n" 688 . "Utilized Buckets: %.2f%% Optimal: %.2f%% Keys In Collision: %.2f%%\n" 689 . "Chain Length - mean: %.2f stddev: %.2f\n", 690 $keys, $used, $buckets, $score, $score <= 1.05 ? "Good" : $score < 1.2 ? "Poor" : "Bad", 691 $utilization_ratio * 100, 692 $keys/$buckets * 100, 693 $collision_pct * 100, 694 $mean, $stddev; 695 696 my @key_depth; 697 $key_depth[$_]= $length_counts[$_] + ( $key_depth[$_+1] || 0 ) 698 for reverse 1 .. $#length_counts; 699 700 if ($keys) { 701 $return .= _bucket_stats_formatted_bars($buckets, \@length_counts, 0, "Buckets", "Len"); 702 $return .= _bucket_stats_formatted_bars($keys, \@key_depth, 1, "Keys", "Pos"); 703 } 704 return $return 705} 706 707=item B<hv_store> 708 709 my $sv = 0; 710 hv_store(%hash,$key,$sv) or die "Failed to alias!"; 711 $hash{$key} = 1; 712 print $sv; # prints 1 713 714Stores an alias to a variable in a hash instead of copying the value. 715 716=item B<hash_traversal_mask> 717 718As of Perl 5.18 every hash has its own hash traversal order, and this order 719changes every time a new element is inserted into the hash. This functionality 720is provided by maintaining an unsigned integer mask (U32) which is xor'ed 721with the actual bucket id during a traversal of the hash buckets using keys(), 722values() or each(). 723 724You can use this subroutine to get and set the traversal mask for a specific 725hash. Setting the mask ensures that a given hash will produce the same key 726order. B<Note> that this does B<not> guarantee that B<two> hashes will produce 727the same key order for the same hash seed and traversal mask, items that 728collide into one bucket may have different orders regardless of this setting. 729 730=back 731 732=head2 Operating on references to hashes. 733 734Most subroutines documented in this module have equivalent versions 735that operate on references to hashes instead of native hashes. 736The following is a list of these subs. They are identical except 737in name and in that instead of taking a %hash they take a $hashref, 738and additionally are not prototyped. 739 740=over 4 741 742=item lock_ref_keys 743 744=item unlock_ref_keys 745 746=item lock_ref_keys_plus 747 748=item lock_ref_value 749 750=item unlock_ref_value 751 752=item lock_hashref 753 754=item unlock_hashref 755 756=item lock_hashref_recurse 757 758=item unlock_hashref_recurse 759 760=item hash_ref_unlocked 761 762=item legal_ref_keys 763 764=item hidden_ref_keys 765 766=back 767 768=head1 CAVEATS 769 770Note that the trapping of the restricted operations is not atomic: 771for example 772 773 eval { %hash = (illegal_key => 1) } 774 775leaves the C<%hash> empty rather than with its original contents. 776 777=head1 BUGS 778 779The interface exposed by this module is very close to the current 780implementation of restricted hashes. Over time it is expected that 781this behavior will be extended and the interface abstracted further. 782 783=head1 AUTHOR 784 785Michael G Schwern <schwern@pobox.com> on top of code by Nick 786Ing-Simmons and Jeffrey Friedl. 787 788hv_store() is from Array::RefElem, Copyright 2000 Gisle Aas. 789 790Additional code by Yves Orton. 791 792=head1 SEE ALSO 793 794L<Scalar::Util>, L<List::Util> and L<perlsec/"Algorithmic Complexity Attacks">. 795 796L<Hash::Util::FieldHash>. 797 798=cut 799 8001; 801