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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1_db).
-export([dbstart/1,dbnew/1,dbsave/2,dbput/3,dbget/2]).
-export([dbstop/0]).
-record(state, {parent, monitor, includes, table}).
%% Interface
dbstart(Includes) ->
Parent = self(),
undefined = get(?MODULE), %Assertion.
put(?MODULE, spawn_link(fun() -> init(Parent, Includes) end)),
ok.
dbnew(Module) -> req({new, Module}).
dbsave(OutFile, Module) -> cast({save, OutFile, Module}).
dbput(Module, K, V) -> cast({set, Module, K, V}).
dbget(Module, K) -> req({get, Module, K}).
dbstop() -> Resp = req(stop), erase(?MODULE), Resp.
%% Internal functions
req(Request) ->
DbPid = get(?MODULE),
Ref = erlang:monitor(process,DbPid),
get(?MODULE) ! {{Ref, self()}, Request},
receive
{{Ref,?MODULE}, Reply} ->
erlang:demonitor(Ref,[flush]),
Reply;
{'DOWN',Ref,_,_,Info} ->
exit({db_error,Info})
end.
cast(Request) ->
get(?MODULE) ! Request,
ok.
reply({Ref,From}, Response) ->
From ! {{Ref,?MODULE}, Response},
ok.
init(Parent, Includes) ->
MRef = erlang:monitor(process, Parent),
loop(#state{parent = Parent, monitor = MRef, includes = Includes,
table = ets:new(?MODULE, [])}).
loop(#state{parent = Parent, monitor = MRef, table = Table,
includes = Includes} = State) ->
receive
{set, Mod, K2, V} ->
[{_, Modtab}] = ets:lookup(Table, Mod),
ets:insert(Modtab, {K2, V}),
loop(State);
{From, {get, Mod, K2}} ->
Result = case ets:lookup(Table, Mod) of
[] -> opentab(Table, Mod, Includes);
[{_, Modtab}] -> {ok, Modtab}
end,
case Result of
{ok, Newtab} -> reply(From, lookup(Newtab, K2));
_Error -> reply(From, undefined)
end,
loop(State);
{save, OutFile, Mod} ->
[{_,Mtab}] = ets:lookup(Table, Mod),
ok = ets:tab2file(Mtab, OutFile),
loop(State);
{From, {new, Mod}} ->
[] = ets:lookup(Table, Mod), %Assertion.
ModTableId = ets:new(list_to_atom(lists:concat(["asn1_",Mod])), []),
ets:insert(Table, {Mod, ModTableId}),
reply(From, ok),
loop(State);
{From, stop} ->
reply(From, stopped); %% Nothing to store
{'DOWN', MRef, process, Parent, Reason} ->
exit(Reason)
end.
opentab(Tab, Mod, []) ->
opentab(Tab, Mod, ["."]);
opentab(Tab, Mod, Includes) ->
Base = lists:concat([Mod, ".asn1db"]),
opentab2(Tab, Base, Mod, Includes, ok).
opentab2(_Tab, _Base, _Mod, [], Error) ->
Error;
opentab2(Tab, Base, Mod, [Ih|It], _Error) ->
File = filename:join(Ih, Base),
case ets:file2tab(File) of
{ok, Modtab} ->
ets:insert(Tab, {Mod, Modtab}),
{ok, Modtab};
NewErr ->
opentab2(Tab, Base, Mod, It, NewErr)
end.
lookup(Tab, K) ->
case ets:lookup(Tab, K) of
[] -> undefined;
[{K,V}] -> V
end.
|