%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2024. 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, t2b_deterministic/1,
         t2b_minor_version/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,
         sub_bin_copy/1,
	 bad_size/1,
         unsorted_map_in_map/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,
         big_binary_to_term/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,
         robustness/1,otp_8117/1,
	 otp_8180/1, trapping/1, large/1,
	 error_after_yield/1, cmp_old_impl/1,
         t2b_system_limit/1,
         term_to_iovec/1,
         is_binary_test/1,
         local_ext/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, t2b_deterministic,
     t2b_minor_version,
     bad_binary_to_term_2, safe_binary_to_term2,
     bad_binary_to_term, bad_terms, t_hash, bad_size,
     big_binary_to_term,
     sub_bin_copy, bad_term_to_binary, t2b_system_limit,
     term_to_iovec, more_bad_terms,
     unsorted_map_in_map,
     otp_5484, otp_5933,
     ordering, unaligned_order, gc_test,
     bit_sized_binary_sizes, otp_6817, otp_8117, deep,
     robustness, otp_8180, trapping, large,
     error_after_yield, cmp_old_impl,
     is_binary_test,
     local_ext].

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).

-define(MAP_EXT, 116).
-define(SMALL_INTEGER_EXT, 97).
-define(SMALL_ATOM_UTF8_EXT, 119).
-define(ATOM_EXT, 100).
-define(NIL, 106).
-define(MAP_SMALL_MAP_LIMIT, 32).
-define(FLOAT_EXT, 99).
-define(NEW_FLOAT_EXT, 70).

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, [iovec])),
    {'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 total_memory() of
                Memory when is_integer(Memory), Memory > 6 ->
                    do_t2b_system_limit();
                _ ->
                    {skipped, "Not enough memory on this machine"}
            end;
        4 ->
            {skipped, "Only interesting on 64-bit builds"}
    end.

do_t2b_system_limit() ->
    F = fun() ->
                io:format("Creating HugeBin~n", []),
                Bits = (1 bsl 32) + 1,
                HugeBin = <<0:Bits/unit:8>>,
                test_t2b_system_limit(HugeBin, term_to_binary,
                                      fun erlang:term_to_binary/1,
                                      fun erlang:term_to_binary/2),
                test_t2b_system_limit(HugeBin, term_to_iovec,
                                      fun erlang:term_to_iovec/1,
                                      fun erlang:term_to_iovec/2),
                garbage_collect(),
                ok
        end,
    {ok, Peer, Node} = ?CT_PEER(),
    erpc:call(Node, F),
    peer:stop(Peer).

test_t2b_system_limit(HugeBin, Name, F1, F2) ->
    io:format("Testing ~p(HugeBin)~n", [Name]),
    {'EXIT',{system_limit,[{erlang,Name,[HugeBin],_}|_]}} =
        t2b_eval(fun() -> F1(HugeBin) end),

    io:format("Testing ~p(HugeBin, [compressed])~n", [Name]),
    {'EXIT',{system_limit,[{erlang,Name,[HugeBin,[compressed]],_}|_]}} =
        t2b_eval(fun() -> F2(HugeBin, [compressed]) end),

    %% Check that it works also after we have trapped...
    io:format("Creating HugeListBin~n", []),
    HugeListBin = [lists:duplicate(2000000, 2000000), HugeBin],

    io:format("Testing ~p(HugeListBin)~n", [Name]),
    {'EXIT',{system_limit,[{erlang,Name,[HugeListBin],_}|_]}} =
        t2b_eval(fun() -> F1(HugeListBin) end),

    io:format("Testing ~p(HugeListBin, [compressed])~n", [Name]),
    {'EXIT',{system_limit,[{erlang,Name,[HugeListBin,[compressed]],_}|_]}} =
        t2b_eval(fun() -> F2(HugeListBin, [compressed]) end),

    ok.

t2b_eval(F) ->
    Result = (catch F()),
    io:put_chars(io_lib:format("~P\n", [Result,100])),
    Result.

term_to_iovec(Config) when is_list(Config) ->
    Bin = list_to_binary(lists:duplicate(1000,100)),
    Bin2 = list_to_binary(lists:duplicate(65,100)),
    check_term_to_iovec({[Bin, atom, Bin, 1244, make_ref(), Bin]}),
    check_term_to_iovec(Bin),
    check_term_to_iovec([Bin,Bin,Bin,Bin]),
    check_term_to_iovec(blipp),
    check_term_to_iovec(lists:duplicate(1000,100)),
    check_term_to_iovec([[Bin2]]),
    check_term_to_iovec([erlang:ports(), Bin, erlang:processes()]),
    ok.

check_term_to_iovec(Term) ->
    IoVec1 = erlang:term_to_iovec(Term),
    ok = check_is_iovec(IoVec1),
    IoVec2 = erlang:term_to_iovec(Term, []),
    ok = check_is_iovec(IoVec2),
    B = erlang:term_to_binary(Term),
    IoVec1Bin = erlang:iolist_to_binary(IoVec1),
    IoVec2Bin = erlang:iolist_to_binary(IoVec2),
    try
        B = IoVec1Bin
    catch
        _:_ ->
            io:format("Binary: ~p~n", [B]),
            io:format("I/O vec1 binary: ~p~n", [IoVec1Bin]),
            io:format("I/O vec1: ~p~n", [IoVec1]),
            ct:fail(not_same_result)
    end,
    try
        B = IoVec2Bin
    catch
        _:_ ->
            io:format("Binary: ~p~n", [B]),
            io:format("I/O vec2 binary: ~p~n", [IoVec2Bin]),
            io:format("I/O vec2: ~p~n", [IoVec2]),
            ct:fail(not_same_result)
    end.
    
check_is_iovec([]) ->
    ok;
check_is_iovec([B|Bs]) when is_binary(B) ->
    check_is_iovec(Bs).

%% 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,

                      BinDet = term_to_binary(Term, [deterministic]),
                      case erlang:external_size(Bin) of
                          DetSz when is_integer(DetSz), byte_size(Bin) =< DetSz ->
                              ok
                      end,

		      Term = binary_to_term_stress(Bin),
		      Term = binary_to_term_stress(Bin, [safe]),
		      Term = binary_to_term_stress(BinDet),
                      Bin_sz = byte_size(Bin),
		      {Term,Bin_sz} = binary_to_term_stress(Bin, [used]),

                      BinE = <<Bin/binary, 1, 2, 3>>,
		      {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 = <<BinC/binary, 1, 2, 3>>,
		      {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>>).

t2b_minor_version(_Config) ->
    Umlaut = "ätöm",
    UmlautLatin1 = unicode:characters_to_binary(Umlaut, latin1, latin1),
    UmlautUtf8   = unicode:characters_to_binary(Umlaut, latin1, utf8),
    UmlautAtom = binary_to_atom(UmlautLatin1, latin1),
    UmlautAtom = binary_to_atom(UmlautUtf8, utf8),
    ExoticBin = <<"こんにちは"/utf8>>,
    ExoticAtom = binary_to_atom(ExoticBin, utf8),

    <<131, ?SMALL_ATOM_UTF8_EXT, 4, "atom">> = term_to_binary(atom),
    <<131, ?SMALL_ATOM_UTF8_EXT, 6, UmlautUtf8/binary>> =
        term_to_binary(UmlautAtom),
    <<131, ?SMALL_ATOM_UTF8_EXT, 15, ExoticBin/binary>> =
        term_to_binary(ExoticAtom),

    <<131, ?SMALL_ATOM_UTF8_EXT, 4, "atom">> =
        term_to_binary(atom, [{minor_version,2}]),
    <<131, ?SMALL_ATOM_UTF8_EXT, 6, UmlautUtf8/binary>> =
        term_to_binary(UmlautAtom, [{minor_version,2}]),
    <<131, ?SMALL_ATOM_UTF8_EXT, 15, ExoticBin/binary>> =
        term_to_binary(ExoticAtom, [{minor_version,2}]),

    <<131, ?ATOM_EXT, 4:16, "atom">> =
        term_to_binary(atom, [{minor_version,1}]),
    <<131, ?ATOM_EXT, 4:16, UmlautLatin1/binary>> =
        term_to_binary(UmlautAtom, [{minor_version,1}]),
    <<131, ?SMALL_ATOM_UTF8_EXT, 15, ExoticBin/binary>> =
        term_to_binary(ExoticAtom, [{minor_version,1}]),

    <<131, ?ATOM_EXT, 4:16, "atom">> =
        term_to_binary(atom, [{minor_version,0}]),
    <<131, ?ATOM_EXT, 4:16, UmlautLatin1/binary>> =
        term_to_binary(UmlautAtom, [{minor_version,0}]),
    <<131, ?SMALL_ATOM_UTF8_EXT, 15, ExoticBin/binary>> =
        term_to_binary(ExoticAtom, [{minor_version,0}]),

    <<131,?NEW_FLOAT_EXT,64,9,30,184,81,235,133,31>> =
        term_to_binary(3.14),
    <<131,?NEW_FLOAT_EXT,64,9,30,184,81,235,133,31>> =
        term_to_binary(3.14, [{minor_version, 2}]),
    <<131,?NEW_FLOAT_EXT,64,9,30,184,81,235,133,31>> =
        term_to_binary(3.14, [{minor_version, 1}]),
    <<131,?FLOAT_EXT,FloatStr:31/binary>> =
        term_to_binary(3.14, [{minor_version, 0}]),
    3.14 = binary_to_float(FloatStr),
    ok.

%% Test term_to_binary(Term, [deterministic]).
t2b_deterministic(_Config) ->
    _ = rand:uniform(),				%Seed generator
    io:format("Seed: ~p", [rand:export_seed()]),
    Map0 = t2b_deterministic_1(100, #{1 => a, 1.0 => b}),

    Map1 = maps:merge(make_map(10000), Map0),
    test_deterministic(Map1),

    case total_memory() of
        Amount when Amount > 15 ->
            t2b_deterministic_heavy(Map0);
        _ ->
            {comment,"heavy tests skipped"}
    end.

t2b_deterministic_heavy(Map0) ->
    Map1 = maps:merge(make_map(1_000_000), Map0),
    test_deterministic(Map1),

    %% If the external representation of the term to be encoded is
    %% estimated to fit in a heap binary (64 bytes), the internal
    %% enc_term_int() function that does the actual encoding will be
    %% called without an context and thus is unable to trap. In the
    %% DEBUG build of the runtime system, maps with more than 3
    %% elements will be large maps, and depending on the keys and
    %% values, it is possible that a large map can fit in 64
    %% bytes. Therefore, the encoding code for deterministic maps must
    %% cannot assume that there always exists a context.

    lists:foreach(fun(N) ->
                          Map = maps:from_list([{E,E} || E <- lists:seq(1, N)]),
                          test_deterministic(Map)
                  end, lists:seq(4, 16)),

    %% Test that terminating a process that is encoding a huge map
    %% will neither crash the runtime system nor leak memory. (Run the
    %% test with memory sanitizer or valgrind to catch potential memory
    %% leaks.)
    erlang:trace_pattern({erlang, term_to_binary, 2}, true, []),
    Map = huge_map(1_000_000, []),
    rinse_and_repeat(2, Map).

rinse_and_repeat(Wait, Map) ->
    {Pid,Ref} = spawn_monitor(fun() ->
                                      receive go -> ok end,
                                      term_to_binary(Map, [deterministic])
                              end),
    erlang:trace(Pid, true, [call,arity]),

    Pid ! go,
    receive
        {trace,Pid,call,{erlang,term_to_binary,2}} ->
            %% Wait a short period to avoid killing term_to_binary/2 during
            %% size calculation part.
            receive after Wait -> ok end,
            exit(Pid, kill);
        Other ->
            error(Other)
    end,

    receive
        {'DOWN',Ref,process,Pid,Reason} ->
            case Reason of
                normal ->
                    %% The encoding operation finished before the process
                    %% was forcefully killed. Done.
                    ok;
                killed ->
                    %% The process was killed in term_to_binary/2. We
                    %% can't know whether it was killed too early, so
                    %% we will try again with a slightly longer time.
                    rinse_and_repeat(Wait + Wait div 2, Map)
            end;
        Other2 ->
            error(Other2)
    end.

huge_map(0, Acc) ->
    maps:from_list(Acc);
huge_map(N, Acc) ->
    %% Create awkward keys to slow down the sorting.
    Key = <<N:127/big>>,
    huge_map(N-1, [{Key,N}|Acc]).

t2b_deterministic_1(0, Map) ->
    Map;
t2b_deterministic_1(N, Map0) ->
    test_deterministic(Map0),
    Map = Map0#{random_term() => N},
    t2b_deterministic_1(N - 1, Map).

test_deterministic(Map) ->
    Bin = term_to_binary(Map, [deterministic]),
    Map = binary_to_term(Bin),

    %% Make sure that the keys for the map are ordered.
    List0 = maps:to_list(Map),
    List = lists:sort(fun(A, B) -> erts_internal:cmp_term(A, B) =< 0 end, List0),
    List = decode_ext_map(Bin),
    ok.

decode_ext_map(MapBin) ->
    MapTag = 116,
    ListTag = 108,
    NilTag = 106,

    %% Rewrite the map to a list before decoding to preserve the order
    %% the map elements.
    <<131,MapTag,Size:32,Bin0/binary>> = MapBin,
    ListBin = <<131,ListTag,(2*Size):32,Bin0/binary,NilTag>>,
    List = binary_to_term(ListBin),
    decode_ext_map_1(List).

decode_ext_map_1([Key,Val|T]) ->
    [{Key,Val}|decode_ext_map_1(T)];
decode_ext_map_1([]) -> [].

make_map(N) ->
    maps:from_list([{I,I*I} || I <- lists:seq(1, N)]).

random_term() ->
    case rand:uniform(11) of
        1 ->
            list_to_atom(integer_to_list(36#aaaa + rand:uniform(100) * 23, 36));
        2 ->
            rand:uniform();
        3 ->
            rand:uniform(10000000);
        4 ->
            (1 bsl rand:uniform(128)) + rand:uniform(10000000);
        5 ->
            self();
        6 ->
            make_ref();
        7 ->
            random_list();
        8 ->
            list_to_tuple(random_list());
        9 ->
            [];
        10 ->
            make_map(33);
        11 ->
            make_map(rand:uniform(10))
    end.

random_list() ->
    [random_term() || _ <- lists:seq(1, rand:uniform(10)-1)].

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 ->
	    <<Bin:N/binary,_/binary>> = Base,
	    Bin;
	_ ->
	    lists:seq(1, N)
    end;
build_iolist(N, Base) when N =< byte_size(Base) ->
    case rand:uniform(3) of
	1 ->
	    <<Bin:N/binary,_/binary>> = Base,
	    Bin;
	2 ->
	    <<Bin:N/binary,_/binary>> = 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_1GB_bin() ->
    iolist_to_binary(lists:duplicate(281, <<-1:4194304/unit:8>>)).

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_1GB_bin(), 32)
                           end),
              {IOListSizeTime, CalculatedSize} =
                  timer:tc(fun() ->
                                   iolist_size(IOList)
                           end),
              5062045981164437504 = 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_1GB_bin(), 32),
                                 IOList = [IOListTmp, [badarg]],
                                 {'EXIT',{badarg,_}} = catch 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
	SMD = memsup:get_system_memory_data(),
        TM = proplists:get_value(
               available_memory, SMD,
               proplists:get_value(
                 total_memory, SMD,
                 proplists:get_value(
                   system_total_memory, SMD))),
        TM div (1024*1024*1024)
    catch
	_ : _ ->
	    undefined
    end.

run_when_enough_resources(Fun) ->
    DemandGb = case erlang:system_info(build_type) of
                   Gb when Gb =:= valgrind; Gb =:= asan ->
                       30;
                   _ ->
                       15
               end,
    case {total_memory(), erlang:system_info(wordsize)} of
        {Mem, 8} when is_integer(Mem) andalso Mem >= DemandGb ->
            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, Peer, N} = ?CT_PEER(),
    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,
    peer:stop(Peer),
    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)).


%% OTP-18343: Decode unsorted flatmap as key in hashmap
unsorted_map_in_map(Config) when is_list(Config) ->
    K1 = 1,
    K2 = 2,
    true = K1 < K2,
    FMap = #{K1 => [], K2 => []},
    FMapBin = <<?MAP_EXT, 2:32,
                %% unsorted list of key/value pairs
                ?SMALL_INTEGER_EXT, K2, ?NIL,
                ?SMALL_INTEGER_EXT, K1, ?NIL>>,
    FMap = binary_to_term(<<131, FMapBin/binary>>),

    HKeys = lists:seq(1, ?MAP_SMALL_MAP_LIMIT+1),
    HMap0 = maps:from_list([{K,[]} || K <- HKeys]),
    HMap0Bin = term_to_binary(HMap0),

    %% Replace last key/value pair with FMap => []
    Prologue = binary:part(HMap0Bin, 0, byte_size(HMap0Bin)-3),
    HMap1Bin = <<Prologue/binary, FMapBin/binary, ?NIL>>,
    HMap1 = binary_to_term(HMap1Bin),

    %% Moment of truth, can we lookup key FMap
    [] = maps:get(FMap, HMap1),
    ok.

%% 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.

%% OTP-18306 Decode binary/bitstring with size >= 2Gbyte
big_binary_to_term(Config) ->
    run_when_enough_resources(
      fun() ->
              Bin = binary:copy(<<0>>, 2 * 1024 * 1024 * 1024),
              big_binary_roundtrip(Bin),
              erlang:garbage_collect(),
              <<_:1, BitStr/bits>> = Bin,
              big_binary_roundtrip(BitStr),
              ok
      end).

big_binary_roundtrip(Bin) ->
    Bin = erlang:binary_to_term(erlang:term_to_binary(Bin)),
    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) ->
    try
	      S = io_lib:format("About to corrupt: ~P", [Term,12]),
	      io:put_chars(S)
	  catch
	      error:badarg ->
		  io:format("About to corrupt: <<bit-level-binary:~p",
			    [bit_size(Term)])
	  end,
    Bin = term_to_binary(Term),
    corrupter(Bin, size(Bin)-1),
    CompressedBin = term_to_binary(Term, [compressed]),
    corrupter(CompressedBin, size(CompressedBin)-1).

corrupter(Bin, Pos) when Pos >= 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
    <<Byte,Tail/binary>> = Rest,
    Fun = fun(M) -> FaultyByte = Byte bxor M,                    
		    catch binary_to_term_stress(<<ShorterBin/binary,
					  FaultyByte, Tail/binary>>) 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 = << <<Bin/binary>> || <<131,Bin/binary>> <- Env1 >>,
    B = <<Arity,Uniq/binary,Index:32,NumFree:32,
	 $d,(byte_size(Mod)):16,Mod/binary,	%Module.
	 $a,OldIndex:8,
	 $b,OldUniq:32,
	 Pid/binary,Env/binary>>,
    <<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 = <<B/binary,L:32>>,
    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),
    <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,   
    bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
otp_8117_do(named_fun,Neg) ->
    % Named fun with negative num_free
    FunBin = term_to_binary(fun F() -> F end),
    <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
    bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
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,

    Port = hd(erlang:ports()),
    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([make_port(), make_ref(), make_pid(), fun() -> ok end,
               Very_Big | lists:seq(1, 75)]),

    Test_Func({}),
    Test_Func({1}),
    Test_Func({a, b}),
    Test_Func({a, b, c}),
    Test_Func({make_port(), make_ref(), make_pid(), fun() -> ok end}),
    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)])),

    %% Funs in a list.
    Test_Func(F = fun(A) -> 42*A end),
    Test_Func(lists:duplicate(32, F)),

    %% External funs in a list.
    Test_Func(FF = fun binary_SUITE:all/0),
    Test_Func(lists:duplicate(32, FF)),

    %% Maps.
    SmallMap = #{a => 1, b => 2, c => 3},
    LargeMap1 = maps:from_list([{list_to_atom(integer_to_list(36#cafe+N*N*N, 36)),N} ||
                                   N <- lists:seq(1, 33)]),
    LargeMap2 = maps:from_list([{list_to_atom(integer_to_list(36#dead+N, 36)),N} ||
                                   N <- lists:seq(1, 50)]),
    MapWithMap = LargeMap1#{SmallMap => a, LargeMap1 => LargeMap2, LargeMap2 => LargeMap1,
                            [LargeMap1,LargeMap2] => LargeMap1,
                            <<"abc">> => SmallMap, <<"qrs">> => LargeMap1,
                            <<"xyz">> => LargeMap2 },
    Test_Func(#{}),
    Test_Func(SmallMap),
    Test_Func(LargeMap1),
    Test_Func(LargeMap2),
    Test_Func(MapWithMap),

    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,Refc} = Binfo1 = Binfo2 = Binfo3,
                %% This is usually 3 but sharing-preserving can bring this down
                %% to 2, and in theory even 1.
                true = Refc =< 3,
		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) ->
    %% Make sure that all references on the faraway process side are cleaned
    %% up by receiving the response in a monitor down and waiting "long enough"
    %% for it to have terminated.
    {Pid, Mon} = spawn_monitor(fun() -> exit(split_binary(B, Pos)) end),
    receive
        {'DOWN', Mon, process, Pid, Result} ->
            timer:sleep(1000),
            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).

%% 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_bitstring_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
		    <<TupleSz:32/big>>, %% 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.
    Rel = integer_to_list(list_to_integer(erlang:system_info(otp_release)) - 1),
    case ?CT_PEER_REL([], Rel, proplists:get_value(priv_dir, Config)) of
	not_available ->
	    {skipped, "No OTP "++Rel++" available"};
        {ok, Peer, Node}  ->
	    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)))]}),

	    peer:stop(Peer),

	    ok
    end.

%% OTP-16265
%% This testcase is mainly targeted toward --enable-sharing-preserving.
sub_bin_copy(Config) when is_list(Config) ->
    Papa = self(),
    Echo = spawn_link(fun() -> echo(Papa) end),
    HeapBin = list_to_binary(lists:seq(1,3)),
    sub_bin_copy_1(HeapBin, Echo),
    ProcBin = list_to_binary(lists:seq(1,65)),
    sub_bin_copy_1(ProcBin, Echo),
    unlink(Echo),
    exit(Echo, kill),
    ok.

sub_bin_copy_1(RealBin, Echo) ->
    Bits = bit_size(RealBin) - 1,
    <<SubBin:Bits/bits, _/bits>> = RealBin,

    %% Send (copy) messages consisting of combinations of both
    %% the SubBin and the RealBin it refers to.
    [begin
         Echo ! Combo,
         {_, Combo} = {Combo, receive M -> M end}
     end
     || Len <- lists:seq(2,5), Combo <- combos([RealBin, SubBin], Len)],
    ok.

combos(_, 0) ->
    [[]];
combos(Elements, Len) ->
    [[E | C] || E <- Elements, C <- combos(Elements,Len-1)].

echo(Papa) ->
    receive M -> Papa ! M end,
    echo(Papa).

%% GH-6239.
is_binary_test(_Config) ->
    <<"foo42">> = concat_stuff(foo, 42),
    <<"foo2749963626218098647">> = concat_stuff(foo, 2749963626218098647), %Bignum.
    <<"foobar">> = concat_stuff(foo, <<"bar">>),
    <<"bar100">> = concat_stuff(<<"bar">>, 100),
    <<"bar2749963626218098647">> = concat_stuff(<<"bar">>, 2749963626218098647), %Bignum.
    <<"barfood">> = concat_stuff(<<"bar">>, <<"food">>),

    ok.

concat_stuff(A, B) when is_integer(B); is_binary(B) ->
    <<(case A of
           X when is_binary(X) -> X;
           _ -> atom_to_binary(A)
       end)/binary,
      (case B of
           %% The JIT would do an unsafe simplification of the is_binary/1 test,
           %% accepting any boxed term (such as a bignum) as a binary.
           Y when is_binary(Y) -> Y;
           _ -> integer_to_binary(B)
       end)/binary>>.

%% 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}) ->
    ResN = rpc:call(Node, M, F, A),
    Res = apply(M, F, A),
    case ResN =:= Res of
        true ->
            ok;
        false ->
            io:format("~p: ~p~n~p: ~p~n",
                      [Node, ResN, node(), Res]),
            ct:fail(different_results)
    end.

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 = <<Bin0/binary,1:1>>,
    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 = <<F:Offs,Bin0/binary,F:Roffs>>,
    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, [], <<X0:X1>>, <<X2:X3>>, <<X4:X5>>];
		 2 ->
		     [Acc, [[[[X0|<<X1:X2>>],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]).

local_ext(Config) when is_list(Config) ->
    SDrv = send_term_local_drv,
    CDrv = call_local_drv,
    DataDir = proplists:get_value(data_dir, Config),
    PrivDir = proplists:get_value(priv_dir, Config),
    FileName = filename:join(PrivDir, "local_ext.data"),
    Args = ["-setcookie", atom_to_list(erlang:get_cookie()),
            "-pa", filename:dirname(code:which(?MODULE))],
    {ok, Peer1, _} = peer:start_link(#{connection => 0, args => Args}),
    {ok, Peer2, _} = peer:start_link(#{connection => 0, args => Args}),
    LongNames = net_kernel:longnames(),
    DynStartOpts = #{name_domain => if LongNames -> longnames;
                                       true -> shortnames
                                    end},
    ExternalPid = self(),
    ExternalRef = make_ref(),
    ExternalPort = hd(erlang:ports()),
    EncDecLocal = fun () ->
                          erl_ddll:start(),
                          ok = erl_ddll:load_driver(DataDir, SDrv),
                          SPort = open_port({spawn, SDrv}, []),
                          ok = erl_ddll:load_driver(DataDir, CDrv),
                          CPort = open_port({spawn, CDrv}, []),
                          false = erlang:is_alive(),
                          nonode@nohost = node(),
                          LocalPid = self(),
                          LocalRef = make_ref(),
                          LocalPort = hd(erlang:ports()),
                          Bin1 = <<4711:800>>,
                          Bin2 = <<4711:703>>,
                          Bin3 = <<4711:600>>,
                          Terms = [
                                   LocalPid,
                                   ExternalPid,
                                   LocalRef,
                                   ExternalRef,
                                   LocalPort,
                                   ExternalPort,
                                   [LocalPid, Bin1, ExternalPid, LocalRef, Bin2,
                                    ExternalRef, Bin3, Bin2, LocalPort,
                                    ExternalPort],
                                   "hej",
                                   [],
                                   {processes(), Bin3, erlang:ports(), Bin3},
                                   #{pid => LocalPid, ref => LocalRef, port => LocalPort}
                                  ],
                          {ok, FD} = file:open(FileName, [write]),
                          ETs = lists:map(fun (Term) ->
                                                  {enc_local(FD, Term), Term}
                                          end, Terms),
                          ok = file:close(FD),
                          CheckET = fun ({LExt, Term}) ->
                                            Term = binary_to_term(LExt),
                                            SPort ! {self(), {command, LExt}},
                                            receive
                                                {SPort, Reply} ->
                                                    Term = Reply
                                            end
                                    end,
                          lists:foreach(CheckET, ETs),
                          call_local_success(CPort, ETs),
                          NodeName = peer:random_name(),
                          {ok, _} = net_kernel:start(list_to_atom(NodeName),
                                                     DynStartOpts),
                          true = erlang:is_alive(),
                          true = nonode@nohost /= node(),
                          lists:foreach(CheckET, ETs),
                          call_local_success(CPort, ETs),
                          ok = net_kernel:stop(),
                          false = erlang:is_alive(),
                          nonode@nohost = node(),
                          lists:foreach(CheckET, ETs),
                          call_local_success(CPort, ETs),
                          {ok, ExtList} = file:consult(FileName),
                          lists:foreach(fun (Ext) when is_binary(Ext) ->
                                                _ = binary_to_term(Ext),
                                                SPort ! {self(), {command, Ext}},
                                                receive
                                                    {SPort, "bad_term_error"} ->
                                                        error(bad_term_error);
                                                    {SPort, _} ->
                                                        ok
                                                end
                                        end,
                                        ExtList),
                          true = port_close(SPort),
                          true = port_close(CPort),
                          ok
                  end,
    ok = peer:call(Peer1, erlang, apply, [EncDecLocal, []]),
    DecOthersLocal = fun () ->
                             %% Verify that decoding of the terms encoded
                             %% on local external format by the other runtime
                             %% system instance fails on this runtime system
                             %% instance...
                             erl_ddll:start(),
                             ok = erl_ddll:load_driver(DataDir, SDrv),
                             SPort = open_port({spawn, SDrv}, []),
                             ok = erl_ddll:load_driver(DataDir, CDrv),
                             CPort = open_port({spawn, CDrv}, []),
                             false = erlang:is_alive(),
                             nonode@nohost = node(),
                             {ok, ExtList} = file:consult(FileName),
                             lists:foreach(fun (Ext) when is_binary(Ext) ->
                                                   try
                                                       Term = binary_to_term(Ext),
                                                       error({successful_decode, Term})
                                                   catch
                                                       error:badarg ->
                                                           ok
                                                   end,
                                                   SPort ! {self(), {command, Ext}},
                                                   receive
                                                       {SPort, Reply} ->
                                                           "bad_term_error" = Reply
                                                   end
                                           end,
                                           ExtList),
                             call_local_fail(CPort, ExtList),
                             true = port_close(SPort),
                             true = port_close(CPort),
                             ok
                     end,
    ok = peer:call(Peer2, erlang, apply, [DecOthersLocal, []]),
    peer:stop(Peer1),
    peer:stop(Peer2),
    ok.

enc_local(FD, Term) ->
    Ext = term_to_binary(Term, [local]),
    Ext = iolist_to_binary(term_to_iovec(Term, [local])),
    Term = binary_to_term(Ext),
    io:format(FD, "~p.~n", [Ext]),
    Ext.

call_local_success(Port, []) ->
    ok;
call_local_success(Port, [{Lext1, T1}]) ->
    Me = self(),
    Ref = make_ref(),
    Term =  {term_to_binary(Me), Lext1, term_to_binary(Ref)},
    {call_result, Me, 4711, T1, 17, Ref, "end_of_data"} = erlang:port_call(Port, 0, Term),
    ok;
call_local_success(Port, [{Lext1, T1}, {Lext3, T3} | Rest]) ->
    Me = self(),
    Term =  {Lext1, term_to_binary(Me), Lext3},
    {call_result, T1, 4711, Me, 17, T3, "end_of_data"} = erlang:port_call(Port, 0, Term),
    call_local_success(Port, Rest).

call_local_fail(Port, []) ->
    ok;
call_local_fail(Port, [Lext1]) ->
    Me = self(),
    Ref = make_ref(),
    Term =  {term_to_binary(Me), Lext1, term_to_binary(Ref)},
    try
        erlang:port_call(Port, 0, Term),
        error(unexpected_port_call_success)
    catch
        error:badarg ->
            ok
    end;
call_local_fail(Port, [Lext1, Lext3 | Rest]) ->
    Me = self(),
    Term =  {Lext1, term_to_binary(Me), Lext3},
    try
        erlang:port_call(Port, 0, Term),
        error(unexpected_port_call_success)
    catch
        error:badarg ->
            ok
    end,
    call_local_fail(Port, Rest).
