Packages
macula
0.7.4
7.1.0
7.0.0
6.0.0
5.2.2
5.2.1
5.2.0
5.1.0
5.0.0
4.8.0
4.7.1
4.7.0
4.6.0
4.5.0
4.4.10
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.1
4.3.0
4.2.9
4.2.8
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.16.0
3.15.3
3.15.2
3.15.1
3.14.0
3.13.0
3.12.1
3.12.0
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.9.0
3.8.0
3.7.0
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
2.0.0
1.5.2
1.5.1
1.4.30
1.4.29
1.4.28
1.4.27
1.4.26
1.4.25
1.4.24
1.4.23
1.4.22
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.0
1.1.0
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.48.6
0.48.5
0.48.4
0.48.3
0.48.2
0.48.1
0.48.0
0.47.1
0.47.0
0.46.3
0.46.1
0.46.0
0.45.3
0.45.2
0.45.1
0.45.0
0.44.2
0.44.1
0.44.0
0.43.3
0.43.2
0.43.1
0.43.0
0.42.9
0.42.8
0.42.7
0.42.6
0.42.5
0.42.4
0.42.3
0.42.2
0.42.1
0.42.0
0.41.1
0.41.0
0.40.1
0.40.0
0.39.9
0.39.8
0.39.7
0.39.6
0.39.5
0.39.4
0.39.3
0.39.2
0.39.1
0.39.0
0.38.8
0.38.7
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.7
0.37.6
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.36.6
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.4
0.35.3
0.35.2
0.35.1
0.35.0
0.34.1
0.34.0
0.33.1
0.33.0
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.9
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.10
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.0
0.28.3
0.28.2
0.28.1
0.28.0
0.27.1
0.27.0
0.26.1
0.26.0
0.25.6
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.6
0.24.5
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.12
0.22.11
0.22.10
0.22.9
0.22.8
0.22.7
0.22.6
0.22.5
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.7
0.21.6
0.21.5
0.21.4
0.21.2
0.21.1
0.21.0
0.20.25
0.20.24
0.20.23
0.20.22
0.20.21
0.20.20
0.20.19
0.20.18
0.20.17
0.20.16
0.20.15
0.20.14
0.20.13
0.20.12
0.20.11
0.20.10
0.20.9
0.20.8
0.20.7
0.20.6
0.20.5
0.20.3
0.20.2
0.20.1
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.12.6
0.12.5
0.12.3
0.11.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.30
0.7.29
0.7.28
0.7.27
0.7.26
0.7.25
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise
Current section
Files
Jump to
Current section
Files
src/macula_cache.erl
%%%-------------------------------------------------------------------
%%% @doc
%%% Generic LRU cache implementation.
%%% Provides least-recently-used eviction with configurable max size.
%%% @end
%%%-------------------------------------------------------------------
-module(macula_cache).
%% API
-export([
new/1,
put/3,
put/4,
get/2,
remove/2,
clear/1,
size/1,
max_size/1,
keys/1
]).
%% Types
-type key() :: term().
-type value() :: term().
-type timestamp() :: integer().
-type entry() :: #{
key := key(),
value := value(),
timestamp := timestamp()
}.
-type cache() :: #{
entries := [entry()], % Ordered: head = most recent, tail = oldest
max_size := pos_integer()
}.
-export_type([cache/0, entry/0, key/0, value/0]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Create new cache with max size.
-spec new(pos_integer()) -> cache().
new(MaxSize) when MaxSize > 0 ->
#{
entries => [],
max_size => MaxSize
}.
%% @doc Put entry in cache with current timestamp.
-spec put(cache(), key(), value()) -> cache().
put(Cache, Key, Value) ->
Timestamp = erlang:system_time(millisecond),
put(Cache, Key, Value, Timestamp).
%% @doc Put entry in cache with custom timestamp (for testing).
-spec put(cache(), key(), value(), timestamp()) -> cache().
put(#{entries := Entries, max_size := MaxSize} = Cache, Key, Value, Timestamp) ->
Entry = #{
key => Key,
value => Value,
timestamp => Timestamp
},
%% Remove existing entry for this key (if any)
EntriesWithoutKey = lists:filter(
fun(E) -> maps:get(key, E) =/= Key end,
Entries
),
%% Add new entry at head (most recent)
NewEntries = [Entry | EntriesWithoutKey],
%% Enforce max size (evict oldest)
FinalEntries = case length(NewEntries) > MaxSize of
true -> lists:sublist(NewEntries, MaxSize);
false -> NewEntries
end,
Cache#{entries => FinalEntries}.
%% @doc Get entry from cache.
%% Returns {ok, Value, UpdatedCache} or not_found.
%% The updated cache has the entry moved to front (LRU).
-spec get(cache(), key()) -> {ok, value(), cache()} | not_found.
get(#{entries := Entries} = Cache, Key) ->
case find_entry(Entries, Key) of
{found, Entry} ->
%% Move to front (most recently used)
Value = maps:get(value, Entry),
Timestamp = erlang:system_time(millisecond),
UpdatedCache = put(Cache, Key, Value, Timestamp),
{ok, Value, UpdatedCache};
not_found ->
not_found
end.
%% @doc Remove entry from cache.
-spec remove(cache(), key()) -> cache().
remove(#{entries := Entries} = Cache, Key) ->
NewEntries = lists:filter(
fun(E) -> maps:get(key, E) =/= Key end,
Entries
),
Cache#{entries => NewEntries}.
%% @doc Clear all entries.
-spec clear(cache()) -> cache().
clear(Cache) ->
Cache#{entries => []}.
%% @doc Get number of entries.
-spec size(cache()) -> non_neg_integer().
size(#{entries := Entries}) ->
length(Entries).
%% @doc Get max size.
-spec max_size(cache()) -> pos_integer().
max_size(#{max_size := MaxSize}) ->
MaxSize.
%% @doc Get all keys in cache (most recent first).
-spec keys(cache()) -> [key()].
keys(#{entries := Entries}) ->
[maps:get(key, E) || E <- Entries].
%%%===================================================================
%%% Internal Functions
%%%===================================================================
%% @doc Find entry by key.
-spec find_entry([entry()], key()) -> {found, entry()} | not_found.
find_entry([], _Key) ->
not_found;
find_entry([Entry | Rest], Key) ->
case maps:get(key, Entry) of
Key -> {found, Entry};
_ -> find_entry(Rest, Key)
end.