-
Notifications
You must be signed in to change notification settings - Fork 184
/
ar_deep_hash.erl
81 lines (70 loc) · 2.34 KB
/
ar_deep_hash.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
-module(ar_deep_hash).
-export([hash/1]).
-include("ar.hrl").
-include_lib("eunit/include/eunit.hrl").
hash(List) when is_list(List) -> hash_bin_or_list(List).
%%% INTERNAL
hash_bin_or_list(Bin) when is_binary(Bin) ->
Tag = <<"blob", (integer_to_binary(byte_size(Bin)))/binary>>,
hash_bin(<<(hash_bin(Tag))/binary, (hash_bin(Bin))/binary>>);
hash_bin_or_list(List) when is_list(List) ->
Tag = <<"list", (integer_to_binary(length(List)))/binary>>,
hash_list(List, hash_bin(Tag)).
hash_list([], Acc) ->
Acc;
hash_list([Head | List], Acc) ->
HashPair = <<Acc/binary, (hash_bin_or_list(Head))/binary>>,
NewAcc = hash_bin(HashPair),
hash_list(List, NewAcc).
hash_bin(Bin) when is_binary(Bin) ->
crypto:hash(?DEEP_HASH_ALG, Bin).
%%% TESTS
hash_test() ->
V1 = crypto:strong_rand_bytes(32),
V2 = crypto:strong_rand_bytes(32),
V3 = crypto:strong_rand_bytes(32),
V4 = crypto:strong_rand_bytes(32),
DeepList = [V1, [V2, V3], V4],
H1 = hash_bin(<<(hash_bin(<<"blob", "32">>))/binary, (hash_bin(V1))/binary>>),
H2 = hash_bin(<<(hash_bin(<<"blob", "32">>))/binary, (hash_bin(V2))/binary>>),
H3 = hash_bin(<<(hash_bin(<<"blob", "32">>))/binary, (hash_bin(V3))/binary>>),
H4 = hash_bin(<<(hash_bin(<<"blob", "32">>))/binary, (hash_bin(V4))/binary>>),
HSublistTag = hash_bin(<<"list", "2">>),
HSublistHead = hash_bin(<<HSublistTag/binary, H2/binary>>),
HSublist = hash_bin(<<HSublistHead/binary, H3/binary>>),
HListTag = hash_bin(<<"list", "3">>),
HHead = hash_bin(<<HListTag/binary, H1/binary>>),
HWithSublist = hash_bin(<<HHead/binary, HSublist/binary>>),
H = hash_bin(<<HWithSublist/binary, H4/binary>>),
?assertEqual(H, hash(DeepList)).
hash_empty_list_test() ->
?assertEqual(hash_bin(<<"list", "0">>), hash([])).
hash_uniqueness_test() ->
?assertNotEqual(
hash([<<"a">>]),
hash([[<<"a">>]])
),
?assertNotEqual(
hash([<<"a">>, <<"b">>]),
hash([<<"b">>, <<"a">>])
),
?assertNotEqual(
hash([<<"a">>, <<>>]),
hash([<<"a">>])
),
?assertNotEqual(
hash([<<"a">>, <<"b">>]),
hash([[<<"a">>], <<"b">>])
),
?assertNotEqual(
hash([<<"a">>, [<<"b">>, <<"c">>]]),
hash([<<"a">>, <<"b">>, <<"c">>])
),
?assertNotEqual(
hash([<<"a">>, [<<"b">>, <<"c">>], [<<"d">>, <<"e">>]]),
hash([<<"a">>, [<<"b">>, <<"c">>, <<"d">>, <<"e">>]])
),
?assertNotEqual(
hash([<<"a">>, [<<"b">>], <<"c">>, <<"d">>]),
hash([<<"a">>, [<<"b">>, <<"c">>], <<"d">>])
).