diff --git a/apps/emqx_rule_engine/src/emqx_rule_funcs.erl b/apps/emqx_rule_engine/src/emqx_rule_funcs.erl index ed2e53efa..a45ac1beb 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_funcs.erl +++ b/apps/emqx_rule_engine/src/emqx_rule_funcs.erl @@ -78,6 +78,10 @@ , bitxor/2 , bitsl/2 , bitsr/2 + , bitsize/1 + , subbits/2 + , subbits/3 + , subbits/6 ]). %% Data Type Convertion @@ -233,7 +237,7 @@ payload() -> payload(Path) -> fun(#{payload := Payload}) when erlang:is_map(Payload) -> - emqx_rule_maps:nested_get(map_path(Path), Payload); + map_get(Path, Payload); (_) -> undefined end. @@ -401,6 +405,74 @@ bitsl(X, I) when is_integer(X), is_integer(I) -> bitsr(X, I) when is_integer(X), is_integer(I) -> X bsr I. +bitsize(Bits) when is_bitstring(Bits) -> + bit_size(Bits). + +subbits(Bits, Len) when is_integer(Len), is_bitstring(Bits) -> + subbits(Bits, 1, Len). + +subbits(Bits, Start, Len) when is_integer(Start), is_integer(Len), is_bitstring(Bits) -> + get_subbits(Bits, Start, Len, <<"integer">>, <<"unsigned">>, <<"big">>). + +subbits(Bits, Start, Len, Type, Signedness, Endianness) when is_integer(Start), is_integer(Len), is_bitstring(Bits) -> + get_subbits(Bits, Start, Len, Type, Signedness, Endianness). + +get_subbits(Bits, Start, Len, Type, Signedness, Endianness) -> + Begin = Start - 1, + case Bits of + <<_:Begin, Rem/bits>> when Rem =/= <<>> -> + Sz = bit_size(Rem), + do_get_subbits(Rem, Sz, Len, Type, Signedness, Endianness); + _ -> undefined + end. + +-define(match_bits(Bits0, Pattern, ElesePattern), + case Bits0 of + Pattern -> + SubBits; + ElesePattern -> + SubBits + end). +do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"unsigned">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"float">>, <<"unsigned">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"unsigned">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); + +do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"signed">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"float">>, <<"signed">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"signed">>, <<"big">>) -> + ?match_bits(Bits, <>, + <>); + +do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"unsigned">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"float">>, <<"unsigned">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"unsigned">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>); + +do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"signed">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"float">>, <<"signed">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>); +do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"signed">>, <<"little">>) -> + ?match_bits(Bits, <>, + <>). + %%------------------------------------------------------------------------------ %% Data Type Convertion Funcs %%------------------------------------------------------------------------------ @@ -607,52 +679,10 @@ map_get(Key, Map) -> map_get(Key, Map, undefined). map_get(Key, Map, Default) -> - case maps:find(Key, Map) of - {ok, Val} -> Val; - error when is_atom(Key) -> - %% the map may have an equivalent binary-form key - BinKey = emqx_rule_utils:bin(Key), - case maps:find(BinKey, Map) of - {ok, Val} -> Val; - error -> Default - end; - error when is_binary(Key) -> - try %% the map may have an equivalent atom-form key - AtomKey = list_to_existing_atom(binary_to_list(Key)), - case maps:find(AtomKey, Map) of - {ok, Val} -> Val; - error -> Default - end - catch error:badarg -> - Default - end; - error -> - Default - end. + emqx_rule_maps:nested_get(map_path(Key), Map, Default). map_put(Key, Val, Map) -> - case maps:find(Key, Map) of - {ok, _} -> maps:put(Key, Val, Map); - error when is_atom(Key) -> - %% the map may have an equivalent binary-form key - BinKey = emqx_rule_utils:bin(Key), - case maps:find(BinKey, Map) of - {ok, _} -> maps:put(BinKey, Val, Map); - error -> maps:put(Key, Val, Map) - end; - error when is_binary(Key) -> - try %% the map may have an equivalent atom-form key - AtomKey = list_to_existing_atom(binary_to_list(Key)), - case maps:find(AtomKey, Map) of - {ok, _} -> maps:put(AtomKey, Val, Map); - error -> maps:put(Key, Val, Map) - end - catch error:badarg -> - maps:put(Key, Val, Map) - end; - error -> - maps:put(Key, Val, Map) - end. + emqx_rule_maps:nested_put(map_path(Key), Val, Map). mget(Key, Map) -> mget(Key, Map, undefined). diff --git a/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl b/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl index 98e62f415..c50d0e02d 100644 --- a/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl +++ b/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl @@ -489,22 +489,75 @@ t_contains(_) -> t_map_get(_) -> ?assertEqual(1, apply_func(map_get, [<<"a">>, #{a => 1}])), - ?assertEqual(undefined, apply_func(map_get, [<<"a">>, #{}])). + ?assertEqual(undefined, apply_func(map_get, [<<"a">>, #{}])), + ?assertEqual(1, apply_func(map_get, [<<"a.b">>, #{a => #{b => 1}}])), + ?assertEqual(undefined, apply_func(map_get, [<<"a.c">>, #{a => #{b => 1}}])). t_map_put(_) -> ?assertEqual(#{<<"a">> => 1}, apply_func(map_put, [<<"a">>, 1, #{}])), - ?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])). + ?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])), + ?assertEqual(#{<<"a">> => #{<<"b">> => 1}}, apply_func(map_put, [<<"a.b">>, 1, #{}])), + ?assertEqual(#{a => #{b => 1, <<"c">> => 1}}, apply_func(map_put, [<<"a.c">>, 1, #{a => #{b => 1}}])). - t_mget(_) -> +t_mget(_) -> ?assertEqual(1, apply_func(map_get, [<<"a">>, #{a => 1}])), ?assertEqual(1, apply_func(map_get, [<<"a">>, #{<<"a">> => 1}])), ?assertEqual(undefined, apply_func(map_get, [<<"a">>, #{}])). - t_mput(_) -> +t_mput(_) -> ?assertEqual(#{<<"a">> => 1}, apply_func(map_put, [<<"a">>, 1, #{}])), ?assertEqual(#{<<"a">> => 2}, apply_func(map_put, [<<"a">>, 2, #{<<"a">> => 1}])), ?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])). +t_bitsize(_) -> + ?assertEqual(8, apply_func(bitsize, [<<"a">>])), + ?assertEqual(4, apply_func(bitsize, [<<15:4>>])). + +t_subbits(_) -> + ?assertEqual(1, apply_func(subbits, [<<255:8>>, 1])), + ?assertEqual(3, apply_func(subbits, [<<255:8>>, 2])), + ?assertEqual(7, apply_func(subbits, [<<255:8>>, 3])), + ?assertEqual(15, apply_func(subbits, [<<255:8>>, 4])), + ?assertEqual(31, apply_func(subbits, [<<255:8>>, 5])), + ?assertEqual(63, apply_func(subbits, [<<255:8>>, 6])), + ?assertEqual(127, apply_func(subbits, [<<255:8>>, 7])), + ?assertEqual(255, apply_func(subbits, [<<255:8>>, 8])). + +t_subbits2(_) -> + ?assertEqual(1, apply_func(subbits, [<<255:8>>, 1, 1])), + ?assertEqual(3, apply_func(subbits, [<<255:8>>, 1, 2])), + ?assertEqual(7, apply_func(subbits, [<<255:8>>, 1, 3])), + ?assertEqual(15, apply_func(subbits, [<<255:8>>, 1, 4])), + ?assertEqual(31, apply_func(subbits, [<<255:8>>, 1, 5])), + ?assertEqual(63, apply_func(subbits, [<<255:8>>, 1, 6])), + ?assertEqual(127, apply_func(subbits, [<<255:8>>, 1, 7])), + ?assertEqual(255, apply_func(subbits, [<<255:8>>, 1, 8])). + +t_subbits2_1(_) -> + ?assertEqual(1, apply_func(subbits, [<<255:8>>, 2, 1])), + ?assertEqual(3, apply_func(subbits, [<<255:8>>, 2, 2])), + ?assertEqual(7, apply_func(subbits, [<<255:8>>, 2, 3])), + ?assertEqual(15, apply_func(subbits, [<<255:8>>, 2, 4])), + ?assertEqual(31, apply_func(subbits, [<<255:8>>, 2, 5])), + ?assertEqual(63, apply_func(subbits, [<<255:8>>, 2, 6])), + ?assertEqual(127, apply_func(subbits, [<<255:8>>, 2, 7])), + ?assertEqual(127, apply_func(subbits, [<<255:8>>, 2, 8])). + +t_subbits2_integer(_) -> + ?assertEqual(456, apply_func(subbits, [<<456:32/integer>>, 1, 32, <<"integer">>, <<"signed">>, <<"big">>])), + ?assertEqual(-456, apply_func(subbits, [<<-456:32/integer>>, 1, 32, <<"integer">>, <<"signed">>, <<"big">>])). + +t_subbits2_float(_) -> + R = apply_func(subbits, [<<5.3:64/float>>, 1, 64, <<"float">>, <<"unsigned">>, <<"big">>]), + RL = (5.3 - R), + ct:pal(";;;;~p", [R]), + ?assert( (RL >= 0 andalso RL < 0.0001) orelse (RL =< 0 andalso RL > -0.0001)), + + R2 = apply_func(subbits, [<<-5.3:64/float>>, 1, 64, <<"float">>, <<"signed">>, <<"big">>]), + + RL2 = (5.3 + R2), + ct:pal(";;;;~p", [R2]), + ?assert( (RL2 >= 0 andalso RL2 < 0.0001) orelse (RL2 =< 0 andalso RL2 > -0.0001)). %%------------------------------------------------------------------------------ %% Test cases for Hash funcs