|
|
@ -2,74 +2,69 @@ |
|
|
|
|
|
|
|
%% 现在要使用硬编码的值。我们不能花时间在记录或地图中查找。 |
|
|
|
|
|
|
|
-export([truncate/1]). |
|
|
|
-export([truncate/2]). |
|
|
|
-export([cut/1]). |
|
|
|
-export([cut/2]). |
|
|
|
|
|
|
|
-define(MAX_DEPTH, 5). |
|
|
|
-define(MAX_BINARY_SIZE, 128). |
|
|
|
-define(MAX_BITS_SIZE, ?MAX_BINARY_SIZE * 8). |
|
|
|
-define(MAX_DATA_STRUCTURES, 5). |
|
|
|
-define(MAX_LIST_LENGTH, 32). |
|
|
|
-define(MAX_MAP_SIZE, 32). |
|
|
|
-define(MAX_TUPLE_SIZE, 32). |
|
|
|
-define(MaxDepth, 5). |
|
|
|
-define(MaxListSize, 32). |
|
|
|
-define(MaxMapSize, 32). |
|
|
|
-define(MaxTupleSize, 32). |
|
|
|
-define(MaxBinSize, 128). |
|
|
|
-define(MaxBitSize, ?MaxBinSize * 8). |
|
|
|
-define(MaxNestStruct, 5). |
|
|
|
|
|
|
|
truncate(Term) -> |
|
|
|
truncate(Term, 1). |
|
|
|
cut(Term) -> |
|
|
|
cut(Term, 1). |
|
|
|
|
|
|
|
truncate(_, Depth) when Depth > ?MAX_DEPTH -> |
|
|
|
cut(_, Depth) when Depth > ?MaxDepth -> |
|
|
|
'$truncated'; |
|
|
|
truncate(Term, _) when bit_size(Term) > ?MAX_BITS_SIZE -> |
|
|
|
<<Truncated:?MAX_BINARY_SIZE/binary, _/bits>> = Term, |
|
|
|
<<Truncated/binary, "$truncated">>; |
|
|
|
truncate(Term, Depth) when is_list(Term), Depth =:= ?MAX_DEPTH -> |
|
|
|
cut(Bits, _) when is_bitstring(Bits), bit_size(Bits) > ?MaxBitSize -> |
|
|
|
<<CutBits:?MaxBinSize/binary, _/bits>> = Bits, |
|
|
|
<<CutBits/binary, "$truncated">>; |
|
|
|
cut(Term, Depth) when is_list(Term), Depth =:= ?MaxDepth -> |
|
|
|
['$truncated']; |
|
|
|
truncate(Term, Depth) when is_list(Term) -> |
|
|
|
truncate_list(Term, Depth, 0, ?MAX_LIST_LENGTH, 0); |
|
|
|
truncate(Term, Depth) when is_map(Term), Depth =:= ?MAX_DEPTH -> |
|
|
|
cut(Term, Depth) when is_list(Term) -> |
|
|
|
cutList(Term, Depth, 0, ?MaxListSize, 0, []); |
|
|
|
cut(Term, Depth) when is_map(Term), Depth =:= ?MaxDepth -> |
|
|
|
#{'$truncated' => '$truncated'}; |
|
|
|
truncate(Term, Depth) when is_map(Term) -> |
|
|
|
maps:from_list(truncate_map(maps_to_list(Term, ?MAX_MAP_SIZE), Depth, 0)); |
|
|
|
truncate(Term, Depth) when is_tuple(Term), Depth =:= ?MAX_DEPTH -> |
|
|
|
cut(Term, Depth) when is_map(Term) -> |
|
|
|
maps:from_list(cutMap(maps_to_list(Term, ?MaxMapSize), Depth, 0)); |
|
|
|
cut(Term, Depth) when is_tuple(Term), Depth =:= ?MaxDepth -> |
|
|
|
{'$truncated'}; |
|
|
|
truncate(Term, Depth) when is_tuple(Term) -> |
|
|
|
list_to_tuple(truncate_list(tuple_to_list(Term), Depth, 0, ?MAX_TUPLE_SIZE, 0)); |
|
|
|
truncate(Term, _) -> |
|
|
|
cut(Term, Depth) when is_tuple(Term) -> |
|
|
|
list_to_tuple(cutList(tuple_to_list(Term), Depth, 0, ?MaxTupleSize, 0)); |
|
|
|
cut(Term, _) -> |
|
|
|
Term. |
|
|
|
|
|
|
|
truncate_list([], _, _, _, _) -> |
|
|
|
cutList([], _, _, _, _, Acc) -> |
|
|
|
lists:reverse(Acc); |
|
|
|
cutList([Term | Tail], Depth, Len, MaxLen, NumStructs, Acc) -> |
|
|
|
case Len > MaxLen orelse NumStructs > ?MaxNestStruct of |
|
|
|
true -> |
|
|
|
lists:reverse(['$truncated' | Acc]); |
|
|
|
_ -> |
|
|
|
%% if List was a cons, Tail can be anything |
|
|
|
cutList(Tail, Depth, Len + 1, MaxLen, NumStructs + isStruct(Term), [cut(Term, Depth + 1) | Acc]) |
|
|
|
end; |
|
|
|
cutList(Term, Depth, _, _, _, Acc) -> %% if List was a cons |
|
|
|
cut(Term, Depth + 1). |
|
|
|
|
|
|
|
cutMap([], _, _) -> |
|
|
|
[]; |
|
|
|
truncate_list(_, _, Len, MaxLen, _) when Len > MaxLen -> |
|
|
|
['$truncated']; |
|
|
|
truncate_list(_, _, _, _, NumStructs) when NumStructs > ?MAX_DATA_STRUCTURES -> |
|
|
|
['$truncated']; |
|
|
|
truncate_list([Term | Tail], Depth, Len, MaxLen, NumStructs) -> |
|
|
|
[truncate(Term, Depth + 1) |
|
|
|
%% if List was a cons, Tail can be anything |
|
|
|
| truncate_list(Tail, Depth, Len + 1, MaxLen, NumStructs + is_struct(Term))]; |
|
|
|
truncate_list(Term, Depth, _, _, _) -> %% if List was a cons |
|
|
|
truncate(Term, Depth + 1). |
|
|
|
|
|
|
|
truncate_map([], _, _) -> |
|
|
|
[]; |
|
|
|
truncate_map(_, _, NumStructs) when NumStructs > ?MAX_DATA_STRUCTURES -> |
|
|
|
cutMap(_, _, NumStructs) when NumStructs > ?MaxNestStruct -> |
|
|
|
[{'$truncated', '$truncated'}]; |
|
|
|
truncate_map([{Key, Value} | Tail], Depth, NumStructs) -> |
|
|
|
AddStruct = is_struct(Key) + is_struct(Value), |
|
|
|
[{truncate(Key, Depth + 1), truncate(Value, Depth + 1)} |
|
|
|
| truncate_map(Tail, Depth, NumStructs + AddStruct)]. |
|
|
|
|
|
|
|
is_struct(Term) -> |
|
|
|
if |
|
|
|
is_list(Term) -> 1; |
|
|
|
is_map(Term) -> 1; |
|
|
|
is_tuple(Term) -> 1; |
|
|
|
true -> 0 |
|
|
|
cutMap([{Key, Value} | Tail], Depth, NumStructs) -> |
|
|
|
AddStruct = isStruct(Key) + isStruct(Value), |
|
|
|
[{cut(Key, Depth + 1), cut(Value, Depth + 1)} | cutMap(Tail, Depth, NumStructs + AddStruct)]. |
|
|
|
|
|
|
|
isStruct(Term) -> |
|
|
|
case is_list(Term) orelse is_map(Term) orelse is_tuple(Term) of |
|
|
|
true -> |
|
|
|
1; |
|
|
|
_ -> |
|
|
|
0 |
|
|
|
end. |
|
|
|
|
|
|
|
%% Map iterators were introduced in Erlang/OTP 21. They replace |
|
|
|
%% the undocumented function erts_internal:maps_to_list/2. |
|
|
|
-ifdef(OTP_RELEASE). |
|
|
|
|
|
|
|
maps_to_list(Map, MaxSize) -> |
|
|
|
I = maps:iterator(Map), |
|
|
|
maps_to_list(maps:next(I), MaxSize, []). |
|
|
@ -83,29 +78,3 @@ maps_to_list(_, 0, Acc) -> |
|
|
|
lists:reverse([{'$truncated', '$truncated'} | Acc]); |
|
|
|
maps_to_list({K, V, I}, N, Acc) -> |
|
|
|
maps_to_list(maps:next(I), N - 1, [{K, V} | Acc]). |
|
|
|
|
|
|
|
-else. |
|
|
|
|
|
|
|
maps_to_list(Map, MaxSize) -> |
|
|
|
erts_internal:maps_to_list(Map, MaxSize). |
|
|
|
|
|
|
|
-endif. |
|
|
|
|
|
|
|
-ifdef(TEST). |
|
|
|
|
|
|
|
maps_to_list_test() -> |
|
|
|
[] = maps_to_list(#{}, 10), |
|
|
|
[{'$truncated', '$truncated'}] = maps_to_list(#{a => b}, 0), |
|
|
|
[{a, b}] = maps_to_list(#{a => b}, 10), |
|
|
|
[{a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list( |
|
|
|
#{a => b, c => d, e => f}, 3)), |
|
|
|
[{'$truncated', '$truncated'}, {a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list( |
|
|
|
#{a => b, c => d, e => f, g => h}, 3)), |
|
|
|
[{'$truncated', '$truncated'}, {a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list( |
|
|
|
#{a => b, c => d, e => f, g => h, i => j}, 3)), |
|
|
|
%% Confirm that truncated values are at the end. |
|
|
|
[_, _, _, {'$truncated', '$truncated'}] = maps_to_list( |
|
|
|
#{a => b, c => d, e => f, g => h, i => j}, 3), |
|
|
|
ok. |
|
|
|
|
|
|
|
-endif. |