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

126 рядки
4.4 KiB

4 роки тому
  1. %% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved.
  2. %%
  3. %% This package, Looking Glass, is double-licensed under the Mozilla
  4. %% Public License 1.1 ("MPL") and the Apache License version 2
  5. %% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
  6. %% please see LICENSE-APACHE2.
  7. %%
  8. %% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
  9. %% either express or implied. See the LICENSE file for specific language governing
  10. %% rights and limitations of this software.
  11. %%
  12. %% If you have any questions regarding licensing, please contact us at
  13. %% info@rabbitmq.com.
  14. %% Going for hardcoded values for now. We can't spend time
  15. %% looking up inside a record or map for this.
  16. -module(lg_term).
  17. -export([truncate/1]).
  18. -export([truncate/2]).
  19. -define(MAX_DEPTH, 5).
  20. -define(MAX_BINARY_SIZE, 128).
  21. -define(MAX_BITSTRING_SIZE, ?MAX_BINARY_SIZE * 8).
  22. -define(MAX_DATA_STRUCTURES, 5).
  23. -define(MAX_LIST_LENGTH, 32).
  24. -define(MAX_MAP_SIZE, 32).
  25. -define(MAX_TUPLE_SIZE, 32).
  26. truncate(Term) ->
  27. truncate(Term, 1).
  28. truncate(_, Depth) when Depth > ?MAX_DEPTH ->
  29. '$truncated';
  30. truncate(Term, _) when bit_size(Term) > ?MAX_BITSTRING_SIZE ->
  31. <<Truncated:?MAX_BINARY_SIZE/binary, _/bits>> = Term,
  32. <<Truncated/binary, "$truncated">>;
  33. truncate(Term, Depth) when is_list(Term), Depth =:= ?MAX_DEPTH ->
  34. ['$truncated'];
  35. truncate(Term, Depth) when is_list(Term) ->
  36. truncate_list(Term, Depth, 0, ?MAX_LIST_LENGTH, 0);
  37. truncate(Term, Depth) when is_map(Term), Depth =:= ?MAX_DEPTH ->
  38. #{'$truncated' => '$truncated'};
  39. truncate(Term, Depth) when is_map(Term) ->
  40. maps:from_list(truncate_map(maps_to_list(Term, ?MAX_MAP_SIZE), Depth, 0));
  41. truncate(Term, Depth) when is_tuple(Term), Depth =:= ?MAX_DEPTH ->
  42. {'$truncated'};
  43. truncate(Term, Depth) when is_tuple(Term) ->
  44. list_to_tuple(truncate_list(tuple_to_list(Term), Depth, 0, ?MAX_TUPLE_SIZE, 0));
  45. truncate(Term, _) ->
  46. Term.
  47. truncate_list([], _, _, _, _) ->
  48. [];
  49. truncate_list(_, _, Len, MaxLen, _) when Len > MaxLen ->
  50. ['$truncated'];
  51. truncate_list(_, _, _, _, NumStructs) when NumStructs > ?MAX_DATA_STRUCTURES ->
  52. ['$truncated'];
  53. truncate_list([Term|Tail], Depth, Len, MaxLen, NumStructs) ->
  54. [truncate(Term, Depth + 1)
  55. %% if List was a cons, Tail can be anything
  56. |truncate_list(Tail, Depth, Len + 1, MaxLen, NumStructs + is_struct(Term))];
  57. truncate_list(Term, Depth, _, _, _) -> %% if List was a cons
  58. truncate(Term, Depth + 1).
  59. truncate_map([], _, _) ->
  60. [];
  61. truncate_map(_, _, NumStructs) when NumStructs > ?MAX_DATA_STRUCTURES ->
  62. [{'$truncated', '$truncated'}];
  63. truncate_map([{Key, Value}|Tail], Depth, NumStructs) ->
  64. AddStruct = is_struct(Key) + is_struct(Value),
  65. [{truncate(Key, Depth + 1), truncate(Value, Depth + 1)}
  66. |truncate_map(Tail, Depth, NumStructs + AddStruct)].
  67. is_struct(Term) ->
  68. if
  69. is_list(Term) -> 1;
  70. is_map(Term) -> 1;
  71. is_tuple(Term) -> 1;
  72. true -> 0
  73. end.
  74. %% Map iterators were introduced in Erlang/OTP 21. They replace
  75. %% the undocumented function erts_internal:maps_to_list/2.
  76. -ifdef(OTP_RELEASE).
  77. maps_to_list(Map, MaxSize) ->
  78. I = maps:iterator(Map),
  79. maps_to_list(maps:next(I), MaxSize, []).
  80. %% Returns elements in arbitrary order. We reverse when we truncate
  81. %% so that the truncated elements come at the end to avoid having
  82. %% two truncated elements in the final output.
  83. maps_to_list(none, _, Acc) ->
  84. Acc;
  85. maps_to_list(_, 0, Acc) ->
  86. lists:reverse([{'$truncated', '$truncated'}|Acc]);
  87. maps_to_list({K, V, I}, N, Acc) ->
  88. maps_to_list(maps:next(I), N - 1, [{K, V}|Acc]).
  89. -else.
  90. maps_to_list(Map, MaxSize) ->
  91. erts_internal:maps_to_list(Map, MaxSize).
  92. -endif.
  93. -ifdef(TEST).
  94. maps_to_list_test() ->
  95. [] = maps_to_list(#{}, 10),
  96. [{'$truncated', '$truncated'}] = maps_to_list(#{a => b}, 0),
  97. [{a, b}] = maps_to_list(#{a => b}, 10),
  98. [{a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list(
  99. #{a => b, c => d, e => f}, 3)),
  100. [{'$truncated', '$truncated'}, {a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list(
  101. #{a => b, c => d, e => f, g => h}, 3)),
  102. [{'$truncated', '$truncated'}, {a, b}, {c, d}, {e, f}] = lists:sort(maps_to_list(
  103. #{a => b, c => d, e => f, g => h, i => j}, 3)),
  104. %% Confirm that truncated values are at the end.
  105. [_, _, _, {'$truncated', '$truncated'}] = maps_to_list(
  106. #{a => b, c => d, e => f, g => h, i => j}, 3),
  107. ok.
  108. -endif.