Packages

32 bit variant of the Murmur3 non-cryptographic hash function

Current section

Files

Jump to
murmur3a src murmur3a.erl
Raw

src/murmur3a.erl

-module(murmur3a).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/murmur3a.gleam").
-export([int_digest/1, bit_array_digest/1, hex_digest/1, hash_bytes/2, hash_string/2, hash_ints/2]).
-export_type([hash/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque hash() :: {hash, integer(), integer(), integer()}.
-file("src/murmur3a.gleam", 151).
?DOC(
" Returns the 32 bit _signed_ integer digest of the hash.\n"
"\n"
" Keep in mind that these values may return negative numbers!\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_ints([1, 2, 3], 0)\n"
" |> int_digest\n"
" // -> -2133732860\n"
" ```\n"
).
-spec int_digest(hash()) -> integer().
int_digest(Hash) ->
erlang:element(4, Hash).
-file("src/murmur3a.gleam", 164).
?DOC(
" Returns 32 bit digest of the hash as a BitArray.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_string(\"murmur3a\", 0)\n"
" |> bit_array_digest\n"
" // -> <<232, 177, 21, 28>>\n"
" ```\n"
).
-spec bit_array_digest(hash()) -> bitstring().
bit_array_digest(Hash) ->
<<(int_digest(Hash)):32>>.
-file("src/murmur3a.gleam", 177).
?DOC(
" Returns a hexadecimal digest of the hash.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_string(\"murmur3a\", 0)\n"
" |> hex_digest\n"
" // -> \"E8B1151C\"\n"
" ```\n"
).
-spec hex_digest(hash()) -> binary().
hex_digest(Hash) ->
_pipe = Hash,
_pipe@1 = bit_array_digest(_pipe),
gleam_stdlib:base16_encode(_pipe@1).
-file("src/murmur3a.gleam", 197).
-spec rotate_left(integer(), integer()) -> integer().
rotate_left(N, Shift) ->
N@1 = erlang:'band'(N, 16#ffffffff),
_pipe = erlang:'bsl'(N@1, Shift),
_pipe@1 = erlang:'band'(_pipe, 16#ffffffff),
erlang:'bor'(_pipe@1, erlang:'bsr'(N@1, 32 - Shift)).
-file("src/murmur3a.gleam", 188).
-spec signed_multiply(integer(), integer()) -> integer().
signed_multiply(M, N) ->
Result = erlang:'band'(M * N, 16#ffffffff),
case Result > 16#7fffffff of
true ->
Result - 16#100000000;
false ->
Result
end.
-file("src/murmur3a.gleam", 98).
-spec mix(integer(), integer()) -> integer().
mix(State, Seed) ->
_pipe = State,
_pipe@1 = signed_multiply(_pipe, 16#cc9e2d51),
_pipe@2 = rotate_left(_pipe@1, 15),
_pipe@3 = signed_multiply(_pipe@2, 16#1b873593),
_pipe@4 = erlang:'bxor'(_pipe@3, Seed),
_pipe@5 = rotate_left(_pipe@4, 13),
_pipe@6 = signed_multiply(_pipe@5, 5),
gleam@int:add(_pipe@6, 16#e6546b64).
-file("src/murmur3a.gleam", 86).
-spec hash_byte(hash(), integer()) -> hash().
hash_byte(Hash, Byte) ->
State = begin
_pipe = Byte,
_pipe@1 = erlang:'bsl'(_pipe, erlang:element(3, Hash)),
erlang:'bor'(_pipe@1, erlang:element(4, Hash))
end,
case erlang:element(3, Hash) of
24 ->
{hash, mix(State, erlang:element(2, Hash)), 0, 0};
_ ->
{hash, erlang:element(2, Hash), erlang:element(3, Hash) + 8, State}
end.
-file("src/murmur3a.gleam", 53).
-spec do_hash_bytes(hash(), bitstring()) -> {ok, hash()} | {error, nil}.
do_hash_bytes(Hash, Key) ->
case Key of
<<Byte, Rest/binary>> ->
_pipe = hash_byte(Hash, Byte),
do_hash_bytes(_pipe, Rest);
<<>> ->
{ok, Hash};
_ ->
{error, nil}
end.
-file("src/murmur3a.gleam", 109).
-spec finalize(hash(), integer()) -> hash().
finalize(Hash, Length) ->
State = begin
_pipe@5 = case erlang:element(4, Hash) of
0 ->
erlang:element(2, Hash);
_ ->
_pipe = erlang:element(4, Hash),
_pipe@1 = signed_multiply(_pipe, 16#cc9e2d51),
_pipe@2 = rotate_left(_pipe@1, 15),
_pipe@3 = signed_multiply(_pipe@2, 16#1b873593),
_pipe@4 = erlang:'bxor'(_pipe@3, erlang:element(2, Hash)),
erlang:'band'(_pipe@4, 16#ffffffff)
end,
erlang:'bxor'(_pipe@5, Length)
end,
State@1 = begin
_pipe@6 = erlang:'bsr'(State, 16),
_pipe@7 = erlang:'bxor'(_pipe@6, State),
_pipe@8 = signed_multiply(_pipe@7, 16#85ebca6b),
erlang:'band'(_pipe@8, 16#ffffffff)
end,
State@2 = begin
_pipe@9 = erlang:'bsr'(State@1, 13),
_pipe@10 = erlang:'bxor'(_pipe@9, State@1),
signed_multiply(_pipe@10, 16#c2b2ae35)
end,
{hash,
erlang:element(2, Hash),
erlang:element(3, Hash),
begin
_pipe@11 = erlang:'bsr'(State@2, 16),
_pipe@12 = erlang:'band'(_pipe@11, 16#ffff),
erlang:'bxor'(_pipe@12, State@2)
end}.
-file("src/murmur3a.gleam", 48).
?DOC(
" Computes a 32 bit Murmur3 hash of the supplied bit array.\n"
"\n"
" Returns `Error(Nil)` if the bit array is not byte-aligned.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_bytes(<<\"murmur3a\">>, 0)\n"
" // -> Ok(Hash(3521997819, 0, -391047908))\n"
" ```\n"
).
-spec hash_bytes(bitstring(), integer()) -> {ok, hash()} | {error, nil}.
hash_bytes(Key, Seed) ->
_pipe = do_hash_bytes(
{hash,
Seed,
erlang:element(3, {hash, 0, 0, 0}),
erlang:element(4, {hash, 0, 0, 0})},
Key
),
gleam@result:map(
_pipe,
fun(_capture) -> finalize(_capture, erlang:byte_size(Key)) end
).
-file("src/murmur3a.gleam", 32).
?DOC(
" Computes a 32 bit Murmur3 hash of the input string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_string(\"murmur3a\", 0)\n"
" // -> Hash(3521997819, 0, -391047908)\n"
" ```\n"
).
-spec hash_string(binary(), integer()) -> hash().
hash_string(Key, Seed) ->
Hash@1 = case hash_bytes(<<Key/binary>>, Seed) of
{ok, Hash} -> Hash;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"murmur3a"/utf8>>,
function => <<"hash_string"/utf8>>,
line => 33,
value => _assert_fail,
start => 755,
'end' => 807,
pattern_start => 766,
pattern_end => 774})
end,
Hash@1.
-file("src/murmur3a.gleam", 74).
?DOC(
" Computes a 32 bit Murmur3 hash of the supplied list of integers.\n"
"\n"
" **Note:** Values outside the 0–255 range will be truncated to their lowest\n"
" byte before hashing.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" hash_ints([1, 2, 3], 0)\n"
" // -> Hash(0, 24, -2133732860)\n"
" ```\n"
).
-spec hash_ints(list(integer()), integer()) -> hash().
hash_ints(Key, Seed) ->
{Hash@1, Length@1} = begin
_pipe = Key,
gleam@list:fold(
_pipe,
{{hash,
Seed,
erlang:element(3, {hash, 0, 0, 0}),
erlang:element(4, {hash, 0, 0, 0})},
0},
fun(Acc, I) ->
{Hash, Length} = Acc,
{hash_byte(Hash, erlang:'band'(I, 16#ff)), Length + 1}
end
)
end,
finalize(Hash@1, Length@1).