Packages
macula
0.42.7
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 = enforce_max_size(NewEntries, MaxSize),
Cache#{entries => FinalEntries}.
%% @private Enforce max size by evicting oldest entries
enforce_max_size(Entries, MaxSize) when length(Entries) > MaxSize ->
lists:sublist(Entries, MaxSize);
enforce_max_size(Entries, _MaxSize) ->
Entries.
%% @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) ->
FindResult = find_entry(Entries, Key),
do_get(FindResult, Cache, Key).
%% @private Entry found - move to front
do_get({found, Entry}, Cache, Key) ->
Value = maps:get(value, Entry),
Timestamp = erlang:system_time(millisecond),
UpdatedCache = put(Cache, Key, Value, Timestamp),
{ok, Value, UpdatedCache};
%% @private Entry not found
do_get(not_found, _Cache, _Key) ->
not_found.
%% @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([#{key := Key} = Entry | _Rest], Key) ->
{found, Entry};
find_entry([_Entry | Rest], Key) ->
find_entry(Rest, Key).