Current section
Files
Jump to
Current section
Files
src/base/iam_base16.erl
%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
%% vim: ts=4 sw=4 ft=erlang noet
%%%-------------------------------------------------------------------
%%% @author Andrew Bennett <potatosaladx@gmail.com>
%%% @copyright 2017, Andrew Bennett
%%% @doc RFC 4648, Section 8: https://tools.ietf.org/html/rfc4648#section-8
%%%
%%% @end
%%% Created : 11 May 2017 by Andrew Bennett <potatosaladx@gmail.com>
%%%-------------------------------------------------------------------
-module(iam_base16).
-include("iam_base.hrl").
%% API
-export([decode/1]).
-export([decode/2]).
-export(['decode!'/1]).
-export(['decode!'/2]).
-export([encode/1]).
-export([encode/2]).
-export([random/1]).
-export([random/2]).
%% Macros
-define(LC_B16_TO_INT(C),
case C of
$0 -> 16#0;
$1 -> 16#1;
$2 -> 16#2;
$3 -> 16#3;
$4 -> 16#4;
$5 -> 16#5;
$6 -> 16#6;
$7 -> 16#7;
$8 -> 16#8;
$9 -> 16#9;
$a -> 16#A;
$b -> 16#B;
$c -> 16#C;
$d -> 16#D;
$e -> 16#E;
$f -> 16#F
end).
-define(MC_B16_TO_INT(C),
case C of
$0 -> 16#0;
$1 -> 16#1;
$2 -> 16#2;
$3 -> 16#3;
$4 -> 16#4;
$5 -> 16#5;
$6 -> 16#6;
$7 -> 16#7;
$8 -> 16#8;
$9 -> 16#9;
$a -> 16#A;
$b -> 16#B;
$c -> 16#C;
$d -> 16#D;
$e -> 16#E;
$f -> 16#F;
$A -> 16#A;
$B -> 16#B;
$C -> 16#C;
$D -> 16#D;
$E -> 16#E;
$F -> 16#F
end).
-define(UC_B16_TO_INT(C),
case C of
$0 -> 16#0;
$1 -> 16#1;
$2 -> 16#2;
$3 -> 16#3;
$4 -> 16#4;
$5 -> 16#5;
$6 -> 16#6;
$7 -> 16#7;
$8 -> 16#8;
$9 -> 16#9;
$A -> 16#A;
$B -> 16#B;
$C -> 16#C;
$D -> 16#D;
$E -> 16#E;
$F -> 16#F
end).
-define(LC_INT_TO_B16(C),
case C of
16#0 -> $0;
16#1 -> $1;
16#2 -> $2;
16#3 -> $3;
16#4 -> $4;
16#5 -> $5;
16#6 -> $6;
16#7 -> $7;
16#8 -> $8;
16#9 -> $9;
16#A -> $a;
16#B -> $b;
16#C -> $c;
16#D -> $d;
16#E -> $e;
16#F -> $f
end).
-define(UC_INT_TO_B16(C),
case C of
16#0 -> $0;
16#1 -> $1;
16#2 -> $2;
16#3 -> $3;
16#4 -> $4;
16#5 -> $5;
16#6 -> $6;
16#7 -> $7;
16#8 -> $8;
16#9 -> $9;
16#A -> $A;
16#B -> $B;
16#C -> $C;
16#D -> $D;
16#E -> $E;
16#F -> $F
end).
%%%===================================================================
%%% API functions
%%%===================================================================
decode(Input) when ?is_iodata(Input) ->
decode(Input, #{}).
decode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) ->
try 'decode!'(Input, Opts) of
Output when is_binary(Output) ->
{ok, Output}
catch
_:_ ->
error
end;
decode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) ->
decode(Input, maps:from_list(Opts)).
'decode!'(Input) when ?is_iodata(Input) ->
'decode!'(Input, #{}).
'decode!'([], #{}) ->
<<>>;
'decode!'(<<>>, #{}) ->
<<>>;
'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) ->
Case = maps:get('case', Opts, 'mixed'),
case {Case, erlang:iolist_size(Input) rem 2} of
{'lower', 0} ->
<< << ((?LC_B16_TO_INT(X) bsl 4) + ?LC_B16_TO_INT(Y)) >> || << X, Y >> <= ?to_binary(Input) >>;
{'mixed', 0} ->
<< << ((?MC_B16_TO_INT(X) bsl 4) + ?MC_B16_TO_INT(Y)) >> || << X, Y >> <= ?to_binary(Input) >>;
{'upper', 0} ->
<< << ((?UC_B16_TO_INT(X) bsl 4) + ?UC_B16_TO_INT(Y)) >> || << X, Y >> <= ?to_binary(Input) >>;
_ ->
erlang:error({badarg, [Input, Opts]})
end;
'decode!'(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) ->
'decode!'(Input, maps:from_list(Opts)).
encode(Input) when ?is_iodata(Input) ->
encode(Input, #{}).
encode(Input, Opts) when ?is_iodata(Input) andalso is_map(Opts) ->
Case = maps:get('case', Opts, 'upper'),
case Case of
'lower' ->
<< << (?LC_INT_TO_B16(V bsr 4)), (?LC_INT_TO_B16(V band 16#F)) >> || << V >> <= ?to_binary(Input) >>;
'upper' ->
<< << (?UC_INT_TO_B16(V bsr 4)), (?UC_INT_TO_B16(V band 16#F)) >> || << V >> <= ?to_binary(Input) >>;
_ ->
erlang:error({badarg, [Input, Opts]})
end;
encode(Input, Opts) when ?is_iodata(Input) andalso is_list(Opts) ->
encode(Input, maps:from_list(Opts)).
random(Bytes) when is_integer(Bytes) andalso Bytes >= 0 ->
random(Bytes, #{}).
random(0, Opts) when is_map(Opts) ->
<<>>;
random(Bytes, Opts) when (Bytes =:= 1 orelse (Bytes rem 2) =/= 0) andalso is_map(Opts) ->
erlang:error({badarg, [Bytes, Opts]});
random(Bytes, Opts) when is_integer(Bytes) andalso Bytes > 0 andalso is_map(Opts) ->
Size = Bytes div 2,
Binary = crypto:strong_rand_bytes(Size),
encode(Binary, Opts);
random(Bytes, Opts) when is_integer(Bytes) andalso Bytes >= 0 andalso is_list(Opts) ->
random(Bytes, maps:from_list(Opts)).