Skip to content

Commit

Permalink
Merge pull request #195 from rvirding/private-data
Browse files Browse the repository at this point in the history
API for storing and retrieving "private" data
  • Loading branch information
davydog187 authored Dec 20, 2024
2 parents 23fff36 + 2306485 commit 47dbbf7
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 34 deletions.
9 changes: 9 additions & 0 deletions doc/src/luerl.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,12 @@ Decode a term in the Luerl form into its Erlang representation.

#### luerl:decode_list([LuerlTerm], State) -> [Term]
Decode a list of Luerl terms into a list of Erlang representations.

#### luerl:put_private(Key, Term, State) -> State.
Puts a private value under key that is not exposed to the runtime.

#### luerl:get_private(Key, State) -> Term.
Get a private value for the given key.

#### luerl:delete_private(Key, State) -> Term.
Deletes the private value for the given key.
72 changes: 39 additions & 33 deletions include/luerl.hrl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%% Copyright (c) 2013-2019 Robert Virding
%% Copyright (c) 2013-2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand All @@ -25,16 +25,17 @@
envs, %Environment table
usds, %Userdata table
fncs, %Function table
g, %Global table
%%
stk=[], %Current stack
cs=[], %Current call stack
%%
meta=[], %Data type metatables
rand, %Random state
g, %Global table
%%
stk=[], %Current stack
cs=[], %Current call stack
%%
meta=[], %Data type metatables
rand, %Random state
tag, %Unique tag
trace_func=none, %Trace function
trace_data %Trace data
trace_data, %Trace data
private=#{}
}).

%% Table structure.
Expand All @@ -46,10 +47,10 @@
%% Metatables for atomic datatypes.

-record(meta, {nil=nil,
boolean=nil,
number=nil,
string=nil
}).
boolean=nil,
number=nil,
string=nil
}).

%% Frames for the call stack.
%% Call return frame
Expand Down Expand Up @@ -111,10 +112,10 @@
body}). %Code block
-define(IS_LUAFUNC(F), is_record(F, lua_func)).

-record(erl_func,{code}). %Erlang code (fun)
-record(erl_func,{code}). %Erlang code (fun)
-define(IS_ERLFUNC(F), is_record(F, erl_func)).

-record(erl_mfa,{m,f,a}). %Erlang code (MFA)
-record(erl_mfa,{m,f,a}). %Erlang code (MFA)
-define(IS_ERLMFA(F), is_record(F, erl_mfa)).

%% Test if it a function, of either sort.
Expand Down Expand Up @@ -145,28 +146,31 @@
-define(SET_TABLE(N, T, Ts), maps:put(N, T, Ts)).
-define(UPD_TABLE(N, Upd, Ts), maps:update_with(N, Upd, Ts)).
-define(DEL_TABLE(N, Ts), maps:remove(N, Ts)).
-define(CHK_TABLE(N, Ts), maps:is_key(N, Ts)).
-define(FILTER_TABLES(Pred, Ts), maps:filter(Pred, Ts)).
-define(FOLD_TABLES(Fun, Acc, Ts), maps:fold(Fun, Acc, Ts)).
-endif.

-ifdef(TS_USE_ARRAY).
%% Use arrays to handle tables.
%% Use arrays to handle tables. We leave the default value as undefined.
-define(MAKE_TABLE(), array:new()).
-define(GET_TABLE(N, Ar), array:get(N, Ar)).
-define(SET_TABLE(N, T, Ar), array:set(N, T, Ar)).
-define(UPD_TABLE(N, Upd, Ar),
array:set(N, (Upd)(array:get(N, Ar)), Ar)).
array:set(N, (Upd)(array:get(N, Ar)), Ar)).
-define(DEL_TABLE(N, Ar), array:reset(N, Ar)).
-define(CHK_TABLE(N, Ar),
((N >= 0) andalso (array:get(N, Ar) =/= undefined))).
-define(FILTER_TABLES(Pred, Ar),
((fun (___Def) ->
___Fil = fun (___K, ___V) ->
case Pred(___K, ___V) of
true -> ___V;
false -> ___Def
end
end,
array:sparse_map(___Fil, Ar)
end)(array:default(Ar)))).
((fun (___Def) ->
___Fil = fun (___K, ___V) ->
case Pred(___K, ___V) of
true -> ___V;
false -> ___Def
end
end,
array:sparse_map(___Fil, Ar)
end)(array:default(Ar)))).
-define(FOLD_TABLES(Fun, Acc, Ar), array:sparse_foldl(Fun, Acc, Ar)).
-endif.

Expand All @@ -177,6 +181,7 @@
-define(SET_TABLE(N, T, Ts), orddict:store(N, T, Ts)).
-define(UPD_TABLE(N, Upd, Ts), orddict:update(N, Upd, Ts)).
-define(DEL_TABLE(N, Ts), orddict:erase(N, Ts)).
-define(CHK_TABLE(N, Ts), orddict:is_key(N, Ts)).
-define(FILTER_TABLES(Pred, Ts), orddict:filter(Pred, Ts)).
-define(FOLD_TABLES(Fun, Acc, Ts), orddict:fold(Fun, Acc, Ts)).
-endif.
Expand All @@ -188,8 +193,9 @@
-define(SET_TABLE(N, T, Pd), put(N, T)).
-define(UPD_TABLE(N, Upd, Pd), put(N, (Upd)(get(N)))).
-define(DEL_TABLE(N, Pd), erase(N)).
-define(FILTER_TABLES(Pred, Pd), Pd). %This needs work
-define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work
-define(CHK_TABLE(N, Pd), (get(N) =/= undefined)).
-define(FILTER_TABLES(Pred, Pd), Pd). %This needs work
-define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work
-endif.

-ifdef(TS_USE_ETS).
Expand All @@ -198,13 +204,13 @@
-define(GET_TABLE(N, E), ets:lookup_element(E, N, 2)).
-define(SET_TABLE(N, T, E), begin ets:insert(E, {N,T}), E end).
-define(UPD_TABLE(N, Upd, E),
begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}),
E end).
begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}),
E end).
-define(DEL_TABLE(N, E), begin ets:delete(E, N), E end).
-define(FILTER_TABLES(Pred, E), E). %This needs work
-define(FILTER_TABLES(Pred, E), E). %This needs work
-define(FOLD_TABLES(Fun, Acc, E),
ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end,
Acc, E)).
ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end,
Acc, E)).
-endif.

%% Define CATCH to handle deprecated get_stacktrace/0
Expand Down
19 changes: 19 additions & 0 deletions src/Elixir.Luerl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

-module('Elixir.Luerl').

-include("luerl.hrl").

%% Basic user API to luerl.
-export([init/0,gc/1,
load/2,load/3,loadfile/2,loadfile/3,
Expand All @@ -49,6 +51,9 @@
%%Helping with storing VM state
-export([externalize/1,internalize/1]).

%% Storing and retrieving private data
-export([put_private/3,get_private/2,delete_private/2]).

init() ->
luerl:init().

Expand Down Expand Up @@ -180,3 +185,17 @@ externalize(St) ->

internalize(St) ->
luerl:internalize(St).

put_private(St, K, V) ->
luerl:put_private(K, V, St).

get_private(St, Key) ->
maps:get(Key, St#luerl.private, nil).

delete_private(St, K) ->
try
luerl:delete_private(K, St)
catch
error:{badkey, _} ->
St
end.
20 changes: 20 additions & 0 deletions src/luerl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
%% Helping with storing VM state
-export([externalize/1,internalize/1]).

%% Storing and retrieving private data
-export([put_private/3,get_private/2,delete_private/2]).

%% init() -> State.

init() ->
Expand Down Expand Up @@ -506,3 +509,20 @@ externalize(S) ->

internalize(S) ->
luerl_lib_math:internalize(S).

%% put_private(Key, Value, State) ->
%% State.
%% get_private(Key, State) ->
%% Value.
%% delete_private(Key, State) ->
%% Value.
put_private(Key, Value, S) ->
Private = maps:put(Key, Value, S#luerl.private),
S#luerl{private=Private}.

get_private(Key, S) ->
maps:get(Key, S#luerl.private).

delete_private(Key, S) ->
Private = maps:remove(Key, S#luerl.private),
S#luerl{private=Private}.
3 changes: 2 additions & 1 deletion src/luerl.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
rand, %Random state
tag, %Unique tag
trace_func=none, %Trace function
trace_data %Trace data
trace_data, %Trace data
private=#{}
}).

%% Table structure.
Expand Down
25 changes: 25 additions & 0 deletions test/Elixir.Luerl_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
%% Copyright (C) 2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

-module('Elixir.Luerl_tests').

-include_lib("eunit/include/eunit.hrl").

private_test() ->
State1 = 'Elixir.Luerl':init(),
State2 = 'Elixir.Luerl':put_private(State1, secret, <<"mysecret">>),
?assertMatch(<<"mysecret">>, 'Elixir.Luerl':get_private(State2, secret)),
?assertMatch(nil, 'Elixir.Luerl':get_private(State2, missing)),
State3 = 'Elixir.Luerl':delete_private(State2, secret),
?assertMatch(nil, 'Elixir.Luerl':get_private(State3, secret)).
8 changes: 8 additions & 0 deletions test/luerl_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ invalid_value_test() ->
State = luerl:init(),
?assertException(error, {badarg, {invalid, value}},
luerl:encode({invalid, value}, State)).

private_test() ->
State1 = luerl:init(),
State2 = luerl:put_private(secret, <<"mysecret">>, State1),
?assertMatch(<<"mysecret">>, luerl:get_private(secret, State2)),
?assertException(error, {badkey, missing}, luerl:get_private(missing, State2)),
State3 = luerl:delete_private(secret, State2),
?assertException(error, {badkey, secret}, luerl:get_private(secret, State3)).

0 comments on commit 47dbbf7

Please sign in to comment.