Packages

An Elixir implementation of cuid2 See https://github.com/paralleldrive/cuid2

Current section

Files

Jump to
cuid2 lib cuid2.ex
Raw

lib/cuid2.ex

defmodule Cuid2 do
@default_length 24
@big_length 32
@alphabet Enum.map(97..122, &<<&1::utf8>>)
@initial_count_max 476_782_367
def create_entropy(length \\ 4, random \\ &:rand.uniform/0, entropy \\ "") do
if entropy > length do
entropy
else
entropy <> ((random.() * 36) |> trunc |> Integer.to_string(36))
end
end
def buf_to_big_int(buf) do
buf
|> :binary.bin_to_list()
|> Enum.reduce(0, &(Bitwise.bsl(&2, 8) + &1))
end
def sha3_512(input) do
:sha3_512
|> :crypto.hash(input)
|> buf_to_big_int()
end
def hash(input \\ "") do
input
|> sha3_512()
|> Integer.to_string(36)
|> String.downcase()
|> String.slice(1..-1)
end
def create_fingerprint(random \\ :rand.uniform() / 0) do
create_entropy(@big_length, random)
|> hash()
|> String.slice(0, @big_length)
end
def create_counter(count) do
fn -> count + 1 end
end
def init(opts \\ []) do
random = Keyword.get(opts, :random, &:rand.uniform/0)
counter =
Keyword.get_lazy(opts, :counter, fn ->
create_counter(trunc(random.() * @initial_count_max))
end)
length = Keyword.get(opts, :length, @default_length)
fingerprint = Keyword.get_lazy(opts, :fingerprint, fn -> create_fingerprint(random) end)
fn ->
first_letter = Enum.random(@alphabet)
time = DateTime.utc_now() |> DateTime.to_unix() |> Integer.to_string(36)
count = counter.() |> Integer.to_string(36)
salt = create_entropy(length, random)
hash_input = time <> salt <> count <> fingerprint
(first_letter <> hash(hash_input)) |> String.slice(1, length)
end
end
def create(opts \\ []) do
init(opts).()
end
def is_cuid?(id, opts \\ [])
def is_cuid?(id, opts) when is_binary(id) do
min_length = Keyword.get(opts, :min_length, 2)
max_length = Keyword.get(opts, :max_length, @big_length)
length = String.length(id)
regex = ~r/^[0-9a-z]+$/
min_length <= length and length <= max_length and Regex.match?(regex, id)
end
def is_cuid?(_, _), do: false
end