Packages

A collection of Gleam utilities all written in pure gleam

Current section

Files

Jump to
glib src glib@map.erl
Raw

src/glib@map.erl

-module(glib@map).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([size/1, is_empty/1, keys/1, values/1, entries/1, to_string/2, get/2, contains_key/2, remove/2, list_size/1, full_count/1, new_with_size_and_load/2, new_with_size/1, new/0, clear/1, put/3]).
-export_type([entry/1, map_/1, rehash_data/1]).
-type entry(GBJ) :: {entry, binary(), GBJ}.
-opaque map_(GBK) :: {map,
list(gleam@option:option(entry(GBK))),
integer(),
integer(),
integer()}.
-type rehash_data(GBL) :: {rehash_data,
list(gleam@option:option(entry(GBL))),
list({{integer(), integer()}, entry(GBL)}),
integer(),
integer()}.
-spec size(map_(any())) -> integer().
size(Map) ->
erlang:element(5, Map).
-spec is_empty(map_(any())) -> boolean().
is_empty(Map) ->
size(Map) =:= 0.
-spec get_at(list(GBZ), integer()) -> GBZ.
get_at(List, Pos) ->
_assert_subject = erlang:element(2, gleam@list:split(List, Pos)),
[R | _] = case _assert_subject of
[_ | _] -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glib/map"/utf8>>,
function => <<"get_at"/utf8>>,
line => 110})
end,
R.
-spec keys(map_(any())) -> list(binary()).
keys(Map) ->
gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, erlang:element(2, En)}
end end).
-spec values(map_(GCQ)) -> list(GCQ).
values(Map) ->
gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, erlang:element(3, En)}
end end).
-spec entries(map_(GCT)) -> list({binary(), GCT}).
entries(Map) ->
gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, {erlang:element(2, En), erlang:element(3, En)}}
end end).
-spec to_string(map_(GCW), fun((GCW) -> binary())) -> binary().
to_string(Map, Value_to_string) ->
<<<<"{"/utf8,
(gleam@string:join(
gleam@list:filter_map(
erlang:element(2, Map),
fun(Opt) -> case Opt of
none ->
{error, Opt};
{some, E} ->
{ok,
<<<<<<<<"\""/utf8,
(erlang:element(2, E))/binary>>/binary,
"\""/utf8>>/binary,
":"/utf8>>/binary,
(Value_to_string(erlang:element(3, E)))/binary>>}
end end
),
<<","/utf8>>
))/binary>>/binary,
"}"/utf8>>.
-spec insert_at(
list(gleam@option:option(entry(GCY))),
integer(),
gleam@option:option(entry(GCY))
) -> list(gleam@option:option(entry(GCY))).
insert_at(Map_list, At, Entry) ->
{Split_left, Split_right} = gleam@list:split(Map_list, At),
gleam@list:concat([Split_left, [Entry], case Split_right of
[] ->
[];
[_ | Right] ->
Right
end]).
-spec do_remove(map_(GDH), integer(), GDH) -> {gleam@option:option(GDH),
map_(GDH)}.
do_remove(Map, Index, Value) ->
New_map = {map,
insert_at(erlang:element(2, Map), Index, none),
erlang:element(3, Map),
erlang:element(4, Map),
erlang:element(5, Map) - 1},
{{some, Value}, New_map}.
-spec find_gap(map_(any()), binary(), integer(), integer()) -> {integer(),
boolean()}.
find_gap(Map, Key, Last_position, Position) ->
case get_at(erlang:element(2, Map), Position) of
none ->
{Position, false};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{Position, true};
false ->
case Position of
Position@1 when Position@1 =:= Last_position ->
{-1, false};
0 ->
find_gap(
Map,
Key,
Last_position,
erlang:element(3, Map) - 1
);
Position@2 ->
find_gap(Map, Key, Last_position, Position@2 - 1)
end
end
end.
-spec find_key(
map_(GDR),
binary(),
integer(),
integer(),
fun((integer(), GDR) -> GDT)
) -> gleam@option:option(GDT).
find_key(Map, Key, Last_position, Position, Ret_fn) ->
case get_at(erlang:element(2, Map), Position) of
none ->
none;
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{some, Ret_fn(Position, erlang:element(3, E))};
false ->
case Position of
Position@1 when Position@1 =:= Last_position ->
none;
0 ->
find_key(
Map,
Key,
Last_position,
erlang:element(3, Map) - 1,
Ret_fn
);
Position@2 ->
find_key(
Map,
Key,
Last_position,
Position@2 - 1,
Ret_fn
)
end
end
end.
-spec ret_value(integer(), GDV) -> GDV.
ret_value(_, Value) ->
Value.
-spec ret_exists(integer(), any()) -> boolean().
ret_exists(_, _) ->
true.
-spec ret_index_and_value(integer(), GDX) -> {integer(), GDX}.
ret_index_and_value(Index, Value) ->
{Index, Value}.
-spec prepend_none(integer(), list(gleam@option:option(entry(GEB)))) -> list(gleam@option:option(entry(GEB))).
prepend_none(Times, Acc) ->
case Times =< 0 of
true ->
Acc;
false ->
prepend_none(Times - 1, [none | Acc])
end.
-spec do_repeat(GEG, integer(), list(GEG)) -> list(GEG).
do_repeat(A, Times, Acc) ->
case Times =< 0 of
true ->
Acc;
false ->
do_repeat(A, Times, [A | Acc])
end.
-spec fix_hash(integer(), integer()) -> integer().
fix_hash(Map_size, Hash) ->
case Map_size of
0 -> 0;
Gleam@denominator -> begin
_pipe = Hash,
gleam@int:absolute_value(_pipe)
end
rem Gleam@denominator
end.
-spec calc_hash(integer(), binary()) -> {integer(), integer()}.
calc_hash(Map_size, Key) ->
Hash_value = glib@hash:hash(Key),
{fix_hash(Map_size, Hash_value), Hash_value}.
-spec get(map_(GCE), binary()) -> gleam@option:option(GCE).
get(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case get_at(erlang:element(2, Map), Hash) of
none ->
none;
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{some, erlang:element(3, E)};
false ->
find_key(Map, Key, case erlang:element(3, Map) of
0 -> 0;
Gleam@denominator -> (Hash + 1) rem Gleam@denominator
end, Hash, fun ret_value/2)
end
end.
-spec contains_key(map_(any()), binary()) -> boolean().
contains_key(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case get_at(erlang:element(2, Map), Hash) of
none ->
false;
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
true;
false ->
gleam@option:unwrap(
find_key(Map, Key, case erlang:element(3, Map) of
0 -> 0;
Gleam@denominator -> (Hash + 1) rem Gleam@denominator
end, Hash, fun ret_exists/2),
false
)
end
end.
-spec remove(map_(GCJ), binary()) -> {gleam@option:option(GCJ), map_(GCJ)}.
remove(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case get_at(erlang:element(2, Map), Hash) of
none ->
{none, Map};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
do_remove(Map, Hash, erlang:element(3, E));
false ->
Item = find_key(Map, Key, case erlang:element(3, Map) of
0 -> 0;
Gleam@denominator -> (Hash + 1) rem Gleam@denominator
end, Hash, fun ret_index_and_value/2),
case Item of
none ->
{none, Map};
{some, {Index, Value}} ->
do_remove(Map, Index, Value)
end
end
end.
-spec list_size(map_(any())) -> integer().
list_size(Map) ->
erlang:length(erlang:element(2, Map)).
-spec full_count(map_(any())) -> integer().
full_count(Map) ->
gleam@list:fold(erlang:element(2, Map), 0, fun(Acc, E) -> case E of
none ->
Acc;
{some, _} ->
Acc + 1
end end).
-spec new_with_size_and_load(integer(), float()) -> map_(any()).
new_with_size_and_load(Size, Load) ->
Load@1 = case (Load >= 1.0) orelse (Load < +0.0) of
true ->
0.75;
false ->
Load
end,
Size@1 = case Size < 1 of
true ->
1;
false ->
Size
end,
{map,
gleam@list:repeat(none, Size@1),
Size@1,
gleam@float:round(Load@1 * 100.0),
0}.
-spec new_with_size(integer()) -> map_(any()).
new_with_size(Size) ->
new_with_size_and_load(Size, 0.75).
-spec new() -> map_(any()).
new() ->
new_with_size(11).
-spec clear(map_(GBS)) -> map_(GBS).
clear(Previous_map) ->
new_with_size_and_load(
erlang:element(3, Previous_map),
gleam@int:to_float(erlang:element(4, Previous_map)) / 100.0
).
-spec optimised_rehash(map_(GDY), integer()) -> map_(GDY).
optimised_rehash(Map, New_size) ->
Entries = begin
_pipe = gleam@list:fold(
erlang:element(2, Map),
[],
fun(Acc, En) -> case En of
{some, Entry} ->
[{calc_hash(New_size, erlang:element(2, Entry)), Entry} |
Acc];
none ->
Acc
end end
),
gleam@list:sort(
_pipe,
fun(I1, I2) ->
{{Hash1, _}, _} = I1,
{{Hash2, _}, _} = I2,
gleam@int:compare(Hash2, Hash1)
end
)
end,
Proc_list = gleam@list:fold(
Entries,
{rehash_data, [], [], New_size, 0},
fun(Acc@1, En@1) ->
{{Hash, _}, _} = En@1,
case erlang:element(4, Acc@1) =:= Hash of
true ->
erlang:setelement(
3,
Acc@1,
[En@1 | erlang:element(3, Acc@1)]
);
false ->
{rehash_data,
[{some, erlang:element(2, En@1)} |
prepend_none(
(erlang:element(4, Acc@1) - Hash) - 1,
erlang:element(2, Acc@1)
)],
erlang:element(3, Acc@1),
Hash,
erlang:element(5, Acc@1) + 1}
end
end
),
It = case erlang:element(4, Proc_list) =:= 0 of
true ->
gleam@iterator:empty();
false ->
gleam@iterator:range(erlang:element(4, Proc_list) - 1, 0)
end,
Res_list = gleam@iterator:fold(
It,
Proc_list,
fun(Acc@2, _) ->
erlang:setelement(2, Acc@2, [none | erlang:element(2, Acc@2)])
end
),
_pipe@1 = erlang:element(3, Res_list),
gleam@list:fold(
_pipe@1,
{map,
erlang:element(2, Res_list),
New_size,
erlang:element(4, Map),
erlang:element(5, Res_list)},
fun(Acc@3, En@2) ->
Entry@1 = erlang:element(2, En@2),
put(Acc@3, erlang:element(2, Entry@1), erlang:element(3, Entry@1))
end
).
-spec put(map_(GCB), binary(), GCB) -> map_(GCB).
put(Map, Key, Value) ->
{Hash, Original_hash} = calc_hash(erlang:element(3, Map), Key),
case get_at(erlang:element(2, Map), Hash) of
{some, E} when erlang:element(2, E) =:= Key ->
{map,
insert_at(
erlang:element(2, Map),
Hash,
{some, {entry, Key, Value}}
),
erlang:element(3, Map),
erlang:element(4, Map),
erlang:element(5, Map)};
_ ->
{Map@1, New_hash} = check_capacity(Map, Original_hash),
New_hash@1 = gleam@option:unwrap(New_hash, Hash),
{Position, Overwrite} = find_gap(
Map@1,
Key,
case erlang:element(3, Map@1) of
0 -> 0;
Gleam@denominator -> (New_hash@1 + 1) rem Gleam@denominator
end,
New_hash@1
),
erlang:setelement(
5,
erlang:setelement(
2,
Map@1,
insert_at(
erlang:element(2, Map@1),
Position,
{some, {entry, Key, Value}}
)
),
case Overwrite of
true ->
erlang:element(5, Map@1);
false ->
erlang:element(5, Map@1) + 1
end
)
end.
-spec check_capacity(map_(GDL), integer()) -> {map_(GDL),
gleam@option:option(integer())}.
check_capacity(Map, Original_hash) ->
case erlang:element(5, Map) >= ((erlang:element(3, Map) * erlang:element(
4,
Map
))
div 100) of
true ->
New_map = optimised_rehash(Map, (erlang:element(3, Map) * 2) + 1),
{New_map,
{some, fix_hash(erlang:element(3, New_map), Original_hash)}};
_ ->
{Map, none}
end.