Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 

126 рядки
4.4 KiB

%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved.
%%
%% This package, Looking Glass, is double-licensed under the Mozilla
%% Public License 1.1 ("MPL") and the Apache License version 2
%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
%% please see LICENSE-APACHE2.
%%
%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
%% either express or implied. See the LICENSE file for specific language governing
%% rights and limitations of this software.
%%
%% If you have any questions regarding licensing, please contact us at
%% info@rabbitmq.com.
%% Going for hardcoded values for now. We can't spend time
%% looking up inside a record or map for this.
-module(lg_term).
-export([truncate/1]).
-export([truncate/2]).
-define(MAX_DEPTH, 5).
-define(MAX_BINARY_SIZE, 128).
-define(MAX_BITSTRING_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).
truncate(Term) ->
truncate(Term, 1).
truncate(_, Depth) when Depth > ?MAX_DEPTH ->
'$truncated';
truncate(Term, _) when bit_size(Term) > ?MAX_BITSTRING_SIZE ->
<<Truncated:?MAX_BINARY_SIZE/binary, _/bits>> = Term,
<<Truncated/binary, "$truncated">>;
truncate(Term, Depth) when is_list(Term), Depth =:= ?MAX_DEPTH ->
['$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 ->
#{'$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 ->
{'$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, _) ->
Term.
truncate_list([], _, _, _, _) ->
[];
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 ->
[{'$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
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, []).
%% Returns elements in arbitrary order. We reverse when we truncate
%% so that the truncated elements come at the end to avoid having
%% two truncated elements in the final output.
maps_to_list(none, _, Acc) ->
Acc;
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.