emqx_utils_binary.erl 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. %%--------------------------------------------------------------------
  2. %% Original file taken from https://github.com/arcusfelis/binary2
  3. %% Copyright (c) 2016 Michael Uvarov
  4. %% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
  5. %%
  6. %% Licensed under the Apache License, Version 2.0 (the "License");
  7. %% you may not use this file except in compliance with the License.
  8. %% You may obtain a copy of the License at
  9. %%
  10. %% http://www.apache.org/licenses/LICENSE-2.0
  11. %%
  12. %% Unless required by applicable law or agreed to in writing, software
  13. %% distributed under the License is distributed on an "AS IS" BASIS,
  14. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. %% See the License for the specific language governing permissions and
  16. %% limitations under the License.
  17. %%--------------------------------------------------------------------
  18. -module(emqx_utils_binary).
  19. %% Bytes
  20. -export([
  21. reverse/1,
  22. join/2,
  23. duplicate/2,
  24. suffix/2,
  25. prefix/2
  26. ]).
  27. %% Bits
  28. -export([
  29. union/2,
  30. subtract/2,
  31. intersection/2,
  32. inverse/1
  33. ]).
  34. %% Trimming
  35. -export([
  36. rtrim/1,
  37. rtrim/2,
  38. ltrim/1,
  39. ltrim/2,
  40. trim/1,
  41. trim/2
  42. ]).
  43. %% Parsing
  44. -export([bin_to_int/1]).
  45. %% Matching
  46. -export([optimize_patterns/1]).
  47. %% CoAP
  48. -export([join_path/1]).
  49. trim(B) -> trim(B, 0).
  50. ltrim(B) -> ltrim(B, 0).
  51. rtrim(B) -> rtrim(B, 0).
  52. rtrim(B, X) when is_binary(B), is_integer(X) ->
  53. S = byte_size(B),
  54. do_rtrim(S, B, X);
  55. rtrim(B, [_ | _] = Xs) when is_binary(B) ->
  56. S = byte_size(B),
  57. do_mrtrim(S, B, Xs).
  58. ltrim(B, X) when is_binary(B), is_integer(X) ->
  59. do_ltrim(B, X);
  60. ltrim(B, [_ | _] = Xs) when is_binary(B) ->
  61. do_mltrim(B, Xs).
  62. %% @doc The second element is a single integer element or an ordset of elements.
  63. trim(B, X) when is_binary(B), is_integer(X) ->
  64. From = ltrimc(B, X, 0),
  65. case byte_size(B) of
  66. From ->
  67. <<>>;
  68. S ->
  69. To = do_rtrimc(S, B, X),
  70. binary:part(B, From, To - From)
  71. end;
  72. trim(B, [_ | _] = Xs) when is_binary(B) ->
  73. From = mltrimc(B, Xs, 0),
  74. case byte_size(B) of
  75. From ->
  76. <<>>;
  77. S ->
  78. To = do_mrtrimc(S, B, Xs),
  79. binary:part(B, From, To - From)
  80. end.
  81. do_ltrim(<<X, B/binary>>, X) ->
  82. do_ltrim(B, X);
  83. do_ltrim(B, _X) ->
  84. B.
  85. %% multi, left trimming.
  86. do_mltrim(<<X, B/binary>> = XB, Xs) ->
  87. case ordsets:is_element(X, Xs) of
  88. true -> do_mltrim(B, Xs);
  89. false -> XB
  90. end;
  91. do_mltrim(<<>>, _Xs) ->
  92. <<>>.
  93. do_rtrim(0, _B, _X) ->
  94. <<>>;
  95. do_rtrim(S, B, X) ->
  96. S2 = S - 1,
  97. case binary:at(B, S2) of
  98. X -> do_rtrim(S2, B, X);
  99. _ -> binary_part(B, 0, S)
  100. end.
  101. %% Multiple version of do_rtrim.
  102. do_mrtrim(0, _B, _Xs) ->
  103. <<>>;
  104. do_mrtrim(S, B, Xs) ->
  105. S2 = S - 1,
  106. X = binary:at(B, S2),
  107. case ordsets:is_element(X, Xs) of
  108. true -> do_mrtrim(S2, B, Xs);
  109. false -> binary_part(B, 0, S)
  110. end.
  111. ltrimc(<<X, B/binary>>, X, C) ->
  112. ltrimc(B, X, C + 1);
  113. ltrimc(_B, _X, C) ->
  114. C.
  115. %% multi, left trimming, returns a count of matched bytes from the left.
  116. mltrimc(<<X, B/binary>>, Xs, C) ->
  117. case ordsets:is_element(X, Xs) of
  118. true -> mltrimc(B, Xs, C + 1);
  119. false -> C
  120. end;
  121. mltrimc(<<>>, _Xs, C) ->
  122. C.
  123. % This clause will never be matched.
  124. %do_rtrimc(0, _B, _X) ->
  125. % 0;
  126. do_rtrimc(S, B, X) ->
  127. S2 = S - 1,
  128. case binary:at(B, S2) of
  129. X -> do_rtrimc(S2, B, X);
  130. _ -> S
  131. end.
  132. do_mrtrimc(S, B, Xs) ->
  133. S2 = S - 1,
  134. X = binary:at(B, S2),
  135. case ordsets:is_element(X, Xs) of
  136. true -> do_mrtrimc(S2, B, Xs);
  137. false -> S
  138. end.
  139. %% @doc Reverse the bytes' order.
  140. reverse(Bin) when is_binary(Bin) ->
  141. S = bit_size(Bin),
  142. <<V:S/integer-little>> = Bin,
  143. <<V:S/integer-big>>.
  144. join([B | Bs], Sep) when is_binary(Sep) ->
  145. iolist_to_binary([B | add_separator(Bs, Sep)]);
  146. join([], _Sep) ->
  147. <<>>.
  148. add_separator([B | Bs], Sep) ->
  149. [Sep, B | add_separator(Bs, Sep)];
  150. add_separator([], _) ->
  151. [].
  152. %% @doc Repeat the binary `B' `C' times.
  153. duplicate(C, B) ->
  154. iolist_to_binary(lists:duplicate(C, B)).
  155. prefix(B, L) when is_binary(B), is_integer(L), L > 0 ->
  156. binary:part(B, 0, L).
  157. suffix(B, L) when is_binary(B), is_integer(L), L > 0 ->
  158. S = byte_size(B),
  159. binary:part(B, S - L, L).
  160. union(B1, B2) ->
  161. S = bit_size(B1),
  162. <<V1:S>> = B1,
  163. <<V2:S>> = B2,
  164. V3 = V1 bor V2,
  165. <<V3:S>>.
  166. subtract(B1, B2) ->
  167. S = bit_size(B1),
  168. <<V1:S>> = B1,
  169. <<V2:S>> = B2,
  170. V3 = (V1 bxor V2) band V1,
  171. <<V3:S>>.
  172. intersection(B1, B2) ->
  173. S = bit_size(B1),
  174. <<V1:S>> = B1,
  175. <<V2:S>> = B2,
  176. V3 = V1 band V2,
  177. <<V3:S>>.
  178. inverse(B1) ->
  179. S = bit_size(B1),
  180. <<V1:S>> = B1,
  181. V2 = bnot V1,
  182. <<V2:S>>.
  183. %% @doc string:to_integer/1 for binaries
  184. bin_to_int(Bin) ->
  185. bin_to_int(Bin, 0).
  186. bin_to_int(<<H, T/binary>>, X) when $0 =< H, H =< $9 ->
  187. bin_to_int(T, (X * 10) + (H - $0));
  188. bin_to_int(Bin, X) ->
  189. {X, Bin}.
  190. %% Remove longer patterns if shorter pattern matches
  191. %% Useful to run before binary:compile_pattern/1
  192. optimize_patterns(Patterns) ->
  193. Sorted = lists:usort(Patterns),
  194. remove_long_duplicates(Sorted).
  195. remove_long_duplicates([H | T]) ->
  196. %% match(Subject, Pattern)
  197. DedupT = [X || X <- T, binary:match(X, H) =:= nomatch],
  198. [H | remove_long_duplicates(DedupT)];
  199. remove_long_duplicates([]) ->
  200. [].
  201. join_path(PathList) ->
  202. join_path(PathList, <<>>).
  203. join_path([], Result) -> Result;
  204. join_path([<<>> | PathList], Result) -> join_path(PathList, Result);
  205. join_path([Path | PathList], Result) -> join_path(PathList, <<Result/binary, "/", Path/binary>>).