dets_utils.erl

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

ERL
1,580
字号
    %% mkeysearch returns the _first_ tuple with a matching key.    case mkeysearch(Key, 1, CL) of	{value, {Key,{_Seq,{insert,Object}}}} when Type =:= set ->	    cache_lookup(Type, Keys, CL, [Object | LU]);	{value, {Key,{_Seq,delete_key}}} ->	    cache_lookup(Type, Keys, CL, LU);	_ ->	    false    end;cache_lookup(_Type, [], _CL, LU) ->    LU.reset_cache(C) ->    WrTime = C#cache.wrtime,    NewWrTime = if 		    WrTime =:= undefined ->			WrTime;		    true ->			now()		end,    PK = family(C#cache.cache),    NewC = C#cache{cache = [], csize = 0, inserts = 0, wrtime = NewWrTime},    {NewC, C#cache.inserts, PK}.is_empty_cache(Cache) ->    Cache#cache.cache =:= [].new_cache({Delay, Size}) ->    #cache{cache = [], csize = 0, inserts = 0, 	   tsize = Size, wrtime = undefined, delay = Delay}.%%%%%% Buddy System%%% %% Definitions for the buddy allocator.-define(MAXBUD, 32).             % 2 GB is maximum file size-define(MAXFREELISTS, 50000000). % Bytes reserved for the free lists (at end).%%-define(DEBUG(X, Y), io:format(X, Y)).-define(DEBUG(X, Y), true).%%% Algorithm : We use a buddy system on each file. This is nicely described%%%             in i.e. the last chapter of the first-grade text book %%%             Data structures and algorithms by Aho, Hopcroft and%%%             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 happeninit_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.%%%%%% To make things (slightly) worse (from a memory usage point of%%% view), each traversal of the file starts with making a "map" of%%% the allocated areas; only the allocated areas will be%%% traversed. Creating a map involves inspecting and sorting the free%%% lists. Since the map is passed on between client and server, it%%% has to be a binary (to avoid copying a possibly huge term).%%%%%% An active map should always be protected by fixing the table. This%%% prevents insertion of objects into the mapped area (where some%%% objects may have been deleted). The means for implementing this%%% protection is a copy of the free lists (using even more memory, if%%% objects are inserted). The position to write an inserted object is%%% found by looking at the free lists from the time when the table%%% was fixed; areas within the mapped area that have been freed are%%% hidden from the allocator.%% -> free_table()%% A free table is a tuple of ?MAXBUD elements, element i handling%% buddies of size 2^(i-1).init_alloc(Base) ->    Ftab = empty_free_lists(),    Empty = bplus_empty_tree(),    setelement(?MAXBUD, Ftab, bplus_insert(Empty, Base)). empty_free_lists() ->    Empty = bplus_empty_tree(),    %% initiate a tuple with ?MAXBUD "Empty" elements    erlang:make_tuple(?MAXBUD, Empty).%% Only used when repairing or initiating.alloc_many(Head, _Sz, 0, _A0) ->    Head;alloc_many(Head, Sz, N, A0) ->    Ftab = Head#head.freelists,    Head#head{freelists = alloc_many1(Ftab, 1, Sz * N, A0, Head)}.%% -> NewFtab | throw(Error)alloc_many1(Ftab, Pos, Size, A0, H) ->    {FPos, Addr} = find_first_free(Ftab, Pos, Pos, H),    true = Addr >= A0, % assertion    if 	?POW(FPos - 1) >= Size ->	    alloc_many2(Ftab, sz2pos(Size), Size, A0, H);	true ->	    NewFtab = reserve_buddy(Ftab, FPos, FPos, Addr),	    NSize = Size - ?POW(FPos-1),	    alloc_many1(NewFtab, FPos, NSize, Addr, H)    end.alloc_many2(Ftab, _Pos, 0, _A0, _H) ->    Ftab;alloc_many2(Ftab, Pos, Size, A0, H) when Size band ?POW(Pos-1) > 0 ->    {FPos, Addr} = find_first_free(Ftab, Pos, Pos, H),    true = Addr >= A0, % assertion    NewFtab = reserve_buddy(Ftab, FPos, Pos, Addr),    NSize = Size - ?POW(Pos - 1),    alloc_many2(NewFtab, Pos-1, NSize, Addr, H);alloc_many2(Ftab, Pos, Size, A0, H) ->    alloc_many2(Ftab, Pos-1, Size, A0, H).%% -> {NewHead, Addr, Log2} | throw(Error)alloc(Head, Sz) when Head#head.fixed =/= false -> % when Sz > 0    ?DEBUG("alloc of size ~p (fixed)", [Sz]),    Pos = sz2pos(Sz),    {Frozen, Ftab} = Head#head.freelists,    {FPos, Addr} = find_first_free(Frozen, Pos, Pos, Head),    NewFrozen = reserve_buddy(Frozen, FPos, Pos, Addr),    Ftab1 = undo_free(Ftab, FPos, Addr, Head#head.base),    NewFtab = move_down(Ftab1, FPos, Pos, Addr),    NewFreelists = {NewFrozen, NewFtab},    {Head#head{freelists = NewFreelists}, Addr, Pos};alloc(Head, Sz) when Head#head.fixed =:= false -> % when Sz > 0    ?DEBUG("alloc of size ~p", [Sz]),    Pos = sz2pos(Sz),    Ftab = Head#head.freelists,    {FPos, Addr} = find_first_free(Ftab, Pos, Pos, Head),    NewFtab = reserve_buddy(Ftab, FPos, Pos, Addr),    {Head#head{freelists = NewFtab}, Addr, Pos}.find_first_free(_Ftab, Pos, _Pos0, Head) when Pos > ?MAXBUD ->    throw({error, {no_more_space_on_file, Head#head.filename}});find_first_free(Ftab, Pos, Pos0, Head) ->    PosTab = element(Pos, Ftab),    case bplus_lookup_first(PosTab) of	undefined -> 	    find_first_free(Ftab, Pos+1, Pos0, Head);	{ok, Addr} when Addr + ?POW(Pos0-1) > ?POW(?MAXBUD-1)-?MAXFREELISTS ->	    %% We would occupy (some of) the area reserved for the free lists.	    throw({error, {no_more_space_on_file, Head#head.filename}});	{ok, Addr} ->	    {Pos, Addr}    end.%% When the table is fixed, free/4 may have joined buddies so that the%% requested block is now part of some larger block. We have to find%% that block, and insert free buddies along the way.undo_free(Ftab, Pos, Addr, Base) ->    PosTab = element(Pos, Ftab),    case bplus_lookup(PosTab, Addr) of	undefined ->	    {BuddyAddr, MoveUpAddr} = my_buddy(Addr, ?POW(Pos-1), Base),	    NewFtab = setelement(Pos, Ftab, bplus_insert(PosTab, BuddyAddr)),	    undo_free(NewFtab, Pos+1, MoveUpAddr, Base);	{ok, Addr} ->	    NewPosTab = bplus_delete(PosTab, Addr),	    setelement(Pos, Ftab, NewPosTab)    end.reserve_buddy(Ftab, Pos, Pos0, Addr) ->    PosTab = element(Pos, Ftab),    NewPosTab = bplus_delete(PosTab, Addr),    NewFtab = setelement(Pos, Ftab, NewPosTab),    move_down(NewFtab, Pos, Pos0, Addr).move_down(Ftab, Pos, Pos, _Addr) ->    ?DEBUG(" to address ~p, table ~p (~p bytes)~n", 	    [_Addr, Pos, ?POW(Pos-1)]),    Ftab;move_down(Ftab, Pos, Pos0, Addr) ->    Pos_1 = Pos - 1,    Size = ?POW(Pos_1),    HighBuddy = (Addr + (Size bsr 1)),    NewPosTab_1 = bplus_insert(element(Pos_1, Ftab), HighBuddy),    NewFtab = setelement(Pos_1, Ftab, NewPosTab_1),     move_down(NewFtab, Pos_1, Pos0, Addr).%% -> {Head, Log2}free(Head, Addr, Sz) ->    ?DEBUG("free of size ~p at address ~p~n", [Sz, Addr]),    Ftab = get_freelists(Head),    Pos = sz2pos(Sz),    {set_freelists(Head, free_in_pos(Ftab, Addr, Pos, Head#head.base)), Pos}.free_in_pos(Ftab, _Addr, Pos, _Base) when Pos > ?MAXBUD ->    Ftab;free_in_pos(Ftab, Addr, Pos, Base) ->    PosTab = element(Pos, Ftab),    {BuddyAddr, MoveUpAddr} = my_buddy(Addr, ?POW(Pos-1), Base),    case bplus_lookup(PosTab, BuddyAddr) of	undefined -> % no buddy found	    ?DEBUG("  table ~p, no buddy~n", [Pos]),	    setelement(Pos, Ftab, bplus_insert(PosTab, Addr));	{ok, BuddyAddr} -> % buddy found	    PosTab1 = bplus_delete(PosTab, Addr),	    PosTab2 = bplus_delete(PosTab1, BuddyAddr),	    ?DEBUG("  table ~p, with buddy ~p~n", [Pos, BuddyAddr]),	    NewFtab = setelement(Pos, Ftab, PosTab2),	    free_in_pos(NewFtab, MoveUpAddr, Pos+1, Base)    end.get_freelists(Head) when Head#head.fixed =:= false ->    Head#head.freelists;get_freelists(Head) when Head#head.fixed =/= false ->    {_Frozen, Current} = Head#head.freelists,    Current.set_freelists(Head, Ftab) when Head#head.fixed =:= false ->    Head#head{freelists = Ftab};set_freelists(Head, Ftab) when Head#head.fixed =/= false ->    {Frozen, _} = Head#head.freelists,    Head#head{freelists = {Frozen,Ftab}}.%% Bug: If Sz0 is equal to 2^k for some k, then 2^(k+1) bytes are%% allocated (wasting 2^k bytes). Inlined.sz2pos(N) when N > 0 ->    1 + log2(N+1).%% Returns the i such that 2^(i-1) < N =< 2^i.log2(N) when is_integer(N), N >= 0 ->    if N > ?POW(8) ->	    if N > ?POW(10) ->		    if N > ?POW(11) ->			    if N > ?POW(12) ->				    12 + if N band (?POW(12)-1) =:= 0 -> 						 log2(N bsr 12);					    true -> log2(1 + (N bsr 12))					 end;			       true -> 12			    end;		       true -> 11		    end;	       N > ?POW(9) -> 10;	       true -> 9	    end;       N > ?POW(4) ->	    if N > ?POW(6) ->		    if N > ?POW(7) -> 8;		       true -> 7		    end;	       N > ?POW(5) -> 6;	       true -> 5	    end;       N > ?POW(2) ->	    if		N > ?POW(3) -> 4;		true -> 3	    end;       N > ?POW(1) -> 2;       N >= ?POW(0) -> 1;       true -> 0    end.make_zeros(0) -> [];make_zeros(N) when N rem 2 =:= 0 ->    P = make_zeros(N div 2),    [P|P];make_zeros(N) ->    P = make_zeros(N div 2),    [0,P|P].%% Calculate the buddy of Addrmy_buddy(Addr, Sz, Base) ->    case (Addr - Base) band Sz of	0 -> % even, buddy is higher addr	    {Addr+Sz, Addr};	_ -> % odd, buddy is lower addr            T = Addr-Sz,	    {T, T}    end.all_free(Head) ->    Tab = get_freelists(Head),    Base = Head#head.base,    case all_free(all(Tab), Base, Base, []) of	[{Base,Base} | L] -> L;	L -> L    end.    all_free([], X0, Y0, F) ->    lists:reverse([{X0,Y0} | F]);all_free([{X,Y} | L], X0, Y0, F) when Y0 =:= X ->    all_free(L, X0, Y, F);all_free([{X,Y} | L], X0, Y0, F) when Y0 < X ->    all_free(L, X, Y, [{X0,Y0} | F]).all_allocated(Head) ->    all_allocated(all(get_freelists(Head)), 0, Head#head.base, []).all_allocated([], _X0, _Y0, []) ->    <<>>;all_allocated([], _X0, _Y0, A0) ->    [<<From:32, To:32>> | A] = lists:reverse(A0),    {From, To, list_to_binary(A)};all_allocated([{X,Y} | L], X0, Y0, A) when Y0 =:= X ->    all_allocated(L, X0, Y, A);all_allocated([{X,Y} | L], _X0, Y0, A) when Y0 < X ->    all_allocated(L, X, Y, [<<Y0:32,X:32>> | A]).all_allocated_as_list(Head) ->    all_allocated_as_list(all(get_freelists(Head)), 0, Head#head.base, []).all_allocated_as_list([], _X0, _Y0, []) ->    [];all_allocated_as_list([], _X0, _Y0, A) ->    lists:reverse(A);all_allocated_as_list([{X,Y} | L], X0, Y0, A) when Y0 =:= X ->    all_allocated_as_list(L, X0, Y, A);all_allocated_as_list([{X,Y} | L], _X0, Y0, A) when Y0 < X ->    all_allocated_as_list(L, X, Y, [[Y0 | X] | A]).all(Tab) ->    all(Tab, size(Tab), []).all(_Tab, 0, L) ->    %% This is not as bad as it looks. L contains less than 32 runs,    %% so there will be only a small number of merges.    lists:sort(L);all(Tab, I, L) ->    LL = collect_tree(element(I, Tab), I, L),    all(Tab, I-1, LL).%% Finds allocated areas between Addr (approx.) and Addr+Length.find_allocated(Ftab, Addr, Length, Base) ->    MaxAddr = Addr + Length,    Ints = collect_all_interval(Ftab, Addr, MaxAddr, Base),    allocated(Ints, Addr, MaxAddr, Ftab, Base).allocated(Some, Addr, Max, Ftab, Base) ->    case allocated1(Some, Addr, Max, []) of        [] ->            case find_next_allocated(Ftab, Addr, Base) of                {From,_} ->                     find_allocated(Ftab, From, ?CHUNK_SIZE, Base);                none ->                    <<>>            end;        L ->             list_to_binary(lists:reverse(L))    end.allocated1([], Y0, Max, A) when Y0 < Max ->    [<<Y0:32,Max:32>> | A];allocated1([], _Y0, _Max, A) ->    A;allocated1([{X,Y} | L], Y0, Max, A) when Y0 >= X ->    allocated1(L, Y, Max, A);allocated1([{X,Y} | L], Y0, Max, A) -> % when Y0 < X    allocated1(L, Y, Max, [<<Y0:32,X:32>> | A]).%% Finds the first allocated area starting at Addr or later.find_next_allocated(Ftab, Addr, Base) ->    case find_next_free(Ftab, Addr, Base) of        none ->            none;        {Addr1, Pos} when Addr1 =< Addr ->            find_next_allocated(Ftab, Addr1 + ?POW(Pos-1), Base);        {Next, _Pos} ->            {Addr, Next}    end.%% Finds the first free address starting att Addr or later. %% -> none | {FirstFreeAddress, FtabPosition}find_next_free(Ftab, Addr, Base) ->    MaxBud = size(Ftab),    find_next_free(Ftab, Addr, 1, MaxBud, -1, -1, Base).find_next_free(Ftab, Addr0, Pos, MaxBud, Next, PosN, Base)                           when Pos =< MaxBud ->    Addr = adjust_addr(Addr0, Pos, Base),    PosTab = element(Pos, Ftab),    case bplus_lookup_next(PosTab, Addr-1) of        undefined ->            find_next_free(Ftab, Addr0, Pos+1, MaxBud, Next, PosN, Base);        {ok, Next1} when PosN =:= -1; Next1 < Next ->            find_next_free(Ftab, Addr0, Pos+1, MaxBud, Next1, Pos, Base);        {ok, _} ->            find_next_free(Ftab, Addr0, Pos+1, MaxBud, Next, PosN, Base)    end;find_next_free(_Ftab, _Addr, _Pos, _MaxBud, -1, _PosN, _Base) ->

⌨️ 快捷键说明

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