dets.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,178 行 · 第 1/5 页
ERL
2,178 行
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id $%%-module(dets).%% Disk based linear hashing lookup dictionary.%% Public.-export([all/0, bchunk/2, close/1, delete/2, delete_all_objects/1, delete_object/2, first/1, foldl/3, foldr/3, from_ets/2, info/1, info/2, init_table/2, init_table/3, insert/2, insert_new/2, is_compatible_bchunk_format/2, is_dets_file/1, lookup/2, match/1, match/2, match/3, match_delete/2, match_object/1, match_object/2, match_object/3, member/2, next/2, open_file/1, open_file/2, pid2name/1, repair_continuation/2, safe_fixtable/2, select/1, select/2, select/3, select_delete/2, slot/2, sync/1, table/1, table/2, to_ets/2, traverse/2, update_counter/3]).%% Server export.-export([start/0, stop/0]).%% To be used by Mnesia only.-export([fixtable/2]).%% Internal exports.-export([istart_link/1, init/2, internal_open/3, add_user/3, internal_close/1, remove_user/2, system_continue/3, system_terminate/4, system_code_change/4]).%% Debug.-export([file_info/1, fsck/1, fsck/2, get_head_field/2, view/1, where/2, verbose/0, verbose/1 ]).%% Not documented, or not ready for publication.-export([lookup_keys/2]).-compile({inline, [{einval,2},{badarg,2},{undefined,1}, {badarg_exit,2},{lookup_reply,2}]}).-include_lib("kernel/include/file.hrl").-include("dets.hrl").%%% This is the implementation of the mnesia file storage. Each (non%%% ram-copy) table is maintained in a corresponding .DAT file. The%%% dat file is organized as a segmented linear hashlist. The head of%%% the file with the split indicator, size etc is held in ram by the%%% server at all times.%%%%%% The parts specific for formats up to and including 8(c) are%%% implemented in dets_v8.erl, parts specific for format 9 are%%% implemented in dets_v9.erl.%% The method of hashing is the so called linear hashing algorithm%% with segments. %%%% Linear hashing:%%%% - n indicates next bucket to split (initially zero); %% - m is the size of the hash table %% - initially next = m and n = 0%% %% - to insert: %% - hash = key mod m %% - if hash < n then hash = key mod 2m %% - when the number of objects exceeds the initial size%% of the hash table, each insertion of an object%% causes bucket n to be split:%% - add a new bucket to the end of the table %% - redistribute the contents of bucket n %% using hash = key mod 2m %% - increment n %% - if n = m then m = 2m, n = 0 %% - to search: %% hash = key mod m %% if hash < n then hash = key mod 2m %% do linear scan of the bucket %% %%% If a file error occurs on a working dets file, update_mode is set%%% to the error tuple. When in 'error' mode, the free lists are not%%% written, and a repair is forced next time the file is opened.-record(dets_cont, { what, % object | bindings | select | bchunk no_objs, % requested number of objects: default | integer() > 0 bin, % small chunk not consumed, or 'eof' at end-of-file alloc, % the part of the file not yet scanned, mostly a binary tab, match_program % true | compiled_match_spec() | undefined }).-record(open_args, { file, type, keypos, repair, min_no_slots, max_no_slots, ram_file, delayed_write, auto_save, access, version }).-define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).-define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).-define(PATTERN_TO_TRUE_MATCH_SPEC(Pat), [{Pat,[],[true]}]).%%-define(DEBUGM(X, Y), io:format(X, Y)).-define(DEBUGM(X, Y), true).%%-define(DEBUGF(X,Y), io:format(X, Y)).-define(DEBUGF(X,Y), void).%%-define(PROFILE(C), C).-define(PROFILE(C), void).%%%----------------------------------------------------------------------%%% API%%%----------------------------------------------------------------------add_user(Pid, Tab, Args) -> req(Pid, {add_user, Tab, Args}). all() -> dets_server:all().bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]);bchunk(Tab, #dets_cont{bin = eof, tab = Tab}) -> '$end_of_table';bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> badarg(treq(Tab, {bchunk, State}), [Tab, State]);bchunk(Tab, Term) -> erlang:error(badarg, [Tab, Term]).close(Tab) -> case dets_server:close(Tab) of badarg -> % Should not happen. {error, not_owner}; % Backwards compatibility... Reply -> Reply end.delete(Tab, Key) -> badarg(treq(Tab, {delete_key, [Key]}), [Tab, Key]).delete_all_objects(Tab) -> case treq(Tab, delete_all_objects) of badarg -> erlang:error(badarg, [Tab]); fixed -> match_delete(Tab, '_'); Reply -> Reply end.delete_object(Tab, O) -> badarg(treq(Tab, {delete_object, [O]}), [Tab, O]).%% Given a filename, fsck it. Debug.fsck(Fname) -> fsck(Fname, default).fsck(Fname, Version) -> catch begin {ok, Fd, FH} = read_file_header(Fname, read, false), ?DEBUGF("FileHeader: ~p~n", [FH]), case (FH#fileheader.mod):check_file_header(FH, Fd) of {error, not_closed} -> fsck(Fd, make_ref(), Fname, FH, default, default, Version); {ok, _Head, _Extra} -> fsck(Fd, make_ref(), Fname, FH, default, default, Version); Error -> Error end end.first(Tab) -> badarg_exit(treq(Tab, first), [Tab]).fixtable(Tab, Bool) when Bool; not Bool -> badarg(treq(Tab, {fixtable, Bool}), [Tab, Bool]);fixtable(Tab, Term) -> erlang:error(badarg, [Tab, Term]).foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab).foldl(Fun, Acc, Tab) -> Ref = make_ref(), do_traverse(Fun, Acc, Tab, Ref).from_ets(DTab, ETab) -> ets:safe_fixtable(ETab, true), Spec = ?PATTERN_TO_OBJECT_MATCH_SPEC('_'), LC = ets:select(ETab, Spec, 100), InitFun = from_ets_fun(LC, ETab), Reply = treq(DTab, {initialize, InitFun, term, default}), ets:safe_fixtable(ETab, false), case Reply of {thrown, Thrown} -> throw(Thrown); Else -> badarg(Else, [DTab, ETab]) end.from_ets_fun(LC, ETab) -> fun(close) -> ok; (read) when LC =:= '$end_of_table' -> end_of_input; (read) -> {L, C} = LC, {L, from_ets_fun(ets:select(C), ETab)} end.info(Tab) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> undefined; Pid -> undefined(req(Pid, info)) end.info(Tab, owner) -> case catch dets_server:get_pid(Tab) of Pid when is_pid(Pid) -> Pid; _ -> undefined end;info(Tab, users) -> % undocumented case dets_server:users(Tab) of [] -> undefined; Users -> Users end;info(Tab, Tag) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> undefined; Pid -> undefined(req(Pid, {info, Tag})) end.init_table(Tab, InitFun) -> init_table(Tab, InitFun, []).init_table(Tab, InitFun, Options) when is_function(InitFun) -> case options(Options, [format, min_no_slots]) of {badarg,_} -> erlang:error(badarg, [Tab, InitFun, Options]); [Format, MinNoSlots] -> case treq(Tab, {initialize, InitFun, Format, MinNoSlots}) of {thrown, Thrown} -> throw(Thrown); Else -> badarg(Else, [Tab, InitFun, Options]) end end;init_table(Tab, InitFun, Options) -> erlang:error(badarg, [Tab, InitFun, Options]).insert(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert, Objs}), [Tab, Objs]);insert(Tab, Obj) -> badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]).insert_new(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]);insert_new(Tab, Obj) -> badarg(treq(Tab, {insert_new, [Obj]}), [Tab, Obj]).internal_close(Pid) -> req(Pid, close).internal_open(Pid, Ref, Args) -> req(Pid, {internal_open, Ref, Args}).is_compatible_bchunk_format(Tab, Term) -> badarg(treq(Tab, {is_compatible_bchunk_format, Term}), [Tab, Term]).is_dets_file(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> file:close(Fd), FH#fileheader.cookie =:= ?MAGIC; {error, {tooshort, _}} -> false; {error, {not_a_dets_file, _}} -> false; Other -> Other end.lookup(Tab, Key) -> badarg(treq(Tab, {lookup_keys, [Key]}), [Tab, Key]).%% Not public.lookup_keys(Tab, Keys) -> case catch lists:usort(Keys) of UKeys when is_list(UKeys), UKeys =/= [] -> badarg(treq(Tab, {lookup_keys, UKeys}), [Tab, Keys]); _Else -> erlang:error(badarg, [Tab, Keys]) end.match(Tab, Pat) -> badarg(safe_match(Tab, Pat, bindings), [Tab, Pat]).match(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). match(State) when State#dets_cont.what =:= bindings -> badarg(chunk_match(State), [State]);match(Term) -> erlang:error(badarg, [Term]).match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]).match_delete(Tab, Pat, What) -> safe_fixtable(Tab, true), case compile_match_spec(What, Pat) of {Spec, MP} -> Proc = dets_server:get_pid(Tab), R = req(Proc, {match_delete_init, MP, Spec}), do_match_delete(Tab, Proc, R, What, 0); badarg -> badarg end.do_match_delete(Tab, _Proc, {done, N1}, select, N) -> safe_fixtable(Tab, false), N + N1;do_match_delete(Tab, _Proc, {done, _N1}, _What, _N) -> safe_fixtable(Tab, false), ok;do_match_delete(Tab, Proc, {cont, State, N1}, What, N) -> do_match_delete(Tab, Proc, req(Proc, {match_delete, State}), What, N+N1);do_match_delete(Tab, _Proc, Error, _What, _N) -> safe_fixtable(Tab, false), Error.match_object(Tab, Pat) -> badarg(safe_match(Tab, Pat, object), [Tab, Pat]).match_object(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). match_object(State) when State#dets_cont.what =:= object -> badarg(chunk_match(State), [State]);match_object(Term) -> erlang:error(badarg, [Term]).member(Tab, Key) -> badarg(treq(Tab, {member, Key}), [Tab, Key]). next(Tab, Key) -> badarg_exit(treq(Tab, {next, Key}), [Tab, Key]).%% Assuming that a file already exists, open it with the%% parameters as already specified in the file itself.%% Return a ref leading to the file.open_file(File) -> case dets_server:open_file(to_list(File)) of badarg -> % Should not happen. erlang:error(dets_process_died, [File]); Reply -> einval(Reply, [File]) end.open_file(Tab, Args) when is_list(Args) -> case catch defaults(Tab, Args) of OpenArgs when is_record(OpenArgs, open_args) -> case dets_server:open_file(Tab, OpenArgs) of badarg -> % Should not happen. erlang:error(dets_process_died, [Tab, Args]); Reply ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?