%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2018. 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_SUITE). %% Tests binaries and the BIFs: %% list_to_binary/1 %% iolist_to_binary/1 %% list_to_bitstring/1 %% binary_to_list/1 %% binary_to_list/3 %% binary_to_term/1 %% binary_to_term/2 %% bitstring_to_list/1 %% term_to_binary/1 %% erlang:external_size/1 %% size(Binary) %% iolist_size/1 %% split_binary/2 %% hash(Binary, N) %% phash(Binary, N) %% phash2(Binary, N) %% -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, copy_terms/1, conversions/1, deep_lists/1, deep_bitstr_lists/1, bad_list_to_binary/1, bad_binary_to_list/1, t_split_binary/1, bad_split/1, terms/1, terms_float/1, float_middle_endian/1, b2t_used_big/1, external_size/1, t_iolist_size/1, t_iolist_size_huge_list/1, t_iolist_size_huge_bad_arg_list/1, t_iolist_size_shallow_trapping/1, t_iolist_size_shallow_short_lists/1, t_iolist_size_shallow_tiny_lists/1, t_iolist_size_deep_trapping/1, t_iolist_size_deep_short_lists/1, t_iolist_size_deep_tiny_lists/1, t_hash/1, bad_size/1, bad_term_to_binary/1, bad_binary_to_term_2/1,safe_binary_to_term2/1, bad_binary_to_term/1, bad_terms/1, more_bad_terms/1, otp_5484/1,otp_5933/1, ordering/1,unaligned_order/1,gc_test/1, bit_sized_binary_sizes/1, otp_6817/1,deep/1, term2bin_tuple_fallbacks/1, robustness/1,otp_8117/1, otp_8180/1, trapping/1, large/1, error_after_yield/1, cmp_old_impl/1, t2b_system_limit/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,4}}]. all() -> [copy_terms, conversions, deep_lists, deep_bitstr_lists, t_split_binary, bad_split, bad_list_to_binary, bad_binary_to_list, terms, terms_float, float_middle_endian, external_size, t_iolist_size, t_iolist_size_huge_list, t_iolist_size_huge_bad_arg_list, {group, iolist_size_benchmarks}, b2t_used_big, bad_binary_to_term_2, safe_binary_to_term2, bad_binary_to_term, bad_terms, t_hash, bad_size, bad_term_to_binary, t2b_system_limit, more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, term2bin_tuple_fallbacks, robustness, otp_8180, trapping, large, error_after_yield, cmp_old_impl]. groups() -> [ { iolist_size_benchmarks, [], [t_iolist_size_shallow_trapping, t_iolist_size_shallow_short_lists, t_iolist_size_shallow_tiny_lists, t_iolist_size_deep_trapping, t_iolist_size_deep_short_lists, t_iolist_size_deep_tiny_lists ] } ]. init_per_suite(Config) -> A0 = case application:start(sasl) of ok -> [sasl]; _ -> [] end, A = case application:start(os_mon) of ok -> [os_mon|A0]; _ -> A0 end, [{started_apps, A}|Config]. end_per_suite(Config) -> As = proplists:get_value(started_apps, Config), lists:foreach(fun (A) -> application:stop(A) end, As), Config. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Config. end_per_testcase(_Func, _Config) -> ok. -define(heap_binary_size, 64). copy_terms(Config) when is_list(Config) -> Self = self(), Pid = spawn_link(fun() -> copy_server(Self) end), F = fun(Term) -> Pid ! Term, receive Term -> ok; Other -> io:format("Sent: ~P\nGot back:~P", [Term,12,Other,12]), ct:fail(bad_term) end end, test_terms(F), ok. copy_server(Parent) -> receive Term -> Parent ! Term, copy_server(Parent) end. %% Tests list_to_binary/1, binary_to_list/1 and size/1, %% using flat lists. conversions(Config) when is_list(Config) -> test_bin([]), test_bin([1]), test_bin([1, 2]), test_bin([1, 2, 3]), test_bin(lists:seq(0, ?heap_binary_size)), test_bin(lists:seq(0, ?heap_binary_size+1)), test_bin(lists:seq(0, 255)), test_bin(lists:duplicate(50000, $@)), %% Binary in list. List = [1,2,3,4,5], B1 = make_sub_binary(list_to_binary(List)), 5 = size(B1), 5 = size(make_unaligned_sub_binary(B1)), 40 = bit_size(B1), 40 = bit_size(make_unaligned_sub_binary(B1)), B2 = list_to_binary([42,B1,19]), B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]), B2 = iolist_to_binary(B2), B2 = iolist_to_binary(make_unaligned_sub_binary(B2)), 7 = size(B2), 7 = size(make_sub_binary(B2)), 56 = bit_size(B2), 56 = bit_size(make_sub_binary(B2)), [42,1,2,3,4,5,19] = binary_to_list(B2), [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)), [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)), [42,1,2,3,4,5,19] = bitstring_to_list(B2), [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)), [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)), ok. test_bin(List) -> Size = length(List), Bin = list_to_binary(List), Bin = iolist_to_binary(List), Bin = list_to_bitstring(List), Size = iolist_size(List), Size = iolist_size(Bin), Size = iolist_size(make_unaligned_sub_binary(Bin)), Size = size(Bin), Size = size(make_sub_binary(Bin)), Size = size(make_unaligned_sub_binary(Bin)), List = binary_to_list(Bin), List = binary_to_list(make_sub_binary(Bin)), List = binary_to_list(make_unaligned_sub_binary(Bin)), List = bitstring_to_list(Bin), List = bitstring_to_list(make_unaligned_sub_binary(Bin)). %% Tests list_to_binary/1, iolist_to_binary/1, list_to_bitstr/1, binary_to_list/1,3, %% bitstr_to_list/1, and size/1, using deep lists. deep_lists(Config) when is_list(Config) -> test_deep_list(["abc"]), test_deep_list([[12,13,[123,15]]]), test_deep_list([[12,13,[lists:seq(0, 255), []]]]), ok. test_deep_list(List) -> FlatList = lists:flatten(List), Size = length(FlatList), Bin = list_to_binary(List), Bin = iolist_to_binary(List), Bin = iolist_to_binary(Bin), Bin = list_to_bitstring(List), Size = size(Bin), Size = iolist_size(List), Size = iolist_size(FlatList), Size = iolist_size(Bin), Bitsize = bit_size(Bin), Bitsize = 8*Size, FlatList = binary_to_list(Bin), FlatList = bitstring_to_list(Bin), io:format("testing plain binary..."), t_binary_to_list_3(FlatList, Bin, 1, Size), io:format("testing unaligned sub binary..."), t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size). t_binary_to_list_3(List, Bin, From, To) -> going_up(List, Bin, From, To), going_down(List, Bin, From, To), going_center(List, Bin, From, To). going_up(List, Bin, From, To) when From =< To -> List = binary_to_list(Bin, From, To), going_up(tl(List), Bin, From+1, To); going_up(_List, _Bin, From, To) when From > To -> ok. going_down(List, Bin, From, To) when To > 0-> compare(List, binary_to_list(Bin, From, To), To-From+1), going_down(List, Bin, From, To-1); going_down(_List, _Bin, _From, _To) -> ok. going_center(List, Bin, From, To) when From >= To -> compare(List, binary_to_list(Bin, From, To), To-From+1), going_center(tl(List), Bin, From+1, To-1); going_center(_List, _Bin, _From, _To) -> ok. compare([X|Rest1], [X|Rest2], Left) when Left > 0 -> compare(Rest1, Rest2, Left-1); compare([_X|_], [_Y|_], _Left) -> ct:fail("compare fail"); compare(_List, [], 0) -> ok. deep_bitstr_lists(Config) when is_list(Config) -> {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]), {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]), {Bin,List} = test_deep_bitstr([42|<<5:3>>]), {Bin,List} = test_deep_bitstr([<<42,5:3>>]), {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]), {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]), {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]), {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]), {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>], <<1:1>>,<<0:1>>,<<1:1>>]), ok. test_deep_bitstr(List) -> %%{'EXIT',{badarg,_}} = list_to_binary(List), Bin = list_to_bitstring(List), {Bin,bitstring_to_list(Bin)}. bad_list_to_binary(Config) when is_list(Config) -> test_bad_bin(<<1:1>>), test_bad_bin(atom), test_bad_bin(42), test_bad_bin([1|2]), test_bad_bin([256]), test_bad_bin([255, [256]]), test_bad_bin([-1]), test_bad_bin([atom_in_list]), test_bad_bin([[<<8>>]|bad_tail]), {'EXIT',{badarg,_}} = (catch list_to_binary(id(<<1,2,3>>))), {'EXIT',{badarg,_}} = (catch list_to_binary(id([<<42:7>>]))), {'EXIT',{badarg,_}} = (catch list_to_bitstring(id(<<1,2,3>>))), %% Funs used to be implemented as a type of binary internally. test_bad_bin(fun(X, Y) -> X*Y end), test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), test_bad_bin([fun(X) -> X + 1 end]), %% Test iolists that do not fit in the address space. %% Unfortunately, it would be too slow to test in a 64-bit emulator. case erlang:system_info(wordsize) of 4 -> huge_iolists(); _ -> ok end. huge_iolists() -> FourGigs = 1 bsl 32, Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ [1 bsl N || N <- lists:seq(33, 37)], Base = <<0:(1 bsl 20)/unit:8>>, [begin L = build_iolist(Sz, Base), {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])), {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) end || Sz <- Sizes], ok. test_bad_bin(List) -> {'EXIT',{badarg,_}} = (catch list_to_binary(List)), {'EXIT',{badarg,_}} = (catch iolist_to_binary(List)), {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)), {'EXIT',{badarg,_}} = (catch iolist_size(List)). %% Tries binary_to_list/1,3 with bad arguments. bad_binary_to_list(Config) when is_list(Config) -> bad_bin_to_list(fun(X) -> X * 42 end), GoodBin = list_to_binary(lists:seq(1, 10)), bad_bin_to_list(fun(X) -> X * 44 end, 1, 2), bad_bin_to_list(GoodBin, 0, 1), bad_bin_to_list(GoodBin, 2, 1), bad_bin_to_list(GoodBin, 11, 11), {'EXIT',{badarg,_}} = (catch binary_to_list(id(<<42:7>>))), ok. bad_bin_to_list(BadBin) -> {'EXIT',{badarg,_}} = (catch binary_to_list(BadBin)), {'EXIT',{badarg,_}} = (catch bitstring_to_list(BadBin)). bad_bin_to_list(Bin, First, Last) -> {'EXIT',{badarg,_}} = (catch binary_to_list(Bin, First, Last)). %% Tries to split a binary at all possible positions. t_split_binary(Config) when is_list(Config) -> L = lists:seq(0, ?heap_binary_size-5), %Heap binary. B = list_to_binary(L), split(L, B, size(B)), %% Sub binary of heap binary. split(L, make_sub_binary(B), size(B)), {X,_Y} = split_binary(B, size(B) div 2), split(binary_to_list(X), X, size(X)), %% Unaligned sub binary of heap binary. split(L, make_unaligned_sub_binary(B), size(B)), {X,_Y} = split_binary(B, size(B) div 2), split(binary_to_list(X), X, size(X)), %% Reference-counted binary. L2 = lists:seq(0, ?heap_binary_size+1), B2 = list_to_binary(L2), split(L2, B2, size(B2)), %% Sub binary of reference-counted binary. split(L2, make_sub_binary(B2), size(B2)), {X2,_Y2} = split_binary(B2, size(B2) div 2), split(binary_to_list(X2), X2, size(X2)), %% Unaligned sub binary of reference-counted binary. split(L2, make_unaligned_sub_binary(B2), size(B2)), {X2,_Y2} = split_binary(B2, size(B2) div 2), split(binary_to_list(X2), X2, size(X2)), ok. split(L, B, Pos) when Pos > 0 -> {B1, B2} = split_binary(B, Pos), B1 = list_to_binary(lists:sublist(L, 1, Pos)), B2 = list_to_binary(lists:nthtail(Pos, L)), split(L, B, Pos-1); split(_L, _B, 0) -> ok. %% Tries split_binary/2 with bad arguments. bad_split(Config) when is_list(Config) -> GoodBin = list_to_binary([1,2,3]), bad_split(GoodBin, -1), bad_split(GoodBin, 4), bad_split(GoodBin, a), %% Funs are a kind of binaries. bad_split(fun(_X) -> 1 end, 1), ok. bad_split(Bin, Pos) -> {'EXIT',{badarg,_}} = (catch split_binary(Bin, Pos)). %% Test hash/2 with different type of binaries. t_hash(Config) when is_list(Config) -> test_hash([]), test_hash([253]), test_hash(lists:seq(1, ?heap_binary_size)), test_hash(lists:seq(1, ?heap_binary_size+1)), test_hash([X rem 256 || X <- lists:seq(1, 312)]), ok. test_hash(List) -> Bin = list_to_binary(List), Sbin = make_sub_binary(List), Unaligned = make_unaligned_sub_binary(Sbin), test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2), test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2). test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> N = 65535, case {Hash(Bin, N),Hash(Sbin, N),Hash(Unaligned, N)} of {H,H,H} -> ok; {H1,H2,H3} -> ct:fail("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]) end. %% Try bad arguments to size/1. bad_size(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)), ok. bad_term_to_binary(Config) when is_list(Config) -> T = id({a,b,c}), {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])), {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])), ok. t2b_system_limit(Config) when is_list(Config) -> case erlang:system_info(wordsize) of 8 -> case proplists:get_value(system_total_memory, memsup:get_system_memory_data()) of Memory when is_integer(Memory), Memory > 6*1024*1024*1024 -> test_t2b_system_limit(), garbage_collect(), ok; _ -> {skipped, "Not enough memory on this machine"} end; 4 -> {skipped, "Only interesting on 64-bit builds"} end. test_t2b_system_limit() -> io:format("Creating HugeBin~n", []), Bits = ((1 bsl 32)+1)*8, HugeBin = <<0:Bits>>, io:format("Testing term_to_binary(HugeBin)~n", []), {'EXIT',{system_limit,[{erlang,term_to_binary, [HugeBin], _} |_]}} = (catch term_to_binary(HugeBin)), io:format("Testing term_to_binary(HugeBin, [compressed])~n", []), {'EXIT',{system_limit,[{erlang,term_to_binary, [HugeBin, [compressed]], _} |_]}} = (catch term_to_binary(HugeBin, [compressed])), %% Check that it works also after we have trapped... io:format("Creating HugeListBin~n", []), HugeListBin = [lists:duplicate(2000000,2000000), HugeBin], io:format("Testing term_to_binary(HugeListBin)~n", []), {'EXIT',{system_limit,[{erlang,term_to_binary, [HugeListBin], _} |_]}} = (catch term_to_binary(HugeListBin)), io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []), {'EXIT',{system_limit,[{erlang,term_to_binary, [HugeListBin, [compressed]], _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])), ok. %% Tests binary_to_term/1 and term_to_binary/1. terms(Config) when is_list(Config) -> TestFun = fun(Term) -> S = io_lib:format("~p", [Term]), io:put_chars(S), Bin = term_to_binary(Term), case erlang:external_size(Bin) of Sz when is_integer(Sz), size(Bin) =< Sz -> ok end, Bin1 = term_to_binary(Term, [{minor_version, 1}]), case erlang:external_size(Bin1, [{minor_version, 1}]) of Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 -> ok end, Term = binary_to_term_stress(Bin), Term = binary_to_term_stress(Bin, [safe]), Bin_sz = byte_size(Bin), {Term,Bin_sz} = binary_to_term_stress(Bin, [used]), BinE = <>, {Term,Bin_sz} = binary_to_term_stress(BinE, [used]), BinU = make_unaligned_sub_binary(Bin), Term = binary_to_term_stress(BinU), Term = binary_to_term_stress(BinU, []), Term = binary_to_term_stress(BinU, [safe]), {Term,Bin_sz} = binary_to_term_stress(BinU, [used]), BinUE = make_unaligned_sub_binary(BinE), {Term,Bin_sz} = binary_to_term_stress(BinUE, [used]), BinC = erlang:term_to_binary(Term, [compressed]), BinC_sz = byte_size(BinC), true = BinC_sz =< size(Bin), Term = binary_to_term_stress(BinC), {Term, BinC_sz} = binary_to_term_stress(BinC, [used]), Bin = term_to_binary(Term, [{compressed,0}]), terms_compression_levels(Term, size(Bin), 1), BinUC = make_unaligned_sub_binary(BinC), Term = binary_to_term_stress(BinUC), {Term,BinC_sz} = binary_to_term_stress(BinUC, [used]), BinCE = <>, {Term,BinC_sz} = binary_to_term_stress(BinCE, [used]), BinUCE = make_unaligned_sub_binary(BinCE), Term = binary_to_term_stress(BinUCE), {Term,BinC_sz} = binary_to_term_stress(BinUCE, [used]) end, test_terms(TestFun), ok. %% Test binary_to_term(_, [used]) returning a big Used integer. b2t_used_big(_Config) -> case erlang:system_info(wordsize) of 8 -> {skipped, "This is not a 32-bit machine"}; 4 -> %% Use a long utf8 atom for large external format but compact on heap. BigAtom = binary_to_atom(<< <<16#F0908D88:32>> || _ <- lists:seq(1,255) >>, utf8), Atoms = (1 bsl 17) + (1 bsl 9), BigAtomList = lists:duplicate(Atoms, BigAtom), BigBin = term_to_binary(BigAtomList), {BigAtomList, Used} = binary_to_term(BigBin, [used]), 2 = erts_debug:size(Used), Used = byte_size(BigBin), Used = 1 + 1 + 4 + Atoms*(1+2+4*255) + 1, ok end. terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> BinC = erlang:term_to_binary(Term, [{compressed,Level}]), Term = binary_to_term_stress(BinC), Sz = byte_size(BinC), true = Sz =< UncompressedSz, terms_compression_levels(Term, UncompressedSz, Level+1); terms_compression_levels(_, _, _) -> ok. terms_float(Config) when is_list(Config) -> test_floats(fun(Term) -> Bin0 = term_to_binary(Term, [{minor_version,0}]), Term = binary_to_term_stress(Bin0), Bin1 = term_to_binary(Term), Bin1 = term_to_binary(Term, [{minor_version,1}]), Term = binary_to_term_stress(Bin1), true = size(Bin1) < size(Bin0), Size0 = erlang:external_size(Term, [{minor_version, 0}]), Size1 = erlang:external_size(Term), Size11 = erlang:external_size(Term, [{minor_version, 1}]), true = (Size1 =:= Size11), true = Size1 < Size0 end). float_middle_endian(Config) when is_list(Config) -> %% Testing for roundtrip is not enough. <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]), 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>). external_size(Config) when is_list(Config) -> %% Build a term whose external size only fits in a big num (on 32-bit CPU). external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF), %% Test that the same binary aligned and unaligned has the same external size. Bin = iolist_to_binary([1,2,3,96]), Unaligned = make_unaligned_sub_binary(Bin), case {erlang:external_size(Bin),erlang:external_size(Unaligned)} of {X,X} -> ok; {Sz1,Sz2} -> ct:fail(" Aligned size: ~p\n" "Unaligned size: ~p\n", [Sz1,Sz2]) end, true = (erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}])), true = (erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}])). external_size_1(Term, Size0, Limit) when Size0 < Limit -> case erlang:external_size(Term) of Size when is_integer(Size), Size0 < Size -> io:format("~p", [Size]), external_size_1([Term|Term], Size, Limit) end; external_size_1(_, _, _) -> ok. t_iolist_size(Config) when is_list(Config) -> _ = rand:uniform(), %Seed generator io:format("Seed: ~p", [rand:export_seed()]), Base = <<0:(1 bsl 20)/unit:8>>, Powers = [1 bsl N || N <- lists:seq(2, 37)], Sizes0 = [[N - rand:uniform(N div 2), lists:seq(N-2, N+2), N+N div 2, N + rand:uniform(N div 2)] || N <- Powers], %% Test sizes around 1^32 more thoroughly. FourGigs = 1 bsl 32, Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0, Sizes2 = lists:flatten(Sizes1), Sizes = lists:usort(Sizes2), io:format("~p sizes:", [length(Sizes)]), io:format("~p\n", [Sizes]), _ = [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes], ok. build_iolist(N, Base) when N < 16 -> case rand:uniform(3) of 1 -> <> = Base, Bin; _ -> lists:seq(1, N) end; build_iolist(N, Base) when N =< byte_size(Base) -> case rand:uniform(3) of 1 -> <> = Base, Bin; 2 -> <> = Base, [Bin]; 3 -> case N rem 2 of 0 -> L = build_iolist(N div 2, Base), [L,L]; 1 -> L = build_iolist(N div 2, Base), [L,L,45] end end; build_iolist(N0, Base) -> Small = rand:uniform(15), Seq = lists:seq(1, Small), N = N0 - Small, case N rem 2 of 0 -> L = build_iolist(N div 2, Base), [L,L|Seq]; 1 -> L = build_iolist(N div 2, Base), [47,L,L|Seq] end. approx_4GB_bin() -> Bin = lists:duplicate(4194304, 255), BinRet = erlang:iolist_to_binary(lists:duplicate(1124, Bin)), BinRet. duplicate_iolist(IOList, 0) -> IOList; duplicate_iolist(IOList, NrOfTimes) -> duplicate_iolist([IOList, IOList], NrOfTimes - 1). t_iolist_size_huge_list(Config) when is_list(Config) -> run_when_enough_resources( fun() -> {TimeToCreateIOList, IOList} = timer:tc(fun()->duplicate_iolist(approx_4GB_bin(), 32) end), {IOListSizeTime, CalculatedSize} = timer:tc(fun()->erlang:iolist_size(IOList) end), 20248183924657750016 = CalculatedSize, {comment, io_lib:format("Time to create iolist: ~f s. Time to calculate size: ~f s.", [TimeToCreateIOList / 1000000, IOListSizeTime / 1000000])} end). t_iolist_size_huge_bad_arg_list(Config) when is_list(Config) -> run_when_enough_resources( fun() -> P = self(), spawn_link(fun()-> IOListTmp = duplicate_iolist(approx_4GB_bin(), 32), IOList = [IOListTmp, [badarg]], {'EXIT',{badarg,_}} = (catch erlang:iolist_size(IOList)), P ! ok end), receive ok -> ok end end). %% iolist_size tests for shallow lists t_iolist_size_shallow_trapping(Config) when is_list(Config) -> Lengths = [2000, 20000, 200000, 200000, 2000000, 20000000], run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2). t_iolist_size_shallow_short_lists(Config) when is_list(Config) -> Lengths = lists:duplicate(15000, 300), run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2). t_iolist_size_shallow_tiny_lists(Config) when is_list(Config) -> Lengths = lists:duplicate(250000, 18), run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2). make_shallow_iolist(SizeDiv2, LastItem) -> lists:map( fun(I) -> case I of SizeDiv2 -> [1, LastItem]; _ -> [1, 1] end end, lists:seq(1, SizeDiv2)). %% iolist_size tests for deep lists t_iolist_size_deep_trapping(Config) when is_list(Config) -> Lengths = [2000, 20000, 200000, 200000, 2000000, 10000000], run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2). t_iolist_size_deep_short_lists(Config) when is_list(Config) -> Lengths = lists:duplicate(10000, 300), run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2). t_iolist_size_deep_tiny_lists(Config) when is_list(Config) -> Lengths = lists:duplicate(150000, 18), run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2). make_deep_iolist(1, LastItem) -> [1, LastItem]; make_deep_iolist(Depth, LastItem) -> [[1, 1], make_deep_iolist(Depth - 1, LastItem)]. % Helper functions for iolist_size tests run_iolist_size_test_and_benchmark(Lengths, ListGenerator) -> run_when_enough_resources( fun() -> GoodListsWithSizes = lists:map(fun(Length) -> {Length*2, ListGenerator(Length, 1)} end, Lengths), BadListsWithSizes = lists:map(fun(Length) -> {Length*2, ListGenerator(Length, bad)} end, Lengths), erlang:garbage_collect(), report_throughput( fun() -> lists:foreach( fun(_)-> lists:foreach( fun({Size, List}) -> Size = iolist_size(List) end, GoodListsWithSizes), lists:foreach( fun({_, List}) -> {'EXIT',_} = (catch (iolist_size(List))) end, BadListsWithSizes) end, lists:seq(1,3)) end, lists:sum(Lengths)*4) end). report_throughput(Fun, NrOfItems) -> Parent = self(), spawn(fun() -> Parent ! timer:tc(Fun) end), {Time, _} = receive D -> D end, ItemsPerMicrosecond = NrOfItems / Time, ct_event:notify(#event{ name = benchmark_data, data = [{value, ItemsPerMicrosecond}]}), {comment, io_lib:format("Items per microsecond: ~p, Nr of items: ~p, Benchmark time: ~p seconds)", [ItemsPerMicrosecond, NrOfItems, Time/1000000])}. total_memory() -> %% Total memory in GB. try MemoryData = memsup:get_system_memory_data(), case lists:keysearch(total_memory, 1, MemoryData) of {value, {total_memory, TM}} -> TM div (1024*1024*1024); false -> {value, {system_total_memory, STM}} = lists:keysearch(system_total_memory, 1, MemoryData), STM div (1024*1024*1024) end catch _ : _ -> undefined end. run_when_enough_resources(Fun) -> case {total_memory(), erlang:system_info(wordsize)} of {Mem, 8} when is_integer(Mem) andalso Mem >= 15 -> Fun(); {Mem, WordSize} -> {skipped, io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)", [Mem, WordSize])} end. %% OTP-4053 bad_binary_to_term_2(Config) when is_list(Config) -> {ok, N} = test_server:start_node(plopp, slave, []), R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]), case R of {badrpc, {'EXIT', _}} -> ok; _Other -> ct:fail({rpcresult, R}) end, test_server:stop_node(N), ok. %% Try bad input to binary_to_term/1. bad_binary_to_term(Config) when is_list(Config) -> bad_bin_to_term(an_atom), bad_bin_to_term({an,tuple}), bad_bin_to_term({a,list}), bad_bin_to_term(fun() -> self() end), bad_bin_to_term(fun(X) -> 42*X end), bad_bin_to_term(fun(X, Y) -> {X,Y} end), bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end), bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))), %% Bad float. bad_bin_to_term(<<131,70,-1:64>>), %% Truncated UTF8 character (ERL-474) bad_bin_to_term(<<131,119,1,194,163>>), ok. bad_bin_to_term(BadBin) -> {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin)). bad_bin_to_term(BadBin,Opts) -> {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin,Opts)). %% Test safety options for binary_to_term/2 safe_binary_to_term2(Config) when is_list(Config) -> bad_bin_to_term(<<131,100,0,14,"undefined_atom">>, [safe]), bad_bin_to_term(<<131,100,0,14,"other_bad_atom">>, [safe]), BadHostAtom = <<100,0,14,"badguy@badhost">>, Empty = <<0,0,0,0>>, BadRef = <<131,114,0,3,BadHostAtom/binary,0,<<0,0,0,255>>/binary, Empty/binary,Empty/binary>>, bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom BadExtFun = <<131,113,100,0,4,98,108,117,101,100,0,4,109,111,111,110,97,3>>, bad_bin_to_term(BadExtFun, [safe]), ok. %% Tests bad input to binary_to_term/1. bad_terms(Config) when is_list(Config) -> test_terms(fun corrupter/1), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,0,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,3:32,9,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,0:32,1,11,22,33>>)), {'EXIT',{badarg,_}} = (catch binary_to_term(<<131,$M,-1:32,1,11,22,33>>)), ok. corrupter(Term) when is_function(Term); is_function(hd(Term)); is_function(element(2,element(2,element(2,Term)))) -> %% Check if lists is native compiled. If it is, we do not try to %% corrupt funs as this can create some very strange behaviour. %% To show the error print `Byte` in the foreach fun in corrupter/2. case erlang:system_info(hipe_architecture) of undefined -> corrupter0(Term); Architecture -> {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), ChunkName = hipe_unified_loader:chunk_name(Architecture), NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), case NativeChunk of {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> S = io_lib:format("Skipping corruption of: ~P", [Term,12]), io:put_chars(S); {error, beam_lib, _} -> corrupter0(Term) end end; corrupter(Term) -> corrupter0(Term). corrupter0(Term) -> try S = io_lib:format("About to corrupt: ~P", [Term,12]), io:put_chars(S) catch error:badarg -> io:format("About to corrupt: <= 0 -> {ShorterBin, Rest} = split_binary(Bin, Pos), catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash MovedBin = list_to_binary([ShorterBin]), catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash %% Bit faults, shouldn't crash <> = Rest, Fun = fun(M) -> FaultyByte = Byte bxor M, catch binary_to_term_stress(<>) end, lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]), corrupter(Bin, Pos-1); corrupter(_Bin, _) -> ok. more_bad_terms(Config) when is_list(Config) -> Data = proplists:get_value(data_dir, Config), BadFile = filename:join(Data, "bad_binary"), ok = io:format("File: ~s\n", [BadFile]), case file:read_file(BadFile) of {ok,Bin} -> {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)), ok; Other -> ct:fail(Other) end. otp_5484(Config) when is_list(Config) -> {'EXIT',_} = (catch binary_to_term_stress( <<131, 104,2, %Tuple, 2 elements 103, %Pid 100,0,20,"wslin1427198@wslin14", %% Obviously bad values follow. 255,255,255,255, 255,255,255,255, 255, 106>>)), {'EXIT',_} = (catch binary_to_term_stress( <<131, 104,2, %Tuple, 2 elements 103, %Pid 106, %[] instead of atom. 0,0,0,17, 0,0,0,135, 2, 106>>)), {'EXIT',_} = (catch binary_to_term_stress( %% A old-type fun in a list containing a bad creator pid. <<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)), {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad creator pid. %% <<131, 108,0,0,0,1, %List, 1 element 112,0,0,0,66,0,52,216,81,158,148,250,237,109,185,9,208,60,202,156,244,218,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72, 103, %Pid. 106, %[] instead of an atom. 0,0,0,27,0,0,0,0,0,106>>)), {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad module. <<131, 108,0,0,0,1, %List, 1 element 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, %%100,0,1,116, 107,0,1,64, %String instead of atom (same length). 97,0,98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad index. <<131, 108,0,0,0,1, %List, 1 element 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, 100,0,1,116, %%97,0, %Integer: 0. 104,0, %Tuple {} instead of integer. 98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), {'EXIT',_} = (catch binary_to_term_stress( %% A new-type fun in a list containing a bad unique value. <<131, 108,0,0,0,1, %List, 1 element 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, 100,0,1,116, 97,0, %Integer: 0. %%98,6,64,82,230, %Integer. 100,0,2,64,65, %Atom instead of integer. 103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), %% An absurdly large atom. {'EXIT',_} = (catch binary_to_term_stress(iolist_to_binary([<<131,100,65000:16>>| lists:duplicate(65000, 42)]))), %% Longer than 255 characters. {'EXIT',_} = (catch binary_to_term_stress(iolist_to_binary([<<131,100,256:16>>| lists:duplicate(256, 42)]))), %% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we %% cover the other error cases for external funs (EXPORT_EXT). {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 97,13, %Integer: 13 97,13, %Integer: 13 97,13>>)), %Integer: 13 {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 97,13, %Integer: 13 97,13>>)), %Integer: 13 {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 106>>)), %NIL {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 98,255,255,255,255>>)), %Integer: -1 {'EXIT',_} = (catch binary_to_term_stress( <<131, 113, %EXPORT_EXP 100,0,1,64, %Atom: '@' 100,0,1,64, %Atom: '@' 113,97,13,97,13,97,13>>)), %fun 13:13/13 %% Bad funs. {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))), ok. fake_fun(Arity, Env0) -> Uniq = erlang:md5([]), Index = 0, NumFree = length(Env0), Mod = list_to_binary(?MODULE_STRING), OldIndex = 0, OldUniq = 16#123456, <<131,Pid/binary>> = term_to_binary(self()), Env1 = [term_to_binary(Term) || Term <- Env0], Env = << <> || <<131,Bin/binary>> <- Env1 >>, B = <>, <<131,$p,(byte_size(B)+4):32,B/binary>>. %% More bad terms submitted by Matthias Lang. otp_5933(Config) when is_list(Config) -> try_bad_lengths(<<131,$m>>), %binary try_bad_lengths(<<131,$n>>), %bignum try_bad_lengths(<<131,$o>>), %huge bignum ok. try_bad_lengths(B) -> try_bad_lengths(B, 16#FFFFFFFF). try_bad_lengths(B, L) when L > 16#FFFFFFF0 -> Bin = <>, io:format("~p\n", [Bin]), {'EXIT',_} = (catch binary_to_term_stress(Bin)), try_bad_lengths(B, L-1); try_bad_lengths(_, _) -> ok. otp_6817(Config) when is_list(Config) -> process_flag(min_heap_size, 20000), %Use the heap, not heap fragments. %% Floats are only validated when the heap fragment has been allocated. BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>, otp_6817_try_bin(BadFloat), %% {Binary,BadFloat}: When the error in float is discovered, a refc-binary %% has been allocated and the list of refc-binaries goes through the %% limbo area between the heap top and stack. BinAndFloat = <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70, 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153, 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229, 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248, 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48, 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>, otp_6817_try_bin(BinAndFloat), %% {Fun,BadFloat} FunAndFloat = <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84, 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111, 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48, $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>, otp_6817_try_bin(FunAndFloat), %% [ExternalPid|BadFloat] ExtPidAndFloat = <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101, 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48, 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>, otp_6817_try_bin(ExtPidAndFloat), ok. otp_6817_try_bin(Bin) -> erlang:garbage_collect(), %% If the bug is present, the heap pointer will moved when the invalid term %% is found and we will have a linked list passing through the limbo area %% between the heap top and the stack pointer. catch binary_to_term_stress(Bin), %% If the bug is present, we will overwrite the pointers in the limbo area. Filler = erlang:make_tuple(1024, 16#3FA), id(Filler), %% Will crash if the bug is present. erlang:garbage_collect(). %% Some bugs in binary_to_term when 32-bit integers are negative. otp_8117(Config) when is_list(Config) -> [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',named_fun,list,tuple], N <- lists:seq(0,31)], ok. otp_8117_do('fun',Neg) -> % Fun with negative num_free FunBin = term_to_binary(fun() -> ok end), <> = FunBin, bad_bin_to_term(<>); otp_8117_do(named_fun,Neg) -> % Named fun with negative num_free FunBin = term_to_binary(fun F() -> F end), <> = FunBin, bad_bin_to_term(<>); otp_8117_do(list,Neg) -> %% List with negative length bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>); otp_8117_do(tuple,Neg) -> %% Tuple with negative arity bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>). %% Tests ordering of binaries. ordering(Config) when is_list(Config) -> B1 = list_to_binary([7,8,9]), B2 = make_sub_binary([1,2,3,4]), B3 = list_to_binary([1,2,3,5]), Unaligned = make_unaligned_sub_binary(B2), %% From R8 binaries are compared as strings. false = B1 == B2, false = B1 =:= B2, true = B1 /= B2, true = B1 =/= B2, true = B1 > B2, true = B2 < B3, true = B2 =< B1, true = B2 =< B3, true = B2 =:= Unaligned, true = B2 == Unaligned, true = Unaligned < B3, true = Unaligned =< B3, %% Binaries are greater than all other terms. true = B1 > 0, true = B1 > 39827491247298471289473333333333333333333333333333, true = B1 > -3489274937438742190467869234328742398347, true = B1 > 3.14, true = B1 > [], true = B1 > [a], true = B1 > {a}, true = B1 > self(), true = B1 > make_ref(), true = B1 > xxx, true = B1 > fun() -> 1 end, true = B1 > fun erlang:send/2, Path = proplists:get_value(priv_dir, Config), AFile = filename:join(Path, "vanilla_file"), Port = open_port(AFile, [out]), true = B1 > Port, true = B1 >= 0, true = B1 >= 39827491247298471289473333333333333333333333333333, true = B1 >= -3489274937438742190467869234328742398347, true = B1 >= 3.14, true = B1 >= [], true = B1 >= [a], true = B1 >= {a}, true = B1 >= self(), true = B1 >= make_ref(), true = B1 >= xxx, true = B1 >= fun() -> 1 end, true = B1 >= fun erlang:send/2, true = B1 >= Port, ok. %% Test that comparison between binaries with different alignment work. unaligned_order(Config) when is_list(Config) -> L = lists:seq(0, 7), [test_unaligned_order(I, J) || I <- L, J <- L], ok. test_unaligned_order(I, J) -> Align = {I,J}, io:format("~p ~p", [I,J]), true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, Align), false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align), true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align), false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align), true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align), true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align), true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align), ok. test_unaligned_order_1(Op, A, B, {Aa,Ba}) -> erlang:Op(unaligned_sub_bin(A, Aa), unaligned_sub_bin(B, Ba)). test_terms(Test_Func) -> Test_Func(atom), Test_Func(''), Test_Func('a'), Test_Func('ab'), Test_Func('abc'), Test_Func('abcd'), Test_Func('abcde'), Test_Func('abcdef'), Test_Func('abcdefg'), Test_Func('abcdefgh'), Test_Func(fun() -> ok end), X = id([a,{b,c},c]), Y = id({x,y,z}), Z = id(1 bsl 8*257), Test_Func(fun() -> X end), Test_Func(fun() -> {X,Y} end), Test_Func([fun() -> {X,Y,Z} end, fun() -> {Z,X,Y} end, fun() -> {Y,Z,X} end]), Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, {1,2,3}}), Test_Func(1), Test_Func(42), Test_Func(-23), Test_Func(256), Test_Func(25555), Test_Func(-3333), Test_Func(1.0), Test_Func(183749783987483978498378478393874), Test_Func(-37894183749783987483978498378478393874), Very_Big = very_big_num(), Test_Func(Very_Big), Test_Func(-Very_Big+1), Test_Func([]), Test_Func("abcdef"), Test_Func([a, b, 1, 2]), Test_Func([a|b]), Test_Func({}), Test_Func({1}), Test_Func({a, b}), Test_Func({a, b, c}), Test_Func(list_to_tuple(lists:seq(0, 255))), Test_Func(list_to_tuple(lists:seq(0, 256))), Test_Func(make_ref()), Test_Func([make_ref(), make_ref()]), Test_Func(make_port()), Test_Func(make_pid()), Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))), Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))), Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))), Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))), Test_Func(make_unaligned_sub_binary(Bin0)), Test_Func(make_unaligned_sub_binary(Bin1)), Test_Func(make_unaligned_sub_binary(Bin2)), Test_Func(make_unaligned_sub_binary(Bin3)), Test_Func(make_sub_binary(lists:seq(42, 43))), Test_Func(make_sub_binary([42,43,44])), Test_Func(make_sub_binary([42,43,44,45])), Test_Func(make_sub_binary([42,43,44,45,46])), Test_Func(make_sub_binary([42,43,44,45,46,47])), Test_Func(make_sub_binary([42,43,44,45,46,47,48])), Test_Func(make_sub_binary(lists:seq(42, 49))), Test_Func(make_sub_binary(lists:seq(0, 14))), Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), Test_Func(make_sub_binary(lists:seq(0, 255))), Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), Test_Func(make_unaligned_sub_binary([42,43,44])), Test_Func(make_unaligned_sub_binary([42,43,44,45])), Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), %% Bit level binaries. Test_Func(<<1:1>>), Test_Func(<<2:2>>), Test_Func(<<42:10>>), Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), Test_Func(F = fun(A) -> 42*A end), Test_Func(lists:duplicate(32, F)), Test_Func(FF = fun binary_SUITE:all/0), Test_Func(lists:duplicate(32, FF)), ok. test_floats(Test_Func) -> Test_Func(5.5), Test_Func(-15.32), Test_Func(1.2435e25), Test_Func(1.2333e-20), Test_Func(199.0e+15), ok. very_big_num() -> very_big_num(33, 1). very_big_num(Left, Result) when Left > 0 -> very_big_num(Left-1, Result*256); very_big_num(0, Result) -> Result. make_port() -> hd(erlang:ports()). make_pid() -> spawn_link(?MODULE, sleeper, []). sleeper() -> receive after infinity -> ok end. %% Test that binaries are garbage collected properly. gc_test(Config) when is_list(Config) -> %% Note: This test is only relevant for REFC binaries. %% Therefore, we take care that all binaries are REFC binaries. true = 192 > ?heap_binary_size, B = list_to_binary(lists:seq(1, 192)), Self = self(), F1 = fun() -> gc(), {binary,[]} = process_info(self(), binary), Self ! {self(),done} end, F = fun() -> receive go -> ok end, {binary,[{_,192,1}]} = process_info(self(), binary), gc(), {B1,B2} = my_split_binary(B, 68), gc(), gc(), {binary,L1} = process_info(self(), binary), [Binfo1,Binfo2,Binfo3] = L1, {_,192,3} = Binfo1 = Binfo2 = Binfo3, 192 = size(B), 68 = size(B1), 124 = size(B2), F1() end, gc(), gc(), 192 = size(B), gc_test1(spawn_opt(erlang, apply, [F,[]], [link,{fullsweep_after,0}])). gc_test1(Pid) -> gc(), Pid ! go, receive {Pid,done} -> ok after 10000 -> ct:fail("timeout") end. %% Like split binary, but returns REFC binaries. Only useful for gc_test/1. my_split_binary(B, Pos) -> Self = self(), Ref = make_ref(), spawn(fun() -> Self ! {Ref,split_binary(B, Pos)} end), receive {Ref,Result} -> Result end. gc() -> erlang:garbage_collect(), gc1(). gc1() -> ok. bit_sized_binary_sizes(Config) when is_list(Config) -> [bsbs_1(A) || A <- lists:seq(1, 8)], ok. bsbs_1(A) -> BinSize = 32+A, io:format("A: ~p BinSize: ~p", [A,BinSize]), Bin = binary_to_term_stress(<<131,$M,5:32,A,0,0,0,0,0>>), BinSize = bit_size(Bin). %% lists:foldl(_,_,lists:seq(_,_)) with less heap consumption lists_foldl_seq(Fun, Acc0, N, To) when N =< To -> Acc1 = Fun(N, Acc0), lists_foldl_seq(Fun, Acc1, N+1, To); lists_foldl_seq(_, Acc, _, _) -> Acc. deep(Config) when is_list(Config) -> deep_roundtrip(lists_foldl_seq(fun(E, A) -> [E,A] end, [], 1, 1000000)), erlang:garbage_collect(), deep_roundtrip(lists_foldl_seq(fun(E, A) -> {E,A} end, [], 1, 1000000)), erlang:garbage_collect(), deep_roundtrip(lists_foldl_seq(fun(E, A) -> fun() -> {E,A} end end, [], 1, 1000000)), erlang:garbage_collect(), ok. deep_roundtrip(T) -> B = term_to_binary(T), T = binary_to_term(B). term2bin_tuple_fallbacks(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), term2bin_tf(fun ?MODULE:all/1), term2bin_tf(<<1:1>>), term2bin_tf(<<90,80:7>>), erts_debug:set_internal_state(available_internal_state, false), ok. term2bin_tf(Term) -> Tuple = case Term of Fun when is_function(Fun) -> {type, external} = erlang:fun_info(Fun, type), {module,M} = erlang:fun_info(Fun, module), {name,F} = erlang:fun_info(Fun, name), {M,F}; BS when bit_size(BS) rem 8 =/= 0 -> Bits = bit_size(BS) rem 8, {<>, Bits} end, Tuple = binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_tuple_fallbacks,Term})). %% Test non-standard encodings never generated by term_to_binary/1 %% but recognized by binary_to_term/1. robustness(Config) when is_list(Config) -> [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string. [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list. %% {[],a} where [] is a zero-length list. {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), %% {42,a} where 42 is a zero-length list with 42 in the tail. {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), %% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail. {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0, 104,2,100,0,1,120,100,0,1, 121,100,0,1,97>>), %% Bignums fitting in 32 bits. 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>), -1 = binary_to_term_stress(<<131,98,255,255,255,255>>), ok. %% OTP-8180: Test several terms that have been known to crash the emulator. %% (Thanks to Scott Lystig Fritchie.) otp_8180(Config) when is_list(Config) -> Data = proplists:get_value(data_dir, Config), Wc = filename:join(Data, "zzz.*"), Files = filelib:wildcard(Wc), [run_otp_8180(F) || F <- Files], ok. run_otp_8180(Name) -> io:format("~s", [Name]), {ok,Bins} = file:consult(Name), [begin io:format("~p\n", [Bin]), {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)) end || Bin <- Bins], ok. %% Test that exit and GC during trapping term_to_binary and binary_to_term %% does not crash. trapping(Config) when is_list(Config)-> do_trapping(5, term_to_binary, fun() -> [lists:duplicate(2000000,2000000)] end), do_trapping(5, binary_to_term, fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end), do_trapping(5, binary_to_list, fun() -> [list_to_binary(lists:duplicate(2000000,$x))] end), do_trapping(5, list_to_binary, fun() -> [lists:duplicate(2000000,$x)] end), do_trapping(5, bitstring_to_list, fun() -> [list_to_bitstring([lists:duplicate(2000000,$x),<<7:4>>])] end), do_trapping(5, list_to_bitstring, fun() -> [[lists:duplicate(2000000,$x),<<7:4>>]] end) . do_trapping(0, _, _) -> ok; do_trapping(N, Bif, ArgFun) -> io:format("N=~p: Do ~p ~s gc.\n", [N, Bif, case N rem 2 of 0 -> "with"; 1 -> "without" end]), Pid = spawn(?MODULE,trapping_loop,[Bif, ArgFun, 1000, self()]), receive ok -> ok end, Ref = make_ref(), case N rem 2 of 0 -> erlang:garbage_collect(Pid, [{async,Ref}]), receive after 1 -> ok end; 1 -> void end, exit(Pid, kill), case N rem 2 of 0 -> receive {garbage_collect, Ref, _} -> ok end; 1 -> void end, receive after 1 -> ok end, do_trapping(N-1, Bif, ArgFun). trapping_loop(Bif, ArgFun, N, Pid) -> Args = ArgFun(), Pid ! ok, trapping_loop2(Bif,Args,N). trapping_loop2(_,_,0) -> ok; trapping_loop2(Bif,Args,N) -> apply(erlang,Bif,Args), trapping_loop2(Bif, Args, N-1). large(Config) when is_list(Config) -> List = lists:flatten(lists:map(fun (_) -> [0,1,2,3,4,5,6,7,8] end, lists:seq(1, 131072))), Bin = list_to_binary(List), List = binary_to_list(Bin), PartList = lists:reverse(tl(tl(lists:reverse(tl(tl(List)))))), PartList = binary_to_list(Bin, 3, length(List)-2), ListBS = List ++ [<<7:4>>], ListBS = bitstring_to_list(list_to_bitstring(ListBS)), BitStr1 = list_to_bitstring(lists:duplicate(1024*1024, [<<1,5:3>>])), BitStr1 = list_to_bitstring(bitstring_to_list(BitStr1)), BitStr2 = list_to_bitstring([lists:duplicate(512*1024, [<<1,5:3>>]), Bin]), BitStr2 = list_to_bitstring(bitstring_to_list(BitStr2)), ok. error_after_yield(Config) when is_list(Config) -> L2BTrap = {erts_internal, list_to_binary_continue, 1}, error_after_yield(badarg, erlang, list_to_binary, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap), error_after_yield(badarg, erlang, iolist_to_binary, 1, fun () -> [[list2iolist(mk_list(1000000)), oops]] end, L2BTrap), error_after_yield(badarg, erlang, list_to_bitstring, 1, fun () -> [[list2bitstrlist(mk_list(1000000)), oops]] end, L2BTrap), error_after_yield(badarg, binary, list_to_bin, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap), B2TTrap = {erts_internal, binary_to_term_trap, 1}, error_after_yield(badarg, erlang, binary_to_term, 1, fun () -> [error_after_yield_bad_ext_term()] end, B2TTrap), error_after_yield(badarg, erlang, binary_to_term, 2, fun () -> [error_after_yield_bad_ext_term(), [safe]] end, B2TTrap), case erlang:system_info(wordsize) of 4 -> SysLimitSz = 1 bsl 32, error_after_yield(system_limit, erlang, list_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), error_after_yield(system_limit, erlang, iolist_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), error_after_yield(system_limit, erlang, list_to_bitstring, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap), error_after_yield(system_limit, binary, list_to_bin, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap); 8 -> % Takes waaaay to long time to test system_limit on 64-bit archs... ok end, ok. error_after_yield(Type, M, F, AN, AFun, TrapFunc) -> io:format("Testing ~p for ~p:~p/~p~n", [Type, M, F, AN]), Tracer = self(), {Pid, Mon} = spawn_monitor(fun () -> A = AFun(), try erlang:yield(), erlang:trace(self(),true,[running,{tracer,Tracer}]), apply(M, F, A), exit({unexpected_success, {M, F, A}}) catch error:Type:Stk -> erlang:trace(self(),false,[running,{tracer,Tracer}]), %% We threw the exception from the native %% function we trapped to, but we want %% the BIF that originally was called %% to appear in the stack trace. [{M, F, A, _} | _] = Stk end end), receive {'DOWN', Mon, process, Pid, Reason} -> normal = Reason end, TD = erlang:trace_delivered(Pid), receive {trace_delivered, Pid, TD} -> NoYields = error_after_yield_sched(Pid, TrapFunc, 0), io:format("No of yields: ~p~n", [NoYields]), true = NoYields > 2 end, ok. error_after_yield_sched(P, TrapFunc, N) -> receive {trace, P, out, TrapFunc} -> receive {trace, P, in, TrapFunc} -> error_after_yield_sched(P, TrapFunc, N+1) after 0 -> exit(trap_sched_mismatch) end; {trace, P, out, Func} -> receive {trace, P, in, Func} -> error_after_yield_sched(P, TrapFunc, N) after 0 -> exit(other_sched_mismatch) end after 0 -> N end. error_after_yield_bad_ext_term() -> TupleSz = 2000000, <<131, % Version magic AtomExt/binary>> = term_to_binary(an_atom_we_use_for_this), BadAtomExt = [100, %% ATOM_EXT 255, 255, % Invalid size of 65535 bytes "oops"], %% Produce a large tuple where the last element is invalid list_to_binary([131, %% Version magic 105, %% LARGE_TUPLE_EXT <>, %% Tuple size lists:duplicate(TupleSz-1, AtomExt), %% Valid atoms BadAtomExt]). %% Invalid atom at the end cmp_old_impl(Config) when is_list(Config) -> %% This test was originally a comparison with the non yielding %% implementation in R16B. Since OTP 22 we can't talk distribution with such %% old nodes (< 19). The test case it kept but compares with previous major %% version for semantic regression test. Cookie = atom_to_list(erlang:get_cookie()), Rel = (integer_to_list(list_to_integer(erlang:system_info(otp_release)) - 1) ++ "_latest"), case test_server:is_release_available(Rel) of false -> {skipped, "No OTP "++Rel++" available"}; true -> {ok, Node} = test_server:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100000))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000000))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000000))]}), cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list_lb(10000000))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100000))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000000))]}), cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000000))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100000))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000000))]}), cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000000))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100000)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000000)))]}), cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000000)))]}), test_server:stop_node(Node), ok end. %% Utilities. huge_iolist(Lim) -> Sz = 1024, huge_iolist(list_to_binary(mk_list(Sz)), Sz, Lim). huge_iolist(X, Sz, Lim) when Sz >= Lim -> X; huge_iolist(X, Sz, Lim) -> huge_iolist([X, X], Sz*2, Lim). cmp_node(Node, {M, F, A}) -> Res = rpc:call(Node, M, F, A), Res = apply(M, F, A), ok. make_sub_binary(Bin) when is_binary(Bin) -> {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), B; make_sub_binary(List) -> make_sub_binary(list_to_binary(List)). make_unaligned_sub_binary(Bin0) when is_binary(Bin0) -> Bin1 = <<0:3,Bin0/binary,31:5>>, Sz = size(Bin0), <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), Bin; make_unaligned_sub_binary(List) -> make_unaligned_sub_binary(list_to_binary(List)). %% Add 1 bit to the size of the binary. bit_sized_binary(Bin0) -> Bin = <>, BitSize = bit_size(Bin), BitSize = 8*size(Bin) + 1, Bin. unaligned_sub_bin(Bin, 0) -> Bin; unaligned_sub_bin(Bin0, Offs) -> F = rand:uniform(256), Roffs = 8-Offs, Bin1 = <>, Sz = size(Bin0), <<_:Offs,Bin:Sz/binary,_:Roffs>> = id(Bin1), Bin. id(I) -> I. %% Stress binary_to_term with different initial reductions binary_to_term_stress(Bin) -> binary_to_term_stress(Bin, no_opts). binary_to_term_stress(Bin, Opts) -> Reds = get_reds(), T = b2t(erlang:system_info(context_reductions), Bin, Opts, catch_binary_to_term(Bin, Opts)), set_reds(Reds), T = case Opts of no_opts -> binary_to_term(Bin); _ -> binary_to_term(Bin,Opts) end. catch_binary_to_term(Bin, no_opts) -> try binary_to_term(Bin) catch error:badarg -> binary_to_term_throws_badarg end; catch_binary_to_term(Bin, Opts) -> try binary_to_term(Bin, Opts) catch error:badarg -> binary_to_term_throws_badarg end. b2t(0, _Bin, _Opts, Term) -> Term; b2t(Reds, Bin, Opts, Term) -> set_reds(Reds), Term = catch_binary_to_term(Bin,Opts), b2t(Reds div 3, Bin, Opts, Term). set_reds(Reds) -> try erts_debug:set_internal_state(reds_left, Reds) catch error:undef -> erts_debug:set_internal_state(available_internal_state, true), set_reds(Reds) end. get_reds() -> try erts_debug:get_internal_state(reds_left) catch error:undef -> erts_debug:set_internal_state(available_internal_state, true), get_reds() end. -define(LARGE_BIN, (512*1024+10)). -define(LARGE_BIN_LIM, (1024*1024)). mk_list(0, Acc) -> Acc; mk_list(Sz, Acc) -> mk_list(Sz-1, [$A+(Sz band 63) | Acc]). mk_list(Sz) when Sz >= ?LARGE_BIN_LIM -> SzLeft = Sz - ?LARGE_BIN, SzHd = SzLeft div 2, SzTl = SzLeft - SzHd, [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])]; mk_list(Sz) -> mk_list(Sz, []). mk_list_lb(Sz) when Sz >= ?LARGE_BIN_LIM -> SzLeft = Sz - ?LARGE_BIN, SzHd = SzLeft div 2, SzTl = SzLeft - SzHd, [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])]; mk_list_lb(Sz) -> mk_list(Sz, []). list2iolist(List) -> list2iolist(List, []). list2iolist([], Acc) -> Acc; list2iolist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256, is_integer(X1), 0 =< X1, X1 < 256, is_integer(X2), 0 =< X2, X2 < 256, is_integer(X3), 0 =< X3, X3 < 256, is_integer(X4), 0 =< X4, X4 < 256, is_integer(X5), 0 =< X5, X5 < 256 -> NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of 0 -> [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]]; 1 -> [Acc, [], erlang:list_to_binary([X0, X1, X2, X3, X4, X5])]; 2 -> [Acc, [[[[X0|erlang:list_to_binary([X1])],[X2|erlang:list_to_binary([X3])],[X4|erlang:list_to_binary([X5])]]]|<<"">>]]; 3 -> [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])] end, list2iolist(Xs, NewAcc); list2iolist([X | Xs], Acc) -> list2iolist(Xs, [Acc,X]). list2bitstrlist(List) -> [list2bitstrlist(List, []), <<4:7>>]. list2bitstrlist([], Acc) -> Acc; list2bitstrlist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256, is_integer(X1), 0 =< X1, X1 < 256, is_integer(X2), 0 =< X2, X2 < 256, is_integer(X3), 0 =< X3, X3 < 256, is_integer(X4), 0 =< X4, X4 < 256, is_integer(X5), 0 =< X5, X5 < 256 -> NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of 0 -> [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]]; 1 -> [Acc, [], <>, <>, <>]; 2 -> [Acc, [[[[X0|<>],X3]],[X4|erlang:list_to_binary([X5])]|<<"">>]]; 3 -> [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])] end, list2bitstrlist(Xs, NewAcc); list2bitstrlist([X | Xs], Acc) -> list2bitstrlist(Xs, [Acc,X]).