1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(bs_construct_SUITE). 22 23-export([all/0, suite/0, 24 init_per_suite/1, end_per_suite/1, 25 test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, 26 not_used/1, in_guard/1, 27 mem_leak/1, coerce_to_float/1, bjorn/1, append_empty_is_same/1, 28 huge_float_field/1, system_limit/1, badarg/1, 29 copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1, 30 otp_7422/1, zero_width/1, bad_append/1, bs_append_overflow/1, 31 reductions/1]). 32 33-include_lib("common_test/include/ct.hrl"). 34 35suite() -> 36 [{ct_hooks,[ts_install_cth]}, 37 {timetrap, {minutes, 1}}]. 38 39all() -> 40 [test1, test2, test3, test4, test5, testf, not_used, 41 in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same, 42 huge_float_field, system_limit, badarg, 43 copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, 44 bad_append, bs_append_overflow, reductions]. 45 46init_per_suite(Config) -> 47 Config. 48 49end_per_suite(_Config) -> 50 application:stop(os_mon). 51 52big(1) -> 53 57285702734876389752897683. 54 55i(X) -> X. 56 57r(L) -> 58 lists:reverse(L). 59 60-define(T(B, L), {B, ??B, L}). 61-define(N(B), {B, ??B, unknown}). 62 63-define(FAIL(Expr), fail_check(catch Expr, ??Expr, [])). 64 65-define(FAIL_VARS(Expr, Vars), fail_check(catch Expr, ??Expr, Vars)). 66 67l(I_13, I_big1) -> 68 [ 69 ?T(<<-43>>, 70 [256-43]), 71 ?T(<<56>>, 72 [56]), 73 ?T(<<1,2>>, 74 [1, 2]), 75 ?T(<<4:4, 7:4>>, 76 [4*16+7]), 77 ?T(<<777:16/big>>, 78 [3, 9]), 79 ?T(<<777:16/little>>, 80 [9, 3]), 81 ?T(<<0.0:32/float>>, 82 [0,0,0,0]), 83 ?T(<<0.125:32/float>>, 84 [62,0,0,0]), 85 ?T(<<0.125:32/little-float>>, 86 [0,0,0,62]), 87 ?T(<<I_big1:32>>, 88 [138, 99, 0, 147]), 89 ?T(<<57285702734876389752897684:32>>, 90 [138, 99, 0, 148]), 91 ?T(<<I_big1:32/little>>, 92 r([138, 99, 0, 147])), 93 ?T(<<-1:17/unit:8>>, 94 lists:duplicate(17, 255)), 95 96 ?T(<<I_13>>, 97 [13]), 98 99 ?T(<<4:8/unit:2,5:2/unit:8>>, 100 [0, 4, 0, 5]), 101 102 ?T(<<1:1, 0:6, 1:1>>, 103 [129]), 104 ?T(<<1:1/little, 0:6/little, 1:1/little>>, 105 [129]), 106 107 ?T(<<<<1,2>>/binary>>, 108 [1, 2]), 109 ?T(<<<<1,2>>:1/binary>>, 110 [1]), 111 ?T(<<4,3,<<1,2>>:1/binary>>, 112 [4,3,1]), 113 114 ?T(<<(256*45+47)>>, 115 [47]), 116 117 ?T(<<57:0>>, 118 []), 119 120 ?T(<<"apa">>, 121 "apa"), 122 123 ?T(<<1:3,"string",9:5>>, 124 [46,110,142,77,45,204,233]), 125 126 ?T(<<>>, 127 []), 128 129 ?T(<<37.98:64/native-float>>, 130 native_3798()), 131 132 ?T(<<32978297842987249827298387697777669766334937:128/native-integer>>, 133 native_bignum()), 134 135 %% Unit tests. 136 ?T(<<<<5:3>>/bitstring>>, <<5:3>>), 137 ?T(<<42,<<7:4>>/binary-unit:4>>, <<42,7:4>>), 138 ?T(<<<<344:17>>/binary-unit:17>>, <<344:17>>), 139 ?T(<<<<42,3,7656:16>>/binary-unit:16>>, <<42,3,7656:16>>) 140 141 ]. 142 143native_3798() -> 144 case <<1:16/native>> of 145 <<0,1>> -> [64,66,253,112,163,215,10,61]; 146 <<1,0>> -> [61,10,215,163,112,253,66,64] 147 end. 148 149native_bignum() -> 150 case <<1:16/native>> of 151 <<0,1>> -> [129,205,18,177,1,213,170,101,39,231,109,128,176,11,73,217]; 152 <<1,0>> -> [217,73,11,176,128,109,231,39,101,170,213,1,177,18,205,129] 153 end. 154 155evaluate(Str, Vars) -> 156 {ok,Tokens,_} = 157 erl_scan:string(Str ++ " . "), 158 {ok, [Expr]} = erl_parse:parse_exprs(Tokens), 159 case erl_eval:expr(Expr, Vars) of 160 {value, Result, _} -> 161 Result 162 end. 163 164eval_list([], _Vars) -> 165 []; 166eval_list([{C_bin, Str, Bytes} | Rest], Vars) -> 167 case catch evaluate(Str, Vars) of 168 {'EXIT', Error} -> 169 io:format("Evaluation error: ~p, ~p, ~p~n", [Str, Vars, Error]), 170 exit(Error); 171 E_bin -> 172 [{C_bin, E_bin, Str, Bytes} | eval_list(Rest, Vars)] 173 end. 174 175one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) -> 176 io:format(" ~s, ~p~n", [Str, Bytes]), 177 Bin = list_to_binary(Bytes), 178 if 179 C_bin == Bin -> 180 ok; 181 true -> 182 io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n", 183 [Str, Bytes, binary_to_list(C_bin)]), 184 ct:fail(comp) 185 end, 186 if 187 E_bin == Bin -> 188 ok; 189 true -> 190 io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n", 191 [Str, Bytes, binary_to_list(E_bin)]), 192 ct:fail(comp) 193 end; 194one_test({C_bin, E_bin, Str, Result}) -> 195 io:format(" ~s ~p~n", [Str, C_bin]), 196 if 197 C_bin == E_bin -> 198 ok; 199 true -> 200 Arbitrary = case Result of 201 unknown -> 202 size(C_bin); 203 _ -> 204 Result 205 end, 206 case equal_lists(binary_to_list(C_bin), 207 binary_to_list(E_bin), 208 Arbitrary) of 209 false -> 210 io:format("ERROR: Compiled not equal to interpreted:" 211 "~n ~p, ~p.~n", 212 [binary_to_list(C_bin), binary_to_list(E_bin)]), 213 ct:fail(comp); 214 0 -> 215 ok; 216 %% For situations where the final bits may not matter, like 217 %% for floats: 218 N when is_integer(N) -> 219 io:format("Info: compiled and interpreted differ in the" 220 " last bytes:~n ~p, ~p.~n", 221 [binary_to_list(C_bin), binary_to_list(E_bin)]), 222 ok 223 end 224 end. 225 226equal_lists([], [], _) -> 227 0; 228equal_lists([], _, _) -> 229 false; 230equal_lists(_, [], _) -> 231 false; 232equal_lists([A|AR], [A|BR], R) -> 233 equal_lists(AR, BR, R); 234equal_lists(A, B, R) -> 235 if 236 length(A) /= length(B) -> 237 false; 238 length(A) =< R -> 239 R; 240 true -> 241 false 242 end. 243 244fail_check({'EXIT',{badarg,_}}, Str, Vars) -> 245 try evaluate(Str, Vars) of 246 Res -> 247 io:format("Interpreted result: ~p", [Res]), 248 ct:fail(did_not_fail_in_intepreted_code) 249 catch 250 error:badarg -> 251 ok 252 end; 253fail_check(Res, _, _) -> 254 io:format("Compiled result: ~p", [Res]), 255 ct:fail(did_not_fail_in_compiled_code). 256 257%%% Simple working cases 258test1(Config) when is_list(Config) -> 259 I_13 = i(13), 260 I_big1 = big(1), 261 Vars = [{'I_13', I_13}, 262 {'I_big1', I_big1}], 263 lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)). 264 265%%% Misc 266 267%%% <<A:S, A:(N-S)>> 268comp(N, A, S) -> 269 M1 = (1 bsl S) - 1, 270 M2 = (1 bsl (N-S)) - 1, 271 [((A band M1) bsl (N-S)) bor (A band M2)]. 272 273gen(N, S, A) -> 274 [?T(<<A:S, A:(N-S)>>, comp(N, A, S))]. 275 276gen_l(N, S, A) -> 277 [?T(<<A:S/little, A:(N-S)/little>>, comp(N, A, S))]. 278 279test2(Config) when is_list(Config) -> 280 test2(0, 8, 2#10101010101010101), 281 test2(0, 8, 2#1111111111). 282 283test2(End, End, _) -> 284 ok; 285test2(I, End, A) -> 286 test2(I, A), 287 test2(I+1, End, A). 288 289test2(S, A) -> 290 N = 8, 291 Vars = [{'A',A}, {'N',N}, {'S',S}], 292 io:format("Vars: ~p\n", [Vars]), 293 lists:foreach(fun one_test/1, eval_list(gen(N, S, A), Vars)), 294 lists:foreach(fun one_test/1, eval_list(gen_l(N, S, A), Vars)). 295 296%%% Tests without facit 297 298t3() -> 299 [?N(<<4711:13, 9876:13, 3:6>>), 300 ?N(<<4.57:64/float>>), 301 ?N(<<4.57:32/float>>), 302 303 ?N(<<>>) 304 ]. 305 306test3(Config) when is_list(Config) -> 307 Vars = [], 308 lists:foreach(fun one_test/1, eval_list(t3(), Vars)). 309 310gen_u(N, S, A) -> 311 [?N(<<A:S, A:(N-S)>>)]. 312 313gen_u_l(N, S, A) -> 314 [?N(<<A:S/little, A:(N-S)/little>>)]. 315 316test4(Config) when is_list(Config) -> 317 test4(0, 16, 2#10101010101010101), 318 test4(0, 16, 2#1111111111). 319 320test4(End, End, _) -> 321 ok; 322test4(I, End, A) -> 323 test4(I, A), 324 test4(I+1, End, A). 325 326test4(S, A) -> 327 N = 16, 328 Vars = [{'A', A}, {'N', 16}, {'S', S}], 329 lists:foreach(fun one_test/1, eval_list(gen_u(N, S, A), Vars)), 330 lists:foreach(fun one_test/1, eval_list(gen_u_l(N, S, A), Vars)). 331 332gen_b(N, S, A) -> 333 [?T(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>, 334 binary_to_list(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>))]. 335 336%% OTP-3995 337test5(Config) when is_list(Config) -> 338 test5(0, 8, <<73>>), 339 test5(0, 8, <<68>>). 340 341test5(End, End, _) -> 342 ok; 343test5(I, End, A) -> 344 test5(I, A), 345 test5(I+1, End, A). 346 347test5(S, A) -> 348 N = 8, 349 Vars = [{'A', A}, {'N', 8}, {'S', S}], 350 lists:foreach(fun one_test/1, eval_list(gen_b(N, S, A), Vars)). 351 352%%% Failure cases 353testf(Config) when is_list(Config) -> 354 ?FAIL(<<3.14>>), 355 ?FAIL(<<<<1,2>>>>), 356 357 ?FAIL(<<2.71/binary>>), 358 ?FAIL(<<24334/binary>>), 359 ?FAIL(<<24334344294788947129487129487219847/binary>>), 360 BigInt = id(24334344294788947129487129487219847), 361 ?FAIL_VARS(<<BigInt/binary>>, [{'BigInt',BigInt}]), 362 ?FAIL_VARS(<<42,BigInt/binary>>, [{'BigInt',BigInt}]), 363 ?FAIL_VARS(<<BigInt:2/binary>>, [{'BigInt',BigInt}]), 364 365 %% One negative field size, but the sum of field sizes will be 1 byte. 366 %% Make sure that we reject that properly. 367 I_minus_777 = id(-777), 368 I_minus_2047 = id(-2047), 369 ?FAIL_VARS(<<I_minus_777:2048/unit:8,57:I_minus_2047/unit:8>>, 370 ordsets:from_list([{'I_minus_777',I_minus_777}, 371 {'I_minus_2047',I_minus_2047}])), 372 ?FAIL(<<<<1,2,3>>/float>>), 373 374 %% Negative field widths. 375 testf_1(-8, <<1,2,3,4,5>>), 376 ?FAIL(<<0:(-(1 bsl 100))>>), 377 378 ?FAIL(<<42:(-16)>>), 379 ?FAIL(<<3.14:(-8)/float>>), 380 ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>), 381 ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>), 382 ?FAIL(<<<<23,56,0,2>>:(anka)>>), 383 ?FAIL(<<<<23,56,0,2>>:(anka)>>), 384 385 %% Unit failures. 386 ?FAIL(<<<<1:1>>/binary>>), 387 Sz = id(1), 388 ?FAIL_VARS(<<<<1:Sz>>/binary>>, [{'Sz',Sz}]), 389 {'EXIT',{badarg,_}} = (catch <<<<1:(id(1))>>/binary>>), 390 ?FAIL(<<<<7,8,9>>/binary-unit:16>>), 391 ?FAIL(<<<<7,8,9,3:7>>/binary-unit:16>>), 392 ?FAIL(<<<<7,8,9,3:7>>/binary-unit:17>>), 393 394 ok. 395 396testf_1(W, B) -> 397 Vars = [{'W',W}], 398 ?FAIL_VARS(<<42:W>>, Vars), 399 ?FAIL_VARS(<<3.14:W/float>>, Vars), 400 ?FAIL_VARS(<<B:W/binary>>, [{'B',B}|Vars]). 401 402%% Test that constructed binaries that are not used will still give an exception. 403not_used(Config) when is_list(Config) -> 404 ok = not_used1(3, <<"dum">>), 405 {'EXIT',{badarg,_}} = (catch not_used1(3, "dum")), 406 {'EXIT',{badarg,_}} = (catch not_used2(444, -2)), 407 {'EXIT',{badarg,_}} = (catch not_used2(444, anka)), 408 {'EXIT',{badarg,_}} = (catch not_used3(444)), 409 ok. 410 411not_used1(I, BinString) -> 412 <<I:32,BinString/binary>>, 413 ok. 414 415not_used2(I, Sz) -> 416 <<I:Sz>>, 417 ok. 418 419not_used3(I) -> 420 <<I:(-8)>>, 421 ok. 422 423in_guard(Config) when is_list(Config) -> 424 1 = in_guard(<<16#74ad:16>>, 16#e95, 5), 425 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), 426 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), 427 3 = in_guard(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), 428 3 = in_guard(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), 429 nope = in_guard(<<1>>, 42, b), 430 nope = in_guard(<<1>>, a, b), 431 nope = in_guard(<<1,2>>, 1, 1), 432 nope = in_guard(<<4,5>>, 1, 2.71), 433 nope = in_guard(<<4,5>>, 1, <<12,13>>), 434 ok. 435 436in_guard(Bin, A, B) when <<A:13,B:3>> == Bin -> 1; 437in_guard(Bin, A, B) when <<A:16,B/binary>> == Bin -> 2; 438in_guard(Bin, A, B) when <<A:14,B/float,3:2>> == Bin -> 3; 439in_guard(Bin, A, B) when {a,b,<<A:14,B/float,3:2>>} == Bin -> cant_happen; 440in_guard(_, _, _) -> nope. 441 442%% Make sure that construction has no memory leak 443mem_leak(Config) when is_list(Config) -> 444 B = make_bin(16, <<0>>), 445 mem_leak(1024, B), 446 ok. 447 448mem_leak(0, _) -> ok; 449mem_leak(N, B) -> 450 big_bin(B, <<23>>), 451 {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), 452 mem_leak(N-1, B). 453 454big_bin(B1, B2) -> 455 <<B1/binary,B1/binary,B1/binary,B1/binary, 456 B1/binary,B1/binary,B1/binary,B1/binary, 457 B1/binary,B1/binary,B1/binary,B1/binary, 458 B1/binary,B1/binary,B1/binary,B1/binary, 459 B2/binary>>. 460 461make_bin(0, Acc) -> Acc; 462make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>). 463 464-define(COF(Int0), 465 (fun(Int) -> 466 true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, 467 true = <<Int:64/float>> =:= <<(float(Int)):64/float>> 468 end)(nonliteral(Int0)), 469 true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>, 470 true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). 471 472-define(COF64(Int0), 473 (fun(Int) -> 474 true = <<Int:64/float>> =:= <<(float(Int)):64/float>> 475 end)(nonliteral(Int0)), 476 true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). 477 478nonliteral(X) -> X. 479 480coerce_to_float(Config) when is_list(Config) -> 481 ?COF(0), 482 ?COF(-1), 483 ?COF(1), 484 ?COF(42), 485 ?COF(255), 486 ?COF(-255), 487 ?COF(38474), 488 ?COF(387498738948729893849444444443), 489 ?COF(-37489378937773899999999999999993), 490 ?COF64(298748888888888888888888888883478264866528467367364766666666666666663), 491 ?COF64(-367546729879999999999947826486652846736736476555566666663), 492 ok. 493 494bjorn(Config) when is_list(Config) -> 495 error = bjorn_1(), 496 ok. 497 498bjorn_1() -> 499 Bitstr = <<7:13>>, 500 try 501 do_something() 502 catch 503 throw:blurf -> 504 ignore 505 end, 506 do_more(Bitstr, 13). 507 508do_more(Bin, Sz) -> 509 %% Previous bug in the bs_bits_to_bytes instruction: The exeption code 510 %% was not set - the previous exception (throw:blurf) would be used, 511 %% causing the catch to slip. 512 try <<Bin:Sz/binary>> of 513 _V -> ok 514 catch 515 error:_ -> 516 error 517 end. 518 519do_something() -> 520 throw(blurf). 521 522append_empty_is_same(Config) when is_list(Config) -> 523 NonWritableBin = <<"123">>, 524 true = erts_debug:same(NonWritableBin, append(NonWritableBin, <<>>)), 525 WritableBin = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>, 526 true = erts_debug:same(WritableBin, append(WritableBin, <<>>)), 527 ok. 528 529append(A, B) -> 530 <<A/binary, B/binary>>. 531 532huge_float_field(Config) when is_list(Config) -> 533 {'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>), 534 huge_float_check(catch <<0.0:67108865/float-unit:64>>), 535 huge_float_check(catch <<0.0:((1 bsl 26)+1)/float-unit:64>>), 536 huge_float_check(catch <<0.0:(id(67108865))/float-unit:64>>), 537%% huge_float_check(catch <<0.0:((1 bsl 60)+1)/float-unit:64>>), 538 huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 26)+1)/float-unit:64>>), 539%% huge_float_check(catch <<3839739387439387383739387987347983:((1 bsl 60)+1)/float-unit:64>>), 540 ok. 541 542huge_float_check({'EXIT',{system_limit,_}}) -> ok; 543huge_float_check({'EXIT',{badarg,_}}) -> ok. 544 545system_limit(Config) when is_list(Config) -> 546 WordSize = erlang:system_info(wordsize), 547 BitsPerWord = WordSize * 8, 548 {'EXIT',{system_limit,_}} = 549 (catch <<0:(id(0)),42:(id(1 bsl BitsPerWord))>>), 550 {'EXIT',{system_limit,_}} = 551 (catch <<42:(id(1 bsl BitsPerWord)),0:(id(0))>>), 552 {'EXIT',{system_limit,_}} = 553 (catch <<(id(<<>>))/binary,0:(id(1 bsl 100))>>), 554 555 %% Would fail to load. 556 {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 67)>>), 557 {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 64)+1)>>), 558 559 case WordSize of 560 4 -> 561 system_limit_32(); 562 8 -> 563 ok 564 end. 565 566system_limit_32() -> 567 {'EXIT',{badarg,_}} = (catch <<42:(-1)>>), 568 {'EXIT',{badarg,_}} = (catch <<42:(id(-1))>>), 569 {'EXIT',{badarg,_}} = (catch <<42:(id(-389739873536870912))/unit:8>>), 570 {'EXIT',{system_limit,_}} = (catch <<42:536870912/unit:8>>), 571 {'EXIT',{system_limit,_}} = (catch <<42:(id(536870912))/unit:8>>), 572 {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:536870912/unit:8>>), 573 {'EXIT',{system_limit,_}} = (catch <<0:(id(8)),42:(id(536870912))/unit:8>>), 574 575 %% The size would be silently truncated, resulting in a crash. 576 {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 35)>>), 577 {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 32)+1)>>), 578 579 %% Would fail to load. 580 {'EXIT',{system_limit,_}} = (catch <<0:(1 bsl 43)>>), 581 {'EXIT',{system_limit,_}} = (catch <<0:((1 bsl 40)+1)>>), 582 ok. 583 584badarg(Config) when is_list(Config) -> 585 {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-1))>>), 586 {'EXIT',{badarg,_}} = (catch <<0:(id(1 bsl 100)),0:(id(-(1 bsl 70)))>>), 587 {'EXIT',{badarg,_}} = (catch <<0:(id(-(1 bsl 70))),0:(id(1 bsl 100))>>), 588 {'EXIT',{badarg,_}} = (catch <<(id(<<>>))/binary,0:(id(-(1 bsl 100)))>>), 589 ok. 590 591copy_writable_binary(Config) when is_list(Config) -> 592 [copy_writable_binary_1(I) || I <- lists:seq(0, 256)], 593 ok. 594 595copy_writable_binary_1(_) -> 596 Bin0 = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>, 597 SubBin = make_sub_bin(Bin0), 598 id(<<42,34,55,Bin0/binary>>), %Make reallocation likelier. 599 Pid = spawn(fun() -> 600 copy_writable_binary_holder(Bin0, SubBin) 601 end), 602 Tab = ets:new(holder, []), 603 ets:insert(Tab, {17,Bin0}), 604 ets:insert(Tab, {42,SubBin}), 605 id(<<Bin0/binary,0:(64*1024*8)>>), 606 Pid ! self(), 607 [{17,Bin0}] = ets:lookup(Tab, 17), 608 [{42,Bin0}] = ets:lookup(Tab, 42), 609 receive 610 {Pid,Bin0,Bin0} -> ok; 611 Other -> 612 ct:fail("Unexpected message: ~p", [Other]) 613 end, 614 ok. 615 616copy_writable_binary_holder(Bin, SubBin) -> 617 receive 618 Pid -> 619 Pid ! {self(),Bin,SubBin} 620 end. 621 622make_sub_bin(Bin0) -> 623 N = bit_size(Bin0), 624 <<_:17,Bin:N/bitstring,_:5>> = <<(-1):17,Bin0/bitstring,(-1):5>>, 625 Bin = Bin0, %Assertion. 626 Bin. 627 628%% Make sure that bit syntax expression with huge field size are 629%% not constructed at compile time. 630 631kostis(Config) when is_list(Config) -> 632 case have_250_terabytes_of_ram() of 633 true -> 634 Bin = <<0:800000000000>>, 635 EmbeddedBin = <<0,(<<0:99999999999>>)/bitstring,1>>, 636 Bin0 = list_to_binary([Bin,Bin,Bin,Bin,Bin]), 637 Bin1 = list_to_binary([Bin0,Bin0,Bin0,Bin0,Bin0,Bin0]), 638 Bin2 = list_to_binary([Bin1,Bin1]), 639 id({EmbeddedBin,Bin0,Bin1,Bin2}); 640 false -> 641 ok 642 end. 643 644%% I'm not even certain how much 250 TB really is... 645%% but I'm sure I don't have it :-) 646 647have_250_terabytes_of_ram() -> false. 648 649%% Test that different ways of using bit syntax instructions 650%% give the same result. 651 652dynamic(Config) when is_list(Config) -> 653 dynamic_1(fun dynamic_big/5), 654 dynamic_1(fun dynamic_little/5), 655 ok. 656 657dynamic_1(Dynamic) -> 658 <<Lpad:128>> = erlang:md5([0]), 659 <<Rpad:128>> = erlang:md5([1]), 660 <<Int:128>> = erlang:md5([2]), 661 8385 = dynamic_2(0, {Int,Lpad,Rpad,Dynamic}, 0). 662 663dynamic_2(129, _, Count) -> Count; 664dynamic_2(Bef, Data, Count0) -> 665 Count = dynamic_3(Bef, 128-Bef, Data, Count0), 666 dynamic_2(Bef+1, Data, Count). 667 668dynamic_3(_, -1, _, Count) -> Count; 669dynamic_3(Bef, N, {Int0,Lpad,Rpad,Dynamic}=Data, Count) -> 670 Int1 = Int0 band ((1 bsl (N+3))-1), 671 Dynamic(Bef, N, Int1, Lpad, Rpad), 672 Dynamic(Bef, N, -Int1, Lpad, Rpad), 673 674 %% OTP-7085: Test a small number in a wide field. 675 Int2 = Int0 band 16#FFFFFF, 676 Dynamic(Bef, N, Int2, Lpad, Rpad), 677 Dynamic(Bef, N, -Int2, Lpad, Rpad), 678 dynamic_3(Bef, N-1, Data, Count+1). 679 680dynamic_big(Bef, N, Int, Lpad, Rpad) -> 681 NumBin = id(<<Int:N>>), 682 MaskedInt = Int band ((1 bsl N) - 1), 683 <<MaskedInt:N>> = NumBin, 684 685 %% Construct the binary in two different ways. 686 Bin = id(<<Lpad:Bef,NumBin/bitstring,Rpad:(128-Bef-N)>>), 687 Bin = <<Lpad:Bef,Int:N,Rpad:(128-Bef-N)>>, 688 689 %% Further verify the result by matching. 690 LpadMasked = Lpad band ((1 bsl Bef) - 1), 691 RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1), 692 Rbits = (128-Bef-N), 693 <<LpadMasked:Bef,MaskedInt:N,RpadMasked:Rbits>> = id(Bin), 694 ok. 695 696dynamic_little(Bef, N, Int, Lpad, Rpad) -> 697 NumBin = id(<<Int:N/little>>), 698 MaskedInt = Int band ((1 bsl N) - 1), 699 <<MaskedInt:N/little>> = NumBin, 700 701 %% Construct the binary in two different ways. 702 Bin = id(<<Lpad:Bef/little,NumBin/bitstring,Rpad:(128-Bef-N)/little>>), 703 Bin = <<Lpad:Bef/little,Int:N/little,Rpad:(128-Bef-N)/little>>, 704 705 %% Further verify the result by matching. 706 LpadMasked = Lpad band ((1 bsl Bef) - 1), 707 RpadMasked = Rpad band ((1 bsl (128-Bef-N)) - 1), 708 Rbits = (128-Bef-N), 709 <<LpadMasked:Bef/little,MaskedInt:N/little,RpadMasked:Rbits/little>> = id(Bin), 710 ok. 711 712%% Test that the bs_add/5 instruction handles big numbers correctly. 713bs_add(Config) when is_list(Config) -> 714 Mod = bs_construct_bs_add, 715 N = 2000, 716 Code = [{module, Mod}, 717 {exports, [{bs_add,2}]}, 718 {labels, 2}, 719 720 %% bs_add(Number, -SmallestBig) -> Number + N 721 {function, bs_add, 2, 2}, 722 {label,1}, 723 {func_info,{atom,Mod},{atom,bs_add},2}, 724 725 {label,2}, 726 {move,{x,0},{x,2}}] ++ 727 lists:duplicate(N-1, {bs_add,{f,0},[{x,2},{integer,1},1],{x,2}}) ++ 728 [{gc_bif,abs,{f,0},3,[{x,1}],{x,4}}, %Force GC, ignore result. 729 {gc_bif,'+',{f,0},3,[{x,2},{integer,1}],{x,0}}, %Safe result in {x,0} 730 return], 731 732 %% Write assembly file and assemble it. 733 PrivDir = proplists:get_value(priv_dir, Config), 734 RootName = filename:join(PrivDir, atom_to_list(Mod)), 735 AsmFile = RootName ++ ".S", 736 {ok,Fd} = file:open(AsmFile, [write]), 737 [io:format(Fd, "~p. \n", [T]) || T <- Code], 738 ok = file:close(Fd), 739 {ok,Mod} = compile:file(AsmFile, [from_asm,report,{outdir,PrivDir}]), 740 LoadRc = code:load_abs(RootName), 741 {module,_Module} = LoadRc, 742 743 %% Find smallest positive bignum. 744 SmallestBig = smallest_big(), 745 io:format("~p\n", [SmallestBig]), 746 Expected = SmallestBig + N, 747 DoTest = fun() -> 748 exit(Mod:bs_add(SmallestBig, -SmallestBig)) 749 end, 750 {Pid,Mref} = spawn_monitor(DoTest), 751 receive 752 {'DOWN',Mref,process,Pid,Res} -> ok 753 end, 754 Expected = Res, 755 756 %% Clean up. 757 ok = file:delete(AsmFile), 758 ok = file:delete(code:which(Mod)), 759 ok. 760 761 762smallest_big() -> 763 smallest_big_1(1 bsl 24). 764 765smallest_big_1(N) -> 766 case erts_debug:flat_size(N) of 767 0 -> smallest_big_1(N+N); 768 _ -> N 769 end. 770 771otp_7422(Config) when is_list(Config) -> 772 otp_7422_int(0), 773 otp_7422_bin(0). 774 775otp_7422_int(N) when N < 512 -> 776 T = erlang:make_tuple(N, []), 777 spawn_link(fun() -> 778 id(T), 779 %% A size of field 0 would write one byte beyond 780 %% the current position in the binary. It could 781 %% overwrite the continuation pointer stored on 782 %% the stack if HTOP was equal to E (the stack pointer). 783 id(<<0:(id(0))>>) 784 end), 785 otp_7422_int(N+1); 786otp_7422_int(_) -> ok. 787 788otp_7422_bin(N) when N < 512 -> 789 T = erlang:make_tuple(N, []), 790 Z = id(<<>>), 791 spawn_link(fun() -> 792 id(T), 793 id(<<Z:(id(0))/bits>>) 794 end), 795 otp_7422_bin(N+1); 796otp_7422_bin(_) -> ok. 797 798zero_width(Config) when is_list(Config) -> 799 Z = id(0), 800 Small = id(42), 801 Big = id(1 bsl 128), 802 <<>> = <<Small:Z>>, 803 <<>> = <<Small:0>>, 804 <<>> = <<Big:Z>>, 805 <<>> = <<Big:0>>, 806 807 {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), 808 {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>), 809 {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>), 810 811 ok. 812 813bad_append(_) -> 814 do_bad_append(<<127:1>>, fun append_unit_3/1), 815 do_bad_append(<<127:2>>, fun append_unit_3/1), 816 do_bad_append(<<127:17>>, fun append_unit_3/1), 817 818 do_bad_append(<<127:3>>, fun append_unit_4/1), 819 do_bad_append(<<127:5>>, fun append_unit_4/1), 820 do_bad_append(<<127:7>>, fun append_unit_4/1), 821 do_bad_append(<<127:199>>, fun append_unit_4/1), 822 823 do_bad_append(<<127:7>>, fun append_unit_8/1), 824 do_bad_append(<<127:9>>, fun append_unit_8/1), 825 826 do_bad_append(<<0:8>>, fun append_unit_16/1), 827 do_bad_append(<<0:15>>, fun append_unit_16/1), 828 do_bad_append(<<0:17>>, fun append_unit_16/1), 829 ok. 830 831do_bad_append(Bin0, Appender) -> 832 {'EXIT',{badarg,_}} = (catch Appender(Bin0)), 833 834 Bin1 = id(<<0:3,Bin0/bitstring>>), 835 <<_:3,Bin2/bitstring>> = Bin1, 836 {'EXIT',{badarg,_}} = (catch Appender(Bin2)), 837 838 %% Create a writable binary. 839 Empty = id(<<>>), 840 Bin3 = <<Empty/bitstring,Bin0/bitstring>>, 841 {'EXIT',{badarg,_}} = (catch Appender(Bin3)), 842 ok. 843 844append_unit_3(Bin) -> 845 <<Bin/binary-unit:3,0:1>>. 846 847append_unit_4(Bin) -> 848 <<Bin/binary-unit:4,0:1>>. 849 850append_unit_8(Bin) -> 851 <<Bin/binary,0:1>>. 852 853append_unit_16(Bin) -> 854 <<Bin/binary-unit:16,0:1>>. 855 856%% Test that the bs_append instruction will correctly check for 857%% overflow by producing a binary whose total size would exceed the 858%% maximum allowed size for a binary on a 32-bit computer. 859 860bs_append_overflow(_Config) -> 861 Memsize = memsize(), 862 io:format("Memsize = ~w Bytes~n", [Memsize]), 863 case erlang:system_info(wordsize) of 864 8 -> 865 %% Not possible to test on a 64-bit computer. 866 {skip, "64-bit architecture"}; 867 _ when Memsize < (2 bsl 30) -> 868 {skip, "Less than 2 GB of memory"}; 869 4 -> 870 {'EXIT', {system_limit, _}} = (catch bs_append_overflow_signed()), 871 erlang:garbage_collect(), 872 {'EXIT', {system_limit, _}} = (catch bs_append_overflow_unsigned()), 873 erlang:garbage_collect(), 874 ok 875 end. 876 877bs_append_overflow_signed() -> 878 %% Produce a large binary that, if cast to signed int, would 879 %% overflow into a negative number that fits a smallnum. 880 Large = <<0:((1 bsl 30)-1)>>, 881 <<Large/bits, Large/bits, Large/bits, Large/bits, 882 Large/bits, Large/bits, Large/bits, Large/bits, 883 Large/bits>>. 884 885bs_append_overflow_unsigned() -> 886 %% The following would succeed but would produce an incorrect result 887 %% where B =:= C! 888 A = <<0:((1 bsl 32)-8)>>, 889 B = <<2, 3>>, 890 C = <<A/binary,1,B/binary>>, 891 true = byte_size(B) < byte_size(C). 892 893reductions(_Config) -> 894 TwoMeg = <<0:(2_000*1024)/unit:8>>, 895 reds_at_least(2000, fun() -> <<0:8,TwoMeg/binary>> end), 896 reds_at_least(4000, fun() -> <<0:8,TwoMeg/binary,TwoMeg/binary>> end), 897 reds_at_least(1000, fun() -> <<0:8,TwoMeg:(1000*1024)/binary>> end), 898 899 %% Here we expect about 500 reductions in the bs_append 900 %% instruction for setting up a writable binary and about 2000 901 %% reductions in the bs_put_binary instruction for copying the 902 %% binary data. 903 reds_at_least(2500, fun() -> <<TwoMeg/binary,TwoMeg:(2000*1024)/binary>> end), 904 ok. 905 906reds_at_least(N, Fun) -> 907 receive after 1 -> ok end, 908 {reductions,Red0} = process_info(self(), reductions), 909 _ = Fun(), 910 {reductions,Red1} = process_info(self(), reductions), 911 Diff = Red1 - Red0, 912 io:format("Expected at least ~p; got ~p\n", [N,Diff]), 913 if 914 Diff >= N -> 915 ok; 916 Diff -> 917 ct:fail({expected,N,got,Diff}) 918 end. 919 920id(I) -> I. 921 922memsize() -> 923 application:ensure_all_started(os_mon), 924 {Tot,_Used,_} = memsup:get_memory_data(), 925 Tot. 926