diff --git a/LICENSE b/LICENSE index e7caf65..1cf1da9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,29 +1,16 @@ -BSD 3-Clause License +Copyright (c) 2016, Mathieu Kerjouan -Copyright (c) 2016, niamtokik -All rights reserved. +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all +copies. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b7c9bc --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ += berl32 + +Quick and Dirty Crockford Base32 Erlang Implementation + +== Build + + $ rebar3 compile + +== Encoding + + > berl32:encode("*"). + {ok,<<"1A">>} + + > berl32:encode(<<"*">>). + {ok,<<"1A">>} + + > berl32:encode("test"). + {ok,<<"1T6AWVM">>} + + > berl32:encode(<<"test">>). + {ok,<<"1T6AWVM">>} + + > berl32:encode(<<18446744073709551615:64/integer>>). + {ok, <<"FZZZZZZZZZZZZ">>} + +== Decoding + + > berl32:decode(<<"1A">>). + {ok,<<"*">>} + + > berl32:decode("1A"). + {ok, <<"*">>} + + > berl32:decode(<<"FZZZZZZZZZZZZZZ">>). + {ok,<<"ÿÿÿÿÿÿÿÿÿ">>} + +== Todo + + * add checksum/control character support + * add more unit test + * add more documentation + * add profiling and benchmarking diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..f618f3e --- /dev/null +++ b/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, []}. \ No newline at end of file diff --git a/src/berl32.app.src b/src/berl32.app.src new file mode 100644 index 0000000..9a5a2c6 --- /dev/null +++ b/src/berl32.app.src @@ -0,0 +1,15 @@ +{application, berl32, + [{description, "crockfork base32 erlang implementation"}, + {vsn, "0.1"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {maintainers, ["Mathieu Kerjouan"]}, + {licenses, ["ISC"]}, + {links, []} + ]}. diff --git a/src/berl32.erl b/src/berl32.erl new file mode 100644 index 0000000..9fb093f --- /dev/null +++ b/src/berl32.erl @@ -0,0 +1,335 @@ +%%%------------------------------------------------------------------- +%%% Copyright (c) 2016, Mathieu Kerjouan +%%% +%%% Permission to use, copy, modify, and/or distribute this software +%%% for any purpose with or without fee is hereby granted, provided +%%% that the above copyright notice and this permission notice appear +%%% in all copies. +%%% +%%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +%%% WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +%%% WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +%%% AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +%%% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +%%% LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +%%% NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +%%% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +%%% +%%%------------------------------------------------------------------- +%%% +%%% @author Mathieu Kerjouan +%%% @copytight 2016 Mathieu Kerjouan +%%% @version 0.1 +%%% @title berl32, base32 crockford erlang implementation +%%% +%%% @doc you can find more documentation here: +%%% - http://www.crockford.com/wrmg/base32.html +%%% - https://crockfordbase32.codeplex.com/ +%%% - https://github.com/jbittel/base32-crockford +%%% @end +%%% +%%% @todo - add type and specification +%%% - add more unit test +%%% - add checksum validation +%%% - add benchmarking and profiling +%%% - add more documentation +%%% @end +%%% +%%%------------------------------------------------------------------- +-module(berl32). +-author('Mathieu Kerjouan'). +-export([struct/0, struct/1]). +-export([check/0, check/1]). +-export([encode/1]). +-export([decode/1]). +-include_lib("eunit/include/eunit.hrl"). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +-type symbol() :: 0 .. 31. +-type decode_symbol() :: string(). +-type decode_symbols() :: [ decode_symbol(), ... ]. +-type encode_symbol() :: string(). +-type symbols() :: { symbol(), decode_symbols(), encode_symbol() }. +-type check_symbols() :: symbols(). + +%%-------------------------------------------------------------------- +%% @doc This function give you all encoded/decoded value in list of +%% tuple. Note, this version give you only list() value and not +%% bitstring. +%% @end +%%-------------------------------------------------------------------- +-spec struct() -> [ symbols(), ... ]. +struct() -> + [{ 0, ["0", "O", "o"], "0" } + ,{ 1, ["1", "I", "i", "L", "l"], "1" } + ,{ 2, ["2"], "2" } + ,{ 3, ["3"], "3" } + ,{ 4, ["4"], "4" } + ,{ 5, ["5"], "5" } + ,{ 6, ["6"], "6" } + ,{ 7, ["7"], "7" } + ,{ 8, ["8"], "8" } + ,{ 9, ["9"], "9" } + ,{ 10, ["A", "a"], "A" } + ,{ 11, ["B", "b"], "B" } + ,{ 12, ["C", "c"], "C" } + ,{ 13, ["D", "d"], "D" } + ,{ 14, ["E", "e"], "E" } + ,{ 15, ["F", "f"], "F" } + ,{ 16, ["G", "g"], "G" } + ,{ 17, ["H", "h"], "H" } + ,{ 18, ["J", "j"], "J" } + ,{ 19, ["K", "k"], "K" } + ,{ 20, ["M", "m"], "M" } + ,{ 21, ["N", "n"], "N" } + ,{ 22, ["P", "p"], "P" } + ,{ 23, ["Q", "q"], "Q" } + ,{ 24, ["R", "r"], "R" } + ,{ 25, ["S", "s"], "S" } + ,{ 26, ["T", "t"], "T" } + ,{ 27, ["V", "v"], "V" } + ,{ 28, ["W", "w"], "W" } + ,{ 29, ["X", "x"], "X" } + ,{ 30, ["Y", "y"], "Y" } + ,{ 31, ["Z", "z"], "Z" } + ]. + +%%-------------------------------------------------------------------- +%% @doc struct/1 convert raw integer() value to bitstring() encoded +%% value and do reverse convertion, bitstring() encoded value to +%% raw integer() value. +%% @end +%% -------------------------------------------------------------------- +-spec struct(integer()) -> bitstring(); + (bitstring()) -> integer(). +struct(0) -> <<"0">>; +struct(1) -> <<"1">>; +struct(2) -> <<"2">>; +struct(3) -> <<"3">>; +struct(4) -> <<"4">>; +struct(5) -> <<"5">>; +struct(6) -> <<"6">>; +struct(7) -> <<"7">>; +struct(8) -> <<"8">>; +struct(9) -> <<"9">>; +struct(10) -> <<"A">>; +struct(11) -> <<"B">>; +struct(12) -> <<"C">>; +struct(13) -> <<"D">>; +struct(14) -> <<"E">>; +struct(15) -> <<"F">>; +struct(16) -> <<"G">>; +struct(17) -> <<"H">>; +struct(18) -> <<"J">>; +struct(19) -> <<"K">>; +struct(20) -> <<"M">>; +struct(21) -> <<"N">>; +struct(22) -> <<"P">>; +struct(23) -> <<"Q">>; +struct(24) -> <<"R">>; +struct(25) -> <<"S">>; +struct(26) -> <<"T">>; +struct(27) -> <<"V">>; +struct(28) -> <<"W">>; +struct(29) -> <<"X">>; +struct(30) -> <<"Y">>; +struct(31) -> <<"Z">>; +struct(<<"0">>) -> 0; +struct(<<"O">>) -> 0; +struct(<<"o">>) -> 0; +struct(<<"1">>) -> 1; +struct(<<"L">>) -> 1; +struct(<<"l">>) -> 1; +struct(<<"I">>) -> 1; +struct(<<"i">>) -> 1; +struct(<<"2">>) -> 2; +struct(<<"3">>) -> 3; +struct(<<"4">>) -> 4; +struct(<<"5">>) -> 5; +struct(<<"6">>) -> 6; +struct(<<"7">>) -> 7; +struct(<<"8">>) -> 8; +struct(<<"9">>) -> 9; +struct(<<"A">>) -> 10; +struct(<<"a">>) -> 10; +struct(<<"B">>) -> 11; +struct(<<"b">>) -> 11; +struct(<<"C">>) -> 12; +struct(<<"c">>) -> 12; +struct(<<"D">>) -> 13; +struct(<<"d">>) -> 13; +struct(<<"E">>) -> 14; +struct(<<"e">>) -> 14; +struct(<<"F">>) -> 15; +struct(<<"f">>) -> 15; +struct(<<"G">>) -> 16; +struct(<<"g">>) -> 16; +struct(<<"H">>) -> 17; +struct(<<"h">>) -> 17; +struct(<<"J">>) -> 18; +struct(<<"j">>) -> 18; +struct(<<"K">>) -> 19; +struct(<<"k">>) -> 19; +struct(<<"M">>) -> 20; +struct(<<"m">>) -> 20; +struct(<<"N">>) -> 21; +struct(<<"n">>) -> 21; +struct(<<"P">>) -> 22; +struct(<<"p">>) -> 22; +struct(<<"Q">>) -> 23; +struct(<<"q">>) -> 23; +struct(<<"R">>) -> 24; +struct(<<"r">>) -> 24; +struct(<<"S">>) -> 25; +struct(<<"s">>) -> 25; +struct(<<"T">>) -> 26; +struct(<<"t">>) -> 26; +struct(<<"V">>) -> 27; +struct(<<"v">>) -> 27; +struct(<<"W">>) -> 28; +struct(<<"w">>) -> 28; +struct(<<"X">>) -> 29; +struct(<<"x">>) -> 29; +struct(<<"Y">>) -> 30; +struct(<<"y">>) -> 30; +struct(<<"Z">>) -> 31; +struct(<<"z">>) -> 31. + +%%-------------------------------------------------------------------- +%% @doc check/0 give you all check characters used in base32. +%% @end +%%-------------------------------------------------------------------- +-spec check() -> [check_symbols(), ...]. +check() -> + [{32, ["*"], "*"} + ,{33, ["~"], "~"} + ,{34, ["$"], "$"} + ,{35, ["="], "="} + ,{36, ["U", "u"], "U"}]. + +%%-------------------------------------------------------------------- +%% @doc check/1 convert raw integer() value to bitstring() encoded +%% value and do also in reverse, bitstring() to integer(). +%% @end +%%-------------------------------------------------------------------- +-spec check(integer()) -> bitstring(); + (bitstring()) -> integer(). +check(32) -> <<"*">>; +check(33) -> <<"~">>; +check(34) -> <<"$">>; +check(35) -> <<"=">>; +check(36) -> <<"U">>; +check(<<"*">>) -> 32; +check(<<"~">>) -> 33; +check(<<"$">>) -> 34; +check(<<"=">>) -> 35; +check(<<"U">>) -> 36; +check(<<"u">>) -> 36. + +%%-------------------------------------------------------------------- +%% @doc encode/1 encode raw value to bitstring() encoded value. +%% @end +%%-------------------------------------------------------------------- +% -spec encode(Data) -> ok. +encode(Data) -> + bitstring_to_base32(Data). + +bitstring_to_base32_test() -> + ?assertEqual(bitstring_to_base32(<<1>>), {ok, <<"1">>}), + ?assertEqual(bitstring_to_base32(<<2>>), {ok, <<"2">>}), + ?assertEqual(bitstring_to_base32(<<3>>), {ok, <<"3">>}), + ?assertEqual(bitstring_to_base32(<<4>>), {ok, <<"4">>}), + ?assertEqual(bitstring_to_base32(<<194>>), {ok, <<"62">>}), + ?assertEqual(bitstring_to_base32(<<18446744073709551615:64/integer>>), + {ok, <<"FZZZZZZZZZZZZ">>}), + ?assertEqual(bitstring_to_base32(<<42>>), {ok, <<"1A">>}). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +-spec bitstring_to_base32(iolist() | binary()) -> {ok, string()}. +bitstring_to_base32(List) + when is_list(List) -> + bitstring_to_base32(list_to_binary(List)); +bitstring_to_base32(<<0:3, A:5/integer>>) -> + {ok, struct(A)}; +bitstring_to_base32(Bitstring) + when is_bitstring(Bitstring)-> + Shift = bit_size(Bitstring) rem 5, + bitstring_to_base32_shift(Shift, Bitstring, <<>>). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +% -spec bitstring_to_base32(_, _) -> ok. +bitstring_to_base32(<<>>, Buf) -> + {ok, Buf}; +bitstring_to_base32(<>, Buf) -> + Base32 = struct(Head), + Return = <>, + bitstring_to_base32(Tail, Return). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +% -spec bitstring_to_base32_shift(_,_,_) -> ok. +bitstring_to_base32_shift(0, Bitstring, Buf) -> + bitstring_to_base32(Bitstring, Buf); +bitstring_to_base32_shift(Shift, Bitstring, Buf) -> + <> = Bitstring, + Base32 = struct(R), + Return = <>, + bitstring_to_base32(Tail, Return). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +% -spec decode(Data) -> ok. +decode(Data) -> + base32_to_bitstring(Data). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +base32_to_bitstring_test() -> + ?assertEqual(base32_to_bitstring(<<"1A">>), {ok, <<42>>}), + ?assertEqual(base32_to_bitstring(<<"FZZZZZZZZZZZZ">>), + {ok, <<18446744073709551615:64/integer>>}), + ?assertEqual(base32_to_bitstring(<<"fZZZZZZZZZZZZ">>), + {ok, <<18446744073709551615:64/integer>>}), + ?assertEqual(base32_to_bitstring(<<"FzzZZZZZZZZZZ">>), + {ok, <<18446744073709551615:64/integer>>}), + ?assertEqual(base32_to_bitstring(<<"FZZZZZZZZZZzz">>), + {ok, <<18446744073709551615:64/integer>>}). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +-spec base32_to_bitstring(string()) -> {ok, iolist() | binary()}. +base32_to_bitstring(List) when is_list(List) -> + base32_to_bitstring(list_to_binary(List)); +base32_to_bitstring(Bitstring) + when is_bitstring(Bitstring)-> + base32_to_bitstring(Bitstring, <<>>). + +%%-------------------------------------------------------------------- +%% +%%-------------------------------------------------------------------- +% -spce base32_to_bitstring(_,_) -> ok. +base32_to_bitstring(<<>>, Buf) -> + Shift = 8 - (bit_size(Buf) rem 8), + base32_to_bitstring_shift(<<0:Shift, Buf/bitstring>>); +base32_to_bitstring(<<$-, Tail/bitstring>>, Buf) -> + base32_to_bitstring(Tail, Buf); +base32_to_bitstring(<<$->>, _) -> + {ok, <<>>}; +base32_to_bitstring(<>, <<>>) -> + Symbol = struct(<>), + {ok, <<0:3, Symbol:5/integer>>}; +base32_to_bitstring(<>, Buf) -> + Symbol = struct(<>), + base32_to_bitstring(Tail, <>). +base32_to_bitstring_shift(<<_:8, Return/bitstring>>) -> + {ok, Return}.