1%% This file is part of khash released under the MIT license.
2%% See the LICENSE file for more information.
3%% Copyright 2013 Cloudant, Inc <support@cloudant.com>
4
5-module(khash).
6-on_load(init/0).
7
8
9-export([
10    new/0,
11    new/1,
12    from_list/1,
13    from_list/2,
14    to_list/1,
15    clear/1,
16    lookup/2,
17    get/2,
18    get/3,
19    put/3,
20    del/2,
21    size/1,
22    iter/1,
23    iter_next/1,
24    fold/3
25]).
26
27
28-define(NOT_LOADED, not_loaded(?LINE)).
29
30
31-type kv() :: {any(), any()}.
32-type khash() :: term().
33-type khash_iter() :: term().
34-type option() :: [].
35
36
37-spec new() -> {ok, khash()}.
38new() ->
39    new([]).
40
41
42-spec new([option()]) -> {ok, khash()}.
43new(_Options) ->
44    ?NOT_LOADED.
45
46
47-spec from_list([kv()]) -> {ok, khash()}.
48from_list(KVList) ->
49    from_list(KVList, []).
50
51
52-spec from_list([kv()], [option()]) -> {ok, khash()}.
53from_list(KVList, Options) ->
54    {ok, Hash} = ?MODULE:new(Options),
55    lists:foreach(fun({Key, Val}) ->
56        ?MODULE:put(Hash, Key, Val)
57    end, KVList),
58    {ok, Hash}.
59
60
61-spec to_list(khash()) -> [kv()].
62to_list(_Hash) ->
63    ?NOT_LOADED.
64
65
66-spec clear(khash()) -> ok.
67clear(_Hash) ->
68    ?NOT_LOADED.
69
70
71-spec lookup(khash(), any()) -> {value, any()} | not_found.
72lookup(Hash, Key) ->
73    lookup_int(Hash, erlang:phash2(Key), Key).
74
75
76-spec get(khash(), any()) -> any().
77get(Hash, Key) ->
78    get(Hash, Key, undefined).
79
80
81-spec get(khash(), any(), any()) -> any().
82get(Hash, Key, Default) ->
83    get_int(Hash, erlang:phash2(Key), Key, Default).
84
85
86-spec put(khash(), any(), any()) -> ok.
87put(Hash, Key, Value) ->
88    put_int(Hash, erlang:phash2(Key), Key, Value).
89
90
91-spec del(khash(), any()) -> ok.
92del(Hash, Key) ->
93    del_int(Hash, erlang:phash2(Key), Key).
94
95
96-spec size(khash()) -> non_neg_integer().
97size(_Hash) ->
98    ?NOT_LOADED.
99
100
101-spec iter(khash()) -> {ok, khash_iter()}.
102iter(_Hash) ->
103    ?NOT_LOADED.
104
105
106-spec iter_next(khash_iter()) ->
107        kv() | end_of_table | {error, expired_iterator}.
108iter_next(_Iter) ->
109    ?NOT_LOADED.
110
111
112-spec fold(khash(), fun(), any()) -> any().
113fold(Hash, FoldFun, Acc) ->
114    {ok, Iter} = ?MODULE:iter(Hash),
115    fold_int(Iter, FoldFun, Acc).
116
117
118fold_int(Iter, FoldFun, Acc) ->
119    case ?MODULE:iter_next(Iter) of
120        {Key, Value} ->
121            NewAcc = FoldFun(Key, Value, Acc),
122            fold_int(Iter, FoldFun, NewAcc);
123        end_of_table ->
124            Acc
125    end.
126
127
128init() ->
129    PrivDir = case code:priv_dir(?MODULE) of
130        {error, _} ->
131            EbinDir = filename:dirname(code:which(?MODULE)),
132            AppPath = filename:dirname(EbinDir),
133            filename:join(AppPath, "priv");
134        Path ->
135            Path
136    end,
137    erlang:load_nif(filename:join(PrivDir, "khash"), 0).
138
139
140lookup_int(_Hash, _HashValue, _Key) ->
141    ?NOT_LOADED.
142
143
144get_int(_Hash, _HashValue, _Key, _Default) ->
145    ?NOT_LOADED.
146
147
148put_int(_Hash, _HashValue, _Key, _Value) ->
149    ?NOT_LOADED.
150
151
152del_int(_Hash, _HashValue, _Key) ->
153    ?NOT_LOADED.
154
155
156not_loaded(Line) ->
157    erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
158