Powered by AppSignal & Oban Pro

MonoID

nb/monoid.livemd

MonoID

SeqMonoID

t = System.os_time(:nanosecond)
{sec, subsec} = {div(t, 1_000_000_000), rem(t, 1_000_000_000)}
unixts = <<sec::32>>
0b10
<<sub_a::12, sub_b::12, sub_c::6>> = <<subsec::30>>
<<sec::32, 0::4, sub_a::12, 0b0111::4, sub_b::12, 0b10::2, sub_c::6>>
defmodule SeqMonoID do
  use GenServer

  @impl true
  def init(:ok) do
    {:ok, {0, 0}}
  end

  def gen_id({now, seq}) do
    {sec, subsec} = {div(now, 1_000_000_000), rem(now, 1_000_000_000)}
    <<sub_a::12, sub_b::12, sub_c::6>> = <<subsec::30>>
    clock = <<sec::36, sub_a::12, 0b0111::4, sub_b::12, 0b10::2, sub_c::6, seq::8>>
    entropy = :crypto.strong_rand_bytes(6)
    clock <> entropy
  end

  @impl true
  def handle_call(:gen, _from, {last, seq}) do
    now = System.os_time(:nanosecond)

    case now <= last do
      true -> {:reply, gen_id({now, seq + 1}), {last, seq + 1}}
      false -> {:reply, gen_id({now, 0}), {now, 0}}
    end
  end
end
defmodule MonoID do
  def gen(:raw) do
    now = System.os_time(:nanosecond)
    {sec, subsec} = {div(now, 1_000_000_000), rem(now, 1_000_000_000)}
    <<sub_a::12, sub_b::12, sub_c::6>> = <<subsec::30>>
    clock = <<sec::36, sub_a::12, 0b0111::4, sub_b::12, 0b10::2, sub_c::6>>
    entropy = :crypto.strong_rand_bytes(7)
    clock <> entropy
  end
end
<<a::32, b::16, c::16, d::16, e::48>> = MonoID.gen(:raw)

Base.encode16(<<a::32>>) <>
  "-" <>
  Base.encode16(<<b::16>>) <>
  "-" <>
  Base.encode16(<<c::16>>) <> "-" <> Base.encode16(<<d::16>>) <> "f-" <> Base.encode16(<<e::48>>)