Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:26
erlang
1891-Property-based-tests-for-the-binary-module...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 1891-Property-based-tests-for-the-binary-module.patch of Package erlang
From 4b675e57c3934b8fec05922686de0e08670ccf19 Mon Sep 17 00:00:00 2001 From: Maria Scott <maria-12648430@hnc-agency.org> Date: Wed, 30 Aug 2023 16:54:52 +0200 Subject: [PATCH 1/4] Property-based tests for the binary module Co-authored-by: Jan Uhlig <juhlig@hnc-agency.org> --- .../test/binary_property_test_SUITE.erl | 134 ++- lib/stdlib/test/property_test/binary_prop.erl | 896 +++++++++++++++++- 2 files changed, 1013 insertions(+), 17 deletions(-) diff --git a/lib/stdlib/test/binary_property_test_SUITE.erl b/lib/stdlib/test/binary_property_test_SUITE.erl index 4ec1ba67ab..87740a0dc5 100644 --- a/lib/stdlib/test/binary_property_test_SUITE.erl +++ b/lib/stdlib/test/binary_property_test_SUITE.erl @@ -22,7 +22,29 @@ -compile([export_all, nowarn_export_all]). all() -> - [encode_hex_case]. + [at_case, at_invalid_case, + bin_to_list_1_case, + bin_to_list_2_3_case, bin_to_list_2_3_invalid_case, + compile_pattern_case, compile_pattern_invalid_case, + copy_case, copy_2_invalid_case, + decode_hex_case, decode_hex_invalid_case, + decode_unsigned_case, + encode_hex_case, + encode_unsigned_case, encode_unsigned_invalid_case, + first_case, + last_case, + list_to_bin_case, list_to_bin_invalid_case, + longest_common_prefix_case, + longest_common_suffix_case, + match_2_case, + match_3_case, match_3_invalid_case, + matches_2_case, + matches_3_case, matches_3_invalid_case, + part_case, part_invalid_case, + replace_3_case, + replace_4_case, replace_4_invalid1_case, replace_4_invalid2_case, + split_2_case, + split_3_case, split_3_invalid_case]. init_per_suite(Config) -> ct_property_test:init_per_suite(Config). @@ -30,6 +52,114 @@ init_per_suite(Config) -> end_per_suite(Config) -> Config. +do_proptest(Prop, Config) -> + ct_property_test:quickcheck(binary_prop:Prop(), Config). + +at_case(Config) -> + do_proptest(prop_at, Config). + +at_invalid_case(Config) -> + do_proptest(prop_at_invalid, Config). + +bin_to_list_1_case(Config) -> + do_proptest(prop_bin_to_list_1, Config). + +bin_to_list_2_3_case(Config) -> + do_proptest(prop_bin_to_list_2_3, Config). + +bin_to_list_2_3_invalid_case(Config) -> + do_proptest(prop_bin_to_list_2_3_invalid, Config). + +compile_pattern_case(Config) -> + do_proptest(prop_compile_pattern, Config). + +compile_pattern_invalid_case(Config) -> + do_proptest(prop_compile_pattern_invalid, Config). + +copy_case(Config) -> + do_proptest(prop_copy, Config). + +copy_2_invalid_case(Config) -> + do_proptest(prop_copy_2_invalid, Config). + +decode_hex_case(Config) -> + do_proptest(prop_decode_hex, Config). + +decode_hex_invalid_case(Config) -> + do_proptest(prop_decode_hex_invalid, Config). + +decode_unsigned_case(Config) -> + do_proptest(prop_decode_unsigned, Config). + encode_hex_case(Config) -> - ct_property_test:quickcheck(binary_prop:prop_hex_encode_2(), Config). + do_proptest(prop_encode_hex, Config). + +encode_unsigned_case(Config) -> + do_proptest(prop_encode_unsigned, Config). + +encode_unsigned_invalid_case(Config) -> + do_proptest(prop_encode_unsigned_invalid, Config). + +first_case(Config) -> + do_proptest(prop_first, Config). + +last_case(Config) -> + do_proptest(prop_last, Config). + +list_to_bin_case(Config) -> + do_proptest(prop_list_to_bin, Config). + +list_to_bin_invalid_case(Config) -> + do_proptest(prop_list_to_bin_invalid, Config). + +longest_common_prefix_case(Config) -> + do_proptest(prop_longest_common_prefix, Config). + +longest_common_suffix_case(Config) -> + do_proptest(prop_longest_common_suffix, Config). + +match_2_case(Config) -> + do_proptest(prop_match_2, Config). + +match_3_case(Config) -> + do_proptest(prop_match_3, Config). + +match_3_invalid_case(Config) -> + do_proptest(prop_match_3_invalid, Config). + +matches_2_case(Config) -> + do_proptest(prop_matches_2, Config). + +matches_3_case(Config) -> + do_proptest(prop_matches_3, Config). + +matches_3_invalid_case(Config) -> + do_proptest(prop_matches_3_invalid, Config). + +part_case(Config) -> + do_proptest(prop_part, Config). + +part_invalid_case(Config) -> + do_proptest(prop_part_invalid, Config). + +replace_3_case(Config) -> + do_proptest(prop_replace_3, Config). + +replace_4_case(Config) -> + do_proptest(prop_replace_4, Config). + +replace_4_invalid1_case(Config) -> + do_proptest(prop_replace_4_invalid1, Config). + +replace_4_invalid2_case(Config) -> + do_proptest(prop_replace_4_invalid2, Config). + +split_2_case(Config) -> + do_proptest(prop_split_2, Config). + +split_3_case(Config) -> + do_proptest(prop_split_3, Config). + +split_3_invalid_case(Config) -> + do_proptest(prop_split_3_invalid, Config). diff --git a/lib/stdlib/test/property_test/binary_prop.erl b/lib/stdlib/test/property_test/binary_prop.erl index 00efa77b8c..b07cf13f43 100644 --- a/lib/stdlib/test/property_test/binary_prop.erl +++ b/lib/stdlib/test/property_test/binary_prop.erl @@ -1,27 +1,856 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2021-2023. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% -module(binary_prop). --export([prop_hex_encode_2/0]). +-include_lib("common_test/include/ct_property_test.hrl"). --compile([export_all, nowarn_export_all]). +%%%%%%%%%%%%%%%%%% +%%% Properties %%% +%%%%%%%%%%%%%%%%%% --include_lib("common_test/include/ct_property_test.hrl"). +%% --- at/2 ----------------------------------------------------------- +prop_at() -> + ?FORALL( + {Bin, Pos, Byte}, + ?LET( + {B1, Bt, B2}, + {binary(), byte(), binary()}, + {<<B1/binary, Bt, B2/binary>>, byte_size(B1), Bt} + ), + Byte =:= binary:at(Bin, Pos) + ). + +prop_at_invalid() -> + ?FORALL( + {Bin, Pos}, + ?LET( + B, + binary(), + {B, gen_index_invalid(B)} + ), + expect_error(fun binary:at/2, [Bin, Pos]) + ). + +%% --- bin_to_list/1 -------------------------------------------------- +prop_bin_to_list_1() -> + ?FORALL( + {Bin, List}, + ?LET( + L, + list(byte()), + {<< <<Bt>> || Bt <- L >>, L} + ), + List =:= binary:bin_to_list(Bin) + ). + +%% --- bin_to_list/2,3 ------------------------------------------------ +prop_bin_to_list_2_3() -> + ?FORALL( + {Bin, List, {Pos, Len}=PosLen}, + ?LET( + {L1, L, L2}, + {list(byte()), list(byte()), list(byte())}, + {<< <<Bt>> || Bt <- L1 ++ L ++ L2 >>, L, {length(L1), length(L)}} + ), + List =:= binary:bin_to_list(Bin, PosLen) andalso + List =:= binary:bin_to_list(Bin, Pos, Len) + ). + +prop_bin_to_list_2_3_invalid() -> + ?FORALL( + {Bin, {Pos, Len}=PosLen}, + ?LET( + B, + binary(), + {B, gen_part_invalid(B)} + ), + expect_error(fun binary:bin_to_list/2, [Bin, PosLen]) andalso + expect_error(fun binary:bin_to_list/3, [Bin, Pos, Len]) + ). + +%% --- compile_pattern/1 ---------------------------------------------- +prop_compile_pattern() -> + ?FORALL( + P, + gen_patterns(), + is_tuple(binary:compile_pattern(P)) + ). + +prop_compile_pattern_invalid() -> + ?FORALL( + P, + oneof([ + <<>>, + [], + ?LET( + L, + non_empty(list(binary())), + case lists:any(fun(P) -> P =:= <<>> end, L) of + true -> + L; + false -> + [<<>> | L] + end) + ]), + expect_error(fun binary:compile_pattern/1, [P]) + ). + +%% --- copy/1,2 ------------------------------------------------------- +prop_copy() -> + ?FORALL( + {Bin, N}, + {binary(), ?SUCHTHAT(N, non_neg_integer(), N =/= 1)}, + begin + Copy1 = binary:copy(Bin), + Copy2 = binary:copy(Bin, 1), + Bin =:= Copy1 andalso + not erts_debug:same(Bin, Copy1) andalso + Bin =:= Copy2 andalso + not erts_debug:same(Bin, Copy2) andalso + << <<Bin/binary>> || _ <- lists:seq(1, N) >> =:= binary:copy(Bin, N) + end + ). + +prop_copy_2_invalid() -> + ?FORALL( + {Bin, N}, + {binary(), neg_integer()}, + expect_error(fun binary:copy/2, [Bin, N]) + ). + +%% --- decode_hex/1 --------------------------------------------------- +prop_decode_hex() -> + ?FORALL( + {BR, BE}, + ?LET( + L, + list({oneof([lower, upper]), range(16#0, 16#f)}), + lists:foldl( + fun + ({_, Nib}, {AccR, AccE}) when Nib < 10 -> + {<<AccR/bitstring, Nib:4>>, <<AccE/binary, ($0 + Nib)>>}; + ({lower, Nib}, {AccR, AccE}) -> + {<<AccR/bitstring, Nib:4>>, <<AccE/binary, ($a + Nib - 10)>>}; + ({upper, Nib}, {AccR, AccE}) -> + {<<AccR/bitstring, Nib:4>>, <<AccE/binary, ($A + Nib - 10)>>} + end, + {<<>>, <<>>}, + case length(L) rem 2 of + 0 -> L; + 1 -> tl(L) + end + ) + ), + BR =:= binary:decode_hex(BE) + ). + +prop_decode_hex_invalid() -> + ?FORALL( + Bin, + ?SUCHTHAT(B, binary(), not is_hex_bin(B)), + expect_error(fun binary:decode_hex/1, [Bin]) + ). + +%% --- decode_unsigned/1,2 -------------------------------------------- +prop_decode_unsigned() -> + ?FORALL( + Bin, + binary(), + begin + Size = bit_size(Bin), + <<Big:Size/integer-unsigned-big>> = Bin, + <<Little:Size/integer-unsigned-little>> = Bin, + Big =:= binary:decode_unsigned(Bin) andalso + Big =:= binary:decode_unsigned(Bin, big) andalso + Little =:= binary:decode_unsigned(Bin, little) + end + ). + +%% --- encode_hex/1,2 ------------------------------------------------- +prop_encode_hex() -> + ?FORALL( + Bin, + binary(), + begin + LowerHex = binary:encode_hex(Bin, lowercase), + UpperHex = binary:encode_hex(Bin, uppercase), + UpperHex =:= binary:encode_hex(Bin) andalso + Bin =:= binary:decode_hex(LowerHex) andalso + Bin =:= binary:decode_hex(UpperHex) andalso + check_hex_encoded(Bin, UpperHex, LowerHex) + end + ). + +%% --- encode_unsigned/1,2 -------------------------------------------- +prop_encode_unsigned() -> + ?FORALL( + I, + non_neg_integer(), + begin + Size = max(8, int_bitsize(I)), + Big = <<I:Size/integer-unsigned-big>>, + Little = <<I:Size/integer-unsigned-little>>, + Big =:= binary:encode_unsigned(I) andalso + Big =:= binary:encode_unsigned(I, big) andalso + Little =:= binary:encode_unsigned(I, little) + end + ). + +prop_encode_unsigned_invalid() -> + ?FORALL( + I, + neg_integer(), + expect_error(fun binary:encode_unsigned/1, [I]) andalso + expect_error(fun binary:encode_unsigned/2, [I, big]) andalso + expect_error(fun binary:encode_unsigned/2, [I, little]) + ). + +%% --- first/1 -------------------------------------------------------- +prop_first() -> + ?FORALL( + {Bin, Byte}, + ?LET( + {B, Bt}, + {binary(), byte()}, + {<<Bt, B/binary>>, Bt} + ), + Byte =:= binary:first(Bin) + ). + +%% --- last/1 --------------------------------------------------------- +prop_last() -> + ?FORALL( + {Bin, Byte}, + ?LET( + {B, Bt}, + {binary(), byte()}, + {<<B/binary, Bt>>, Bt} + ), + Byte =:= binary:last(Bin) + ). + +%% --- list_to_bin/1 -------------------------------------------------- +prop_list_to_bin() -> + ?FORALL( + {List, Bin}, + ?LET( + B, + binary(), + {[Bt || <<Bt>> <= B], B} + ), + Bin =:= binary:list_to_bin(List) + ). + +prop_list_to_bin_invalid() -> + ?FORALL( + List, + ?SUCHTHAT( + L, + non_empty(list(integer())), + lists:any(fun(I) -> I < 16#00 orelse I > 16#ff end, L) + ), + expect_error(fun binary:list_to_bin/1, [List]) + ). + +%% --- longest_common_prefix/1 ---------------------------------------- +prop_longest_common_prefix() -> + ?FORALL( + Bins, + ?LET( + {C, Bs}, + {binary(), non_empty(list(binary()))}, + [<<C/binary, B/binary>> || B <- Bs] + ), + find_longest_common_prefix(Bins) =:= binary:longest_common_prefix(Bins) + ). + +%% --- longest_common_suffix/1 ---------------------------------------- +prop_longest_common_suffix() -> + ?FORALL( + Bins, + ?LET( + {C, Bs}, + {binary(), non_empty(list(binary()))}, + [<<B/binary, C/binary>> || B <- Bs] + ), + find_longest_common_suffix(Bins) =:= binary:longest_common_suffix(Bins) + ). + +%% --- match/2 -------------------------------------------------------- +prop_match_2() -> + ?FORALL( + {Bin, Pattern}, + ?LET( + B, + binary(), + {B, gen_patterns(B)} + ), + begin + Match = binary:match(Bin, Pattern), + Match =:= binary:match(Bin, binary:compile_pattern(Pattern)) andalso + Match =:= find_match(Bin, Pattern) + end + ). + +%% --- match/3 -------------------------------------------------------- +prop_match_3() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(B), gen_opts([], [gen_scope_opt(B)])} + ), + begin + Match = binary:match(Bin, Pattern, Opts), + Match =:= binary:match(Bin, binary:compile_pattern(Pattern), Opts) andalso + Match =:= find_match(Bin, Pattern, Opts) + end + ). + +prop_match_3_invalid() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(), gen_opts([gen_scope_invalid_opt(B)], [])} + ), + expect_error(fun binary:match/3, [Bin, Pattern, Opts]) andalso + expect_error(fun binary:match/3, [Bin, binary:compile_pattern(Pattern), Opts]) + ). + +%% --- matches/2 ------------------------------------------------------ +prop_matches_2() -> + ?FORALL( + {Bin, Pattern}, + ?LET( + B, + binary(), + {B, gen_patterns(B)} + ), + begin + Match = lists:sort(binary:matches(Bin, Pattern)), + Match =:= lists:sort(binary:matches(Bin, binary:compile_pattern(Pattern))) andalso + Match =:= lists:sort(find_matches(Bin, Pattern)) + end + ). + +%% --- matches/3 ------------------------------------------------------ +prop_matches_3() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(B), gen_opts([], [gen_scope_opt(B)])} + ), + begin + Match = lists:sort(binary:matches(Bin, Pattern, Opts)), + Match =:= lists:sort(binary:matches(Bin, binary:compile_pattern(Pattern), Opts)) andalso + Match =:= lists:sort(find_matches(Bin, Pattern, Opts)) + end + ). + +prop_matches_3_invalid() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(), gen_opts([gen_scope_invalid_opt(B)], [])} + ), + expect_error(fun binary:matches/3, [Bin, Pattern, Opts]) andalso + expect_error(fun binary:matches/3, [Bin, binary:compile_pattern(Pattern), Opts]) + ). + +%% --- part/2,3 ------------------------------------------------------- +prop_part() -> + ?FORALL( + {Bin, {Pos, Len}=PosLen}, + ?LET( + B, + binary(), + {B, gen_part(B)} + ), + begin + {_, Part, _} = part_split(Bin, PosLen), + Part =:= binary:part(Bin, PosLen) andalso + Part =:= binary:part(Bin, Pos, Len) andalso + Part =:= erlang:binary_part(Bin, PosLen) andalso + Part =:= erlang:binary_part(Bin, Pos, Len) + end + ). + +prop_part_invalid() -> + ?FORALL( + {Bin, {Pos, Len}=PosLen}, + ?LET( + B, + binary(), + {B, gen_part_invalid(B)} + ), + expect_error(fun binary:part/2, [Bin, PosLen]) andalso + expect_error(fun binary:part/3, [Bin, Pos, Len]) + ). + +%% --- replace/3 ------------------------------------------------------ +prop_replace_3() -> + ?FORALL( + {Bin, Pattern, Replacement}, + ?LET( + {B, R}, + {binary(), oneof([binary(), function1(binary())])}, + {B, gen_patterns(B), R} + ), + begin + Replaced = binary:replace(Bin, Pattern, Replacement), + Replaced =:= binary:replace(Bin, binary:compile_pattern(Pattern), Replacement) andalso + Replaced =:= do_replace(Bin, Pattern, Replacement) + end + ). + +%% --- replace/4 ------------------------------------------------------ +prop_replace_4() -> + ?FORALL( + {Bin, Pattern, Replacement, Opts}, + ?LET( + {B, R}, + {binary(), oneof([binary(), function1(binary())])}, + {B, gen_patterns(B), R, gen_opts([], [global, gen_scope_opt(B), gen_insert_replaced_opt(R)])} + ), + begin + Replaced = binary:replace(Bin, Pattern, Replacement, Opts), + Replaced =:= binary:replace(Bin, binary:compile_pattern(Pattern), Replacement, Opts) andalso + Replaced =:= do_replace(Bin, Pattern, Replacement, Opts) + end + ). + +prop_replace_4_invalid1() -> + ?FORALL( + {Bin, Pattern, Replacement, Opts}, + ?LET( + {B, R}, + {binary(), oneof([binary(), function1(binary())])}, + {B, gen_patterns(B), R, gen_opts([gen_scope_invalid_opt(B)], [global, gen_insert_replaced_opt(R)])} + ), + expect_error(fun binary:replace/4, [Bin, Pattern, Replacement, Opts]) andalso + expect_error(fun binary:replace/4, [Bin, binary:compile_pattern(Pattern), Replacement, Opts]) + ). + +prop_replace_4_invalid2() -> + ?FORALL( + {Bin, Pattern, Replacement, Opts}, + ?LET( + {B, R}, + {binary(), binary()}, + {B, gen_patterns(B), R, gen_opts([gen_insert_replaced_invalid_opt(R)], [global, gen_scope_opt(B)])} + ), + expect_error(fun binary:replace/4, [Bin, Pattern, Replacement, Opts]) andalso + expect_error(fun binary:replace/4, [Bin, binary:compile_pattern(Pattern), Replacement, Opts]) + ). + +%% --- split/2 -------------------------------------------------------- +prop_split_2() -> + ?FORALL( + {Bin, Pattern}, + ?LET( + B, + binary(), + {B, gen_patterns(B)} + ), + begin + Split = binary:split(Bin, Pattern), + Split =:= binary:split(Bin, binary:compile_pattern(Pattern)) andalso + Split =:= do_split(Bin, Pattern) + end + ). + +%% --- split/3 -------------------------------------------------------- +prop_split_3() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(B), gen_opts([], [global, trim, trim_all, gen_scope_opt(B)])} + ), + begin + Split = binary:split(Bin, Pattern, Opts), + Split =:= binary:split(Bin, binary:compile_pattern(Pattern), Opts) andalso + Split =:= do_split(Bin, Pattern, Opts) + end + ). + +prop_split_3_invalid() -> + ?FORALL( + {Bin, Pattern, Opts}, + ?LET( + B, + binary(), + {B, gen_patterns(B), gen_opts([gen_scope_invalid_opt(B)], [global, trim, trim_all])} + ), + expect_error(fun binary:split/3, [Bin, Pattern, Opts]) andalso + expect_error(fun binary:split/3, [Bin, binary:compile_pattern(Pattern), Opts]) + ). + + +%%%%%%%%%%%%%%%%%% +%%% Generators %%% +%%%%%%%%%%%%%%%%%% -prop_hex_encode_2() -> - ?FORALL(Data, data(), +%% Generator for lists of options, in random order. +%% The options given in RequiredOpts will be present. +%% The options given in MaybeOpts may or may not be present. +gen_opts(RequiredOpts, MaybeOpts) -> + ?LET( + {UseOpts, Shuffles}, + {vector(length(MaybeOpts), boolean()), vector(length(RequiredOpts) + length(MaybeOpts), integer())}, begin - UpperHex = binary:encode_hex(Data, uppercase), - LowerHex = binary:encode_hex(Data, lowercase), - binary:decode_hex(LowerHex) =:= Data andalso - binary:decode_hex(UpperHex) =:= Data andalso - check_hex_encoded(Data, UpperHex, LowerHex) - end). + UsedMaybeOpts = [Opt || {true, Opt} <- lists:zip(UseOpts, MaybeOpts)], + UsedOpts = RequiredOpts ++ UsedMaybeOpts, + ShuffledOpts = lists:sort(lists:zip(Shuffles, UsedOpts, trim)), + [Opt || {_, Opt} <- ShuffledOpts] + end + ). -data() -> - ?SIZED(Size, resize(Size * 2, binary())). +%% Generator for scope options, valid for the binary given in Bin +gen_scope_opt(Bin) -> + {scope, gen_part(Bin)}. +%% Generator for scope options, invalid for the binary given in Bin +gen_scope_invalid_opt(Bin) -> + {scope, gen_part_invalid(Bin)}. + +%% Generator for a part range, valid for the binary given in Bin +gen_part(Bin) -> + Size = byte_size(Bin), + ?LET( + S, + range(0, Size), + {S, range(-S, Size-S)} + ). + +%% Generator for a part range, invalid for the binary given in Bin +gen_part_invalid(Bin) -> + Size = byte_size(Bin), + ?SUCHTHAT( + {S, L}, + {integer(), integer()}, + S < 0 orelse S > Size orelse S + L < 0 orelse S + L > Size + ). + +%% Generator for an index within the binary given in Bin +gen_index(Bin) -> + range(0, byte_size(Bin)). + +%% Generator for an index outside the binary given in Bin +gen_index_invalid(Bin) -> + oneof([neg_integer(), range(byte_size(Bin) + 1, inf)]). + +%% Generator for insert_replaced options, valid for the binary given in Replacement +gen_insert_replaced_opt(Replacement) when is_binary(Replacement) -> + {insert_replaced, oneof([gen_index(Replacement), list(gen_index(Replacement))])}; +gen_insert_replaced_opt(_Replacement) -> + {insert_replaced, oneof([non_neg_integer(), list(non_neg_integer())])}. + +%% Generator for insert_replaced options, invalid for the binary given in Replacement +gen_insert_replaced_invalid_opt(Replacement) when is_binary(Replacement) -> + {insert_replaced, oneof([gen_index_invalid(Replacement), non_empty(list(gen_index_invalid(Replacement)))])}; +gen_insert_replaced_invalid_opt(_Replacement) -> + {insert_replaced, oneof([neg_integer(), non_empty(list(neg_integer()))])}. + +%% Generator for patterns, that is a single or a list of pattern binaries, +%% which may or may not be present in the optionally binary given in Bin. +gen_patterns() -> + oneof([gen_pattern(), non_empty(list(gen_pattern()))]). + +gen_patterns(Bin) -> + oneof([gen_pattern(Bin), non_empty(list(gen_pattern(Bin)))]). + +%% Generator for a single pattern. +%% The pattern may or may not be present in the optionally binary given in Bin. +gen_pattern() -> + non_empty(binary()). + +gen_pattern(<<>>) -> + non_empty(binary()); +gen_pattern(Bin) -> + oneof([non_empty(binary()), + ?LET( + S, + range(0, byte_size(Bin) - 1), + ?LET( + L, + range(1, byte_size(Bin) - S), + begin + <<_:S/binary, P:L/binary, _/binary>> = Bin, + P + end + ) + )]). + + +%%%%%%%%%%%%%%% +%%% Helpers %%% +%%%%%%%%%%%%%%% + +%% Determine the number of bits required to store the given integer in full bytes +int_bitsize(0) -> + 0; +int_bitsize(I) -> + 8 + int_bitsize(I div 16#100). + +%% Extract an indicator for whether or not to perform a global search from the given options list +opts_to_how(Opts) -> + case lists:member(global, Opts) of + true -> + all; + false -> + first + end. + +%% Divide the given binary in the parts before, in, and after the scope specified in the given options list +opts_to_scope_parts(Bin, Opts) -> + case lists:keyfind(scope, 1, Opts) of + false -> + {<<>>, Bin, <<>>}; + {scope, PosLen} -> + part_split(Bin, PosLen) + end. + +%% Decide the trimming mode from the trim or trim_all options specified in the given options list +opts_to_trim(Opts) -> + case {lists:member(trim_all, Opts), lists:member(trim, Opts)} of + {true, _} -> + all; + {_, true} -> + rear; + _ -> + none + end. + +%% Process a replacement based on the insert_replaced options specified in the given options list +opts_to_replacement(Replacement, _Opts) when is_function(Replacement) -> + Replacement; +opts_to_replacement(Replacement, Opts) -> + case lists:keyfind(insert_replaced, 1, Opts) of + false -> + Replacement; + {insert_replaced, []} -> + Replacement; + {insert_replaced, [_|_] = Positions} -> + split_replacement(Positions, Replacement); + {insert_replaced, Position} -> + <<Front:Position/binary, Rear/binary>> = Replacement, + [Front, Rear] + end. + +split_replacement(Inserts, Bin) -> + split_replacement1(Bin, lists:sort(Inserts), 0, []). + +split_replacement1(Bin, [], L, Acc) -> + lists:reverse([extract_part(Bin, L, byte_size(Bin))|Acc]); +split_replacement1(Bin, [P|Ps], L, Acc) -> + split_replacement1(Bin, Ps, P, [extract_part(Bin, L, P)|Acc]). + +extract_part(Bin, S, E) -> + <<_:S/binary, P:(E-S)/binary, _/binary>> = Bin, + P. + +join_replacement(_Join, []) -> + <<>>; +join_replacement(_Join, [P]) -> + P; +join_replacement(Join, [P|Ps]) -> + <<P/binary, Join/binary, (join_replacement(Join, Ps))/binary>>. + +replace_match(Replacement, Match) when is_function(Replacement) -> + Replacement(Match); +replace_match(Replacement, Match) when is_list(Replacement) -> + join_replacement(Match, Replacement); +replace_match(Replacement, _Match) -> + Replacement. + +%% Split the given binary in the parts before, within, and after the scope +%% defined by Pos and Len +part_split(Bin, {Pos, Len}) when Len >= 0 -> + <<Front:Pos/binary, Middle:Len/binary, Rear/binary>> = Bin, + {Front, Middle, Rear}; +part_split(Bin, {Pos, Len}) -> + <<Front:(Pos + Len)/binary, Middle:(-Len)/binary, Rear/binary>> = Bin, + {Front, Middle, Rear}. + +%% Sort the given list of patterns by descending size. +%% If a single pattern is given, wraps it in a list. +sort_patterns(Patterns) when is_list(Patterns) -> + lists:sort(fun(P1, P2) -> byte_size(P1) >= byte_size(P2) end, Patterns); +sort_patterns(Pattern) -> + [Pattern]. + +%% Control implementation for longest_common_prefix/1 +find_longest_common_prefix([B|_]=Bs) -> + find_longest_common_prefix1(Bs, byte_size(B)). + +find_longest_common_prefix1(_Bs, 0) -> + 0; +find_longest_common_prefix1([_B], N) -> + N; +find_longest_common_prefix1([B1|[B2|_]=More], N) -> + find_longest_common_prefix1(More, min(N, find_longest_common_prefix2(B1, B2, 0))). + +find_longest_common_prefix2(<<C, B1/binary>>, <<C, B2/binary>>, N) -> + find_longest_common_prefix2(B1, B2, N + 1); +find_longest_common_prefix2(_B1, _B2, N) -> + N. + +%% Control implementation for longest_common_suffix/1 +find_longest_common_suffix([B|_]=Bs) -> + find_longest_common_suffix1(Bs, byte_size(B)). + +find_longest_common_suffix1(_Bs, 0) -> + 0; +find_longest_common_suffix1([_B], N) -> + N; +find_longest_common_suffix1([B1|[B2|_]=More], N) -> + S1 = byte_size(B1), + S2 = byte_size(B2), + <<_:(max(0, S1 - S2))/bytes, B1_1/binary>> = B1, + <<_:(max(0, S2 - S1))/bytes, B2_1/binary>> = B2, + find_longest_common_suffix1(More, min(N, find_longest_common_suffix2(B1_1, B2_1, 0))). + +find_longest_common_suffix2(<<>>, <<>>, N) -> + N; +find_longest_common_suffix2(<<C, B1/binary>>, <<C, B2/binary>>, N) -> + find_longest_common_suffix2(B1, B2, N + 1); +find_longest_common_suffix2(<<_, B1/binary>>, <<_, B2/binary>>, _N) -> + find_longest_common_suffix2(B1, B2, 0). + +%% Control implementation for match/2 +find_match(Bin, Patterns) -> + find_match(Bin, Patterns, []). + +%% Control implementation for match/3 +find_match(Bin, Patterns, Opts) -> + case find_matches(Bin, Patterns, Opts) of + [] -> + nomatch; + [Match|_] -> + Match + end. + +%% Control implementation for matches/2 +find_matches(Bin, Patterns) -> + find_matches(Bin, Patterns, []). + +%% Control implementation for matches/3 +find_matches(Bin, Patterns, Opts) -> + {Front, SearchBin, _} = opts_to_scope_parts(Bin, Opts), + SortedPatterns = sort_patterns(Patterns), + Matches = find_matches1(SearchBin, SortedPatterns, SortedPatterns, 0, []), + Offset = byte_size(Front), + [{Pos + Offset, Len} || {Pos, Len} <- Matches]. + +find_matches1(<<>>, _Patterns, _AllPatterns, _Pos, Acc) -> + lists:reverse(Acc); +find_matches1(<<_, Bin/binary>>, [], AllPatterns, Pos, Acc) -> + find_matches1(Bin, AllPatterns, AllPatterns, Pos + 1, Acc); +find_matches1(Bin, [Pattern|Patterns], AllPatterns, Pos, Acc) -> + case Bin of + <<Pattern:(byte_size(Pattern))/binary, Bin1/binary>> -> + find_matches1(Bin1, AllPatterns, AllPatterns, Pos + byte_size(Pattern), [{Pos, byte_size(Pattern)}|Acc]); + _ -> + find_matches1(Bin, Patterns, AllPatterns, Pos, Acc) + end. + +%% Control implementation for replace/3 +do_replace(Bin, Patterns, Replacement) -> + do_replace(Bin, Patterns, Replacement, []). + +%% Control implementation for replace/4 +do_replace(Bin, Patterns, Replacement, Opts) -> + How = opts_to_how(Opts), + {Front, ReplaceBin, Rear} = opts_to_scope_parts(Bin, Opts), + Replacement1 = opts_to_replacement(Replacement, Opts), + SortedPatterns = sort_patterns(Patterns), + Replaced = do_replace1(How, ReplaceBin, SortedPatterns, SortedPatterns, Replacement1, <<>>), + <<Front/binary, Replaced/binary, Rear/binary>>. + +do_replace1(_How, <<>>, _Patterns, _AllPatterns, _Replacement, Acc) -> + Acc; +do_replace1(How, <<C, Bin/binary>>, [], AllPatterns, Replacement, Acc) -> + do_replace1(How, Bin, AllPatterns, AllPatterns, Replacement, <<Acc/binary, C>>); +do_replace1(How, Bin, [Pattern|Patterns], AllPatterns, Replacement, Acc) -> + case Bin of + <<Pattern:(byte_size(Pattern))/binary, Bin1/binary>> -> + case How of + first -> + <<Acc/binary, (replace_match(Replacement, Pattern))/binary, Bin1/binary>>; + all -> + do_replace1(How, Bin1, AllPatterns, AllPatterns, Replacement, <<Acc/binary, (replace_match(Replacement, Pattern))/binary>>) + end; + _ -> + do_replace1(How, Bin, Patterns, AllPatterns, Replacement, Acc) + end. + +%% Control implementation for split/2 +do_split(Bin, Patterns) -> + do_split(Bin, Patterns, []). + +%% Control implementation for split/3 +do_split(Bin, Patterns, Opts) -> + How = opts_to_how(Opts), + {Front, SplitBin, Rear} = opts_to_scope_parts(Bin, Opts), + Trim = opts_to_trim(Opts), + SortedPatterns = sort_patterns(Patterns), + Splitted0 = do_split1(How, SplitBin, SortedPatterns, SortedPatterns, <<>>, []), + Splitted1 = [<<Front/binary, (hd(Splitted0))/binary>> | tl(Splitted0)], + Splitted2 = lists:droplast(Splitted1) ++ [<<(lists:last(Splitted1))/binary, Rear/binary>>], + case Trim of + none -> + Splitted2; + all -> + lists:filter(fun(Part) -> Part =/= <<>> end, Splitted2); + rear -> + lists:reverse(lists:dropwhile(fun(Part) -> Part =:= <<>> end, lists:reverse(Splitted2))) + end. + +do_split1(_How, <<>>, _Patterns, _AllPatterns, AccB, AccL) -> + lists:reverse([AccB | AccL]); +do_split1(How, <<C, Bin/binary>>, [], AllPatterns, AccB, AccL) -> + do_split1(How, Bin, AllPatterns, AllPatterns, <<AccB/binary, C>>, AccL); +do_split1(How, Bin, [Pattern|Patterns], AllPatterns, AccB, AccL) -> + case Bin of + <<Pattern:(byte_size(Pattern))/binary, Bin1/binary>> -> + case How of + first -> + [AccB, Bin1]; + all -> + do_split1(How, Bin1, AllPatterns, AllPatterns, <<>>, [AccB | AccL]) + end; + _ -> + do_split1(How, Bin, Patterns, AllPatterns, AccB, AccL) + end. %% @doc Credit to the <a href="https://github.com/erlang/otp/pull/6297#discussion_r1034771450">comment</a> of @Maria-12648430 --spec check_hex_encoded(Data :: binary(), UpperHex :: binary(), LoweHex :: binary()) -> boolean(). +-spec check_hex_encoded(Data :: binary(), UpperHex :: binary(), LowerHex :: binary()) -> boolean(). check_hex_encoded(<<I1:4, I2:4, Ins/binary>>, <<U1:8, U2:8, UCs/binary>>, <<L1:8, L2:8, LCs/binary>>) -> check_hex_chars_match(I1, U1, L1) andalso check_hex_chars_match(I2, U2, L2) andalso @@ -34,4 +863,41 @@ check_hex_encoded(_, _, _) -> check_hex_chars_match(X, U, L) when X < 10 -> (U =:= $0 + X) andalso (L =:= $0 + X); check_hex_chars_match(X, U, L) -> - (U =:= $A + X -10) andalso (L =:= $a + X -10). + (U =:= $A + X - 10) andalso (L =:= $a + X - 10). + +%% Determine whether the given binary consist of an even number of +%% bytes, with each byte in one of the ranges of $0..$9, $a..$f, $A..$F +is_hex_bin(<<>>) -> + true; +is_hex_bin(<<C1, C2, Bin/binary>>) -> + is_hex_char(C1) andalso + is_hex_char(C2) andalso + is_hex_bin(Bin); +is_hex_bin(_) -> + false. + +%% Check if the given byte is a hex digit +is_hex_char(C) when C >= $0, C =< $9 -> + true; +is_hex_char(C) when C >= $a, C =< $f -> + true; +is_hex_char(C) when C >= $A, C =< $F -> + true; +is_hex_char(_) -> + false. + +%% Returns whether an error exception was raised when the +%% given function is applied to the given arguments +expect_error(Fun, Args) when is_function(Fun, length(Args)) -> + try + erlang:apply(Fun, Args) + of + _ -> + false + catch + error:_ -> + true; + _:_ -> + false + end. + -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor