emqx/apps/emqx_lwm2m/src/binary_util.erl

231 lines
4.8 KiB
Erlang

-module(binary_util).
%% copied from https://github.com/arcusfelis/binary2
%% Bytes
-export([ reverse/1
, join/2
, duplicate/2
, suffix/2
, prefix/2
]).
%% Bits
-export([ union/2
, subtract/2
, intersection/2
, inverse/1
]).
%% Trimming
-export([ rtrim/1
, rtrim/2
, ltrim/1
, ltrim/2
, trim/1
, trim/2
]).
%% Parsing
-export([ bin_to_int/1]).
%% Matching
-export([ optimize_patterns/1]).
%% CoAP
-export([ join_path/1]).
trim(B) -> trim(B, 0).
ltrim(B) -> ltrim(B, 0).
rtrim(B) -> rtrim(B, 0).
rtrim(B, X) when is_binary(B), is_integer(X) ->
S = byte_size(B),
do_rtrim(S, B, X);
rtrim(B, [_|_]=Xs) when is_binary(B) ->
S = byte_size(B),
do_mrtrim(S, B, Xs).
ltrim(B, X) when is_binary(B), is_integer(X) ->
do_ltrim(B, X);
ltrim(B, [_|_]=Xs) when is_binary(B) ->
do_mltrim(B, Xs).
%% @doc The second element is a single integer element or an ordset of elements.
trim(B, X) when is_binary(B), is_integer(X) ->
From = ltrimc(B, X, 0),
case byte_size(B) of
From ->
<<>>;
S ->
To = do_rtrimc(S, B, X),
binary:part(B, From, To - From)
end;
trim(B, [_|_]=Xs) when is_binary(B) ->
From = mltrimc(B, Xs, 0),
case byte_size(B) of
From ->
<<>>;
S ->
To = do_mrtrimc(S, B, Xs),
binary:part(B, From, To - From)
end.
do_ltrim(<<X, B/binary>>, X) ->
do_ltrim(B, X);
do_ltrim(B, _X) ->
B.
%% multi, left trimming.
do_mltrim(<<X, B/binary>> = XB, Xs) ->
case ordsets:is_element(X, Xs) of
true -> do_mltrim(B, Xs);
false -> XB
end;
do_mltrim(<<>>, _Xs) ->
<<>>.
do_rtrim(0, _B, _X) ->
<<>>;
do_rtrim(S, B, X) ->
S2 = S - 1,
case binary:at(B, S2) of
X -> do_rtrim(S2, B, X);
_ -> binary_part(B, 0, S)
end.
%% Multiple version of do_rtrim.
do_mrtrim(0, _B, _Xs) ->
<<>>;
do_mrtrim(S, B, Xs) ->
S2 = S - 1,
X = binary:at(B, S2),
case ordsets:is_element(X, Xs) of
true -> do_mrtrim(S2, B, Xs);
false -> binary_part(B, 0, S)
end.
ltrimc(<<X, B/binary>>, X, C) ->
ltrimc(B, X, C+1);
ltrimc(_B, _X, C) ->
C.
%% multi, left trimming, returns a count of matched bytes from the left.
mltrimc(<<X, B/binary>>, Xs, C) ->
case ordsets:is_element(X, Xs) of
true -> mltrimc(B, Xs, C+1);
false -> C
end;
mltrimc(<<>>, _Xs, C) ->
C.
% This clause will never be matched.
%do_rtrimc(0, _B, _X) ->
% 0;
do_rtrimc(S, B, X) ->
S2 = S - 1,
case binary:at(B, S2) of
X -> do_rtrimc(S2, B, X);
_ -> S
end.
do_mrtrimc(S, B, Xs) ->
S2 = S - 1,
X = binary:at(B, S2),
case ordsets:is_element(X, Xs) of
true -> do_mrtrimc(S2, B, Xs);
false -> S
end.
%% @doc Reverse the bytes' order.
reverse(Bin) when is_binary(Bin) ->
S = bit_size(Bin),
<<V:S/integer-little>> = Bin,
<<V:S/integer-big>>.
join([B|Bs], Sep) when is_binary(Sep) ->
iolist_to_binary([B|add_separator(Bs, Sep)]);
join([], _Sep) ->
<<>>.
add_separator([B|Bs], Sep) ->
[Sep, B | add_separator(Bs, Sep)];
add_separator([], _) ->
[].
%% @doc Repeat the binary `B' `C' times.
duplicate(C, B) ->
iolist_to_binary(lists:duplicate(C, B)).
prefix(B, L) when is_binary(B), is_integer(L), L > 0 ->
binary:part(B, 0, L).
suffix(B, L) when is_binary(B), is_integer(L), L > 0 ->
S = byte_size(B),
binary:part(B, S-L, L).
union(B1, B2) ->
S = bit_size(B1),
<<V1:S>> = B1,
<<V2:S>> = B2,
V3 = V1 bor V2,
<<V3:S>>.
subtract(B1, B2) ->
S = bit_size(B1),
<<V1:S>> = B1,
<<V2:S>> = B2,
V3 = (V1 bxor V2) band V1,
<<V3:S>>.
intersection(B1, B2) ->
S = bit_size(B1),
<<V1:S>> = B1,
<<V2:S>> = B2,
V3 = V1 band V2,
<<V3:S>>.
inverse(B1) ->
S = bit_size(B1),
<<V1:S>> = B1,
V2 = bnot V1,
<<V2:S>>.
%% @doc string:to_integer/1 for binaries
bin_to_int(Bin) ->
bin_to_int(Bin, 0).
bin_to_int(<<H, T/binary>>, X) when $0 =< H, H =< $9 ->
bin_to_int(T, (X*10)+(H-$0));
bin_to_int(Bin, X) ->
{X, Bin}.
%% Remove longer patterns if shorter pattern matches
%% Useful to run before binary:compile_pattern/1
optimize_patterns(Patterns) ->
Sorted = lists:usort(Patterns),
remove_long_duplicates(Sorted).
remove_long_duplicates([H|T]) ->
%% match(Subject, Pattern)
DedupT = [X || X <- T, binary:match(X, H) =:= nomatch],
[H|remove_long_duplicates(DedupT)];
remove_long_duplicates([]) ->
[].
join_path(PathList) ->
join_path(PathList, <<>>).
join_path([], Result) -> Result;
join_path([<<>> | PathList], Result) ->
join_path(PathList, Result);
join_path([Path | PathList], Result) ->
join_path(PathList, <<Result/binary, "/", Path/binary>>).