summaryrefslogtreecommitdiff
path: root/src/ts.erl
blob: 841fdd7e708a9a9fa1e2d7d72b9226c759a23cf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
% tree storage
-module(ts).
-include_lib("eunit/include/eunit.hrl").
-export_type([tree_store/0]).
-export([new/0, store/3, append/3, truncate/2, retrieve/2, retrieve_hash/2, delete/2]).

%% FIXME: Keep the arrays in an array instead of in a map? Or maybe an
%% array of binaries? Hashes do have fixed lenght.
-record(tree_store, {arrays :: map()}). % Map of arrays, indexed by layer.
-type tree_store() :: #tree_store{}.

new() ->
    #tree_store{arrays = #{}}.

-spec store(tree_store(), tuple(), binary()) -> tree_store().
store(S = #tree_store{arrays = Arrays}, {I, R}, Hash) ->
    {A, NewArrays} = get_array(R, Arrays),
    Index = case I of
                append -> array:size(A);
                N -> N
            end,
    S#tree_store{arrays = maps:put(R, array:set(Index, Hash, A), NewArrays)}.

-spec retrieve(tree_store(), tuple()) -> {tuple(), binary()}.
retrieve(#tree_store{arrays = Arrays}, {I, R}) ->
    {{I, R},
     case maps:get(R, Arrays, undefined) of
         undefined -> undefined;
         A -> array:get(I, A)
     end}.

-spec retrieve_hash(tree_store(), tuple()) -> binary().
retrieve_hash(S, IR) ->
    element(2, retrieve(S, IR)).

-spec delete(tree_store(), tuple()) -> tree_store().
delete(S, {I, R}) ->
    {A, NewArrays} = get_array(R, S#tree_store.arrays),
    Index = case I of
                last -> array:size(A) - 1;
                N -> N
            end,
    S#tree_store{arrays = maps:put(R,
                                   array:reset(Index, A),
                                   NewArrays)}.
append(S, R, Hash) ->
    store(S, {append, R}, Hash).

truncate(S, R) ->
    delete(S, {last, R}).

%% Private
get_array(Layer, Arrays) ->
    case maps:is_key(Layer, Arrays) of
        true -> {maps:get(Layer, Arrays), Arrays} ;
        false ->
            NewArray = array:new(),
            {NewArray, maps:put(Layer, NewArray, Arrays)}
    end.

%%%%%%%%%%%%%%%%%%%%
%% Testing ts.
-define(TEST_VECTOR, {{{0,0}, <<00>>},
                      {{0,1}, <<01>>},
                      {{1,0}, <<10>>}}).
store_test_() ->
    T = ?TEST_VECTOR,
    S = new(),
    S0 = store(S, {0,0}, <<00>>),
    S1 = store(S0, {0,1}, <<01>>),
    S2 = store(S1, {1,0}, <<10>>),
    [?_assertEqual(retrieve(S2, {1,0}), element(3, T)),
     ?_assertEqual(retrieve(S2, {0,1}), element(2, T)),
     ?_assertEqual(retrieve(S2, {0,0}), element(1, T)),
     ?_assertEqual(retrieve_hash(S2, {3,0}), undefined),
     ?_assertEqual(retrieve_hash(S1, {1,0}), undefined)
    ].