%% %CopyrightBegin% %% %% Copyright Ericsson AB 2013. 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(map_SUITE). -export([all/0, suite/0, init_per_suite/1, end_per_suite/1]). -export([t_build_and_match_literals/1, t_build_and_match_literals_large/1, t_update_literals/1, t_update_literals_large/1, t_match_and_update_literals/1, t_match_and_update_literals_large/1, t_update_map_expressions/1, t_update_assoc/1, t_update_assoc_large/1, t_update_exact/1, t_update_exact_large/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_sequence_large/1, t_guard_update/1, t_guard_update_large/1, t_guard_receive/1, t_guard_receive_large/1, t_guard_fun/1, t_update_deep/1, t_list_comprehension/1, t_map_sort_literals/1, t_map_equal/1, t_map_compare/1, t_map_size/1, t_map_get/1, t_is_map/1, t_is_map_key/1, %% Specific Map BIFs t_bif_map_get/1, t_bif_map_find/1, t_bif_map_is_key/1, t_bif_map_keys/1, t_bif_map_merge/1, t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, t_bif_map_take/1, t_bif_map_take_large/1, t_bif_map_update/1, t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, t_bif_map_next/1, %% erlang t_erlang_hash/1, t_map_encode_decode/1, t_gc_rare_map_overflow/1, %% non specific BIF related t_bif_build_and_check/1, t_bif_merge_and_check/1, %% maps module not bifs t_maps_fold/1, t_maps_map/1, t_maps_size/1, t_maps_without/1, %% misc t_hashmap_balance/1, t_erts_internal_order/1, t_erts_internal_hash/1, t_pdict/1, t_ets/1, t_dets/1, t_tracing/1, t_hash_entropy/1, %% instruction-level tests t_has_map_fields/1, y_regs/1, badmap_17/1, %%Bugs t_large_unequal_bins_same_hash_bug/1]). -include_lib("stdlib/include/ms_transform.hrl"). -define(CHECK(Cond,Term), case (catch (Cond)) of true -> true; _ -> io:format("###### CHECK FAILED ######~nINPUT: ~p~n", [Term]), exit(Term) end). suite() -> []. all() -> [t_build_and_match_literals, t_build_and_match_literals_large, t_update_literals, t_update_literals_large, t_match_and_update_literals, t_match_and_update_literals_large, t_update_map_expressions, t_update_assoc, t_update_assoc_large, t_update_exact, t_update_exact_large, t_guard_bifs, t_guard_sequence, t_guard_sequence_large, t_guard_update, t_guard_update_large, t_guard_receive, t_guard_receive_large, t_guard_fun, t_list_comprehension, t_update_deep, t_map_equal, t_map_compare, t_map_sort_literals, %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, t_bif_map_keys, t_bif_map_merge, t_bif_map_new, t_bif_map_put, t_bif_map_remove, t_bif_map_take, t_bif_map_take_large, t_bif_map_update, t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, t_bif_map_next, %% erlang t_erlang_hash, t_map_encode_decode, t_gc_rare_map_overflow, t_map_size, t_map_get, t_is_map, %% non specific BIF related t_bif_build_and_check, t_bif_merge_and_check, %% maps module t_maps_fold, t_maps_map, t_maps_size, t_maps_without, %% Other functions t_hashmap_balance, t_erts_internal_order, t_erts_internal_hash, t_pdict, t_ets, t_tracing, t_hash_entropy, %% instruction-level tests t_has_map_fields, y_regs, badmap_17, %% Bugs t_large_unequal_bins_same_hash_bug]. 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. %% tests t_build_and_match_literals(Config) when is_list(Config) -> #{} = id(#{}), #{1:=a} = id(#{1=>a}), #{1:=a,2:=b} = id(#{1=>a,2=>b}), #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}), #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), #{[]:=a,42.0:=b,x:={x,y},[a,b]:=list} = id(#{[]=>a,42.0=>b,x=>{x,y},[a,b]=>list}), #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} = id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} = id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), %% error case %V = 32, %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. t_build_and_match_literals_large(Config) when is_list(Config) -> % normal non-repeating M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M0, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M0, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M0, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M0, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M0, 60 = map_size(M0), 60 = maps:size(M0), % with repeating M1 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 10=>na0,20=>nb0,30=>"nc0","40"=>"nd0",<<"50">>=>"ne0",{["00"]}=>"n10", 11=>na1,21=>nb1,31=>"nc1","41"=>"nd1",<<"51">>=>"ne1",{["01"]}=>"n11", 12=>na2,22=>nb2,32=>"nc2","42"=>"nd2",<<"52">>=>"ne2",{["02"]}=>"n12", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 13=>na3,23=>nb3,33=>"nc3","43"=>"nd3",<<"53">>=>"ne3",{["03"]}=>"n13", 14=>na4,24=>nb4,34=>"nc4","44"=>"nd4",<<"54">>=>"ne4",{["04"]}=>"n14", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, 60 = map_size(M1), 60 = maps:size(M1), % with floats M2 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9"}), #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, 90 = map_size(M2), 90 = maps:size(M2), % with bignums M3 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9", 36893488147419103232=>big1, 73786976294838206464=>big2, 147573952589676412928=>big3, 18446744073709551616=>big4, 4294967296=>big5, 8589934592=>big6, 4294967295=>big7, 67108863=>big8 }), #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, 98 = map_size(M3), 98 = maps:size(M3), %% with maps M4 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9", #{ one => small, map => key } => "small map key 1", #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M4, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M4, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M4, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M4, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M4, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M4, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M4, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M4, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M4, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M4, #{ #{ one => small, map => key } := "small map key 1", #{ second => small, map => key } := "small map key 2", #{ third => small, map => key } := "small map key 3" } = M4, #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M4, #{ 15:=V1,25:=b5,35:=V2,"45":="d5",<<"55">>:=V3,{["05"]}:="15", #{ one => small, map => key } := "small map key 1", #{ second => small, map => key } := V4, #{ third => small, map => key } := "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := V5 } = M4, a5 = V1, "c5" = V2, "e5" = V3, "small map key 2" = V4, "large map key 1" = V5, 95 = map_size(M4), 95 = maps:size(M4), % call for value M5 = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), #{ one => small, map => key } => id("small map key 1"), #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M5, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M5, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M5, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M5, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M5, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M5, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M5, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M5, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M5, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M5, #{ #{ one => small, map => key } := "small map key 1", #{ second => small, map => key } := "small map key 2", #{ third => small, map => key } := "small map key 3" } = M5, #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M5, 95 = map_size(M5), 95 = maps:size(M5), %% remember #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, ok. t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), 1 = map_size(id(#{a=>1})), 1 = map_size(id(#{a=>"wat"})), 2 = map_size(id(#{a=>1, b=>2})), 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), true = map_is_size(#{a=>1}, 1), true = map_is_size(#{a=>1, a=>2}, 1), M = #{ "a" => 1, "b" => 2}, true = map_is_size(M, 2), false = map_is_size(M, 3), true = map_is_size(M#{ "a" => 2}, 2), false = map_is_size(M#{ "c" => 2}, 2), Ks = [build_key(fun(K) -> <<1,K:32,1>> end,I)||I<-lists:seq(1,100)], ok = build_and_check_size(Ks,0,#{}), %% try deep collisions %% statistically we get another subtree at 50k -> 100k elements %% Try to be nice and don't use too much memory in the testcase, N = 500000, Is = lists:seq(1,N), N = map_size(maps:from_list([{I,I}||I<-Is])), N = map_size(maps:from_list([{<>,I}||I<-Is])), N = map_size(maps:from_list([{integer_to_list(I),I}||I<-Is])), N = map_size(maps:from_list([{float(I),I}||I<-Is])), %% Error cases. do_badmap(fun(T) -> {'EXIT',{{badmap,T},_}} = (catch map_size(T)) end), ok. t_map_get(Config) when is_list(Config) -> %% small map 1 = map_get(a, id(#{a=>1})), 2 = map_get(b, id(#{a=>1, b=>2})), "hi" = map_get("hello", id(#{a=>1, "hello"=>"hi"})), "tuple hi" = map_get({1,1.0}, id(#{a=>a, {1,1.0}=>"tuple hi"})), M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), "v4" = map_get(<<"k2">>, M0#{<<"k2">> => "v4"}), %% large map M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"}, {k1,"v1"},{<<"k2">>,"v3"}]), 1 = map_get(a, M1), 2 = map_get(b, M1), "hi" = map_get("hello", M1), "tuple hi" = map_get({1,1.0}, M1), "v3" = map_get(<<"k2">>, M1), %% error cases do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{erlang,map_get,_,_}|_]}} = (catch map_get(a, T)) end), {'EXIT',{{badkey,{1,1}},[{erlang,map_get,_,_}|_]}} = (catch map_get({1,1}, id(#{{1,1.0}=>"tuple"}))), {'EXIT',{{badkey,a},[{erlang,map_get,_,_}|_]}} = (catch map_get(a, id(#{}))), {'EXIT',{{badkey,a},[{erlang,map_get,_,_}|_]}} = (catch map_get(a, id(#{b=>1, c=>2}))), %% in guards M2 = id(#{a=>1}), true = if map_get(a, M2) =:= 1 -> true; true -> false end, false = if map_get(x, M2) =:= 1 -> true; true -> false end, do_badmap(fun (T) when map_get(x, T) =:= 1 -> ok; (T) -> false = is_map(T) end), ok. t_is_map_key(Config) when is_list(Config) -> %% small map true = is_map_key(a, id(#{a=>1})), true = is_map_key(b, id(#{a=>1, b=>2})), true = is_map_key("hello", id(#{a=>1, "hello"=>"hi"})), true = is_map_key({1,1.0}, id(#{a=>a, {1,1.0}=>"tuple hi"})), M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), true = is_map_key(<<"k2">>, M0#{<<"k2">> => "v4"}), %% large map M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"}, {k1,"v1"},{<<"k2">>,"v3"}]), true = is_map_key(a, M1), true = is_map_key(b, M1), true = is_map_key("hello", M1), true = is_map_key({1,1.0}, M1), true = is_map_key(<<"k2">>, M1), %% error cases do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{erlang,is_map_key,_,_}|_]}} = (catch is_map_key(a, T)) end), false = is_map_key({1,1}, id(#{{1,1.0}=>"tuple"})), false = is_map_key(a, id(#{})), false = is_map_key(a, id(#{b=>1, c=>2})), %% in guards M2 = id(#{a=>1}), true = if is_map_key(a, M2) -> true; true -> false end, false = if is_map_key(x, M2) -> true; true -> false end, do_badmap(fun (T) when is_map_key(T, x) =:= 1 -> ok; (T) -> false = is_map(T) end), ok. build_and_check_size([K|Ks],N,M0) -> N = map_size(M0), M1 = M0#{ K => K }, build_and_check_size(Ks,N + 1,M1); build_and_check_size([],N,M) -> N = map_size(M), ok. map_is_size(M,N) when map_size(M) =:= N -> true; map_is_size(_,_) -> false. t_is_map(Config) when is_list(Config) -> true = is_map(#{}), true = is_map(#{a=>1}), false = is_map({a,b}), false = is_map(x), if is_map(#{}) -> ok end, if is_map(#{b=>1}) -> ok end, if not is_map([1,2,3]) -> ok end, if not is_map(x) -> ok end, ok. % test map updates without matching t_update_literals_large(Config) when is_list(Config) -> Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), #{ one => small, map => key } => id("small map key 1"), #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ {"a","1"},{"b","2"},{"c","3"},{"d","4"} ]), ok. t_update_literals(Config) when is_list(Config) -> Map = #{x=>1,y=>2,z=>3,q=>4}, #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ {"a","1"},{"b","2"},{"c","3"},{"d","4"} ]), ok. loop_update_literals_x_q(Map, []) -> Map; loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). % test map updates with matching t_match_and_update_literals(Config) when is_list(Config) -> Map = #{ x=>0,y=>"untouched",z=>"also untouched",q=>1, #{ "one" => small, map => key } => "small map key 1" }, #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}), M1 = id(#{}), M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, M0 = M2, #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. t_match_and_update_literals_large(Config) when is_list(Config) -> Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), x=>0,y=>"untouched",z=>"also untouched",q=>1, #{ "one" => small, map => key } => id("small map key 1"), #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), M0 = id(Map#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}), M1 = id(Map#{}), M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, M0 = M2, #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{ q:=Q0, x:=X0, #{ "one" => small, map => key } := "small map key 1" } = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). t_update_map_expressions(Config) when is_list(Config) -> M = maps:new(), #{ a := 1 } = M#{a => 1}, #{ b := 2 } = (maps:new())#{ b => 2 }, #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, Ks = lists:seq($a,$z), #{ "aa" := {$a,$a}, "ac":=41, "dc":=42 } = (maps:from_list([{[K1,K2],{K1,K2}}|| K1 <- Ks, K2 <- Ks]))#{ "ac" := 41, "dc" => 42 }, %% Error cases. do_badmap(fun(T) -> {'EXIT',{{badmap,T},_}} = (catch (T)#{a:=42,b=>2}) end), ok. t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, M2 = M0#{3.0=>new}, #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, M2 = M0#{3.0:=wrong,3.0=>new}, %% Errors cases. do_badmap(fun(T) -> {'EXIT',{{badmap,T},_}} = (catch T#{nonexisting=>val}) end), ok. t_update_assoc_large(Config) when is_list(Config) -> M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9", #{ one => small, map => key } => "small map key 1", #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, #{1:=42,2:=100,10.0:=fa0,4:=[a,b,c],25:=b5} = M1, #{ 10:=43, 24:=b4, 15:=a5, 35:="c5", 2.0:=100, 13.0:=fa3, 4.0:=[a,b,c]} = M0#{1.0=>float,10:=43,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, M2 = M0#{13.0=>new}, #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2, M2 = M0#{13.0:=wrong,13.0=>new}, ok. t_update_exact(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]}, M2 = M0#{3.0:=new}, #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, M2 = M0#{3.0=>wrong,3.0:=new}, true = M2 =/= M0#{3=>right,3.0:=new}, #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new}, M3 = id(#{ 1 => val}), #{1 := update2,1.0 := new_val4} = M3#{ 1.0 => new_val1, 1 := update, 1=> update3, 1 := update2, 1.0 := new_val2, 1.0 => new_val3, 1.0 => new_val4 }, %% Errors cases. do_badmap(fun(T) -> {'EXIT',{{badmap,T},_}} = (catch T#{nonexisting=>val}) end), Empty = id(#{}), {'EXIT',{{badkey,nonexisting},_}} = (catch Empty#{nonexisting:=val}), {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. t_update_exact_large(Config) when is_list(Config) -> M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9", #{ one => small, map => key } => "small map key 1", #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), M1 = M0#{10:=42,<<"55">>:=100,10.0:=[a,b,c]}, #{ 10:=42,<<"55">>:=100,{["05"]}:="15",10.0:=[a,b,c], #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1" } = M1, M1 = M0#{10:=wrong,10=>42,<<"55">>=>wrong,<<"55">>:=100,10.0:=[a,b,c]}, M2 = M0#{13.0:=new}, #{10:=a0,20:=b0,13.0:=new} = M2, M2 = M0#{13.0=>wrong,13.0:=new}, %% Errors cases. {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. t_update_deep(Config) when is_list(Config) -> N = 250000, M0 = maps:from_list([{integer_to_list(I),a}||I<-lists:seq(1,N)]), #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, M1 = M0#{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b }, #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, M2 = M0#{ "1" => c, "10" => c, "100" => c, "1000" => c, "10000" => c }, #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2, M3 = M2#{ "n1" => d, "n10" => d, "n100" => d, "n1000" => d, "n10000" => d }, #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0, #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1, #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2, #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M3, #{ "n1" := d, "n10" := d, "n100" := d, "n1000" := d, "n10000" := d } = M3, ok. t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), false = map_guard_head([]), true = map_guard_body(#{a=>1}), false = map_guard_body({}), true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }), false = map_guard_pattern("list"), ok. map_guard_head(M) when is_map(M) -> true; map_guard_head(_) -> false. map_guard_body(M) -> is_map(M). map_guard_pattern(#{}) -> true; map_guard_pattern(_) -> false. t_guard_sequence(Config) when is_list(Config) -> {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})), {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), %% error case {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), ok. t_guard_sequence_large(Config) when is_list(Config) -> M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", 10.0=>fa0,20.0=>fb0,30.0=>"fc0", 11.0=>fa1,21.0=>fb1,31.0=>"fc1", 12.0=>fa2,22.0=>fb2,32.0=>"fc2", 13.0=>fa3,23.0=>fb3,33.0=>"fc3", 14.0=>fa4,24.0=>fb4,34.0=>"fc4", 15.0=>fa5,25.0=>fb5,35.0=>"fc5", 16.0=>fa6,26.0=>fb6,36.0=>"fc6", 17.0=>fa7,27.0=>fb7,37.0=>"fc7", 18.0=>fa8,28.0=>fb8,38.0=>"fc8", 19.0=>fa9,29.0=>fb9,39.0=>"fc9", #{ one => small, map => key } => "small map key 1", #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), {1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}), {2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}), {3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}), {4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}), {5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}), {1,M1} = map_guard_sequence_2(M1 = id(M0#{a=>3})), {2,M2} = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})), {3,gg,M3} = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})), {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})), {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})), {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})), {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})), ok. map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}. map_guard_sequence_2(#{ a:=3 }=M) -> {1, M}; map_guard_sequence_2(#{ a:=4 }=M) -> {2, M}; map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M}; map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M}; map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}. t_guard_update(Config) when is_list(Config) -> error = map_guard_update(#{},#{}), first = map_guard_update(#{}, #{x=>first}), second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), ok. t_guard_update_large(Config) when is_list(Config) -> M0 = id(#{ 70=>a0,80=>b0,90=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", 71=>a1,81=>b1,91=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", 72=>a2,82=>b2,92=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", 73=>a3,83=>b3,93=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", 74=>a4,84=>b4,94=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", 75=>a5,85=>b5,95=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", 76=>a6,86=>b6,96=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", 77=>a7,87=>b7,97=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", 78=>a8,88=>b8,98=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", 79=>a9,89=>b9,99=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", 70.0=>fa0,80.0=>fb0,90.0=>"fc0", 71.0=>fa1,81.0=>fb1,91.0=>"fc1", 72.0=>fa2,82.0=>fb2,92.0=>"fc2", 73.0=>fa3,83.0=>fb3,93.0=>"fc3", 74.0=>fa4,84.0=>fb4,94.0=>"fc4", 75.0=>fa5,85.0=>fb5,95.0=>"fc5", 76.0=>fa6,86.0=>fb6,96.0=>"fc6", 77.0=>fa7,87.0=>fb7,97.0=>"fc7", 78.0=>fa8,88.0=>fb8,98.0=>"fc8", 79.0=>fa9,89.0=>fb9,99.0=>"fc9", #{ one => small, map => key } => "small map key 1", #{ second => small, map => key } => "small map key 2", #{ third => small, map => key } => "small map key 3", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), error = map_guard_update(M0#{},M0#{}), first = map_guard_update(M0#{},M0#{x=>first}), second = map_guard_update(M0#{y=>old}, M0#{x=>second,y=>old}), ok. map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; map_guard_update(_, _) -> error. t_guard_receive(Config) when is_list(Config) -> M0 = #{ id => 0 }, Pid = spawn_link(fun() -> guard_receive_loop() end), Big = 36893488147419103229, B1 = <<"some text">>, B2 = <<"was appended">>, B3 = <>, #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), %% update old maps and check id update #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}), %% cleanup done = call(Pid, done), ok. -define(t_guard_receive_large_procs, 1500). t_guard_receive_large(Config) when is_list(Config) -> M = lists:foldl(fun(_,#{procs := Ps } = M) -> M#{ procs := Ps#{ spawn_link(fun() -> grecv_loop() end) => 0 }} end, #{procs => #{}, done => 0}, lists:seq(1,?t_guard_receive_large_procs)), lists:foreach(fun(Pid) -> Pid ! {self(), hello} end, maps:keys(maps:get(procs,M))), ok = guard_receive_large_loop(M), ok. guard_receive_large_loop(#{done := ?t_guard_receive_large_procs}) -> ok; guard_receive_large_loop(M) -> receive #{pid := Pid, msg := hello} -> case M of #{done := Count, procs := #{Pid := 150}} -> Pid ! {self(), done}, guard_receive_large_loop(M#{done := Count + 1}); #{procs := #{Pid := Count} = Ps} -> Pid ! {self(), hello}, guard_receive_large_loop(M#{procs := Ps#{Pid := Count + 1}}) end end. grecv_loop() -> receive {_, done} -> ok; {Pid, hello} -> Pid ! #{pid=>self(), msg=>hello}, grecv_loop() end. call(Pid, M) -> Pid ! {self(), M}, receive {Pid, Res} -> Res end. guard_receive_loop() -> receive {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) -> Pid ! {self(), M#{ id=>Id+1, res=><>}}, guard_receive_loop(); {Pid, #{ id:=Id, op:=add, in:={X,Y}}} -> Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, guard_receive_loop(); {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, guard_receive_loop(); {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, guard_receive_loop(); {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, guard_receive_loop(); {Pid, done} -> Pid ! {self(), done}; {Pid, Other} -> Pid ! {error, Other}, guard_receive_loop() end. t_list_comprehension(Config) when is_list(Config) -> [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], Ks = lists:seq($a,$z), Ms = [#{[K1,K2]=>{K1,K2}} || K1 <- Ks, K2 <- Ks], [#{"aa" := {$a,$a}},#{"ab":={$a,$b}}|_] = Ms, [#{"zz" := {$z,$z}},#{"zy":={$z,$y}}|_] = lists:reverse(Ms), ok. t_guard_fun(Config) when is_list(Config) -> F1 = fun (#{s:=v,v:=V}) -> {v,V}; (#{s:=t,v:={V,V}}) -> {t,V}; (#{s:=l,v:=[V,V]}) -> {l,V} end, F2 = fun (#{s:=T,v:={V,V}}) -> {T,V}; (#{s:=T,v:=[V,V]}) -> {T,V}; (#{s:=T,v:=V}) -> {T,V} end, V = <<"hi">>, {v,V} = F1(#{s=>v,v=>V}), {t,V} = F1(#{s=>t,v=>{V,V}}), {l,V} = F1(#{s=>l,v=>[V,V]}), {v,V} = F2(#{s=>v,v=>V}), {t,V} = F2(#{s=>t,v=>{V,V}}), {l,V} = F2(#{s=>l,v=>[V,V]}), %% error case {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), ok. t_map_sort_literals(Config) when is_list(Config) -> % test relation %% size order true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}), true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}), false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}), %% key order true = #{ a => 1 } < id(#{ b => 1}), false = #{ b => 1 } < id(#{ a => 1}), true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}), true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}), true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}), true = #{ "a" => 1 } < id(#{ <<"a">> => 1}), false = #{ <<"a">> => 1 } < id(#{ "a" => 1}), true = #{ 1 => 1 } < id(#{ 1.0 => 1}), false = #{ 1.0 => 1 } < id(#{ 1 => 1}), %% value order true = #{ a => 1 } < id(#{ a => 2}), false = #{ a => 2 } < id(#{ a => 1}), false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}), true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}), false = #{ a => 1 } < id(#{ a => 1.0}), false = #{ a => 1.0 } < id(#{ a => 1}), true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}), %% large maps M = maps:from_list([{I,I}||I <- lists:seq(1,500)]), %% size order true = M#{ a => 1, b => 2} < id(M#{ a => 1, b => 1, c => 1}), true = M#{ b => 1, a => 1} < id(M#{ c => 1, a => 1, b => 1}), false = M#{ c => 1, b => 1, a => 1} < id(M#{ c => 1, a => 1}), %% key order true = M#{ a => 1 } < id(M#{ b => 1}), false = M#{ b => 1 } < id(M#{ a => 1}), true = M#{ a => 1, b => 1, c => 1 } < id(M#{ b => 1, c => 1, d => 1}), true = M#{ b => 1, c => 1, d => 1 } > id(M#{ a => 1, b => 1, c => 1}), true = M#{ c => 1, b => 1, a => 1 } < id(M#{ b => 1, c => 1, d => 1}), true = M#{ "a" => 1 } < id(M#{ <<"a">> => 1}), false = M#{ <<"a">> => 1 } < id(#{ "a" => 1}), true = M#{ 1 => 1 } < id(maps:remove(1,M#{ 1.0 => 1})), false = M#{ 1.0 => 1 } < id(M#{ 1 => 1}), %% value order true = M#{ a => 1 } < id(M#{ a => 2}), false = M#{ a => 2 } < id(M#{ a => 1}), false = M#{ a => 2, b => 1 } < id(M#{ a => 1, b => 3}), true = M#{ a => 1, b => 1 } < id(M#{ a => 1, b => 3}), false = M#{ a => 1 } < id(M#{ a => 1.0}), false = M#{ a => 1.0 } < id(M#{ a => 1}), true = M#{ "a" => "hi", b => 134 } == id(M#{ b => 134,"a" => "hi"}), %% lists:sort SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs), [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)), ok. t_map_equal(Config) when is_list(Config) -> true = id(#{}) =:= id(#{}), false = id(#{}) =:= id(#{a=>1}), false = id(#{a=>1}) =:= id(#{}), true = id(#{ "a" => "hi", b => 134 }) =:= id(#{ b => 134,"a" => "hi"}), false = id(#{ a => 1 }) =:= id(#{ a => 2}), false = id(#{ a => 2 }) =:= id(#{ a => 1}), false = id(#{ a => 2, b => 1 }) =:= id(#{ a => 1, b => 3}), false = id(#{ a => 1, b => 1 }) =:= id(#{ a => 1, b => 3}), true = id(#{ a => 1 }) =:= id(#{ a => 1}), true = id(#{ "a" => 2 }) =:= id(#{ "a" => 2}), true = id(#{ "a" => 2, b => 3 }) =:= id(#{ "a" => 2, b => 3}), true = id(#{ a => 1, b => 3, c => <<"wat">> }) =:= id(#{ a => 1, b => 3, c=><<"wat">>}), ok. t_map_compare(Config) when is_list(Config) -> rand:seed(exsplus), io:format("seed = ~p\n", [rand:export_seed()]), repeat(100, fun(_) -> float_int_compare() end, []), repeat(100, fun(_) -> recursive_compare() end, []), ok. float_int_compare() -> Terms = numeric_keys(3), %%io:format("Keys to use: ~p\n", [Terms]), Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms), lists:foreach(fun(Size) -> MapGen = fun() -> map_gen(list_to_tuple(Pairs), Size) end, repeat(100, fun do_compare/1, [MapGen, MapGen]) end, lists:seq(1,length(Terms))), ok. numeric_keys(N) -> lists:foldl(fun(_,Acc) -> Int = rand:uniform(N*4) - N*2, Float = float(Int), [Int, Float, Float * 0.99, Float * 1.01 | Acc] end, [], lists:seq(1,N)). repeat(0, _, _) -> ok; repeat(N, Fun, Arg) -> Fun(Arg), repeat(N-1, Fun, Arg). copy_term(T) -> Papa = self(), P = spawn_link(fun() -> receive Msg -> Papa ! Msg end end), P ! T, receive R -> R end. do_compare([Gen1, Gen2]) -> M1 = Gen1(), M2 = Gen2(), %%io:format("Maps to compare: ~p AND ~p\n", [M1, M2]), C = (M1 < M2), Erlang = maps_lessthan(M1, M2), C = Erlang, ?CHECK(M1==M1, M1), %% Change one key from int to float (or vice versa) and check compare ML1 = maps:to_list(M1), {K1,V1} = lists:nth(rand:uniform(length(ML1)), ML1), case K1 of I when is_integer(I) -> case maps:find(float(I),M1) of error -> M1f = maps:remove(I, maps:put(float(I), V1, M1)), ?CHECK(M1f > M1, [M1f, M1]); _ -> ok end; F when is_float(F), round(F) == F -> case maps:find(round(F),M1) of error -> M1i = maps:remove(F, maps:put(round(F), V1, M1)), ?CHECK(M1i < M1, [M1i, M1]); _ -> ok end; _ -> ok % skip floats with decimals end, ?CHECK(M2 == M2, [M2]). maps_lessthan(M1, M2) -> case {maps:size(M1),maps:size(M2)} of {_S,_S} -> {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))), {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))), case erts_internal:cmp_term(K1,K2) of -1 -> true; 0 -> (V1 < V2); 1 -> false end; {S1, S2} -> S1 < S2 end. term_sort(L) -> lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) =< 0 end, L). cmp(T1, T2, Exact) when is_tuple(T1) and is_tuple(T2) -> case {size(T1),size(T2)} of {_S,_S} -> cmp(tuple_to_list(T1), tuple_to_list(T2), Exact); {S1,S2} when S1 < S2 -> -1; {S1,S2} when S1 > S2 -> 1 end; cmp([H1|T1], [H2|T2], Exact) -> case cmp(H1,H2, Exact) of 0 -> cmp(T1,T2, Exact); C -> C end; cmp(M1, M2, Exact) when is_map(M1) andalso is_map(M2) -> cmp_maps(M1,M2,Exact); cmp(M1, M2, Exact) -> cmp_others(M1, M2, Exact). cmp_maps(M1, M2, Exact) -> case {maps:size(M1),maps:size(M2)} of {_S,_S} -> {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))), {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))), case cmp(K1, K2, true) of 0 -> cmp(V1, V2, Exact); C -> C end; {S1,S2} when S1 < S2 -> -1; {S1,S2} when S1 > S2 -> 1 end. cmp_others(I, F, true) when is_integer(I), is_float(F) -> -1; cmp_others(F, I, true) when is_float(F), is_integer(I) -> 1; cmp_others(T1, T2, _) -> case {T1 -1; {false,true} -> 0; {false,false} -> 1 end. map_gen(Pairs, Size) -> {_,L} = lists:foldl(fun(_, {Keys, Acc}) -> KI = rand:uniform(size(Keys)), K = element(KI,Keys), KV = element(rand:uniform(size(K)), K), {erlang:delete_element(KI,Keys), [KV | Acc]} end, {Pairs, []}, lists:seq(1,Size)), maps:from_list(L). recursive_compare() -> Leafs = {atom, 17, 16.9, 17.1, [], self(), spawn(fun() -> ok end), make_ref(), make_ref()}, {A, B} = term_gen_recursive(Leafs, 0, 0), %%io:format("Recursive term A = ~p\n", [A]), %%io:format("Recursive term B = ~p\n", [B]), ?CHECK({true,false} =:= case do_cmp(A, B, false) of -1 -> {A=B}; 0 -> {A==B, A/=B}; 1 -> {A>B, A= C = cmp(A, B, Exact), C. %% Generate two terms {A,B} that may only differ %% at float vs integer types. term_gen_recursive(Leafs, Flags, Depth) -> MaxDepth = 10, Rnd = case {Flags, Depth} of {_, MaxDepth} -> % Only leafs rand:uniform(size(Leafs)) + 3; {0, 0} -> % Only containers rand:uniform(3); {0,_} -> % Anything rand:uniform(size(Leafs)+3) end, case Rnd of 1 -> % Make map Size = rand:uniform(size(Leafs)), lists:foldl(fun(_, {Acc1,Acc2}) -> {K1,K2} = term_gen_recursive(Leafs, Flags, Depth+1), {V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1), {maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)} end, {maps:new(), maps:new()}, lists:seq(1,Size)); 2 -> % Make cons {Car1,Car2} = term_gen_recursive(Leafs, Flags, Depth+1), {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1), {[Car1 | Cdr1], [Car2 | Cdr2]}; 3 -> % Make tuple Size = rand:uniform(size(Leafs)), L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end, lists:seq(1,Size)), {L1, L2} = lists:unzip(L), {list_to_tuple(L1), list_to_tuple(L2)}; N -> % Make leaf case element(N-3, Leafs) of I when is_integer(I) -> case rand:uniform(4) of 1 -> {I, float(I)}; 2 -> {float(I), I}; _ -> {I,I} end; T -> {T,T} end end. %% BIFs t_bif_map_get(Config) when is_list(Config) -> %% small map 1 = maps:get(a, #{ a=> 1}), 2 = maps:get(b, #{ a=> 1, b => 2}), "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}), "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), "v4" = maps:get(<<"k2">>, M0#{<<"k2">> => "v4"}), %% large map M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"}, {k1,"v1"},{<<"k2">>,"v3"}]), 1 = maps:get(a, M1), 2 = maps:get(b, M1), "hi" = maps:get("hello", M1), "tuple hi" = maps:get({1,1.0}, M1), "v3" = maps:get(<<"k2">>, M1), %% error cases do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} = (catch maps:get(a, T)) end), {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})), {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> %% small map {ok, 1} = maps:find(a, #{ a=> 1}), {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), {ok, "int"} = maps:find(1, #{ 1 => "int"}), {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), {ok, "v4"} = maps:find(<<"k2">>, M0#{ <<"k2">> => "v4" }), %% large map M1 = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++ [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"}, {k1,"v1"},{<<"k2">>,"v3"}]), {ok, 1} = maps:find(a, M1), {ok, 2} = maps:find(b, M1), {ok, "hi"} = maps:find("hello", M1), {ok, "tuple hi"} = maps:find({1,1.0}, M1), {ok, "v3"} = maps:find(<<"k2">>, M1), %% error case error = maps:find(a,#{}), error = maps:find(a,#{b=>1, c=>2}), error = maps:find(1.0, #{ 1 => "int"}), error = maps:find(1, #{ 1.0 => "float"}), error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,find,_,_}|_]}} = (catch maps:find(a, T)) end), ok. t_bif_map_is_key(Config) when is_list(Config) -> M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, true = maps:is_key("hi", M1), true = maps:is_key(int, M1), true = maps:is_key(<<"key">>, M1), true = maps:is_key(4, M1), false = maps:is_key(5, M1), false = maps:is_key(<<"key2">>, M1), false = maps:is_key("h", M1), false = maps:is_key("hello", M1), false = maps:is_key(atom, M1), false = maps:is_key(any, id(#{})), false = maps:is_key("hi", maps:remove("hi", M1)), true = maps:is_key("hi", M1), true = maps:is_key(1, maps:put(1, "number", M1)), false = maps:is_key(1.0, maps:put(1, "number", M1)), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a, T)) end), ok. t_bif_map_keys(Config) when is_list(Config) -> [] = maps:keys(#{}), [1,2,3,4,5] = lists:sort(maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})), [1,2,3,4,5] = lists:sort(maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})), % values in key order: [4,int,"hi",<<"key">>] M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, [4,int,"hi",<<"key">>] = lists:sort(maps:keys(M1)), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,keys,_,_}|_]}} = (catch maps:keys(T)) end), ok. t_bif_map_new(Config) when is_list(Config) -> #{} = maps:new(), 0 = erlang:map_size(maps:new()), ok. t_bif_map_merge(Config) when is_list(Config) -> 0 = erlang:map_size(maps:merge(#{},#{})), M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0), #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}), M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer }, #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3, {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0), #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3, {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), %% try deep collisions N = 150000, Is = lists:seq(1,N), M2 = maps:from_list([{I,I}||I<-Is]), 150000 = maps:size(M2), M3 = maps:from_list([{<>,I}||I<-Is]), 150000 = maps:size(M3), M4 = maps:merge(M2,M3), 300000 = maps:size(M4), M5 = maps:from_list([{integer_to_list(I),I}||I<-Is]), 150000 = maps:size(M5), M6 = maps:merge(M4,M5), 450000 = maps:size(M6), M7 = maps:from_list([{float(I),I}||I<-Is]), 150000 = maps:size(M7), M8 = maps:merge(M7,M6), 600000 = maps:size(M8), #{ 1 := 1, "1" := 1, <<1:32>> := 1 } = M8, #{ 10 := 10, "10" := 10, <<10:32>> := 10 } = M8, #{ 100 := 100, "100" := 100, <<100:32>> := 100 } = M8, #{ 1000 := 1000, "1000" := 1000, <<1000:32>> := 1000 } = M8, #{ 10000 := 10000, "10000" := 10000, <<10000:32>> := 10000 } = M8, #{ 100000 := 100000, "100000" := 100000, <<100000:32>> := 100000 } = M8, %% overlapping M8 = maps:merge(M2,M8), M8 = maps:merge(M3,M8), M8 = maps:merge(M4,M8), M8 = maps:merge(M5,M8), M8 = maps:merge(M6,M8), M8 = maps:merge(M7,M8), M8 = maps:merge(M8,M8), %% maps:merge/2 and mixed Ks1 = [764492191,2361333849], %% deep collision Ks2 = lists:seq(1,33), M9 = maps:from_list([{K,K}||K <- Ks1]), M10 = maps:from_list([{K,K}||K <- Ks2]), M11 = maps:merge(M9,M10), ok = check_keys_exist(Ks1 ++ Ks2, M11), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = (catch maps:merge(#{}, T)), {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = (catch maps:merge(T, #{})), {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} = (catch maps:merge(T, T)) end), ok. t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}), true = is_members(["hi"],maps:keys(M1)), true = is_members(["hello"],maps:values(M1)), M2 = #{ int := 3 } = maps:put(int, 3, M1), true = is_members([int,"hi"],maps:keys(M2)), true = is_members([3,"hello"],maps:values(M2)), M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2), true = is_members([int,"hi",<<"key">>],maps:keys(M3)), true = is_members([3,"hello",<<"value">>],maps:values(M3)), M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3), true = is_members([18446744073709551629,int,"hi",<<"key">>],maps:keys(M4)), true = is_members([wat,3,"hello",<<"value">>],maps:values(M4)), M0 = #{ 4 := number } = M5 = maps:put(4, number, M4), true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M5)), true = is_members([number,wat,3,"hello",<<"value">>],maps:values(M5)), M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5), true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M6)), true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, T)) end), ok. is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false; is_members(Ks,Ls) -> is_members_do(Ks,Ls). is_members_do([],[]) -> true; is_members_do([],_) -> false; is_members_do([K|Ks],Ls) -> is_members_do(Ks, lists:delete(K,Ls)). t_bif_map_remove(Config) when is_list(Config) -> 0 = erlang:map_size(maps:remove(some_key, #{})), M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, M1 = maps:remove("hi", M0), true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)), true = is_members([number,wat,3,<<"value">>],maps:values(M1)), M2 = maps:remove(int, M1), true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)), true = is_members([number,wat,<<"value">>],maps:values(M2)), M3 = maps:remove(<<"key">>, M2), true = is_members([4,18446744073709551629],maps:keys(M3)), true = is_members([number,wat],maps:values(M3)), M4 = maps:remove(18446744073709551629, M3), true = is_members([4],maps:keys(M4)), true = is_members([number],maps:values(M4)), M5 = maps:remove(4, M4), [] = maps:keys(M5), [] = maps:values(M5), M0 = maps:remove(5,M0), M0 = maps:remove("hi there",M0), #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, T)) end), ok. t_bif_map_take(Config) when is_list(Config) -> error = maps:take(some_key, #{}), M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, 5 = maps:size(M0), {"hello", M1} = maps:take("hi", M0), true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)), true = is_members([number,wat,3,<<"value">>],maps:values(M1)), error = maps:take("hi", M1), 4 = maps:size(M1), {3, M2} = maps:take(int, M1), true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)), true = is_members([number,wat,<<"value">>],maps:values(M2)), error = maps:take(int, M2), 3 = maps:size(M2), {<<"value">>,M3} = maps:take(<<"key">>, M2), true = is_members([4,18446744073709551629],maps:keys(M3)), true = is_members([number,wat],maps:values(M3)), error = maps:take(<<"key">>, M3), 2 = maps:size(M3), {wat,M4} = maps:take(18446744073709551629, M3), true = is_members([4],maps:keys(M4)), true = is_members([number],maps:values(M4)), error = maps:take(18446744073709551629, M4), 1 = maps:size(M4), {number,M5} = maps:take(4, M4), [] = maps:keys(M5), [] = maps:values(M5), error = maps:take(4, M5), 0 = maps:size(M5), {wat,#{ "hi" := "hello", int := 3, 4 := number, <<"key">> := <<"value">>}} = maps:take(18446744073709551629,M0), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,take,_,_}|_]}} = (catch maps:take(a, T)) end), ok. t_bif_map_take_large(Config) when is_list(Config) -> KVs = [{{erlang:md5(<>),I}, I}|| I <- lists:seq(1,500)], M0 = maps:from_list(KVs), ok = bif_map_take_all(KVs, M0), ok. bif_map_take_all([], M0) -> 0 = maps:size(M0), ok; bif_map_take_all([{K,V}|KVs],M0) -> {ok,V} = maps:find(K,M0), {V,M1} = maps:take(K,M0), error = maps:find(K,M1), error = maps:take(K,M1), bif_map_take_all(KVs,M1). t_bif_map_update(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>, 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0), #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>, 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0), #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>, 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0), #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0), #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,update,_,_}|_]}} = (catch maps:update(1, none, T)) end), ok. t_bif_map_values(Config) when is_list(Config) -> [] = maps:values(#{}), [1] = maps:values(#{a=>1}), true = is_members([a,b,c,d,e],maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})), true = is_members([a,b,c,d,e],maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})), M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, true = is_members([number,3,"hello2",<<"value2">>],maps:values(M2)), true = is_members([number,3,"hello",<<"value">>],maps:values(M1)), Vs = lists:seq(1000,20000), M3 = maps:from_list([{K,K}||K<-Vs]), M4 = maps:merge(M1,M3), M5 = maps:merge(M2,M3), true = is_members(Vs,maps:values(M3)), true = is_members([number,3,"hello",<<"value">>]++Vs,maps:values(M4)), true = is_members([number,3,"hello2",<<"value2">>]++Vs,maps:values(M5)), %% error case do_badmap(fun(T) -> {'EXIT',{{badmap,T},[{maps,values,_,_}|_]}} = (catch maps:values(T)) end), ok. t_erlang_hash(Config) when is_list(Config) -> ok = t_bif_erlang_phash2(), ok = t_bif_erlang_phash(), ok. t_bif_erlang_phash2() -> 39679005 = erlang:phash2(#{}), 33667975 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), % 78942764 95332690 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), % 37338230 108954384 = erlang:phash2(#{ 1 => a }), % 14363616 59617982 = erlang:phash2(#{ a => 1 }), % 51612236 42770201 = erlang:phash2(#{{} => <<>>}), % 37468437 71687700 = erlang:phash2(#{<<>> => {}}), % 44049159 M0 = #{ a => 1, "key" => <<"value">> }, M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 70249457 = erlang:phash2(M0), % 118679416 59617982 = erlang:phash2(M1), % 51612236 70249457 = erlang:phash2(M2), % 118679416 ok. t_bif_erlang_phash() -> Sz = 1 bsl 32, 1113425985 = erlang:phash(#{},Sz), % 268440612 1510068139 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 1196461908 3182345590 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 3944426064 2927531828 = erlang:phash(#{ 1 => a },Sz), % 1394238263 1670235874 = erlang:phash(#{ a => 1 },Sz), % 4066388227 3935089469 = erlang:phash(#{{} => <<>>},Sz), % 1578050717 71692856 = erlang:phash(#{<<>> => {}},Sz), % 1578050717 M0 = #{ a => 1, "key" => <<"value">> }, M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 2620391445 = erlang:phash(M0,Sz), % 3590546636 1670235874 = erlang:phash(M1,Sz), % 4066388227 2620391445 = erlang:phash(M2,Sz), % 3590546636 ok. t_map_encode_decode(Config) when is_list(Config) -> <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), Pairs = [ {a,b},{"key","values"},{<<"key">>,<<"value">>}, {1,b},{[atom,1],{<<"wat">>,1,2,3}}, {aa,"values"}, {1 bsl 64 + (1 bsl 50 - 1), sc1}, {99, sc2}, {1 bsl 65 + (1 bsl 51 - 1), sc3}, {88, sc4}, {1 bsl 66 + (1 bsl 52 - 1), sc5}, {77, sc6}, {1 bsl 67 + (1 bsl 53 - 1), sc3}, {75, sc6}, {-10,sc8}, {<<>>, sc9}, {3.14158, sc10}, {[3.14158], sc11}, {more_atoms, sc12}, {{more_tuples}, sc13}, {self(), sc14}, {{},{}},{[],[]}, {map_s, #{a=>a, 2=>b, 3=>c}}, {map_l, maps:from_list([{I,I}||I <- lists:seq(1,74)])} ], ok = map_encode_decode_and_match(Pairs,[],#{}), %% check sorting %% literally #{ b=>2, a=>1 } in the internal order #{ a:=1, b:=2 } = erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>), %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3, 107,0,2,104,105, % "hi" :: list() 107,0,5,118,97,108,117,101, % "value" :: list() 100,0,1,97, % a :: atom() 97,33, % 33 :: integer() 100,0,1,98, % b :: atom() 97,55 % 55 :: integer() >>), %% Maps of different sizes lists:foldl(fun(Key, M0) -> M1 = M0#{Key => Key}, case Key rem 17 of 0 -> M1 = binary_to_term(term_to_binary(M1)); _ -> ok end, M1 end, #{}, lists:seq(1,10000)), %% many maps in same binary MapList = lists:foldl(fun(K, [M|_]=Acc) -> [M#{K => K} | Acc] end, [#{}], lists:seq(1,100)), MapList = binary_to_term(term_to_binary(MapList)), MapListR = lists:reverse(MapList), MapListR = binary_to_term(term_to_binary(MapListR)), %% error cases %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> %% which is: #{ a=>1, b=>1 } %% uniqueness violation %% literally #{ a=>1, "hi"=>"value", a=>2 } {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch erlang:binary_to_term(<<131,116,0,0,0,3, 100,0,1,97, 97,1, 107,0,2,104,105, 107,0,5,118,97,108,117,101, 100,0,1,97, 97,2>>)), %% bad size (too large) {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,97,1,100,0,1,98,97,1>>)), %% bad size (too small) .. should fail just truncate it .. weird. %% possibly change external format so truncated will be #{a:=1} #{ a:=b } = erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), %% specific fannerl (opensource app) binary_to_term error in 18.1 #{bias := {1,1,0}, bit_fail := 0, connections := #{{2,9} := _, {8,14} := _, {2,12} := _, {5,7} := _, {11,16} := _, {11,15} := _}, layers := {5,7,3}, network_type := fann_nettype_layer, num_input := 5, num_layers := 3, num_output := 3, rprop_delta_max := _, rprop_delta_min := _, total_connections := 66, total_neurons := 17, train_error_function := fann_errorfunc_tanh, train_stop_function := fann_stopfunc_mse, training_algorithm := fann_train_rprop} = erlang:binary_to_term(fannerl()), ok. map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), Ls = [{erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs], ok = match_encoded_map(B0, length(Ls), Ls), %% decode and match it M1 = erlang:binary_to_term(B0), map_encode_decode_and_match(Pairs,Ls,M1); map_encode_decode_and_match([],_,_) -> ok. match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) -> match_encoded_map_stripped_size(Encoded,Items,Items); match_encoded_map(_,_,_) -> no_match_size. match_encoded_map_stripped_size(<<>>,_,_) -> ok; match_encoded_map_stripped_size(B0,[{<<131,K/binary>>,<<131,V/binary>>}|Items],Ls) -> Ksz = byte_size(K), Vsz = byte_size(V), case B0 of <> -> match_encoded_map_stripped_size(B1,Ls,Ls); _ -> match_encoded_map_stripped_size(B0,Items,Ls) end; match_encoded_map_stripped_size(_,[],_) -> fail. t_bif_map_to_list(Config) when is_list(Config) -> [] = maps:to_list(#{}), [{a,1},{b,2}] = lists:sort(maps:to_list(#{a=>1,b=>2})), [{a,1},{b,2},{c,3}] = lists:sort(maps:to_list(#{c=>3,a=>1,b=>2})), [{a,1},{b,2},{g,3}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2})), [{a,1},{b,2},{g,3},{"c",4}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4})), [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5})), [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10})), %% error cases do_badmap(fun(T) -> {'EXIT', {{badmap,T},_}} = (catch maps:to_list(T)) end), ok. t_bif_map_from_list(Config) when is_list(Config) -> #{} = maps:from_list([]), A = maps:from_list([]), 0 = erlang:map_size(A), #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]), #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]), #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]), #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]), #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% repeated keys (large -> small) Ps1 = [{a,I}|| I <- lists:seq(1,32)], Ps2 = [{a,I}|| I <- lists:seq(33,64)], M = maps:from_list(Ps1 ++ [{b,1},{c,1}] ++ Ps2), #{ a := 64, b := 1, c := 1 } = M, %% error cases {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. t_bif_map_next(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), try none = maps:next(maps:iterator(id(#{}))), verify_iterator(#{}), verify_iterator(#{a => 1, b => 2, c => 3}), %% Use fatmap in order to test iterating in very deep maps FM = fatmap(43), verify_iterator(FM), {'EXIT', {{badmap,[{a,b},b]},_}} = (catch maps:iterator(id([{a,b},b]))), {'EXIT', {badarg,_}} = (catch maps:next(id(a))), {'EXIT', {badarg,_}} = (catch maps:next(id([a|FM]))), {'EXIT', {badarg,_}} = (catch maps:next(id([1|#{}]))), {'EXIT', {badarg,_}} = (catch maps:next(id([-1|#{}]))), {'EXIT', {badarg,_}} = (catch maps:next(id([-1|FM]))), {'EXIT', {badarg,_}} = (catch maps:next(id([16#FFFFFFFFFFFFFFFF|FM]))), {'EXIT', {badarg,_}} = (catch maps:next(id([-16#FFFFFFFFFFFFFFFF|FM]))), %% This us a whitebox test that the error code works correctly. %% It uses a path for a tree of depth 4 and tries to do next on %% each of those paths. (fun F(0) -> ok; F(N) -> try maps:next([N|FM]) of none -> F(N-1); {_K,_V,_I} -> F(N-1) catch error:badarg -> F(N-1) end end)(16#FFFF), ok after erts_debug:set_internal_state(available_internal_state, false) end. verify_iterator(Map) -> KVs = t_fold(fun(K, V, A) -> [{K, V} | A] end, [], Map), %% Verify that KVs created by iterating Map is of %% correct size and contains all elements true = length(KVs) == maps:size(Map), [maps:get(K, Map) || {K, _} <- KVs], ok. t_fold(Fun, Init, Map) -> t_fold_1(Fun, Init, maps:iterator(Map)). t_fold_1(Fun, Acc, Iter) -> case maps:next(Iter) of {K, V, NextIter} -> t_fold_1(Fun, Fun(K,V,Acc), NextIter); none -> Acc end. t_bif_build_and_check(Config) when is_list(Config) -> ok = check_build_and_remove(750,[fun(K) -> [K,K] end, fun(K) -> [float(K),K] end, fun(K) -> K end, fun(K) -> {1,K} end, fun(K) -> {K} end, fun(K) -> [K|K] end, fun(K) -> [K,1,2,3,4] end, fun(K) -> {K,atom} end, fun(K) -> float(K) end, fun(K) -> integer_to_list(K) end, fun(K) -> list_to_atom(integer_to_list(K)) end, fun(K) -> [K,{K,[K,{K,[K]}]}] end, fun(K) -> <> end]), ok. check_build_and_remove(_,[]) -> ok; check_build_and_remove(N,[F|Fs]) -> {M,Ks} = build_and_check(N, maps:new(), F, []), ok = remove_and_check(Ks,M), check_build_and_remove(N,Fs). build_and_check(0, M0, _, Ks) -> {M0, Ks}; build_and_check(N, M0, F, Ks) -> K = build_key(F,N), M1 = maps:put(K,K,M0), ok = check_keys_exist([I||{I,_} <- [{K,M1}|Ks]], M1), M2 = maps:update(K,v,M1), v = maps:get(K,M2), build_and_check(N-1,M1,F,[{K,M1}|Ks]). remove_and_check([],_) -> ok; remove_and_check([{K,Mc}|Ks], M0) -> K = maps:get(K,M0), true = maps:is_key(K,M0), true = Mc =:= M0, true = M0 == Mc, M1 = maps:remove(K,M0), false = M1 =:= Mc, false = Mc == M1, false = maps:is_key(K,M1), true = maps:is_key(K,M0), ok = check_keys_exist([I||{I,_} <- Ks],M1), error = maps:find(K,M1), remove_and_check(Ks, M1). build_key(F,N) when N rem 3 =:= 0 -> F(N); build_key(F,N) when N rem 3 =:= 1 -> K = F(N), {K,K}; build_key(F,N) when N rem 3 =:= 2 -> K = F(N), [K,K]. check_keys_exist([], _) -> ok; check_keys_exist([K|Ks],M) -> true = maps:is_key(K,M), check_keys_exist(Ks,M). t_bif_merge_and_check(Config) when is_list(Config) -> io:format("rand:export_seed() -> ~p\n",[rand:export_seed()]), %% simple disjunct ones %% make sure all keys are unique Kss = [[a,b,c,d], [1,2,3,4], [], ["hi"], [e], [build_key(fun(K) -> {small,K} end, I) || I <- lists:seq(1,32)], lists:seq(5, 28), lists:seq(29, 59), [build_key(fun(K) -> integer_to_list(K) end, I) || I <- lists:seq(2000,10000)], [build_key(fun(K) -> <> end, I) || I <- lists:seq(1,80)], [build_key(fun(K) -> {<>} end, I) || I <- lists:seq(100,1000)]], KsMs = build_keys_map_pairs(Kss), Cs = [{CKs1,CM1,CKs2,CM2} || {CKs1,CM1} <- KsMs, {CKs2,CM2} <- KsMs], ok = merge_and_check_combo(Cs), %% overlapping ones KVs1 = [{a,1},{b,2},{c,3}], KVs2 = [{b,3},{c,4},{d,5}], KVs = [{I,I} || I <- lists:seq(1,32)], KVs3 = KVs1 ++ KVs, KVs4 = KVs2 ++ KVs, M1 = maps:from_list(KVs1), M2 = maps:from_list(KVs2), M3 = maps:from_list(KVs3), M4 = maps:from_list(KVs4), M12 = maps:merge(M1,M2), ok = check_key_values(KVs2 ++ [{a,1}], M12), M21 = maps:merge(M2,M1), ok = check_key_values(KVs1 ++ [{d,5}], M21), M34 = maps:merge(M3,M4), ok = check_key_values(KVs4 ++ [{a,1}], M34), M43 = maps:merge(M4,M3), ok = check_key_values(KVs3 ++ [{d,5}], M43), M14 = maps:merge(M1,M4), ok = check_key_values(KVs4 ++ [{a,1}], M14), M41 = maps:merge(M4,M1), ok = check_key_values(KVs1 ++ [{d,5}] ++ KVs, M41), [begin Ma = random_map(SzA, a), Mb = random_map(SzB, b), ok = merge_maps(Ma, Mb) end || SzA <- [3,10,20,100,200,1000], SzB <- [3,10,20,100,200,1000]], ok. % Generate random map with an average of Sz number of pairs: K -> {V,K} random_map(Sz, V) -> random_map_insert(#{}, 0, V, Sz*2). random_map_insert(M0, K0, _, Sz) when K0 > Sz -> M0; random_map_insert(M0, K0, V, Sz) -> Key = K0 + rand:uniform(3), random_map_insert(M0#{Key => {V,Key}}, Key, V, Sz). merge_maps(A, B) -> AB = maps:merge(A, B), %%io:format("A=~p\nB=~p\n",[A,B]), maps_foreach(fun(K,VB) -> VB = maps:get(K, AB) end, B), maps_foreach(fun(K,VA) -> case {maps:get(K, AB),maps:find(K, B)} of {VA, error} -> ok; {VB, {ok, VB}} -> ok end end, A), maps_foreach(fun(K,V) -> case {maps:find(K, A),maps:find(K, B)} of {{ok, V}, error} -> ok; {error, {ok, V}} -> ok; {{ok,_}, {ok, V}} -> ok end end, AB), ok. maps_foreach(Fun, Map) -> maps:fold(fun(K,V,_) -> Fun(K,V) end, void, Map). check_key_values([],_) -> ok; check_key_values([{K,V}|KVs],M) -> V = maps:get(K,M), check_key_values(KVs,M). merge_and_check_combo([]) -> ok; merge_and_check_combo([{Ks1,M1,Ks2,M2}|Cs]) -> M12 = maps:merge(M1,M2), ok = check_keys_exist(Ks1 ++ Ks2, M12), M21 = maps:merge(M2,M1), ok = check_keys_exist(Ks1 ++ Ks2, M21), true = M12 =:= M21, M12 = M21, merge_and_check_combo(Cs). build_keys_map_pairs([]) -> []; build_keys_map_pairs([Ks|Kss]) -> M = maps:from_list(keys_to_pairs(Ks)), ok = check_keys_exist(Ks, M), [{Ks,M}|build_keys_map_pairs(Kss)]. keys_to_pairs(Ks) -> [{K,K} || K <- Ks]. %% Maps module, not BIFs t_maps_fold(_Config) -> Vs = lists:seq(1,100), M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), %% fold 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M), ok. t_maps_map(_Config) -> Vs = lists:seq(1,100), M1 = maps:from_list([{I,I}||I<-Vs]), M2 = maps:from_list([{I,{token,I}}||I<-Vs]), M2 = maps:map(fun(_K,V) -> {token,V} end, M1), ok. t_maps_size(_Config) -> Vs = lists:seq(1,100), lists:foldl(fun(I,M) -> M1 = maps:put(I,I,M), I = maps:size(M1), M1 end, #{}, Vs), ok. t_maps_without(_Config) -> Ki = [11,22,33,44,55,66,77,88,99], M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]), M1 = maps:without([{k,I}||I <- Ki],M0), ok. %% MISC %% Verify that the the number of nodes in hashmaps %% of different types and sizes does not deviate too %% much from the theoretical model. t_hashmap_balance(_Config) -> io:format("Integer keys\n", []), hashmap_balance(fun(I) -> I end), io:format("Float keys\n", []), hashmap_balance(fun(I) -> float(I) end), io:format("String keys\n", []), hashmap_balance(fun(I) -> integer_to_list(I) end), io:format("Binary (big) keys\n", []), hashmap_balance(fun(I) -> <> end), io:format("Binary (little) keys\n", []), hashmap_balance(fun(I) -> <> end), io:format("Atom keys\n", []), erts_debug:set_internal_state(available_internal_state, true), hashmap_balance(fun(I) -> erts_debug:get_internal_state({atom,I}) end), erts_debug:set_internal_state(available_internal_state, false), ok. hashmap_balance(KeyFun) -> F = fun(I, {M0,Max0}) -> Key = KeyFun(I), M1 = M0#{Key => Key}, Max1 = case erts_internal:term_type(M1) of hashmap -> Nodes = hashmap_nodes(M1), Avg = maps:size(M1) * 0.4, StdDev = math:sqrt(maps:size(M1)) / 3, SD_diff = abs(Nodes - Avg) / StdDev, %%io:format("~p keys: ~p nodes avg=~p SD_diff=~p\n", %% [maps:size(M1), Nodes, Avg, SD_diff]), {MaxDiff0, _} = Max0, case {Nodes > Avg, SD_diff > MaxDiff0} of {true, true} -> {SD_diff, M1}; _ -> Max0 end; flatmap -> Max0 end, {M1, Max1} end, {_,{MaxDiff,MaxMap}} = lists:foldl(F, {#{}, {0, 0}}, lists:seq(1,10000)), io:format("Max std dev diff ~p for map of size ~p (nodes=~p, flatsize=~p)\n", [MaxDiff, maps:size(MaxMap), hashmap_nodes(MaxMap), erts_debug:flat_size(MaxMap)]), true = (MaxDiff < 6), % The probability of this line failing is about 0.000000001 % for a uniform hash. I've set the probability this "high" for now % to detect flaws in our make_internal_hash. % Hard limit is 15 (see hashmap_over_estimated_heap_size). ok. hashmap_nodes(M) -> Info = erts_debug:map_info(M), lists:foldl(fun(Tpl,Acc) -> case element(1,Tpl) of bitmaps -> Acc + element(2,Tpl); arrays -> Acc + element(2,Tpl); _ -> Acc end end, 0, Info). t_erts_internal_order(_Config) when is_list(_Config) -> -1 = erts_internal:cmp_term(1,2), 1 = erts_internal:cmp_term(2,1), 0 = erts_internal:cmp_term(2,2), -1 = erts_internal:cmp_term(1,a), 1 = erts_internal:cmp_term(a,1), 0 = erts_internal:cmp_term(a,a), -1 = erts_internal:cmp_term(1,1.0), 1 = erts_internal:cmp_term(1.0,1), 0 = erts_internal:cmp_term(1.0,1.0), -1 = erts_internal:cmp_term(1,1 bsl 65), 1 = erts_internal:cmp_term(1 bsl 65,1), 0 = erts_internal:cmp_term(1 bsl 65, 1 bsl 65), -1 = erts_internal:cmp_term(1 bsl 65,float(1)), 1 = erts_internal:cmp_term(float(1),1 bsl 65), -1 = erts_internal:cmp_term(1,float(1 bsl 65)), 1 = erts_internal:cmp_term(float(1 bsl 65),1), 0 = erts_internal:cmp_term(float(1 bsl 65), float(1 bsl 65)), %% reported errors -1 = erts_internal:cmp_term(0,2147483648), 0 = erts_internal:cmp_term(2147483648,2147483648), 1 = erts_internal:cmp_term(2147483648,0), M = #{0 => 0,2147483648 => 0}, true = M =:= binary_to_term(term_to_binary(M)), F1 = fun(_, _) -> 0 end, F2 = fun(_, _) -> 1 end, M0 = maps:from_list( [{-2147483649, 0}, {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0}, {<<>>, 0}]), M1 = maps:merge(M0, #{0 => 1}), 8 = maps:size(M1), 1 = maps:get(0,M1), ok. t_erts_internal_hash(_Config) when is_list(_Config) -> K1 = 0.0, K2 = 0.0/-1, M = maps:from_list([{I,I}||I<-lists:seq(1,32)]), M1 = M#{ K1 => a, K2 => b }, b = maps:get(K2,M1), M2 = M#{ K2 => a, K1 => b }, b = maps:get(K1,M2), %% test previously faulty hash list optimization M3 = M#{[0] => a, [0,0] => b, [0,0,0] => c, [0,0,0,0] => d}, a = maps:get([0],M3), b = maps:get([0,0],M3), c = maps:get([0,0,0],M3), d = maps:get([0,0,0,0],M3), M4 = M#{{[0]} => a, {[0,0]} => b, {[0,0,0]} => c, {[0,0,0,0]} => d}, a = maps:get({[0]},M4), b = maps:get({[0,0]},M4), c = maps:get({[0,0,0]},M4), d = maps:get({[0,0,0,0]},M4), M5 = M3#{[0,0,0] => e, [0,0,0,0] => f, [0,0,0,0,0] => g, [0,0,0,0,0,0] => h, [0,0,0,0,0,0,0] => i, [0,0,0,0,0,0,0,0] => j, [0,0,0,0,0,0,0,0,0] => k}, a = maps:get([0],M5), b = maps:get([0,0],M5), e = maps:get([0,0,0],M5), f = maps:get([0,0,0,0],M5), g = maps:get([0,0,0,0,0],M5), h = maps:get([0,0,0,0,0,0],M5), i = maps:get([0,0,0,0,0,0,0],M5), j = maps:get([0,0,0,0,0,0,0,0],M5), k = maps:get([0,0,0,0,0,0,0,0,0],M5), M6 = M4#{{[0,0,0]} => e, {[0,0,0,0]} => f, {[0,0,0,0,0]} => g, {[0,0,0,0,0,0]} => h, {[0,0,0,0,0,0,0]} => i, {[0,0,0,0,0,0,0,0]} => j, {[0,0,0,0,0,0,0,0,0]} => k}, a = maps:get({[0]},M6), b = maps:get({[0,0]},M6), e = maps:get({[0,0,0]},M6), f = maps:get({[0,0,0,0]},M6), g = maps:get({[0,0,0,0,0]},M6), h = maps:get({[0,0,0,0,0,0]},M6), i = maps:get({[0,0,0,0,0,0,0]},M6), j = maps:get({[0,0,0,0,0,0,0,0]},M6), k = maps:get({[0,0,0,0,0,0,0,0,0]},M6), M7 = maps:merge(M5,M6), a = maps:get([0],M7), b = maps:get([0,0],M7), e = maps:get([0,0,0],M7), f = maps:get([0,0,0,0],M7), g = maps:get([0,0,0,0,0],M7), h = maps:get([0,0,0,0,0,0],M7), i = maps:get([0,0,0,0,0,0,0],M7), j = maps:get([0,0,0,0,0,0,0,0],M7), k = maps:get([0,0,0,0,0,0,0,0,0],M7), a = maps:get({[0]},M7), b = maps:get({[0,0]},M7), e = maps:get({[0,0,0]},M7), f = maps:get({[0,0,0,0]},M7), g = maps:get({[0,0,0,0,0]},M7), h = maps:get({[0,0,0,0,0,0]},M7), i = maps:get({[0,0,0,0,0,0,0]},M7), j = maps:get({[0,0,0,0,0,0,0,0]},M7), k = maps:get({[0,0,0,0,0,0,0,0,0]},M7), ok. t_pdict(_Config) -> put(#{ a => b, b => a},#{ c => d}), put(get(#{ a => b, b => a}),1), 1 = get(#{ c => d}), #{ c := d } = get(#{ a => b, b => a}). t_ets(_Config) -> Tid = ets:new(map_table,[]), [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)], [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }), %% Test equal [3,4] = lists:sort( ets:select(Tid,[{{'$1','$2'}, [{'or',{'==','$1',#{ 3 => -3 }}, {'==','$1',#{ 4 => -4 }}}], ['$2']}])), %% Test match [30,50] = lists:sort( ets:select(Tid, [{{#{ 30 => -30}, '$1'},[],['$1']}, {{#{ 50 => -50}, '$1'},[],['$1']}] )), ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}), %% Test equal with map of different size [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), %% Test match with map of different size %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), %%% Test match with don't care value %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), %% Test is_map bif 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), ets:insert(Tid,{not_a_map,2}), 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), ets:insert(Tid,{{nope,a,tuple},2}), 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), %% Test map_size bif [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], [{map_size,'$1'}]}]), true = ets:delete(Tid,#{50 => -50}), [] = ets:lookup(Tid,#{50 => -50}), ets:delete(Tid), ok. t_dets(_Config) -> ok. t_tracing(_Config) -> dbg:stop_clear(), {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}), dbg:p(self(),c), %% Test basic map call {ok,_} = dbg:tpl(?MODULE,id,x), id(#{ a => b }), {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer), dbg:ctpl(), %% Test equals in argument list {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}], [{return_trace}]}]), id(#{ a => b }), id(#{ b => c }), {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer), dbg:ctpl(), %% Test match in head {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]), id(#{ a => b }), id(#{ b => c }), {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), dbg:ctpl(), % Test map guard bifs {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]), id(#{ a => b }), id({1,2}), id({#{ a => b},2}), {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), dbg:ctpl(), {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]), id(#{ a => b }), id({1,2}), id({#{ a => b},2}), id({#{ a => b, b => c},atom}), {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer), dbg:ctpl(), %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end), %dbg:tpl(?MODULE,id,MS), %id(#{ a => b }), %id(#{ b => c }), %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), %dbg:ctpl(), %% Check to extra messages timeout = getmsg(Tracer), dbg:stop_clear(), ok. getmsg(_Tracer) -> receive V -> V after 100 -> timeout end. trace_collector(Msg,Parent) -> io:format("~p~n",[Msg]), Parent ! Msg, Parent. t_has_map_fields(Config) when is_list(Config) -> true = has_map_fields_1(#{one=>1}), true = has_map_fields_1(#{one=>1,two=>2}), false = has_map_fields_1(#{two=>2}), false = has_map_fields_1(#{}), true = has_map_fields_2(#{c=>1,b=>2,a=>3}), true = has_map_fields_2(#{c=>1,b=>2,a=>3,x=>42}), false = has_map_fields_2(#{b=>2,c=>1}), false = has_map_fields_2(#{x=>y}), false = has_map_fields_2(#{}), true = has_map_fields_3(#{c=>1,b=>2,a=>3}), true = has_map_fields_3(#{c=>1,b=>2,a=>3,[]=>42}), true = has_map_fields_3(#{b=>2,a=>3,[]=>42,42.0=>43}), true = has_map_fields_3(#{a=>3,[]=>42,42.0=>43}), true = has_map_fields_3(#{[]=>42,42.0=>43}), false = has_map_fields_3(#{b=>2,c=>1}), false = has_map_fields_3(#{[]=>y}), false = has_map_fields_3(#{42.0=>x,a=>99}), false = has_map_fields_3(#{}), ok. has_map_fields_1(#{one:=_}) -> true; has_map_fields_1(#{}) -> false. has_map_fields_2(#{a:=_,b:=_,c:=_}) -> true; has_map_fields_2(#{}) -> false. has_map_fields_3(#{a:=_,b:=_}) -> true; has_map_fields_3(#{[]:=_,42.0:=_}) -> true; has_map_fields_3(#{}) -> false. y_regs(Config) when is_list(Config) -> Val = [length(Config)], Map0 = y_regs_update(#{}, Val), Map2 = y_regs_update(Map0, Val), Map3 = maps:from_list([{I,I*I} || I <- lists:seq(1, 100)]), Map4 = y_regs_update(Map3, Val), true = is_map(Map2) andalso is_map(Map4), gurka = y_regs_literal(0), gaffel = y_regs_literal(1), ok. y_regs_literal(Key) when is_integer(Key) -> %% Forces the key to be placed in a Y register. lists:seq(1, 2), case is_map_key(Key, #{ 0 => 0 }) of true -> gurka; false -> gaffel end. y_regs_update(Map0, Val0) -> Val1 = {t,Val0}, K1 = id({key,1}), K2 = id({key,2}), Map1 = Map0#{K1=>K1, a=>Val0,b=>Val0,c=>Val0,d=>Val0,e=>Val0, f=>Val0,g=>Val0,h=>Val0,i=>Val0,j=>Val0, k=>Val0,l=>Val0,m=>Val0,n=>Val0,o=>Val0, p=>Val0,q=>Val0,r=>Val0,s=>Val0,t=>Val0, u=>Val0,v=>Val0,w=>Val0,x=>Val0,y=>Val0, z=>Val0, aa=>Val0,ab=>Val0,ac=>Val0,ad=>Val0,ae=>Val0, af=>Val0,ag=>Val0,ah=>Val0,ai=>Val0,aj=>Val0, ak=>Val0,al=>Val0,am=>Val0,an=>Val0,ao=>Val0, ap=>Val0,aq=>Val0,ar=>Val0,as=>Val0,at=>Val0, au=>Val0,av=>Val0,aw=>Val0,ax=>Val0,ay=>Val0, az=>Val0, K2=>[a,b,c]}, Map2 = Map1#{K1=>K1, a:=Val1,b:=Val1,c:=Val1,d:=Val1,e:=Val1, f:=Val1,g:=Val1,h:=Val1,i:=Val1,j:=Val1, k:=Val1,l:=Val1,m:=Val1,n:=Val1,o:=Val1, p:=Val1,q:=Val1,r:=Val1,s:=Val1,t:=Val1, u:=Val1,v:=Val1,w:=Val1,x:=Val1,y:=Val1, z:=Val1, aa:=Val1,ab:=Val1,ac:=Val1,ad:=Val1,ae:=Val1, af:=Val1,ag:=Val1,ah:=Val1,ai:=Val1,aj:=Val1, ak:=Val1,al:=Val1,am:=Val1,an:=Val1,ao:=Val1, ap:=Val1,aq:=Val1,ar:=Val1,as:=Val1,at:=Val1, au:=Val1,av:=Val1,aw:=Val1,ax:=Val1,ay:=Val1, az:=Val1, K2=>[a,b,c]}, %% Traverse the maps to validate them. _ = erlang:phash2({Map1,Map2}, 100000), _ = id({K1,K2,Val0,Val1}), %Force use of Y registers. Map2. do_badmap(Test) -> Terms = [Test,fun erlang:abs/1,make_ref(),self(),0.0/id(-1), <<0:1024>>,<<1:1>>,<<>>,<<1,2,3>>, [],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3], [Test(T) || T <- Terms]. %% Test that a module compiled with the OTP 17 compiler will %% generate the correct 'badmap' exception. badmap_17(Config) -> case ?MODULE of map_SUITE -> do_badmap_17(Config); _ -> {skip,"Run in map_SUITE"} end. do_badmap_17(Config) -> Mod = badmap_17, DataDir = test_server:lookup_config(data_dir, Config), Beam = filename:join(DataDir, Mod), {module,Mod} = code:load_abs(Beam), do_badmap(fun Mod:update/1). %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. %% OTP-13763 t_hash_entropy(Config) when is_list(Config) -> %% entropy bug in 18.3, 19.0 M1 = maps:from_list([{#{"id" => I}, ok}||I <- lists:seq(1,50000)]), #{ #{"id" => 100} := ok, #{"id" => 200} := ok, #{"id" => 300} := ok, #{"id" => 400} := ok, #{"id" => 500} := ok, #{"id" => 600} := ok, #{"id" => 700} := ok, #{"id" => 800} := ok, #{"id" => 900} := ok, #{"id" => 25061} := ok, #{"id" => 39766} := ok } = M1, M0 = maps:from_list([{I,ok}||I <- lists:seq(1,33)]), M2 = maps:from_list([{M0#{"id" => I}, ok}||I <- lists:seq(1,50000)]), ok = maps:get(M0#{"id" => 100}, M2), ok = maps:get(M0#{"id" => 200}, M2), ok = maps:get(M0#{"id" => 300}, M2), ok = maps:get(M0#{"id" => 400}, M2), ok = maps:get(M0#{"id" => 500}, M2), ok = maps:get(M0#{"id" => 600}, M2), ok = maps:get(M0#{"id" => 700}, M2), ok = maps:get(M0#{"id" => 800}, M2), ok = maps:get(M0#{"id" => 900}, M2), ok = maps:get(M0#{"id" => 25061}, M2), ok = maps:get(M0#{"id" => 39766}, M2), ok. %% OTP-13146 %% Provoke major GC with a lot of "fat" maps on external format in msg queue %% causing heap fragments to be allocated. t_gc_rare_map_overflow(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]), erts_debug:set_internal_state(available_internal_state, true), try Echo = spawn_link(Node, fun Loop() -> receive {From,Msg} -> From ! Msg end, Loop() end), FatMap = fatmap(34), false = (flatmap =:= erts_internal:term_type(FatMap)), t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end), %% Repeat test for minor gc: t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> minor_collect() end), unlink(Echo), %% Test fatmap in exit signal Exiter = spawn_link(Node, fun Loop() -> receive {_From,Msg} -> "not_a_map" = Msg % badmatch! end, Loop() end), process_flag(trap_exit, true), Exiter ! {self(), FatMap}, {'EXIT', Exiter, {{badmatch,FatMap}, _}} = receive M -> M end, ok after process_flag(trap_exit, false), erts_debug:set_internal_state(available_internal_state, false), test_server:stop_node(Node) end. t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) -> Master = self(), true = receive _M -> false after 0 -> true end, % assert empty msg queue Echo ! {Master, token}, repeat(1000, fun(_) -> Echo ! {Master, FatMap} end, void), timer:sleep(100), % Wait for maps to arrive in our msg queue token = receive Tok -> Tok end, % and provoke move from outer to inner msg queue %% Do GC that will "overflow" and create heap frags due to all the fat maps GcFun(), %% Now check that all maps in msg queueu are intact %% Will crash emulator in OTP-18.1 repeat(1000, fun(_) -> FatMap = receive FM -> FM end end, void), ok. minor_collect() -> Before = minor_gcs(), erts_debug:set_internal_state(force_gc, self()), erlang:yield(), After = minor_gcs(), io:format("minor_gcs: ~p -> ~p\n", [Before, After]). minor_gcs() -> {garbage_collection, Info} = process_info(self(), garbage_collection), {minor_gcs, GCS} = lists:keyfind(minor_gcs, 1, Info), GCS. %% Generate a map with N (or N+1) keys that has an abnormal heap demand. %% Done by finding keys that collide in the first 32-bit hash. fatmap(N) -> %%erts_debug:set_internal_state(available_internal_state, true), Table = ets:new(void, [bag, private]), Seed0 = rand:seed_s(exsplus, {4711, 3141592, 2718281}), Seed1 = fatmap_populate(Table, Seed0, (1 bsl 16)), Keys = fatmap_generate(Table, Seed1, N, []), ets:delete(Table), maps:from_list([{K,K} || K <- Keys]). fatmap_populate(_, Seed, 0) -> Seed; fatmap_populate(Table, Seed, N) -> {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed), Hash = internal_hash(I), ets:insert(Table, [{Hash, I}]), fatmap_populate(Table, NextSeed, N-1). fatmap_generate(_, _, N, Acc) when N =< 0 -> Acc; fatmap_generate(Table, Seed, N0, Acc0) -> {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed), Hash = internal_hash(I), case ets:member(Table, Hash) of true -> NewKeys = [I | ets:lookup_element(Table, Hash, 2)], Acc1 = lists:usort(Acc0 ++ NewKeys), N1 = N0 - (length(Acc1) - length(Acc0)), fatmap_generate(Table, NextSeed, N1, Acc1); false -> fatmap_generate(Table, NextSeed, N0, Acc0) end. internal_hash(Term) -> erts_debug:get_internal_state({internal_hash, Term}). %% map external_format (fannerl). fannerl() -> <<131,116,0,0,0,28,100,0,13,108,101,97,114,110,105,110,103,95,114, 97,116,101,70,63,230,102,102,96,0,0,0,100,0,17,108,101,97,114,110,105,110, 103,95,109,111,109,101,110,116,117,109,70,0,0,0,0,0,0,0,0,100,0, 18,116,114,97,105,110,105,110,103,95,97,108,103,111,114,105,116,104,109,100,0, 16,102,97,110,110,95,116,114,97,105,110,95,114,112,114,111,112, 100,0,17,109,101,97,110,95,115,113,117,97,114,101,95,101,114,114,111,114,70, 0,0,0,0,0,0,0,0,100,0,8,98,105,116,95,102,97,105,108,97,0,100,0,20, 116,114,97,105,110,95,101,114,114,111,114,95,102,117,110,99,116,105,111, 110,100,0,19,102,97,110,110,95,101,114,114,111,114,102,117,110,99, 95,116,97,110,104,100,0,9,110,117,109,95,105,110,112,117,116,97,5,100,0,10,110, 117,109,95,111,117,116,112,117,116,97,3,100,0,13,116,111,116,97,108, 95,110,101,117,114,111,110,115,97,17,100,0,17,116,111,116,97,108,95,99,111,110, 110,101,99,116,105,111,110,115,97,66,100,0,12,110,101,116,119,111,114,107, 95,116,121,112,101,100,0,18,102,97,110,110,95,110,101,116,116,121,112,101, 95,108,97,121,101,114,100,0,15,99,111,110,110,101,99,116,105,111,110,95, 114,97,116,101,70,63,240,0,0,0,0,0,0,100,0,10,110,117,109,95,108,97,121,101, 114,115,97,3,100,0,19,116,114,97,105,110,95,115,116,111,112,95,102,117,110, 99,116,105,111,110,100,0,17,102,97,110,110,95,115,116,111,112,102,117,110, 99,95,109,115,101,100,0,15,113,117,105,99,107,112,114,111,112,95,100,101,99, 97,121,70,191,26,54,226,224,0,0,0,100,0,12,113,117,105,99,107,112,114, 111,112,95,109,117,70,63,252,0,0,0,0,0,0,100,0,21,114,112,114,111,112,95,105, 110,99,114,101,97,115,101,95,102,97,99,116,111,114,70,63,243,51,51, 64,0,0,0,100,0,21,114,112,114,111,112,95,100,101,99,114,101,97,115,101, 95,102,97,99,116,111,114,70,63,224,0,0,0,0,0,0,100,0,15,114,112,114,111,112, 95,100,101,108,116,97,95,109,105,110,70,0,0,0,0,0,0,0,0,100,0,15,114,112,114, 111,112,95,100,101,108,116,97,95,109,97,120,70,64,73,0,0,0,0,0,0,100,0, 16,114,112,114,111,112,95,100,101,108,116,97,95,122,101,114,111,70,63,185,153, 153,160,0,0,0,100,0,26,115,97,114,112,114,111,112,95,119,101,105,103, 104,116,95,100,101,99,97,121,95,115,104,105,102,116,70,192,26,147,116,192,0,0,0, 100,0,35,115,97,114,112,114,111,112,95,115,116,101,112,95,101,114, 114,111,114,95,116,104,114,101,115,104,111,108,100,95,102,97,99,116,111,114,70, 63,185,153,153,160,0,0,0,100,0,24,115,97,114,112,114,111,112,95,115, 116,101,112,95,101,114,114,111,114,95,115,104,105,102,116,70,63,246,40,245, 192,0,0,0,100,0,19,115,97,114,112,114,111,112,95,116,101,109,112,101,114, 97,116,117,114,101,70,63,142,184,81,224,0,0,0,100,0,6,108,97,121,101,114,115, 104,3,97,5,97,7,97,3,100,0,4,98,105,97,115,104,3,97,1,97,1,97,0,100,0,11, 99,111,110,110,101,99,116,105,111,110,115,116,0,0,0,66,104,2,97,0,97,6,70, 191,179,51,44,64,0,0,0,104,2,97,1,97,6,70,63,178,130,90,32,0,0,0,104,2,97,2, 97,6,70,63,82,90,88,0,0,0,0,104,2,97,3,97,6,70,63,162,91,63,192,0,0,0,104,2, 97,4,97,6,70,191,151,70,169,0,0,0,0,104,2,97,5,97,6,70,191,117,52,222,0,0,0, 0,104,2,97,0,97,7,70,63,152,240,139,0,0,0,0,104,2,97,1,97,7,70,191,166,31, 187,160,0,0,0,104,2,97,2,97,7,70,191,150,70,63,0,0,0,0,104,2,97,3,97,7,70, 63,152,181,126,128,0,0,0,104,2,97,4,97,7,70,63,151,187,162,128,0,0,0,104,2, 97,5,97,7,70,191,143,161,101,0,0,0,0,104,2,97,0,97,8,70,191,153,102,36,128,0, 0,0,104,2,97,1,97,8,70,63,160,139,250,64,0,0,0,104,2,97,2,97,8,70,63,164,62, 196,64,0,0,0,104,2,97,3,97,8,70,191,178,78,209,192,0,0,0,104,2,97,4,97,8,70, 191,185,19,76,224,0,0,0,104,2,97,5,97,8,70,63,183,142,196,96,0,0,0,104,2,97,0, 97,9,70,63,150,104,248,0,0,0,0,104,2,97,1,97,9,70,191,164,4,100,224,0,0,0, 104,2,97,2,97,9,70,191,169,42,42,224,0,0,0,104,2,97,3,97,9,70,63,145,54,78,128,0, 0,0,104,2,97,4,97,9,70,63,126,243,134,0,0,0,0,104,2,97,5,97,9,70,63,177, 203,25,96,0,0,0,104,2,97,0,97,10,70,63,172,104,47,64,0,0,0,104,2,97,1,97,10, 70,63,161,242,193,64,0,0,0,104,2,97,2,97,10,70,63,175,208,241,192,0,0,0,104,2, 97,3,97,10,70,191,129,202,161,0,0,0,0,104,2,97,4,97,10,70,63,178,151,55,32,0,0,0, 104,2,97,5,97,10,70,63,137,155,94,0,0,0,0,104,2,97,0,97,11,70,191,179, 106,160,0,0,0,0,104,2,97,1,97,11,70,63,184,253,164,96,0,0,0,104,2,97,2,97,11, 70,191,143,30,157,0,0,0,0,104,2,97,3,97,11,70,63,153,225,140,128,0,0,0,104, 2,97,4,97,11,70,63,161,35,85,192,0,0,0,104,2,97,5,97,11,70,63,175,200,55,192, 0,0,0,104,2,97,0,97,12,70,191,180,116,132,96,0,0,0,104,2,97,1,97,12,70,191, 165,151,152,0,0,0,0,104,2,97,2,97,12,70,191,180,197,91,160,0,0,0,104,2,97,3,97,12, 70,191,91,30,160,0,0,0,0,104,2,97,4,97,12,70,63,180,251,45,32,0,0,0, 104,2,97,5,97,12,70,63,165,134,77,64,0,0,0,104,2,97,6,97,14,70,63,181,56,242,96, 0,0,0,104,2,97,7,97,14,70,191,165,239,234,224,0,0,0,104,2,97,8,97,14, 70,191,154,65,216,128,0,0,0,104,2,97,9,97,14,70,63,150,250,236,0,0,0,0,104,2,97, 10,97,14,70,191,141,105,108,0,0,0,0,104,2,97,11,97,14,70,191,152,40, 165,0,0,0,0,104,2,97,12,97,14,70,63,141,159,46,0,0,0,0,104,2,97,13,97,14,70, 191,183,172,137,32,0,0,0,104,2,97,6,97,15,70,63,163,26,123,192,0,0,0,104, 2,97,7,97,15,70,63,176,184,106,32,0,0,0,104,2,97,8,97,15,70,63,152,234,144, 0,0,0,0,104,2,97,9,97,15,70,191,172,58,70,160,0,0,0,104,2,97,10,97,15,70, 63,161,211,211,192,0,0,0,104,2,97,11,97,15,70,191,148,171,120,128,0,0,0,104, 2,97,12,97,15,70,63,180,117,214,224,0,0,0,104,2,97,13,97,15,70,191,104, 230,216,0,0,0,0,104,2,97,6,97,16,70,63,178,53,103,96,0,0,0,104,2,97,7,97,16, 70,63,170,230,232,64,0,0,0,104,2,97,8,97,16,70,191,183,45,100,192,0,0,0, 104,2,97,9,97,16,70,63,184,100,97,32,0,0,0,104,2,97,10,97,16,70,63,169,174, 254,64,0,0,0,104,2,97,11,97,16,70,191,119,121,234,0,0,0,0,104,2,97,12,97, 16,70,63,149,12,170,128,0,0,0,104,2,97,13,97,16,70,191,144,193,191,0,0,0,0>>. %% This test case checks that the bug with ticket number OTP-15707 is %% fixed. The bug could cause a crash or memory usage to grow until %% the machine ran out of memory. t_large_unequal_bins_same_hash_bug(Config) when is_list(Config) -> run_when_enough_resources( fun() -> K1 = get_4GB_bin(1), K2 = get_4GB_bin(2), Map = make_map(500), Map2 = maps:put(K1, 42, Map), %% The map needed to contain at least 32 key-value pairs %% at this point to get the crash or out of memory %% problem on the next line Map3 = maps:put(K2, 43, Map2), %% The following line should avoid that the compiler %% optimizes away the above io:format("~p ~p~n", [erlang:phash2(Map3), maps:size(Map3)]) end). make_map(0) -> #{}; make_map(Size) -> maps:put(Size, Size, make_map(Size-1)). get_4GB_bin(Value) -> List = lists:duplicate(65536, Value), Bin = erlang:iolist_to_binary(List), IOList4GB = duplicate_iolist(Bin, 16), Bin4GB = erlang:iolist_to_binary(IOList4GB), 4294967296 = size(Bin4GB), Bin4GB. duplicate_iolist(IOList, 0) -> IOList; duplicate_iolist(IOList, NrOfTimes) -> duplicate_iolist([IOList, IOList], NrOfTimes - 1). run_when_enough_resources(Fun) -> case {total_memory(), erlang:system_info(wordsize)} of {Mem, 8} when is_integer(Mem) andalso Mem >= 31 -> Fun(); {Mem, WordSize} -> {skipped, io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)", [Mem, WordSize])} end. total_memory() -> %% Total memory in GB. try MemoryData = memsup:get_system_memory_data(), case lists:keysearch(total_memory, 1, MemoryData) of {value, {total_memory, TM}} -> TM div (1024*1024*1024); false -> {value, {system_total_memory, STM}} = lists:keysearch(system_total_memory, 1, MemoryData), STM div (1024*1024*1024) end catch _ : _ -> undefined end.