aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src/ets.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src/ets.erl')
-rw-r--r--lib/stdlib/src/ets.erl304
1 files changed, 185 insertions, 119 deletions
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 9f84e3639f..afa914a456 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. 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(ets).
@@ -42,28 +42,20 @@
-export([i/0, i/1, i/2, i/3]).
-%%------------------------------------------------------------------------------
+-export_type([tab/0, tid/0, match_spec/0]).
--type tab() :: atom() | tid().
+%%-----------------------------------------------------------------------------
--type ext_info() :: 'md5sum' | 'object_count'.
--type protection() :: 'private' | 'protected' | 'public'.
--type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'.
+-type tab() :: atom() | tid().
--type table_info() :: {'name', atom()}
- | {'type', type()}
- | {'protection', protection()}
- | {'named_table', boolean()}
- | {'keypos', non_neg_integer()}
- | {'size', non_neg_integer()}
- | {'extended_info', [ext_info()]}
- | {'version', {non_neg_integer(), non_neg_integer()}}.
+%% a similar definition is also in erl_types
+-opaque tid() :: integer().
%% these ones are also defined in erl_bif_types
-type match_pattern() :: atom() | tuple().
--type match_specs() :: [{match_pattern(), [_], [_]}].
+-type match_spec() :: [{match_pattern(), [_], [_]}].
-%%------------------------------------------------------------------------------
+%%-----------------------------------------------------------------------------
%% The following functions used to be found in this module, but
%% are now BIFs (i.e. implemented in C).
@@ -81,6 +73,7 @@
%% insert/2
%% is_compiled_ms/1
%% last/1
+%% member/2
%% next/2
%% prev/2
%% rename/2
@@ -96,11 +89,14 @@
%% select/1
%% select/2
%% select/3
+%% select_count/2
%% select_reverse/1
%% select_reverse/2
%% select_reverse/3
%% select_delete/2
+%% setopts/2
%% update_counter/3
+%% update_element/3
%%
-opaque comp_match_spec() :: any(). %% this one is REALLY opaque
@@ -114,7 +110,9 @@ match_spec_run(List, CompiledMS) ->
| {tab(),integer(),integer(),binary(),list(),integer()}
| {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
--spec repair_continuation(continuation(), match_specs()) -> continuation().
+-spec repair_continuation(Continuation, MatchSpec) -> Continuation when
+ Continuation :: continuation(),
+ MatchSpec :: match_spec().
%% $end_of_table is an allowed continuation in ets...
repair_continuation('$end_of_table', _) ->
@@ -148,7 +146,9 @@ repair_continuation(Untouched = {Table,N1,N2,Bin,L,N3}, MS)
{Table,N1,N2,ets:match_spec_compile(MS),L,N3}
end.
--spec fun2ms(function()) -> match_specs().
+-spec fun2ms(LiteralFun) -> MatchSpec when
+ LiteralFun :: function(),
+ MatchSpec :: match_spec().
fun2ms(ShellFun) when is_function(ShellFun) ->
%% Check that this is really a shell fun...
@@ -172,7 +172,13 @@ fun2ms(ShellFun) when is_function(ShellFun) ->
shell]}})
end.
--spec foldl(fun((_, term()) -> term()), term(), tab()) -> term().
+-spec foldl(Function, Acc0, Tab) -> Acc1 when
+ Function :: fun((Element :: term(), AccIn) -> AccOut),
+ Tab :: tab(),
+ Acc0 :: term(),
+ Acc1 :: term(),
+ AccIn :: term(),
+ AccOut :: term().
foldl(F, Accu, T) ->
ets:safe_fixtable(T, true),
@@ -193,7 +199,13 @@ do_foldl(F, Accu0, Key, T) ->
ets:next(T, Key), T)
end.
--spec foldr(fun((_, term()) -> term()), term(), tab()) -> term().
+-spec foldr(Function, Acc0, Tab) -> Acc1 when
+ Function :: fun((Element :: term(), AccIn) -> AccOut),
+ Tab :: tab(),
+ Acc0 :: term(),
+ Acc1 :: term(),
+ AccIn :: term(),
+ AccOut :: term().
foldr(F, Accu, T) ->
ets:safe_fixtable(T, true),
@@ -214,7 +226,9 @@ do_foldr(F, Accu0, Key, T) ->
ets:prev(T, Key), T)
end.
--spec from_dets(tab(), dets:tab_name()) -> 'true'.
+-spec from_dets(Tab, DetsTab) -> 'true' when
+ Tab :: tab(),
+ DetsTab :: dets:tab_name().
from_dets(EtsTable, DetsTable) ->
case (catch dets:to_ets(DetsTable, EtsTable)) of
@@ -230,7 +244,9 @@ from_dets(EtsTable, DetsTable) ->
erlang:error(Unexpected,[EtsTable,DetsTable])
end.
--spec to_dets(tab(), dets:tab_name()) -> tab().
+-spec to_dets(Tab, DetsTab) -> DetsTab when
+ Tab :: tab(),
+ DetsTab :: dets:tab_name().
to_dets(EtsTable, DetsTable) ->
case (catch dets:from_ets(DetsTable, EtsTable)) of
@@ -246,8 +262,11 @@ to_dets(EtsTable, DetsTable) ->
erlang:error(Unexpected,[EtsTable,DetsTable])
end.
--spec test_ms(tuple(), match_specs()) ->
- {'ok', term()} | {'error', [{'warning'|'error', string()}]}.
+-spec test_ms(Tuple, MatchSpec) -> {'ok', Result} | {'error', Errors} when
+ Tuple :: tuple(),
+ MatchSpec :: match_spec(),
+ Result :: term(),
+ Errors :: [{'warning'|'error', string()}].
test_ms(Term, MS) ->
case erlang:match_spec_test(Term, MS, table) of
@@ -257,7 +276,11 @@ test_ms(Term, MS) ->
Error
end.
--spec init_table(tab(), fun(('read' | 'close') -> term())) -> 'true'.
+-spec init_table(Tab, InitFun) -> 'true' when
+ Tab :: tab(),
+ InitFun :: fun((Arg) -> Res),
+ Arg :: 'read' | 'close',
+ Res :: 'end_of_input' | {Objects :: [term()], InitFun} | term().
init_table(Table, Fun) ->
ets:delete_all_objects(Table),
@@ -282,7 +305,9 @@ init_table_sub(Table, [H|T]) ->
ets:insert(Table, H),
init_table_sub(Table, T).
--spec match_delete(tab(), match_pattern()) -> 'true'.
+-spec match_delete(Tab, Pattern) -> 'true' when
+ Tab :: tab(),
+ Pattern :: match_pattern().
match_delete(Table, Pattern) ->
ets:select_delete(Table, [{Pattern,[],[true]}]),
@@ -290,7 +315,9 @@ match_delete(Table, Pattern) ->
%% Produce a list of tuples from a table
--spec tab2list(tab()) -> [tuple()].
+-spec tab2list(Tab) -> [Object] when
+ Tab :: tab(),
+ Object :: tuple().
tab2list(T) ->
ets:match_object(T, '_').
@@ -329,15 +356,21 @@ do_filter(Tab, Key, F, A, Ack) ->
md5sum = false :: boolean()
}).
--type fname() :: string() | atom().
--type t2f_option() :: {'extended_info', [ext_info()]}.
-
--spec tab2file(tab(), fname()) -> 'ok' | {'error', term()}.
+-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when
+ Tab :: tab(),
+ Filename :: file:name(),
+ Reason :: term().
tab2file(Tab, File) ->
tab2file(Tab, File, []).
--spec tab2file(tab(), fname(), [t2f_option()]) -> 'ok' | {'error', term()}.
+-spec tab2file(Tab, Filename, Options) -> 'ok' | {'error', Reason} when
+ Tab :: tab(),
+ Filename :: file:name(),
+ Options :: [Option],
+ Option :: {'extended_info', [ExtInfo]},
+ ExtInfo :: 'md5sum' | 'object_count',
+ Reason :: term().
tab2file(Tab, File, Options) ->
try
@@ -496,18 +529,24 @@ parse_ft_info_options(_,Malformed) ->
%% Opt := {verify,boolean()}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--type f2t_option() :: {'verify', boolean()}.
-
--spec file2tab(fname()) -> {'ok', tab()} | {'error', term()}.
+-spec file2tab(Filename) -> {'ok', Tab} | {'error', Reason} when
+ Filename :: file:name(),
+ Tab :: tab(),
+ Reason :: term().
file2tab(File) ->
file2tab(File, []).
--spec file2tab(fname(), [f2t_option()]) -> {'ok', tab()} | {'error', term()}.
+-spec file2tab(Filename, Options) -> {'ok', Tab} | {'error', Reason} when
+ Filename :: file:name(),
+ Tab :: tab(),
+ Options :: [Option],
+ Option :: {'verify', boolean()},
+ Reason :: term().
file2tab(File, Opts) ->
try
- {ok,Verify} = parse_f2t_opts(Opts,false),
+ {ok,Verify,TabArg} = parse_f2t_opts(Opts,false,[]),
Name = make_ref(),
{ok, Major, Minor, FtOptions, MD5State, FullHeader, DLContext} =
case disk_log:open([{name, Name},
@@ -535,7 +574,7 @@ file2tab(File, Opts) ->
true ->
ok
end,
- {ok, Tab, HeadCount} = create_tab(FullHeader),
+ {ok, Tab, HeadCount} = create_tab(FullHeader, TabArg),
StrippedOptions =
case Verify of
true ->
@@ -622,14 +661,14 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) ->
end,
{ok,Tab};
{ok,{FinalMD5State,FinalCount,['$end_of_table',LastInfo],_}} ->
- ECount = case lists:keysearch(count,1,LastInfo) of
- {value,{count,N}} ->
+ ECount = case lists:keyfind(count,1,LastInfo) of
+ {count,N} ->
N;
_ ->
false
end,
- EMD5 = case lists:keysearch(md5,1,LastInfo) of
- {value,{md5,M}} ->
+ EMD5 = case lists:keyfind(md5,1,LastInfo) of
+ {md5,M} ->
M;
_ ->
false
@@ -671,15 +710,17 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) ->
{ok,Tab}
end.
-parse_f2t_opts([],Verify) ->
- {ok,Verify};
-parse_f2t_opts([{verify, true}|T],_OV) ->
- parse_f2t_opts(T,true);
-parse_f2t_opts([{verify,false}|T],OV) ->
- parse_f2t_opts(T,OV);
-parse_f2t_opts([Unexpected|_],_) ->
+parse_f2t_opts([],Verify,Tab) ->
+ {ok,Verify,Tab};
+parse_f2t_opts([{verify, true}|T],_OV,Tab) ->
+ parse_f2t_opts(T,true,Tab);
+parse_f2t_opts([{verify,false}|T],OV,Tab) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([{table,Tab}|T],OV,[]) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([Unexpected|_],_,_) ->
throw({unknown_option,Unexpected});
-parse_f2t_opts(Malformed,_) ->
+parse_f2t_opts(Malformed,_,_) ->
throw({malformed_option,Malformed}).
count_mandatory([]) ->
@@ -742,22 +783,21 @@ get_header_data(Name,true) ->
false ->
throw(badfile);
true ->
- Major = case lists:keysearch(major,1,L) of
- {value,{major,Maj}} ->
+ Major = case lists:keyfind(major,1,L) of
+ {major,Maj} ->
Maj;
_ ->
0
end,
- Minor = case lists:keysearch(minor,1,L) of
- {value,{minor,Min}} ->
+ Minor = case lists:keyfind(minor,1,L) of
+ {minor,Min} ->
Min;
_ ->
0
end,
FtOptions =
- case lists:keysearch(extended_info,1,L) of
- {value,{extended_info,I}}
- when is_list(I) ->
+ case lists:keyfind(extended_info,1,L) of
+ {extended_info,I} when is_list(I) ->
#filetab_options
{
object_count =
@@ -786,29 +826,28 @@ get_header_data(Name,true) ->
end;
get_header_data(Name, false) ->
- case wrap_chunk(Name,start,1,false) of
+ case wrap_chunk(Name, start, 1, false) of
{C,[Tup]} when is_tuple(Tup) ->
L = tuple_to_list(Tup),
case verify_header_mandatory(L) of
false ->
throw(badfile);
true ->
- Major = case lists:keysearch(major_version,1,L) of
- {value,{major_version,Maj}} ->
+ Major = case lists:keyfind(major_version, 1, L) of
+ {major_version, Maj} ->
Maj;
_ ->
0
end,
- Minor = case lists:keysearch(minor_version,1,L) of
- {value,{minor_version,Min}} ->
+ Minor = case lists:keyfind(minor_version, 1, L) of
+ {minor_version, Min} ->
Min;
_ ->
0
end,
FtOptions =
- case lists:keysearch(extended_info,1,L) of
- {value,{extended_info,I}}
- when is_list(I) ->
+ case lists:keyfind(extended_info, 1, L) of
+ {extended_info, I} when is_list(I) ->
#filetab_options
{
object_count =
@@ -825,25 +864,26 @@ get_header_data(Name, false) ->
throw(badfile)
end.
-md5_and_convert([],MD5State,Count) ->
+md5_and_convert([], MD5State, Count) ->
{[],MD5State,Count,[]};
-md5_and_convert([H|T],MD5State,Count) when is_binary(H) ->
+md5_and_convert([H|T], MD5State, Count) when is_binary(H) ->
case (catch binary_to_term(H)) of
{'EXIT', _} ->
md5_and_convert(T,MD5State,Count);
- ['$end_of_table',Dat] ->
- {[],MD5State,Count,['$end_of_table',Dat]};
+ ['$end_of_table',_Dat] = L ->
+ {[],MD5State,Count,L};
Term ->
- X = erlang:md5_update(MD5State,H),
- {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T,X,Count+1),
+ X = erlang:md5_update(MD5State, H),
+ {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T, X, Count+1),
{[Term | Rest],NewMD5,NewCount,NewLast}
end.
-scan_for_endinfo([],Count) ->
+
+scan_for_endinfo([], Count) ->
{[],Count,[]};
-scan_for_endinfo([['$end_of_table',Dat]],Count) ->
+scan_for_endinfo([['$end_of_table',Dat]], Count) ->
{['$end_of_table',Dat],Count,[]};
-scan_for_endinfo([Term|T],Count) ->
- {NewLast,NCount,Rest} = scan_for_endinfo(T,Count+1),
+scan_for_endinfo([Term|T], Count) ->
+ {NewLast,NCount,Rest} = scan_for_endinfo(T, Count+1),
{NewLast,NCount,[Term | Rest]}.
load_table(ReadFun, State, Tab) ->
@@ -852,23 +892,32 @@ load_table(ReadFun, State, Tab) ->
[] ->
{ok,NewState};
List ->
- ets:insert(Tab,List),
- load_table(ReadFun,NewState,Tab)
+ ets:insert(Tab, List),
+ load_table(ReadFun, NewState, Tab)
end.
-create_tab(I) ->
- {value, {name, Name}} = lists:keysearch(name, 1, I),
- {value, {type, Type}} = lists:keysearch(type, 1, I),
- {value, {protection, P}} = lists:keysearch(protection, 1, I),
- {value, {named_table, Val}} = lists:keysearch(named_table, 1, I),
- {value, {keypos, Kp}} = lists:keysearch(keypos, 1, I),
- {value, {size, Sz}} = lists:keysearch(size, 1, I),
- try
- Tab = ets:new(Name, [Type, P, {keypos, Kp} | named_table(Val)]),
- {ok, Tab, Sz}
- catch
- _:_ ->
- throw(cannot_create_table)
+create_tab(I, TabArg) ->
+ {name, Name} = lists:keyfind(name, 1, I),
+ {type, Type} = lists:keyfind(type, 1, I),
+ {protection, P} = lists:keyfind(protection, 1, I),
+ {named_table, Val} = lists:keyfind(named_table, 1, I),
+ {keypos, _Kp} = Keypos = lists:keyfind(keypos, 1, I),
+ {size, Sz} = lists:keyfind(size, 1, I),
+ Comp = case lists:keyfind(compressed, 1, I) of
+ {compressed, true} -> [compressed];
+ {compressed, false} -> [];
+ false -> []
+ end,
+ case TabArg of
+ [] ->
+ try
+ Tab = ets:new(Name, [Type, P, Keypos] ++ named_table(Val) ++ Comp),
+ {ok, Tab, Sz}
+ catch _:_ ->
+ throw(cannot_create_table)
+ end;
+ _ ->
+ {ok, TabArg, Sz}
end.
named_table(true) -> [named_table];
@@ -880,7 +929,22 @@ named_table(false) -> [].
%% information
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec tabfile_info(fname()) -> {'ok', [table_info()]} | {'error', term()}.
+-spec tabfile_info(Filename) -> {'ok', TableInfo} | {'error', Reason} when
+ Filename :: file:name(),
+ TableInfo :: [InfoItem],
+ InfoItem :: {'name', atom()}
+ | {'type', Type}
+ | {'protection', Protection}
+ | {'named_table', boolean()}
+ | {'keypos', non_neg_integer()}
+ | {'size', non_neg_integer()}
+ | {'extended_info', [ExtInfo]}
+ | {'version', {Major :: non_neg_integer(),
+ Minor :: non_neg_integer()}},
+ ExtInfo :: 'md5sum' | 'object_count',
+ Type :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set',
+ Protection :: 'private' | 'protected' | 'public',
+ Reason :: term().
tabfile_info(File) when is_list(File) ; is_atom(File) ->
try
@@ -905,9 +969,9 @@ tabfile_info(File) when is_list(File) ; is_atom(File) ->
{value, Val} = lists:keysearch(named_table, 1, FullHeader),
{value, Kp} = lists:keysearch(keypos, 1, FullHeader),
{value, Sz} = lists:keysearch(size, 1, FullHeader),
- Ei = case lists:keysearch(extended_info, 1, FullHeader) of
- {value, Ei0} -> Ei0;
- _ -> {extended_info, []}
+ Ei = case lists:keyfind(extended_info, 1, FullHeader) of
+ false -> {extended_info, []};
+ Ei0 -> Ei0
end,
{ok, [N,Type,P,Val,Kp,Sz,Ei,{version,{Major,Minor}}]}
catch
@@ -917,20 +981,22 @@ tabfile_info(File) when is_list(File) ; is_atom(File) ->
{error,ExReason}
end.
--type qlc__query_handle() :: term(). %% XXX: belongs in 'qlc'
-
--type num_objects() :: 'default' | pos_integer().
--type trav_method() :: 'first_next' | 'last_prev'
- | 'select' | {'select', match_specs()}.
--type table_option() :: {'n_objects', num_objects()}
- | {'traverse', trav_method()}.
-
--spec table(tab()) -> qlc__query_handle().
+-spec table(Tab) -> QueryHandle when
+ Tab :: tab(),
+ QueryHandle :: qlc:query_handle().
table(Tab) ->
table(Tab, []).
--spec table(tab(), table_option() | [table_option()]) -> qlc__query_handle().
+-spec table(Tab, Options) -> QueryHandle when
+ Tab :: tab(),
+ QueryHandle :: qlc:query_handle(),
+ Options :: [Option] | Option,
+ Option :: {'n_objects', NObjects}
+ | {'traverse', TraverseMethod},
+ NObjects :: 'default' | pos_integer(),
+ TraverseMethod :: 'first_next' | 'last_prev'
+ | 'select' | {'select', MatchSpec :: match_spec()}.
table(Tab, Opts) ->
case options(Opts, [traverse, n_objects]) of
@@ -1021,21 +1087,20 @@ options(Option, Keys) ->
options([Option], Keys, []).
options(Options, [Key | Keys], L) when is_list(Options) ->
- V = case lists:keysearch(Key, 1, Options) of
- {value, {n_objects, default}} ->
+ V = case lists:keyfind(Key, 1, Options) of
+ {n_objects, default} ->
{ok, default_option(Key)};
- {value, {n_objects, NObjs}} when is_integer(NObjs),
- NObjs >= 1 ->
+ {n_objects, NObjs} when is_integer(NObjs), NObjs >= 1 ->
{ok, NObjs};
- {value, {traverse, select}} ->
+ {traverse, select} ->
{ok, select};
- {value, {traverse, {select, MS}}} ->
- {ok, {select, MS}};
- {value, {traverse, first_next}} ->
+ {traverse, {select, _MS} = Select} ->
+ {ok, Select};
+ {traverse, first_next} ->
{ok, first_next};
- {value, {traverse, last_prev}} ->
+ {traverse, last_prev} ->
{ok, last_prev};
- {value, {Key, _}} ->
+ {Key, _} ->
badarg;
false ->
Default = default_option(Key),
@@ -1121,7 +1186,8 @@ to_string(X) ->
lists:flatten(io_lib:format("~p", [X])).
%% view a specific table
--spec i(tab()) -> 'ok'.
+-spec i(Tab) -> 'ok' when
+ Tab :: tab().
i(Tab) ->
i(Tab, 40).