Base 16 in Erlang

Hello everybody,

Sorry for the noob question, but is there a way to encode to base 16 in Erlang?

I am trying to replicate this line, from Elixir to Erlang…

:crypto.mac(:hmac, :sha256, secret, source) |> Base.encode16 |> String.downcase

The closest I have is this

Hmac = crypto:mac(hmac, sha256, Secret, Source).

string:to_lower(...)

I could not find the equivalent of Base.encode16/1

I saw a base16 package on hex.pm, but wanted to know if this exists in std lib.

Thanks for taking time.

4 Likes

You can use io_lib:format("~.16b", [HMac]) to get the number as a hex string (which is assume that you want). Note that using the formatting directive b generates lower case directly, where B would give you uppercase.

There is also erlang:integer_to_list(HMac, 16), but that gives uppercase and you would need to use string:to_lower/1 on the the result.

Hope this helps.

3 Likes

A simple way but not the fastest way to encode a binary in base16 is to use a binary comprehension:

base16_bc(Bin) when is_binary(Bin) ->
    << <<(integer_to_binary(N, 16))/bitstring>> || <<N:4>> <= Bin >>.

It will produce a string with uppercase hex digits.

A faster way is the following:

base16_loop(Bin) when is_binary(Bin) ->
    base16_loop(Bin, <<>>).

base16_loop(<<N:8/unit:4,Rest/bitstring>>, Acc) ->
    Hex = integer_to_binary(N, 16),
    PadLen = 8 - byte_size(Hex),
    base16_loop(Rest, <<Acc/binary,
                        <<"00000000">>:PadLen/binary,
                        (integer_to_binary(N, 16))/binary>>);
base16_loop(<<N:1/unit:4,Rest/bitstring>>, Acc) ->
    base16_loop(Rest, <<Acc/binary, (integer_to_binary(N, 16))/binary>>);
base16_loop(<<>>, Acc) ->
    Acc.

According to erlperf running on my computer, this version is roughly three times faster:

$ erlperf 'r(X) -> t:base16_bc(X).' --init_runner 'rand:bytes(1_000_000).' 'r(X) -> t:base16_loop(X).' --init_runner 'rand:bytes(1_000_000).'
Code                              ||        QPS       Time     Rel
r(X) -> t:base16_loop(X).          1         41   24390 us    100%
r(X) -> t:base16_bc(X).            1         14   71429 us     34%
3 Likes

There is also binary:encode_hex/1 and binary:decode_hex/1, recently added to the binary module: Erlang -- binary

9 Likes

I forgot that those have been added. binary:encode_hex/1 is roughly three times as fast as base16_loop/1 according to erlperf:

$ erlperf 'r(X) -> t:base16_bc(X).' --init_runner 'rand:bytes(1_000_000).' 'r(X) -> t:base16_loop(X).' --init_runner 'rand:bytes(1_000_000).' 'r(X) -> binary:encode_hex(X).' --init_runner 'rand:bytes(1_000_000).'
Code                                  ||        QPS       Time     Rel
r(X) -> binary:encode_hex(X).          1        108    9259 us    100%
r(X) -> t:base16_loop(X).              1         39   25641 us     36%
r(X) -> t:base16_bc(X).                1         13   76923 us     12%
6 Likes

Thank You very much everybody…

There are different solutions to my problem, but the latest addition binary:encode_hex/1 seems to fit the most what I am looking for.

Thanks a lot again :heart:

3 Likes