dets_v8.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,591 行 · 第 1/4 页

ERL
1,591
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id $%%-module(dets_v8).%% Dets files, implementation part. This module handles versions up to%% and including 8(c). To be called from dets.erl only.-export([constants/0, 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]).-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 compatability 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).%% {Bump}constants() ->    {?BUMP, ?BASE}.%% -> ok | throw({NewHead,Error})mark_dirty(Head) ->    Dirty = [{?CLOSED_PROPERLY_POS, <<?NOT_PROPERLY_CLOSED:32>>}],    dets_utils:pwrite(Head, Dirty),    dets_utils:sync(Head),    dets_utils:position(Head, Head#head.freelists_p),    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}  ->	    FreeList = lists:reverse(Term),	    dets_utils:init_slots_from_old_file(FreeList, Ftab);	_ ->	    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}  ->	    Term;	_ ->	    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,		     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,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?