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