Skip to content

Commit

Permalink
Added encode_hex/decode_hex functions & unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
snaiper80 committed Apr 8, 2015
1 parent 5ad30c4 commit 1299d15
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 23 deletions.
24 changes: 23 additions & 1 deletion doc/hashids.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ hashids_context() = #hashids_context{salt = list(), min_length = non_neg_integer
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#alphabet-1">alphabet/1</a></td><td>returns adjusted custom alphabet from context.</td></tr><tr><td valign="top"><a href="#decode-2">decode/2</a></td><td>decode hash string.</td></tr><tr><td valign="top"><a href="#encode-2">encode/2</a></td><td>encode numbers.</td></tr><tr><td valign="top"><a href="#min_hash_length-1">min_hash_length/1</a></td><td>returns minimum hash length from context.</td></tr><tr><td valign="top"><a href="#new-0">new/0</a></td><td>make a new hashids context (convenient function).</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>make a new hashids context.</td></tr><tr><td valign="top"><a href="#salt-1">salt/1</a></td><td>returns salt from context.</td></tr></table>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#alphabet-1">alphabet/1</a></td><td>returns adjusted custom alphabet from context.</td></tr><tr><td valign="top"><a href="#decode-2">decode/2</a></td><td>decode hash string.</td></tr><tr><td valign="top"><a href="#decode_hex-2">decode_hex/2</a></td><td>decode hash string to decoded hex string.</td></tr><tr><td valign="top"><a href="#encode-2">encode/2</a></td><td>encode numbers.</td></tr><tr><td valign="top"><a href="#encode_hex-2">encode_hex/2</a></td><td>encode hex string.</td></tr><tr><td valign="top"><a href="#min_hash_length-1">min_hash_length/1</a></td><td>returns minimum hash length from context.</td></tr><tr><td valign="top"><a href="#new-0">new/0</a></td><td>make a new hashids context (convenient function).</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>make a new hashids context.</td></tr><tr><td valign="top"><a href="#salt-1">salt/1</a></td><td>returns salt from context.</td></tr></table>


<a name="functions"></a>
Expand Down Expand Up @@ -57,6 +57,17 @@ decode(Context::<a href="#type-hashids_context">hashids_context()</a>, HashStr::
<br />

decode hash string
<a name="decode_hex-2"></a>

### decode_hex/2 ###


<pre><code>
decode_hex(Context::<a href="#type-hashids_context">hashids_context()</a>, HashStr::string()) -&gt; string()
</code></pre>
<br />

decode hash string to decoded hex string
<a name="encode-2"></a>

### encode/2 ###
Expand All @@ -68,6 +79,17 @@ encode(Context::<a href="#type-hashids_context">hashids_context()</a>, N::intege
<br />

encode numbers
<a name="encode_hex-2"></a>

### encode_hex/2 ###


<pre><code>
encode_hex(Context::<a href="#type-hashids_context">hashids_context()</a>, Str::string()) -&gt; string()
</code></pre>
<br />

encode hex string
<a name="min_hash_length-1"></a>

### min_hash_length/1 ###
Expand Down
35 changes: 35 additions & 0 deletions src/hashids.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-module(hashids).
-export([new/0, new/1,
encode/2, decode/2,
encode_hex/2, decode_hex/2,
salt/1, alphabet/1, min_hash_length/1]).

-ifdef(TEST).
Expand Down Expand Up @@ -76,6 +77,14 @@ encode(Context, N) when is_list(N) ->
_ -> internal_encode(Context, N)
end.


%% @doc encode hex string
%% @spec encode_hex(hashids_context(), string()) -> string()
-spec encode_hex(hashids_context(), string()) -> string().
encode_hex(Context, Str) when is_list(Str) ->
encode(Context, [list_to_integer([$1 | S], 16) || S <- parts(Str, 12)]).


%% @doc decode hash string
%% @spec decode(hashids_context(), string()) -> [integer(), ...]
-spec decode(hashids_context(), string()) -> [integer(), ...].
Expand All @@ -84,6 +93,15 @@ decode(_, []) ->
decode(Context, HashStr) when is_list(HashStr) ->
internal_decode(Context, HashStr).


%% @doc decode hash string to decoded hex string
%% @spec decode_hex(hashids_context(), string()) -> string()
-spec decode_hex(hashids_context(), string()) -> string().
decode_hex(Context, HashStr) when is_list(HashStr) ->
DecodedNums = decode(Context, HashStr),
lists:concat([begin [_ | T] = integer_to_list(I, 16), T end || I <- DecodedNums]).


%% @doc returns salt from context
%% @spec salt(hashids_context()) -> string()
-spec salt(hashids_context()) -> string().
Expand Down Expand Up @@ -369,3 +387,20 @@ ceiling(X) ->
true -> T;
false -> T + 1
end.


parts(List, Max) ->
RevList = split_list(List, Max),
lists:foldl(fun(E, Acc) ->
[lists:reverse(E)|Acc]
end, [], RevList).

split_list(List, Max) ->
element(1, lists:foldl(fun
(E, {[Buff|Acc], C}) when C < Max ->
{[[E|Buff]|Acc], C+1};
(E, {[Buff|Acc], _}) ->
{[[E],Buff|Acc], 1};
(E, {[], _}) ->
{[[E]], 1}
end, {[], 0}, List)).
94 changes: 72 additions & 22 deletions test/hashids_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,28 @@ setup_context_test_() ->
setup, fun() -> ok end,
fun teardown/1,
fun(_) ->
[
?_assertMatch({ hashids_context, [], 0,
[
?_assertMatch({ hashids_context, [], 0,
"gjklmnopqrvwxyzABDEGJKLMNOPQRVWXYZ1234567890",
"cfhistuCFHISTU",
"abde"
}, hashids:new()),

?_assertMatch({ hashids_context, "123", 16,
?_assertMatch({ hashids_context, "123", 16,
"9a645b8e72d",
"fc01",
"3"
}, hashids:new([{salt, "123"},
}, hashids:new([{salt, "123"},
{min_hash_length, 16},
{default_alphabet, "0123456789abcdef"}])),

?_assertMatch({ hashids_context, "this is my salt", 8,
?_assertMatch({ hashids_context, "this is my salt", 8,
"5N6y2rljDQak4xgzn8ZR1oKYLmJpEbVq3OBv9WwXPMe7",
"UHuhtcITCsFifS",
"AdG0"
}, hashids:new([{salt, "this is my salt"}, {min_hash_length, 8}])),

?_assertMatch({ hashids_context, "", 0,
?_assertMatch({ hashids_context, "", 0,
"01",
"fhistuCFHISTU",
"c"
Expand Down Expand Up @@ -73,11 +73,12 @@ encode_test_() ->
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch("NkK9", hashids:encode(Ctx, 12345)),
?_assertMatch("aBMswoO2UB3Sj", hashids:encode(Ctx, [683, 94108, 123, 5])),
?_assertMatch("1Wc8cwcE", hashids:encode(Ctx, [5, 5, 5, 5])),
?_assertMatch("kRHnurhptKcjIDTWC3sx", hashids:encode(Ctx, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
?_assertMatch("kRHnurhptKcjIDTWC3sx", hashids:encode(Ctx, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
?_assertMatch("5x", hashids:encode(Ctx, 0))
]
end
},
Expand All @@ -86,7 +87,7 @@ encode_test_() ->
setup, fun() -> hashids:new() end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch("", hashids:encode(Ctx, -1)),
?_assertMatch("", hashids:encode(Ctx, [10, -10])),
?_assertMatch("jR", hashids:encode(Ctx, 1)),
Expand All @@ -103,7 +104,7 @@ encode_test_() ->
setup, fun() -> hashids:new([{min_hash_length, 18}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch("J4q2VolejRejNmGQBW", hashids:encode(Ctx, 1)),
?_assertMatch("Q16nIJGnhxVRXtwovKu1ypq6hGV5o9", hashids:encode(Ctx, [4140, 21147, 115975, 678570, 4213597, 27644437]))
]
Expand All @@ -114,7 +115,7 @@ encode_test_() ->
setup, fun() -> hashids:new([{default_alphabet, "ABCDEFGhijklmn34567890-:"}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch(":mi8C8F7Am", hashids:encode(Ctx, [1,2,3,4,5]))
]
end
Expand All @@ -124,7 +125,7 @@ encode_test_() ->
setup, fun() -> hashids:new() end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch("wpfLh9iwsqt0uyCEFjHM", hashids:encode(Ctx, lists:seq(1, 10))) ]
end
},
Expand All @@ -133,7 +134,7 @@ encode_test_() ->
setup, fun() -> hashids:new() end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch("jR", hashids:encode(Ctx, 1)),
?_assertMatch("k5", hashids:encode(Ctx, 2)),
?_assertMatch("l5", hashids:encode(Ctx, 3)),
Expand All @@ -152,7 +153,7 @@ decode_test_() ->
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch([], hashids:decode(Ctx, "")),
?_assertMatch([12345], hashids:decode(Ctx, "NkK9")),
?_assertMatch([683, 94108, 123, 5], hashids:decode(Ctx, "aBMswoO2UB3Sj")),
Expand All @@ -166,7 +167,7 @@ decode_test_() ->
setup, fun() -> ok end,
fun teardown/1,
fun(_) ->
[
[
?_assertMatch([12345], hashids:decode(hashids:new([{salt, "this is my salt"}]), "NkK9")),
?_assertMatch([], hashids:decode(hashids:new([{salt, "123456"}]), "NkK9"))
]
Expand All @@ -177,7 +178,7 @@ decode_test_() ->
setup, fun() -> hashids:new([{salt, "this is my salt"}, {min_hash_length, 8}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertMatch([1], hashids:decode(Ctx, "gB0NV05e")),
?_assertMatch([25, 100, 950], hashids:decode(Ctx, "mxi8XH87")),
?_assertMatch([5,200,195, 1], hashids:decode(Ctx, "KQcmkIW8hX"))
Expand All @@ -189,7 +190,7 @@ decode_test_() ->
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
[
?_assertException(error,
{badmatch, {error, cant_unhash}},
hashids:decode(Ctx, "asdf-'"))
Expand All @@ -198,14 +199,63 @@ decode_test_() ->
}
].

encode_decode_hex_test_() ->
[
{
"Encoding Hex String Tests",
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
?_assertMatch("", hashids:encode_hex(Ctx, "")),
?_assertMatch("lzY", hashids:encode_hex(Ctx, "FA")),
?_assertMatch("MemE", hashids:encode_hex(Ctx, "26dd")),
?_assertMatch("eBMrb", hashids:encode_hex(Ctx, "FF1A")),
?_assertMatch("D9NPE", hashids:encode_hex(Ctx, "12abC")),
?_assertMatch("9OyNW", hashids:encode_hex(Ctx, "185b0")),
?_assertMatch("MRWNE", hashids:encode_hex(Ctx, "17b8d")),
?_assertMatch("4o6Z7KqxE", hashids:encode_hex(Ctx, "1d7f21dd38")),
?_assertMatch("ooweQVNB", hashids:encode_hex(Ctx, "20015111d")),
?_assertMatch("j436e8M4Mjfry18O4xnNcJV", hashids:encode_hex(Ctx, "4f5ce2be8afa96092600000134")),
?_assertMatch("yNyaoWeKWVINWqvaM9bw", hashids:encode_hex(Ctx, "507f191e810c19729de860ea"))

]
end
},
{
"Decoding HashString to hex string Tests",
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
?_assertMatch("", hashids:decode_hex(Ctx, "")),
?_assertMatch("FA", hashids:decode_hex(Ctx, "lzY")),
?_assertMatch("26DD", hashids:decode_hex(Ctx, "MemE")),
?_assertMatch("FF1A", hashids:decode_hex(Ctx, "eBMrb")),
?_assertMatch("4F5CE2BE8AFA96092600000134", hashids:decode_hex(Ctx, "j436e8M4Mjfry18O4xnNcJV"))
]
end
},
{
"Encoding Hex String Tests BUT invalid hexstring",
setup, fun() -> hashids:new([{salt, "this is my salt"}]) end,
fun teardown/1,
fun(Ctx) ->
[
?_assertException(error, badarg, hashids:encode_hex(Ctx, "HH"))
]
end
}
].

consistent_shuffle_test_() ->
[
{
"consistent_shuffle function Tests",
setup, fun() -> ok end,
fun teardown/1,
fun(_) ->
[
[
?_assertMatch(?DEFAULT_ALPHABET, hashids:consistent_shuffle(?DEFAULT_ALPHABET, "")),

?_assertMatch("ba", hashids:consistent_shuffle("ab", "this is my salt")),
Expand All @@ -216,7 +266,7 @@ consistent_shuffle_test_() ->
?_assertMatch("f17a8zvCwo0iuqYDXlJ4RmAS2end5ghTcpjbOWLK9GFyE6xUI3ZBMQtPsNHrkV",
hashids:consistent_shuffle(?DEFAULT_ALPHABET, "salt")),

?_assertMatch("fcaodykrgqvblxjwmtupzeisnh",
?_assertMatch("fcaodykrgqvblxjwmtupzeisnh",
hashids:consistent_shuffle("abcdefghijklmnopqrstuvwxyz",
"this is my salt"))
]
Expand Down Expand Up @@ -249,7 +299,7 @@ private_hash_unhash_test_() ->
setup, fun() -> ok end,
fun teardown/1,
fun(_) ->
[
[
?_assertMatch(59, hashids:unhash("abbd", "abcdefg")),
?_assertMatch(66, hashids:unhash("abcd", "abcdefg")),
?_assertMatch(100, hashids:unhash("acac", "abcdefg")),
Expand All @@ -266,8 +316,8 @@ getter_test_() ->
[
{
"getter Tests",
setup, fun() -> hashids:new([{salt, "this is my salt"},
{min_hash_length, 18},
setup, fun() -> hashids:new([{salt, "this is my salt"},
{min_hash_length, 18},
{default_alphabet, "ABCDEFGhijklmn34567890-:"}]) end,
fun teardown/1,
fun(Ctx) ->
Expand Down

0 comments on commit 1299d15

Please sign in to comment.