Просмотр исходного кода

feat(funcs): add bitsize/1 and subbits/2,3,6 functions

Given following Rule SQL:

```
select
  subbits(payload, 1, 8) as firstByte,
  case
    when firstByte = 123 then 't/{'
    when firstByte = 125 then 't/}'
    else 't/' + str(firstByte)
  end as tp
from "t/#"
```

Input Payload: `{x}`
Output: `{"tp": "t/{", "firstByte": 123}`

Input Payload: `}x`
Output: `{"tp": "t/}", "firstByte": 125}`

Input Payload: `a`
Output: `{"tp": "t/97", "firstByte": 97}`
Shawn 5 лет назад
Родитель
Сommit
15d048d8ec

+ 75 - 45
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, <<SubBits:Len/integer-unsigned-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/integer-unsigned-big-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"float">>, <<"unsigned">>, <<"big">>) ->
+    ?match_bits(Bits, <<SubBits:Len/float-unsigned-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/float-unsigned-big-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"unsigned">>, <<"big">>) ->
+    ?match_bits(Bits, <<SubBits:Len/bits-unsigned-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/bits-unsigned-big-unit:1>>);
+
+do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"signed">>, <<"big">>) ->
+    ?match_bits(Bits, <<SubBits:Len/integer-signed-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/integer-signed-big-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"float">>, <<"signed">>, <<"big">>) ->
+    ?match_bits(Bits, <<SubBits:Len/float-signed-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/float-signed-big-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"signed">>, <<"big">>) ->
+    ?match_bits(Bits, <<SubBits:Len/bits-signed-big-unit:1, _/bits>>,
+                      <<SubBits:Sz/bits-signed-big-unit:1>>);
+
+do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"unsigned">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/integer-unsigned-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/integer-unsigned-little-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"float">>, <<"unsigned">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/float-unsigned-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/float-unsigned-little-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"unsigned">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/bits-unsigned-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/bits-unsigned-little-unit:1>>);
+
+do_get_subbits(Bits, Sz, Len, <<"integer">>, <<"signed">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/integer-signed-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/integer-signed-little-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"float">>, <<"signed">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/float-signed-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/float-signed-little-unit:1>>);
+do_get_subbits(Bits, Sz, Len, <<"bits">>, <<"signed">>, <<"little">>) ->
+    ?match_bits(Bits, <<SubBits:Len/bits-signed-little-unit:1, _/bits>>,
+                      <<SubBits:Sz/bits-signed-little-unit:1>>).
+
 %%------------------------------------------------------------------------------
 %% 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).

+ 57 - 4
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