Hash::Util#
General-utility hash subroutines - lock and unlock keys or values, inspect bucket layout, and seed the hash function.
A restricted hash is an ordinary hash that rejects new keys and, optionally, refuses writes to its values. You build one by calling lock_keys on a populated hash; from that point on a write to any key not already present croaks. Locking individual values with lock_value, or the whole hash with lock_hash, makes the existing values immutable as well. Every lock has a matching unlock.
Alongside the locking primitives the module exposes a handful of introspection helpers: bucket_ratio, num_buckets, used_buckets, bucket_info, bucket_array, and the summary reports bucket_stats and bucket_stats_formatted. They report how the hash is laid out internally and are useful when tuning the size of very large hashes.
hash_seed and hash_value expose the hash function itself - the random per-process seed, and the integer hash of a given string under that seed (or a caller-supplied seed). Both are sensitive: handing them to untrusted code enables algorithmic-complexity attacks against any hash in the program.
Every ref-suffixed variant (lock_ref_keys, lock_hashref, hashref_locked, legal_ref_keys, hidden_ref_keys, etc.) does the same work as its plain counterpart but takes an already-taken hash reference rather than relying on the \% prototype.
Synopsis#
use Hash::Util qw(lock_keys unlock_keys lock_value hash_locked hidden_keys);
my %h = (foo => 1, bar => 2);
lock_keys(%h); # fix the keyset
$h{foo} = 10; # ok
eval { $h{baz} = 99 }; # croaks - attempt to access disallowed key
lock_value(%h, 'foo'); # make $h{foo} immutable too
print hash_locked(%h) ? "yes" : "no"; # yes
unlock_keys(%h); # lift the keyset restriction
Locking keys#
lock_keys(%hash) restricts the hash to its current keys; lock_keys(%hash, @keys) restricts it to @keys. Afterwards, assigning to a key outside the set croaks with Attempt to access disallowed key '...' in a restricted hash. With an explicit list, an existing key that is not in the list makes the call croak with Hash has key '...' which is not in the new key set; keys in the list that are not present yet become hidden placeholders, legal to assign to later. Every key is compared as a string, so lock_keys(%h, 1) allows the key "1".
delete and exists still work on a locked hash and do not change the legal keyset: a deleted key can be assigned again, a new key still cannot. A locked hash cannot be blessed; bless it first.
unlock_keys(%hash) lifts the keyset restriction. Values locked with lock_value stay locked, and placeholders stay in place until the next lock_keys, which clears them before it locks.
lock_keys_plus(%hash, @additional_keys) locks the hash to its current keys plus @additional_keys - the same as lock_keys(%hash, @additional_keys, keys %hash).
All of them return a reference to the hash.
Locking values#
lock_value(%hash, $key) makes the value under $key read-only: a later assignment croaks with Modification of a read-only value attempted. Unless the keyset is locked too, the key can still be deleted, and a later assignment then creates a fresh writable value; for that reason, with warnings enabled, locking a value in an unlocked hash warns Cannot usefully lock values in an unlocked hash. unlock_value(%hash, $key) makes the value writable again.
lock_hash(%hash) locks the keyset and every value; unlock_hash undoes both. lock_hash_recurse and unlock_hash_recurse do the same and descend into every value that is a hash reference - only into hashes, so a hash of arrays of hashes has only its outermost hash locked.
All of them return a reference to the hash.
Querying the lock state#
hash_locked(%hash) is true when the keyset is locked and hash_unlocked(%hash) is its negation. Both return Perl’s boolean true and false values, so a false result prints as the empty string. Value locks alone do not count.
legal_keys(%hash) lists every key the hash may carry - the visible keys plus the hidden placeholders - and hidden_keys(%hash) lists the placeholders only: legal keys that fail both exists and defined. On an unrestricted hash legal_keys equals keys %hash and hidden_keys is empty.
Hash-reference forms#
Each function above has a form that takes a hash reference instead of relying on the \% prototype: lock_ref_keys, unlock_ref_keys, lock_ref_keys_plus, lock_ref_value, unlock_ref_value, lock_hashref, unlock_hashref, lock_hashref_recurse, unlock_hashref_recurse, hashref_locked, hashref_unlocked, legal_ref_keys and hidden_ref_keys.
Bucket statistics#
bucket_stats(\%hash) extends bucket_info with derived quantities:
my ($keys, $buckets, $used, $quality, $utilization_ratio,
$collision_pct, $mean, $stddev, @length_counts)
= bucket_stats(\%hash);
$quality is the Red Dragon quality score (ideal 1.0, acceptable around 0.95 to 1.05), $utilization_ratio the fraction of used buckets, $collision_pct the fraction of keys that share a bucket, and $mean and $stddev describe the chain lengths of the used buckets. For an empty hash only the first three values are returned.
bucket_stats_formatted(\%hash) renders the same data as a report: a summary with the quality score read as “Good”, “Poor” or “Bad”, then two bar charts - chain lengths, which is what a failed lookup costs, and key positions within their chains, which is what a successful one costs. Useful when deciding whether to pre-size a large hash with keys(%h) = N.
Security#
hash_seed, hash_value, and bucket_array reveal enough about the hash function to enable collision attacks. Do not expose their output to code you do not trust.
This is a native implementation of the XS half of Hash::Util 0.32; its Hash/Util.pm is used unchanged. Nothing is exported by default.
Differences from upstream#
None.
Modules#
Hash::Util::FieldHash- Support for Inside-Out Classes
Functions#
Hash locking#
all_keys#
Split a restricted hash’s keys into visible and hidden sets, writing the results into the caller’s arrays.
legal_ref_keys#
List every key a restricted hash is allowed to carry - the visible keys plus the hidden placeholders.
Synopsis
my @legal = legal_ref_keys(\%hash);
my @legal = legal_keys(%hash); # the \% form
On an unrestricted hash this is equivalent to keys %hash.
hv_store#
Install an alias from $hash{$key} to an existing scalar, instead of copying its value in the usual way.
lock_keys#
Restrict a hash to its current keys, or to an explicit list.
unlock_keys#
Lift the keyset restriction imposed by lock_keys.
lock_ref_keys#
Hash-reference variant of lock_keys.
unlock_ref_keys#
Hash-reference variant of unlock_keys.
lock_ref_keys_plus#
Lock the hash to the union of its current keys and a given extra list.
lock_value#
Make the value under a given key immutable.
unlock_value#
Undo a previous lock_value, making the value at $key writable again.
lock_ref_value#
Hash-reference variant of lock_value.
Synopsis
lock_ref_value($href, $key);
Same behaviour as lock_value but takes a hash reference.
unlock_ref_value#
Hash-reference variant of unlock_value.
Synopsis
unlock_ref_value($href, $key);
Same behaviour as unlock_value but takes a hash reference.
lock_hash#
Lock the whole hash: both its keyset and every value.
unlock_hash#
Undo a previous lock_hash, making every key and value writable again.
Synopsis
unlock_hash(%hash);
Clears the read-only flag on each existing value and lifts the keyset restriction. Returns a reference to the hash.
lock_hashref#
Hash-reference variant of lock_hash.
Synopsis
lock_hashref($href);
Same behaviour as lock_hash but takes a hash reference.
unlock_hashref#
Hash-reference variant of unlock_hash.
Synopsis
unlock_hashref($href);
Same behaviour as unlock_hash but takes a hash reference.
lock_hash_recurse#
Lock a hash and every nested hash it references, transitively.
unlock_hash_recurse#
Undo a previous lock_hash_recurse, unlocking every nested hash it touched.
Synopsis
unlock_hash_recurse(%hash);
Traverses the same subset of references as lock_hash_recurse - hash-valued entries only. Returns a reference to the hash.
lock_hashref_recurse#
Hash-reference variant of lock_hash_recurse.
Synopsis
lock_hashref_recurse($href);
Same behaviour as lock_hash_recurse but takes a hash reference.
unlock_hashref_recurse#
Hash-reference variant of unlock_hash_recurse.
Synopsis
unlock_hashref_recurse($href);
Same behaviour as unlock_hash_recurse but takes a hash reference.
hash_locked#
Report whether the hash’s keyset is currently locked.
hash_unlocked#
Report whether the hash’s keyset is not currently locked.
hashref_locked#
Hash-reference variant of hash_locked.
Synopsis
hashref_locked($href) and print "restricted\n";
Same behaviour as hash_locked but takes a hash reference.
hashref_unlocked#
Hash-reference variant of hash_unlocked.
Synopsis
hashref_unlocked($href) and print "open\n";
Same behaviour as hash_unlocked but takes a hash reference.
legal_keys#
List every key the restricted hash is allowed to carry - visible keys plus hidden placeholders.
Bucket introspection#
hash_traversal_mask#
Read or set the per-hash bucket-traversal mask used by keys, values, and each.
bucket_info#
Return a flat list summarising the hash’s bucket layout.
bucket_array#
Return a packed array-of-arrays describing which key lives in which bucket.
bucket_ratio#
Return a "used/total" string describing how many of the hash’s internal buckets hold at least one key.
num_buckets#
Return the total number of buckets the hash holds (or would hold if its bucket array were allocated).
used_buckets#
Return the count of buckets that currently hold at least one key.
bucket_stats#
Return a rich statistical summary of the hash’s bucket layout.
bucket_stats_formatted#
Return a multi-line human-readable report of bucket_stats, complete with ASCII barcharts.
Hash function#
hash_seed#
Return the per-process random bytes that seed the hash function.
hash_value#
Return the integer hash value of a string under the current (or a supplied) seed.