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