%% -*- erlang-indent-level: 2 -*- %%-------------------------------------------------------------------- %% Tests that basic cases of binary pattern matching work %%-------------------------------------------------------------------- -module(bs_pmatch). -export([test/0]). test() -> %% construct some binaries Bin42 = <<42>>, Bin = <<12,17,42,0,0,0>>, BinSS = <<0,1,0,0,0>>, %% do some pattern matching ok = pm_const(Bin42), <<17,42,0,0,0>> = pm_tail(Bin), 42 = pm_little(<<0:1,42:7>>), 42 = pm_rec(Bin), 30 = pm_rec_acc(<<1,2,3,4,5,6,7,8,9,10>>, 0), 42 = pm_binary_tuple(Bin42), -1 = pm_with_illegal_float(), %% do some pattern matching with bound segments ok = pm_bound_var(), ok = pm_bound_tail(), %% do some tests with floating point numbers ok = pm_float(), ok = pm_float_little(), %% do some pattern matching with segments of unknown sizes {<<17>>, <<42,0,0,0>>} = pm_body_s(Bin, 1), {<<17>>, <<42,0,0,0>>} = pm_body_ss(Bin, 1, 4), {<<45>>, <<>>} = pm_size_split(<<1:16,45>>), {<<45>>, <<46,47>>} = pm_size_split(<<1:16,45,46,47>>), {<<45,46>>, <<47>>} = pm_size_split(<<2:16,45,46,47>>), {<<45,46>>, <<47>>} = pm_size_split_2(2, <<2:16,45,46,47>>), {'EXIT',{function_clause,_}} = (catch pm_size_split_2(42, <<2:16,45,46,47>>)), {<<45,46,47>>, <<48>>} = pm_sizes_split(<<16:8,3:16,45,46,47,48>>), <<"cdef">> = pm_skip_segment(<<2:8, "abcdef">>), -1 = pm_double_size_in_head(BinSS), -1 = pm_double_size_in_body(BinSS), %% and finally some cases which were problematic for various reasons ok = pm_bigs(), ok = pm_sean(), ok = pm_bin8(<<1,2,3,4,5,6,7,8>>), ok = pm_bs_match_string(), ok = pm_till_gc(), ok. %%-------------------- %% Test cases below %%-------------------- pm_const(<<42>>) -> ok. pm_tail(<<12, Bin/binary>>) -> Bin. pm_little(<<_:1, X:15/little>>) -> {wrong, X}; pm_little(<<_:1, X:7/little>>) -> X. pm_rec(<<12, Bin/binary>>) -> pm_rec(Bin); pm_rec(<<17, Word:4/little-signed-integer-unit:8>>) -> Word. pm_rec_acc(<<_:4, A:4, Rest/binary>>, Acc) -> case Rest of <> -> pm_rec_acc(NewRest, X+Y+Acc); <> -> pm_rec_acc(NewRest, X+Acc); <<2, NewRest/binary>> -> pm_rec_acc(NewRest, 1+Acc); <> -> pm_rec_acc(NewRest, A+Acc) end; pm_rec_acc(<<>>, Acc) -> Acc. pm_binary_tuple(<>) -> X; pm_binary_tuple({Y, Z}) -> Y + Z. pm_with_illegal_float() -> Bin = <<-1:64>>, % create a binary which is illegal as float pm_float_integer(Bin). % try to match it out as a float pm_float_integer(<>) -> F; pm_float_integer(<>) -> I. %%-------------------------------------------------------------------- %% Some tests with bound variables in segments pm_bound_var() -> ok = pm_bound_var(42, 13, <<42,13>>), no = pm_bound_var(42, 13, <<42,255>>), no = pm_bound_var(42, 13, <<154,255>>), ok. pm_bound_var(A, B, <>) -> ok; pm_bound_var(_, _, _) -> no. pm_bound_tail() -> ok = pm_bound_tail(<<>>, <<13,14>>), ok = pm_bound_tail(<<2,3>>, <<1,1,2,3>>), no = pm_bound_tail(<<2,3>>, <<1,1,2,7>>), no = pm_bound_tail(<<2,3>>, <<1,1,2,3,4>>), no = pm_bound_tail(<<2,3>>, <<>>), ok. pm_bound_tail(T, <<_:16, T/binary>>) -> ok; pm_bound_tail(_, _) -> no. %%-------------------------------------------------------------------- %% Floating point tests pm_float() -> F = f1(), G = f_one(), G = match_float(<<63,128,0,0>>, 32, 0), G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0), fcmp(F, match_float(<>, 32, 0)), fcmp(F, match_float(<>, 64, 0)), fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)), fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)), fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)), fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)), ok. fcmp(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok. match_float(Bin0, Fsz, I) -> Bin = make_sub_bin(Bin0), Bsz = bit_size(Bin), Tsz = Bsz - Fsz - I, <<_:I,F:Fsz/float,_:Tsz>> = Bin, F. pm_float_little() -> F = f2(), G = f_one(), G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0), G = match_float_little(<<0,0,128,63>>, 32, 0), fcmp(F, match_float_little(<>, 32, 0)), fcmp(F, match_float_little(<>, 64, 0)), fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)), fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)), fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)), fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)), ok. match_float_little(Bin0, Fsz, I) -> Bin = make_sub_bin(Bin0), Bsz = bit_size(Bin), Tsz = Bsz - Fsz - I, <<_:I, F:Fsz/float-little, _:Tsz>> = Bin, F. make_sub_bin(Bin0) -> Sz = byte_size(Bin0), Bin1 = <<37,Bin0/binary,38,39>>, <<_:8,Bin:Sz/binary,_:8,_:8>> = Bin1, Bin. f1() -> 3.1415. f2() -> 2.7133. f_one() -> 1.0. %%-------------------------------------------------------------------- %% Some tests using size fields specified within the binary pm_body_s(Bin, S1) -> <<12, B1:S1/binary, B2:4/binary>> = Bin, %% 4 is hard-coded {B1, B2}. pm_body_ss(Bin, S1, S2) -> <<12, B1:S1/binary, B2:S2/binary>> = Bin, {B1, B2}. pm_size_split(<>) -> {B, T}. pm_size_split_2(N, <>) -> {B, T}. pm_sizes_split(<>) -> {B, T}. pm_skip_segment(<>) -> T. %%-------------------------------------------------------------------- %% Some tests using multiple occurrences of size fields pm_double_size_in_head(<>) -> -S. pm_double_size_in_body(Bin) -> <> = Bin, -S. %%-------------------------------------------------------------------- %% matching with 64-bit integers which become big nums -define(BIG, 16#7fffffff7fffffff). pm_bigs() -> <> = <>, true = (X =:= big()), <> = <>, true = (Y =:= big()), ok. big() -> ?BIG. %%-------------------------------------------------------------------- pm_sean() -> small = sean1(<<>>), small = sean1(<<1>>), small = sean1(<<1,2>>), small = sean1(<<1,2,3>>), large = sean1(<<1,2,3,4>>), small = sean1(<<4>>), small = sean1(<<4,5>>), small = sean1(<<4,5,6>>), {'EXIT', {function_clause, _}} = (catch sean1(<<4,5,6,7>>)), ok. sean1(<>) when byte_size(B) < 4 -> small; sean1(<<1, _/binary>>) -> large. %%-------------------------------------------------------------------- %% Crashed on SPARC due to a bug in linear scan register allocator pm_bin8(<>) -> 10 = add4(A, B, C, D), 26 = add4(E, F, G, H), ok. add4(X, Y, Z, W) -> X + Y + Z + W. %%-------------------------------------------------------------------- %% Cases that exposed bugs in the handling of bs_match_string with an %% empty destination list. Reported on 2013/2/12 and fixed 2013/3/10. pm_bs_match_string() -> Bin = <<42,42>>, Bin = pm_match_string_head(Bin), ok = (pm_match_string_fun())(Bin). pm_match_string_head(<<42, _/bits>> = B) -> B. pm_match_string_fun() -> fun (<>) when X =:= 42 -> ok end. %%-------------------------------------------------------------------- %% Match a lot to force a garbage collection which exposed a bug pm_till_gc() -> Bin = <<16#76543210:32>>, 16#76543210 = pm_a_lot(Bin, 1000000), ok. pm_a_lot(<>, 0) -> X; pm_a_lot(<>, N) -> pm_a_lot(<>, N-1).