-module(gsiphash). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([siphash/4, siphash_2_4/2]). -spec i64_cast(integer()) -> integer(). i64_cast(Value) -> erlang:'band'(Value, 16#ffffffffffffffff). -spec i64_get(bitstring()) -> {ok, integer()} | {error, binary()}. i64_get(Data) -> case gleam_stdlib:bit_array_slice(<>, 0, 8) of {ok, <>} -> {ok, Value}; _ -> {error, <<"gsiphash.i64_get: couldn't get integer from data"/utf8>>} end. -spec rotate_left(integer(), integer()) -> integer(). rotate_left(Value, Bits) -> erlang:'bor'( i64_cast(erlang:'bsl'(Value, Bits)), i64_cast(erlang:'bsr'(Value, (8 * 8) - Bits)) ). -spec sip_round(integer(), integer(), integer(), integer()) -> {integer(), integer(), integer(), integer()}. sip_round(V0, V1, V2, V3) -> V0@1 = i64_cast(V0 + V1), V1@1 = rotate_left(V1, 13), V1@2 = erlang:'bxor'(V1@1, V0@1), V0@2 = rotate_left(V0@1, 32), V2@1 = i64_cast(V2 + V3), V3@1 = rotate_left(V3, 16), V3@2 = erlang:'bxor'(V3@1, V2@1), V2@2 = i64_cast(V2@1 + V1@2), V1@3 = rotate_left(V1@2, 17), V1@4 = erlang:'bxor'(V1@3, V2@2), V2@3 = rotate_left(V2@2, 32), V0@3 = i64_cast(V0@2 + V3@2), V3@3 = rotate_left(V3@2, 21), V3@4 = erlang:'bxor'(V3@3, V0@3), {V0@3, V1@4, V2@3, V3@4}. -spec siphash(bitstring(), bitstring(), integer(), integer()) -> {ok, integer()} | {error, binary()}. siphash(Data, Key, C, D) -> Data_length = erlang:byte_size(Data), gleam@bool:guard( C =< 0, {error, <<"gsiphash.siphash: the count of C rounds must be greater than zero"/utf8>>}, fun() -> gleam@bool:guard( D =< 0, {error, <<"gsiphash.siphash: the count of D rounds must be greater than zero"/utf8>>}, fun() -> gleam@bool:guard( erlang:byte_size(Key) /= 16, {error, <<"gsiphash.siphash: the length of the key must be 16 bytes (128 bits)"/utf8>>}, fun() -> Left_out_bytes_count = case 8 of 0 -> 0; Gleam@denominator -> Data_length rem Gleam@denominator end, gleam@result:'try'( (gleam@result:'try'( gleam@result:replace_error( gleam_stdlib:bit_array_slice(Key, 0, 8), <<"gsiphash.siphash: couldn't get the MSB slice of the key"/utf8>> ), fun(Slice) -> i64_get(Slice) end )), fun(K0) -> gleam@result:'try'( (gleam@result:'try'( gleam@result:replace_error( gleam_stdlib:bit_array_slice( Key, 8, 8 ), <<"gsiphash.siphash: couldn't get the LSB slice of the key"/utf8>> ), fun(Slice@1) -> i64_get(Slice@1) end )), fun(K1) -> V0 = erlang:'bxor'( K0, 16#736f6d6570736575 ), V1 = erlang:'bxor'( K1, 16#646f72616e646f6d ), V2 = erlang:'bxor'( K0, 16#6c7967656e657261 ), V3 = erlang:'bxor'( K1, 16#7465646279746573 ), gleam@result:'try'( gleam@iterator:fold( begin _pipe = gleam@iterator:iterate( 0, fun(X) -> X + 8 end ), gleam@iterator:take( _pipe, case 8 of 0 -> 0; Gleam@denominator@1 -> (Data_length - Left_out_bytes_count) div Gleam@denominator@1 end ) end, {ok, {V0, V1, V2, V3}}, fun(Acc, Position) -> gleam@result:'try'( (gleam@result:'try'( gleam@result:replace_error( gleam_stdlib:bit_array_slice( Data, Position, gleam@int:min( 8, Data_length ) ), <<"gsiphash.siphash: couldn't get message block at position "/utf8, (gleam@int:to_string( Position ))/binary>> ), fun(Slice@2) -> i64_get( Slice@2 ) end )), fun(Message_block) -> gleam@result:'try'( Acc, fun(_use0) -> {V0@1, V1@1, V2@1, V3@1} = _use0, V3@2 = erlang:'bxor'( V3@1, Message_block ), {V0@3, V1@3, V2@3, V3@4} = gleam@iterator:fold( gleam@iterator:range( 1, C ), {V0@1, V1@1, V2@1, V3@2}, fun( Acc@1, _ ) -> {V0@2, V1@2, V2@2, V3@3} = Acc@1, sip_round( V0@2, V1@2, V2@2, V3@3 ) end ), V0@4 = erlang:'bxor'( V0@3, Message_block ), {ok, {V0@4, V1@3, V2@3, V3@4}} end ) end ) end ), fun(_use0@1) -> {V0@5, V1@4, V2@4, V3@5} = _use0@1, Last_message_block = i64_cast( erlang:'bsl'( Data_length, 56 ) ), gleam@result:'try'( case Left_out_bytes_count > 0 of false -> {ok, Last_message_block}; true -> gleam@iterator:fold( gleam@iterator:range( Data_length - Left_out_bytes_count, Data_length - 1 ), {ok, Last_message_block}, fun( Acc@2, Position@1 ) -> gleam@result:'try'( Acc@2, fun( Last_message_block@1 ) -> gleam@result:'try'( (gleam@result:'try'( gleam@result:replace_error( gleam_stdlib:bit_array_slice( Data, Position@1, 1 ), <<"gsiphash.siphash: couldn't get the left out byte at position "/utf8, (gleam@int:to_string( Position@1 ))/binary>> ), fun( Slice@3 ) -> i64_get( Slice@3 ) end )), fun( Left_out_byte ) -> Bits_to_shift = 8 * (Position@1 - (Data_length - Left_out_bytes_count)), Last_message_block@2 = erlang:'bor'( Last_message_block@1, i64_cast( erlang:'bsl'( Left_out_byte, Bits_to_shift ) ) ), {ok, Last_message_block@2} end ) end ) end ) end, fun( Last_message_block@3 ) -> V3@6 = erlang:'bxor'( V3@5, Last_message_block@3 ), {V0@7, V1@6, V2@6, V3@8} = gleam@iterator:fold( gleam@iterator:range( 1, C ), {V0@5, V1@4, V2@4, V3@6}, fun(Acc@3, _) -> {V0@6, V1@5, V2@5, V3@7} = Acc@3, sip_round( V0@6, V1@5, V2@5, V3@7 ) end ), V0@8 = erlang:'bxor'( V0@7, Last_message_block@3 ), V2@7 = erlang:'bxor'( V2@6, 16#ff ), {V0@10, V1@8, V2@9, V3@10} = gleam@iterator:fold( gleam@iterator:range( 1, D ), {V0@8, V1@6, V2@7, V3@8}, fun(Acc@4, _) -> {V0@9, V1@7, V2@8, V3@9} = Acc@4, sip_round( V0@9, V1@7, V2@8, V3@9 ) end ), Result = erlang:'bxor'( erlang:'bxor'( V0@10, V1@8 ), erlang:'bxor'( V2@9, V3@10 ) ), {ok, Result} end ) end ) end ) end ) end ) end ) end ). -spec siphash_2_4(bitstring(), bitstring()) -> {ok, integer()} | {error, binary()}. siphash_2_4(Data, Key) -> siphash(Data, Key, 2, 4).