summaryrefslogtreecommitdiff
path: root/lib/stdlib/src/base64.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src/base64.erl')
-rw-r--r--lib/stdlib/src/base64.erl743
1 files changed, 487 insertions, 256 deletions
diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl
index be4d9d42b4..62bcd0d24f 100644
--- a/lib/stdlib/src/base64.erl
+++ b/lib/stdlib/src/base64.erl
@@ -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.
@@ -21,12 +21,22 @@
-module(base64).
--export([encode/1, decode/1, mime_decode/1,
- encode_to_string/1, decode_to_string/1, mime_decode_to_string/1]).
-
+-export([encode/1, encode/2,
+ decode/1, decode/2,
+ mime_decode/1, mime_decode/2,
+ encode_to_string/1, encode_to_string/2,
+ decode_to_string/1, decode_to_string/2,
+ mime_decode_to_string/1, mime_decode_to_string/2,
+ format_error/2]).
%% RFC 4648: Base 64 Encoding alphabet
--type base64_alphabet() :: $A..$Z | $a..$z | $0..$9 | $+ | $/ | $=.
+-type base64_alphabet() :: $A..$Z | $a..$z | $0..$9 | $+ | $/ | $- | $_ | $=.
+
+%% Selector for the Base 64 alphabet, `standard' for RFC 4648
+%% Section 4, `urlsafe' for RFC 4648 Section 5.
+-type base64_mode() :: 'standard' | 'urlsafe'.
+
+-type options() :: #{padding => boolean(), mode => base64_mode()}.
%% The following type is a subtype of string() for return values
%% of encoding functions.
@@ -40,67 +50,126 @@
Data :: byte_string() | binary(),
Base64String :: base64_string().
-encode_to_string(Bin) when is_binary(Bin) ->
- encode_to_string(binary_to_list(Bin));
-encode_to_string(List) when is_list(List) ->
- encode_list_to_string(List).
+encode_to_string(Data) ->
+ encode_to_string(Data, #{}).
+
+-spec encode_to_string(Data, Options) -> Base64String when
+ Data :: byte_string() | binary(),
+ Options :: options(),
+ Base64String :: base64_string().
+
+encode_to_string(Bin, Options) when is_binary(Bin), is_map(Options) ->
+ encode_to_string(binary_to_list(Bin), Options);
+encode_to_string(List, Options) when is_list(List), is_map(Options) ->
+ encode_list_to_string(get_encoding_offset(Options), get_padding(Options), List).
-spec encode(Data) -> Base64 when
Data :: byte_string() | binary(),
Base64 :: base64_binary().
-encode(Bin) when is_binary(Bin) ->
- encode_binary(Bin, <<>>);
-encode(List) when is_list(List) ->
- encode_list(List, <<>>).
+encode(Data) ->
+ encode(Data, #{}).
+
+-spec encode(Data, Options) -> Base64 when
+ Data :: byte_string() | binary(),
+ Options :: options(),
+ Base64 :: base64_binary().
-encode_list_to_string([]) ->
+encode(Bin, Options) when is_binary(Bin), is_map(Options) ->
+ encode_binary(get_encoding_offset(Options), get_padding(Options), Bin, <<>>);
+encode(List, Options) when is_list(List) ->
+ encode_list(get_encoding_offset(Options), get_padding(Options), List, <<>>).
+
+encode_list_to_string(_ModeOffset, _Padding, []) ->
[];
-encode_list_to_string([B1]) ->
- [b64e(B1 bsr 2),
- b64e((B1 band 3) bsl 4), $=, $=];
-encode_list_to_string([B1,B2]) ->
- [b64e(B1 bsr 2),
- b64e(((B1 band 3) bsl 4) bor (B2 bsr 4)),
- b64e((B2 band 15) bsl 2), $=];
-encode_list_to_string([B1,B2,B3|Ls]) ->
+encode_list_to_string(ModeOffset, Padding, [B1]) ->
+ [b64e(B1 bsr 2, ModeOffset),
+ b64e((B1 band 3) bsl 4, ModeOffset) |
+ case Padding of
+ true -> "==";
+ false -> ""
+ end];
+encode_list_to_string(ModeOffset, Padding, [B1,B2]) ->
+ [b64e(B1 bsr 2, ModeOffset),
+ b64e(((B1 band 3) bsl 4) bor (B2 bsr 4), ModeOffset),
+ b64e((B2 band 15) bsl 2, ModeOffset) |
+ case Padding of
+ true -> "=";
+ false -> ""
+ end];
+encode_list_to_string(ModeOffset, Padding, [B1,B2,B3|Ls]) ->
BB = (B1 bsl 16) bor (B2 bsl 8) bor B3,
- [b64e(BB bsr 18),
- b64e((BB bsr 12) band 63),
- b64e((BB bsr 6) band 63),
- b64e(BB band 63) | encode_list_to_string(Ls)].
-
-encode_binary(<<>>, A) ->
+ [b64e(BB bsr 18, ModeOffset),
+ b64e((BB bsr 12) band 63, ModeOffset),
+ b64e((BB bsr 6) band 63, ModeOffset),
+ b64e(BB band 63, ModeOffset) | encode_list_to_string(ModeOffset, Padding, Ls)].
+
+encode_binary(ModeOffset, Padding, <<B1:6, B2:6, B3:6, B4:6, B5:6, B6:6, B7:6, B8:6, Ls/bits>>, A) ->
+ encode_binary(ModeOffset,
+ Padding,
+ Ls,
+ <<A/bits,
+ (b64e(B1, ModeOffset)):8,
+ (b64e(B2, ModeOffset)):8,
+ (b64e(B3, ModeOffset)):8,
+ (b64e(B4, ModeOffset)):8,
+ (b64e(B5, ModeOffset)):8,
+ (b64e(B6, ModeOffset)):8,
+ (b64e(B7, ModeOffset)):8,
+ (b64e(B8, ModeOffset)):8>>);
+encode_binary(_ModeOffset, _Padding, <<>>, A) ->
A;
-encode_binary(<<B1:8>>, A) ->
- <<A/bits,(b64e(B1 bsr 2)):8,(b64e((B1 band 3) bsl 4)):8,$=:8,$=: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, $=:8>>;
-encode_binary(<<B1:8, B2:8, B3:8, Ls/bits>>, A) ->
- BB = (B1 bsl 16) bor (B2 bsl 8) bor B3,
- 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>>).
+encode_binary(ModeOffset, Padding, <<B1:6, B2:6, B3:6, B4:6, Ls/bits>>, A) ->
+ encode_binary(ModeOffset,
+ Padding,
+ Ls,
+ <<A/bits,
+ (b64e(B1, ModeOffset)):8,
+ (b64e(B2, ModeOffset)):8,
+ (b64e(B3, ModeOffset)):8,
+ (b64e(B4, ModeOffset)):8>>);
+encode_binary(ModeOffset, Padding, <<B1:6, B2:2>>, A) ->
+ E1 = b64e(B1, ModeOffset),
+ E2 = b64e(B2 bsl 4, ModeOffset),
+ case Padding of
+ true -> <<A/bits,E1,E2,$=,$=>>;
+ _ -> <<A/bits,E1,E2>>
+ end;
+encode_binary(ModeOffset, Padding, <<B1:6, B2:6, B3:4>>, A) ->
+ E1 = b64e(B1, ModeOffset),
+ E2 = b64e(B2, ModeOffset),
+ E3 = b64e(B3 bsl 2, ModeOffset),
+ case Padding of
+ true -> <<A/bits,E1,E2,E3,$=>>;
+ _ -> <<A/bits,E1,E2,E3>>
+ end.
-encode_list([], A) ->
+encode_list(_ModeOffset, _Padding, [], A) ->
A;
-encode_list([B1], A) ->
- <<A/bits,(b64e(B1 bsr 2)):8,(b64e((B1 band 3) bsl 4)):8,$=:8,$=:8>>;
-encode_list([B1,B2], 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, $=:8>>;
-encode_list([B1,B2,B3|Ls], A) ->
+encode_list(ModeOffset, Padding, [B1], A) ->
+ E1 = b64e(B1 bsr 2, ModeOffset),
+ E2 = b64e((B1 band 3) bsl 4, ModeOffset),
+ case Padding of
+ true -> <<A/bits,E1,E2,$=,$=>>;
+ false -> <<A/bits,E1,E2>>
+ end;
+encode_list(ModeOffset, Padding, [B1,B2], A) ->
+ E1 = b64e(B1 bsr 2, ModeOffset),
+ E2 = b64e(((B1 band 3) bsl 4) bor (B2 bsr 4), ModeOffset),
+ E3 = b64e((B2 band 15) bsl 2, ModeOffset),
+ case Padding of
+ true -> <<A/bits,E1,E2,E3,$=>>;
+ false -> <<A/bits,E1,E2,E3>>
+ end;
+encode_list(ModeOffset, Padding, [B1,B2,B3|Ls], A) ->
BB = (B1 bsl 16) bor (B2 bsl 8) bor B3,
- encode_list(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>>).
+ encode_list(ModeOffset,
+ Padding,
+ Ls,
+ <<A/bits,(b64e(BB bsr 18, ModeOffset)):8,
+ (b64e((BB bsr 12) band 63, ModeOffset)):8,
+ (b64e((BB bsr 6) band 63, ModeOffset)):8,
+ (b64e(BB band 63, ModeOffset)):8>>).
%% mime_decode strips away all characters not Base64 before
%% converting, whereas decode crashes if an illegal character is found
@@ -109,19 +178,35 @@ encode_list([B1,B2,B3|Ls], A) ->
Base64 :: base64_string() | base64_binary(),
Data :: binary().
-decode(Bin) when is_binary(Bin) ->
- decode_binary(Bin, <<>>);
-decode(List) when is_list(List) ->
- decode_list(List, <<>>).
+decode(Base64) ->
+ decode(Base64, #{}).
+
+-spec decode(Base64, Options) -> Data when
+ Base64 :: base64_string() | base64_binary(),
+ Options :: options(),
+ Data :: binary().
+
+decode(Bin, Options) when is_binary(Bin) ->
+ decode_binary(get_decoding_offset(Options), get_padding(Options), Bin, <<>>);
+decode(List, Options) when is_list(List) ->
+ decode_list(get_decoding_offset(Options), get_padding(Options), List, <<>>).
-spec mime_decode(Base64) -> Data when
Base64 :: base64_string() | base64_binary(),
Data :: binary().
-mime_decode(Bin) when is_binary(Bin) ->
- mime_decode_binary(Bin, <<>>);
-mime_decode(List) when is_list(List) ->
- mime_decode_list(List, <<>>).
+mime_decode(Base64) ->
+ mime_decode(Base64, #{}).
+
+-spec mime_decode(Base64, Options) -> Data when
+ Base64 :: base64_string() | base64_binary(),
+ Options :: options(),
+ Data :: binary().
+
+mime_decode(Bin, Options) when is_binary(Bin) ->
+ mime_decode_binary(get_decoding_offset(Options), get_padding(Options), Bin, <<>>);
+mime_decode(List, Options) when is_list(List) ->
+ mime_decode_list(get_decoding_offset(Options), get_padding(Options), List, <<>>).
%% mime_decode_to_string strips away all characters not Base64 before
%% converting, whereas decode_to_string crashes if an illegal
@@ -131,324 +216,439 @@ mime_decode(List) when is_list(List) ->
Base64 :: base64_string() | base64_binary(),
DataString :: byte_string().
-decode_to_string(Bin) when is_binary(Bin) ->
- decode_to_string(binary_to_list(Bin));
-decode_to_string(List) when is_list(List) ->
- decode_list_to_string(List).
+decode_to_string(Base64) ->
+ decode_to_string(Base64, #{}).
+
+-spec decode_to_string(Base64, Options) -> DataString when
+ Base64 :: base64_string() | base64_binary(),
+ Options :: options(),
+ DataString :: byte_string().
+
+decode_to_string(Bin, Options) when is_binary(Bin) ->
+ decode_to_string(binary_to_list(Bin), Options);
+decode_to_string(List, Options) when is_list(List) ->
+ decode_list_to_string(get_decoding_offset(Options), get_padding(Options), List).
-spec mime_decode_to_string(Base64) -> DataString when
Base64 :: base64_string() | base64_binary(),
DataString :: byte_string().
-mime_decode_to_string(Bin) when is_binary(Bin) ->
- mime_decode_to_string(binary_to_list(Bin));
-mime_decode_to_string(List) when is_list(List) ->
- mime_decode_list_to_string(List).
+mime_decode_to_string(Base64) ->
+ mime_decode_to_string(Base64, #{}).
+
+-spec mime_decode_to_string(Base64, Options) -> DataString when
+ Base64 :: base64_string() | base64_binary(),
+ Options :: options(),
+ DataString :: byte_string().
+
+mime_decode_to_string(Bin, Options) when is_binary(Bin) ->
+ mime_decode_to_string(binary_to_list(Bin), Options);
+mime_decode_to_string(List, Options) when is_list(List) ->
+ mime_decode_list_to_string(get_decoding_offset(Options), get_padding(Options), List).
%% Skipping pad character if not at end of string. Also liberal about
%% excess padding and skipping of other illegal (non-base64 alphabet)
%% characters. See section 3.3 of RFC4648
-mime_decode_list([0 | Cs], A) ->
- mime_decode_list(Cs, A);
-mime_decode_list([C1 | Cs], A) ->
- case b64d(C1) of
- B1 when is_integer(B1) -> mime_decode_list(Cs, A, B1);
- _ -> mime_decode_list(Cs, A) % eq is padding
+mime_decode_list(ModeOffset, Padding, [C1 | Cs], A) ->
+ case b64d(C1, ModeOffset) of
+ B1 when is_integer(B1) -> mime_decode_list(ModeOffset, Padding, Cs, A, B1);
+ _ -> mime_decode_list(ModeOffset, Padding, Cs, A) % eq is padding
end;
-mime_decode_list([], A) ->
+mime_decode_list(_ModeOffset, _Padding, [], A) ->
A.
-mime_decode_list([0 | Cs], A, B1) ->
- mime_decode_list(Cs, A, B1);
-mime_decode_list([C2 | Cs], A, B1) ->
- case b64d(C2) of
+mime_decode_list(ModeOffset, Padding, [C2 | Cs], A, B1) ->
+ case b64d(C2, ModeOffset) of
B2 when is_integer(B2) ->
- mime_decode_list(Cs, A, B1, B2);
- _ -> mime_decode_list(Cs, A, B1) % eq is padding
+ mime_decode_list(ModeOffset, Padding, Cs, A, B1, B2);
+ _ -> mime_decode_list(ModeOffset, Padding, Cs, A, B1) % eq is padding
end.
-mime_decode_list([0 | Cs], A, B1, B2) ->
- mime_decode_list(Cs, A, B1, B2);
-mime_decode_list([C3 | Cs], A, B1, B2) ->
- case b64d(C3) of
+mime_decode_list(ModeOffset, Padding, [C3 | Cs], A, B1, B2) ->
+ case b64d(C3, ModeOffset) of
B3 when is_integer(B3) ->
- mime_decode_list(Cs, A, B1, B2, B3);
+ mime_decode_list(ModeOffset, Padding, Cs, A, B1, B2, B3);
eq=B3 ->
- mime_decode_list_after_eq(Cs, A, B1, B2, B3);
- _ -> mime_decode_list(Cs, A, B1, B2)
+ mime_decode_list_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ _ -> mime_decode_list(ModeOffset, Padding, Cs, A, B1, B2)
+ end;
+mime_decode_list(ModeOffset, Padding, [], A, B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_list_after_eq(ModeOffset, Padding, [], A, B1, B2, eq)
end.
-mime_decode_list([0 | Cs], A, B1, B2, B3) ->
- mime_decode_list(Cs, A, B1, B2, B3);
-mime_decode_list([C4 | Cs], A, B1, B2, B3) ->
- case b64d(C4) of
+mime_decode_list(ModeOffset, Padding, [C4 | Cs], A, B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
B4 when is_integer(B4) ->
- mime_decode_list(Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>);
+ mime_decode_list(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>);
eq ->
- mime_decode_list_after_eq(Cs, A, B1, B2, B3);
- _ -> mime_decode_list(Cs, A, B1, B2, B3)
+ mime_decode_list_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ _ -> mime_decode_list(ModeOffset, Padding, Cs, A, B1, B2, B3)
+ end;
+mime_decode_list(ModeOffset, Padding, [], A, B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_list_after_eq(ModeOffset, Padding, [], A, B1, B2, B3)
end.
-mime_decode_list_after_eq([0 | Cs], A, B1, B2, B3) ->
- mime_decode_list_after_eq(Cs, A, B1, B2, B3);
-mime_decode_list_after_eq([C | Cs], A, B1, B2, B3) ->
- case b64d(C) of
+mime_decode_list_after_eq(ModeOffset, Padding, [C | Cs], A, B1, B2, B3) ->
+ case b64d(C, ModeOffset) of
B when is_integer(B) ->
%% More valid data, skip the eq as invalid
case B3 of
- eq -> mime_decode_list(Cs, A, B1, B2, B);
- _ -> mime_decode_list(Cs, <<A/bits,B1:6,B2:6,B3:6,B:6>>)
+ eq -> mime_decode_list(ModeOffset, Padding, Cs, A, B1, B2, B);
+ _ -> mime_decode_list(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B:6>>)
end;
- _ -> mime_decode_list_after_eq(Cs, A, B1, B2, B3)
+ _ -> mime_decode_list_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3)
end;
-mime_decode_list_after_eq([], A, B1, B2, eq) ->
+mime_decode_list_after_eq(_ModeOffset, _Padding, [], A, B1, B2, eq) ->
<<A/bits,B1:6,(B2 bsr 4):2>>;
-mime_decode_list_after_eq([], A, B1, B2, B3) ->
+mime_decode_list_after_eq(_ModeOffset, _Padding, [], A, B1, B2, B3) ->
<<A/bits,B1:6,B2:6,(B3 bsr 2):4>>.
-mime_decode_binary(<<0:8, Cs/bits>>, A) ->
- mime_decode_binary(Cs, A);
-mime_decode_binary(<<C1:8, Cs/bits>>, A) ->
- case b64d(C1) of
- B1 when is_integer(B1) -> mime_decode_binary(Cs, A, B1);
- _ -> mime_decode_binary(Cs, A) % eq is padding
+mime_decode_binary(ModeOffset, Padding, <<C1:8, Cs/bits>>, A) ->
+ case b64d(C1, ModeOffset) of
+ B1 when is_integer(B1) -> mime_decode_binary(ModeOffset, Padding, Cs, A, B1);
+ _ -> mime_decode_binary(ModeOffset, Padding, Cs, A) % eq is padding
end;
-mime_decode_binary(<<>>, A) ->
+mime_decode_binary(_ModeOffset, _Padding, <<>>, A) ->
A.
-mime_decode_binary(<<0:8, Cs/bits>>, A, B1) ->
- mime_decode_binary(Cs, A, B1);
-mime_decode_binary(<<C2:8, Cs/bits>>, A, B1) ->
- case b64d(C2) of
+mime_decode_binary(ModeOffset, Padding, <<C2:8, Cs/bits>>, A, B1) ->
+ case b64d(C2, ModeOffset) of
B2 when is_integer(B2) ->
- mime_decode_binary(Cs, A, B1, B2);
- _ -> mime_decode_binary(Cs, A, B1) % eq is padding
+ mime_decode_binary(ModeOffset, Padding, Cs, A, B1, B2);
+ _ -> mime_decode_binary(ModeOffset, Padding, Cs, A, B1) % eq is padding
end.
-mime_decode_binary(<<0:8, Cs/bits>>, A, B1, B2) ->
- mime_decode_binary(Cs, A, B1, B2);
-mime_decode_binary(<<C3:8, Cs/bits>>, A, B1, B2) ->
- case b64d(C3) of
+mime_decode_binary(ModeOffset, Padding, <<C3:8, Cs/bits>>, A, B1, B2) ->
+ case b64d(C3, ModeOffset) of
B3 when is_integer(B3) ->
- mime_decode_binary(Cs, A, B1, B2, B3);
+ mime_decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3);
eq=B3 ->
- mime_decode_binary_after_eq(Cs, A, B1, B2, B3);
- _ -> mime_decode_binary(Cs, A, B1, B2)
+ mime_decode_binary_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ _ -> mime_decode_binary(ModeOffset, Padding, Cs, A, B1, B2)
+ end;
+mime_decode_binary(ModeOffset, Padding, <<Cs/bits>>, A, B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_binary_after_eq(ModeOffset, Padding, Cs, A, B1, B2, eq)
end.
-mime_decode_binary(<<0:8, Cs/bits>>, A, B1, B2, B3) ->
- mime_decode_binary(Cs, A, B1, B2, B3);
-mime_decode_binary(<<C4:8, Cs/bits>>, A, B1, B2, B3) ->
- case b64d(C4) of
+mime_decode_binary(ModeOffset, Padding, <<C4:8, Cs/bits>>, A, B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
B4 when is_integer(B4) ->
- mime_decode_binary(Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>);
+ mime_decode_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>);
eq ->
- mime_decode_binary_after_eq(Cs, A, B1, B2, B3);
- _ -> mime_decode_binary(Cs, A, B1, B2, B3)
+ mime_decode_binary_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ _ -> mime_decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3)
+ end;
+mime_decode_binary(ModeOffset, Padding, <<Cs/bits>>, A, B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_binary_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3)
end.
-mime_decode_binary_after_eq(<<0:8, Cs/bits>>, A, B1, B2, B3) ->
- mime_decode_binary_after_eq(Cs, A, B1, B2, B3);
-mime_decode_binary_after_eq(<<C:8, Cs/bits>>, A, B1, B2, B3) ->
- case b64d(C) of
+mime_decode_binary_after_eq(ModeOffset, Padding, <<C:8, Cs/bits>>, A, B1, B2, B3) ->
+ case b64d(C, ModeOffset) of
B when is_integer(B) ->
%% More valid data, skip the eq as invalid
case B3 of
- eq -> mime_decode_binary(Cs, A, B1, B2, B);
- _ -> mime_decode_binary(Cs, <<A/bits,B1:6,B2:6,B3:6,B:6>>)
+ eq -> mime_decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B);
+ _ -> mime_decode_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B:6>>)
end;
- _ -> mime_decode_binary_after_eq(Cs, A, B1, B2, B3)
+ _ -> mime_decode_binary_after_eq(ModeOffset, Padding, Cs, A, B1, B2, B3)
end;
-mime_decode_binary_after_eq(<<>>, A, B1, B2, eq) ->
+mime_decode_binary_after_eq(_ModeOffset, _Padding, <<>>, A, B1, B2, eq) ->
<<A/bits,B1:6,(B2 bsr 4):2>>;
-mime_decode_binary_after_eq(<<>>, A, B1, B2, B3) ->
+mime_decode_binary_after_eq(_ModeOffset, _Padding, <<>>, A, B1, B2, B3) ->
<<A/bits,B1:6,B2:6,(B3 bsr 2):4>>.
-mime_decode_list_to_string([0 | Cs]) ->
- mime_decode_list_to_string(Cs);
-mime_decode_list_to_string([C1 | Cs]) ->
- case b64d(C1) of
- B1 when is_integer(B1) -> mime_decode_list_to_string(Cs, B1);
- _ -> mime_decode_list_to_string(Cs) % eq is padding
+mime_decode_list_to_string(ModeOffset, Padding, [C1 | Cs]) ->
+ case b64d(C1, ModeOffset) of
+ B1 when is_integer(B1) -> mime_decode_list_to_string(ModeOffset, Padding, Cs, B1);
+ _ -> mime_decode_list_to_string(ModeOffset, Padding, Cs) % eq is padding
end;
-mime_decode_list_to_string([]) ->
+mime_decode_list_to_string(_ModeOffset, _Padding, []) ->
[].
-mime_decode_list_to_string([0 | Cs], B1) ->
- mime_decode_list_to_string(Cs, B1);
-mime_decode_list_to_string([C2 | Cs], B1) ->
- case b64d(C2) of
+mime_decode_list_to_string(ModeOffset, Padding, [C2 | Cs], B1) ->
+ case b64d(C2, ModeOffset) of
B2 when is_integer(B2) ->
- mime_decode_list_to_string(Cs, B1, B2);
- _ -> mime_decode_list_to_string(Cs, B1) % eq is padding
+ mime_decode_list_to_string(ModeOffset, Padding, Cs, B1, B2);
+ _ -> mime_decode_list_to_string(ModeOffset, Padding, Cs, B1) % eq is padding
end.
-mime_decode_list_to_string([0 | Cs], B1, B2) ->
- mime_decode_list_to_string(Cs, B1, B2);
-mime_decode_list_to_string([C3 | Cs], B1, B2) ->
- case b64d(C3) of
+mime_decode_list_to_string(ModeOffset, Padding, [C3 | Cs], B1, B2) ->
+ case b64d(C3, ModeOffset) of
B3 when is_integer(B3) ->
- mime_decode_list_to_string(Cs, B1, B2, B3);
- eq=B3 -> mime_decode_list_to_string_after_eq(Cs, B1, B2, B3);
- _ -> mime_decode_list_to_string(Cs, B1, B2)
+ mime_decode_list_to_string(ModeOffset, Padding, Cs, B1, B2, B3);
+ eq=B3 -> mime_decode_list_to_string_after_eq(ModeOffset, Padding, Cs, B1, B2, B3);
+ _ -> mime_decode_list_to_string(ModeOffset, Padding, Cs, B1, B2)
+ end;
+mime_decode_list_to_string(ModeOffset, Padding, [], B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_list_to_string_after_eq(ModeOffset, Padding, [], B1, B2, eq)
end.
-mime_decode_list_to_string([0 | Cs], B1, B2, B3) ->
- mime_decode_list_to_string(Cs, B1, B2, B3);
-mime_decode_list_to_string([C4 | Cs], B1, B2, B3) ->
- case b64d(C4) of
+mime_decode_list_to_string(ModeOffset, Padding, [C4 | Cs], B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
B4 when is_integer(B4) ->
Bits4x6 = (B1 bsl 18) bor (B2 bsl 12) bor (B3 bsl 6) bor B4,
Octet1 = Bits4x6 bsr 16,
Octet2 = (Bits4x6 bsr 8) band 16#ff,
Octet3 = Bits4x6 band 16#ff,
- [Octet1, Octet2, Octet3 | mime_decode_list_to_string(Cs)];
+ [Octet1, Octet2, Octet3 | mime_decode_list_to_string(ModeOffset, Padding, Cs)];
eq ->
- mime_decode_list_to_string_after_eq(Cs, B1, B2, B3);
- _ -> mime_decode_list_to_string(Cs, B1, B2, B3)
+ mime_decode_list_to_string_after_eq(ModeOffset, Padding, Cs, B1, B2, B3);
+ _ -> mime_decode_list_to_string(ModeOffset, Padding, Cs, B1, B2, B3)
+ end;
+mime_decode_list_to_string(ModeOffset, Padding, [], B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> mime_decode_list_to_string_after_eq(ModeOffset, Padding, [], B1, B2, B3)
end.
-mime_decode_list_to_string_after_eq([0 | Cs], B1, B2, B3) ->
- mime_decode_list_to_string_after_eq(Cs, B1, B2, B3);
-mime_decode_list_to_string_after_eq([C | Cs], B1, B2, B3) ->
- case b64d(C) of
+mime_decode_list_to_string_after_eq(ModeOffset, Padding, [C | Cs], B1, B2, B3) ->
+ case b64d(C, ModeOffset) of
B when is_integer(B) ->
%% More valid data, skip the eq as invalid
case B3 of
- eq -> mime_decode_list_to_string(Cs, B1, B2, B);
+ eq -> mime_decode_list_to_string(ModeOffset, Padding, Cs, B1, B2, B);
_ ->
Bits4x6 = (B1 bsl 18) bor (B2 bsl 12) bor (B3 bsl 6) bor B,
Octet1 = Bits4x6 bsr 16,
Octet2 = (Bits4x6 bsr 8) band 16#ff,
Octet3 = Bits4x6 band 16#ff,
- [Octet1, Octet2, Octet3 | mime_decode_list_to_string(Cs)]
+ [Octet1, Octet2, Octet3 | mime_decode_list_to_string(ModeOffset, Padding, Cs)]
end;
- _ -> mime_decode_list_to_string_after_eq(Cs, B1, B2, B3)
+ _ -> mime_decode_list_to_string_after_eq(ModeOffset, Padding, Cs, B1, B2, B3)
end;
-mime_decode_list_to_string_after_eq([], B1, B2, eq) ->
+mime_decode_list_to_string_after_eq(_ModeOffset, _Padding, [], B1, B2, eq) ->
binary_to_list(<<B1:6,(B2 bsr 4):2>>);
-mime_decode_list_to_string_after_eq([], B1, B2, B3) ->
+mime_decode_list_to_string_after_eq(_ModeOffset, _Padding, [], B1, B2, B3) ->
binary_to_list(<<B1:6,B2:6,(B3 bsr 2):4>>).
-decode_list([C1 | Cs], A) ->
- case b64d(C1) of
- ws -> decode_list(Cs, A);
- B1 -> decode_list(Cs, A, B1)
+decode_list(ModeOffset, Padding, [C1 | Cs], A) ->
+ case b64d(C1, ModeOffset) of
+ ws -> decode_list(ModeOffset, Padding, Cs, A);
+ B1 -> decode_list(ModeOffset, Padding, Cs, A, B1)
end;
-decode_list([], A) ->
+decode_list(_ModeOffset, _Padding, [], A) ->
A.
-decode_list([C2 | Cs], A, B1) ->
- case b64d(C2) of
- ws -> decode_list(Cs, A, B1);
- B2 -> decode_list(Cs, A, B1, B2)
+decode_list(ModeOffset, Padding, [C2 | Cs], A, B1) ->
+ case b64d(C2, ModeOffset) of
+ ws -> decode_list(ModeOffset, Padding, Cs, A, B1);
+ B2 -> decode_list(ModeOffset, Padding, Cs, A, B1, B2)
end.
-decode_list([C3 | Cs], A, B1, B2) ->
- case b64d(C3) of
- ws -> decode_list(Cs, A, B1, B2);
- B3 -> decode_list(Cs, A, B1, B2, B3)
+decode_list(ModeOffset, Padding, [C3 | Cs], A, B1, B2) ->
+ case b64d(C3, ModeOffset) of
+ ws -> decode_list(ModeOffset, Padding, Cs, A, B1, B2);
+ B3 -> decode_list(ModeOffset, Padding, Cs, A, B1, B2, B3)
+ end;
+decode_list(ModeOffset, Padding, [], A, B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> decode_list(ModeOffset, Padding, [], A, B1, B2, eq)
end.
-decode_list([C4 | Cs], A, B1, B2, B3) ->
- case b64d(C4) of
- ws -> decode_list(Cs, A, B1, B2, B3);
- eq when B3 =:= eq -> only_ws(Cs, <<A/bits,B1:6,(B2 bsr 4):2>>);
- eq -> only_ws(Cs, <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>);
- B4 -> decode_list(Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>)
+decode_list(ModeOffset, Padding, [C4 | Cs], A, B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
+ ws -> decode_list(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ eq when B3 =:= eq -> only_ws(ModeOffset, Padding, Cs, <<A/bits,B1:6,(B2 bsr 4):2>>);
+ eq -> only_ws(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>);
+ B4 -> decode_list(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>)
+ end;
+decode_list(_ModeOffset, Padding, [], A, B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false when B3 == eq -> <<A/bits,B1:6,(B2 bsr 4):2>>;
+ false -> <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>
end.
-decode_binary(<<C1:8, Cs/bits>>, A) ->
- case b64d(C1) of
- ws -> decode_binary(Cs, A);
- B1 -> decode_binary(Cs, A, B1)
+decode_binary(ModeOffset, Padding, <<C1:8, C2:8, C3:8, C4:8, Cs/bits>>, A) ->
+ case {b64d(C1, ModeOffset), b64d(C2, ModeOffset), b64d(C3, ModeOffset), b64d(C4, ModeOffset)} of
+ {B1, B2, B3, B4} when is_integer(B1), is_integer(B2),
+ is_integer(B3), is_integer(B4) ->
+ decode_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>);
+ {B1, B2, B3, B4} ->
+ dec_bin(ModeOffset, Padding, Cs, B1, B2, B3, B4, A)
end;
-decode_binary(<<>>, A) ->
- A.
+decode_binary(_ModeOffset, _Padding, <<>>, A) ->
+ A;
+decode_binary(ModeOffset, Padding, <<C1:8, Cs/bits>>, A) ->
+ case b64d(C1, ModeOffset) of
+ ws -> decode_binary(ModeOffset, Padding, Cs, A);
+ B1 -> decode_binary(ModeOffset, Padding, Cs, A, B1)
+ end.
-decode_binary(<<C2:8, Cs/bits>>, A, B1) ->
- case b64d(C2) of
- ws -> decode_binary(Cs, A, B1);
- B2 -> decode_binary(Cs, A, B1, B2)
+dec_bin(ModeOffset, Padding, Cs, ws, B2, B3, B4, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B2, B3, B4, A);
+dec_bin(ModeOffset, Padding, Cs, B1, ws, B3, B4, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B1, B3, B4, A);
+dec_bin(ModeOffset, Padding, Cs, B1, B2, ws, B4, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B1, B2, B4, A);
+dec_bin(ModeOffset, Padding, Cs, B1, B2, B3, B4, A) ->
+ case B4 of
+ ws -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ eq when B3 =:= eq -> only_ws_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,(B2 bsr 4):2>>);
+ eq -> only_ws_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>);
+ B4 -> decode_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>)
end.
-decode_binary(<<C3:8, Cs/bits>>, A, B1, B2) ->
- case b64d(C3) of
- ws -> decode_binary(Cs, A, B1, B2);
- B3 -> decode_binary(Cs, A, B1, B2, B3)
+dec_bin(ModeOffset, Padding, Cs, ws, B2, B3, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B2, B3, A);
+dec_bin(ModeOffset, Padding, Cs, B1, ws, B3, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B1, B3, A);
+dec_bin(ModeOffset, Padding, Cs, B1, B2, ws, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B1, B2, A);
+dec_bin(ModeOffset, Padding, Cs, B1, B2, B3, A) ->
+ decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3).
+
+dec_bin(ModeOffset, Padding, Cs, ws, B2, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B2, A);
+dec_bin(ModeOffset, Padding, Cs, B1, ws, A) ->
+ dec_bin(ModeOffset, Padding, Cs, B1, A);
+dec_bin(ModeOffset, Padding, Cs, B1, B2, A) ->
+ decode_binary(ModeOffset, Padding, Cs, A, B1, B2).
+
+dec_bin(ModeOffset, Padding, Cs, ws, A) ->
+ decode_binary(ModeOffset, Padding, Cs, A);
+dec_bin(ModeOffset, Padding, Cs, B1, A) ->
+ decode_binary(ModeOffset, Padding,Cs, A, B1).
+
+decode_binary(ModeOffset, Padding, <<C2:8, Cs/bits>>, A, B1) ->
+ case b64d(C2, ModeOffset) of
+ ws -> decode_binary(ModeOffset, Padding, Cs, A, B1);
+ B2 -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2)
end.
-decode_binary(<<C4:8, Cs/bits>>, A, B1, B2, B3) ->
- case b64d(C4) of
- ws -> decode_binary(Cs, A, B1, B2, B3);
- eq when B3 =:= eq -> only_ws_binary(Cs, <<A/bits,B1:6,(B2 bsr 4):2>>);
- eq -> only_ws_binary(Cs, <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>);
- B4 -> decode_binary(Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>)
+decode_binary(ModeOffset, Padding, <<C3:8, Cs/bits>>, A, B1, B2) ->
+ case b64d(C3, ModeOffset) of
+ ws -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2);
+ B3 -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3)
+ end;
+decode_binary(ModeOffset, Padding, <<Cs/bits>>, A, B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2, eq)
+ end.
+
+decode_binary(ModeOffset, Padding, <<C4:8, Cs/bits>>, A, B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
+ ws -> decode_binary(ModeOffset, Padding, Cs, A, B1, B2, B3);
+ eq when B3 =:= eq -> only_ws_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,(B2 bsr 4):2>>);
+ eq -> only_ws_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>);
+ B4 -> decode_binary(ModeOffset, Padding, Cs, <<A/bits,B1:6,B2:6,B3:6,B4:6>>)
+ end;
+decode_binary(_ModeOffset, Padding, <<>>, A, B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false when B3 =:= eq -> <<A/bits,B1:6,(B2 bsr 4):2>>;
+ false -> <<A/bits,B1:6,B2:6,(B3 bsr 2):4>>
end.
-only_ws_binary(<<>>, A) ->
+only_ws_binary(_ModeOffset, _Padding, <<>>, A) ->
A;
-only_ws_binary(<<C:8, Cs/bits>>, A) ->
- case b64d(C) of
- ws -> only_ws_binary(Cs, A)
+only_ws_binary(ModeOffset, Padding, <<C:8, Cs/bits>>, A) ->
+ case b64d(C, ModeOffset) of
+ ws -> only_ws_binary(ModeOffset, Padding, Cs, A)
end.
-decode_list_to_string([C1 | Cs]) ->
- case b64d(C1) of
- ws -> decode_list_to_string(Cs);
- B1 -> decode_list_to_string(Cs, B1)
+decode_list_to_string(ModeOffset, Padding, [C1 | Cs]) ->
+ case b64d(C1, ModeOffset) of
+ ws -> decode_list_to_string(ModeOffset, Padding, Cs);
+ B1 -> decode_list_to_string(ModeOffset, Padding, Cs, B1)
end;
-decode_list_to_string([]) ->
+decode_list_to_string(_ModeOffset, _Padding, []) ->
[].
-decode_list_to_string([C2 | Cs], B1) ->
- case b64d(C2) of
- ws -> decode_list_to_string(Cs, B1);
- B2 -> decode_list_to_string(Cs, B1, B2)
+decode_list_to_string(ModeOffset, Padding, [C2 | Cs], B1) ->
+ case b64d(C2, ModeOffset) of
+ ws -> decode_list_to_string(ModeOffset, Padding, Cs, B1);
+ B2 -> decode_list_to_string(ModeOffset, Padding, Cs, B1, B2)
end.
-decode_list_to_string([C3 | Cs], B1, B2) ->
- case b64d(C3) of
- ws -> decode_list_to_string(Cs, B1, B2);
- B3 -> decode_list_to_string(Cs, B1, B2, B3)
+decode_list_to_string(ModeOffset, Padding, [C3 | Cs], B1, B2) ->
+ case b64d(C3, ModeOffset) of
+ ws -> decode_list_to_string(ModeOffset, Padding, Cs, B1, B2);
+ B3 -> decode_list_to_string(ModeOffset, Padding, Cs, B1, B2, B3)
+ end;
+decode_list_to_string(ModeOffset, Padding, [], B1, B2) ->
+ case Padding of
+ true -> missing_padding_error();
+ false -> decode_list_to_string(ModeOffset, Padding, [], B1, B2, eq)
end.
-decode_list_to_string([C4 | Cs], B1, B2, B3) ->
- case b64d(C4) of
+decode_list_to_string(ModeOffset, Padding, [C4 | Cs], B1, B2, B3) ->
+ case b64d(C4, ModeOffset) of
ws ->
- decode_list_to_string(Cs, B1, B2, B3);
+ decode_list_to_string(ModeOffset, Padding, Cs, B1, B2, B3);
eq when B3 =:= eq ->
- only_ws(Cs, binary_to_list(<<B1:6,(B2 bsr 4):2>>));
+ only_ws(ModeOffset, Padding, Cs, binary_to_list(<<B1:6,(B2 bsr 4):2>>));
eq ->
- only_ws(Cs, binary_to_list(<<B1:6,B2:6,(B3 bsr 2):4>>));
+ only_ws(ModeOffset, Padding, Cs, binary_to_list(<<B1:6,B2:6,(B3 bsr 2):4>>));
B4 ->
Bits4x6 = (B1 bsl 18) bor (B2 bsl 12) bor (B3 bsl 6) bor B4,
Octet1 = Bits4x6 bsr 16,
Octet2 = (Bits4x6 bsr 8) band 16#ff,
Octet3 = Bits4x6 band 16#ff,
- [Octet1, Octet2, Octet3 | decode_list_to_string(Cs)]
+ [Octet1, Octet2, Octet3 | decode_list_to_string(ModeOffset, Padding, Cs)]
+ end;
+decode_list_to_string(_ModeOffset, Padding, [], B1, B2, B3) ->
+ case Padding of
+ true -> missing_padding_error();
+ false when B3 =:= eq -> binary_to_list(<<B1:6,(B2 bsr 4):2>>);
+ false -> binary_to_list(<<B1:6,B2:6,(B3 bsr 2):4>>)
end.
-only_ws([], A) ->
+only_ws(_ModeOffset, _Padding, [], A) ->
A;
-only_ws([C | Cs], A) ->
- case b64d(C) of
- ws -> only_ws(Cs, A)
+only_ws(ModeOffset, Padding, [C | Cs], A) ->
+ case b64d(C, ModeOffset) of
+ ws -> only_ws(ModeOffset, Padding, Cs, A)
end.
%%%========================================================================
+%%% 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, #{}}]).
+
+format_error(missing_padding, _) ->
+ #{general => "data to decode is missing final = padding characters, if this is intended, use the `padding => false` option"};
+format_error(_, _) ->
+ #{}.
+
+%%%========================================================================
%%% Internal functions
%%%========================================================================
-%% accessors
--compile({inline, [{b64d, 1}]}).
-%% One-based decode map.
-b64d(X) ->
- element(X,
- {bad,bad,bad,bad,bad,bad,bad,bad,ws,ws,bad,bad,ws,bad,bad, %1-15
+%% accessors
+
+get_padding(#{padding := Bool}) when is_boolean(Bool) -> Bool;
+get_padding(#{}) -> true.
+
+get_decoding_offset(#{mode := standard}) -> 1;
+get_decoding_offset(#{mode := urlsafe}) -> 257;
+get_decoding_offset(#{}) -> 1.
+
+-compile({inline, [{b64d, 2}]}).
+b64d(X, Off) ->
+ element(X + Off,
+ {
+ %% standard base64 alphabet (RFC 4648 Section 4)
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,ws,ws,bad,bad,ws,bad,bad, %0-15
bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad, %16-31
ws,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,62,bad,bad,bad,63, %32-47
- 52,53,54,55,56,57,58,59,60,61,bad,bad,bad,eq,bad,bad, %48-63
+ 52,53,54,55,56,57,58,59,60,61,bad,bad,bad,eq,bad,bad, %48-61
bad,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,bad,bad,bad,bad,bad,
bad,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
@@ -460,13 +660,44 @@ b64d(X) ->
bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+
+ %% alternative base64url alphabet (RFC 4648 Section 5)
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,ws,ws,bad,bad,ws,bad,bad, %0-15
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad, %16-31
+ ws,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,62,bad,bad, %32-47
+ 52,53,54,55,56,57,58,59,60,61,bad,bad,bad,eq,bad,bad, %48-61
+ bad,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,bad,bad,bad,bad,63,
+ bad,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
+ bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,
bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad,bad}).
--compile({inline, [{b64e, 1}]}).
-b64e(X) ->
- element(X+1,
- {$A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N,
+get_encoding_offset(#{mode := standard}) -> 1;
+get_encoding_offset(#{mode := urlsafe}) -> 65;
+get_encoding_offset(#{}) -> 1.
+
+-compile({inline, [{b64e, 2}]}).
+b64e(X, Off) ->
+ element(X + Off,
+ {
+ %% standard base64 alphabet (RFC 4648 Section 4)
+ $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,
+ $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $+, $/,
+
+ %% alternative base64url alphabet (RFC 4648 Section 5)
+ $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,
- $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $+, $/}).
+ $0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $-, $_}).