Algorithm-MinPerfHashTwoLevel
view release on metacpan or search on metacpan
lib/Algorithm/MinPerfHashTwoLevel.pm view on Meta::CPAN
sub _seed {
my $self= shift;
if (@_) {
my $seed= shift;
Carp::confess(sprintf "Seed should be undef, or a string exactly %d bytes long, not %d bytes",
STADTX_HASH_SEED_BYTES,length($seed))
if defined($seed) and length($seed) != 16;
$self->{seed}= $seed;
delete $self->{state};
}
if ( !defined $self->{seed} ) {
#1234567812345678
$self->{seed}= "MinPerfHash2Levl";
delete $self->{state};
}
return $self->{seed};
}
sub set_seed {
my ($self,$value)= @_;
my $seed= $self->_seed($value);
return $self->{constructor_seed}= $seed;
}
sub get_seed {
my ($self)= @_;
return $self->_seed();
}
sub get_state {
my $self= shift;
return $self->{state} ||= seed_state($self->_seed());
}
sub get_failed_seeds {
my $self= shift;
return @{$self->{failed_seeds}||[]};
}
1;
__END__
=head1 NAME
Algorithm::MinPerfHashTwoLevel - construct a "two level" minimal perfect hash
=head1 SYNOPSIS
use Algorithm::MinPerfHashTwoLevel;
my $buckets= Algorithm::MinPerfHashTwoLevel->compute(\%source_hash);
=head1 DESCRIPTION
This module implements an algorithm to construct (relatively efficiently) a
minimal perfect hash using the "two level" algorithm. A perfect hash is one
which has no collisions for any keys, a minimal perfect hash has exactly the
same number of buckets as it has keys. The "two level" algorithm involves
computing two hash values for each key. The first is used as an initial lookup
into the bucket array to find a mask value which is used to modify the second
hash value to determine the actual bucket to read from. This module computes
the appropriate mask values.
In this implementation only one 64 bit hash value is computed, but the high
and low 32 bits are used as if there were two hash values. The hash function
used is SipHash 1-3. The full 64 bit hash is called h0, the high 32 bits are
called h1, and the low 32 bits are called h2.)
Computing the hash and mask is done in C (via XS).
The process for looking up a value in a two level hash with n buckets is
as follows:
0. compute the h0 for the key. (giving: h1 = h0 >> 32; h2 = h0 & 0xFFFFFFFF;)
1. compute idx1 = h1 % n;
2. find the xor_val for bucket[idx1]
3. if the xor_val is zero we are done, the key is not in the hash
4. compute idx2:
if int(xor_val) < 0
idx2 = -xor_val-1
else
idx2 = INTHASH(h2 ^ xor_val) % n;
5. compare the key data associated with bucket[idx2] with the key provided
6. if they match return the desired value, otherwise the key is not in the hash.
In essence this module performs the task of computing the xor_val for
each bucket such that the idx2 for every element is unique, it does it in C/XS so
that it is fast.
The INTHASH() function used is:
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
which is just a simple 32 bit integer hash function I found at
https://stackoverflow.com/a/12996028, but any decent reversible
integer hash function would do.
*NOTE* in Perl a given string may have differing binary representations
if it is encoded as utf8 or not. This module uses the same conventions
as Perl itself, which is that keys are stored in their minimal form when
possible, and are only stored in their unicode (utf8) form when they
cannot be downgraded to latin-1. This ensures that the unicode and latin-1
representations of a given string are treated as the same key. This module
deals with this by "normalizing" the keys and values into latin-1, but
tracking the representation as a flag. See key_normalized and key_is_utf8
(and their 'val' equivalents) documented in the construct method.
=head2 METHODS
=over 4
=item new
Construct a new Algorithm::MinPerfHashTwoLevel object. Optional arguments
which may be provided are 'source_hash' which is a hash reference to use
as the source for the minimal perfect hash, 'seed' which is expected to be
a 16 byte string, and 'debug' which is expected to be 0 or 1, as well
as variant, which may be 5 (use version v0.14 for variants before 5).
lib/Algorithm/MinPerfHashTwoLevel.pm view on Meta::CPAN
{
"h1_keys" => 2,
"h0" => "17713559403787135240",
"idx" => 0,
"key" => "\x{103}",
"key_is_utf8" => 1,
"key_normalized" => "\304\203",
"val" => "\x{103}",
"val_is_utf8" => 1,
"val_normalized" => "\304\203",
"xor_val" => 2
},
The meaning of these keys is as follows:
=over 4
=item h1_keys
The number of keys which collide into this bucket and which are
disambiguated by the 'xor_val' for this bucket.
=item h0
The hash value computed for this key.
=item idx
The index of this bucket.
=item key
The key for this bucket as a perl string. (See key_normalized.)
=item key_is_utf8
Whether this key is encoded as utf8. Will be one of
0 for "not utf8", 1 for "is utf8", and 2 for "was utf8"
meaning the key is stored as latin-1, but will be upgraded
when fetched.
=item key_normalized
The raw bytes of the normalized key. (See key_is_utf8.)
=item val
The value for this bucket as a perl string. (See val_normalized.)
=item val_is_utf8
Whether this key is encoded as utf8. Will be either
0 for "not utf8" or 1 for "is utf8".
=item val_normalized
The raw bytes of the normalized key. (See val_is_utf8).
=item xor_val
The mask to be xor'ed with the second hash (h2) to determine
the actual lookup bucket. If the xor_val for a given bucket
is 0 then the key is not in the hash.
=back
=back
=head2 EXPORT
None by default.
=head1 SEE ALSO
Tie::Hash::MinPerfHashTwoLevel::OnDisk
=head1 AUTHOR
Yves Orton
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2019 by Yves Orton
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.18.4 or,
at your option, any later version of Perl 5 you may have available.
=cut
( run in 1.113 second using v1.01-cache-2.11-cpan-39bf76dae61 )