diff options
Diffstat (limited to 'lib/stdlib/src')
| -rw-r--r-- | lib/stdlib/src/Makefile | 2 | ||||
| -rw-r--r-- | lib/stdlib/src/dets.erl | 480 | ||||
| -rw-r--r-- | lib/stdlib/src/dets.hrl | 162 | ||||
| -rw-r--r-- | lib/stdlib/src/dets_utils.erl | 26 | ||||
| -rw-r--r-- | lib/stdlib/src/dets_v8.erl | 1594 | ||||
| -rw-r--r-- | lib/stdlib/src/dets_v9.erl | 112 | ||||
| -rw-r--r-- | lib/stdlib/src/stdlib.app.src | 1 | 
7 files changed, 326 insertions, 2051 deletions
| diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 302834f9d0..d6c0ff8d8d 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -51,7 +51,6 @@ MODULES= \  	dets_server \  	dets_sup \  	dets_utils \ -	dets_v8 \  	dets_v9 \  	dict \  	digraph \ @@ -225,7 +224,6 @@ $(EBIN)/beam_lib.beam: ../include/erl_compile.hrl ../../kernel/include/file.hrl  $(EBIN)/dets.beam: dets.hrl ../../kernel/include/file.hrl  $(EBIN)/dets_server.beam: dets.hrl  $(EBIN)/dets_utils.beam: dets.hrl -$(EBIN)/dets_v8.beam: dets.hrl  $(EBIN)/dets_v9.beam: dets.hrl  $(EBIN)/erl_bits.beam: ../include/erl_bits.hrl  $(EBIN)/erl_compile.beam: ../include/erl_compile.hrl ../../kernel/include/file.hrl diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 8ce29f23d3..5bc9475fc8 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -105,9 +105,6 @@  %%% 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.  @@ -140,28 +137,33 @@  %%% 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, -         proc,        % the pid of the Dets process -         match_program % true | compiled_match_spec() | undefined +          what :: 'undefined' | 'bchunk' | 'bindings' | 'object' | 'select', +          no_objs :: 'default' | pos_integer(), % requested number of objects +          bin :: 'eof' | binary(), % small chunk not consumed, +                                  % or 'eof' at end-of-file +          alloc :: binary() % the part of the file not yet scanned +                 | {From :: non_neg_integer(), +                    To :: non_neg_integer, +                    binary()}, +          tab :: tab_name(), +          proc :: 'undefined' | pid(), % the pid of the Dets process +          match_program :: 'true' +                         | 'undefined' +                         | {'match_spec', ets:comp_match_spec()}  	 }).  -record(open_args, { -          file, -          type, -          keypos, -          repair, -          min_no_slots, -	  max_no_slots, -          ram_file, -          delayed_write, -          auto_save, -          access, -          version, -          debug +          file :: list(), +          type :: type(), +          keypos :: keypos(), +          repair :: 'force' | boolean(), +          min_no_slots :: no_slots(), +	  max_no_slots :: no_slots(), +          ram_file :: boolean(), +          delayed_write :: cache_parms(), +          auto_save :: auto_save(), +          access :: access(), +          debug :: boolean()           }).  -define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]). @@ -177,20 +179,13 @@  %%-define(PROFILE(C), C).  -define(PROFILE(C), void). --type access()    :: 'read' | 'read_write'. --type auto_save() :: 'infinity' | non_neg_integer().  -opaque bindings_cont() :: #dets_cont{}.  -opaque cont()    :: #dets_cont{}. --type keypos()    :: pos_integer().  -type match_spec()  :: ets:match_spec().  -type object()    :: tuple(). --type no_slots()  :: non_neg_integer() | 'default'.  -opaque object_cont() :: #dets_cont{}.  -type pattern()   :: atom() | tuple().  -opaque select_cont() :: #dets_cont{}. --type tab_name() :: term(). --type type()      :: 'bag' | 'duplicate_bag' | 'set'. --type version()   :: 8 | 9 | 'default'.  %%% Some further debug code was added in R12B-1 (stdlib-1.15.1):  %%% - there is a new open_file() option 'debug'; @@ -273,19 +268,20 @@ delete_all_objects(Tab) ->  delete_object(Tab, O) ->      badarg(treq(Tab, {delete_object, [O]}), [Tab, O]). +%% Backwards compatibility. +fsck(Fname, _Version) -> +    fsck(Fname). +  %% 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 +      case dets_v9: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); +              fsck(Fd, make_ref(), Fname, FH, default, default); +          {ok, _Head} -> +              fsck(Fd, make_ref(), Fname, FH, default, default);            Error ->                Error        end @@ -372,7 +368,7 @@ info(Tab) ->        Item :: 'access' | 'auto_save' | 'bchunk_format'              | 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory'              | 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file' -            | 'safe_fixed' | 'safe_fixed_monotonic_time' | 'size' | 'type' | 'version', +            | 'safe_fixed' | 'safe_fixed_monotonic_time' | 'size' | 'type',        Value :: term().  info(Tab, owner) -> @@ -640,8 +636,7 @@ open_file(File) ->                  | {'keypos', keypos()}                  | {'ram_file', boolean()}                  | {'repair', boolean() | 'force'} -                | {'type', type()} -                | {'version', version()}, +                | {'type', type()},        Reason :: term().  open_file(Tab, Args) when is_list(Args) -> @@ -674,13 +669,13 @@ remove_user(Pid, From) ->        Continuation2 :: select_cont(),        MatchSpec :: match_spec(). -repair_continuation(#dets_cont{match_program = B}=Cont, MS)  -    when is_binary(B) -> +repair_continuation(#dets_cont{match_program = {match_spec, B}}=Cont, MS) ->      case ets:is_compiled_ms(B) of  	true ->  	    Cont;  	false -> -            Cont#dets_cont{match_program = ets:match_spec_compile(MS)} +            Cont#dets_cont{match_program = {match_spec, +                                            ets:match_spec_compile(MS)}}      end;  repair_continuation(#dets_cont{}=Cont, _MS) ->      Cont; @@ -999,7 +994,9 @@ init_chunk_match(Tab, Pat, What, N, Safe) when is_integer(N), N >= 0;                      case req(Proc, {match, MP, Spec, N, Safe}) of                          {done, L} ->                              {L, #dets_cont{tab = Tab, proc = Proc, -                                           what = What, bin = eof}}; +                                           what = What, bin = eof, +                                           no_objs = default, +                                           alloc = <<>>}};                          {cont, State} ->                              chunk_match(State#dets_cont{what = What,                                                          tab = Tab, @@ -1041,17 +1038,17 @@ chunk_match(#dets_cont{proc = Proc}=State, Safe) ->  do_foldl_bins(Bins, true) ->      foldl_bins(Bins, []); -do_foldl_bins(Bins, MP) -> +do_foldl_bins(Bins, {match_spec, MP}) ->      foldl_bins(Bins, MP, []).  foldl_bins([], Terms) -> -    %% Preserve time order (version 9).  +    %% Preserve time order.      Terms;  foldl_bins([Bin | Bins], Terms) ->          foldl_bins(Bins, [binary_to_term(Bin) | Terms]).  foldl_bins([], _MP, Terms) -> -    %% Preserve time order (version 9). +    %% Preserve time order.      Terms;  foldl_bins([Bin | Bins], MP, Terms) ->      Term = binary_to_term(Bin), @@ -1068,7 +1065,7 @@ compile_match_spec(select, ?PATTERN_TO_OBJECT_MATCH_SPEC('_') = Spec) ->  compile_match_spec(select, Spec) ->      case catch ets:match_spec_compile(Spec) of  	X when is_binary(X) -> -	    {Spec, X}; +	    {Spec, {match_spec, X}};  	_ ->  	    badarg      end; @@ -1091,16 +1088,10 @@ defaults(Tab, Args) ->                             delayed_write = ?DEFAULT_CACHE,                             auto_save = timer:minutes(?DEFAULT_AUTOSAVE),                             access = read_write, -                           version = default,                             debug = false},      Fun = fun repl/2,      Defaults = lists:foldl(Fun, Defaults0, Args), -    case Defaults#open_args.version of -        8 -> -            Defaults#open_args{max_no_slots = default}; -        _ -> -            is_comp_min_max(Defaults) -    end. +    is_comp_min_max(Defaults).  to_list(T) when is_atom(T) -> atom_to_list(T);  to_list(T) -> T. @@ -1131,7 +1122,6 @@ repl({file, File}, Defs) when is_atom(File) ->  repl({keypos, P}, Defs) when is_integer(P), P > 0 ->      Defs#open_args{keypos =P};  repl({max_no_slots, I}, Defs)  -> -    %% Version 9 only.      MaxSlots = is_max_no_slots(I),      Defs#open_args{max_no_slots = MaxSlots};  repl({min_no_slots, I}, Defs)  -> @@ -1147,8 +1137,9 @@ repl({type, T}, Defs) ->      mem(T, [set, bag, duplicate_bag]),      Defs#open_args{type =T};  repl({version, Version}, Defs) -> -    V = is_version(Version), -    Defs#open_args{version = V}; +    %% Backwards compatibility. +    is_version(Version), +    Defs;  repl({debug, Bool}, Defs) ->      %% Not documented.      mem(Bool, [true, false]), @@ -1164,16 +1155,15 @@ is_max_no_slots(default) -> default;  is_max_no_slots(I) when is_integer(I), I > 0, I < 1 bsl 31 -> I.  is_comp_min_max(Defs) -> -    #open_args{max_no_slots = Max, min_no_slots = Min, version = V} = Defs, -    case V of -	_ when Min =:= default -> Defs; -	_ when Max =:= default -> Defs; -	_ -> true = Min =< Max, Defs +    #open_args{max_no_slots = Max, min_no_slots = Min} = Defs, +    if +        Min =:= default -> Defs; +	Max =:= default -> Defs; +	true -> true = Min =< Max, Defs      end. -is_version(default) -> default; -is_version(8) -> 8; -is_version(9) -> 9. +is_version(default) -> true; +is_version(9) -> true.  mem(X, L) ->      case lists:member(X, L) of @@ -1288,17 +1278,23 @@ badarg_exit(Reply, _A) ->  init(Parent, Server) ->      process_flag(trap_exit, true), -    open_file_loop(#head{parent = Parent, server = Server}). - -open_file_loop(Head) ->      %% The Dets server pretends the file is open before      %% internal_open() has been called, which means that unless the      %% internal_open message is applied first, other processes can      %% find the pid by calling dets_server:get_pid() and do things      %% before Head has been initialized properly.      receive -        ?DETS_CALL(From, {internal_open, _Ref, _Args}=Op) -> -            do_apply_op(Op, From, Head, 0) +        ?DETS_CALL(From, {internal_open, Ref, Args}=Op) -> +            try do_internal_open(Parent, Server, From, Ref, Args) of +                Head -> +                    open_file_loop(Head, 0) +            catch +                exit:normal -> +                    exit(normal); +                _:Bad -> +                    bug_found(no_name, Op, Bad, From), +                    exit(Bad) % give up +            end      end.  open_file_loop(Head, N) when element(1, Head#head.update_mode) =:= error -> @@ -1379,28 +1375,7 @@ do_apply_op(Op, From, Head, N) ->          exit:normal ->               exit(normal);          _:Bad ->  -            Name = Head#head.name, -            case dets_utils:debug_mode() of -                true -> -                    %% If stream_op/5 found more requests, this is not -                    %% the last operation. -                    error_logger:format -                      ("** dets: Bug was found when accessing table ~w,~n" -                       "** dets: operation was ~p and reply was ~w.~n" -                       "** dets: Stacktrace: ~w~n", -                       [Name, Op, Bad, erlang:get_stacktrace()]); -                false -> -                    error_logger:format -                      ("** dets: Bug was found when accessing table ~w~n", -                       [Name]) -            end, -            if -                From =/= self() -> -                    From ! {self(), {error, {dets_bug, Name, Op, Bad}}}, -                    ok; -                true -> % auto_save | may_grow | {delayed_write, _} -                    ok -            end, +            bug_found(Head#head.name, Op, Bad, From),              open_file_loop(Head, N)      end. @@ -1408,10 +1383,7 @@ apply_op(Op, From, Head, N) ->      case Op of  	{add_user, Tab, OpenArgs}->              #open_args{file = Fname, type = Type, keypos = Keypos,  -                       ram_file = Ram, access = Access,  -		       version = Version} = OpenArgs, -            VersionOK = (Version =:= default) or  -                        (Head#head.version =:= Version), +                       ram_file = Ram, access = Access} = OpenArgs,  	    %% min_no_slots and max_no_slots are not tested  	    Res = if  		      Tab =:= Head#head.name, @@ -1419,7 +1391,6 @@ apply_op(Op, From, Head, N) ->  		      Head#head.type =:= Type,  		      Head#head.ram_file =:= Ram,  		      Head#head.access =:= Access, -		      VersionOK,  		      Fname =:= Head#head.filename ->  			  ok;  		      true -> @@ -1475,21 +1446,14 @@ apply_op(Op, From, Head, N) ->              From ! {self(), Res},              ok;  	{internal_open, Ref, Args} -> -	    ?PROFILE(ep:do()), -	    case do_open_file(Args, Head#head.parent, Head#head.server,Ref) of -		{ok, H2} ->  -		    From ! {self(), ok}, -		    H2; -		Error ->  -		    From ! {self(), Error}, -		    exit(normal) -	    end; +            do_internal_open(Head#head.parent, Head#head.server, From, +                             Ref, Args);  	may_grow when Head#head.update_mode =/= saved ->  	    if  		Head#head.update_mode =:= dirty ->  		    %% Won't grow more if the table is full.  		    {H2, _Res} =  -			(Head#head.mod):may_grow(Head, 0, many_times), +			dets_v9:may_grow(Head, 0, many_times),  		    {N + 1, H2};  		true ->   		    ok @@ -1519,21 +1483,10 @@ apply_op(Op, From, Head, N) ->  	    From ! {self(), Res},  	    erlang:garbage_collect(),  	    {0, H2}; -	{delete_key, Keys} when Head#head.update_mode =:= dirty -> -	    if -		Head#head.version =:= 8 -> -		    {H2, Res} = fdelete_key(Head, Keys), -		    From ! {self(), Res}, -		    {N + 1, H2}; -		true -> -		    stream_op(Op, From, [], Head, N) -	    end; +	{delete_key, _Keys} when Head#head.update_mode =:= dirty -> +            stream_op(Op, From, [], Head, N);  	{delete_object, Objs} when Head#head.update_mode =:= dirty ->  	    case check_objects(Objs, Head#head.keypos) of -		true when Head#head.version =:= 8 -> -		    {H2, Res} = fdelete_object(Head, Objs), -		    From ! {self(), Res}, -		    {N + 1, H2};  		true ->  		    stream_op(Op, From, [], Head, N);  		false -> @@ -1551,10 +1504,6 @@ apply_op(Op, From, Head, N) ->              H2;  	{insert, Objs} when Head#head.update_mode =:= dirty ->  	    case check_objects(Objs, Head#head.keypos) of -		true when Head#head.version =:= 8 -> -		    {H2, Res} = finsert(Head, Objs), -		    From ! {self(), Res}, -		    {N + 1, H2};  		true ->  		    stream_op(Op, From, [], Head, N);  		false -> @@ -1565,10 +1514,6 @@ apply_op(Op, From, Head, N) ->              {H2, Res} = finsert_new(Head, Objs),              From ! {self(), Res},              {N + 1, H2}; -	{lookup_keys, Keys}  when Head#head.version =:= 8 -> -	    {H2, Res} = flookup_keys(Head, Keys), -	    From ! {self(), Res}, -	    H2;  	{lookup_keys, _Keys} ->  	    stream_op(Op, From, [], Head, N);  	{match_init, State, Safe} -> @@ -1584,10 +1529,6 @@ apply_op(Op, From, Head, N) ->  	    {H2, Res} = fmatch(Head, MP, Spec, NObjs, Safe, From),  	    From ! {self(), Res},  	    H2; -	{member, Key} when Head#head.version =:= 8 -> -	    {H2, Res} = fmember(Head, Key), -	    From ! {self(), Res}, -	    H2;  	{member, _Key} = Op ->  	    stream_op(Op, From, [], Head, N);  	{next, Key} -> @@ -1628,7 +1569,7 @@ apply_op(Op, From, Head, N) ->  	    apply_op(WriteOp, From, H2, 0);  	WriteOp when Head#head.access =:= read_write,  		     Head#head.update_mode =:= saved -> -	    case catch (Head#head.mod):mark_dirty(Head) of +	    case catch dets_v9:mark_dirty(Head) of  		ok ->  		    start_auto_save_timer(Head),  		    H2 = Head#head{update_mode = dirty}, @@ -1643,6 +1584,40 @@ apply_op(Op, From, Head, N) ->  	    ok      end. +bug_found(Name, Op, Bad, From) -> +    case dets_utils:debug_mode() of +        true -> +            %% If stream_op/5 found more requests, this is not +            %% the last operation. +            error_logger:format +              ("** dets: Bug was found when accessing table ~w,~n" +               "** dets: operation was ~p and reply was ~w.~n" +               "** dets: Stacktrace: ~w~n", +               [Name, Op, Bad, erlang:get_stacktrace()]); +        false -> +            error_logger:format +              ("** dets: Bug was found when accessing table ~w~n", +               [Name]) +    end, +    if +        From =/= self() -> +            From ! {self(), {error, {dets_bug, Name, Op, Bad}}}, +            ok; +        true -> % auto_save | may_grow | {delayed_write, _} +            ok +    end. + +do_internal_open(Parent, Server, From, Ref, Args) -> +    ?PROFILE(ep:do()), +    case do_open_file(Args, Parent, Server, Ref) of +        {ok, Head} -> +            From ! {self(), ok}, +            Head; +        Error -> +            From ! {self(), Error}, +            exit(normal) +    end. +  start_auto_save_timer(Head) when Head#head.auto_save =:= infinity ->      ok;  start_auto_save_timer(Head) -> @@ -1650,7 +1625,7 @@ start_auto_save_timer(Head) ->      _Ref = erlang:send_after(Millis, self(), ?DETS_CALL(self(), auto_save)),      ok. -%% Version 9: Peek the message queue and try to evaluate several +%% Peek the message queue and try to evaluate several  %% lookup requests in parallel. Evalute delete_object, delete and  %% insert as well.  stream_op(Op, Pid, Pids, Head, N) -> @@ -1760,7 +1735,7 @@ lookup_reply(P, O) ->  %% Callback functions for system messages handling.  %%-----------------------------------------------------------------  system_continue(_Parent, _, Head) -> -    open_file_loop(Head). +    open_file_loop(Head, 0).  system_terminate(Reason, _Parent, _, Head) ->      _NewHead = do_stop(Head), @@ -1793,7 +1768,8 @@ read_file_header(FileName, Access, RamFile) ->          dets_utils:pread_close(Fd, FileName, ?FILE_FORMAT_VERSION_POS, 4),      if           Version =< 8 -> -            dets_v8:read_file_header(Fd, FileName); +            _ = file:close(Fd), +            throw({error, {format_8_no_longer_supported, FileName}});          Version =:= 9 ->              dets_v9:read_file_header(Fd, FileName);          true -> @@ -1820,7 +1796,7 @@ perform_save(Head, DoSync) when Head#head.update_mode =:= dirty;  				Head#head.update_mode =:= new_dirty ->      case catch begin                     {Head1, []} = write_cache(Head), -                   {Head2, ok} = (Head1#head.mod):do_perform_save(Head1), +                   {Head2, ok} = dets_v9:do_perform_save(Head1),                     ok = ensure_written(Head2, DoSync),                     {Head2#head{update_mode = saved}, ok}                 end of @@ -1853,7 +1829,7 @@ ensure_written(Head, false) when not Head#head.ram_file ->  do_bchunk_init(Head, Tab) ->      case catch write_cache(Head) of  	{H2, []} -> -	    case (H2#head.mod):table_parameters(H2) of +	    case dets_v9:table_parameters(H2) of  		undefined ->  		    {H2, {error, old_version}};  		Parms -> @@ -1862,9 +1838,9 @@ do_bchunk_init(Head, Tab) ->                                L =:= <<>> -> eof;                                true -> <<>>                            end, -                    C0 = #dets_cont{no_objs = default, bin = Bin, alloc = L},  		    BinParms = term_to_binary(Parms), -		    {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk}, +		    {H2, {#dets_cont{no_objs = default, bin = Bin, alloc = L, +                                     tab = Tab, proc = self(),what = bchunk},                            [BinParms]}}  	    end;  	{NewHead, _} = HeadError when is_record(NewHead, head) -> @@ -1904,16 +1880,8 @@ do_delete_all_objects(Head) ->  	  max_no_slots = MaxSlots, cache = Cache} = Head,       CacheSz = dets_utils:cache_size(Cache),      ok = dets_utils:truncate(Fd, Fname, bof), -    (Head#head.mod):initiate_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, -				  Ram, CacheSz, Auto, true). - -%% -> {NewHead, Reply}, Reply = ok | Error. -fdelete_key(Head, Keys) -> -    do_delete(Head, Keys, delete_key). - -%% -> {NewHead, Reply}, Reply = ok | badarg | Error. -fdelete_object(Head, Objects) -> -    do_delete(Head, Objects, delete_object). +    dets_v9:initiate_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, +                          Ram, CacheSz, Auto, true).  ffirst(H) ->      Ref = make_ref(), @@ -1930,7 +1898,7 @@ ffirst1(H) ->      ffirst(NH, 0).  ffirst(H, Slot) -> -    case (H#head.mod):slot_objs(H, Slot) of +    case dets_v9:slot_objs(H, Slot) of  	'$end_of_table' -> {H, '$end_of_table'};  	[] -> ffirst(H, Slot+1);  	[X|_] -> {H, element(H#head.keypos, X)} @@ -2067,7 +2035,7 @@ finfo(H, auto_save) -> {H, H#head.auto_save};  finfo(H, bchunk_format) ->       case catch write_cache(H) of          {H2, []} -> -            case (H2#head.mod):table_parameters(H2) of +            case dets_v9:table_parameters(H2) of                  undefined = Undef ->                      {H2, Undef};                  Parms -> @@ -2100,7 +2068,7 @@ finfo(H, no_keys) ->  	{H2, _} = HeadError when is_record(H2, head) ->  	    HeadError      end; -finfo(H, no_slots) -> {H, (H#head.mod):no_slots(H)}; +finfo(H, no_slots) -> {H, dets_v9:no_slots(H)};  finfo(H, pid) -> {H, self()};  finfo(H, ram_file) -> {H, H#head.ram_file};  finfo(H, safe_fixed) -> @@ -2127,7 +2095,7 @@ finfo(H, size) ->  	    HeadError      end;  finfo(H, type) -> {H, H#head.type}; -finfo(H, version) -> {H, H#head.version}; +finfo(H, version) -> {H, 9};  finfo(H, _) -> {H, undefined}.  file_size(Fd, FileName) ->  @@ -2136,8 +2104,6 @@ file_size(Fd, FileName) ->  test_bchunk_format(_Head, undefined) ->      false; -test_bchunk_format(Head, _Term) when Head#head.version =:= 8 -> -    false;  test_bchunk_format(Head, Term) ->      dets_v9:try_bchunk_header(Term, Head) =/= not_ok. @@ -2206,7 +2172,7 @@ do_finit(Head, Init, Format, NoSlots) ->      #head{fptr = Fd, type = Type, keypos = Kp, auto_save = Auto,            cache = Cache, filename = Fname, ram_file = Ram,  	  min_no_slots = MinSlots0, max_no_slots = MaxSlots, -          name = Tab, update_mode = UpdateMode, mod = HMod} = Head, +          name = Tab, update_mode = UpdateMode} = Head,      CacheSz = dets_utils:cache_size(Cache),      {How, Head1} =  	case Format of @@ -2219,9 +2185,10 @@ do_finit(Head, Init, Format, NoSlots) ->  			{general_init, Head};  		    true ->  			ok = dets_utils:truncate(Fd, Fname, bof), -			{ok, H} = HMod:initiate_file(Fd, Tab, Fname, Type, Kp, -						     MinSlots, MaxSlots, Ram, -						     CacheSz, Auto, false), +			{ok, H} = +                            dets_v9:initiate_file(Fd, Tab, Fname, Type, Kp, +                                                  MinSlots, MaxSlots, Ram, +                                                  CacheSz, Auto, false),  			{general_init, H}  		end;  	    bchunk -> @@ -2230,7 +2197,7 @@ do_finit(Head, Init, Format, NoSlots) ->  	end,      case How of  	bchunk_init ->  -	    case HMod:bchunk_init(Head1, Init) of +	    case dets_v9:bchunk_init(Head1, Init) of  		{ok, NewHead} ->  		    {ok, NewHead#head{update_mode = dirty}};  		Error -> @@ -2238,10 +2205,10 @@ do_finit(Head, Init, Format, NoSlots) ->  	    end;  	general_init ->  	    Cntrs = ets:new(dets_init, []), -	    Input = HMod:bulk_input(Head1, Init, Cntrs), +	    Input = dets_v9:bulk_input(Head1, Init, Cntrs),  	    SlotNumbers = {Head1#head.min_no_slots, bulk_init, MaxSlots},  	    {Reply, SizeData} =  -		do_sort(Head1, SlotNumbers, Input, Cntrs, Fname, not_used), +		do_sort(Head1, SlotNumbers, Input, Cntrs, Fname),  	    Bulk = true,  	    case Reply of   		{ok, NoDups, H1} -> @@ -2297,7 +2264,8 @@ fmatch(Head, MP, Spec, N, Safe, From) ->  	    {NewHead, Reply} = flookup_keys(Head, Keys),  	    case Reply of  		Objs when is_list(Objs) -> -		    MatchingObjs = ets:match_spec_run(Objs, MP), +                    {match_spec, MS} = MP, +		    MatchingObjs = ets:match_spec_run(Objs, MS),  		    {NewHead, {done, MatchingObjs}};  		Error ->  		    {NewHead, Error} @@ -2377,7 +2345,7 @@ fmatch_delete(Head, C) ->  	{[], _} ->  	    {Head, {done, 0}};  	{RTs, NC} -> -	    MP = C#dets_cont.match_program, +	    {match_spec, MP} = C#dets_cont.match_program,  	    case catch filter_binary_terms(RTs, MP, []) of  		{'EXIT', _} ->                      Bad = dets_utils:bad_object(fmatch_delete, RTs), @@ -2405,7 +2373,7 @@ do_fmatch_delete_var_keys(Head, MP, _Spec, From) ->      C0 = init_scan(NewHead, default),      {NewHead, {cont, C0#dets_cont{match_program = MP}, 0}}. -do_fmatch_constant_keys(Head, Keys, MP) -> +do_fmatch_constant_keys(Head, Keys, {match_spec, MP}) ->      case flookup_keys(Head, Keys) of  	{NewHead, ReadTerms} when is_list(ReadTerms) ->  	    Terms = filter_terms(ReadTerms, MP, []), @@ -2454,18 +2422,8 @@ do_delete(Head, Things, What) ->  	    HeadError      end. -fmember(Head, Key) -> -    case catch begin -                   {Head2, [{_NoPid,Objs}]} =  -                       update_cache(Head, [Key], {lookup, nopid}), -                   {Head2, Objs =/= []} -               end of -        {NewHead, _} = Reply when is_record(NewHead, head) -> -            Reply -    end. -  fnext(Head, Key) -> -    Slot = (Head#head.mod):db_hash(Key, Head), +    Slot = dets_v9:db_hash(Key, Head),      Ref = make_ref(),      case catch {Ref, fnext(Head, Key, Slot)} of  	{Ref, {H, R}} ->  @@ -2476,7 +2434,7 @@ fnext(Head, Key) ->  fnext(H, Key, Slot) ->      {NH, []} = write_cache(H), -    case (H#head.mod):slot_objs(NH, Slot) of +    case dets_v9:slot_objs(NH, Slot) of  	'$end_of_table' -> {NH, '$end_of_table'};  	L -> fnext_search(NH, Key, Slot, L)      end. @@ -2490,7 +2448,7 @@ fnext_search(H, K, Slot, L) ->  %% We've got to continue to search for the next key in the next slot  fnext_slot(H, K, Slot) -> -    case (H#head.mod):slot_objs(H, Slot) of +    case dets_v9:slot_objs(H, Slot) of  	'$end_of_table' -> {H, '$end_of_table'};  	[] -> fnext_slot(H, K, Slot+1);  	L -> {H, element(H#head.keypos, hd(L))} @@ -2518,11 +2476,10 @@ fopen2(Fname, Tab) ->  	    Acc = read_write,  	    Ram = false,   	    {ok, Fd, FH} = read_file_header(Fname, Acc, Ram), -            Mod = FH#fileheader.mod, -            Do = case Mod:check_file_header(FH, Fd) of -                     {ok, Head1, ExtraInfo} -> +            Do = case dets_v9:check_file_header(FH, Fd) of +                     {ok, Head1} ->                           Head2 = Head1#head{filename = Fname}, -                         try {ok, Mod:init_freelist(Head2, ExtraInfo)} +                         try {ok, dets_v9:init_freelist(Head2)}                           catch                               throw:_ ->                                   {repair, " has bad free lists, repairing ..."} @@ -2536,8 +2493,7 @@ fopen2(Fname, Tab) ->              case Do of  		{repair, Mess} ->                      io:format(user, "dets: file ~tp~s~n", [Fname, Mess]), -                    Version = default, -                    case fsck(Fd, Tab, Fname, FH, default, default, Version) of +                    case fsck(Fd, Tab, Fname, FH, default, default) of                          ok ->                              fopen2(Fname, Tab);                          Error -> @@ -2570,33 +2526,23 @@ fopen_existing_file(Tab, OpenArgs) ->      #open_args{file = Fname, type = Type, keypos = Kp, repair = Rep,                 min_no_slots = MinSlots, max_no_slots = MaxSlots,                 ram_file = Ram, delayed_write = CacheSz, auto_save = -               Auto, access = Acc, version = Version, debug = Debug} = +               Auto, access = Acc, debug = Debug} =          OpenArgs,      {ok, Fd, FH} = read_file_header(Fname, Acc, Ram), -    V9 = (Version =:= 9) or (Version =:= default),      MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots),      MaxF = (MaxSlots =:= default) or (MaxSlots =:= FH#fileheader.max_no_slots), -    Mod = (FH#fileheader.mod), -    Wh = case Mod:check_file_header(FH, Fd) of -	     {ok, Head, true} when Rep =:= force, Acc =:= read_write, -				   FH#fileheader.version =:= 9, -				   FH#fileheader.no_colls =/= undefined, -				   MinF, MaxF, V9 -> -		 {compact, Head, true}; -             {ok, _Head, _Extra} when Rep =:= force, Acc =:= read -> +    Wh = case dets_v9:check_file_header(FH, Fd) of +	     {ok, Head} when Rep =:= force, Acc =:= read_write, +                             FH#fileheader.no_colls =/= undefined, +                             MinF, MaxF -> +	         {compact, Head}; +             {ok, _Head} when Rep =:= force, Acc =:= read ->                   throw({error, {access_mode, Fname}}); -	     {ok, Head, need_compacting} when Acc =:= read -> -                 {final, Head, true}; % Version 8 only. -	     {ok, _Head, need_compacting} when Rep =:= true -> -		 %% The file needs to be compacted due to a very big -		 %% and fragmented free_list. Version 8 only. -		 M = " is now compacted ...", -		 {repair, M}; -	     {ok, _Head, _Extra} when Rep =:= force -> +	     {ok, _Head} when Rep =:= force ->  		 M = ", repair forced.",  		 {repair, M}; -	     {ok, Head, ExtraInfo} -> -		 {final, Head, ExtraInfo}; +	     {ok, Head} -> +		 {final, Head};  	     {error, not_closed} when Rep =:= force, Acc =:= read_write ->  		 M = ", repair forced.",  		 {repair, M}; @@ -2605,17 +2551,13 @@ fopen_existing_file(Tab, OpenArgs) ->  		 {repair, M};  	     {error, not_closed} when Rep =:= false ->  		 throw({error, {needs_repair, Fname}}); -	     {error, version_bump} when Rep =:= true, Acc =:= read_write -> -                 %% Version 8 only -		 M = " old version, upgrading ...", -		 {repair, M};  	     {error, Reason} ->  		 throw({error, {Reason, Fname}})  	 end,      Do = case Wh of -             {Tag, Hd, Extra} when Tag =:= final; Tag =:= compact -> +             {Tag, Hd} when Tag =:= final; Tag =:= compact ->                   Hd1 = Hd#head{filename = Fname}, -                 try {Tag, Mod:init_freelist(Hd1, Extra)} +                 try {Tag, dets_v9:init_freelist(Hd1)}                   catch                       throw:_ ->                           {repair, " has bad free lists, repairing ..."} @@ -2643,23 +2585,20 @@ fopen_existing_file(Tab, OpenArgs) ->  			      "now repairing ...~n", [Fname]),                      {ok, Fd2, _FH} = read_file_header(Fname, Acc, Ram),                      do_repair(Fd2, Tab, Fname, FH, MinSlots, MaxSlots,  -			      Version, OpenArgs) +			      OpenArgs)  	    end;  	{repair, Mess} ->  	    io:format(user, "dets: file ~tp~s~n", [Fname, Mess]),              do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots,  -		      Version, OpenArgs); -	_ when FH#fileheader.version =/= Version, Version =/= default -> -	    throw({error, {version_mismatch, Fname}}); +		      OpenArgs);  	{final, H} ->  	    H1 = H#head{auto_save = Auto},  	    open_final(H1, Fname, Acc, Ram, CacheSz, Tab, Debug)      end. -do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots, Version, OpenArgs) -> -    case fsck(Fd, Tab, Fname, FH, MinSlots, MaxSlots, Version) of +do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots, OpenArgs) -> +    case fsck(Fd, Tab, Fname, FH, MinSlots, MaxSlots) of  	ok -> -	    %% No need to update 'version'.  	    erlang:garbage_collect(),  	    fopen3(Tab, OpenArgs#open_args{repair = false});  	Error -> @@ -2673,8 +2612,8 @@ open_final(Head, Fname, Acc, Ram, CacheSz, Tab, Debug) ->  		      filename = Fname,  		      name = Tab,  		      cache = dets_utils:new_cache(CacheSz)}, -    init_disk_map(Head1#head.version, Tab, Debug), -    (Head1#head.mod):cache_segps(Head1#head.fptr, Fname, Head1#head.next), +    init_disk_map(Tab, Debug), +    dets_v9:cache_segps(Head1#head.fptr, Fname, Head1#head.next),      check_growth(Head1),      {ok, Head1}. @@ -2683,7 +2622,7 @@ fopen_init_file(Tab, OpenArgs) ->      #open_args{file = Fname, type = Type, keypos = Kp,                  min_no_slots = MinSlotsArg, max_no_slots = MaxSlotsArg,   	       ram_file = Ram, delayed_write = CacheSz, auto_save = Auto,  -               version = UseVersion, debug = Debug} = OpenArgs, +               debug = Debug} = OpenArgs,      MinSlots = choose_no_slots(MinSlotsArg, ?DEFAULT_MIN_NO_SLOTS),      MaxSlots = choose_no_slots(MaxSlotsArg, ?DEFAULT_MAX_NO_SLOTS),      FileSpec = if @@ -2691,20 +2630,11 @@ fopen_init_file(Tab, OpenArgs) ->  		   true -> Fname  	       end,      {ok, Fd} = dets_utils:open(FileSpec, open_args(read_write, Ram)), -    Version = if -                  UseVersion =:= default -> -                      case os:getenv("DETS_USE_FILE_FORMAT") of -                          "8" -> 8; -                          _ -> 9 -                      end; -                  true -> -                      UseVersion -              end, -    Mod = version2module(Version),      %% No need to truncate an empty file. -    init_disk_map(Version, Tab, Debug), -    case catch Mod:initiate_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, -				 Ram, CacheSz, Auto, true) of +    init_disk_map(Tab, Debug), +    case catch dets_v9:initiate_file(Fd, Tab, Fname, Type, Kp, +                                     MinSlots, MaxSlots, +                                     Ram, CacheSz, Auto, true) of  	{error, Reason} when Ram ->  	    _ = file:close(Fd),  	    throw({error, Reason}); @@ -2719,15 +2649,13 @@ fopen_init_file(Tab, OpenArgs) ->      end.  %% Debug. -init_disk_map(9, Name, Debug) -> +init_disk_map(Name, Debug) ->      case Debug orelse dets_utils:debug_mode() of          true ->               dets_utils:init_disk_map(Name);          false ->              ok -    end; -init_disk_map(_Version, _Name, _Debug) -> -    ok. +    end.  open_args(Access, RamFile) ->      A1 = case Access of @@ -2740,15 +2668,7 @@ open_args(Access, RamFile) ->  	   end,      A1 ++ A2 ++ [binary, read]. -version2module(V) when V =< 8 -> dets_v8; -version2module(9) -> dets_v9. - -module2version(dets_v8) -> 8; -module2version(dets_v9) -> 9; -module2version(not_used) -> 9. -  %% -> ok | throw(Error)  -%% For version 9 tables only.  compact(SourceHead) ->      #head{name = Tab, filename = Fname, fptr = SFd, type = Type, keypos = Kp,  	  ram_file = Ram, auto_save = Auto} = SourceHead, @@ -2759,7 +2679,7 @@ compact(SourceHead) ->      %% It is normally not possible to have two open tables in the same      %% process since the process dictionary is used for caching      %% segment pointers, but here is works anyway--when reading a file -    %% serially the pointers to not need to be used. +    %% serially the pointers do not need to be used.      Head = case catch dets_v9:prep_table_copy(Fd, Tab, Tmp, Type, Kp, Ram,   					      CacheSz, Auto, TblParms) of  	       {ok, H} -> @@ -2794,7 +2714,7 @@ compact(SourceHead) ->  %% -> ok | Error  %% Closes Fd. -fsck(Fd, Tab, Fname, FH, MinSlotsArg, MaxSlotsArg, Version) -> +fsck(Fd, Tab, Fname, FH, MinSlotsArg, MaxSlotsArg) ->      %% MinSlots and MaxSlots are the option values.      #fileheader{min_no_slots = MinSlotsFile,                   max_no_slots = MaxSlotsFile} = FH, @@ -2807,10 +2727,10 @@ fsck(Fd, Tab, Fname, FH, MinSlotsArg, MaxSlotsArg, Version) ->      %% If the number of objects (keys) turns out to be significantly      %% different from NoSlots, we try again with the correct number of      %% objects (keys). -    case fsck_try(Fd, Tab, FH, Fname, SlotNumbers, Version) of +    case fsck_try(Fd, Tab, FH, Fname, SlotNumbers) of          {try_again, BetterNoSlots} ->  	    BetterSlotNumbers = {MinSlots, BetterNoSlots, MaxSlots}, -            case fsck_try(Fd, Tab, FH, Fname, BetterSlotNumbers, Version) of +            case fsck_try(Fd, Tab, FH, Fname, BetterSlotNumbers) of                  {try_again, _} ->                      _ = file:close(Fd),                      {error, {cannot_repair, Fname}}; @@ -2829,7 +2749,7 @@ choose_no_slots(NoSlots, _) -> NoSlots.  %% Initiating a table using a fun and repairing (or converting) a  %% file are completely different things, but nevertheless the same  %% method is used in both cases... -fsck_try(Fd, Tab, FH, Fname, SlotNumbers, Version) -> +fsck_try(Fd, Tab, FH, Fname, SlotNumbers) ->      Tmp = tempfile(Fname),      #fileheader{type = Type, keypos = KeyPos} = FH,      {_MinSlots, EstNoSlots, MaxSlots} = SlotNumbers, @@ -2838,7 +2758,7 @@ fsck_try(Fd, Tab, FH, Fname, SlotNumbers, Version) ->  			  max_no_slots = MaxSlots,                            ram_file = false, delayed_write = ?DEFAULT_CACHE,                            auto_save = infinity, access = read_write, -                          version = Version, debug = false}, +                          debug = false},      case catch fopen3(Tab, OpenArgs) of  	{ok, Head} ->              case fsck_try_est(Head, Fd, Fname, SlotNumbers, FH) of @@ -2888,10 +2808,9 @@ assure_no_file(File) ->  %% -> {ok, NewHead} | {try_again, integer()} | Error  fsck_try_est(Head, Fd, Fname, SlotNumbers, FH) ->      %% Mod is the module to use for reading input when repairing. -    Mod = FH#fileheader.mod,      Cntrs = ets:new(dets_repair, []), -    Input = Mod:fsck_input(Head, Fd, Cntrs, FH), -    {Reply, SizeData} = do_sort(Head, SlotNumbers, Input, Cntrs, Fname, Mod), +    Input = dets_v9:fsck_input(Head, Fd, Cntrs, FH), +    {Reply, SizeData} = do_sort(Head, SlotNumbers, Input, Cntrs, Fname),      Bulk = false,      case Reply of           {ok, NoDups, H1} -> @@ -2906,14 +2825,13 @@ fsck_try_est(Head, Fd, Fname, SlotNumbers, FH) ->  	    Else      end. -do_sort(Head, SlotNumbers, Input, Cntrs, Fname, Mod) -> -    OldV = module2version(Mod), +do_sort(Head, SlotNumbers, Input, Cntrs, Fname) ->      %% output_objs/4 replaces {LogSize,NoObjects} in Cntrs by      %% {LogSize,Position,Data,NoObjects | NoCollections}.      %% Data = {FileName,FileDescriptor} | [object()] -    %% For small tables Data may be a list of objects which is more +    %% For small tables Data can be a list of objects which is more      %% efficient since no temporary files are created. -    Output = (Head#head.mod):output_objs(OldV, Head, SlotNumbers, Cntrs), +    Output = dets_v9:output_objs(Head, SlotNumbers, Cntrs),      TmpDir = filename:dirname(Fname),      Reply = (catch file_sorter:sort(Input, Output,   				    [{format, binary},{tmpdir, TmpDir}])), @@ -2954,13 +2872,6 @@ fsck_copy1([SzData | L], Head, Bulk, NoDups) ->  	{ok, Copied} when Copied =:= ExpectedSize;  			  NoObjects =:= 0 -> % the segments  	    fsck_copy1(L, Head, Bulk, NoDups); -	{ok, Copied} when Bulk, Head#head.version =:= 8 -> -	    NoZeros = ExpectedSize - Copied, -	    Dups = NoZeros div Size, -	    Addr = Pos+Copied, -	    NewHead = free_n_objects(Head, Addr, Size-1, NoDups), -	    NewNoDups = NoDups - Dups, -	    fsck_copy1(L, NewHead, Bulk, NewNoDups);  	{ok, _Copied} -> % should never happen  	    close_files(Bulk, L, Head),  	    Reason = if Bulk -> initialization_failed;  @@ -2975,13 +2886,6 @@ fsck_copy1([], Head, _Bulk, NoDups) when NoDups =/= 0 ->  fsck_copy1([], Head, _Bulk, _NoDups) ->      {ok, Head#head{update_mode = dirty}}.  -free_n_objects(Head, _Addr, _Size, 0) -> -    Head; -free_n_objects(Head, Addr, Size, N) -> -    {NewHead, _} = dets_utils:free(Head, Addr, Size), -    NewAddr = Addr + Size + 1, -    free_n_objects(NewHead, NewAddr, Size, N-1). -  close_files(false, SizeData, Head) ->      _ = file:close(Head#head.fptr),      close_files(true, SizeData, Head); @@ -3000,7 +2904,7 @@ close_tmp(Fd) ->  fslot(H, Slot) ->      case catch begin                     {NH, []} = write_cache(H), -                   Objs = (NH#head.mod):slot_objs(NH, Slot), +                   Objs = dets_v9:slot_objs(NH, Slot),                     {NH, Objs}                 end of          {NewHead, _Objects} = Reply when is_record(NewHead, head) -> @@ -3050,7 +2954,7 @@ where_is_object(Head, Object) ->  	true ->  	    case catch write_cache(Head) of  		{NewHead, []} -> -		    {NewHead, (Head#head.mod):find_object(NewHead, Object)}; +		    {NewHead, dets_v9:find_object(NewHead, Object)};  		{NewHead, _} = HeadError when is_record(NewHead, head) ->  		    HeadError  	    end; @@ -3063,13 +2967,9 @@ check_objects([T | Ts], Kp) when tuple_size(T) >= Kp ->  check_objects(L, _Kp) ->      L =:= []. -no_things(Head) when Head#head.no_keys =:= undefined -> -    Head#head.no_objects;  no_things(Head) ->      Head#head.no_keys. -file_no_things(FH) when FH#fileheader.no_keys =:= undefined -> -    FH#fileheader.no_objects;  file_no_things(FH) ->      FH#fileheader.no_keys. @@ -3110,7 +3010,7 @@ update_cache(Head, ToAdd) ->      if   	Lookup; NewSize >= Cache#cache.tsize ->  	    %% The cache is considered full, or some lookup. -	    {NewHead, LU, PwriteList} = (Head#head.mod):write_cache(Head1), +	    {NewHead, LU, PwriteList} = dets_v9:write_cache(Head1),  	    {NewHead, Found ++ LU, PwriteList};  	NewC =:= [] ->  	    {Head1, Found, []}; @@ -3195,7 +3095,7 @@ delayed_write(Head, WrTime) ->  %% -> {NewHead, [LookedUpObject]} | throw({NewHead, Error})  write_cache(Head) -> -    {Head1, LU, PwriteList} = (Head#head.mod):write_cache(Head), +    {Head1, LU, PwriteList} = dets_v9:write_cache(Head),      {NewHead, ok} = dets_utils:pwrite(Head1, PwriteList),      {NewHead, LU}. @@ -3248,7 +3148,7 @@ scan(Head, C) -> % when is_record(C, dets_cont)      scan(Bin, Head, From, To, L, [], R, {C, Head#head.type}).  scan(Bin, H, From, To, L, Ts, R, {C0, Type} = C) -> -    case (H#head.mod):scan_objs(H, Bin, From, To, L, Ts, R, Type) of +    case dets_v9:scan_objs(H, Bin, From, To, L, Ts, R, Type) of          {more, NFrom, NTo, NL, NTs, NR, Sz} ->              scan_read(H, NFrom, NTo, Sz, NL, NTs, NR, C);          {stop, <<>>=B, NFrom, NTo, <<>>=NL, NTs} -> @@ -3317,7 +3217,7 @@ file_info(FileName) ->      case catch read_file_header(FileName, read, false) of  	{ok, Fd, FH} ->  	    _ = file:close(Fd), -            (FH#fileheader.mod):file_info(FH); +            dets_v9:file_info(FH);  	Other ->  	    Other      end. @@ -3332,15 +3232,13 @@ get_head_field(Fd, Field) ->  view(FileName) ->      case catch read_file_header(FileName, read, false) of          {ok, Fd, FH} -> -	    Mod = FH#fileheader.mod, -            try Mod:check_file_header(FH, Fd) of -                {ok, H0, ExtraInfo} -> -                    Mod = FH#fileheader.mod, -                    case Mod:check_file_header(FH, Fd) of -                        {ok, H0, ExtraInfo} -> -                            H = Mod:init_freelist(H0, ExtraInfo), +            try dets_v9:check_file_header(FH, Fd) of +                {ok, H0} -> +                    case dets_v9:check_file_header(FH, Fd) of +                        {ok, H0} -> +                            H = dets_v9:init_freelist(H0),                              v_free_list(H), -                            Mod:v_segments(H), +                            dets_v9:v_segments(H),                              ok;                          X ->                              X diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl index 6ebeb96156..b5e732b08f 100644 --- a/lib/stdlib/src/dets.hrl +++ b/lib/stdlib/src/dets.hrl @@ -21,7 +21,7 @@  -define(DEFAULT_MIN_NO_SLOTS, 256).  -define(DEFAULT_MAX_NO_SLOTS, 32*1024*1024).  -define(DEFAULT_AUTOSAVE, 3). % minutes --define(DEFAULT_CACHE, {3000, 14000}). % {delay,size} in {milliseconds,bytes} +-define(DEFAULT_CACHE, {3000, 14000}). % cache_parms()  %% Type.  -define(SET, 1). @@ -46,83 +46,111 @@  -define(DETS_CALL(Pid, Req), {'$dets_call', Pid, Req}). +-type access()      :: 'read' | 'read_write'. +-type auto_save()   :: 'infinity' | non_neg_integer(). +-type hash_bif()    :: 'phash' | 'phash2'. +-type keypos()      :: pos_integer(). +-type no_colls()    :: [{LogSize :: non_neg_integer(), +                         NoCollections :: non_neg_integer()}]. +-type no_slots()    :: 'default' | non_neg_integer(). +-type tab_name()    :: term(). +-type type()        :: 'bag' | 'duplicate_bag' | 'set'. +-type update_mode() :: 'dirty' +                     | 'new_dirty' +                     | 'saved' +                     | {'error', Reason :: term()}. +  %% Record holding the file header and more.  -record(head,  { -	  m,               % size -	  m2,              % m * 2 -	  next,            % next position for growth (segm mgmt only) -	  fptr,            % the file descriptor -	  no_objects,      % number of objects in table, -	  no_keys,         % number of keys (version 9 only) -	  maxobjsize,      % 2-log of the size of the biggest object -                           % collection (version 9 only) +	  m :: non_neg_integer(),    % size +	  m2 :: non_neg_integer(),   % m * 2 +	  next :: non_neg_integer(), % next position for growth +                                     % (segm mgmt only) +	  fptr :: file:fd(),         % the file descriptor +	  no_objects :: non_neg_integer() , % number of objects in table, +	  no_keys :: non_neg_integer(),     % number of keys +	  maxobjsize :: 'undefined' | non_neg_integer(), % 2-log of +                           % the size of the biggest object collection  	  n,               % split indicator -	  type,            % set | bag | duplicate_bag -	  keypos,          % default is 1 as for ets -	  freelists,       % tuple of free lists of buddies -	                   % if fixed =/= false, then a pair of freelists -	  freelists_p,     % cached FreelistsPointer -	  no_collections,  % [{LogSize,NoCollections}] | undefined; number of -	                   % object collections per size (version 9(b)) -	  auto_save,       % Integer | infinity  -	  update_mode,     % saved | dirty | new_dirty | {error, Reason} -	  fixed = false,   % false | {now_time(), [{pid(),Counter}]} -                           % time of first fix, and number of fixes per process -	  hash_bif,        % hash bif used for this file (phash2, phash, hash) -          has_md5,         % whether the header has an MD5 sum (version 9(c)) -	  min_no_slots,    % minimum number of slots (default or integer) -	  max_no_slots,    % maximum number of slots (default or integer) -	  cache,           % cache(). Write cache. - -	  filename,             % name of the file being used -	  access = read_write,  % read | read_write -	  ram_file = false,     % true | false -	  name,                 % the name of the table - -	  parent,               % The supervisor of Dets processes. -	  server,               % The creator of Dets processes. - -          %% Depending on the file format: -          version, -          mod, -          bump, -          base +	  type :: type(), +	  keypos :: keypos(), % default is 1 as for ets +	  freelists :: 'undefined' +                     | tuple(), % tuple of free lists of buddies +	                        % if fixed =/= false, then a pair of freelists +	  freelists_p :: 'undefined' +                       | non_neg_integer(),  % cached FreelistsPointer +	  no_collections :: 'undefined' +                          | no_colls(), % number of object collections +                                        % per size (version 9(b)) +	  auto_save :: auto_save(), +	  update_mode :: update_mode(), +	  fixed = false :: 'false' +                         | {{integer(), integer()}, % time of first fix, +                            [{pid(),   % and number of fixes per process +                              non_neg_integer()}]}, +	  hash_bif :: hash_bif(),  % hash bif used for this file +          has_md5 :: boolean(),    % whether the header has +                                   % an MD5 sum (version 9(c)) +	  min_no_slots :: no_slots(),  % minimum number of slots +	  max_no_slots :: no_slots(),  % maximum number of slots +	  cache :: 'undefined' | cache(), % Write cache. + +	  filename :: file:name(), % name of the file being used +	  access = read_write :: access(), +	  ram_file = false :: boolean(), +	  name :: tab_name(),      % the name of the table + +	  parent :: 'undefined' | pid(), % The supervisor of Dets processes. +	  server :: 'undefined' | pid(), % The creator of Dets processes. + +          bump :: non_neg_integer(), +          base :: non_neg_integer()  	 }).  %% Info extracted from the file header.  -record(fileheader, { -	  freelist, -          fl_base, -	  cookie, -	  closed_properly, -	  type, -	  version, -	  m, -	  next, -	  keypos, -	  no_objects, -	  no_keys, -	  min_no_slots, -	  max_no_slots, -	  no_colls, -	  hash_method, -          read_md5, -          has_md5, -          md5, -	  trailer, -	  eof, -	  n, -	  mod +	  freelist :: non_neg_integer(), +          fl_base :: non_neg_integer(), +	  cookie :: non_neg_integer(), +	  closed_properly :: non_neg_integer(), +	  type :: 'badtype' | type(), +	  version :: non_neg_integer(), +	  m :: non_neg_integer(), +	  next :: non_neg_integer(), +	  keypos :: keypos(), +	  no_objects :: non_neg_integer(), +	  no_keys :: non_neg_integer(), +	  min_no_slots :: non_neg_integer(), +	  max_no_slots :: non_neg_integer(), +	  no_colls :: 'undefined' | no_colls(), +	  hash_method :: non_neg_integer(), +          read_md5 :: binary(), +          has_md5 :: boolean(), +          md5 :: binary(), +	  trailer :: non_neg_integer(), +	  eof :: non_neg_integer(), +	  n  	}). +-type delay() :: non_neg_integer(). +-type threshold() :: non_neg_integer(). +-type cache_parms() :: +        {Delay :: delay(), % max time items are kept in RAM only, +                           % in milliseconds +         Size :: threshold()}. % threshold size of cache, in bytes +  %% Write Cache.  -record(cache, { -	 cache,   % [{Key,{Seq,Item}}], write cache, last item first -         csize,   % current size of the cached items -         inserts, % upper limit on number of inserted keys -	 wrtime,  % last write or update time -	 tsize,   % threshold size of cache, in bytes -	 delay    % max time items are kept in RAM only, in milliseconds +	 cache :: % write cache, last item first +                 [{Key :: term(), +                   {Seq :: non_neg_integer(), Item :: term()}}], +         csize :: non_neg_integer(), % current size of the cached items +         inserts :: % upper limit on number of inserted keys +                    non_neg_integer(), +	 wrtime :: 'undefined' | integer(),  % last write or update time +	 tsize :: threshold(), % threshold size of cache +	 delay :: delay()      % max time items are kept in RAM only  	 }). +-type cache() :: #cache{}. diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl index 34a8ddddaa..da6ebd18f2 100644 --- a/lib/stdlib/src/dets_utils.erl +++ b/lib/stdlib/src/dets_utils.erl @@ -20,13 +20,13 @@  -module(dets_utils).  %% Utility functions common to several dets file formats. -%% To be used from dets, dets_v8 and dets_v9 only. +%% To be used from modules dets and dets_v9 only.  -export([cmp/2, msort/1, mkeysort/2, mkeysearch/3, family/1]).  -export([rename/2, pread/2, pread/4, ipread/3, pwrite/2, write/2,           truncate/2, position/2, sync/1, open/2, truncate/3, fwrite/3, -         write_file/2, position/3, position_close/3, pwrite/4, +         write_file/2, position/3, position_close/3,           pwrite/3, pread_close/4, read_n/2, pread_n/3, read_4/2]).  -export([code_to_type/1, type_to_code/1]). @@ -44,8 +44,6 @@           all_allocated_as_list/1, find_allocated/4, find_next_allocated/3,           log2/1, make_zeros/1]). --export([init_slots_from_old_file/2]). -  -export([list_to_tree/1, tree_to_bin/5]).  -compile({inline, [{sz2pos,1}, {adjust_addr,3}]}). @@ -308,12 +306,6 @@ position_close(Fd, FileName, Pos) ->  	OK -> OK      end. -pwrite(Fd, FileName, Position, B) -> -    case file:pwrite(Fd, Position, B) of -	ok -> ok; -	Error -> file_error(FileName, {error, Error}) -    end. -  pwrite(Fd, FileName, Bins) ->      case file:pwrite(Fd, Bins) of  	ok -> @@ -478,20 +470,6 @@ new_cache({Delay, Size}) ->  %%%             Ullman. I think buddy systems were invented by Knuth, a long  %%%             time ago. -init_slots_from_old_file([{Slot,Addr} | T], Ftab) -> -    init_slot(Slot+1,[{Slot,Addr} | T], Ftab); -init_slots_from_old_file([], Ftab) -> -    Ftab. - -init_slot(_Slot,[], Ftab) -> -    Ftab; % should never happen -init_slot(_Slot,[{_Addr,0}|T], Ftab) -> -    init_slots_from_old_file(T, Ftab); -init_slot(Slot,[{_Slot1,Addr}|T], Ftab) -> -    Stree = element(Slot, Ftab), -    %%    io:format("init_slot ~p:~p~n",[Slot, Addr]), -    init_slot(Slot,T,setelement(Slot, Ftab, bplus_insert(Stree, Addr))). -  %%% The free lists are kept in RAM, and written to the end of the file  %%% from time to time. It is possible that a considerable amount of  %%% memory is used for a fragmented file. diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl deleted file mode 100644 index 1bf53d91b1..0000000000 --- a/lib/stdlib/src/dets_v8.erl +++ /dev/null @@ -1,1594 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%%     http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% --module(dets_v8). - -%% Dets files, implementation part. This module handles versions up to -%% and including 8(c). To be called from dets.erl only. - --export([mark_dirty/1, read_file_header/2, -         check_file_header/2, do_perform_save/1, initiate_file/11, -         init_freelist/2, fsck_input/4, -         bulk_input/3, output_objs/4, write_cache/1, may_grow/3, -         find_object/2, re_hash/2, slot_objs/2, scan_objs/8, -         db_hash/2, no_slots/1, table_parameters/1]). - --export([file_info/1, v_segments/1]). - --export([cache_segps/3]). - -%% For backward compatibility. --export([sz2pos/1]). - --dialyzer(no_improper_lists). - --compile({inline, [{sz2pos,1},{scan_skip,7}]}). --compile({inline, [{skip_bytes,5}, {get_segp,1}]}). --compile({inline, [{wl_lookup,5}]}). --compile({inline, [{actual_seg_size,0}]}). - --include("dets.hrl"). - -%%  The layout of the file is : -%% -%%   bytes   decsription -%%  ---------------------- File header -%%    4      FreelistsPointer -%%    4      Cookie -%%    4      ClosedProperly (pos=8) -%%    4      Type (pos=12) -%%    4      Version (pos=16) -%%    4      M -%%    4      Next -%%    4      KeyPos -%%    4      NoObjects -%%    4      N -%%  ------------------ end of file header -%%    4*8192 SegmentArray -%%  ------------------ -%%    4*256  First segment -%%  ----------------------------- This is BASE. -%%    ???    Objects (free and alive) -%%    4*256  Second segment (2 kB now, due to a bug) -%%    ???    Objects (free and alive) -%%    ... more objects and segments ... -%%  ----------------------------- -%%    ???    Free lists -%%  ----------------------------- -%%    4      File size, in bytes.  - -%%  The first slot (0) in the segment array always points to the -%%  pre-allocated first segment. -%%  Before we can find an object we must find the slot where the -%%  object resides. Each slot is a (possibly empty) list (or chain) of -%%  objects that hash to the same slot. If the value stored in the -%%  slot is zero, the slot chain is empty. If the slot value is -%%  non-zero, the value points to a position in the file where the -%%  chain starts. Each object in a chain has the following layout: -%% -%%   bytes  decsription -%%  -------------------- -%%    4     Pointer to the next object of the chain. -%%    4     Size of the object in bytes (Sz). -%%    4     Status  (FREE or ACTIVE) -%%    Sz    Binary representing the object -%% -%%  The status field is used while repairing a file (but not next or size). -%% -%%|---------------| -%%|      head     | -%%|       	  | -%%|               | -%%|_______________| -%%|		  |------| -%%|___seg ptr1____|      | -%%|		  |      | -%%|__ seg ptr 2___|      | -%%|               |      |    segment 1 -%%|	....	  |      V _____________ -%%			 |		| -%%			 |		| -%%			 |___slot 0 ____| -%%                       |              | -%%                       |___slot 1 ____|-----| -%%			 |		|     | -%%			 |   .....	|     |  1:st obj in slot 1 -%%					      V  segment 1 -%%						|-----------| -%%						|  next     | -%%						|___________| -%%						|  size     | -%%						|___________| -%%						|  status   | -%%						|___________| -%%						|	    | -%%						|           | -%%						|   obj     | -%%						|           | - -%%% -%%% File header -%%% - --define(HEADSZ, 40).          % The size of the file header, in bytes. --define(SEGSZ, 256).          % Size of a segment, in words. --define(SEGSZ_LOG2, 8). --define(SEGARRSZ, 8192).      % Maximal number of segments. --define(SEGADDR(SegN), (?HEADSZ + (4 * (SegN)))). --define(BASE, ?SEGADDR((?SEGSZ + ?SEGARRSZ))). --define(MAXOBJS, (?SEGSZ * ?SEGARRSZ)). % 2 M objects - --define(SLOT2SEG(S), ((S) bsr ?SEGSZ_LOG2)). - -%% BIG is used for hashing. BIG must be greater than the maximum -%% number of slots, currently MAXOBJS. --define(BIG, 16#ffffff). - -%% Hard coded positions into the file header: --define(FREELIST_POS, 0). --define(CLOSED_PROPERLY_POS, 8). --define(D_POS, 20). --define(NO_OBJECTS_POS, (?D_POS + 12)). - -%% The version of a dets file is indicated by the ClosedProperly -%% field. Version 6 was used in the R1A release, and version 7 in the -%% R1B release up to and including the R3B01 release. Both version 6 -%% and version 7 indicate properly closed files by the value -%% CLOSED_PROPERLY. -%% -%% The current version, 8, has three sub-versions: -%% -%% - 8(a), indicated by the value CLOSED_PROPERLY (same as in versions 6  -%%         and 7), introduced in R3B02; -%% - 8(b), indicated by the value CLOSED_PROPERLY2(_NEED_COMPACTING), -%%         introduced in R5A and used up to and including R6A; -%% - 8(c), indicated by the value CLOSED_PROPERLY_NEW_HASH(_NEED_COMPACTING), -%%         in use since R6B. -%% -%% The difference between the 8(a) and the 8(b) versions is the format -%% used for free lists saved on dets files. -%% The 8(c) version uses a different hashing algorithm, erlang:phash -%% (former versions use erlang:hash). -%% Version 8(b) files are only converted to version 8(c) if repair is -%% done, so we need compatibility with 8(b) for a _long_ time. -%% -%% There are known bugs due to the fact that keys and objects are -%% sometimes compared (==) and sometimes matched (=:=). The version -%% used by default (9, see dets_v9.erl) does not have this problem. - --define(NOT_PROPERLY_CLOSED,0). --define(CLOSED_PROPERLY,1). --define(CLOSED_PROPERLY2,2). --define(CLOSED_PROPERLY2_NEED_COMPACTING,3). --define(CLOSED_PROPERLY_NEW_HASH,4). --define(CLOSED_PROPERLY_NEW_HASH_NEED_COMPACTING,5). - --define(FILE_FORMAT_VERSION, 8). --define(CAN_BUMP_BY_REPAIR, [6, 7]). --define(CAN_CONVERT_FREELIST, [8]). - -%%% -%%% Object header (next, size, status). -%%% - --define(OHDSZ, 12).         % The size of the object header, in bytes. --define(STATUS_POS, 8).     % Position of the status field. - -%% The size of each object is a multiple of 16. -%% BUMP is used when repairing files. --define(BUMP, 16). - --define(ReadAhead, 512). - -%%-define(DEBUGF(X,Y), io:format(X, Y)). --define(DEBUGF(X,Y), void). - -%% -> ok | throw({NewHead,Error}) -mark_dirty(Head) -> -    Dirty = [{?CLOSED_PROPERLY_POS, <<?NOT_PROPERLY_CLOSED:32>>}], -    {_NewHead, ok} = dets_utils:pwrite(Head, Dirty), -    ok = dets_utils:sync(Head), -    {ok, _Pos} = dets_utils:position(Head, Head#head.freelists_p), -    ok = dets_utils:truncate(Head, cur). - -%% -> {ok, head()} | throw(Error) -initiate_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots,  -		Ram, CacheSz, Auto, _DoInitSegments) -> -    Freelist = 0, -    Cookie = ?MAGIC, -    ClosedProperly = ?NOT_PROPERLY_CLOSED, % immediately overwritten -    Version = ?FILE_FORMAT_VERSION, -    Factor = est_no_segments(MinSlots), -    N = 0, -    M = Next = ?SEGSZ * Factor, -    NoObjects = 0, -    dets_utils:pwrite(Fd, Fname, 0,  -                      <<Freelist:32, -                      Cookie:32, -                      ClosedProperly:32, -                      (dets_utils:type_to_code(Type)):32, -                      Version:32, -                      M:32, -                      Next:32, -                      Kp:32, -                      NoObjects:32, -                      N:32, -		      0:(?SEGARRSZ*4)/unit:8, % Initialize SegmentArray -		      0:(?SEGSZ*4)/unit:8>>), % Initialize first segment -    %% We must set the first slot of the segment pointer array to -    %% point to the first segment -    Pos = ?SEGADDR(0), -    SegP = (?HEADSZ + (4 * ?SEGARRSZ)), -    dets_utils:pwrite(Fd, Fname, Pos, <<SegP:32>>), -    segp_cache(Pos, SegP), - -    Ftab = dets_utils:init_alloc(?BASE), -    H0 = #head{freelists=Ftab, fptr = Fd, base = ?BASE}, -    {H1, Ws} = init_more_segments(H0, 1, Factor, undefined, []), - -    %% This is not optimal but simple: always initiate the segments. -    dets_utils:pwrite(Fd, Fname, Ws), - -    %% Return a new nice head structure -    Head = #head{ -      m  = M, -      m2 = M * 2, -      next = Next, -      fptr = Fd, -      no_objects = NoObjects, -      n = N, -      type = Type, -      update_mode = dirty, -      freelists = H1#head.freelists, -      auto_save = Auto, -      hash_bif = phash, -      keypos = Kp, -      min_no_slots = Factor * ?SEGSZ, -      max_no_slots = no_segs(MaxSlots) * ?SEGSZ, -       -      ram_file = Ram,  -      filename = Fname,  -      name = Tab, -      cache = dets_utils:new_cache(CacheSz), -      version = Version, -      bump = ?BUMP, -      base = ?BASE, -      mod = ?MODULE -     }, -    {ok, Head}. - -est_no_segments(MinSlots) when 1 + ?SLOT2SEG(MinSlots) > ?SEGARRSZ -> -    ?SEGARRSZ; -est_no_segments(MinSlots) -> -    1 + ?SLOT2SEG(MinSlots). - -init_more_segments(Head, SegNo, Factor, undefined, Ws) when SegNo < Factor -> -    init_more_segments(Head, SegNo, Factor, seg_zero(), Ws); -init_more_segments(Head, SegNo, Factor, SegZero, Ws) when SegNo < Factor -> -    {NewHead, W} = allocate_segment(Head, SegZero, SegNo), -    init_more_segments(NewHead, SegNo+1, Factor, SegZero, W++Ws); -init_more_segments(Head, _SegNo, _Factor, _SegZero, Ws) -> -    {Head, Ws}. - -allocate_segment(Head, SegZero, SegNo) -> -    %% may throw error: -    {NewHead, Segment, _} = dets_utils:alloc(Head, 4 * ?SEGSZ), -    InitSegment = {Segment, SegZero}, -    Pos = ?SEGADDR(SegNo), -    segp_cache(Pos, Segment), -    SegPointer = {Pos, <<Segment:32>>}, -    {NewHead, [InitSegment, SegPointer]}. - -%% Read free lists (using a Buddy System) from file.  -init_freelist(Head, {convert_freelist,_Version}) -> -    %% This function converts the saved freelist of the form -    %% [{Slot1,Addr1},{Addr1,Addr2},...,{AddrN,0},{Slot2,Addr},...] -    %% i.e each slot is a linked list which ends with a 0. -    %% This is stored in a bplus_tree per Slot. -    %% Each Slot is a position in a tuple. - -    Ftab = dets_utils:empty_free_lists(), -    Pos = Head#head.freelists_p, -    case catch prterm(Head, Pos, ?OHDSZ) of -	{0, _Sz, Term}  -> -	    FreeList1 = lists:reverse(Term), -            FreeList = dets_utils:init_slots_from_old_file(FreeList1, Ftab), -            Head#head{freelists = FreeList, base = ?BASE}; -	_ -> -	    throw({error, {bad_freelists, Head#head.filename}}) -    end; -init_freelist(Head, _) -> -    %% bplus_tree stored as is -    Pos = Head#head.freelists_p, -    case catch prterm(Head, Pos, ?OHDSZ) of -	{0, _Sz, Term}  -> -            Head#head{freelists = Term, base = ?BASE}; -	_ -> -	    throw({error, {bad_freelists, Head#head.filename}}) -    end. - -%% -> {ok, Fd, fileheader()} | throw(Error) -read_file_header(Fd, FileName) -> -    {ok, Bin} = dets_utils:pread_close(Fd, FileName, 0, ?HEADSZ), -    [Freelist, Cookie, CP, Type2, Version, M, Next, Kp, NoObjects, N] =  -	bin2ints(Bin), -    {ok, EOF} = dets_utils:position_close(Fd, FileName, eof), -    {ok, <<FileSize:32>>} = dets_utils:pread_close(Fd, FileName, EOF-4, 4), -    FH = #fileheader{freelist = Freelist, -                     fl_base = ?BASE, -		     cookie = Cookie, -		     closed_properly = CP, -		     type = dets_utils:code_to_type(Type2), -		     version = Version, -		     m = M, -		     next = Next, -		     keypos = Kp, -		     no_objects = NoObjects, -		     min_no_slots = ?DEFAULT_MIN_NO_SLOTS, -		     max_no_slots = ?DEFAULT_MAX_NO_SLOTS, -		     trailer = FileSize, -		     eof = EOF, -		     n = N, -		     mod = ?MODULE}, -    {ok, Fd, FH}. - -%% -> {ok, head(), ExtraInfo} | {error, Reason} (Reason lacking file name) -%% ExtraInfo = {convert_freelist, Version} | true | need_compacting  -check_file_header(FH, Fd) -> -    Test =  -	if -	    FH#fileheader.cookie =/= ?MAGIC -> -		{error, not_a_dets_file}; -	    FH#fileheader.type =:= badtype -> -		{error, invalid_type_code}; -	    FH#fileheader.version =/= ?FILE_FORMAT_VERSION ->  -		case lists:member(FH#fileheader.version, -                                  ?CAN_BUMP_BY_REPAIR) of -		    true -> -			{error, version_bump}; -		    false -> -			{error, bad_version} -		end; -	    FH#fileheader.trailer =/= FH#fileheader.eof -> -		{error, not_closed}; -	    FH#fileheader.closed_properly =:= ?CLOSED_PROPERLY -> -		case lists:member(FH#fileheader.version, -				  ?CAN_CONVERT_FREELIST) of -		    true -> -			{ok, {convert_freelist, FH#fileheader.version}, hash}; -		    false -> -			{error, not_closed} % should not happen -		end; -	    FH#fileheader.closed_properly =:= ?CLOSED_PROPERLY2 -> -		{ok, true, hash}; -	    FH#fileheader.closed_properly =:=  -	          ?CLOSED_PROPERLY2_NEED_COMPACTING  -> -		{ok, need_compacting, hash}; -	    FH#fileheader.closed_properly =:= ?CLOSED_PROPERLY_NEW_HASH -> -		{ok, true, phash}; -	    FH#fileheader.closed_properly =:=  -	         ?CLOSED_PROPERLY_NEW_HASH_NEED_COMPACTING  -> -		{ok, need_compacting, phash}; -	    FH#fileheader.closed_properly =:= ?NOT_PROPERLY_CLOSED -> -		{error, not_closed}; -	    FH#fileheader.closed_properly >  -	         ?CLOSED_PROPERLY_NEW_HASH_NEED_COMPACTING -> -		{error, not_closed}; -	    true -> -		{error, not_a_dets_file} -	end, -    case Test of -	{ok, ExtraInfo, HashAlg} -> -	    H = #head{ -	      m = FH#fileheader.m, -	      m2 = FH#fileheader.m * 2, -	      next = FH#fileheader.next, -	      fptr = Fd, -	      no_objects= FH#fileheader.no_objects, -	      n = FH#fileheader.n, -	      type = FH#fileheader.type, -	      update_mode = saved, -	      auto_save = infinity,             % not saved on file -	      fixed = false,			% not saved on file -	      freelists_p = FH#fileheader.freelist, -	      hash_bif = HashAlg, -	      keypos = FH#fileheader.keypos, -	      min_no_slots = FH#fileheader.min_no_slots, -	      max_no_slots = FH#fileheader.max_no_slots, -	      version = ?FILE_FORMAT_VERSION, -	      mod = ?MODULE, -	      bump = ?BUMP, -	      base = FH#fileheader.fl_base}, -	    {ok, H, ExtraInfo}; -	Error -> -	    Error -    end. - -cache_segps(Fd, FileName, M) -> -    NSegs = no_segs(M), -    {ok, Bin} = dets_utils:pread_close(Fd, FileName, ?HEADSZ, 4 * NSegs), -    Fun = fun(S, P) -> segp_cache(P, S), P+4 end, -    lists:foldl(Fun, ?HEADSZ, bin2ints(Bin)). - -no_segs(NoSlots) -> -    ?SLOT2SEG(NoSlots - 1) + 1. - -bin2ints(<<Int:32, B/binary>>) -> -    [Int | bin2ints(B)]; -bin2ints(<<>>) -> -    []. - -%%% -%%% Repair, conversion and initialization of a dets file. -%%% - -bulk_input(Head, InitFun, Cntrs) -> -    bulk_input(Head, InitFun, Cntrs, make_ref()). - -bulk_input(Head, InitFun, Cntrs, Ref) -> -    fun(close) -> -	    ok; -       (read) -> -	    case catch {Ref, InitFun(read)} of -		{Ref, end_of_input} -> -		    end_of_input; -		{Ref, {L0, NewInitFun}} when is_list(L0),  -                                             is_function(NewInitFun) -> -		    Kp = Head#head.keypos, -		    case catch bulk_objects(L0, Head, Cntrs, Kp, []) of -			{'EXIT', _Error} -> -			    _ = (catch NewInitFun(close)), -			    {error, invalid_objects_list}; -			L -> -			    {L, bulk_input(Head, NewInitFun, Cntrs, Ref)} -		    end; -		{Ref, Value} -> -		    {error, {init_fun, Value}}; -		Error -> -		    throw({thrown, Error}) -	    end -    end. - -bulk_objects([T | Ts], Head, Cntrs, Kp, L) -> -    BT = term_to_binary(T), -    Sz = byte_size(BT), -    LogSz = sz2pos(Sz+?OHDSZ), -    count_object(Cntrs, LogSz), -    Key = element(Kp, T), -    bulk_objects(Ts, Head, Cntrs, Kp, [make_object(Head, Key, LogSz, BT) | L]); -bulk_objects([], _Head, _Cntrs, _Kp, L) -> -    L. - --define(FSCK_SEGMENT, 10000). - --define(DCT(D, CT), [D | CT]). - --define(VNEW(N, E), erlang:make_tuple(N, E)). --define(VSET(I, V, E), setelement(I, V, E)). --define(VGET(I, V), element(I, V)). - -%% OldVersion not used, assuming later versions have been converted already. -output_objs(OldVersion, Head, SlotNumbers, Cntrs) -> -    fun(close) -> -	    {ok, 0, Head}; -       ([]) -> -	    output_objs(OldVersion, Head, SlotNumbers, Cntrs); -       (L) -> -	    %% Descending sizes. -	    Count = lists:sort(ets:tab2list(Cntrs)), -	    RCount = lists:reverse(Count), -	    NoObjects = lists:foldl(fun({_Sz,No}, A) -> A + No end, 0, Count), -	    {_, MinSlots, _} = SlotNumbers, -	    if -		%% Using number of objects for bags and duplicate bags -		%% is not ideal; number of (unique) keys should be -		%% used instead. The effect is that there will be more -		%% segments than "necessary". -		MinSlots =/= bulk_init, -		abs(?SLOT2SEG(NoObjects) - ?SLOT2SEG(MinSlots)) > 5, -		(NoObjects < ?MAXOBJS) -> -		    {try_again, NoObjects}; -		true -> -                    Head1 = Head#head{no_objects = NoObjects}, -		    SegSz = actual_seg_size(), -		    {_, End, _} = dets_utils:alloc(Head, SegSz-1), -		    %% Now {LogSize,NoObjects} in Cntrs is replaced by -		    %% {LogSize,Position,{FileName,FileDescriptor},NoObjects}. -		    {Head2, CT} = allocate_all_objects(Head1, RCount, Cntrs), -		    [E | Es] = bin2term(L, []), -		    {NE, Acc, DCT1} =  -			output_slots(E, Es, [E], Head2, ?DCT(0, CT)), -		    NDCT = write_all_sizes(DCT1, Cntrs), -		    Max = ets:info(Cntrs, size), -                    output_objs2(NE, Acc, Head2, Cntrs, NDCT, End, Max,Max) -	    end -    end. - -output_objs2(E, Acc, Head, Cntrs, DCT, End, 0, MaxNoChunks) -> -    NDCT = write_all_sizes(DCT, Cntrs), -    output_objs2(E, Acc, Head, Cntrs, NDCT, End, MaxNoChunks, MaxNoChunks); -output_objs2(E, Acc, Head, Cntrs, DCT, End, ChunkI, MaxNoChunks) -> -    fun(close) -> -	    DCT1 = output_slot(Acc, Head, DCT), -	    NDCT = write_all_sizes(DCT1, Cntrs), -	    ?DCT(NoDups, CT) = NDCT, -	    [SegAddr | []] = ?VGET(tuple_size(CT), CT), -            FinalZ = End - SegAddr, -            [{?FSCK_SEGMENT, _, {FileName, Fd}, _}] =  -		ets:lookup(Cntrs, ?FSCK_SEGMENT), -	    ok = dets_utils:fwrite(Fd, FileName,  -				   dets_utils:make_zeros(FinalZ)), -            NewHead = Head#head{no_objects = Head#head.no_objects - NoDups}, -	    {ok, NoDups, NewHead}; -       (L) -> -	    Es = bin2term(L, []), -	    {NE, NAcc, NDCT} = output_slots(E, Es, Acc, Head, DCT), -	    output_objs2(NE, NAcc, Head, Cntrs, NDCT, End,  -			 ChunkI-1, MaxNoChunks) -    end. - -%% By allocating bigger objects before smaller ones, holes in the -%% buddy system memory map are avoided. Unfortunately, the segments -%% are always allocated first, so if there are objects bigger than a -%% segment, there is a hole to handle. (Haven't considered placing the -%% segments among other objects of the same size.) -allocate_all_objects(Head, Count, Cntrs) -> -    SegSize = actual_seg_size(), -    {Head1, HSz, HN, HA} = alloc_hole(Count, Head, SegSize), -    {Max, _} = hd(Count), -    CT = ?VNEW(Max+1, not_used), -    {Head2, NCT} = allocate_all(Head1, Count, Cntrs, CT), -    Head3 = free_hole(Head2, HSz, HN, HA), -    {Head3, NCT}. - -alloc_hole([{LSize,_} | _], Head, SegSz) when ?POW(LSize-1) > SegSz -> -    {_, SegAddr, _} = dets_utils:alloc(Head, SegSz-1), -    Size = ?POW(LSize-1)-1, -    {_, Addr, _} = dets_utils:alloc(Head, Size), -    N = (Addr - SegAddr) div SegSz, -    Head1 = dets_utils:alloc_many(Head, SegSz, N, SegAddr), -    {Head1, SegSz-1, N, SegAddr}; -alloc_hole(_Count, Head, _SegSz) -> -    {Head, 0, 0, 0}. - -free_hole(Head, _Size, 0, _Addr) -> -    Head; -free_hole(Head, Size, N, Addr) -> -    {Head1, _} = dets_utils:free(Head, Addr, Size), -    free_hole(Head1, Size, N-1, Addr+Size+1). - -%% One (temporary) file for each buddy size, write all objects of that -%% size to the file. -allocate_all(Head, [{LSize,NoObjects} | Count], Cntrs, CT) -> -    Size = ?POW(LSize-1)-1, -    {_Head, Addr, _} = dets_utils:alloc(Head, Size), -    NewHead = dets_utils:alloc_many(Head, Size+1, NoObjects, Addr), -    {FileName, Fd} = temp_file(Head, LSize), -    true = ets:insert(Cntrs, {LSize, Addr, {FileName, Fd}, NoObjects}), -    NCT = ?VSET(LSize, CT, [Addr | []]), -    allocate_all(NewHead, Count, Cntrs, NCT); -allocate_all(Head, [], Cntrs, CT) -> -    %% Note that space for the segments has been allocated already. -    %% And one file for the segments... -    {FileName, Fd} = temp_file(Head, ?FSCK_SEGMENT), -    Addr = ?SEGADDR(?SEGARRSZ), -    true = ets:insert(Cntrs, {?FSCK_SEGMENT, Addr, {FileName, Fd}, 0}), -    NCT = ?VSET(tuple_size(CT), CT, [Addr | []]), -    {Head, NCT}. - -temp_file(Head, N) -> -    TmpName = lists:concat([Head#head.filename, '.', N]), -    {ok, Fd} = dets_utils:open(TmpName, [raw, binary, write]), -    {TmpName, Fd}. - -bin2term([<<Slot:32, LogSize:8, BinTerm/binary>> | BTs], L) -> -    bin2term(BTs, [{Slot, LogSize, BinTerm} | L]); -bin2term([], L) -> -    lists:reverse(L). - -write_all_sizes(?DCT(D, CT), Cntrs) -> -    ?DCT(D, write_sizes(1, tuple_size(CT), CT, Cntrs)). - -write_sizes(Sz, Sz, CT, Cntrs) -> -    write_size(Sz, ?FSCK_SEGMENT, CT, Cntrs); -write_sizes(Sz, MaxSz, CT, Cntrs) -> -    NCT = write_size(Sz, Sz, CT, Cntrs), -    write_sizes(Sz+1, MaxSz, NCT, Cntrs). - -write_size(Sz, I, CT, Cntrs) -> -    case ?VGET(Sz, CT) of -	not_used -> -	    CT; -	[Addr | L] -> -	    {FileName, Fd} = ets:lookup_element(Cntrs, I, 3), -	    case file:write(Fd, lists:reverse(L)) of -		ok -> -		    ?VSET(Sz, CT, [Addr | []]); -		Error -> -		    dets_utils:file_error(FileName, Error) -	    end -    end. - -output_slots(E, [E1 | Es], Acc, Head, DCT)  -                       when element(1, E) =:= element(1, E1) -> -    output_slots(E1, Es, [E1 | Acc], Head, DCT); -output_slots(_E, [E | L], Acc, Head, DCT) -> -    NDCT = output_slot(Acc, Head, DCT), -    output_slots(E, L, [E], Head, NDCT); -output_slots(E, [], Acc, _Head, DCT) -> -    {E, Acc, DCT}. - -output_slot([E], _Head, ?DCT(D, CT)) -> -    ?DCT(D, output_slot([{foo, E}], 0, foo, CT)); -output_slot(Es0, Head, ?DCT(D, CT)) -> -    Kp = Head#head.keypos, -    Fun = fun({_Slot, _LSize, BinTerm} = E) ->  -		  Key = element(Kp, binary_to_term(BinTerm)), -		  {Key, E} -	  end, -    Es = lists:map(Fun, Es0), -    NEs = case Head#head.type of -	      set -> -		  [{Key0,_} = E | L0] = lists:sort(Es), -		  choose_one(lists:sort(L0), Key0, [E]); -	      bag ->  -		  lists:usort(Es); -	      duplicate_bag ->  -		  lists:sort(Es) -	  end, -    Dups = D + length(Es) - length(NEs), -    ?DCT(Dups, output_slot(NEs, 0, foo, CT)). - -choose_one([{Key,_} | Es], Key, L) -> -    choose_one(Es, Key, L); -choose_one([{Key,_} = E | Es], _Key, L) -> -    choose_one(Es, Key, [E | L]); -choose_one([], _Key, L) -> -    L. - -output_slot([E | Es], Next, _Slot, CT) -> -    {_Key, {Slot, LSize, BinTerm}} = E, -    Size = byte_size(BinTerm), -    Size2 = ?POW(LSize-1), -    Pad = <<0:(Size2-Size-?OHDSZ)/unit:8>>, -    BinObject = [<<Next:32, Size:32, ?ACTIVE:32>>, BinTerm | Pad], -    [Addr | L] = ?VGET(LSize, CT), -    NCT = ?VSET(LSize, CT, [Addr+Size2 | [BinObject | L]]), -    output_slot(Es, Addr, Slot, NCT); -output_slot([], Next, Slot, CT) -> -    I = tuple_size(CT), -    [Addr | L] = ?VGET(I, CT), -    {Pos, _} = slot_position(Slot), -    NoZeros = Pos - Addr, -    BinObject = if  -		    NoZeros > 100 -> -			[dets_utils:make_zeros(NoZeros) | <<Next:32>>]; -		    true -> -			<<0:NoZeros/unit:8,Next:32>> -   	        end, -    Size = NoZeros+4, -    ?VSET(I, CT, [Addr+Size | [BinObject | L]]). - -%% Does not close Fd. -fsck_input(Head, Fd, Cntrs, _FileHeader) -> -    %% The file is not compressed, so the object size cannot exceed -    %% the filesize, for all objects. -    MaxSz = case file:position(Fd, eof) of -                {ok, Pos} -> -                    Pos; -                _ -> -                    (1 bsl 32) - 1 -            end, -    State0 = fsck_read(?BASE, Fd, []), -    fsck_input1(Head, State0, Fd, MaxSz, Cntrs). - -fsck_input1(Head, State, Fd, MaxSz, Cntrs) -> -    fun(close) -> -	    ok; -       (read) -> -	    case State of -		done -> -		    end_of_input; -		{done, L} -> -		    R = count_input(Cntrs, L, []), -		    {R, fsck_input1(Head, done, Fd, MaxSz, Cntrs)}; -		{cont, L, Bin, Pos} -> -		    R = count_input(Cntrs, L, []), -                    FR = fsck_objs(Bin, Head#head.keypos, Head, []), -		    NewState = fsck_read(FR, Pos, Fd, MaxSz, Head), -		    {R, fsck_input1(Head, NewState, Fd, MaxSz, Cntrs)} -	    end -    end. - -%% The ets table Cntrs is used for counting objects per size. -count_input(Cntrs, [[LogSz | B] | Ts], L) -> -    count_object(Cntrs, LogSz), -    count_input(Cntrs, Ts, [B | L]); -count_input(_Cntrs, [], L) -> -    L. - -count_object(Cntrs, LogSz) -> -    case catch ets:update_counter(Cntrs, LogSz, 1) of -	N when is_integer(N) -> ok; -	_Badarg -> true = ets:insert(Cntrs, {LogSz, 1}) -    end. - -fsck_read(Pos, F, L) -> -    case file:position(F, Pos) of -	{ok, _} -> -	    read_more_bytes(<<>>, 0, Pos, F, L); -	_Error -> -	    {done, L} -    end. - -fsck_read({more, Bin, Sz, L}, Pos, F, MaxSz, Head) when Sz > MaxSz -> -    FR = skip_bytes(Bin, ?BUMP, Head#head.keypos, Head, L), -    fsck_read(FR, Pos, F, MaxSz, Head); -fsck_read({more, Bin, Sz, L}, Pos, F, _MaxSz, _Head) -> -    read_more_bytes(Bin, Sz, Pos, F, L); -fsck_read({new, Skip, L}, Pos, F, _MaxSz, _Head) -> -    NewPos = Pos + Skip, -    fsck_read(NewPos, F, L). - -read_more_bytes(B, Min, Pos, F, L) -> -    Max = if  -	      Min < ?CHUNK_SIZE -> ?CHUNK_SIZE;  -	      true -> Min  -	  end, -    case dets_utils:read_n(F, Max) of -	eof -> -	    {done, L}; -	Bin -> -	    NewPos = Pos + byte_size(Bin), -	    {cont, L, list_to_binary([B, Bin]), NewPos} -    end. - -fsck_objs(Bin = <<_N:32, Sz:32, Status:32, Tail/binary>>, Kp, Head, L) -> -    if  -	Status =:= ?ACTIVE -> -	    case Tail of -		<<BinTerm:Sz/binary, Tail2/binary>> -> -		    case catch element(Kp, binary_to_term(BinTerm)) of -			{'EXIT', _} -> -			    skip_bytes(Bin, ?BUMP, Kp, Head, L); -			Key -> -			    LogSz = sz2pos(Sz+?OHDSZ), -			    Obj = make_object(Head, Key, LogSz, BinTerm), -			    NL = [[LogSz | Obj] | L], -			    Skip = ?POW(LogSz-1) - Sz - ?OHDSZ, -			    skip_bytes(Tail2, Skip, Kp, Head, NL) -		    end; -		_ -> -                    {more, Bin, Sz, L} -	    end; -	true ->  -	    skip_bytes(Bin, ?BUMP, Kp, Head, L) -    end; -fsck_objs(Bin, _Kp, _Head, L) -> -    {more, Bin, 0, L}. -     -%% Version 8 has to know about version 9. -make_object(Head, Key, _LogSz, BT) when Head#head.version =:= 9 -> -    Slot = dets_v9:db_hash(Key, Head), -    <<Slot:32, BT/binary>>; -make_object(Head, Key, LogSz, BT) -> -    Slot = db_hash(Key, Head), -    <<Slot:32, LogSz:8, BT/binary>>. - -%% Inlined. -skip_bytes(Bin, Skip, Kp, Head, L) -> -    case Bin of -	<<_:Skip/binary, Tail/binary>> -> -	    fsck_objs(Tail, Kp, Head, L); -	_ -> -            {new, Skip - byte_size(Bin), L} -    end. - -%% -> {NewHead, ok} | throw({Head, Error}) -do_perform_save(H) -> -    FL = dets_utils:get_freelists(H), -    B = term_to_binary(FL), -    Size = byte_size(B), -    ?DEBUGF("size of freelist = ~p~n", [Size]), -    ?DEBUGF("head.m = ~p~n", [H#head.m]), -    ?DEBUGF("head.no_objects = ~p~n", [H#head.no_objects]), - -    {ok, Pos} = dets_utils:position(H, eof), -    H1 = H#head{freelists_p = Pos}, -    W1 = {?FREELIST_POS, <<Pos:32>>}, -    W2 = {Pos, [<<0:32, Size:32, ?FREE:32>>, B]}, -     -    W3 = {?D_POS, <<(H1#head.m):32,  -	            (H1#head.next):32,  -	            (H1#head.keypos):32, -	            (H1#head.no_objects):32, -		    (H1#head.n):32>>}, -    {ClosedProperly, ClosedProperlyNeedCompacitng} =  -	case H1#head.hash_bif of -	    hash -> -		{?CLOSED_PROPERLY2, ?CLOSED_PROPERLY2_NEED_COMPACTING}; -	    phash -> -		{?CLOSED_PROPERLY_NEW_HASH,  -		 ?CLOSED_PROPERLY_NEW_HASH_NEED_COMPACTING} -	end, -    W4 =  -	if  -	    Size > 1000, Size > H1#head.no_objects -> -		{?CLOSED_PROPERLY_POS,  -		 <<ClosedProperlyNeedCompacitng:32>>}; -	    true -> -		{?CLOSED_PROPERLY_POS, <<ClosedProperly:32>>} -	end, -    W5 = {?FILE_FORMAT_VERSION_POS, <<?FILE_FORMAT_VERSION:32>>}, -    {H2, ok} = dets_utils:pwrite(H1, [W1,W2,W3,W4,W5]), -    {ok, Pos2} = dets_utils:position(H2, eof), -    ?DEBUGF("Writing file size ~p, eof at ~p~n", [Pos2+4, Pos2]), -    dets_utils:pwrite(H2, [{Pos2, <<(Pos2 + 4):32>>}]). - -%% -> [term()] | throw({Head, Error}) -slot_objs(H, Slot) when Slot >= H#head.next -> -    '$end_of_table'; -slot_objs(H, Slot) -> -    {_Pos, Chain} = chain(H, Slot), -    collect_chain(H, Chain). - -collect_chain(_H, 0) -> []; -collect_chain(H, Pos) -> -    {Next, _Sz, Term} = prterm(H, Pos, ?ReadAhead), -    [Term | collect_chain(H, Next)]. - -db_hash(Key, Head) -> -    H = h(Key, Head#head.hash_bif), -    Hash = H rem Head#head.m, -    if -	Hash < Head#head.n -> -	    H rem (Head#head.m2); % H rem (2 * m) -	true -> -	    Hash -    end. - -h(I, phash) -> erlang:phash(I, ?BIG) - 1; -h(I, HF) -> erlang:HF(I, ?BIG) - 1. %% stupid BIF has 1 counts. - -no_slots(_Head) -> -    undefined. - -table_parameters(_Head) -> -    undefined. - -%% Re-hashing a segment, starting with SlotStart. -%% -%% On the average, half of the objects of the chain are put into a new -%% chain. If the slot of the old chain is i, then the slot of the new -%% chain is i+m. -%% Note that the insertion of objects into the new chain is simplified -%% by the fact that the chains are not sorted on key, which means that -%% each moved object can be inserted first in the new chain. -%% (It is also a fact that the objects with the same key are not sorted.) -%% -%% -> {ok, Writes} | throw({Head, Error}) -re_hash(Head, SlotStart) -> -    {SlotPos, _4} = slot_position(SlotStart), -    {ok, Bin} = dets_utils:pread(Head, SlotPos, 4*?SEGSZ, 0), -    {Read, Cs} = split_bin(SlotPos, Bin, [], []), -    re_hash_read(Head, [], Read, Cs). - -split_bin(Pos, <<P:32, B/binary>>, R, Cs) -> -    if -	P =:= 0 -> -	    split_bin(Pos+4, B, R, Cs); -	true -> -	    split_bin(Pos+4, B, [{P,?ReadAhead} | R], [[Pos] | Cs]) -    end; -split_bin(_Pos, <<>>, R, Cs) -> -    {R, Cs}. - -re_hash_read(Head, Cs, R, RCs) -> -    {ok, Bins} = dets_utils:pread(R, Head), -    re_hash_read(Head, R, RCs, Bins, Cs, [], []). - -re_hash_read(Head, [{Pos, Size} | Ps], [C | Cs],  -	     [<<Next:32, Sz:32, _Status:32, Bin0/binary>> | Bins],  -	     DoneCs, R, RCs) -> -    case byte_size(Bin0) of -	BinSz when BinSz >= Sz -> -	    case catch binary_to_term(Bin0) of -		{'EXIT', _Error} -> -		    throw(dets_utils:corrupt_reason(Head, bad_object)); -		Term -> -		    Key = element(Head#head.keypos, Term), -		    New = h(Key, Head#head.hash_bif) rem Head#head.m2, -		    NC = case New >= Head#head.m of -			     true -> [{Pos,New} | C]; -			     false -> [Pos | C] -			 end, -		    if -			Next =:= 0 -> -			    NDoneCs = [NC | DoneCs],  -			    re_hash_read(Head, Ps, Cs, Bins, NDoneCs, R, RCs); -			true -> -			    NR = [{Next,?ReadAhead} | R], -			    NRCs = [NC | RCs], -			    re_hash_read(Head, Ps, Cs, Bins, DoneCs, NR, NRCs) -		    end -	    end; -	BinSz when Size =:= BinSz+?OHDSZ -> -	    NR = [{Pos, Sz+?OHDSZ} | R], -	    re_hash_read(Head, Ps, Cs, Bins, DoneCs, NR, [C | RCs]); -	_BinSz -> -	    throw({Head, {error, {premature_eof, Head#head.filename}}}) -    end; -re_hash_read(Head, [], [], [], Cs, [], []) -> -    re_hash_traverse_chains(Cs, Head, [], [], []); -re_hash_read(Head, [], [], [], Cs, R, RCs) -> -    re_hash_read(Head, Cs, R, RCs). - -re_hash_traverse_chains([C | Cs], Head, Rs, Ns, Ws) -> -    case re_hash_find_new(C, Rs, start, start) of -	false -> -	    re_hash_traverse_chains(Cs, Head, Rs, Ns, Ws); -	{NRs, FirstNew, LastNew} ->  -	    LastInNew = case C of -			    [{_,_} | _] -> true; -			    _ -> false -			end, -	    N = {FirstNew, LastNew, LastInNew}, -	    NWs = re_hash_link(C, start, start, start, Ws), -	    re_hash_traverse_chains(Cs, Head, NRs, [N | Ns], NWs) -    end; -re_hash_traverse_chains([], Head, Rs, Ns, Ws) -> -    {ok, Bins} = dets_utils:pread(Rs, Head), -    {ok, insert_new(Rs, Bins, Ns, Ws)}. - -re_hash_find_new([{Pos,NewSlot} | C], R, start, start) -> -    {SPos, _4} = slot_position(NewSlot), -    re_hash_find_new(C, [{SPos,4} | R], Pos, Pos); -re_hash_find_new([{Pos,_SPos} | C], R, _FirstNew, LastNew) -> -    re_hash_find_new(C, R, Pos, LastNew); -re_hash_find_new([_Pos | C], R, FirstNew, LastNew) -> -    re_hash_find_new(C, R, FirstNew, LastNew); -re_hash_find_new([], _R, start, start) -> -    false; -re_hash_find_new([], R, FirstNew, LastNew) -> -    {R, FirstNew, LastNew}. - -re_hash_link([{Pos,_SPos} | C], LastOld, start, _LastInNew, Ws) -> -    re_hash_link(C, LastOld, Pos, true, Ws); -re_hash_link([{Pos,_SPos} | C], LastOld, LastNew, false, Ws) -> -    re_hash_link(C, LastOld, Pos, true, [{Pos,<<LastNew:32>>} | Ws]); -re_hash_link([{Pos,_SPos} | C], LastOld, _LastNew, LastInNew, Ws) -> -    re_hash_link(C, LastOld, Pos, LastInNew, Ws); -re_hash_link([Pos | C], start, LastNew, true, Ws) -> -    re_hash_link(C, Pos, LastNew, false, [{Pos,<<0:32>>} | Ws]); -re_hash_link([Pos | C], LastOld, LastNew, true, Ws) -> -    re_hash_link(C, Pos, LastNew, false, [{Pos,<<LastOld:32>>} | Ws]); -re_hash_link([Pos | C], _LastOld, LastNew, LastInNew, Ws) -> -    re_hash_link(C, Pos, LastNew, LastInNew, Ws); -re_hash_link([], _LastOld, _LastNew, _LastInNew, Ws) -> -    Ws. - -insert_new([{NewSlotPos,_4} | Rs], [<<P:32>> = PB | Bins], [N | Ns], Ws) -> -    {FirstNew, LastNew, LastInNew} = N, -    Ws1 = case P of -	      0 when LastInNew -> -		  Ws; -	      0 -> -		  [{LastNew, <<0:32>>} | Ws]; -	      _ -> -		  [{LastNew, PB} | Ws] -	  end, -    NWs = [{NewSlotPos, <<FirstNew:32>>} | Ws1], -    insert_new(Rs, Bins, Ns, NWs); -insert_new([], [], [], Ws) -> -    Ws. - -%% When writing the cache, a 'work list' is first created: -%%   WorkList = [{Key, {Delete,Lookup,[Inserted]}}] -%%   Delete = keep | delete -%%   Lookup = skip | lookup -%%   Inserted = {object(), No} -%%   No = integer() -%% If No =< 0 then there will be -No instances of object() on the file -%% when the cache has been written. If No > 0 then No instances of -%% object() will be added to the file. -%% If Delete has the value 'delete', then all objects with the key Key -%% have been deleted. (This could be viewed as a shorthand for {Object,0} -%% for each object Object on the file not mentioned in some Inserted.) -%% If Lookup has the value 'lookup', all objects with the key Key will -%% be returned. -%% - -%% -> {NewHead, [LookedUpObject], pwrite_list()} | throw({NewHead, Error}) -write_cache(Head) -> -    #head{cache = C, type = Type} = Head, -    case dets_utils:is_empty_cache(C) of -	true -> {Head, [], []}; -	false -> -	    {NewC, _MaxInserts, PerKey} = dets_utils:reset_cache(C), -	    %% NoInsertedKeys is an upper limit on the number of new keys. -	    {WL, NoInsertedKeys} = make_wl(PerKey, Type), -	    Head1 = Head#head{cache = NewC}, -	    case may_grow(Head1, NoInsertedKeys, once) of -		{Head2, ok} -> -		    eval_work_list(Head2, WL); -		HeadError -> -		    throw(HeadError) -	    end -    end. - -make_wl(PerKey, Type) -> -    make_wl(PerKey, Type, [], 0). - -make_wl([{Key,L} | PerKey], Type, WL, Ins) -> -    [Cs | I] = wl(L, Type), -    make_wl(PerKey, Type, [{Key,Cs} | WL], Ins+I); -make_wl([], _Type, WL, Ins) -> -    {WL, Ins}. - -wl(L, Type) -> -    wl(L, Type, keep, skip, 0, []). - -wl([{_Seq, delete_key} | Cs], Type, _Del, Lookup, _I, _Objs) -> -    wl(Cs, Type, delete, Lookup, 0, []); -wl([{_Seq, {delete_object, Object}} | Cs], Type, Del, Lookup, I, Objs) -> -    NObjs = lists:keydelete(Object, 1, Objs), -    wl(Cs, Type, Del, Lookup, I, [{Object,0} | NObjs]); -wl([{_Seq, {insert, Object}} | Cs], Type, _Del, Lookup, _I, _Objs)  -                    when Type =:= set -> -    wl(Cs, Type, delete, Lookup, 1, [{Object,-1}]); -wl([{_Seq, {insert, Object}} | Cs], Type, Del, Lookup, _I, Objs) -> -    NObjs =  -	case lists:keyfind(Object, 1, Objs) of -	    {_, 0} -> -		lists:keyreplace(Object, 1, Objs, {Object,-1}); -	    {_, _C} when Type =:= bag -> % C =:= 1; C =:= -1 -		Objs; -	    {_, C} when C < 0 -> % when Type =:= duplicate_bag -		lists:keyreplace(Object, 1, Objs, {Object,C-1}); -	    {_, C} -> % when C > 0, Type =:= duplicate_bag -		lists:keyreplace(Object, 1, Objs, {Object,C+1}); -	    false when Del =:= delete -> -		[{Object, -1} | Objs]; -	    false -> -		[{Object, 1} | Objs] -	end, -    wl(Cs, Type, Del, Lookup, 1, NObjs); -wl([{_Seq, {lookup,_Pid}=Lookup} | Cs], Type, Del, _Lookup, I, Objs) -> -    wl(Cs, Type, Del, Lookup, I, Objs); -wl([], _Type, Del, Lookup, I, Objs) -> -    [{Del, Lookup, Objs} | I]. - -%% -> {NewHead, ok} | {NewHead, Error} -may_grow(Head, 0, once) -> -    {Head, ok}; -may_grow(Head, _N, _How) when Head#head.fixed =/= false -> -    {Head, ok}; -may_grow(#head{access = read}=Head, _N, _How) -> -    {Head, ok}; -may_grow(Head, _N, _How) when Head#head.next >= ?MAXOBJS -> -    {Head, ok}; -may_grow(Head, N, How) -> -    Extra = erlang:min(2*?SEGSZ, Head#head.no_objects + N - Head#head.next), -    case catch may_grow1(Head, Extra, How) of -	{error, Reason} -> % alloc may throw error -	    {Head, {error, Reason}}; -	Reply -> -	    Reply -    end. - -may_grow1(Head, Extra, many_times) when Extra > ?SEGSZ -> -    Reply = grow(Head, 1, undefined), -    self() ! ?DETS_CALL(self(), may_grow), -    Reply; -may_grow1(Head, Extra, _How) ->     -    grow(Head, Extra, undefined). - -%% -> {Head, ok} | throw({Head, Error}) -grow(Head, Extra, _SegZero) when Extra =< 0 -> -    {Head, ok}; -grow(Head, Extra, undefined) -> -    grow(Head, Extra, seg_zero()); -grow(Head, Extra, SegZero) -> -    #head{n = N, next = Next, m = M} = Head, -    SegNum = ?SLOT2SEG(Next), -    {Head0, Ws1} = allocate_segment(Head, SegZero, SegNum), -    {Head1, ok} = dets_utils:pwrite(Head0, Ws1), -    %% If re_hash fails, segp_cache has been called, but it does not matter. -    {ok, Ws2} = re_hash(Head1, N), -    {Head2, ok} = dets_utils:pwrite(Head1, Ws2), -    NewHead = -	if  -	    N + ?SEGSZ =:= M -> -		Head2#head{n = 0, next = Next + ?SEGSZ, m = 2 * M, m2 = 4 * M}; -	    true -> -		Head2#head{n = N + ?SEGSZ, next = Next + ?SEGSZ} -	end, -    grow(NewHead, Extra - ?SEGSZ, SegZero). - -seg_zero() -> -    <<0:(4*?SEGSZ)/unit:8>>. - -find_object(Head, Object) -> -    Key = element(Head#head.keypos, Object), -    Slot = db_hash(Key, Head), -    find_object(Head, Object, Slot).     - -find_object(H, _Obj, Slot) when Slot >= H#head.next -> -    false; -find_object(H, Obj, Slot) -> -    {_Pos, Chain} = chain(H, Slot), -    case catch find_obj(H, Obj, Chain) of -	{ok, Pos} -> -	    {ok, Pos}; -	_Else -> -	    false -    end. - -find_obj(H, Obj, Pos) when Pos > 0 -> -    {Next, _Sz, Term} = prterm(H, Pos, ?ReadAhead), -    if  -	Term == Obj -> -	    {ok, Pos}; -	true -> -	    find_obj(H, Obj, Next) -    end. - -%% Given, a slot, return the {Pos, Chain} in the file where the -%% objects hashed to this slot reside. Pos is the position in the -%% file where the chain pointer is written and Chain is the position -%% in the file where the first object resides. -chain(Head, Slot) -> -    Pos = ?SEGADDR(?SLOT2SEG(Slot)), -    Segment = get_segp(Pos), -    FinalPos = Segment + (4 * ?REM2(Slot, ?SEGSZ)), -    {ok, <<Chain:32>>} = dets_utils:pread(Head, FinalPos, 4, 0), -    {FinalPos, Chain}. - -%%% -%%% Cache routines depending on the dets file format. -%%% - -%% -> {Head, [LookedUpObject], pwrite_list()} | throw({Head, Error}) -eval_work_list(Head, WorkLists) -> -    SWLs = tag_with_slot(WorkLists, Head, []), -    P1 = dets_utils:family(SWLs),  -    {PerSlot, SlotPositions} = remove_slot_tag(P1, [], []), -    {ok, Bins} = dets_utils:pread(SlotPositions, Head), -    first_object(PerSlot, SlotPositions, Bins, Head, [], [], [], []). - -tag_with_slot([{K,_} = WL | WLs], Head, L) -> -    tag_with_slot(WLs, Head, [{db_hash(K, Head), WL} | L]); -tag_with_slot([], _Head, L) -> -    L. - -remove_slot_tag([{S,SWLs} | SSWLs], Ls, SPs) -> -    remove_slot_tag(SSWLs, [SWLs | Ls], [slot_position(S) | SPs]); -remove_slot_tag([], Ls, SPs) -> -    {Ls, SPs}. - -%% The initial chain pointers and the first object in each chain are -%% read "in parallel", that is, with one call to file:pread/2 (two -%% calls altogether). The following chain objects are read one by -%% one. This is a compromise: if the chains are long and threads are -%% active, it would be faster to keep a state for each chain and read -%% the objects of the chains in parallel, but the overhead would be -%% quite substantial. - -first_object([WorkLists | SPs], [{P1,_4} | Ss], [<<P2:32>> | Bs], Head, -	      ObjsToRead, ToRead, Ls, LU) when P2 =:= 0 -> -    L0 = [{old,P1}], -    {L, NLU} = eval_slot(Head, ?ReadAhead, P2, WorkLists, L0, LU), -    first_object(SPs, Ss, Bs, Head, ObjsToRead, ToRead, [L | Ls], NLU); -first_object([WorkLists | SPs], [{P1,_4} | Ss], [<<P2:32>> | Bs], Head,  -	      ObjsToRead, ToRead, Ls, LU) -> -    E = {P1,P2,WorkLists}, -    first_object(SPs, Ss, Bs, Head,  -		 [E | ObjsToRead], [{P2, ?ReadAhead} | ToRead], Ls, LU); -first_object([], [], [], Head, ObjsToRead, ToRead, Ls, LU) -> -    {ok, Bins} = dets_utils:pread(ToRead, Head), -    case catch eval_first(Bins, ObjsToRead, Head, Ls, LU) of -	{ok, NLs, NLU} ->  -	    case create_writes(NLs, Head, [], 0) of -		{Head1, [], 0} -> -		    {Head1, NLU, []}; -		{Head1, Ws, No} -> -		    {NewHead, Ws2} = update_no_objects(Head1, Ws, No), -		    {NewHead, NLU, Ws2} -	    end; -	_Error ->  -	    throw(dets_utils:corrupt_reason(Head, bad_object)) -    end. - -%% Update no_objects on the file too, if the number of segments that -%% dets:fsck/6 use for estimate has changed. -update_no_objects(Head, Ws, 0) -> {Head, Ws}; -update_no_objects(Head, Ws, Delta) -> -    No = Head#head.no_objects, -    NewNo = No + Delta, -    NWs =  -	if  -	    NewNo > ?MAXOBJS -> -		Ws; -	    ?SLOT2SEG(No) =:= ?SLOT2SEG(NewNo) -> -		Ws; -	    true -> -		[{?NO_OBJECTS_POS, <<NewNo:32>>} | Ws] -	end, -    {Head#head{no_objects = NewNo}, NWs}. - -eval_first([<<Next:32, Sz:32, _Status:32, Bin/binary>> | Bins],  -	   [SP | SPs], Head, Ls, LU) -> -    {P1, P2, WLs} = SP, -    L0 = [{old,P1}], -    case byte_size(Bin) of -	BinSz when BinSz >= Sz -> -	    Term = binary_to_term(Bin), -	    Key = element(Head#head.keypos, Term), -	    {L, NLU} = find_key(Head, P2, Next, Sz, Term, Key, WLs, L0, LU), -	    eval_first(Bins, SPs, Head, [L | Ls], NLU); -	_BinSz -> -	    {L, NLU} = eval_slot(Head, Sz+?OHDSZ, P2, WLs, L0, LU), -	    eval_first(Bins, SPs, Head, [L | Ls], NLU) -    end; -eval_first([], [], _Head, Ls, LU) -> -    {ok, Ls, LU}. - -eval_slot(_Head, _TrySize, _Pos=0, [], L, LU) -> -    {L, LU}; -eval_slot(Head, _TrySize, Pos=0, [WL | WLs], L, LU) -> -    {_Key, {_Delete, LookUp, Objects}} = WL, -    {NL, NLU} = end_of_key(Objects, LookUp, L, []), -    eval_slot(Head, ?ReadAhead, Pos, WLs, NL, NLU++LU); -eval_slot(Head, TrySize, Pos, WLs, L, LU) -> -    {NextPos, Size, Term} = prterm(Head, Pos, TrySize), -    Key = element(Head#head.keypos, Term), -    find_key(Head, Pos, NextPos, Size, Term, Key, WLs, L, LU). - -find_key(Head, Pos, NextPos, Size, Term, Key, WLs, L, LU) -> -    case lists:keyfind(Key, 1, WLs) of -	{_, {Delete, LookUp, Objects}} = WL -> -	    NWLs = lists:delete(WL, WLs), -	    {NewObjects, NL, LUK} = eval_object(Size, Term, Delete, LookUp,  -						Objects, Head, Pos, L, []), -	    eval_key(Key, Delete, LookUp, NewObjects, Head, NextPos,  -		     NWLs, NL, LU, LUK); -	false -> -	    L0 = [{old,Pos} | L], -	    eval_slot(Head, ?ReadAhead, NextPos, WLs, L0, LU) -    end. - -eval_key(_Key, _Delete, Lookup, _Objects, Head, Pos, WLs, L, LU, LUK)  -                            when Head#head.type =:= set -> -    NLU = case Lookup of -	      {lookup, Pid} -> [{Pid,LUK} | LU]; -	      skip -> LU -	  end, -    eval_slot(Head, ?ReadAhead, Pos, WLs, L, NLU); -eval_key(_Key, _Delete, LookUp, Objects, Head, Pos, WLs, L, LU, LUK)  -                                                          when Pos =:= 0 -> -    {NL, NLU} = end_of_key(Objects, LookUp, L, LUK), -    eval_slot(Head, ?ReadAhead, Pos, WLs, NL, NLU++LU); -eval_key(Key, Delete, LookUp, Objects, Head, Pos, WLs, L, LU, LUK) -> -    {NextPos, Size, Term} = prterm(Head, Pos, ?ReadAhead), -    case element(Head#head.keypos, Term) of -	Key -> -	    {NewObjects, NL, LUK1} =  -		eval_object(Size, Term, Delete, LookUp,Objects,Head,Pos,L,LUK), -	    eval_key(Key, Delete, LookUp, NewObjects, Head, NextPos, WLs,  -		     NL, LU, LUK1); -	Key2 -> -	    {L1, NLU} = end_of_key(Objects, LookUp, L, LUK), -	    find_key(Head, Pos, NextPos, Size, Term, Key2, WLs, L1, NLU++LU) -    end. - -%% All objects in Objects have the key Key. -eval_object(Size, Term, Delete, LookUp, Objects, Head, Pos, L, LU) -> -    Type = Head#head.type, -    case lists:keyfind(Term, 1, Objects) of -	{_Object, N} when N =:= 0 -> -	    L1 = [{delete,Pos,Size} | L], -	    {Objects, L1, LU}; -	{_Object, N} when N < 0, Type =:= set -> -	    L1 = [{old,Pos} | L], -	    wl_lookup(LookUp, Objects, Term, L1, LU); -	{Object, _N} when Type =:= bag -> % when N =:= 1; N =:= -1 -	    L1 = [{old,Pos} | L], -	    Objects1 = lists:keydelete(Object, 1, Objects), -	    wl_lookup(LookUp, Objects1, Term, L1, LU); -	{Object, N} when N < 0, Type =:= duplicate_bag -> -	    L1 = [{old,Pos} | L], -	    Objects1 = lists:keyreplace(Object, 1, Objects, {Object,N+1}), -	    wl_lookup(LookUp, Objects1, Term, L1, LU); -	{_Object, N} when N > 0, Type =:= duplicate_bag -> -	    L1 = [{old,Pos} | L], -	    wl_lookup(LookUp, Objects, Term, L1, LU); -	false when Type =:= set, Delete =:= delete -> -	    case lists:keyfind(-1, 2, Objects) of -		false -> % no inserted object, perhaps deleted objects -		    L1 = [{delete,Pos,Size} | L], -		    {[], L1, LU}; -		{Term2, -1} -> -		    Bin2 = term_to_binary(Term2), -		    NSize = byte_size(Bin2), -		    Overwrite =  -			if -			    NSize =:= Size -> -				true; -			    true -> -				SizePos = sz2pos(Size+?OHDSZ), -				NSizePos = sz2pos(NSize+?OHDSZ), -				SizePos =:= NSizePos -			end, -		    E = if  -			    Overwrite -> -				{overwrite,Bin2,Pos}; -			    true -> -				{replace,Bin2,Pos,Size} -			end, -		    wl_lookup(LookUp, [], Term2, [E | L], LU) -	    end; -	false when Delete =:= delete -> -	    L1 = [{delete,Pos,Size} | L], -	    {Objects, L1, LU}; -	false -> -	    L1 = [{old,Pos} | L], -	    wl_lookup(LookUp, Objects, Term, L1, LU) -    end. - -%% Inlined. -wl_lookup({lookup,_}, Objects, Term, L, LU) -> -    {Objects, L, [Term | LU]}; -wl_lookup(skip, Objects, _Term, L, LU) -> -    {Objects, L, LU}. - -end_of_key([{Object,N0} | Objs], LookUp, L, LU) when N0 =/= 0 -> -    N = abs(N0), -    NL = [{insert,N,term_to_binary(Object)} | L], -    NLU = case LookUp of  -	      {lookup, _} -> -		  lists:duplicate(N, Object) ++ LU; -	      skip -> -		  LU -	  end, -    end_of_key(Objs, LookUp, NL, NLU); -end_of_key([_ | Objects], LookUp, L, LU) -> -    end_of_key(Objects, LookUp, L, LU); -end_of_key([], {lookup,Pid}, L, LU) -> -    {L, [{Pid,LU}]}; -end_of_key([], skip, L, LU) -> -    {L, LU}. - -create_writes([L | Ls], H, Ws, No) -> -    {NH, NWs, NNo} = create_writes(L, H, Ws, No, 0, true), -    create_writes(Ls, NH, NWs, NNo); -create_writes([], H, Ws, No) -> -    {H, lists:reverse(Ws), No}. - -create_writes([{old,Pos} | L], H, Ws, No, _Next, true) -> -    create_writes(L, H, Ws, No, Pos, true); -create_writes([{old,Pos} | L], H, Ws, No, Next, false) -> -    W = {Pos, <<Next:32>>}, -    create_writes(L, H, [W | Ws], No, Pos, true); -create_writes([{insert,N,Bin} | L], H, Ws, No, Next, _NextIsOld) -> -    {NH, NWs, Pos} = create_inserts(N, H, Ws, Next, byte_size(Bin), Bin), -    create_writes(L, NH, NWs, No+N, Pos, false); -create_writes([{overwrite,Bin,Pos} | L], H, Ws, No, Next, _) -> -    Size = byte_size(Bin), -    W = {Pos, [<<Next:32, Size:32, ?ACTIVE:32>>, Bin]}, -    create_writes(L, H, [W | Ws], No, Pos, true); -create_writes([{replace,Bin,Pos,OSize} | L], H, Ws, No, Next, _) -> -    Size = byte_size(Bin), -    {H1, _} = dets_utils:free(H, Pos, OSize+?OHDSZ), -    {NH, NewPos, _} = dets_utils:alloc(H1, ?OHDSZ + Size), -    W1 = {NewPos, [<<Next:32, Size:32, ?ACTIVE:32>>, Bin]}, -    NWs = if  -	      Pos =:= NewPos ->  -		  [W1 | Ws]; -	      true ->  -		  W2 = {Pos+?STATUS_POS, <<?FREE:32>>}, -		  [W1,W2 | Ws] -	  end, -    create_writes(L, NH, NWs, No, NewPos, false); -create_writes([{delete,Pos,Size} | L], H, Ws, No, Next, _) -> -    {NH, _} = dets_utils:free(H, Pos, Size+?OHDSZ), -    NWs = [{Pos+?STATUS_POS,<<?FREE:32>>} | Ws], -    create_writes(L, NH, NWs, No-1, Next, false); -create_writes([], H, Ws, No, _Next, _NextIsOld) -> -    {H, Ws, No}. - -create_inserts(0, H, Ws, Next, _Size, _Bin) -> -    {H, Ws, Next}; -create_inserts(N, H, Ws, Next, Size, Bin) -> -    {NH, Pos, _} = dets_utils:alloc(H, ?OHDSZ + Size), -    W = {Pos, [<<Next:32, Size:32, ?ACTIVE:32>>, Bin]}, -    create_inserts(N-1, NH, [W | Ws], Pos, Size, Bin). - -slot_position(S) -> -    Pos = ?SEGADDR(?SLOT2SEG(S)), -    Segment = get_segp(Pos), -    FinalPos = Segment + (4 * ?REM2(S, ?SEGSZ)), -    {FinalPos, 4}. - -%% Twice the size of a segment due to the bug in sz2pos/1. Inlined. -actual_seg_size() -> -    ?POW(sz2pos(?SEGSZ*4)-1). - -segp_cache(Pos, Segment) -> -    put(Pos, Segment). - -%% Inlined. -get_segp(Pos) -> -    get(Pos). - -%% Bug: If Sz0 is equal to 2**k for some k, then 2**(k+1) bytes are -%% allocated (wasting 2**k bytes). -sz2pos(N) -> -    1 + dets_utils:log2(N+1). - -scan_objs(_Head, Bin, From, To, L, Ts, R, _Type) -> -    scan_objs(Bin, From, To, L, Ts, R). - -scan_objs(Bin, From, To, L, Ts, -1) -> -    {stop, Bin, From, To, L, Ts}; -scan_objs(B = <<_N:32, Sz:32, St:32, T/binary>>, From, To, L, Ts, R) -> -    if  -	St =:= ?ACTIVE; -	St =:= ?FREE -> % deleted after scanning started -	    case T of -		<<BinTerm:Sz/binary, T2/binary>> -> -		    NTs = [BinTerm | Ts], -		    OSz = Sz + ?OHDSZ, -		    Skip = ?POW(sz2pos(OSz)-1) - OSz, -		    F2 = From + OSz, -		    NR = if  -			     R < 0 -> -				 R + 1; -			     true -> -				 R + OSz + Skip -			 end, -		    scan_skip(T2, F2, To, Skip, L, NTs, NR); -		_ -> -                    {more, From, To, L, Ts, R, Sz+?OHDSZ} -	    end; -	true -> % a segment -	    scan_skip(B, From, To, actual_seg_size(), L, Ts, R) -    end; -scan_objs(_B, From, To, L, Ts, R) -> -    {more, From, To, L, Ts, R, 0}. - -scan_skip(Bin, From, To, Skip, L, Ts, R) when From + Skip < To -> -    SkipPos = From + Skip, -    case Bin of -	<<_:Skip/binary, Tail/binary>> -> -	    scan_objs(Tail, SkipPos, To, L, Ts, R); -	_ -> -            {more, SkipPos, To, L, Ts, R, 0} -    end; -scan_skip(Bin, From, To, Skip, L, Ts, R) when From + Skip =:= To -> -    scan_next_allocated(Bin, From, To, L, Ts, R); -scan_skip(_Bin, From, _To, Skip, L, Ts, R) -> % when From + Skip > _To  -    From1 = From + Skip, -    {more, From1, From1, L, Ts, R, 0}. - -scan_next_allocated(_Bin, _From, To, <<>>=L, Ts, R) -> -    {more, To, To, L, Ts, R, 0}; -scan_next_allocated(Bin, From0, _To, <<From:32, To:32, L/binary>>, Ts, R) -> -    Skip = From - From0, -    scan_skip(Bin, From0, To, Skip, L, Ts, R). - -%% Read term from file at position Pos -prterm(Head, Pos, ReadAhead) -> -    Res = dets_utils:pread(Head, Pos, ?OHDSZ, ReadAhead), -    ?DEBUGF("file:pread(~tp, ~p, ?) -> ~p~n", [Head#head.filename, Pos, Res]), -    {ok, <<Next:32, Sz:32, _Status:32, Bin0/binary>>} = Res, -    ?DEBUGF("{Next, Sz} = ~p~n", [{Next, Sz}]), -    Bin = case byte_size(Bin0) of -	      Actual when Actual >= Sz -> -		  Bin0; -	      _ -> -		  {ok, Bin1} = dets_utils:pread(Head, Pos +  ?OHDSZ, Sz, 0), -		  Bin1 -	  end, -    Term = binary_to_term(Bin), -    {Next, Sz, Term}. - -%%%%%%%%%%%%%%%%%  DEBUG functions %%%%%%%%%%%%%%%% - -file_info(FH) -> -    #fileheader{closed_properly = CP, keypos = Kp, -                m = M, next = Next, n = N, version = Version, -                type = Type, no_objects = NoObjects}  -        = FH, -    if -        CP =:= 0 -> -            {error, not_closed}; -        FH#fileheader.cookie =/= ?MAGIC -> -            {error, not_a_dets_file}; -        FH#fileheader.version =/= ?FILE_FORMAT_VERSION -> -            {error, bad_version}; -        true -> -            {ok, [{closed_properly,CP},{keypos,Kp},{m, M}, -                  {n,N},{next,Next},{no_objects,NoObjects}, -                  {type,Type},{version,Version}]} -    end. - -v_segments(H) -> -    v_segments(H, 0). - -v_segments(_H, ?SEGARRSZ) -> -    done; -v_segments(H, SegNo) -> -    Seg = dets_utils:read_4(H#head.fptr, ?SEGADDR(SegNo)), -    if -	Seg =:= 0 -> -	    done; -	true -> -	    io:format("SEGMENT ~w ", [SegNo]), -	    io:format("At position ~w~n", [Seg]), -	    v_segment(H, SegNo, Seg, 0), -	    v_segments(H, SegNo+1) -    end. - -v_segment(_H, _, _SegPos, ?SEGSZ) -> -    done; -v_segment(H, SegNo, SegPos, SegSlot) -> -    Slot = SegSlot + (SegNo * ?SEGSZ), -    Chain = dets_utils:read_4(H#head.fptr, SegPos + (4 * SegSlot)), -    if  -	Chain =:= 0 ->  %% don't print empty chains -	    true; -	true -> -	    io:format("   <~p>~p: [",[SegPos + (4 * SegSlot), Slot]), -	    print_chain(H, Chain) -    end, -    v_segment(H, SegNo, SegPos, SegSlot+1). - -print_chain(_H, 0) -> -    io:format("] \n", []); -print_chain(H, Pos) -> -    {ok, _} = file:position(H#head.fptr, Pos), -    case rterm(H#head.fptr) of -	{ok, 0, _Sz, Term} -> -	    io:format("<~p>~p] \n",[Pos, Term]); -	{ok, Next, _Sz, Term} -> -	    io:format("<~p>~p, ", [Pos, Term]), -	    print_chain(H, Next); -	Other -> -	    io:format("~nERROR ~p~n", [Other]) -    end. - -%% Can't be used at the bucket level!!!! -%% Only when we go down a chain -rterm(F) -> -    case catch rterm2(F) of -	{'EXIT', Reason} -> %% truncated DAT file  -	    dets_utils:vformat("** dets: Corrupt or truncated dets file~n",  -                               []),  -	    {error, Reason}; -	Other ->  -	    Other -    end. - -rterm2(F) -> -    {ok, <<Next:32, Sz:32, _:32>>} = file:read(F, ?OHDSZ), -    {ok, Bin} = file:read(F, Sz), -    Term = binary_to_term(Bin), -    {ok, Next, Sz, Term}. - - diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl index 6c406fc03a..3ab8f87ebf 100644 --- a/lib/stdlib/src/dets_v9.erl +++ b/lib/stdlib/src/dets_v9.erl @@ -24,8 +24,8 @@  -export([mark_dirty/1, read_file_header/2,           check_file_header/2, do_perform_save/1, initiate_file/11, -         prep_table_copy/9, init_freelist/2, fsck_input/4, -         bulk_input/3, output_objs/4, bchunk_init/2, +         prep_table_copy/9, init_freelist/1, fsck_input/4, +         bulk_input/3, output_objs/3, bchunk_init/2,           try_bchunk_header/2, compact_init/3, read_bchunks/2,           write_cache/1, may_grow/3, find_object/2, slot_objs/2,           scan_objs/8, db_hash/2, no_slots/1, table_parameters/1]). @@ -228,8 +228,8 @@  -define(CLOSED_PROPERLY_POS, 8).  -define(D_POS, 20). -%%% Dets file versions up to 8 are handled in dets_v8. This module -%%% handles version 9, introduced in R8. +%%% This module handles Dets file format version 9, introduced in +%%% Erlang/OTP R8.  %%%   %%% Version 9(a) tables have 256 reserved bytes in the file header,  %%% all initialized to zero. @@ -249,32 +249,32 @@  -define(OHDSZ, 8).          % The size of the object header, in bytes.  -define(STATUS_POS, 4).     % Position of the status field. --define(OHDSZ_v8, 12).      % The size of the version 8 object header. -  %% The size of each object is a multiple of 16.  %% BUMP is used when repairing files.  -define(BUMP, 16). -%%% '$hash' is the value of HASH_PARMS in R8, '$hash2' is the value in R9. +%%% '$hash' is the value of HASH_PARMS in Erlang/OTP R8, '$hash2' is +%%% the value in Erlang/OTP R9.  %%%  %%% The fields of the ?HASH_PARMS records are the same, but having -%%% different tags makes bchunk_init on R8 nodes reject data from R9 -%%% nodes, and vice versa. This is overkill, and due to an oversight. -%%% What should have been done in R8 was to check the hash method, not -%%% only the type of the table and the key position. R8 nodes cannot -%%% handle the phash2 method. +%%% different tags makes bchunk_init on Erlang/OTP R8 nodes reject +%%% data from Erlang/OTP R9 nodes, and vice versa. This is overkill, +%%% and due to an oversight. What should have been done in Erlang/OTP +%%% R8 was to check the hash method, not only the type of the table +%%% and the key position. Erlang/OTP R8 nodes cannot handle the phash2 +%%% method.  -define(HASH_PARMS, '$hash2').  -define(BCHUNK_FORMAT_VERSION, 1).  -record(?HASH_PARMS, { -	   file_format_version,  +	   file_format_version,  	   bchunk_format_version,   	   file, type, keypos, hash_method,  	   n,m,next,  	   min,max,  	   no_objects,no_keys, -	   no_colls  % [{LogSz,NoColls}], NoColls >= 0 +	   no_colls :: no_colls()  	  }).  -define(ACTUAL_SEG_SIZE, (?SEGSZ*4)). @@ -364,10 +364,8 @@ init_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, Ram, CacheSz,        filename = Fname,         name = Tab,        cache = dets_utils:new_cache(CacheSz), -      version = ?FILE_FORMAT_VERSION,        bump = ?BUMP, -      base = ?BASE, % to be overwritten -      mod = ?MODULE +      base = ?BASE % to be overwritten       },      FreeListsPointer = 0, @@ -457,7 +455,7 @@ alloc_seg(Head, SegZero, SegNo, Part) ->      {NewHead, InitSegment, [SegPointer]}.  %% Read free lists (using a Buddy System) from file.  -init_freelist(Head, true) -> +init_freelist(Head) ->      Pos = Head#head.freelists_p,      free_lists_from_file(Head, Pos). @@ -510,12 +508,10 @@ read_file_header(Fd, FileName) ->                       md5 = erlang:md5(MD5DigestedPart),  		     trailer = FileSize + FlBase,  		     eof = EOF, -		     n = N, -		     mod = ?MODULE}, +		     n = N},      {ok, Fd, FH}. -%% -> {ok, head(), ExtraInfo} | {error, Reason} (Reason lacking file name) -%% ExtraInfo = true +%% -> {ok, head()} | {error, Reason} (Reason lacking file name)  check_file_header(FH, Fd) ->      HashBif = code_to_hash_method(FH#fileheader.hash_method),      Test =  @@ -534,14 +530,14 @@ check_file_header(FH, Fd) ->  	    HashBif =:= undefined ->  		{error, bad_hash_bif};  	    FH#fileheader.closed_properly =:= ?CLOSED_PROPERLY -> -		{ok, true}; +		ok;  	    FH#fileheader.closed_properly =:= ?NOT_PROPERLY_CLOSED ->  		{error, not_closed};  	    true ->  		{error, not_a_dets_file}  	end,      case Test of -	{ok, ExtraInfo} -> +	ok ->              MaxObjSize = max_objsize(FH#fileheader.no_colls),  	    H = #head{  	      m = FH#fileheader.m, @@ -563,11 +559,9 @@ check_file_header(FH, Fd) ->  	      min_no_slots = FH#fileheader.min_no_slots,  	      max_no_slots = FH#fileheader.max_no_slots,  	      no_collections = FH#fileheader.no_colls, -	      version = ?FILE_FORMAT_VERSION, -	      mod = ?MODULE,  	      bump = ?BUMP,  	      base = FH#fileheader.fl_base}, -	    {ok, H, ExtraInfo}; +	    {ok, H};  	Error ->  	    Error      end. @@ -621,7 +615,7 @@ no_segs(NoSlots) ->  %%%  %%% bulk_input/3. Initialization, the general case (any stream of objects). -%%% output_objs/4. Initialization (general case) and repair. +%%% output_objs/3. Initialization (general case) and repair.  %%% bchunk_init/2. Initialization using bchunk.  bulk_input(Head, InitFun, _Cntrs) -> @@ -678,7 +672,7 @@ bulk_objects([], _Head, Kp, Seq, L) when is_integer(Kp), is_integer(Seq) ->  -define(OBJ_COUNTER, 2).  -define(KEY_COUNTER, 3). -output_objs(OldV, Head, SlotNums, Cntrs) when OldV =< 9 -> +output_objs(Head, SlotNums, Cntrs) ->      fun(close) ->              %% Make sure that the segments are initialized in case              %% init_table has been called. @@ -686,31 +680,31 @@ output_objs(OldV, Head, SlotNums, Cntrs) when OldV =< 9 ->              Acc = [], % This is the only way Acc can be empty.              true = ets:insert(Cntrs, {?FSCK_SEGMENT,0,[],0}),  	    true = ets:insert(Cntrs, {?COUNTERS, 0, 0}), -            Fun = output_objs2(foo, Acc, OldV, Head, Cache, Cntrs, +            Fun = output_objs2(foo, Acc, Head, Cache, Cntrs,  			       SlotNums, bar),              Fun(close);         ([]) -> -	    output_objs(OldV, Head, SlotNums, Cntrs); +	    output_objs(Head, SlotNums, Cntrs);         (L) ->  	    %% Information about number of objects per size is not  	    %% relevant for version 9. It is the number of collections  	    %% that matters.              true = ets:delete_all_objects(Cntrs),  	    true = ets:insert(Cntrs, {?COUNTERS, 0, 0}), -	    Es = bin2term(L, OldV, Head#head.keypos), +	    Es = bin2term(L, Head#head.keypos),  	    %% The cache is a tuple indexed by the (log) size. An element  	    %% is [BinaryObject].  	    Cache = ?VEMPTY,  	    {NE, NAcc, NCache} = output_slots(Es, Head, Cache, Cntrs, 0, 0), -	    output_objs2(NE, NAcc, OldV, Head, NCache, Cntrs, SlotNums, 1) +	    output_objs2(NE, NAcc, Head, NCache, Cntrs, SlotNums, 1)      end. -output_objs2(E, Acc, OldV, Head, Cache, SizeT, SlotNums, 0) -> +output_objs2(E, Acc, Head, Cache, SizeT, SlotNums, 0) ->      NCache = write_all_sizes(Cache, SizeT, Head, more),      %% Number of handled file_sorter chunks before writing:      Max = erlang:max(1, erlang:min(tuple_size(NCache), 10)), -    output_objs2(E, Acc, OldV, Head, NCache, SizeT, SlotNums, Max); -output_objs2(E, Acc, OldV, Head, Cache, SizeT, SlotNums, ChunkI) -> +    output_objs2(E, Acc, Head, NCache, SizeT, SlotNums, Max); +output_objs2(E, Acc, Head, Cache, SizeT, SlotNums, ChunkI) ->      fun(close) ->              {_, [], Cache1} =                    if @@ -747,11 +741,10 @@ output_objs2(E, Acc, OldV, Head, Cache, SizeT, SlotNums, ChunkI) ->  		    end  	    end;         (L) -> -	    Es = bin2term(L, OldV, Head#head.keypos), +	    Es = bin2term(L, Head#head.keypos),  	    {NE, NAcc, NCache} =   		output_slots(E, Es, Acc, Head, Cache, SizeT, 0, 0), -	    output_objs2(NE, NAcc, OldV, Head, NCache, SizeT, SlotNums, -			 ChunkI-1) +	    output_objs2(NE, NAcc, Head, NCache, SizeT, SlotNums, ChunkI-1)      end.  %%% Compaction.  @@ -1245,10 +1238,8 @@ allocate_all(Head, [{LSize,_,Data,NoCollections} | DTL], L) ->      E = {LSize,Addr,Data,NoCollections},      allocate_all(NewHead, DTL, [E | L]). -bin2term(Bin, 9, Kp) -> -    bin2term1(Bin, Kp, []); -bin2term(Bin, 8, Kp) -> -    bin2term_v8(Bin, Kp, []). +bin2term(Bin, Kp) -> +    bin2term1(Bin, Kp, []).  bin2term1([<<Slot:32, Seq:32, BinTerm/binary>> | BTs], Kp, L) ->      Term = binary_to_term(BinTerm), @@ -1257,13 +1248,6 @@ bin2term1([<<Slot:32, Seq:32, BinTerm/binary>> | BTs], Kp, L) ->  bin2term1([], _Kp, L) ->      lists:reverse(L). -bin2term_v8([<<Slot:32, BinTerm/binary>> | BTs], Kp, L) -> -    Term = binary_to_term(BinTerm), -    Key = element(Kp, Term), -    bin2term_v8(BTs, Kp, [{Slot, Key, foo, Term, BinTerm} | L]); -bin2term_v8([], _Kp, L) -> -    lists:reverse(L). -  write_all_sizes({}=Cache, _SizeT, _Head, _More) ->      Cache;  write_all_sizes(Cache, SizeT, Head, More) -> @@ -1461,7 +1445,7 @@ temp_file(Head, SizeT, N) ->  %% Does not close Fd.  fsck_input(Head, Fd, Cntrs, FileHeader) ->      MaxSz0 = case FileHeader#fileheader.has_md5 of -                 true when is_integer(FileHeader#fileheader.no_colls) ->  +                 true when is_list(FileHeader#fileheader.no_colls) ->                       ?POW(max_objsize(FileHeader#fileheader.no_colls));                   _ ->                       %% The file is not compressed, so the bucket size @@ -1485,10 +1469,10 @@ fsck_input(Head, State, Fd, MaxSz, Cntrs) ->  		done ->  		    end_of_input;  		{done, L, _Seq} -> -		    R = count_input(Head, Cntrs, L), +		    R = count_input(L),  		    {R, fsck_input(Head, done, Fd, MaxSz, Cntrs)};  		{cont, L, Bin, Pos, Seq} -> -		    R = count_input(Head, Cntrs, L), +		    R = count_input(L),                      FR = fsck_objs(Bin, Head#head.keypos, Head, [], Seq),                      NewState = fsck_read(FR, Pos, Fd, MaxSz, Head),  		    {R, fsck_input(Head, NewState, Fd, MaxSz, Cntrs)} @@ -1496,20 +1480,9 @@ fsck_input(Head, State, Fd, MaxSz, Cntrs) ->      end.  %% The ets table Cntrs is used for counting objects per size. -count_input(Head, Cntrs, L) when Head#head.version =:= 8 -> -    count_input1(Cntrs, L, []); -count_input(_Head, _Cntrs, L) -> +count_input(L) ->      lists:reverse(L). -count_input1(Cntrs, [[LogSz | B] | Ts], L) -> -    case catch ets:update_counter(Cntrs, LogSz, 1) of -	N when is_integer(N) -> ok; -	_Badarg -> true = ets:insert(Cntrs, {LogSz, 1}) -    end, -    count_input1(Cntrs, Ts, [B | L]); -count_input1(_Cntrs, [], L) -> -    L. -  fsck_read(Pos, F, L, Seq) ->      case file:position(F, Pos) of  	{ok, _} -> @@ -1564,11 +1537,6 @@ fsck_objs(Bin = <<Sz:32, Status:32, Tail/binary>>, Kp, Head, L, Seq) ->  fsck_objs(Bin, _Kp, _Head, L, Seq) ->      {more, Bin, 0, L, Seq}. -make_objects([{K,BT}|Os], Seq, Kp, Head, L) when Head#head.version =:= 8 -> -    LogSz = dets_v8:sz2pos(byte_size(BT)+?OHDSZ_v8), -    Slot = dets_v8:db_hash(K, Head), -    Obj = [LogSz | <<Slot:32, LogSz:8, BT/binary>>], -    make_objects(Os, Seq, Kp, Head, [Obj | L]);  make_objects([{K,BT} | Os], Seq, Kp, Head, L) ->      Obj = make_object(Head, K, Seq, BT),      make_objects(Os, Seq+1, Kp, Head, [Obj | L]); @@ -1607,7 +1575,7 @@ do_perform_save(H) ->      FileHeader = file_header(H1, FreeListsPointer, ?CLOSED_PROPERLY),      case dets_utils:debug_mode() of          true ->  -            TmpHead0 = init_freelist(H1#head{fixed = false}, true), +            TmpHead0 = init_freelist(H1#head{fixed = false}),              TmpHead = TmpHead0#head{base = H1#head.base},              case                   catch dets_utils:all_allocated_as_list(TmpHead) @@ -1794,7 +1762,7 @@ table_parameters(Head) ->  				      (E, A) -> [E | A]  				   end, [], CL),  	    NoColls = lists:reverse(NoColls0), -	    #?HASH_PARMS{file_format_version = Head#head.version,  +	    #?HASH_PARMS{file_format_version = ?FILE_FORMAT_VERSION,  			 bchunk_format_version = ?BCHUNK_FORMAT_VERSION,  			 file = filename:basename(Head#head.filename),  			 type = Head#head.type, diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index 8cf46482dd..82ab484ea6 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -31,7 +31,6 @@               dets_server,  	     dets_sup,  	     dets_utils, -	     dets_v8,  	     dets_v9,  	     dict,  	     digraph, | 
