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(FOX) :: {entry, binary(), FOX}.
-opaque map_(FOY) :: {map,
list(gleam@option:option(entry(FOY))),
integer(),
integer(),
integer()}.
-type rehash_data(FOZ) :: {rehash_data,
list(gleam@option:option(entry(FOZ))),
list({{integer(), integer()}, entry(FOZ)}),
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 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_(FQC)) -> list(FQC).
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_(FQF)) -> list({binary(), FQF}).
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_(FQI), fun((FQI) -> 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(FQK))),
integer(),
gleam@option:option(entry(FQK))
) -> list(gleam@option:option(entry(FQK))).
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_(FQT), integer(), FQT) -> {gleam@option:option(FQT),
map_(FQT)}.
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 gleam@list:at(erlang:element(2, Map), Position) of
{ok, none} ->
{Position, false};
{error, nil} ->
{Position, false};
{ok, {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_(FRD),
binary(),
integer(),
integer(),
fun((integer(), FRD) -> FRF)
) -> gleam@option:option(FRF).
find_key(Map, Key, Last_position, Position, Ret_fn) ->
case gleam@list:at(erlang:element(2, Map), Position) of
{ok, none} ->
none;
{error, nil} ->
none;
{ok, {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(), FRH) -> FRH.
ret_value(_, Value) ->
Value.
-spec ret_exists(integer(), any()) -> boolean().
ret_exists(_, _) ->
true.
-spec ret_index_and_value(integer(), FRJ) -> {integer(), FRJ}.
ret_index_and_value(Index, Value) ->
{Index, Value}.
-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_(FPQ), binary()) -> gleam@option:option(FPQ).
get(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case gleam@list:at(erlang:element(2, Map), Hash) of
{ok, none} ->
none;
{error, nil} ->
none;
{ok, {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 gleam@list:at(erlang:element(2, Map), Hash) of
{ok, none} ->
false;
{error, nil} ->
false;
{ok, {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_(FPV), binary()) -> {gleam@option:option(FPV), map_(FPV)}.
remove(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case gleam@list:at(erlang:element(2, Map), Hash) of
{ok, none} ->
{none, Map};
{error, nil} ->
{none, Map};
{ok, {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) ->
gleam@list: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_(FPG)) -> map_(FPG).
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_(FRK), integer()) -> map_(FRK).
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) ->
gleam@int:compare(
erlang:element(1, (erlang:element(1, I2))),
erlang:element(1, (erlang:element(1, I1)))
)
end
)
end,
Proc_list = gleam@list:fold(
Entries,
{rehash_data, [], [], New_size, 0},
fun(Acc@1, En@1) ->
case erlang:element(4, Acc@1) =:= erlang:element(
1,
(erlang:element(1, En@1))
) of
true ->
erlang:setelement(
3,
Acc@1,
[En@1 | erlang:element(3, Acc@1)]
);
false ->
{rehash_data,
gleam@list:append(
[{some, erlang:element(2, En@1)} |
gleam@list:repeat(
none,
(erlang:element(4, Acc@1) - erlang:element(
1,
(erlang:element(1, En@1))
))
- 1
)],
erlang:element(2, Acc@1)
),
erlang:element(3, Acc@1),
erlang:element(1, (erlang:element(1, En@1))),
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_(FPN), binary(), FPN) -> map_(FPN).
put(Map, Key, Value) ->
{Hash, Original_hash} = calc_hash(erlang:element(3, Map), Key),
case gleam@list:at(erlang:element(2, Map), Hash) of
{ok, {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_(FQX), integer()) -> {map_(FQX),
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.