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]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Maps are a structure similar to dict in that they map keys to values.\n"
" Duplicate keys cannot exist and a key can map to at most one value\n"
" \n"
" The Keys are strings, values can be any type but values must be of the same type\n"
" \n"
" Maps are unordered\n"
).
-type entry(HWP) :: {entry, binary(), HWP}.
-opaque map_(HWQ) :: {map,
glib@treelist:tree_list(gleam@option:option(entry(HWQ))),
integer(),
integer(),
integer(),
boolean()}.
-file("src/glib/map.gleam", 115).
?DOC(
" Determines the size of the map, i.e. the number of key/values\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> size\n"
" // -> 0\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> size\n"
" // -> 1\n"
" ```\n"
).
-spec size(map_(any())) -> integer().
size(Map) ->
erlang:element(5, Map).
-file("src/glib/map.gleam", 97).
?DOC(
" Determines whether the map is empty, i.e. contains no key/values\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> is_empty\n"
" // -> True\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> is_empty\n"
" // -> False\n"
" ```\n"
).
-spec is_empty(map_(any())) -> boolean().
is_empty(Map) ->
size(Map) =:= 0.
-file("src/glib/map.gleam", 306).
?DOC(
" Returns a list of the keys contained in the map\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> keys\n"
" // -> []\n"
" ```\n"
" \n"
" ``` gleam\n"
" new() |> put(\"key\", \"value\") |> keys\n"
" // -> [\"key\"]\n"
" ```\n"
).
-spec keys(map_(any())) -> list(binary()).
keys(Map) ->
_pipe = glib@treelist:to_iterator(erlang:element(2, Map)),
_pipe@1 = gleam@yielder:filter_map(_pipe, fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, erlang:element(2, En)}
end end),
gleam@yielder:to_list(_pipe@1).
-file("src/glib/map.gleam", 331).
?DOC(
" Returns a list of the values contained in the map\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> values\n"
" // -> []\n"
" ```\n"
" \n"
" ``` gleam\n"
" new() |> put(\"key\", \"value\") |> values\n"
" // -> [\"value\"]\n"
" ```\n"
).
-spec values(map_(HYH)) -> list(HYH).
values(Map) ->
_pipe = glib@treelist:to_iterator(erlang:element(2, Map)),
_pipe@1 = gleam@yielder:filter_map(_pipe, fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, erlang:element(3, En)}
end end),
gleam@yielder:to_list(_pipe@1).
-file("src/glib/map.gleam", 356).
?DOC(
" Returns a list of the tuples #(key, value) contained in the map\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> entries\n"
" // -> []\n"
" ```\n"
" \n"
" ``` gleam\n"
" new() |> put(\"key\", \"value\") |> entries\n"
" // -> [#(\"key\", \"value\")]\n"
" ```\n"
).
-spec entries(map_(HYK)) -> list({binary(), HYK}).
entries(Map) ->
_pipe = glib@treelist:to_iterator(erlang:element(2, Map)),
_pipe@1 = gleam@yielder:filter_map(_pipe, fun(E) -> case E of
none ->
{error, nil};
{some, En} ->
{ok, {erlang:element(2, En), erlang:element(3, En)}}
end end),
gleam@yielder:to_list(_pipe@1).
-file("src/glib/map.gleam", 382).
?DOC(
" Returns a string representation of the passed map\n"
" Requires a value_to_string fn to generate the output\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> to_string(fn(v) { v })\n"
" // -> {}\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> to_string(fn(v) { v })\n"
" // -> {\"key\": \"value\"}\n"
" ```\n"
).
-spec to_string(map_(HYN), fun((HYN) -> binary())) -> {ok, binary()} |
{error, nil}.
to_string(Map, Value_to_string) ->
{ok,
<<<<"{"/utf8,
(gleam@string:join(
begin
_pipe = glib@treelist:to_iterator(
erlang:element(2, Map)
),
_pipe@1 = gleam@yielder:filter_map(
_pipe,
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
),
gleam@yielder:to_list(_pipe@1)
end,
<<","/utf8>>
))/binary>>/binary,
"}"/utf8>>}.
-file("src/glib/map.gleam", 404).
-spec do_remove(map_(HYR), integer(), HYR) -> {ok,
{gleam@option:option(HYR), map_(HYR)}} |
{error, nil}.
do_remove(Map, Index, Value) ->
gleam@result:'try'(
glib@treelist:set(erlang:element(2, Map), Index, none),
fun(Entry) ->
New_map = begin
_record = Map,
{map,
Entry,
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, Map) - 1,
erlang:element(6, _record)}
end,
{ok, {{some, Value}, New_map}}
end
).
-file("src/glib/map.gleam", 438).
-spec find_gap(map_(any()), binary(), integer(), integer()) -> {ok,
{integer(), boolean()}} |
{error, nil}.
find_gap(Map, Key, Last_position, Position) ->
gleam@result:'try'(
glib@treelist:get(erlang:element(2, Map), Position),
fun(Entry) -> case Entry of
none ->
{ok, {Position, false}};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{ok, {Position, true}};
false ->
case Position of
Position@1 when Position@1 =:= Last_position ->
{ok, {-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 end
).
-file("src/glib/map.gleam", 472).
-spec find_key(
map_(HZH),
binary(),
integer(),
integer(),
fun((integer(), HZH) -> HZJ)
) -> {ok, gleam@option:option(HZJ)} | {error, nil}.
find_key(Map, Key, Last_position, Position, Ret_fn) ->
gleam@result:'try'(
glib@treelist:get(erlang:element(2, Map), Position),
fun(Entry) -> case Entry of
none ->
{ok, none};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{ok, {some, Ret_fn(Position, erlang:element(3, E))}};
false ->
case Position of
Position@1 when Position@1 =:= Last_position ->
{ok, 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 end
).
-file("src/glib/map.gleam", 497).
-spec ret_value(integer(), HZN) -> HZN.
ret_value(_, Value) ->
Value.
-file("src/glib/map.gleam", 501).
-spec ret_exists(integer(), any()) -> boolean().
ret_exists(_, _) ->
true.
-file("src/glib/map.gleam", 505).
-spec ret_index_and_value(integer(), HZP) -> {integer(), HZP}.
ret_index_and_value(Index, Value) ->
{Index, Value}.
-file("src/glib/map.gleam", 622).
-spec prepend_none(integer(), list(gleam@option:option(entry(HZV)))) -> list(gleam@option:option(entry(HZV))).
prepend_none(Times, Acc) ->
case Times =< 0 of
true ->
Acc;
false ->
prepend_none(Times - 1, [none | Acc])
end.
-file("src/glib/map.gleam", 629).
-spec do_repeat(IAA, integer(), list(IAA)) -> list(IAA).
do_repeat(A, Times, Acc) ->
case Times =< 0 of
true ->
Acc;
false ->
do_repeat(A, Times, [A | Acc])
end.
-file("src/glib/map.gleam", 636).
-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.
-file("src/glib/map.gleam", 644).
-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}.
-file("src/glib/map.gleam", 194).
?DOC(
" Retrieves the option wrapped value from the map for the stored key\n"
" \n"
" The key may not exist so then None is returned\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> get(\"key\")\n"
" // -> Ok(\"value\")\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> get(\"non-existent\")\n"
" // -> None\n"
" ```\n"
).
-spec get(map_(HXR), binary()) -> {ok, gleam@option:option(HXR)} | {error, nil}.
get(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
gleam@result:'try'(
glib@treelist:get(erlang:element(2, Map), Hash),
fun(Entry) -> case Entry of
none ->
{ok, none};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
{ok, {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 end
).
-file("src/glib/map.gleam", 225).
?DOC(
" Returns the existence in the map for the stored key\n"
" \n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> contains_key(\"key\")\n"
" // -> True\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> contains_key(\"non-existent\")\n"
" // -> False\n"
" ```\n"
).
-spec contains_key(map_(any()), binary()) -> boolean().
contains_key(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
case glib@treelist:get(erlang:element(2, Map), Hash) of
{error, _} ->
false;
{ok, none} ->
false;
{ok, {some, E}} ->
case erlang:element(2, E) =:= Key of
true ->
true;
false ->
gleam@option:unwrap(
gleam@result: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),
none
),
false
)
end
end.
-file("src/glib/map.gleam", 262).
?DOC(
" Removes the specified key from the map and returns a tuple containing\n"
" the removed option wrapped value or None and the altered map without the\n"
" specified key\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> remove(\"key\")\n"
" // -> #(Some(\"value\"), {})\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", \"value\") |> remove(\"non-existent\")\n"
" // -> #(None, {\"key\": \"value\"})\n"
" ```\n"
).
-spec remove(map_(HXY), binary()) -> {ok, {gleam@option:option(HXY), map_(HXY)}} |
{error, nil}.
remove(Map, Key) ->
{Hash, _} = calc_hash(erlang:element(3, Map), Key),
gleam@result:'try'(
glib@treelist:get(erlang:element(2, Map), Hash),
fun(Entry) -> case Entry of
none ->
{ok, {none, Map}};
{some, E} ->
case erlang:element(2, E) =:= Key of
true ->
do_remove(Map, Hash, erlang:element(3, E));
false ->
gleam@result:'try'(
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
),
fun(Item) -> case Item of
none ->
{ok, {none, Map}};
{some, {Index, Value}} ->
do_remove(Map, Index, Value)
end end
)
end
end end
).
-file("src/glib/map.gleam", 651).
?DOC(
" Returns the internal storage size of the map\n"
" This is mainly for testing use\n"
).
-spec list_size(map_(any())) -> integer().
list_size(Map) ->
glib@treelist:size(erlang:element(2, Map)).
-file("src/glib/map.gleam", 659).
?DOC(
" Returns a count of the number of entries in the map\n"
" This is used for testing to compare against the cached size\n"
" Performs a full iteration of the list incrementing for all Some(_)\n"
" entries\n"
).
-spec full_count(map_(any())) -> integer().
full_count(Map) ->
_pipe = glib@treelist:to_iterator(erlang:element(2, Map)),
gleam@yielder:fold(_pipe, 0, fun(Acc, E) -> case E of
none ->
Acc;
{some, _} ->
Acc + 1
end end).
-file("src/glib/map.gleam", 59).
?DOC(
" Creates an empty map with specified size and loading factor\n"
" load is a value 0->1 (non-inclusive) which specifies a percentage (e.g. 0.5 is 50%)\n"
" at which point the backing list is resized\n"
" This should be kept around 0.6-0.8 to avoid either excessive resizing or\n"
" excessive key hash collisions\n"
).
-spec new_with_size_and_load(integer(), float()) -> {ok, map_(any())} |
{error, nil}.
new_with_size_and_load(Size, Load) ->
Load@1 = case (Load >= 1.0) orelse (Load < +0.0) of
true ->
0.75;
false ->
Load
end * 100.0,
Size@1 = case Size < 1 of
true ->
1;
false ->
Size
end,
gleam@result:'try'(
glib@treelist:repeat(none, Size@1),
fun(Backing_list) ->
{ok, {map, Backing_list, Size@1, erlang:round(Load@1), 0, false}}
end
).
-file("src/glib/map.gleam", 49).
?DOC(
" Creates an empty map with specified size\n"
" The loading factor is set to default\n"
).
-spec new_with_size(integer()) -> {ok, map_(any())} | {error, nil}.
new_with_size(Size) ->
new_with_size_and_load(Size, 0.75).
-file("src/glib/map.gleam", 43).
?DOC(
" Creates an empty map\n"
" The size and loading factor are set to the default\n"
" The size is the starting size for the list that contains the values\n"
" The loading factor is the value 0 -> 1 that determines when the\n"
" list is resized. This is the percentage of the backing list that is filled.\n"
" For example, if the loading factor was 0.5 then when one half of the backing\n"
" list is populated, the next addition to the Map will trigger a resize to\n"
" ensure the map has usable space\n"
).
-spec new() -> {ok, map_(any())} | {error, nil}.
new() ->
new_with_size(11).
-file("src/glib/map.gleam", 76).
?DOC(
" Creates a new empty map with the same sizing/loading properties as the\n"
" passed map\n"
).
-spec clear(map_(HXD)) -> {ok, map_(HXD)} | {error, nil}.
clear(Previous_map) ->
new_with_size_and_load(
erlang:element(3, Previous_map),
erlang:float(erlang:element(4, Previous_map)) / 100.0
).
-file("src/glib/map.gleam", 607).
-spec basic_rehash(map_(HZQ), integer()) -> {ok, map_(HZQ)} | {error, nil}.
basic_rehash(Map, New_size) ->
gleam@result:'try'(
new_with_size_and_load(
New_size,
erlang:float(erlang:element(4, Map)) / 100.0
),
fun(New_map) ->
_pipe = glib@treelist:to_iterator(erlang:element(2, Map)),
gleam@yielder:try_fold(
_pipe,
begin
_record = New_map,
{map,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
true}
end,
fun(Map@1, Entry) -> case Entry of
{some, Entry@1} ->
put(
Map@1,
erlang:element(2, Entry@1),
erlang:element(3, Entry@1)
);
none ->
{ok, Map@1}
end end
)
end
).
-file("src/glib/map.gleam", 140).
?DOC(
" Inserts a value into the map with the given key\n"
" \n"
" Will replace value if key already exists\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", 999) |> to_string(fn(i) { int.to_string(i) })\n"
" // -> {\"key\":999}\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", 999) |> put(\"key2\", 111) |> to_string(fn(i) { int.to_string(i) })\n"
" // -> {\"key\":999, \"key2\":111}\n"
" ```\n"
" \n"
" ```gleam\n"
" new() |> put(\"key\", 999) |> put(\"key\", 123) |> to_string(fn(i) { int.to_string(i) })\n"
" // -> {\"key\":123}\n"
" ```\n"
).
-spec put(map_(HXM), binary(), HXM) -> {ok, map_(HXM)} | {error, nil}.
put(Map, Key, Value) ->
{Hash, Original_hash} = calc_hash(erlang:element(3, Map), Key),
gleam@result:'try'(
glib@treelist:get(erlang:element(2, Map), Hash),
fun(Entry) -> gleam@result:'try'(case Entry of
{some, E} when erlang:element(2, E) =:= Key ->
{ok, {Map, Hash, true}};
_ ->
gleam@result:'try'(
check_capacity(Map, Original_hash),
fun(_use0) ->
{Map@1, New_hash} = _use0,
New_hash@1 = gleam@option:unwrap(New_hash, Hash),
gleam@result:'try'(
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
),
fun(_use0@1) ->
{Entry_pos, Overwrite} = _use0@1,
{ok, {Map@1, Entry_pos, Overwrite}}
end
)
end
)
end, fun(_use0@2) ->
{Map@2, Entry_pos@1, Overwrite@1} = _use0@2,
gleam@result:'try'(
glib@treelist:set(
erlang:element(2, Map@2),
Entry_pos@1,
{some, {entry, Key, Value}}
),
fun(Inner) ->
{ok,
begin
_record = Map@2,
{map,
Inner,
erlang:element(3, _record),
erlang:element(4, _record),
case Overwrite@1 of
true ->
erlang:element(5, Map@2);
false ->
erlang:element(5, Map@2) + 1
end,
erlang:element(6, _record)}
end}
end
)
end) end
).
-file("src/glib/map.gleam", 421).
?DOC(
" Checks whether the current map contains >= load entries\n"
" If so return a tuple containing the new resized map and the new hash of the \n"
" key we are currently processing\n"
" Otherwise just return the a tuple containing the original map and None to signify\n"
" no change to the capacity\n"
).
-spec check_capacity(map_(HYX), integer()) -> {ok,
{map_(HYX), gleam@option:option(integer())}} |
{error, nil}.
check_capacity(Map, Original_hash) ->
case {erlang:element(6, Map),
erlang:element(5, Map) >= ((erlang:element(3, Map) * erlang:element(
4,
Map
))
div 100)} of
{false, true} ->
gleam@result:'try'(
basic_rehash(Map, (erlang:element(3, Map) * 2) + 1),
fun(New_map) ->
{ok,
{begin
_record = New_map,
{map,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
false}
end,
{some,
fix_hash(
erlang:element(3, New_map),
Original_hash
)}}}
end
);
{_, _} ->
{ok, {Map, none}}
end.