dets_utils.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,580 行 · 第 1/4 页
ERL
1,580 行
%% ``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_utils).%% Utility functions common to several dets file formats.%% To be used from dets, dets_v8 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, pwrite/3, pread_close/4, read_n/2, pread_n/3, read_4/2]).-export([code_to_type/1, type_to_code/1]).-export([corrupt_reason/2, corrupt/2, corrupt_file/2, vformat/2, file_error/2]).-export([cache_lookup/4, cache_size/1, new_cache/1, reset_cache/1, is_empty_cache/1]).-export([empty_free_lists/0, init_alloc/1, alloc_many/4, alloc/2, free/3, get_freelists/1, all_free/1, all_allocated/1, 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}]}).-compile({inline, [{bplus_mk_leaf,1}, {bplus_get_size,1}, {bplus_get_tree,2}, {bplus_get_lkey,2}, {bplus_get_rkey,2}]}).-include("dets.hrl").%%% A total ordering of all Erlang terms.%% -> -1 | 0 | 1. T1 is (smaller than | equal | greater than) T2.%% If is_integer(I), is_float(F), I == F then I is deemed smaller than F.cmp(T, T) -> 0;cmp([E1 | T1], [E2 | T2]) -> case cmp(E1, E2) of 0 -> cmp(T1, T2); R -> R end;cmp(T1, T2) when is_tuple(T1), is_tuple(T2), size(T1) =:= size(T2) -> tcmp(T1, T2, 1, size(T1));cmp(I, F) when is_integer(I), is_float(F) -> -1;cmp(F, I) when is_float(F), is_integer(I) -> 1;cmp(T1, T2) when T1 < T2 -> -1;cmp(_T1, _T2) -> % when _T1 > _T2 1.tcmp(T1, T2, I, I) -> cmp(element(I, T1), element(I, T2));tcmp(T1, T2, I, N) -> case cmp(element(I, T1), element(I, T2)) of 0 -> tcmp(T1, T2, I + 1, N); R -> R end.msort(L) -> %% sort is very much faster than msort, let it do most of the work. F = fun(X, Y) -> cmp(X, Y) =< 0 end, lists:sort(F, lists:sort(L)).mkeysort(I, L) -> F = fun(X, Y) -> cmp(element(I, X), element(I, Y)) =< 0 end, %% keysort is much faster than mkeysort, let it do most of the work. lists:sort(F, lists:keysort(I, L)).mkeysearch(Key, I, L) -> case lists:keysearch(Key, I, L) of {value, Value}=Reply when element(I, Value) =:= Key -> Reply; false -> false; _ -> mkeysearch2(Key, I, L) end.mkeysearch2(_Key, _I, []) -> false;mkeysearch2(Key, I, [E | _L]) when element(I, E) =:= Key -> {value, E};mkeysearch2(Key, I, [_ | L]) -> mkeysearch(Key, I, L).%% Be careful never to compare key, but use matching instead.%% Otherwise sofs could have been used:%% sofs:to_external(sofs:relation_to_family(sofs:relation(L, 2))).family([]) -> [];family(L) -> [{K,V}|KVL] = mkeysort(1, L), per_key(KVL, K, [V], []).per_key([], K, Vs, KVs) -> lists:reverse(KVs, [{K,msort(Vs)}]);per_key([{K,V}|L], K, Vs, KVs) -> % match per_key(L, K, [V|Vs], KVs);per_key([{K1,V}|L], K, Vs, KVs) -> per_key(L, K1, [V], [{K,msort(Vs)}|KVs]).rename(From, To) -> case file:rename(From, To) of ok -> ok; {error, Reason} -> {error, {file_error, {From, To}, Reason}} end.%% -> {ok, Bins} | throw({NewHead, Error})pread(Positions, Head) -> R = case file:pread(Head#head.fptr, Positions) of {ok, Bins} -> %% file:pread/2 can return 'eof' as "data". case lists:member(eof, Bins) of true -> {error, {premature_eof, Head#head.filename}}; false -> {ok, Bins} end; {error, Reason} when enomem =:= Reason; einval =:= Reason -> {error, {bad_object_header, Head#head.filename}}; {error, Reason} -> {file_error, Head#head.filename, Reason} end, case R of {ok, _Bins} -> R; Error -> throw(corrupt(Head, Error)) end.%% -> {ok, binary()} | throw({NewHead, Error})pread(Head, Pos, Min, Extra) -> R = case file:pread(Head#head.fptr, Pos, Min+Extra) of {error, Reason} when enomem =:= Reason; einval =:= Reason -> {error, {bad_object_header, Head#head.filename}}; {error, Reason} -> {file_error, Head#head.filename, Reason}; {ok, Bin} when size(Bin) < Min -> {error, {premature_eof, Head#head.filename}}; OK -> OK end, case R of {ok, _Bin} -> R; Error -> throw(corrupt(Head, Error)) end. %% -> eof | [] | {ok, Pointer, binary()}ipread(Fd, Pos1, MaxSize) -> case file:ipread_s32bu_p32bu(Fd, Pos1, MaxSize) of {ok, {0, 0, eof}} -> []; {ok, Reply} -> {ok, Reply}; _Else -> eof end.%% -> {Head, ok} | throw({Head, Error})pwrite(Head, []) -> {Head, ok};pwrite(Head, Bins) -> case file:pwrite(Head#head.fptr, Bins) of ok -> {Head, ok}; Error -> corrupt_file(Head, Error) end.%% -> ok | throw({Head, Error})write(_Head, []) -> ok;write(Head, Bins) -> case file:write(Head#head.fptr, Bins) of ok -> ok; Error -> corrupt_file(Head, Error) end.%% -> ok | throw({Head, Error})%% Same as file:write_file/2, but calls file:sync/1.write_file(Head, Bin) -> R = case file:open(Head#head.filename, [binary, raw, write]) of {ok, Fd} -> R1 = file:write(Fd, Bin), R2 = file:sync(Fd), file:close(Fd), if R1 =:= ok -> R2; true -> R1 end; Else -> Else end, case R of ok -> ok; Error -> corrupt_file(Head, Error) end.%% -> ok | throw({Head, Error})truncate(Head, Pos) -> case catch truncate(Head#head.fptr, Head#head.filename, Pos) of ok -> ok; Error -> throw(corrupt(Head, Error)) end.%% -> {ok, Pos} | throw({Head, Error})position(Head, Pos) -> case file:position(Head#head.fptr, Pos) of {error, _Reason} = Error -> corrupt_file(Head, Error); OK -> OK end. %% -> ok | throw({Head, Error})sync(Head) -> case file:sync(Head#head.fptr) of ok -> ok; Error -> corrupt_file(Head, Error) end.open(FileSpec, Args) -> case file:open(FileSpec, Args) of {ok, Fd} -> {ok, Fd}; Error -> file_error(FileSpec, Error) end.truncate(Fd, FileName, Pos) -> if Pos =:= cur -> ok; true -> position(Fd, FileName, Pos) end, case file:truncate(Fd) of ok -> ok; Error -> file_error(FileName, {error, Error}) end. fwrite(Fd, FileName, B) -> case file:write(Fd, B) of ok -> ok; Error -> file_error_close(Fd, FileName, Error) end.position(Fd, FileName, Pos) -> case file:position(Fd, Pos) of {error, Error} -> file_error(FileName, {error, Error}); OK -> OK end. position_close(Fd, FileName, Pos) -> case file:position(Fd, Pos) of {error, Error} -> file_error_close(Fd, FileName, {error, Error}); 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 -> ok; {error, {_NoWrites, Reason}} -> file_error(FileName, {error, Reason}) end.pread_close(Fd, FileName, Pos, Size) -> case file:pread(Fd, Pos, Size) of {error, Error} -> file_error_close(Fd, FileName, {error, Error}); {ok, Bin} when size(Bin) < Size -> file:close(Fd), throw({error, {tooshort, FileName}}); eof -> file:close(Fd), throw({error, {tooshort, FileName}}); OK -> OK end. file_error(FileName, {error, Reason}) -> throw({error, {file_error, FileName, Reason}}).file_error_close(Fd, FileName, {error, Reason}) -> file:close(Fd), throw({error, {file_error, FileName, Reason}}). read_n(Fd, Max) -> case file:read(Fd, Max) of {ok, Bin} -> Bin; _Else -> eof end.pread_n(Fd, Position, Max) -> case file:pread(Fd, Position, Max) of {ok, Bin} -> Bin; _ -> eof end.read_4(Fd, Position) -> {ok, _} = file:position(Fd, Position), <<Four:32>> = dets_utils:read_n(Fd, 4), Four.corrupt_file(Head, {error, Reason}) -> Error = {error, {file_error, Head#head.filename, Reason}}, throw(corrupt(Head, Error)).%% -> {NewHead, Error}corrupt_reason(Head, Reason) -> Error = {error, {Reason, Head#head.filename}}, corrupt(Head, Error).corrupt(Head, Error) -> case get(verbose) of yes -> error_logger:format("** dets: Corrupt table ~p: ~p\n", [Head#head.name, Error]); _ -> ok end, case Head#head.update_mode of {error, _} -> {Head, Error}; _ -> {Head#head{update_mode = Error}, Error} end.vformat(F, As) -> case get(verbose) of yes -> error_logger:format(F, As); _ -> ok end.code_to_type(?SET) -> set;code_to_type(?BAG) -> bag;code_to_type(?DUPLICATE_BAG) -> duplicate_bag;code_to_type(_Type) -> badtype.type_to_code(set) -> ?SET;type_to_code(bag) -> ?BAG;type_to_code(duplicate_bag) -> ?DUPLICATE_BAG.%%%%%% Write Cache%%% cache_size(C) -> {C#cache.delay, C#cache.tsize}.%% -> [object()] | falsecache_lookup(Type, [Key | Keys], CL, LU) ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?