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 + -
显示快捷键?