Packages

A Gleam implementation of CUID2, the secure, collision-resistant ids optimized for horizontal scaling and performance.

Current section

Files

Jump to
glecuid src glecuid@cuid2.erl
Raw

src/glecuid@cuid2.erl

-module(glecuid@cuid2).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glecuid/cuid2.gleam").
-export([with_counter/2, with_fingerprint/2, with_length/2, with_randomizer/2, bit_array_to_base36/1, bump_counter/1, get_global_object/0, new_counter/1, generate/1, create_fingerprint/1, is_cuid/1, new/0, create_id/0]).
-export_type([generator/0, counter/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque generator() :: {generator,
counter(),
binary(),
integer(),
fun(() -> float())}.
-opaque counter() :: {counter, atomic_array:atomic_array()} |
{custom_counter, fun(() -> integer())}.
-file("src/glecuid/cuid2.gleam", 110).
?DOC(
" Sets a custom counter to be used by the `Generator`.\n"
" \n"
" Counter is a function that returns an incrementing `Int` \n"
" every time it is called.\n"
).
-spec with_counter(generator(), fun(() -> integer())) -> generator().
with_counter(G, Counter) ->
{generator,
{custom_counter, Counter},
erlang:element(3, G),
erlang:element(4, G),
erlang:element(5, G)}.
-file("src/glecuid/cuid2.gleam", 119).
?DOC(
" Sets a custom fingerprint to be used by the `Generator`.\n"
" \n"
" Fingerprint is a unique `String` to help prevent collision \n"
" when generating ids in a distributed system.\n"
).
-spec with_fingerprint(generator(), binary()) -> generator().
with_fingerprint(G, Fingerprint) ->
{generator,
erlang:element(2, G),
Fingerprint,
erlang:element(4, G),
erlang:element(5, G)}.
-file("src/glecuid/cuid2.gleam", 125).
?DOC(" Sets the length of the ids generated by the `Generator`.\n").
-spec with_length(generator(), integer()) -> generator().
with_length(G, Length) ->
{generator,
erlang:element(2, G),
erlang:element(3, G),
Length,
erlang:element(5, G)}.
-file("src/glecuid/cuid2.gleam", 134).
?DOC(
" Sets a custom randomizer to be used by the `Generator`.\n"
" \n"
" Randomizer is a function that returns a random `Float` \n"
" between zero (inclusive) and one (exclusive).\n"
).
-spec with_randomizer(generator(), fun(() -> float())) -> generator().
with_randomizer(G, Randomizer) ->
{generator,
erlang:element(2, G),
erlang:element(3, G),
erlang:element(4, G),
Randomizer}.
-file("src/glecuid/cuid2.gleam", 156).
-spec bit_array_to_int_loop(integer(), bitstring()) -> integer().
bit_array_to_int_loop(Accumulator, Bit_array) ->
case Bit_array of
<<>> ->
Accumulator;
<<X:1>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:2>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:3>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:4>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:5>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:6>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:7>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X:8>> ->
_pipe = Accumulator,
_pipe@1 = erlang:'bsl'(_pipe, 8),
gleam@int:add(_pipe@1, X);
<<X@1, Rest/bitstring>> ->
_pipe@2 = Accumulator,
_pipe@3 = erlang:'bsl'(_pipe@2, 8),
_pipe@4 = gleam@int:add(_pipe@3, X@1),
bit_array_to_int_loop(_pipe@4, Rest);
_ ->
Accumulator
end.
-file("src/glecuid/cuid2.gleam", 152).
?DOC(" Converts `BitArray` into `Int`.\n").
-spec bit_array_to_int(bitstring()) -> integer().
bit_array_to_int(Bit_array) ->
bit_array_to_int_loop(0, Bit_array).
-file("src/glecuid/cuid2.gleam", 144).
?DOC(false).
-spec bit_array_to_base36(bitstring()) -> binary().
bit_array_to_base36(Bit_array) ->
_pipe = Bit_array,
_pipe@1 = bit_array_to_int(_pipe),
gleam@int:to_base36(_pipe@1).
-file("src/glecuid/cuid2.gleam", 187).
?DOC(false).
-spec bump_counter(counter()) -> integer().
bump_counter(C) ->
case C of
{counter, C@1} ->
case begin
_pipe = C@1,
atomic_array_ffi:add(_pipe, 0, 1)
end of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glecuid/cuid2"/utf8>>,
function => <<"bump_counter"/utf8>>,
line => 190,
value => _assert_fail,
start => 4405,
'end' => 4451,
pattern_start => 4416,
pattern_end => 4421})
end,
V@1 = case begin
_pipe@1 = C@1,
atomic_array_ffi:get(_pipe@1, 0)
end of
{ok, V} -> V;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glecuid/cuid2"/utf8>>,
function => <<"bump_counter"/utf8>>,
line => 191,
value => _assert_fail@1,
start => 4458,
'end' => 4501,
pattern_start => 4469,
pattern_end => 4474})
end,
V@1;
{custom_counter, C@2} ->
C@2()
end.
-file("src/glecuid/cuid2.gleam", 239).
?DOC(false).
-spec get_global_object() -> binary().
get_global_object() ->
_pipe = erlang:self(),
gleam@string:inspect(_pipe).
-file("src/glecuid/cuid2.gleam", 245).
?DOC(" Hashes the input.\n").
-spec hash(binary()) -> binary().
hash(Input) ->
_pipe = gleam_stdlib:identity(Input),
_pipe@1 = gleam@crypto:hash(sha512, _pipe),
_pipe@2 = bit_array_to_base36(_pipe@1),
gleam@string:drop_start(_pipe@2, 1).
-file("src/glecuid/cuid2.gleam", 269).
?DOC(" Creates a counter using the specified initial value.\n").
-spec new_counter(integer()) -> counter().
new_counter(Initial_count) ->
Counter = atomic_array:new_unsigned(1),
case begin
_pipe = Counter,
atomic_array_ffi:set(_pipe, 0, Initial_count)
end of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glecuid/cuid2"/utf8>>,
function => <<"new_counter"/utf8>>,
line => 271,
value => _assert_fail,
start => 6659,
'end' => 6723,
pattern_start => 6670,
pattern_end => 6675})
end,
{counter, Counter}.
-file("src/glecuid/cuid2.gleam", 279).
?DOC(
" Generates a random int between zero and the given maximum \n"
" using the provided randomizer.\n"
" The lower number is inclusive, the upper number is exclusive.\n"
).
-spec random_int(fun(() -> float()), integer()) -> integer().
random_int(Randomizer, Max) ->
_pipe = (Randomizer() * erlang:float(Max)),
_pipe@1 = math:floor(_pipe),
erlang:round(_pipe@1).
-file("src/glecuid/cuid2.gleam", 206).
-spec create_entropy_loop(binary(), integer(), fun(() -> float()), integer()) -> binary().
create_entropy_loop(Accumulator, Iteration, Randomizer, Length) ->
gleam@bool:guard(
Iteration >= Length,
Accumulator,
fun() ->
_pipe@1 = (<<Accumulator/binary,
(begin
_pipe = random_int(Randomizer, 36),
gleam@int:to_base36(_pipe)
end)/binary>>),
create_entropy_loop(_pipe@1, Iteration + 1, Randomizer, Length)
end
).
-file("src/glecuid/cuid2.gleam", 202).
?DOC(
" Generates a random alphanumeric string with a specified length\n"
" using the provided randomizer.\n"
).
-spec create_entropy(fun(() -> float()), integer()) -> binary().
create_entropy(Randomizer, Length) ->
create_entropy_loop(<<""/utf8>>, 0, Randomizer, Length).
-file("src/glecuid/cuid2.gleam", 288).
?DOC(
" Generates a random lowercase letter using the provided \n"
" randomizer.\n"
).
-spec random_letter(fun(() -> float())) -> binary().
random_letter(Randomizer) ->
C@1 = case begin
_pipe = random_int(Randomizer, 27),
_pipe@1 = gleam@int:add(_pipe, 97),
gleam@string:utf_codepoint(_pipe@1)
end of
{ok, C} -> C;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glecuid/cuid2"/utf8>>,
function => <<"random_letter"/utf8>>,
line => 289,
value => _assert_fail,
start => 7195,
'end' => 7293,
pattern_start => 7206,
pattern_end => 7211})
end,
gleam_stdlib:utf_codepoint_list_to_string([C@1]).
-file("src/glecuid/cuid2.gleam", 70).
?DOC(
" Generates a CUIDv2 using custom generator.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" cuid2.new()\n"
" |> cuid2.with_length(10)\n"
" |> cuid2.with_fingerprint(\"my_machine\")\n"
" |> cuid2.generate()\n"
" // -> \"av77nekw5e\"\n"
" ```\n"
).
-spec generate(generator()) -> binary().
generate(G) ->
{generator, Counter, Fingerprint, Length, Randomizer} = G,
First_letter = random_letter(Randomizer),
Time = begin
_pipe = gleam@time@timestamp:system_time(),
_pipe@1 = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(_pipe),
_pipe@2 = gleam@pair:first(_pipe@1),
gleam@int:to_base36(_pipe@2)
end,
Count = begin
_pipe@3 = bump_counter(Counter),
gleam@int:to_base36(_pipe@3)
end,
Salt = create_entropy(Randomizer, Length),
<<First_letter/binary,
(begin
_pipe@4 = (<<<<<<Time/binary, Salt/binary>>/binary, Count/binary>>/binary,
Fingerprint/binary>>),
_pipe@5 = hash(_pipe@4),
_pipe@6 = gleam@string:slice(_pipe@5, 1, Length - 1),
string:lowercase(_pipe@6)
end)/binary>>.
-file("src/glecuid/cuid2.gleam", 219).
?DOC(" Generates a fingerprint of the host environtment.\n").
-spec create_fingerprint(fun(() -> float())) -> binary().
create_fingerprint(Randomizer) ->
<<(get_global_object())/binary,
(begin
_pipe = create_entropy(Randomizer, 32),
_pipe@1 = hash(_pipe),
gleam@string:slice(_pipe@1, 0, 32)
end)/binary>>.
-file("src/glecuid/cuid2.gleam", 257).
?DOC(" Determines whether or not the string is a valid CUIDv2.\n").
-spec is_cuid(binary()) -> boolean().
is_cuid(Input) ->
Length = begin
_pipe = Input,
string:length(_pipe)
end,
Regex@1 = case gleam@regexp:from_string(<<"^[a-z][0-9a-z]+$"/utf8>>) of
{ok, Regex} -> Regex;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glecuid/cuid2"/utf8>>,
function => <<"is_cuid"/utf8>>,
line => 259,
value => _assert_fail,
start => 6316,
'end' => 6377,
pattern_start => 6327,
pattern_end => 6336})
end,
case gleam@regexp:check(Regex@1, Input) of
true when (2 =< Length) andalso (Length =< 32) ->
true;
_ ->
false
end.
-file("src/glecuid/cuid2.gleam", 94).
?DOC(" Creates a `Generator` with default values to be customized.\n").
-spec new() -> generator().
new() ->
Randomizer = fun rand:uniform/0,
{generator,
begin
_pipe = random_int(Randomizer, 476782367),
new_counter(_pipe)
end,
create_fingerprint(Randomizer),
24,
Randomizer}.
-file("src/glecuid/cuid2.gleam", 54).
?DOC(
" Generates a CUIDv2 using the default generator.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" cuid2.create_id()\n"
" // -> \"avu4793cnw6ljhov1s7Oxidg\" \n"
" ```\n"
).
-spec create_id() -> binary().
create_id() ->
generate(new()).