Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: update base64 basecode. #5

Merged
merged 4 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nuid
![nuid](https://github.com/nomasystems/nuid/actions/workflows/build.yml/badge.svg)
[![nuid](https://github.com/nomasystems/nuid/actions/workflows/ci.yml/badge.svg)](https://github.com/nomasystems/nuid/actions/workflows/ci.yml)

`nuid` is an OTP library to generate unique identifiers.

Expand Down
1 change: 1 addition & 0 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
]}.

{cover_opts, [verbose]}.
{cover_excl_mods, [nuid_base64]}.
{cover_enabled, true}.

{xref_ignores, [nuid]}.
134 changes: 71 additions & 63 deletions src/nuid_base64.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2021. All Rights Reserved.
%% Copyright Ericsson AB 2007-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand All @@ -21,68 +21,109 @@

-module(nuid_base64).

-export([encode/1, decode/1]).
-export([
encode/1,
decode/1
]).

%% RFC 4648§5: base64url - Base 64 Encoding alphabet
%-type base64_alphabet() :: $0..$9 | $- | $A..$Z | $_ | $a..$z.
%% Alphabet reordered to be lexicographically ordered.
%% RFC 4648: Base 64 Encoding alphabet
%% -type base64_alphabet() :: $A..$Z | $a..$z | $0..$9 | $- | $_.
%% Section 4, `urlsafe' for RFC 4648 Section 5.
%% It's been reordered to be lexicographically sortable.

%% The following type is a subtype of string() for return values
%% of encoding functions.
-type base64_binary() :: binary().

%% Decoded sequence of octets
-type byte_string() :: [byte()].

-spec encode(Data) -> Base64 when
Data :: binary(),
Data :: byte_string() | binary(),
Base64 :: base64_binary().

encode(Bin) ->
encode(Bin) when is_binary(Bin) ->
encode_binary(Bin, <<>>).

encode_binary(<<B1:6, B2:6, B3:6, B4:6, B5:6, B6:6, B7:6, B8:6, Ls/bits>>, A) ->
encode_binary(
Ls,
<<A/bits, (b64e(B1)):8, (b64e(B2)):8, (b64e(B3)):8, (b64e(B4)):8, (b64e(B5)):8,
(b64e(B6)):8, (b64e(B7)):8, (b64e(B8)):8>>
);
encode_binary(<<>>, A) ->
A;
encode_binary(<<B1:8>>, A) ->
<<A/bits, (b64e(B1 bsr 2)):8, (b64e((B1 band 3) bsl 4)):8>>;
encode_binary(<<B1:8, B2:8>>, A) ->
<<A/bits, (b64e(B1 bsr 2)):8, (b64e(((B1 band 3) bsl 4) bor (B2 bsr 4))):8,
(b64e((B2 band 15) bsl 2)):8>>;
encode_binary(<<B1:8, B2:8, B3:8, Ls/bits>>, A) ->
BB = (B1 bsl 16) bor (B2 bsl 8) bor B3,
encode_binary(<<B1:6, B2:6, B3:6, B4:6, Ls/bits>>, A) ->
encode_binary(
Ls,
<<A/bits, (b64e(BB bsr 18)):8, (b64e((BB bsr 12) band 63)):8, (b64e((BB bsr 6) band 63)):8,
(b64e(BB band 63)):8>>
).
<<A/bits, (b64e(B1)):8, (b64e(B2)):8, (b64e(B3)):8, (b64e(B4)):8>>
);
encode_binary(<<B1:6, B2:2>>, A) ->
E1 = b64e(B1),
E2 = b64e(B2 bsl 4),
<<A/bits, E1, E2, $=, $=>>;
encode_binary(<<B1:6, B2:6, B3:4>>, A) ->
E1 = b64e(B1),
E2 = b64e(B2),
E3 = b64e(B3 bsl 2),
<<A/bits, E1, E2, E3, $=>>.

%% mime_decode strips away all characters not Base64 before
%% converting, whereas decode crashes if an illegal character is found

-spec decode(Base64) -> Data when
Base64 :: base64_binary(),
Data :: binary().

decode(Bin) ->
decode_binary(Bin, <<>>).

decode_binary(<<C1:8, Cs/bits>>, A) ->
decode_binary(Cs, A, b64d(C1));
decode(Base64) when is_binary(Base64) ->
decode_binary(Base64, <<>>).

decode_binary(<<C1:8, C2:8, C3:8, C4:8, Cs/bits>>, A) ->
case {b64d(C1), b64d(C2), b64d(C3), b64d(C4)} of
{B1, B2, B3, B4} when
is_integer(B1),
is_integer(B2),
is_integer(B3),
is_integer(B4)
->
decode_binary(Cs, <<A/bits, B1:6, B2:6, B3:6, B4:6>>);
{B1, B2, B3, B4} ->
dec_bin(Cs, B1, B2, B3, B4, A)
end;
decode_binary(<<>>, A) ->
A.
A;
decode_binary(<<C1:8, Cs/bits>>, A) ->
B1 = b64d(C1),
decode_binary(Cs, A, B1).

dec_bin(Cs, B1, B2, B3, B4, A) ->
decode_binary(Cs, <<A/bits, B1:6, B2:6, B3:6, B4:6>>).

decode_binary(<<C2:8, Cs/bits>>, A, B1) ->
decode_binary(Cs, A, B1, b64d(C2)).
B2 = b64d(C2),
decode_binary(Cs, A, B1, B2).

decode_binary(<<C3:8, Cs/bits>>, A, B1, B2) ->
decode_binary(Cs, A, B1, B2, b64d(C3));
decode_binary(<<>>, A, B1, B2) ->
<<A/bits, B1:6, (B2 bsr 4):2>>.
B3 = b64d(C3),
decode_binary(Cs, A, B1, B2, B3);
decode_binary(<<_Cs/bits>>, _A, _B1, _B2) ->
missing_padding_error().

decode_binary(<<C4:8, Cs/bits>>, A, B1, B2, B3) ->
B4 = b64d(C4),
decode_binary(Cs, <<A/bits, B1:6, B2:6, B3:6, B4:6>>);
decode_binary(<<>>, A, B1, B2, B3) ->
<<A/bits, B1:6, B2:6, (B3 bsr 2):4>>.
decode_binary(<<>>, _A, _B1, _B2, _B3) ->
missing_padding_error().

%%%========================================================================
%%% Internal functions
%%% Error handling functions
%%%========================================================================

% always inlined for useful stacktraces when called in tail position
-compile({inline, missing_padding_error/0}).
missing_padding_error() ->
error(missing_padding, none, [{error_info, #{}}]).

%%%========================================================================

%% accessors
Expand Down Expand Up @@ -178,36 +219,3 @@ b64e(X) ->
$z
}
).

%%-----------------------------------------------------------------------
%% Code to generate decode table
%%-----------------------------------------------------------------------
%% code({value, {Pos, _Value}}) ->
%% Pos;
%% code(_) ->
%% bad.
%%
%% alphabet_pos([], _Pos, Acc) ->
%% lists:reverse(Acc);
%% alphabet_pos([Char | Rest], Pos, Acc) ->
%% alphabet_pos(Rest, Pos + 1, [{Pos, Char} | Acc]).
%%
%% decode_tuple(AlphabetPos) ->
%% Seq = lists:seq(1, 256),
%% decode_tuple(AlphabetPos, Seq, []).
%%
%%
%% decode_tuple(_AlphabetPos, [], Acc) ->
%% list_to_tuple(lists:reverse(Acc));
%% decode_tuple(AlphabetPos, [Char | Rest], Acc) ->
%% Value = code(lists:keysearch(Char, 2, AlphabetPos)),
%% decode_tuple(AlphabetPos, Rest, [Value | Acc]).
%%
%% decode_table() ->
%% Alphabet = [$-, $0, $1, $2, $3, $4, $5, $6, $7, $8, $9,
%% $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N,
%% $O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z,
%% $_, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n,
%% $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z],
%% AlphabetPos = alphabet_pos(Alphabet, 0, []),
%% decode_tuple(AlphabetPos).
Loading