1288 lines
37 KiB
Elixir
1288 lines
37 KiB
Elixir
defmodule Base do
|
|
import Bitwise
|
|
|
|
@moduledoc """
|
|
This module provides data encoding and decoding functions
|
|
according to [RFC 4648](https://tools.ietf.org/html/rfc4648).
|
|
|
|
This document defines the commonly used base 16, base 32, and base
|
|
64 encoding schemes.
|
|
|
|
## Base 16 alphabet
|
|
|
|
| Value | Encoding | Value | Encoding | Value | Encoding | Value | Encoding |
|
|
|------:|:---------|------:|:---------|------:|:---------|------:|:---------|
|
|
| 0 | 0 | 4 | 4 | 8 | 8 | 12 | C |
|
|
| 1 | 1 | 5 | 5 | 9 | 9 | 13 | D |
|
|
| 2 | 2 | 6 | 6 | 10 | A | 14 | E |
|
|
| 3 | 3 | 7 | 7 | 11 | B | 15 | F |
|
|
|
|
## Base 32 alphabet
|
|
|
|
| Value | Encoding | Value | Encoding | Value | Encoding | Value | Encoding |
|
|
|------:|:---------|------:|:---------|------:|:---------|------:|:---------|
|
|
| 0 | A | 9 | J | 18 | S | 27 | 3 |
|
|
| 1 | B | 10 | K | 19 | T | 28 | 4 |
|
|
| 2 | C | 11 | L | 20 | U | 29 | 5 |
|
|
| 3 | D | 12 | M | 21 | V | 30 | 6 |
|
|
| 4 | E | 13 | N | 22 | W | 31 | 7 |
|
|
| 5 | F | 14 | O | 23 | X | | |
|
|
| 6 | G | 15 | P | 24 | Y | (pad) | = |
|
|
| 7 | H | 16 | Q | 25 | Z | | |
|
|
| 8 | I | 17 | R | 26 | 2 | | |
|
|
|
|
|
|
## Base 32 (extended hex) alphabet
|
|
|
|
| Value | Encoding | Value | Encoding | Value | Encoding | Value | Encoding |
|
|
|------:|:---------|------:|:---------|------:|:---------|------:|:---------|
|
|
| 0 | 0 | 9 | 9 | 18 | I | 27 | R |
|
|
| 1 | 1 | 10 | A | 19 | J | 28 | S |
|
|
| 2 | 2 | 11 | B | 20 | K | 29 | T |
|
|
| 3 | 3 | 12 | C | 21 | L | 30 | U |
|
|
| 4 | 4 | 13 | D | 22 | M | 31 | V |
|
|
| 5 | 5 | 14 | E | 23 | N | | |
|
|
| 6 | 6 | 15 | F | 24 | O | (pad) | = |
|
|
| 7 | 7 | 16 | G | 25 | P | | |
|
|
| 8 | 8 | 17 | H | 26 | Q | | |
|
|
|
|
## Base 64 alphabet
|
|
|
|
| Value | Encoding | Value | Encoding | Value | Encoding | Value | Encoding |
|
|
|------:|:----------|------:|:---------|------:|:---------|------:|:---------|
|
|
| 0 | A | 17 | R | 34 | i | 51 | z |
|
|
| 1 | B | 18 | S | 35 | j | 52 | 0 |
|
|
| 2 | C | 19 | T | 36 | k | 53 | 1 |
|
|
| 3 | D | 20 | U | 37 | l | 54 | 2 |
|
|
| 4 | E | 21 | V | 38 | m | 55 | 3 |
|
|
| 5 | F | 22 | W | 39 | n | 56 | 4 |
|
|
| 6 | G | 23 | X | 40 | o | 57 | 5 |
|
|
| 7 | H | 24 | Y | 41 | p | 58 | 6 |
|
|
| 8 | I | 25 | Z | 42 | q | 59 | 7 |
|
|
| 9 | J | 26 | a | 43 | r | 60 | 8 |
|
|
| 10 | K | 27 | b | 44 | s | 61 | 9 |
|
|
| 11 | L | 28 | c | 45 | t | 62 | + |
|
|
| 12 | M | 29 | d | 46 | u | 63 | / |
|
|
| 13 | N | 30 | e | 47 | v | | |
|
|
| 14 | O | 31 | f | 48 | w | (pad) | = |
|
|
| 15 | P | 32 | g | 49 | x | | |
|
|
| 16 | Q | 33 | h | 50 | y | | |
|
|
|
|
## Base 64 (URL and filename safe) alphabet
|
|
|
|
| Value | Encoding | Value | Encoding | Value | Encoding | Value | Encoding |
|
|
|------:|:---------|------:|:---------|------:|:---------|------:|:---------|
|
|
| 0 | A | 17 | R | 34 | i | 51 | z |
|
|
| 1 | B | 18 | S | 35 | j | 52 | 0 |
|
|
| 2 | C | 19 | T | 36 | k | 53 | 1 |
|
|
| 3 | D | 20 | U | 37 | l | 54 | 2 |
|
|
| 4 | E | 21 | V | 38 | m | 55 | 3 |
|
|
| 5 | F | 22 | W | 39 | n | 56 | 4 |
|
|
| 6 | G | 23 | X | 40 | o | 57 | 5 |
|
|
| 7 | H | 24 | Y | 41 | p | 58 | 6 |
|
|
| 8 | I | 25 | Z | 42 | q | 59 | 7 |
|
|
| 9 | J | 26 | a | 43 | r | 60 | 8 |
|
|
| 10 | K | 27 | b | 44 | s | 61 | 9 |
|
|
| 11 | L | 28 | c | 45 | t | 62 | - |
|
|
| 12 | M | 29 | d | 46 | u | 63 | _ |
|
|
| 13 | N | 30 | e | 47 | v | | |
|
|
| 14 | O | 31 | f | 48 | w | (pad) | = |
|
|
| 15 | P | 32 | g | 49 | x | | |
|
|
| 16 | Q | 33 | h | 50 | y | | |
|
|
|
|
"""
|
|
|
|
b16_alphabet = '0123456789ABCDEF'
|
|
b64_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
b64url_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
|
|
b32_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
|
|
b32hex_alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUV'
|
|
|
|
defmacrop encode_pair(alphabet, case, value) do
|
|
quote do
|
|
case unquote(value) do
|
|
unquote(encode_pair_clauses(alphabet, case))
|
|
end
|
|
end
|
|
end
|
|
|
|
defp encode_pair_clauses(alphabet, case) when case in [:sensitive, :upper] do
|
|
shift = shift(alphabet)
|
|
|
|
alphabet
|
|
|> Enum.with_index()
|
|
|> encode_clauses(shift)
|
|
end
|
|
|
|
defp encode_pair_clauses(alphabet, :lower) do
|
|
shift = shift(alphabet)
|
|
|
|
alphabet
|
|
|> Stream.map(fn c -> if c in ?A..?Z, do: c - ?A + ?a, else: c end)
|
|
|> Enum.with_index()
|
|
|> encode_clauses(shift)
|
|
end
|
|
|
|
defp shift(alphabet) do
|
|
alphabet
|
|
|> length()
|
|
|> :math.log2()
|
|
|> round()
|
|
end
|
|
|
|
defp encode_clauses(alphabet, shift) do
|
|
for {encoding1, value1} <- alphabet,
|
|
{encoding2, value2} <- alphabet do
|
|
encoding = bsl(encoding1, 8) + encoding2
|
|
value = bsl(value1, shift) + value2
|
|
[clause] = quote(do: (unquote(value) -> unquote(encoding)))
|
|
clause
|
|
end
|
|
end
|
|
|
|
defmacrop decode_char(alphabet, case, encoding) do
|
|
quote do
|
|
case unquote(encoding) do
|
|
unquote(decode_char_clauses(alphabet, case))
|
|
end
|
|
end
|
|
end
|
|
|
|
defp decode_char_clauses(alphabet, case) when case in [:sensitive, :upper] do
|
|
clauses =
|
|
alphabet
|
|
|> Enum.with_index()
|
|
|> decode_clauses()
|
|
|
|
clauses ++ bad_digit_clause()
|
|
end
|
|
|
|
defp decode_char_clauses(alphabet, :lower) do
|
|
{uppers, rest} =
|
|
alphabet
|
|
|> Stream.with_index()
|
|
|> Enum.split_with(fn {encoding, _} -> encoding in ?A..?Z end)
|
|
|
|
lowers = Enum.map(uppers, fn {encoding, value} -> {encoding - ?A + ?a, value} end)
|
|
|
|
if length(uppers) > length(rest) do
|
|
decode_mixed_clauses(lowers, rest)
|
|
else
|
|
decode_mixed_clauses(rest, lowers)
|
|
end
|
|
end
|
|
|
|
defp decode_char_clauses(alphabet, :mixed) when length(alphabet) == 16 do
|
|
alphabet = Enum.with_index(alphabet)
|
|
|
|
lowers =
|
|
alphabet
|
|
|> Stream.filter(fn {encoding, _} -> encoding in ?A..?Z end)
|
|
|> Enum.map(fn {encoding, value} -> {encoding - ?A + ?a, value} end)
|
|
|
|
decode_mixed_clauses(alphabet, lowers)
|
|
end
|
|
|
|
defp decode_char_clauses(alphabet, :mixed) when length(alphabet) == 32 do
|
|
clauses =
|
|
alphabet
|
|
|> Stream.with_index()
|
|
|> Enum.flat_map(fn {encoding, value} = pair ->
|
|
if encoding in ?A..?Z do
|
|
[pair, {encoding - ?A + ?a, value}]
|
|
else
|
|
[pair]
|
|
end
|
|
end)
|
|
|> decode_clauses()
|
|
|
|
clauses ++ bad_digit_clause()
|
|
end
|
|
|
|
defp decode_mixed_clauses(first, second) do
|
|
first_clauses = decode_clauses(first)
|
|
second_clauses = decode_clauses(second) ++ bad_digit_clause()
|
|
|
|
join_clause =
|
|
quote do
|
|
encoding ->
|
|
case encoding do
|
|
unquote(second_clauses)
|
|
end
|
|
end
|
|
|
|
first_clauses ++ join_clause
|
|
end
|
|
|
|
defp decode_clauses(alphabet) do
|
|
for {encoding, value} <- alphabet do
|
|
[clause] = quote(do: (unquote(encoding) -> unquote(value)))
|
|
clause
|
|
end
|
|
end
|
|
|
|
defp bad_digit_clause() do
|
|
quote do
|
|
c ->
|
|
raise ArgumentError,
|
|
"non-alphabet digit found: #{inspect(<<c>>, binaries: :as_strings)} (byte #{c})"
|
|
end
|
|
end
|
|
|
|
defp maybe_pad(body, "", _, _), do: body
|
|
defp maybe_pad(body, tail, false, _), do: body <> tail
|
|
|
|
defp maybe_pad(body, tail, _, group_size) do
|
|
case group_size - rem(byte_size(tail), group_size) do
|
|
^group_size -> body <> tail
|
|
6 -> body <> tail <> "======"
|
|
5 -> body <> tail <> "====="
|
|
4 -> body <> tail <> "===="
|
|
3 -> body <> tail <> "==="
|
|
2 -> body <> tail <> "=="
|
|
1 -> body <> tail <> "="
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Encodes a binary string into a base 16 encoded string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to use when encoding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - uses upper case characters (default)
|
|
* `:lower` - uses lower case characters
|
|
|
|
## Examples
|
|
|
|
iex> Base.encode16("foobar")
|
|
"666F6F626172"
|
|
|
|
iex> Base.encode16("foobar", case: :lower)
|
|
"666f6f626172"
|
|
|
|
"""
|
|
@spec encode16(binary, keyword) :: binary
|
|
def encode16(data, opts \\ []) when is_binary(data) do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
do_encode16(case, data)
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 16 encoded string into a binary string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode16("666F6F626172")
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode16("666f6f626172", case: :lower)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode16("666f6F626172", case: :mixed)
|
|
{:ok, "foobar"}
|
|
|
|
"""
|
|
@spec decode16(binary, keyword) :: {:ok, binary} | :error
|
|
def decode16(string, opts \\ []) do
|
|
{:ok, decode16!(string, opts)}
|
|
rescue
|
|
ArgumentError -> :error
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 16 encoded string into a binary string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
An `ArgumentError` exception is raised if the padding is incorrect or
|
|
a non-alphabet character is present in the string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode16!("666F6F626172")
|
|
"foobar"
|
|
|
|
iex> Base.decode16!("666f6f626172", case: :lower)
|
|
"foobar"
|
|
|
|
iex> Base.decode16!("666f6F626172", case: :mixed)
|
|
"foobar"
|
|
|
|
"""
|
|
@spec decode16!(binary, keyword) :: binary
|
|
def decode16!(string, opts \\ [])
|
|
|
|
def decode16!(string, opts) when is_binary(string) and rem(byte_size(string), 2) == 0 do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
do_decode16(case, string)
|
|
end
|
|
|
|
def decode16!(string, _opts) when is_binary(string) do
|
|
raise ArgumentError, "odd-length string"
|
|
end
|
|
|
|
@doc """
|
|
Encodes a binary string into a base 64 encoded string.
|
|
|
|
Accepts `padding: false` option which will omit padding from
|
|
the output string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.encode64("foobar")
|
|
"Zm9vYmFy"
|
|
|
|
iex> Base.encode64("foob")
|
|
"Zm9vYg=="
|
|
|
|
iex> Base.encode64("foob", padding: false)
|
|
"Zm9vYg"
|
|
|
|
"""
|
|
@spec encode64(binary, keyword) :: binary
|
|
def encode64(data, opts \\ []) when is_binary(data) do
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_encode64(data, pad?)
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 64 encoded string into a binary string.
|
|
|
|
Accepts `ignore: :whitespace` option which will ignore all the
|
|
whitespace characters in the input string.
|
|
|
|
Accepts `padding: false` option which will ignore padding from
|
|
the input string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode64("Zm9vYmFy")
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode64("Zm9vYmFy\\n", ignore: :whitespace)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode64("Zm9vYg==")
|
|
{:ok, "foob"}
|
|
|
|
iex> Base.decode64("Zm9vYg", padding: false)
|
|
{:ok, "foob"}
|
|
|
|
"""
|
|
@spec decode64(binary, keyword) :: {:ok, binary} | :error
|
|
def decode64(string, opts \\ []) when is_binary(string) do
|
|
{:ok, decode64!(string, opts)}
|
|
rescue
|
|
ArgumentError -> :error
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 64 encoded string into a binary string.
|
|
|
|
Accepts `ignore: :whitespace` option which will ignore all the
|
|
whitespace characters in the input string.
|
|
|
|
Accepts `padding: false` option which will ignore padding from
|
|
the input string.
|
|
|
|
An `ArgumentError` exception is raised if the padding is incorrect or
|
|
a non-alphabet character is present in the string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode64!("Zm9vYmFy")
|
|
"foobar"
|
|
|
|
iex> Base.decode64!("Zm9vYmFy\\n", ignore: :whitespace)
|
|
"foobar"
|
|
|
|
iex> Base.decode64!("Zm9vYg==")
|
|
"foob"
|
|
|
|
iex> Base.decode64!("Zm9vYg", padding: false)
|
|
"foob"
|
|
|
|
"""
|
|
@spec decode64!(binary, keyword) :: binary
|
|
def decode64!(string, opts \\ []) when is_binary(string) do
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
string |> remove_ignored(opts[:ignore]) |> do_decode64(pad?)
|
|
end
|
|
|
|
@doc """
|
|
Encodes a binary string into a base 64 encoded string with URL and filename
|
|
safe alphabet.
|
|
|
|
Accepts `padding: false` option which will omit padding from
|
|
the output string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.url_encode64(<<255, 127, 254, 252>>)
|
|
"_3_-_A=="
|
|
|
|
iex> Base.url_encode64(<<255, 127, 254, 252>>, padding: false)
|
|
"_3_-_A"
|
|
|
|
"""
|
|
@spec url_encode64(binary, keyword) :: binary
|
|
def url_encode64(data, opts \\ []) when is_binary(data) do
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_encode64url(data, pad?)
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 64 encoded string with URL and filename safe alphabet
|
|
into a binary string.
|
|
|
|
Accepts `ignore: :whitespace` option which will ignore all the
|
|
whitespace characters in the input string.
|
|
|
|
Accepts `padding: false` option which will ignore padding from
|
|
the input string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.url_decode64("_3_-_A==")
|
|
{:ok, <<255, 127, 254, 252>>}
|
|
|
|
iex> Base.url_decode64("_3_-_A==\\n", ignore: :whitespace)
|
|
{:ok, <<255, 127, 254, 252>>}
|
|
|
|
iex> Base.url_decode64("_3_-_A", padding: false)
|
|
{:ok, <<255, 127, 254, 252>>}
|
|
|
|
"""
|
|
@spec url_decode64(binary, keyword) :: {:ok, binary} | :error
|
|
def url_decode64(string, opts \\ []) when is_binary(string) do
|
|
{:ok, url_decode64!(string, opts)}
|
|
rescue
|
|
ArgumentError -> :error
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 64 encoded string with URL and filename safe alphabet
|
|
into a binary string.
|
|
|
|
Accepts `ignore: :whitespace` option which will ignore all the
|
|
whitespace characters in the input string.
|
|
|
|
Accepts `padding: false` option which will ignore padding from
|
|
the input string.
|
|
|
|
An `ArgumentError` exception is raised if the padding is incorrect or
|
|
a non-alphabet character is present in the string.
|
|
|
|
## Examples
|
|
|
|
iex> Base.url_decode64!("_3_-_A==")
|
|
<<255, 127, 254, 252>>
|
|
|
|
iex> Base.url_decode64!("_3_-_A==\\n", ignore: :whitespace)
|
|
<<255, 127, 254, 252>>
|
|
|
|
iex> Base.url_decode64!("_3_-_A", padding: false)
|
|
<<255, 127, 254, 252>>
|
|
|
|
"""
|
|
@spec url_decode64!(binary, keyword) :: binary
|
|
def url_decode64!(string, opts \\ []) when is_binary(string) do
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
string |> remove_ignored(opts[:ignore]) |> do_decode64url(pad?)
|
|
end
|
|
|
|
@doc """
|
|
Encodes a binary string into a base 32 encoded string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to use when encoding
|
|
* `:padding` - specifies whether to apply padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - uses upper case characters (default)
|
|
* `:lower` - uses lower case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - pad the output string to the nearest multiple of 8 (default)
|
|
* `false` - omit padding from the output string
|
|
|
|
## Examples
|
|
|
|
iex> Base.encode32("foobar")
|
|
"MZXW6YTBOI======"
|
|
|
|
iex> Base.encode32("foobar", case: :lower)
|
|
"mzxw6ytboi======"
|
|
|
|
iex> Base.encode32("foobar", padding: false)
|
|
"MZXW6YTBOI"
|
|
|
|
"""
|
|
@spec encode32(binary, keyword) :: binary
|
|
def encode32(data, opts \\ []) when is_binary(data) do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_encode32(case, data, pad?)
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 32 encoded string into a binary string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
* `:padding` - specifies whether to require padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - requires the input string to be padded to the nearest multiple of 8 (default)
|
|
* `false` - ignores padding from the input string
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode32("MZXW6YTBOI======")
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode32("mzxw6ytboi======", case: :lower)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode32("mzXW6ytBOi======", case: :mixed)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.decode32("MZXW6YTBOI", padding: false)
|
|
{:ok, "foobar"}
|
|
|
|
"""
|
|
@spec decode32(binary, keyword) :: {:ok, binary} | :error
|
|
def decode32(string, opts \\ []) do
|
|
{:ok, decode32!(string, opts)}
|
|
rescue
|
|
ArgumentError -> :error
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 32 encoded string into a binary string.
|
|
|
|
An `ArgumentError` exception is raised if the padding is incorrect or
|
|
a non-alphabet character is present in the string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
* `:padding` - specifies whether to require padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - requires the input string to be padded to the nearest multiple of 8 (default)
|
|
* `false` - ignores padding from the input string
|
|
|
|
## Examples
|
|
|
|
iex> Base.decode32!("MZXW6YTBOI======")
|
|
"foobar"
|
|
|
|
iex> Base.decode32!("mzxw6ytboi======", case: :lower)
|
|
"foobar"
|
|
|
|
iex> Base.decode32!("mzXW6ytBOi======", case: :mixed)
|
|
"foobar"
|
|
|
|
iex> Base.decode32!("MZXW6YTBOI", padding: false)
|
|
"foobar"
|
|
|
|
"""
|
|
@spec decode32!(binary, keyword) :: binary
|
|
def decode32!(string, opts \\ []) when is_binary(string) do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_decode32(case, string, pad?)
|
|
end
|
|
|
|
@doc """
|
|
Encodes a binary string into a base 32 encoded string with an
|
|
extended hexadecimal alphabet.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to use when encoding
|
|
* `:padding` - specifies whether to apply padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - uses upper case characters (default)
|
|
* `:lower` - uses lower case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - pad the output string to the nearest multiple of 8 (default)
|
|
* `false` - omit padding from the output string
|
|
|
|
## Examples
|
|
|
|
iex> Base.hex_encode32("foobar")
|
|
"CPNMUOJ1E8======"
|
|
|
|
iex> Base.hex_encode32("foobar", case: :lower)
|
|
"cpnmuoj1e8======"
|
|
|
|
iex> Base.hex_encode32("foobar", padding: false)
|
|
"CPNMUOJ1E8"
|
|
|
|
"""
|
|
@spec hex_encode32(binary, keyword) :: binary
|
|
def hex_encode32(data, opts \\ []) when is_binary(data) do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_encode32hex(case, data, pad?)
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 32 encoded string with extended hexadecimal alphabet
|
|
into a binary string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
* `:padding` - specifies whether to require padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - requires the input string to be padded to the nearest multiple of 8 (default)
|
|
* `false` - ignores padding from the input string
|
|
|
|
## Examples
|
|
|
|
iex> Base.hex_decode32("CPNMUOJ1E8======")
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.hex_decode32("cpnmuoj1e8======", case: :lower)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.hex_decode32("cpnMuOJ1E8======", case: :mixed)
|
|
{:ok, "foobar"}
|
|
|
|
iex> Base.hex_decode32("CPNMUOJ1E8", padding: false)
|
|
{:ok, "foobar"}
|
|
|
|
"""
|
|
@spec hex_decode32(binary, keyword) :: {:ok, binary} | :error
|
|
def hex_decode32(string, opts \\ []) do
|
|
{:ok, hex_decode32!(string, opts)}
|
|
rescue
|
|
ArgumentError -> :error
|
|
end
|
|
|
|
@doc """
|
|
Decodes a base 32 encoded string with extended hexadecimal alphabet
|
|
into a binary string.
|
|
|
|
An `ArgumentError` exception is raised if the padding is incorrect or
|
|
a non-alphabet character is present in the string.
|
|
|
|
## Options
|
|
|
|
The accepted options are:
|
|
|
|
* `:case` - specifies the character case to accept when decoding
|
|
* `:padding` - specifies whether to require padding
|
|
|
|
The values for `:case` can be:
|
|
|
|
* `:upper` - only allows upper case characters (default)
|
|
* `:lower` - only allows lower case characters
|
|
* `:mixed` - allows mixed case characters
|
|
|
|
The values for `:padding` can be:
|
|
|
|
* `true` - requires the input string to be padded to the nearest multiple of 8 (default)
|
|
* `false` - ignores padding from the input string
|
|
|
|
## Examples
|
|
|
|
iex> Base.hex_decode32!("CPNMUOJ1E8======")
|
|
"foobar"
|
|
|
|
iex> Base.hex_decode32!("cpnmuoj1e8======", case: :lower)
|
|
"foobar"
|
|
|
|
iex> Base.hex_decode32!("cpnMuOJ1E8======", case: :mixed)
|
|
"foobar"
|
|
|
|
iex> Base.hex_decode32!("CPNMUOJ1E8", padding: false)
|
|
"foobar"
|
|
|
|
"""
|
|
@spec hex_decode32!(binary, keyword) :: binary
|
|
def hex_decode32!(string, opts \\ []) when is_binary(string) do
|
|
case = Keyword.get(opts, :case, :upper)
|
|
pad? = Keyword.get(opts, :padding, true)
|
|
do_decode32hex(case, string, pad?)
|
|
end
|
|
|
|
defp remove_ignored(string, nil), do: string
|
|
|
|
defp remove_ignored(string, :whitespace) do
|
|
for <<char::8 <- string>>, char not in '\s\t\r\n', into: <<>>, do: <<char::8>>
|
|
end
|
|
|
|
enc16 = [upper: :enc16_upper, lower: :enc16_lower]
|
|
|
|
for {case, fun} <- enc16 do
|
|
defp unquote(fun)(char) do
|
|
encode_pair(unquote(b16_alphabet), unquote(case), char)
|
|
end
|
|
end
|
|
|
|
defp do_encode16(_, <<>>), do: <<>>
|
|
|
|
for {case, fun} <- enc16 do
|
|
defp do_encode16(unquote(case), data) do
|
|
split = 8 * div(byte_size(data), 8)
|
|
<<main::size(split)-binary, rest::binary>> = data
|
|
|
|
main =
|
|
for <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(fun)(c1)::16,
|
|
unquote(fun)(c2)::16,
|
|
unquote(fun)(c3)::16,
|
|
unquote(fun)(c4)::16,
|
|
unquote(fun)(c5)::16,
|
|
unquote(fun)(c6)::16,
|
|
unquote(fun)(c7)::16,
|
|
unquote(fun)(c8)::16
|
|
>>
|
|
end
|
|
|
|
case rest do
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8>> ->
|
|
<<
|
|
main::binary,
|
|
unquote(fun)(c1)::16,
|
|
unquote(fun)(c2)::16,
|
|
unquote(fun)(c3)::16,
|
|
unquote(fun)(c4)::16,
|
|
unquote(fun)(c5)::16,
|
|
unquote(fun)(c6)::16,
|
|
unquote(fun)(c7)::16
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8>> ->
|
|
<<
|
|
main::binary,
|
|
unquote(fun)(c1)::16,
|
|
unquote(fun)(c2)::16,
|
|
unquote(fun)(c3)::16,
|
|
unquote(fun)(c4)::16,
|
|
unquote(fun)(c5)::16,
|
|
unquote(fun)(c6)::16
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8>> ->
|
|
<<
|
|
main::binary,
|
|
unquote(fun)(c1)::16,
|
|
unquote(fun)(c2)::16,
|
|
unquote(fun)(c3)::16,
|
|
unquote(fun)(c4)::16,
|
|
unquote(fun)(c5)::16
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8>> ->
|
|
<<
|
|
main::binary,
|
|
unquote(fun)(c1)::16,
|
|
unquote(fun)(c2)::16,
|
|
unquote(fun)(c3)::16,
|
|
unquote(fun)(c4)::16
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8>> ->
|
|
<<main::binary, unquote(fun)(c1)::16, unquote(fun)(c2)::16, unquote(fun)(c3)::16>>
|
|
|
|
<<c1::8, c2::8>> ->
|
|
<<main::binary, unquote(fun)(c1)::16, unquote(fun)(c2)::16>>
|
|
|
|
<<c1::8>> ->
|
|
<<main::binary, unquote(fun)(c1)::16>>
|
|
|
|
<<>> ->
|
|
main
|
|
end
|
|
end
|
|
end
|
|
|
|
dec16 = [upper: :dec16_upper, lower: :dec16_lower, mixed: :dec16_mixed]
|
|
|
|
for {case, fun} <- dec16 do
|
|
defp unquote(fun)(encoding) do
|
|
decode_char(unquote(b16_alphabet), unquote(case), encoding)
|
|
end
|
|
end
|
|
|
|
defp do_decode16(_, <<>>), do: <<>>
|
|
|
|
for {case, fun} <- dec16 do
|
|
defp do_decode16(unquote(case), string) do
|
|
split = 8 * div(byte_size(string), 8)
|
|
<<main::size(split)-binary, rest::binary>> = string
|
|
|
|
main =
|
|
for <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(fun)(c1)::4,
|
|
unquote(fun)(c2)::4,
|
|
unquote(fun)(c3)::4,
|
|
unquote(fun)(c4)::4,
|
|
unquote(fun)(c5)::4,
|
|
unquote(fun)(c6)::4,
|
|
unquote(fun)(c7)::4,
|
|
unquote(fun)(c8)::4
|
|
>>
|
|
end
|
|
|
|
case rest do
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::4,
|
|
unquote(fun)(c2)::4,
|
|
unquote(fun)(c3)::4,
|
|
unquote(fun)(c4)::4,
|
|
unquote(fun)(c5)::4,
|
|
unquote(fun)(c6)::4
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::4,
|
|
unquote(fun)(c2)::4,
|
|
unquote(fun)(c3)::4,
|
|
unquote(fun)(c4)::4
|
|
>>
|
|
|
|
<<c1::8, c2::8>> ->
|
|
<<main::bits, unquote(fun)(c1)::4, unquote(fun)(c2)::4>>
|
|
|
|
<<_::8>> ->
|
|
raise ArgumentError, "odd-length string"
|
|
|
|
<<>> ->
|
|
main
|
|
end
|
|
end
|
|
end
|
|
|
|
for {base, alphabet} <- ["64": b64_alphabet, "64url": b64url_alphabet] do
|
|
pair = :"enc#{base}_pair"
|
|
char = :"enc#{base}_char"
|
|
do_encode = :"do_encode#{base}"
|
|
|
|
defp unquote(pair)(value) do
|
|
encode_pair(unquote(alphabet), :sensitive, value)
|
|
end
|
|
|
|
defp unquote(char)(value) do
|
|
value
|
|
|> unquote(pair)()
|
|
|> band(0x00FF)
|
|
end
|
|
|
|
defp unquote(do_encode)(<<>>, _), do: <<>>
|
|
|
|
defp unquote(do_encode)(data, pad?) do
|
|
split = 6 * div(byte_size(data), 6)
|
|
<<main::size(split)-binary, rest::binary>> = data
|
|
|
|
main =
|
|
for <<c1::12, c2::12, c3::12, c4::12 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(pair)(c1)::16,
|
|
unquote(pair)(c2)::16,
|
|
unquote(pair)(c3)::16,
|
|
unquote(pair)(c4)::16
|
|
>>
|
|
end
|
|
|
|
tail =
|
|
case rest do
|
|
<<c1::12, c2::12, c3::12, c::4>> ->
|
|
<<
|
|
unquote(pair)(c1)::16,
|
|
unquote(pair)(c2)::16,
|
|
unquote(pair)(c3)::16,
|
|
unquote(char)(bsl(c, 2))::8
|
|
>>
|
|
|
|
<<c1::12, c2::12, c3::8>> ->
|
|
<<unquote(pair)(c1)::16, unquote(pair)(c2)::16, unquote(pair)(bsl(c3, 4))::16>>
|
|
|
|
<<c1::12, c2::12>> ->
|
|
<<unquote(pair)(c1)::16, unquote(pair)(c2)::16>>
|
|
|
|
<<c1::12, c2::4>> ->
|
|
<<unquote(pair)(c1)::16, unquote(char)(bsl(c2, 2))::8>>
|
|
|
|
<<c1::8>> ->
|
|
<<unquote(pair)(bsl(c1, 4))::16>>
|
|
|
|
<<>> ->
|
|
<<>>
|
|
end
|
|
|
|
maybe_pad(main, tail, pad?, 4)
|
|
end
|
|
end
|
|
|
|
for {base, alphabet} <- ["64": b64_alphabet, "64url": b64url_alphabet] do
|
|
fun = :"dec#{base}"
|
|
do_decode = :"do_decode#{base}"
|
|
|
|
defp unquote(fun)(encoding) do
|
|
decode_char(unquote(alphabet), :sensitive, encoding)
|
|
end
|
|
|
|
defp unquote(do_decode)(<<>>, _), do: <<>>
|
|
|
|
defp unquote(do_decode)(string, pad?) do
|
|
segs = div(byte_size(string) + 7, 8) - 1
|
|
<<main::size(segs)-binary-unit(64), rest::binary>> = string
|
|
|
|
main =
|
|
for <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
unquote(fun)(c6)::6,
|
|
unquote(fun)(c7)::6,
|
|
unquote(fun)(c8)::6
|
|
>>
|
|
end
|
|
|
|
case rest do
|
|
<<c1::8, c2::8, ?=, ?=>> ->
|
|
<<main::bits, unquote(fun)(c1)::6, bsr(unquote(fun)(c2), 4)::2>>
|
|
|
|
<<c1::8, c2::8, c3::8, ?=>> ->
|
|
<<main::bits, unquote(fun)(c1)::6, unquote(fun)(c2)::6, bsr(unquote(fun)(c3), 2)::4>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, ?=, ?=>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
bsr(unquote(fun)(c6), 4)::2
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, ?=>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
unquote(fun)(c6)::6,
|
|
bsr(unquote(fun)(c7), 2)::4
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
unquote(fun)(c6)::6,
|
|
unquote(fun)(c7)::6,
|
|
unquote(fun)(c8)::6
|
|
>>
|
|
|
|
<<c1::8, c2::8>> when not pad? ->
|
|
<<main::bits, unquote(fun)(c1)::6, bsr(unquote(fun)(c2), 4)::2>>
|
|
|
|
<<c1::8, c2::8, c3::8>> when not pad? ->
|
|
<<main::bits, unquote(fun)(c1)::6, unquote(fun)(c2)::6, bsr(unquote(fun)(c3), 2)::4>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8>> when not pad? ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
bsr(unquote(fun)(c6), 4)::2
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8>> when not pad? ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::6,
|
|
unquote(fun)(c2)::6,
|
|
unquote(fun)(c3)::6,
|
|
unquote(fun)(c4)::6,
|
|
unquote(fun)(c5)::6,
|
|
unquote(fun)(c6)::6,
|
|
bsr(unquote(fun)(c7), 2)::4
|
|
>>
|
|
|
|
_ ->
|
|
raise ArgumentError, "incorrect padding"
|
|
end
|
|
end
|
|
end
|
|
|
|
for {base, alphabet} <- ["32": b32_alphabet, "32hex": b32hex_alphabet],
|
|
case <- [:upper, :lower] do
|
|
pair = :"enc#{base}_#{case}_pair"
|
|
char = :"enc#{base}_#{case}_char"
|
|
do_encode = :"do_encode#{base}"
|
|
|
|
defp unquote(pair)(value) do
|
|
encode_pair(unquote(alphabet), unquote(case), value)
|
|
end
|
|
|
|
defp unquote(char)(value) do
|
|
value
|
|
|> unquote(pair)()
|
|
|> band(0x00FF)
|
|
end
|
|
|
|
defp unquote(do_encode)(_, <<>>, _), do: <<>>
|
|
|
|
defp unquote(do_encode)(unquote(case), data, pad?) do
|
|
split = 5 * div(byte_size(data), 5)
|
|
<<main::size(split)-binary, rest::binary>> = data
|
|
|
|
main =
|
|
for <<c1::10, c2::10, c3::10, c4::10 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(pair)(c1)::16,
|
|
unquote(pair)(c2)::16,
|
|
unquote(pair)(c3)::16,
|
|
unquote(pair)(c4)::16
|
|
>>
|
|
end
|
|
|
|
tail =
|
|
case rest do
|
|
<<c1::10, c2::10, c3::10, c4::2>> ->
|
|
<<
|
|
unquote(pair)(c1)::16,
|
|
unquote(pair)(c2)::16,
|
|
unquote(pair)(c3)::16,
|
|
unquote(char)(bsl(c4, 3))::8
|
|
>>
|
|
|
|
<<c1::10, c2::10, c3::4>> ->
|
|
<<unquote(pair)(c1)::16, unquote(pair)(c2)::16, unquote(char)(bsl(c3, 1))::8>>
|
|
|
|
<<c1::10, c2::6>> ->
|
|
<<unquote(pair)(c1)::16, unquote(pair)(bsl(c2, 4))::16>>
|
|
|
|
<<c1::8>> ->
|
|
<<unquote(pair)(bsl(c1, 2))::16>>
|
|
|
|
<<>> ->
|
|
<<>>
|
|
end
|
|
|
|
maybe_pad(main, tail, pad?, 8)
|
|
end
|
|
end
|
|
|
|
for {base, alphabet} <- ["32": b32_alphabet, "32hex": b32hex_alphabet],
|
|
case <- [:upper, :lower, :mixed] do
|
|
fun = :"dec#{base}_#{case}"
|
|
do_decode = :"do_decode#{base}"
|
|
|
|
defp unquote(fun)(encoding) do
|
|
decode_char(unquote(alphabet), unquote(case), encoding)
|
|
end
|
|
|
|
defp unquote(do_decode)(_, <<>>, _), do: <<>>
|
|
|
|
defp unquote(do_decode)(unquote(case), string, pad?) do
|
|
segs = div(byte_size(string) + 7, 8) - 1
|
|
<<main::size(segs)-binary-unit(64), rest::binary>> = string
|
|
|
|
main =
|
|
for <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8 <- main>>, into: <<>> do
|
|
<<
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
unquote(fun)(c5)::5,
|
|
unquote(fun)(c6)::5,
|
|
unquote(fun)(c7)::5,
|
|
unquote(fun)(c8)::5
|
|
>>
|
|
end
|
|
|
|
case rest do
|
|
<<c1::8, c2::8, ?=, ?=, ?=, ?=, ?=, ?=>> ->
|
|
<<main::bits, unquote(fun)(c1)::5, bsr(unquote(fun)(c2), 2)::3>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, ?=, ?=, ?=, ?=>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
bsr(unquote(fun)(c4), 4)::1
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, ?=, ?=, ?=>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
bsr(unquote(fun)(c5), 1)::4
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, ?=>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
unquote(fun)(c5)::5,
|
|
unquote(fun)(c6)::5,
|
|
bsr(unquote(fun)(c7), 3)::2
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8>> ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
unquote(fun)(c5)::5,
|
|
unquote(fun)(c6)::5,
|
|
unquote(fun)(c7)::5,
|
|
unquote(fun)(c8)::5
|
|
>>
|
|
|
|
<<c1::8, c2::8>> when not pad? ->
|
|
<<main::bits, unquote(fun)(c1)::5, bsr(unquote(fun)(c2), 2)::3>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8>> when not pad? ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
bsr(unquote(fun)(c4), 4)::1
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8>> when not pad? ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
bsr(unquote(fun)(c5), 1)::4
|
|
>>
|
|
|
|
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8>> when not pad? ->
|
|
<<
|
|
main::bits,
|
|
unquote(fun)(c1)::5,
|
|
unquote(fun)(c2)::5,
|
|
unquote(fun)(c3)::5,
|
|
unquote(fun)(c4)::5,
|
|
unquote(fun)(c5)::5,
|
|
unquote(fun)(c6)::5,
|
|
bsr(unquote(fun)(c7), 3)::2
|
|
>>
|
|
|
|
_ ->
|
|
raise ArgumentError, "incorrect padding"
|
|
end
|
|
end
|
|
end
|
|
end
|