1defmodule Atex.Base32Sortable do
2 @moduledoc """
3 Codec for the base32-sortable encoding.
4 """
5
6 @alphabet ~c(234567abcdefghijklmnopqrstuvwxyz)
7 @alphabet_len length(@alphabet)
8
9 @doc """
10 Encode an integer as a base32-sortable string.
11 """
12 @spec encode(integer()) :: String.t()
13 def encode(int) when is_integer(int), do: do_encode(int, "")
14
15 @spec do_encode(integer(), String.t()) :: String.t()
16 defp do_encode(0, acc), do: acc
17
18 defp do_encode(int, acc) do
19 char_index = rem(int, @alphabet_len)
20 new_int = div(int, @alphabet_len)
21
22 # Chars are prepended to the accumulator because rem/div is pulling them off the tail of the integer.
23 do_encode(new_int, <<Enum.at(@alphabet, char_index)>> <> acc)
24 end
25
26 @doc """
27 Decode a base32-sortable string to an integer.
28 """
29 @spec decode(String.t()) :: integer()
30 def decode(str) when is_binary(str), do: do_decode(str, 0)
31
32 @spec do_decode(String.t(), integer()) :: integer()
33 defp do_decode(<<>>, acc), do: acc
34
35 defp do_decode(<<char::utf8, rest::binary>>, acc) do
36 i = Enum.find_index(@alphabet, fn x -> x == char end)
37 do_decode(rest, acc * @alphabet_len + i)
38 end
39end