From 331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 23 May 2012 16:08:59 +0200 Subject: BEAM loader: Support preservation of extra operand in transforms It was not possible to preserve extra arguments in transformations. The following (hypothetical) example will now work: some_op Lit=c SizeArg Rest=* => move Lit x | some_op x SizeArg Rest --- erts/emulator/beam/beam_load.c | 18 ++++++++++++++++-- erts/emulator/utils/beam_makeops | 23 ++++++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 938fd8f2c9..58207ec75b 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4376,6 +4376,7 @@ transform_engine(LoaderState* st) Uint* restart; /* Where to restart if current match fails. */ GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */ GenOpArg* var = def_vars; + int num_vars = 0; int i; /* General index. */ Uint mask; GenOp* instr; @@ -4578,9 +4579,9 @@ transform_engine(LoaderState* st) { int n = *pc++; int formal_arity = gen_opc[instr->op].arity; - int num_vars = n + (instr->arity - formal_arity); int j = formal_arity; + num_vars = n + (instr->arity - formal_arity); var = erts_alloc(ERTS_ALC_T_LOADER_TMP, num_vars * sizeof(GenOpArg)); for (i = 0; i < n; i++) { @@ -4592,7 +4593,6 @@ transform_engine(LoaderState* st) } break; #endif - case TOP_next_arg: ap++; break; @@ -4680,6 +4680,20 @@ transform_engine(LoaderState* st) instr->a[ap].val = var[i].val; ap++; break; +#if defined(TOP_store_rest_args) + case TOP_store_rest_args: + { + int n = *pc++; + int num_extra = num_vars - n; + + ASSERT(n <= num_vars); + GENOP_ARITY(instr, instr->arity+num_extra); + memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); + memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg)); + ap += num_extra; + } + break; +#endif case TOP_try_me_else: restart = pc + 1; restart += *pc++; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 16a949c2a6..0b7c16f606 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1202,6 +1202,7 @@ sub parse_transformation { my($from, $to) = split(/\s*=>\s*/); my(@op); + my $rest_var; # The source instructions. @@ -1212,7 +1213,7 @@ sub parse_transformation { $_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist))); } else { (@op) = split; - $_ = &compile_transform(1, @op); + ($rest_var,$_) = compile_transform(1, $rest_var, @op); } } @@ -1230,7 +1231,7 @@ sub parse_transformation { @to = split(/\s*\|\s*/, $to); foreach (@to) { (@op) = split; - $_ = &compile_transform(0, @op); + (undef,$_) = compile_transform(0, $rest_var, @op); } } push(@transformations, [$., $orig, [@from], [reverse @to]]); @@ -1243,12 +1244,18 @@ sub compile_transform_function { } sub compile_transform { - my($src, $name, @ops) = @_; + my($src, $rest_var, $name, @ops) = @_; my $arity = 0; - + foreach (@ops) { my(@list) = &tr_parse_op($src, $_); - $arity++ unless $list[1] eq '*'; + if ($list[1] eq '*') { + $rest_var = $list[0]; + } elsif (defined $rest_var and $list[0] eq $rest_var) { + $list[1] = '*'; + } else { + $arity++; + } $_ = [ @list ]; } @@ -1260,7 +1267,7 @@ sub compile_transform { $is_transformed{$name,$arity} = 1; } - [$name,$arity,@ops]; + ($rest_var,[$name,$arity,@ops]); } sub tr_parse_op { @@ -1681,7 +1688,9 @@ sub tr_gen_to { foreach $op (@ops) { my($var, $type, $type_val) = @$op; - if ($var ne '') { + if ($type eq '*') { + push(@code, make_op($var, 'store_rest_args', $var{$var})); + } elsif ($var ne '') { &error($where, "variable '$var' unbound") unless defined $var{$var}; push(@code, &make_op($var, 'store_var_next_arg', $var{$var})); -- cgit v1.2.3 From 64ee859fc4c17259ab95192abf7493fed8f2b0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 May 2012 10:50:30 +0200 Subject: Implement support for maps in the compiler To make it possible to build the entire OTP system, also define dummys for the instructions in ops.tab. --- erts/emulator/beam/ops.tab | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index c29f3f9b1b..b52ecfaefc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1465,6 +1465,15 @@ fclearerror apply I apply_last I P +# +# Map instructions in R16. +# + +put_map Fail Src Dst Live Size Rest=* => jump Fail +is_map Fail Src => jump Fail +has_map_field Fail Src Key => jump Fail +get_map_element Fail Src Key Dst => jump Fail + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). -- cgit v1.2.3 From 345bf9d4634f81ad0343e8c60442bdd293e41835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 21 May 2013 17:00:11 +0200 Subject: erts: Maps beam-instruction definitions --- erts/emulator/beam/ops.tab | 67 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b52ecfaefc..b89bdb2e3a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1466,14 +1466,69 @@ apply I apply_last I P # -# Map instructions in R16. -# - -put_map Fail Src Dst Live Size Rest=* => jump Fail -is_map Fail Src => jump Fail -has_map_field Fail Src Key => jump Fail +# Map instructions in R17. +# + +# put_map Fail Src Dst Live Size Rest=* => jump Fail +# is_map Fail Src => jump Fail +# has_map_field Fail Src Key => jump Fail +# get_map_element Fail Src Key Dst => jump Fail + +put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest + +new_map j d I I +update_map j d d I I + +is_map Fail cq => jump Fail + +%macro: is_map IsMap -fail_action +is_map f r +is_map f x +is_map f y + +has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key +has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x + +%macro: i_has_map_field HasMapField -fail_action +i_has_map_field f r a +i_has_map_field f x a +i_has_map_field f y a +i_has_map_field f r r +i_has_map_field f x r +i_has_map_field f y r +i_has_map_field f r x +i_has_map_field f x x +i_has_map_field f y x +i_has_map_field f r y +i_has_map_field f x y +i_has_map_field f y y + +get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst +get_map_element Fail Src=rxy Key=rycq Dst => \ + move Key x | i_get_map_element Fail Src x Dst get_map_element Fail Src Key Dst => jump Fail +%macro: i_get_map_element GetMapElement -fail_action +i_get_map_element f r a r +i_get_map_element f x a r +i_get_map_element f y a r +i_get_map_element f r a x +i_get_map_element f x a x +i_get_map_element f y a x +i_get_map_element f r a y +i_get_map_element f x a y +i_get_map_element f y a y +i_get_map_element f r x r +i_get_map_element f x x r +i_get_map_element f y x r +i_get_map_element f r x x +i_get_map_element f x x x +i_get_map_element f y x x +i_get_map_element f r x y +i_get_map_element f x x y +i_get_map_element f y x y + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). -- cgit v1.2.3 From 03d5bb0d1bd5d7c4e09ee793a680f2d538fe0122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:09:55 +0200 Subject: erts: Initial Map instructions, type and structure --- erts/emulator/Makefile.in | 2 +- erts/emulator/beam/beam_emu.c | 341 ++++++++++++++++++++++++++++++++++- erts/emulator/beam/bif.tab | 6 + erts/emulator/beam/copy.c | 32 ++++ erts/emulator/beam/erl_bif_op.c | 12 +- erts/emulator/beam/erl_db_util.c | 1 + erts/emulator/beam/erl_debug.c | 1 + erts/emulator/beam/erl_gc.c | 1 + erts/emulator/beam/erl_gc.h | 37 ++-- erts/emulator/beam/erl_map.c | 225 +++++++++++++++++++++++ erts/emulator/beam/erl_map.h | 64 +++++++ erts/emulator/beam/erl_printf_term.c | 52 +++++- erts/emulator/beam/erl_term.c | 6 +- erts/emulator/beam/erl_term.h | 44 +++-- erts/emulator/beam/utils.c | 41 ++++- erts/emulator/hipe/hipe_bif2.c | 1 + erts/emulator/hipe/hipe_debug.c | 1 + 17 files changed, 811 insertions(+), 56 deletions(-) create mode 100644 erts/emulator/beam/erl_map.c create mode 100644 erts/emulator/beam/erl_map.h (limited to 'erts') diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index b270099566..54365f279c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -780,7 +780,7 @@ RUN_OBJS = \ $(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \ $(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \ $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \ - $(OBJDIR)/erl_ptab.o + $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o ifeq ($(TARGET),win32) DRV_OBJS = \ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7fecdd5c5f..5c955b9566 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -31,6 +31,7 @@ #include "big.h" #include "beam_load.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_bits.h" #include "dist.h" #include "beam_bp.h" @@ -701,6 +702,19 @@ extern int count_instructions; Fail; \ } +#define IsMap(Src, Fail) if (is_not_map(Src)) { Fail; } + +#define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; } + +#define GetMapElement(Src, Key, Dst, Fail) \ + do { \ + Eterm _res = get_map_element(Src, Key); \ + if (is_non_value(_res)) { \ + Fail; \ + } \ + Dst = _res; \ + } while (0) + #define IsFunction(X, Action) \ do { \ if ( !(is_any_fun(X)) ) { \ @@ -944,7 +958,11 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg) NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; - +static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; +static Eterm update_map(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static int has_not_map_field(Eterm map, Eterm key); +static Eterm get_map_element(Eterm map, Eterm key); /* * Functions not directly called by process_main(). OK to inline. @@ -2323,6 +2341,37 @@ void process_main(void) Goto(*I); } + OpCase(new_map_jdII): { + Eterm res; + + x(0) = r(0); + SWAPOUT; + res = new_map(c_p, reg, I); + SWAPIN; + r(0) = x(0); + StoreResult(res, Arg(1)); + Next(4+Arg(3)); + } + + OpCase(update_map_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map(c_p, reg, map, I); + SWAPIN; + if (res) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto lb_Cl_error; + } + } + + /* * All guards with zero arguments have special instructions: * self/0 @@ -6227,6 +6276,296 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } +static int has_not_map_field(Eterm map, Eterm key) +{ + map_t* mp; + Eterm* keys; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + keys = map_get_keys(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (keys[i] == key) { + return 0; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(keys[i], key)) { + return 0; + } + } + } + return 1; +} + +static Eterm get_map_element(Eterm map, Eterm key) +{ + map_t *mp; + Eterm* ks, *vs; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (ks[i] == key) { + return vs[i]; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(ks[i], key)) { + return vs[i]; + } + } + } + return THE_NON_VALUE; +} + +#define GET_TERM(term, dest) \ +do { \ + Eterm src = term; \ + switch (src & _TAG_IMMED1_MASK) { \ + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(0); \ + break; \ + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(src >> _TAG_IMMED1_SIZE); \ + break; \ + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = y(src >> _TAG_IMMED1_SIZE); \ + break; \ + default: \ + dest = src; \ + break; \ + } \ +} while(0) + + +static Eterm +new_map(Process* p, Eterm* reg, BeamInstr* I) +{ + Uint n = Arg(3); + Uint i; + Uint need = n + 1 /* hdr */ + 1 /*size*/ + 1 /* ptr */ + 1 /* arity */; + Eterm keys; + Eterm *mhp,*thp; + Eterm *E; + Eterm *tp; + map_t *mp; + + if (HeapWordsLeft(p) < need) { + erts_garbage_collect(p, need, reg, Arg(2)); + } + + thp = p->htop; + mhp = thp + 1 + n/2; + E = p->stop; + tp = &Arg(4); + keys = make_tuple(thp); + *thp++ = make_arityval(n/2); + + mp = (map_t *)mhp; mhp += 3; + mp->thing_word = MAP_HEADER; + mp->size = n/2; + mp->keys = keys; + + for (i = 0; i < n/2; i++) { + GET_TERM(*tp++, *thp++); + GET_TERM(*tp++, *mhp++); + } + p->htop = mhp; + return make_map(mp); +} + + +/* This entire instruction will be split into two. + * 1) update_map_exact (literals) <- this can be much more optimized + * 2) update_map_assoc (literals) + * Also update_map is pretty bad code as it stands now. + */ + +static Eterm +update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint num_updates; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + Eterm* kp; + + if (is_not_map(map)) { + return 0; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate heap space for the worst case (i.e. all keys are new). + */ + + num_updates = Arg(4) / 2; + need = 2*(num_old+num_updates) + 4; + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, optimistically assuming that there are no + * new keys, allowing us to keep the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + ASSERT(num_updates > 0); + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + n = num_updates; + + for (i = 0; i < num_old; i++) { + if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { + /* not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + new_key = THE_NON_VALUE; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * If we have exhausted the update list we are done. + */ + + if (n == 0) { + p->htop = hp; + return res; + } + + /* + * There were some new keys. We'll have to start over and rebuild + * the key tuple too. + */ + + kp = p->htop; + *kp++ = make_arityval(0); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->keys = make_tuple(kp-1); + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + n = num_updates; + + for (;;) { + Eterm key; + Sint c; + + ASSERT(kp < (Eterm *)mp); + key = *old_keys; + if ((c = cmp(key, new_key)) < 0) { + *kp++ = key; + *hp++ = *old_vals; + old_keys++, old_vals++, num_old--; + } else { /* Replace or insert new */ + *kp++ = new_key; + GET_TERM(new_p[1], *hp++); + if (c == 0) { /* If replacement */ + old_keys++, old_vals++, num_old--; + } + n--; + if (n == 0) { + break; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + if (num_old == 0) { + break; + } + } + + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + + /* + * All updates done. Now copy the remaining part of the frame's + * keys and values. + */ + + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } + if ((n = (Eterm *)mp - kp) > 0) { + *kp = make_pos_bignum_header(n-1); + } + n = kp - p->htop - 1; /* Actual number of keys/values */ + *p->htop = make_arityval(n); + mp->thing_word = MAP_HEADER; + mp->size = n; + p->htop = hp; + return res; +} +#undef GET_TERM int catchlevel(Process *p) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 3ec534f0bc..42515d0494 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -578,6 +578,12 @@ bif os:unsetenv/1 bif re:inspect/2 +ubif erlang:is_map/1 +bif map:to_list/1 +bif map:new/0 +bif map:get/2 +bif map:put/3 + # # Obsolete # diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 23c0fca6aa..3a987e213b 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -27,6 +27,7 @@ #include "erl_process.h" #include "erl_gc.h" #include "big.h" +#include "erl_map.h" #include "erl_binary.h" #include "erl_bits.h" #include "dtrace-wrapper.h" @@ -150,6 +151,24 @@ Uint size_object(Eterm obj) goto pop_next; } break; + case MAP_SUBTAG: + { + Uint n; + map_t *mp; + mp = (map_t*)map_val_rel(obj,base); + ptr = (Eterm *)mp; + n = map_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + ESTACK_PUSH(s, obj); + } + } + goto pop_next; + } + break; case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_object: matchstate term not allowed"); @@ -318,6 +337,15 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } break; + case MAP_SUBTAG: + { + i = map_get_size(objp) + 3; + *argp = make_map_rel(htop, dst_base); + while (i--) { + *htop++ = *objp++; + } + } + break; case REFC_BINARY_SUBTAG: { ProcBin* pb; @@ -537,6 +565,10 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } goto off_heap_common; + case MAP_SUBTAG: + *hp++ = *tp++; + sz--; + break; case EXTERNAL_PID_SUBTAG: case EXTERNAL_PORT_SUBTAG: case EXTERNAL_REF_SUBTAG: diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index adac0052d6..37dd6457db 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -36,6 +36,7 @@ #include "dist.h" #include "erl_version.h" #include "erl_binary.h" +#include "erl_map.h" BIF_RETTYPE and_2(BIF_ALIST_2) { @@ -321,7 +322,10 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3) BIF_RET(am_false); } - - - - +BIF_RETTYPE is_map_1(BIF_ALIST_1) +{ + if (is_map(BIF_ARG_1)) { + BIF_RET(am_true); + } + BIF_RET(am_false); +} diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a358ecf326..d903053aa4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -35,6 +35,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_thr_progress.h" #include "erl_db_util.h" diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 873a9860da..50bdc79506 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -29,6 +29,7 @@ #include "bif.h" #include "beam_catches.h" #include "erl_debug.h" +#include "erl_map.h" #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y)) diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index ab8448e8a1..2022f70cbb 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -28,6 +28,7 @@ #include "beam_catches.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "error.h" #include "big.h" #include "erl_gc.h" diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 1801df359a..5203dda263 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -20,6 +20,8 @@ #ifndef __ERL_GC_H__ #define __ERL_GC_H__ +#include "erl_map.h" + /* GC declarations shared by beam/erl_gc.c and hipe/hipe_gc.c */ #if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -42,23 +44,24 @@ do { \ HTOP += 2; /* update tospace htop */ \ } while(0) -#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ -do { \ - Eterm gval; \ - Sint nelts; \ - \ - ASSERT(is_header(HDR)); \ - gval = make_boxed(HTOP); \ - *ORIG = gval; \ - *HTOP++ = HDR; \ - *PTR++ = gval; \ - nelts = header_arity(HDR); \ - switch ((HDR) & _HEADER_SUBTAG_MASK) { \ - case SUB_BINARY_SUBTAG: nelts++; break; \ - case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR-1))->num_free+1; break; \ - } \ - while (nelts--) \ - *HTOP++ = *PTR++; \ +#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ +do { \ + Eterm gval; \ + Sint nelts; \ + \ + ASSERT(is_header(HDR)); \ + nelts = header_arity(HDR); \ + switch ((HDR) & _HEADER_SUBTAG_MASK) { \ + case SUB_BINARY_SUBTAG: nelts++; break; \ + case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break; \ + case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break; \ + } \ + gval = make_boxed(HTOP); \ + *ORIG = gval; \ + *HTOP++ = HDR; \ + *PTR++ = gval; \ + while (nelts--) *HTOP++ = *PTR++; \ + \ } while(0) #define in_area(ptr,start,nbytes) \ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c new file mode 100644 index 0000000000..1fd7943c30 --- /dev/null +++ b/erts/emulator/beam/erl_map.c @@ -0,0 +1,225 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + * + * Author: Björn-Egil Dahlberg + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "erl_process.h" +#include "error.h" +#include "bif.h" + + +#include "erl_map.h" + + +BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Uint n; + Eterm* hp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + hp = HAlloc(BIF_P, (2 + 3) * n); + res = NIL; + + while(n--) { + tup = TUPLE2(hp, ks[n], vs[n]); hp += 3; + res = CONS(hp, tup, res); hp += 2; + } + + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +BIF_RETTYPE map_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, error; + map_t *mp; + Uint n,i; + char *s_error; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + goto error; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(vs[i]); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(vs[i]); + } + } +error: + + s_error = "bad_key"; + error = am_atom_put(s_error, sys_strlen(s_error)); + + hp = HAlloc(BIF_P, 3); + BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_ERROR(BIF_P, EXC_ERROR_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, key, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + hp = HAlloc(BIF_P, 4 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = 1; + *hp++ = tup; + *hp++ = BIF_ARG_2; + + BIF_RET(res); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (c) + BIF_RET(res); + + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); + + hp = HAlloc(BIF_P, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + ASSERT(n >= 0); + + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } + + *shp++ = key; + *hp++ = BIF_ARG_2; + + ASSERT(n >= 0); + + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; + } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h new file mode 100644 index 0000000000..ca8ce0f974 --- /dev/null +++ b/erts/emulator/beam/erl_map.h @@ -0,0 +1,64 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + + +#ifndef __ERL_MAP_H__ +#define __ERL_MAP_H__ + +#include "sys.h" +/* MAP */ + +typedef struct map_s { + Eterm thing_word; + Uint size; + Eterm keys; /* tuple */ +} map_t; +/* map node + * + * ----------- + * Eterm THING + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * ---- + * Eterm V1 + * ... + * Eterm Vn, where n = arity + * ----------- + */ + + + +/* erl_term.h stuff */ +#define make_map(x) make_boxed((Eterm*)(x)) +#define make_map_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) +#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val((x)))) +#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) +#define is_not_map(x) (!is_map((x))) +#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) +#define header_is_map(x) ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG)) +#define map_val(x) (_unchecked_boxed_val((x))) +#define map_val_rel(RTERM, BASE) map_val(rterm2wterm(RTERM, BASE)) + +#define map_get_values(x) (((Eterm *)(x)) + 3) +#define map_get_keys(x) (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1) +#define map_get_size(x) (((map_t*)(x))->size) + +#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) + +#endif + diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 436147749e..d18760dc43 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -24,6 +24,7 @@ #include "erl_printf_term.h" #include "sys.h" #include "big.h" +#include "erl_map.h" #define PRINT_CHAR(CNT, FN, ARG, C) \ do { \ @@ -216,14 +217,15 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) } -#define PRT_BAR ((Eterm) 0) -#define PRT_COMMA ((Eterm) 1) -#define PRT_CLOSE_LIST ((Eterm) 2) -#define PRT_CLOSE_TUPLE ((Eterm) 3) -#define PRT_TERM ((Eterm) 4) -#define PRT_ONE_CONS ((Eterm) 5) -#define PRT_PATCH_FUN_SIZE ((Eterm) 6) -#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */ +#define PRT_BAR ((Eterm) 0) +#define PRT_COMMA ((Eterm) 1) +#define PRT_CLOSE_LIST ((Eterm) 2) +#define PRT_CLOSE_TUPLE ((Eterm) 3) +#define PRT_ASSOC ((Eterm) 4) +#define PRT_TERM ((Eterm) 5) +#define PRT_ONE_CONS ((Eterm) 6) +#define PRT_PATCH_FUN_SIZE ((Eterm) 7) +#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */ static int print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, @@ -260,6 +262,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, case PRT_CLOSE_TUPLE: PRINT_CHAR(res, fn, arg, '}'); goto L_outer_loop; + case PRT_ASSOC: + PRINT_STRING(res, fn, arg, "=>"); + goto L_outer_loop; default: popped.word = WSTACK_POP(s); @@ -483,6 +488,37 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_CHAR(res, fn, arg, '>'); } break; + case MAP_DEF: + { + Uint n; + Eterm *ks, *vs; + map_t *mp = (map_t *)map_val(wobj); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + PRINT_CHAR(res, fn, arg, '#'); + PRINT_CHAR(res, fn, arg, '{'); + WSTACK_PUSH(s, PRT_CLOSE_TUPLE); + if (n > 0) { + n--; + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + + while (n--) { + WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + } + } + } + break; default: PRINT_STRING(res, fn, arg, " #include @@ -85,7 +86,10 @@ unsigned tag_val_def(Wterm x) case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF; case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF; case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF; - default: return BINARY_DEF; + case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; } break; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 50d3e63c58..f10a3a9d38 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -135,11 +135,12 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define REF_SUBTAG (0x4 << _TAG_PRIMARY_SIZE) /* REF */ #define FUN_SUBTAG (0x5 << _TAG_PRIMARY_SIZE) /* FUN */ #define FLOAT_SUBTAG (0x6 << _TAG_PRIMARY_SIZE) /* FLOAT */ -#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ +#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ #define _BINARY_XXX_MASK (0x3 << _TAG_PRIMARY_SIZE) #define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */ #define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */ #define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */ +#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */ #define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */ #define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */ #define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */ @@ -155,6 +156,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) #define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) #define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) +#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) #define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) #define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) @@ -354,7 +356,10 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm) #define is_value(x) ((x) != THE_NON_VALUE) /* binary object access methods */ -#define is_binary_header(x) (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_REFC_BIN) +#define is_binary_header(x) \ + ((((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_REFC_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HEAP_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_SUB_BIN)) #define make_binary(x) make_boxed((Eterm*)(x)) #define is_binary(x) (is_boxed((x)) && is_binary_header(*boxed_val((x)))) #define is_not_binary(x) (!is_binary((x))) @@ -1064,8 +1069,8 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) /* * Backwards compatibility definitions: - * - #define virtal *_DEF constants with values that fit term order: - * number < atom < ref < fun < port < pid < tuple < nil < cons < binary + * - #define virtual *_DEF constants with values that fit term order: + * number < atom < ref < fun < port < pid < tuple < map < nil < cons < binary * - tag_val_def() function generates virtual _DEF tag * - not_eq_tags() and NUMBER_CODE() defined in terms * of the tag_val_def() function @@ -1074,19 +1079,20 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) #define BINARY_DEF 0x0 #define LIST_DEF 0x1 #define NIL_DEF 0x2 -#define TUPLE_DEF 0x3 -#define PID_DEF 0x4 -#define EXTERNAL_PID_DEF 0x5 -#define PORT_DEF 0x6 -#define EXTERNAL_PORT_DEF 0x7 -#define EXPORT_DEF 0x8 -#define FUN_DEF 0x9 -#define REF_DEF 0xa -#define EXTERNAL_REF_DEF 0xb -#define ATOM_DEF 0xc -#define FLOAT_DEF 0xd -#define BIG_DEF 0xe -#define SMALL_DEF 0xf +#define MAP_DEF 0x3 +#define TUPLE_DEF 0x4 +#define PID_DEF 0x5 +#define EXTERNAL_PID_DEF 0x6 +#define PORT_DEF 0x7 +#define EXTERNAL_PORT_DEF 0x8 +#define EXPORT_DEF 0x9 +#define FUN_DEF 0xa +#define REF_DEF 0xb +#define EXTERNAL_REF_DEF 0xc +#define ATOM_DEF 0xd +#define FLOAT_DEF 0xe +#define BIG_DEF 0xf +#define SMALL_DEF 0x10 #if ET_DEBUG extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); @@ -1096,8 +1102,8 @@ extern unsigned tag_val_def(Wterm); #endif #define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y))) -#define NUMBER_CODE(x,y) ((tag_val_def(x) << 4) | tag_val_def(y)) -#define _NUMBER_CODE(TX,TY) ((TX << 4) | TY) +#define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y)) +#define _NUMBER_CODE(TX,TY) ((TX << 5) | TY) #define SMALL_SMALL _NUMBER_CODE(SMALL_DEF,SMALL_DEF) #define SMALL_BIG _NUMBER_CODE(SMALL_DEF,BIG_DEF) #define SMALL_FLOAT _NUMBER_CODE(SMALL_DEF,FLOAT_DEF) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index e0776cf67d..6cdfd1c989 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -31,6 +31,7 @@ #include "bif.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "packet_parser.h" #include "erl_gc.h" #define ERTS_WANT_DB_INTERNAL__ @@ -785,10 +786,10 @@ Uint32 make_hash(Eterm term_arg) unsigned op; /* Must not collide with the real tag_val_def's: */ -#define MAKE_HASH_TUPLE_OP 0x10 -#define MAKE_HASH_FUN_OP 0x11 -#define MAKE_HASH_CDR_PRE_OP 0x12 -#define MAKE_HASH_CDR_POST_OP 0x13 +#define MAKE_HASH_TUPLE_OP 0x11 +#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_CDR_PRE_OP 0x13 +#define MAKE_HASH_CDR_POST_OP 0x14 /* ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit @@ -2007,6 +2008,18 @@ tailrecur_ne: ++bb; goto term_array; } + case MAP_SUBTAG: + { + aa = map_val_rel(a, a_base); + if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + goto not_equal; + bb = map_val_rel(b,b_base); + if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next; + aa += 2; + bb += 2; + sz += 1; /* increment for tuple-keys */ + goto term_array; + } case REFC_BINARY_SUBTAG: case HEAP_BINARY_SUBTAG: case SUB_BINARY_SUBTAG: @@ -2281,7 +2294,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2) * * According to the Erlang Standard, types are orderered as follows: * numbers < (characters) < atoms < refs < funs < ports < pids < - * tuples < [] < conses < binaries. + * tuples < maps < [] < conses < binaries. * * Note that characters are currently not implemented. * @@ -2464,7 +2477,25 @@ tailrecur_ne: ++aa; ++bb; goto term_array; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) : + if (!is_map_rel(b,b_base)) { + a_tag = MAP_DEF; + goto mixed_types; + } + aa = (Eterm *)map_val_rel(a,a_base); + bb = (Eterm *)map_val_rel(b,b_base); + i = map_get_size((map_t*)aa); + if (i != map_get_size((map_t*)bb)) { + RETURN_NEQ((int)(i - map_get_size((map_t*)bb))); + } + if (i == 0) { + goto pop_next; + } + aa += 2; + bb += 2; + i += 1; /* increment for tuple-keys */ + goto term_array; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): if (!is_float_rel(b,b_base)) { a_tag = FLOAT_DEF; diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index e09988e2c5..c3687681cf 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -31,6 +31,7 @@ #include "erl_process.h" #include "bif.h" #include "big.h" +#include "erl_map.h" #include "hipe_debug.h" #include "hipe_mode_switch.h" #include "hipe_arch.h" diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index bf25ba82af..32694a8f97 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -36,6 +36,7 @@ #include "beam_load.h" #include "hipe_mode_switch.h" #include "hipe_debug.h" +#include "erl_map.h" static const char dashes[2*sizeof(long)+5] = { [0 ... 2*sizeof(long)+3] = '-' -- cgit v1.2.3 From 92303a2e1abdf74aa3bc3af095131a59a601c45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 19:27:54 +0200 Subject: erts: Add phash2 Map functionality --- erts/emulator/beam/utils.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6cdfd1c989..bc7e91295d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1088,6 +1088,7 @@ make_hash2(Eterm term) #define HCONST_13 0x08d12e65UL #define HCONST_14 0xa708a81eUL #define HCONST_15 0x454021d7UL +#define HCONST_16 0xe3779b90UL #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ @@ -1190,6 +1191,28 @@ make_hash2(Eterm term) term = elem[1]; } break; + case MAP_SUBTAG: + { + map_t *mp = (map_t *)map_val(term); + int i; + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + UINT32_HASH(size, HCONST_16); + if (size == 0) /* Empty Map */ + goto hash2_common; + + for (i = size - 1; i >= 0; i--) { + tmp = vs[i]; + ESTACK_PUSH(s, tmp); + } + for (i = size - 1; i >= 1; i--) { + tmp = ks[i]; + ESTACK_PUSH(s, tmp); + } + term = ks[0]; + } + break; case EXPORT_SUBTAG: { Export* ep = *((Export **) (export_val(term) + 1)); -- cgit v1.2.3 From 63ef0bbfdfb70673fe7f3ce2fc6fa4f0f801747d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 19 Sep 2013 17:48:28 +0200 Subject: erts: Add the size-testing guard BIF map_size/1 --- erts/emulator/beam/beam_emu.c | 2 ++ erts/emulator/beam/beam_load.c | 2 ++ erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_bif_guard.c | 23 +++++++++++++++++++++++ erts/emulator/beam/erl_map.c | 23 +++++++++++++++++++++-- erts/emulator/beam/global.h | 1 + 6 files changed, 50 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 5c955b9566..1cad31be2c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5080,6 +5080,8 @@ translate_gc_bif(void* gcf) return bit_size_1; } else if (gcf == erts_gc_byte_size_1) { return byte_size_1; + } else if (gcf == erts_gc_map_size_1) { + return map_size_1; } else if (gcf == erts_gc_abs_1) { return abs_1; } else if (gcf == erts_gc_float_1) { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 58207ec75b..b589d1c930 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3783,6 +3783,8 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, op->a[1].val = (BeamInstr) (void *) erts_gc_bit_size_1; } else if (bf == byte_size_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_byte_size_1; + } else if (bf == map_size_1) { + op->a[1].val = (BeamInstr) (void *) erts_gc_map_size_1; } else if (bf == abs_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_abs_1; } else if (bf == float_1) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 42515d0494..a8623fcf61 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -579,6 +579,7 @@ bif os:unsetenv/1 bif re:inspect/2 ubif erlang:is_map/1 +ubif erlang:map_size/1 bif map:to_list/1 bif map:new/0 bif map:get/2 diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index a715756c15..bbd8aa31d9 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -33,6 +33,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live); @@ -455,6 +456,28 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live) } } +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) +{ + Eterm arg = reg[live]; + if (is_map(arg)) { + map_t *mp = (map_t*)map_val(arg); + Uint size = map_get_size(mp); + if (IS_USMALL(0, size)) { + return make_small(size); + } else { + Eterm* hp; + if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { + erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); + } + hp = p->htop; + p->htop += BIG_UINT_HEAP_SIZE; + return uint_to_big(size, hp); + } + } else { + BIF_ERROR(p, BADARG); + } +} + Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live) { Eterm arg; diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 1fd7943c30..6a59fa29b8 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -30,10 +30,8 @@ #include "error.h" #include "bif.h" - #include "erl_map.h" - BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -58,6 +56,27 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + BIF_RETTYPE map_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 83a8911a36..8fcb95d0e2 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -998,6 +998,7 @@ Eterm erts_gc_length_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_bit_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live); +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_float_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live); -- cgit v1.2.3 From 43ed0cc716039c3b2f65a5395f00424169639309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:11:39 +0200 Subject: erts: Add the type-testing guard BIF is_map/1 To add a type-testing guard BIF, the following steps are needed: * The BIF itself is added to bif.tab (note that it should be declared using "ubif", not "bif"), and its implementation to erl_bif_op.c. * erl_internal must be modified in 3 places: The type test must be recognized as guard BIF, as a type test, and it must be auto-imported. * There must be an instruction that implements the same type test as the BIF (it will be used in guards). beam_utils:bif_to_test/3 must be updated to recognize the new guard BIF. --- erts/emulator/beam/bif.tab | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index a8623fcf61..8f85f931c9 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,6 +572,7 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 + # # New in R17A # -- cgit v1.2.3 From bc0960c095e9482ba0f452c9df3623f5c9c54e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 30 Sep 2013 20:13:07 +0200 Subject: erts: Introduce more Maps BIFs * map:remove/2 * map:keys/1 * map:values/1 * map:is_key/2 * map:update/3 - Equivalent to ':=' operator in #{ K := V } maps. * map:from_list/1 - map:from_list/1 takes any unsorted key/value list, [{K,V}], and produces a map. Duplicate keys are removed. The latest key is kept. * map:find/2 - Searches for a pair that *equals* input key. * map:merge/2 - Merge two maps to one map. --- erts/emulator/beam/bif.tab | 11 +- erts/emulator/beam/erl_map.c | 565 ++++++++++++++++++++++++++++++++++++++++--- erts/emulator/beam/erl_map.h | 6 +- 3 files changed, 547 insertions(+), 35 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8f85f931c9..306861a4c5 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,7 +572,6 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 - # # New in R17A # @@ -582,9 +581,17 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 bif map:to_list/1 -bif map:new/0 +bif map:find/2 bif map:get/2 +bif map:from_list/1 +bif map:is_key/2 +bif map:keys/1 +bif map:merge/2 +bif map:new/0 bif map:put/3 +bif map:remove/2 +bif map:update/3 +bif map:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6a59fa29b8..6fd60f0689 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -32,6 +32,57 @@ #include "erl_map.h" +/* BIFs + * + * DONE: + * - erlang:is_map/1 + * - erlang:map_size/1 + * + * - map:find/2 + * - map:from_list/1 + * - map:get/2 + * - map:is_key/2 + * - map:keys/1 + * - map:merge/2 + * - map:new/0 + * - map:put/3 + * - map:remove/2 + * - map:to_list/1 + * - map:update/3 + * - map:values/1 + * + * TODO: + * - map:foldl/3 + * - map:foldr/3 + * - map:map/3 + * - map:size/1 + * - map:without/2 + * + */ + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:to_list/1 + */ + BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -56,43 +107,40 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } - -/* erlang:map_size/1 - * the corresponding instruction is implemented in: - * beam/erl_bif_guard.c +/* map:find/2 + * return value if key *equals* a key in the map */ -BIF_RETTYPE map_size_1(BIF_ALIST_1) { - if (is_map(BIF_ARG_1)) { - Eterm *hp; - Uint hsz = 0; - map_t *mp = (map_t*)map_val(BIF_ARG_1); - Uint n = map_get_size(mp); +BIF_RETTYPE map_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, res; + map_t *mp; + Uint n,i; - erts_bld_uint(NULL, &hsz, n); - hp = HAlloc(BIF_P, hsz); - BIF_RET(erts_bld_uint(&hp, NULL, n)); - } + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = vs[i]; + BIF_RET(res); + } + } + BIF_RET(am_error); + } BIF_ERROR(BIF_P, BADARG); } - -BIF_RETTYPE map_new_0(BIF_ALIST_0) { - Eterm* hp; - Eterm tup; - map_t *mp; - - hp = HAlloc(BIF_P, (3 + 1)); - tup = make_tuple(hp); - *hp++ = make_arityval(0); - - mp = (map_t*)hp; - mp->thing_word = MAP_HEADER; - mp->size = 0; - mp->keys = tup; - - BIF_RET(make_map(mp)); -} +/* map:get/2 + * return value if key *matches* a key in the map + * exception bad_key if none matches + */ BIF_RETTYPE map_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { @@ -136,6 +184,297 @@ error: BIF_ERROR(BIF_P, BADARG); } +/* map:from_list/1 + * List may be unsorted [{K,V}] + */ + +BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { + Eterm *kv, item = BIF_ARG_1; + Eterm *hp, *thp,*vs, *ks, keys, res; + map_t *mp; + Uint size = 0, unused_size = 0; + Sint c = 0; + Sint idx = 0; + + if (is_list(item) || is_nil(item)) { + + /* Calculate size and check validity */ + + while(is_list(item)) { + res = CAR(list_val(item)); + if (is_not_tuple(res)) + goto error; + + kv = tuple_val(res); + if (*kv != make_arityval(2)) + goto error; + + size++; + item = CDR(list_val(item)); + } + + if (is_not_nil(item)) + goto error; + + hp = HAlloc(BIF_P, 3 + 1 + (2 * size)); + thp = hp; + keys = make_tuple(hp); + *hp++ = make_arityval(size); + ks = hp; + hp += size; + mp = (map_t*)hp; + res = make_map(mp); + hp += MAP_HEADER_SIZE; + vs = hp; + + mp->thing_word = MAP_HEADER; + mp->size = size; /* set later, might shrink*/ + mp->keys = keys; + + if (size == 0) + BIF_RET(res); + + item = BIF_ARG_1; + + /* first entry */ + kv = tuple_val(CAR(list_val(item))); + ks[0] = kv[1]; + vs[0] = kv[2]; + size = 1; + item = CDR(list_val(item)); + + /* insert sort key/value pairs */ + while(is_list(item)) { + + kv = tuple_val(CAR(list_val(item))); + + /* compare ks backwards + * idx represent word index to be written (hole position). + * We cannot copy the elements when searching since we might + * have an equal key. So we search for just the index first =( + * + * It is perhaps faster to move the values in the first pass. + * Check for uniqueness during insert phase and then have a + * second phace compacting the map if duplicates are found + * during insert. .. or do someother sort .. shell-sort perhaps. + */ + + idx = size; + + while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + + if (c == 0) { + /* last compare was equal, + * i.e. we have to release memory + * and overwrite that key/value + */ + ks[idx-1] = kv[1]; + vs[idx-1] = kv[2]; + unused_size++; + } else { + Uint i = size; + while(i > idx) { + ks[i] = ks[i-1]; + vs[i] = vs[i-1]; + i--; + } + ks[idx] = kv[1]; + vs[idx] = kv[2]; + size++; + } + item = CDR(list_val(item)); + } + + if (unused_size) { + /* the key tuple is embedded in the heap + * write a bignum to clear it. + */ + /* release values as normal since they are on the top of the heap */ + + ks[size] = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + size + unused_size, vs + size); + } + + *thp = make_arityval(size); + mp->size = size; + BIF_RET(res); + } + +error: + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:is_key/2 + */ + +BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *ks, key; + map_t *mp; + Uint n,i; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + + if (n == 0) + BIF_RET(am_false); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(am_true); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(am_true); + } + } + BIF_RET(am_false); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:keys/1 + */ + +BIF_RETTYPE map_keys_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *ks, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + ks = map_get_keys(mp); + + while(n--) { + res = CONS(hp, ks[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:merge/2 + */ + +BIF_RETTYPE map_merge_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { + Eterm *hp,*thp; + Eterm tup; + Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; + map_t *mp1,*mp2,*mp_new; + Uint n1,n2,i1,i2,need,unused_size=0; + int c = 0; + + mp1 = (map_t*)map_val(BIF_ARG_1); + mp2 = (map_t*)map_val(BIF_ARG_2); + n1 = map_get_size(mp1); + n2 = map_get_size(mp2); + + need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2); + + hp = HAlloc(BIF_P, need); + thp = hp; + tup = make_tuple(thp); + ks = hp + 1; hp += 1 + n1 + n2; + mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE; + vs = hp; hp += n1 + n2; + + mp_new->thing_word = MAP_HEADER; + mp_new->size = 0; + mp_new->keys = tup; + + i1 = 0; i2 = 0; + ks1 = map_get_keys(mp1); + vs1 = map_get_values(mp1); + ks2 = map_get_keys(mp2); + vs2 = map_get_values(mp2); + + while(i1 < n1 && i2 < n2) { + c = CMP(ks1[i1],ks2[i2]); + if ( c == 0) { + /* use righthand side arguments map value, + * but advance both maps */ + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i1++, i2++, unused_size++; + } else if ( c < 0) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } else { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + } + + /* copy remaining */ + while (i1 < n1) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } + + while (i2 < n2) { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + + if (unused_size) { + /* the key tuple is embedded in the heap, write a bignum to clear it. + * + * release values as normal since they are on the top of the heap + * size = n1 + n1 - unused_size + */ + + *ks = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + unused_size, vs); + } + + mp_new->size = n1 + n2 - unused_size; + *thp = make_arityval(n1 + n2 - unused_size); + + BIF_RET(make_map(mp_new)); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:new/2 + */ + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +/* map:put/3 + */ + BIF_RETTYPE map_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; @@ -242,3 +581,167 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } + +/* map:remove/3 + */ + +BIF_RETTYPE map_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, res, key,tup; + map_t *mp = (map_t*)map_val(BIF_ARG_2); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + BIF_RET(BIF_ARG_2); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ + + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(BIF_P, need); + mhp = thp + n; /* offset with tuple heap size */ + + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); + + res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; + + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } + + if (found) + BIF_RET(res); + + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(BIF_P, thp + need, thp); + BIF_RET(BIF_ARG_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:update/3 + */ + +BIF_RETTYPE map_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint found = 0; + Eterm* hp,*shp; + Eterm *ks,*vs, res, key; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + BIF_ERROR(BIF_P, BADARG); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (found) + BIF_RET(res); + + HRelease(BIF_P, shp + 3 + n, shp); + } + BIF_ERROR(BIF_P, BADARG); +} + + +/* map:values/1 + */ + +BIF_RETTYPE map_values_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *vs, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + vs = map_get_values(mp); + + while(n--) { + res = CONS(hp, vs[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index ca8ce0f974..4f0d26e100 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -33,11 +33,12 @@ typedef struct map_s { * * ----------- * Eterm THING - * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * Uint size + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = size * ---- * Eterm V1 * ... - * Eterm Vn, where n = arity + * Eterm Vn, where n = size * ----------- */ @@ -59,6 +60,7 @@ typedef struct map_s { #define map_get_size(x) (((map_t*)(x))->size) #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) +#define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) #endif -- cgit v1.2.3 From 60e7d2695dcf8ac2484db87c649ca69f1d0b763c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 May 2013 17:35:16 +0200 Subject: erts: Add testsuite for Maps --- erts/emulator/test/Makefile | 1 + erts/emulator/test/map_SUITE.erl | 532 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 533 insertions(+) create mode 100644 erts/emulator/test/map_SUITE.erl (limited to 'erts') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index f02ca3cb98..0b0568c31a 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -68,6 +68,7 @@ MODULES= \ hash_SUITE \ hibernate_SUITE \ list_bif_SUITE \ + map_SUITE \ match_spec_SUITE \ module_info_SUITE \ monitor_SUITE \ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl new file mode 100644 index 0000000000..bbc5aa07b4 --- /dev/null +++ b/erts/emulator/test/map_SUITE.erl @@ -0,0 +1,532 @@ +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(map_SUITE). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2 + ]). + +-export([ + t_build_and_match_literals/1, + t_update_literals/1,t_match_and_update_literals/1, + t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, + t_guard_receive/1, t_guard_fun/1, + t_list_comprehension/1, + t_map_sort_literals/1, + t_size/1, t_map_size/1, + + %% 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_new/1, + t_bif_map_put/1, + t_bif_map_remove/1, + t_bif_map_values/1 + ]). + +-include_lib("test_server/include/test_server.hrl"). + + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [ + t_build_and_match_literals, + t_update_literals, t_match_and_update_literals, + t_guard_bifs, t_guard_sequence, t_guard_update, + t_guard_receive,t_guard_fun, t_list_comprehension, + 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_new,t_bif_map_put, + t_bif_map_remove,t_bif_map_values + ]. + +groups() -> []. + +init_per_suite(Config) -> Config. +end_per_suite(_Config) -> ok. + +init_per_group(_GroupName, Config) -> Config. +end_per_group(_GroupName, Config) -> 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}), + + #{<<"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. + +%% Tests size(Map). + +t_size(Config) when is_list(Config) -> +% 0 = size(#{}), +% 1 = size(#{a=>1}), +% 1 = size(#{a=>#{a=>1}}), +% 2 = size(#{a=>1, b=>2}), +% 3 = size(#{a=>1, b=>2, b=>"3"}), + 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}, 2), + M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M#{ "a" => 2}, 2), + false = map_is_size(M#{ "a" => 2}, 3), + + %% Error cases. + {'EXIT',{badarg,_}} = (catch map_size([])), + {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{badarg,_}} = (catch map_size(1)), + ok. + +map_is_size(M,N) when map_size(M) =:= N -> true; +map_is_size(_,_) -> false. + +% test map updates without matching +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}, + #{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. + +loop_match_and_update_literals_x_q(Map, []) -> Map; +loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) -> + loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + + +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. + +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. + +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. + +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]], + 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}), + false = #{ 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}), + + true = #{ "a" => "hi", b => 134 } == id(#{ 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. + +%% BIFs +t_bif_map_get(Config) when is_list(Config) -> + + 1 = map:get(a, #{ a=> 1}), + 2 = map:get(b, #{ a=> 1, b => 2}), + "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + ok. + +t_bif_map_find(Config) when is_list(Config) -> + + {ok, 1} = map:find(a, #{ a=> 1}), + {ok, 2} = map:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = map:find(1, #{ 1 => "int"}), + {ok, "int"} = map:find(1.0, #{ 1 => "int"}), + {ok, "float"} = map:find(1, #{ 1.0 => "float"}), + {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + + {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + error = map:find(a,#{}), + error = map:find(a,#{b=>1, c=>2}), + + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + ok. + + +t_bif_map_is_key(Config) when is_list(Config) -> + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + + true = map:is_key("hi", M1), + true = map:is_key(int, M1), + true = map:is_key(<<"key">>, M1), + true = map:is_key(4, M1), + + false = map:is_key(5, M1), + false = map:is_key(<<"key2">>, M1), + false = map:is_key("h", M1), + false = map:is_key("hello", M1), + false = map:is_key(atom, M1), + + false = map:is_key("hi", map:remove("hi", M1)), + true = map:is_key("hi", M1), + true = map:is_key(1, map:put(1, "number", M1)), + false = map:is_key(1.0, map:put(1, "number", M1)), + ok. + +t_bif_map_keys(Config) when is_list(Config) -> + [] = map:keys(#{}), + + [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = map: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">>] = map:keys(M1), + + %% error case + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + ok. + +t_bif_map_new(Config) when is_list(Config) -> + #{} = map:new(), + 0 = erlang:map_size(map:new()), + ok. + +t_bif_map_put(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + + ["hi"] = map:keys(M1), + ["hello"] = map:values(M1), + + M2 = #{ int => 3 } = map:put(int, 3, M1), + + [int,"hi"] = map:keys(M2), + [3,"hello"] = map:values(M2), + + M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + + [int,"hi",<<"key">>] = map:keys(M3), + [3,"hello",<<"value">>] = map:values(M3), + + M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + + [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), + [wat,3,"hello",<<"value">>] = map:values(M4), + + M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + + [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), + [number,wat,3,"hello",<<"value">>] = map:values(M5), + + %% error case + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + ok. + +t_bif_map_remove(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = map:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = map:keys(M1), + [number,wat,3,<<"value">>] = map:values(M1), + + M2 = map:remove(int, M1), + [4,18446744073709551629,<<"key">>] = map:keys(M2), + [number,wat,<<"value">>] = map:values(M2), + + M3 = map:remove(<<"key">>, M2), + [4,18446744073709551629] = map:keys(M3), + [number,wat] = map:values(M3), + + M4 = map:remove(18446744073709551629, M3), + [4] = map:keys(M4), + [number] = map:values(M4), + + M5 = map:remove(4, M4), + [] = map:keys(M5), + [] = map:values(M5), + + M0 = map:remove(5,M0), + M0 = map:remove("hi there",M0), + + #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + + %% error case + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + ok. + + +t_bif_map_values(Config) when is_list(Config) -> + + [] = map:values(#{}), + + [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = map:values(#{ 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}, + M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, + [number,3,"hello2",<<"value2">>] = map:values(M2), + [number,3,"hello",<<"value">>] = map:values(M1), + + %% error case + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + + ok. + + +%% Use this function to avoid compile-time evaluation of an expression. +id(I) -> I. -- cgit v1.2.3 From aee6128a1bfb2356440b27a9c7db44650336e4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 00:09:37 +0200 Subject: erts: Conform map_SUITE to extended syntax --- erts/emulator/test/map_SUITE.erl | 122 +++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 61 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index bbc5aa07b4..6d82a2cb59 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -70,36 +70,36 @@ end_per_group(_GroupName, Config) -> Config. 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">>} = + #{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"} = + #{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} = + #{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}), - #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}), + #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), - #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =) + #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) - #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} = + #{ 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} = + 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"}))), + {'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. %% Tests size(Map). @@ -137,7 +137,7 @@ map_is_size(_,_) -> false. % test map updates without matching 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, [ + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ {"a","1"},{"b","2"},{"c","3"},{"d","4"} ]), ok. @@ -149,7 +149,7 @@ loop_update_literals_x_q(Map, [{X,Q}|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}, - #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [ + #{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">>, @@ -159,11 +159,11 @@ t_match_and_update_literals(Config) when is_list(Config) -> 4 => number, 18446744073709551629 => wat}, M0 = M2, - #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number }, + #{ 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} = Map, [{X,Q}|Vs]) -> +loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). @@ -202,17 +202,17 @@ t_guard_sequence(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{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_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}. +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) -> @@ -233,18 +233,18 @@ t_guard_receive(Config) when is_list(Config) -> 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}}), + #{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}}), + #{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), @@ -255,19 +255,19 @@ call(Pid, M) -> guard_receive_loop() -> receive - {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) -> + {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, #{ 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, #{ 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, #{ 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, #{ id:=Id, op:=imul, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, guard_receive_loop(); {Pid, done} -> @@ -279,20 +279,20 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], 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} + (#{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} + (#{s:=T,v:={V,V}}) -> {T,V}; + (#{s:=T,v:=[V,V]}) -> {T,V}; + (#{s:=T,v:=V}) -> {T,V} end, V = <<"hi">>, @@ -305,7 +305,7 @@ t_guard_fun(Config) when is_list(Config) -> {l,V} = F2(#{s=>l,v=>[V,V]}), %% error case - {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), ok. @@ -339,9 +339,9 @@ t_map_sort_literals(Config) when is_list(Config) -> %% 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)), + [#{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. @@ -435,27 +435,27 @@ t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), ["hi"] = map:keys(M1), ["hello"] = map:values(M1), - M2 = #{ int => 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = map:put(int, 3, M1), [int,"hi"] = map:keys(M2), [3,"hello"] = map:values(M2), - M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), [int,"hi",<<"key">>] = map:keys(M3), [3,"hello",<<"value">>] = map:values(M3), - M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), [wat,3,"hello",<<"value">>] = map:values(M4), - M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = map:put(4, number, M4), [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), [number,wat,3,"hello",<<"value">>] = map:values(M5), @@ -495,7 +495,7 @@ t_bif_map_remove(Config) when is_list(Config) -> M0 = map:remove(5,M0), M0 = map:remove("hi there",M0), - #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), %% error case {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), -- cgit v1.2.3 From c2702eb35db00ad67f922708eeea48616d908306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Oct 2013 14:06:57 +0200 Subject: compiler: Implement different instructions for => and := --- erts/emulator/beam/beam_emu.c | 301 +++++++++++++++++++++++++++------------ erts/emulator/beam/ops.tab | 11 +- erts/emulator/test/map_SUITE.erl | 38 +++++ 3 files changed, 257 insertions(+), 93 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1cad31be2c..b666b7c3f7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -959,8 +959,10 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; -static Eterm update_map(Process* p, Eterm* reg, - Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_assoc(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_exact(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; static int has_not_map_field(Eterm map, Eterm key); static Eterm get_map_element(Eterm map, Eterm key); @@ -2353,21 +2355,39 @@ void process_main(void) Next(4+Arg(3)); } - OpCase(update_map_jddII): { + OpCase(update_map_assoc_jddII): { Eterm res; Eterm map; GetArg1(1, map); x(0) = r(0); SWAPOUT; - res = update_map(c_p, reg, map, I); + res = update_map_assoc(c_p, reg, map, I); SWAPIN; - if (res) { + if (is_value(res)) { r(0) = x(0); StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { - goto lb_Cl_error; + goto badarg; + } + } + + OpCase(update_map_exact_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map_exact(c_p, reg, map, I); + SWAPIN; + if (is_value(res)) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto badarg; } } @@ -6387,18 +6407,10 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) return make_map(mp); } - -/* This entire instruction will be split into two. - * 1) update_map_exact (literals) <- this can be much more optimized - * 2) update_map_assoc (literals) - * Also update_map is pretty bad code as it stands now. - */ - static Eterm -update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) { Uint n; - Uint i; Uint num_old; Uint num_updates; Uint need; @@ -6413,7 +6425,7 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* kp; if (is_not_map(map)) { - return 0; + return THE_NON_VALUE; } old_mp = (map_t *) map_val(map); @@ -6428,11 +6440,12 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Allocate heap space for the worst case (i.e. all keys are new). + * Allocate heap space for the worst case (i.e. all keys in the + * update list are new). */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 4; + need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6442,67 +6455,37 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Update map, optimistically assuming that there are no - * new keys, allowing us to keep the old key tuple. + * Build the skeleton for the map, ready to be filled in. + * + * +-----------------------------------+ + * | (Space for aritvyal for keys) | <-----------+ + * +-----------------------------------+ | + * | (Space for key 1) | | <-- kp + * +-----------------------------------+ | + * . | + * . | + * . | + * +-----------------------------------+ | + * | (Space for last key) | | + * +-----------------------------------+ | + * | MAP_HEADER | | + * +-----------------------------------+ | + * | (Space for number of keys/values) | | + * +-----------------------------------+ | + * | Boxed tuple pointer >----------------+ + * +-----------------------------------+ + * | (Space for value 1) | <-- hp + * +-----------------------------------+ */ - hp = p->htop; - E = p->stop; - - old_vals = map_get_values(old_mp); - old_keys = map_get_keys(old_mp); + E = p->stop; + kp = p->htop + 1; /* Point to first key */ + hp = kp + num_old + num_updates; res = make_map(hp); - mp = (map_t *)hp; hp += 3; + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); mp->thing_word = MAP_HEADER; - mp->size = num_old; - mp->keys = old_mp->keys; - - ASSERT(num_updates > 0); - - /* Get array of key/value pairs to be updated */ - new_p = &Arg(5); - GET_TERM(*new_p, new_key); - - n = num_updates; - - for (i = 0; i < num_old; i++) { - if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { - /* not same keys */ - *hp++ = *old_vals; - } else { - GET_TERM(new_p[1], *hp); - hp++; - n--; - if (n == 0) { - new_key = THE_NON_VALUE; - } else { - new_p += 2; - GET_TERM(*new_p, new_key); - } - } - old_vals++, old_keys++; - } - - /* - * If we have exhausted the update list we are done. - */ - - if (n == 0) { - p->htop = hp; - return res; - } - - /* - * There were some new keys. We'll have to start over and rebuild - * the key tuple too. - */ - - kp = p->htop; - *kp++ = make_arityval(0); - - res = make_map(hp); - mp = (map_t *)hp; hp += 3; mp->keys = make_tuple(kp-1); old_vals = map_get_values(old_mp); @@ -6512,20 +6495,28 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) GET_TERM(*new_p, new_key); n = num_updates; + /* + * Fill in keys and values, until we run out of either updates + * or old values and keys. + */ + for (;;) { Eterm key; Sint c; ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = cmp(key, new_key)) < 0) { + if ((c = CMP(key, new_key)) < 0) { + /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; old_keys++, old_vals++, num_old--; } else { /* Replace or insert new */ - *kp++ = new_key; GET_TERM(new_p[1], *hp++); - if (c == 0) { /* If replacement */ + if (c > 0) { /* If new new key */ + *kp++ = new_key; + } else { /* If replacement */ + *kp++ = key; old_keys++, old_vals++, num_old--; } n--; @@ -6541,32 +6532,162 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } } - while (n-- > 0) { - GET_TERM(new_p[0], *kp++); - GET_TERM(new_p[1], *hp++); - new_p += 2; - } - /* - * All updates done. Now copy the remaining part of the frame's - * keys and values. + * At this point, we have run out of either old keys and values, + * or the update list. In other words, at least of one n and + * num_old must be zero. */ - while (num_old-- > 0) { - ASSERT(kp < (Eterm *)mp); - *kp++ = *old_keys++; - *hp++ = *old_vals++; + if (n > 0) { + /* + * All old keys and values have been copied, but there + * are still new keys and values in the update list that + * must be copied. + */ + ASSERT(num_old == 0); + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + } else { + /* + * All updates are now done. We may still have old + * keys and values that we must copy. + */ + ASSERT(n == 0); + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } } + + /* + * Calculate how many values that are unused at the end of the + * key tuple and fill it out with a bignum header. + */ if ((n = (Eterm *)mp - kp) > 0) { *kp = make_pos_bignum_header(n-1); } + + /* + * Fill in the size of the map in both the key tuple and in the map. + */ + n = kp - p->htop - 1; /* Actual number of keys/values */ *p->htop = make_arityval(n); - mp->thing_word = MAP_HEADER; mp->size = n; p->htop = hp; return res; } + +/* + * Update values for keys that already exist in the map. + */ + +static Eterm +update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + + if (is_not_map(map)) { + return THE_NON_VALUE; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate the exact heap space needed. + */ + + need = num_old + sizeof(map_t) / sizeof(Eterm); + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, keeping the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + /* Update all values */ + n = Arg(4) / 2; /* Number of values to be updated */ + ASSERT(n > 0); + for (i = 0; i < num_old; i++) { + if (!EQ(*old_keys, new_key)) { + /* Not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + /* + * All updates done. Copy remaining values + * and return the result. + */ + for (i++, old_vals++; i < num_old; i++) { + *hp++ = *old_vals++; + } + ASSERT(hp == p->htop + need); + p->htop = hp; + return res; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * Updates left. That means that at least one the keys in the + * update list did not previously exist. + */ + ASSERT(hp == p->htop + need); + return THE_NON_VALUE; +} #undef GET_TERM int catchlevel(Process *p) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b89bdb2e3a..f35997efee 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1474,11 +1474,16 @@ apply_last I P # has_map_field Fail Src Key => jump Fail # get_map_element Fail Src Key Dst => jump Fail -put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest +put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_assoc F Src Dst Live Size Rest=* => \ + update_map_assoc F Src Dst Live Size Rest +put_map_exact F Src Dst Live Size Rest=* => \ + update_map_exact F Src Dst Live Size Rest new_map j d I I -update_map j d d I I +update_map_assoc j d d I I +update_map_exact j d d I I is_map Fail cq => jump Fail diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6d82a2cb59..81d39fc97a 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + update_assoc/1,update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -48,6 +49,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + update_assoc,update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -166,6 +168,42 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). +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, + M1 = M0#{1.0=>wrong,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. + BadMap = id(badmap), + {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + + ok. + +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}, + M2 = M0#{3=>wrong,3.0:=new}, + + %% Errors cases. + {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + ok. t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), -- cgit v1.2.3 From 813f61de8e7872481a0369de3297596c1b11881d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 15:46:03 +0200 Subject: erts: Fixup map instructions for halfword --- erts/emulator/beam/beam_emu.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b666b7c3f7..fe2e196785 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6353,7 +6353,7 @@ static Eterm get_map_element(Eterm map, Eterm key) #define GET_TERM(term, dest) \ do { \ - Eterm src = term; \ + Eterm src = (Eterm)(term); \ switch (src & _TAG_IMMED1_MASK) { \ case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ dest = x(0); \ @@ -6380,7 +6380,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) Eterm keys; Eterm *mhp,*thp; Eterm *E; - Eterm *tp; + BeamInstr *ptr; map_t *mp; if (HeapWordsLeft(p) < need) { @@ -6390,18 +6390,18 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) thp = p->htop; mhp = thp + 1 + n/2; E = p->stop; - tp = &Arg(4); + ptr = &Arg(4); keys = make_tuple(thp); *thp++ = make_arityval(n/2); - mp = (map_t *)mhp; mhp += 3; + mp = (map_t *)mhp; mhp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = n/2; mp->keys = keys; for (i = 0; i < n/2; i++) { - GET_TERM(*tp++, *thp++); - GET_TERM(*tp++, *mhp++); + GET_TERM(*ptr++, *thp++); + GET_TERM(*ptr++, *mhp++); } p->htop = mhp; return make_map(mp); @@ -6420,7 +6420,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; Eterm* kp; @@ -6445,7 +6445,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); + need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6484,7 +6484,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->keys = make_tuple(kp-1); @@ -6599,7 +6599,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; if (is_not_map(map)) { @@ -6621,7 +6621,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * Allocate the exact heap space needed. */ - need = num_old + sizeof(map_t) / sizeof(Eterm); + need = num_old + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6642,7 +6642,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = num_old; mp->keys = old_mp->keys; -- cgit v1.2.3 From eb218eb375d353bcd78ae895805d1d45f927b3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 16:19:22 +0200 Subject: erts: Teach term_to_binary/1 and binary_to_term/1 Map encoding --- erts/emulator/beam/external.c | 197 ++++++++++++++++++++++++++++++++++++++---- erts/emulator/beam/external.h | 1 + 2 files changed, 183 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5e7a5cab6e..a31b2731be 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -42,6 +42,7 @@ #include "erl_binary.h" #include "erl_bits.h" #include "erl_zlib.h" +#include "erl_map.h" #ifdef HIPE #include "hipe_mode_switch.h" @@ -2555,6 +2556,38 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Eterm *mptr; + + *ep++ = MAP_EXT; + put_int32(size, ep); ep += 4; + + /* Push values first */ + if (size > 0) { + mptr = map_get_values(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[0]); + + mptr = map_get_keys(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + obj = mptr[0]; + goto L_jump_start; + } + } + break; + case FLOAT_DEF: GET_DOUBLE(obj, f); if (dflags & DFLAG_NEW_FLOATS) { @@ -2845,6 +2878,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ + Eterm *maps_head = NULL; /* for validation of maps */ Eterm* next; SWord reds; @@ -3469,6 +3503,65 @@ dec_term_atom_common: break; } break; + case MAP_EXT: + { + map_t *mp; + Uint32 size,n; + Eterm *kptr,*vptr; + Eterm keys; + + size = get_int32(ep); ep += 4; + + keys = make_tuple(hp); + *hp++ = make_arityval(size); + kptr = hp; + hp += size; + + mp = (map_t*)hp; + hp += MAP_HEADER_SIZE; + vptr = hp; + hp += size; + + /* kptr, first word for keys + * vptr, first word for values + */ + + /* + * Use thing_word to link through decoded maps. + * The list of maps is for later validation. + */ + + mp->thing_word = (Eterm) COMPRESS_POINTER(maps_head); + maps_head = (Eterm *) mp; + + mp->size = size; + mp->keys = keys; + *objp = make_map(mp); + + /* We assume the map is wellformed, meaning: + * - ascending key order + * - unique keys + */ + + objp = vptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + + objp = kptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + } + break; case NEW_FUN_EXT: { ErlFunThing* funp = (ErlFunThing *) hp; @@ -3678,21 +3771,7 @@ dec_term_atom_common: } default: - error: - /* UNDO: - * Must unlink all off-heap objects that may have been - * linked into the process. - */ - if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ - hp = *hpp; /* the largest must be the freshest */ - } - undo_offheap_in_area(off_heap, hp_saved, hp); - *hpp = hp_saved; - if (ctx) { - ctx->state = B2TDecodeFail; - ctx->reds = reds; - } - return NULL; + goto error; } if (--reds <= 0) { @@ -3710,12 +3789,53 @@ dec_term_atom_common: } } } + + /* Iterate through all the maps and check for validity + * - done here for when we know it is complete. + */ + + while (maps_head) { + Eterm *keys; + Sint arity; + + next = (Eterm *)(EXPAND_POINTER(*maps_head)); + keys = tuple_val(*(maps_head + 2)); + arity = arityval(*keys++); + + while(arity-- > 1) { + if (CMP(keys[arity-1],keys[arity]) >= 0) { + goto error; + } + } + + *maps_head = MAP_HEADER; + maps_head = next; + } + if (ctx) { ctx->state = B2TDone; ctx->reds = reds; } + *hpp = hp; return ep; + +error: + /* UNDO: + * Must unlink all off-heap objects that may have been + * linked into the process. + */ + if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ + hp = *hpp; /* the largest must be the freshest */ + } + undo_offheap_in_area(off_heap, hp_saved, hp); + *hpp = hp_saved; + if (ctx) { + ctx->state = B2TDecodeFail; + ctx->reds = reds; + } + + return NULL; } /* returns the number of bytes needed to encode an object @@ -3885,6 +4005,46 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, goto outer_loop; } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Uint i; + Eterm *ptr; + + result += 1 + 4; /* tag + 4 bytes size */ + + /* push values first */ + ptr = map_get_values(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + + ptr = map_get_keys(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + goto outer_loop; + } + break; case FLOAT_DEF: if (dflags & DFLAG_NEW_FLOATS) { result += 9; @@ -4175,6 +4335,13 @@ init_done: ADDTERMS(n); heap_size += n + 1; break; + case MAP_EXT: + CHKSIZE(4); + n = get_int32(ep); + ep += 4; + ADDTERMS(2*n); + heap_size += 3 + n + 1 + n; + break; case STRING_EXT: CHKSIZE(2); n = get_int16(ep); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 83001b2c7e..bf00958eb1 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -50,6 +50,7 @@ #define LARGE_BIG_EXT 'o' #define NEW_FUN_EXT 'p' #define EXPORT_EXT 'q' +#define MAP_EXT 't' #define FUN_EXT 'u' #define ATOM_UTF8_EXT 'v' #define SMALL_ATOM_UTF8_EXT 'w' -- cgit v1.2.3 From 97d087263e1836f3efb4267e3990219fe3e3c045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Oct 2013 00:35:45 +0200 Subject: erts: Add Map tests for hashing and external format - Update map_SUITE with hash and encode tests - Update map_SUITE with external format decode tests - Update map_SUITE with map:to_list/1 and map:from_list/1 tests --- erts/emulator/test/map_SUITE.erl | 143 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 81d39fc97a..27f35ccd29 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -30,7 +30,7 @@ t_map_sort_literals/1, t_size/1, t_map_size/1, - %% BIFs + %% Specific Map BIFs t_bif_map_get/1, t_bif_map_find/1, t_bif_map_is_key/1, @@ -38,7 +38,13 @@ t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, - t_bif_map_values/1 + t_bif_map_values/1, + t_bif_map_to_list/1, + t_bif_map_from_list/1, + + %% erlang + t_erlang_hash/1, + t_map_encode_decode/1 ]). -include_lib("test_server/include/test_server.hrl"). @@ -57,7 +63,11 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values + t_bif_map_remove,t_bif_map_values, + t_bif_map_to_list, t_bif_map_from_list, + + %% erlang + t_erlang_hash, t_map_encode_decode ]. groups() -> []. @@ -562,7 +572,134 @@ t_bif_map_values(Config) when is_list(Config) -> {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + ok. + +t_erlang_hash(Config) when is_list(Config) -> + + 39679005 = erlang:phash2(#{}), + 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 87559846 = erlang:phash2(#{ 1 => a }), + 76038729 = erlang:phash2(#{ a => 1 }), + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 67968757 = erlang:phash2(M0), + 76038729 = erlang:phash2(M1), + 67968757 = erlang:phash2(M2), + 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}, + {{},{}},{[],[]} + ], + ok = map_encode_decode_and_match(Pairs,[],#{}), + + %% 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 } + + %% order violation + %% literally #{ b=>1, a=>1 } in the internal order (bad) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), + + %% uniqueness violation + %% literally #{ a=>1, a=>1 } + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + + %% bad size (too large) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,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>>), + + ok. + +map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> + M1 = map:put(K,V,M0), + B0 = erlang:term_to_binary(M1), + Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + %% sort Ks and Vs according to term spec, then match it + ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-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(Encoded,Items); +match_encoded_map(_,_,_) -> no_match_size. + +match_encoded_map(<<>>,[]) -> ok; +match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> + Size = erlang:byte_size(Item), + <> = Bin, + EncodedTerm = Item, %% Asssert + match_encoded_map(Bin1,Items). + + +t_bif_map_to_list(Config) when is_list(Config) -> + [] = map:to_list(#{}), + [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), + + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map: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 + {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + ok. + + +t_bif_map_from_list(Config) when is_list(Config) -> + #{} = map:from_list([]), + A = map:from_list([]), + 0 = erlang:map_size(A), + + #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + + #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]), + + #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = + map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + + #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = + map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), + %% error cases + {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. -- cgit v1.2.3 From ac2954982d6089b0970798581b2230bdd1a065d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 17:21:30 +0200 Subject: erts: Add Maps to erlang:phash/2 and erlang:hash/2 The hashing a map in these functions uses the same strategy as the other terms. The exception being a prime number with size so we do not get erlang:phash(#{}) -> 1 which would be the same as erlang:phash({}) and erlang:phash(<<>>). Same argument for erlang:hash/1. --- erts/emulator/beam/utils.c | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc7e91295d..3774f379cc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -735,6 +735,8 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, #define FUNNY_NUMBER10 268440479 #define FUNNY_NUMBER11 268440577 #define FUNNY_NUMBER12 268440581 +#define FUNNY_NUMBER13 268440593 +#define FUNNY_NUMBER14 268440611 static Uint32 hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash) @@ -787,7 +789,7 @@ Uint32 make_hash(Eterm term_arg) /* Must not collide with the real tag_val_def's: */ #define MAKE_HASH_TUPLE_OP 0x11 -#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_TERM_ARRAY_OP 0x12 #define MAKE_HASH_CDR_PRE_OP 0x13 #define MAKE_HASH_CDR_POST_OP 0x14 @@ -878,7 +880,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -968,6 +970,24 @@ tail_recur: hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3; break; } + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -977,7 +997,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1206,6 +1226,9 @@ make_hash2(Eterm term) tmp = vs[i]; ESTACK_PUSH(s, tmp); } + /* We do not want to expose the tuple representation. + * Do not push the keys as a tuple. + */ for (i = size - 1; i >= 1; i--) { tmp = ks[i]; ESTACK_PUSH(s, tmp); @@ -1514,7 +1537,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -1627,6 +1650,24 @@ tail_recur: } break; + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -1636,7 +1677,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1664,7 +1705,7 @@ tail_recur: return hash; #undef MAKE_HASH_TUPLE_OP -#undef MAKE_HASH_FUN_OP +#undef MAKE_HASH_TERM_ARRAY_OP #undef MAKE_HASH_CDR_PRE_OP #undef MAKE_HASH_CDR_POST_OP } -- cgit v1.2.3 From 912f0f2aae82ee4316a2ee1863775bcf1d8a3ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 18:06:34 +0200 Subject: erts: Add Map tests for erlang:phash/2 --- erts/emulator/test/map_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 27f35ccd29..70b5c33fb2 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,7 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, - update_assoc/1,update_exact/1, + t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -55,7 +55,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, - update_assoc,update_exact, + t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -178,7 +178,7 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). -update_assoc(Config) when is_list(Config) -> +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]}, @@ -195,7 +195,7 @@ update_assoc(Config) when is_list(Config) -> ok. -update_exact(Config) when is_list(Config) -> +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]}, @@ -576,12 +576,23 @@ t_bif_map_values(Config) when is_list(Config) -> t_erlang_hash(Config) when is_list(Config) -> + ok = t_bif_erlang_phash2(), + ok = t_bif_erlang_phash(), + ok = t_bif_erlang_hash(), + + ok. + +t_bif_erlang_phash2() -> + 39679005 = erlang:phash2(#{}), 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), 87559846 = erlang:phash2(#{ 1 => a }), 76038729 = erlang:phash2(#{ a => 1 }), + 79950245 = erlang:phash2(#{{} => <<>>}), + 11244490 = erlang:phash2(#{<<>> => {}}), + M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, @@ -591,6 +602,47 @@ t_erlang_hash(Config) when is_list(Config) -> 67968757 = erlang:phash2(M2), ok. +t_bif_erlang_phash() -> + Sz = 1 bsl 32, + 268440612 = erlang:phash(#{},Sz), + 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 1394238263 = erlang:phash(#{ 1 => a },Sz), + 4066388227 = erlang:phash(#{ a => 1 },Sz), + + 1578050717 = erlang:phash(#{{} => <<>>},Sz), + 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 3590546636 = erlang:phash(M0,Sz), + 4066388227 = erlang:phash(M1,Sz), + 3590546636 = erlang:phash(M2,Sz), + ok. + +t_bif_erlang_hash() -> + Sz = 1 bsl 27 - 1, + 5158 = erlang:hash(#{},Sz), + 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 126071654 = erlang:hash(#{ 1 => a },Sz), + 126426236 = erlang:hash(#{ a => 1 },Sz), + + 101655720 = erlang:hash(#{{} => <<>>},Sz), + 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 38260486 = erlang:hash(M0,Sz), + 126426236 = erlang:hash(M1,Sz), + 38260486 = erlang:hash(M2,Sz), + ok. + + t_map_encode_decode(Config) when is_list(Config) -> <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), Pairs = [ -- cgit v1.2.3 From ab54731c41f28a21bce526249e582b56106d4965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 17:07:54 +0100 Subject: erts: erlang:phash2 should hash Maps independent of order --- erts/emulator/beam/utils.c | 63 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 3774f379cc..8958d334ae 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1091,9 +1091,11 @@ Uint32 make_hash2(Eterm term) { Uint32 hash; + Uint32 hash_xor_keys = 0; + Uint32 hash_xor_values = 0; DeclareTmpHeapNoproc(tmp_big,2); -/* (HCONST * {2, ..., 14}) mod 2^32 */ +/* (HCONST * {2, ..., 16}) mod 2^32 */ #define HCONST_2 0x3c6ef372UL #define HCONST_3 0xdaa66d2bUL #define HCONST_4 0x78dde6e4UL @@ -1110,6 +1112,10 @@ make_hash2(Eterm term) #define HCONST_15 0x454021d7UL #define HCONST_16 0xe3779b90UL +#define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF)) +#define HASH_MAP_KEY (_make_header(2,_TAG_HEADER_REF)) +#define HASH_MAP_VAL (_make_header(3,_TAG_HEADER_REF)) + #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ Uint32 a,b; \ @@ -1204,36 +1210,45 @@ make_hash2(Eterm term) UINT32_HASH(arity, HCONST_9); if (arity == 0) /* Empty tuple */ goto hash2_common; - for (i = arity; i >= 2; i--) { + for (i = arity; i >= 1; i--) { tmp = elem[i]; ESTACK_PUSH(s, tmp); } - term = elem[1]; + goto hash2_common; } break; case MAP_SUBTAG: { map_t *mp = (map_t *)map_val(term); int i; - int size = map_get_size(mp); + int size = map_get_size(mp); Eterm *ks = map_get_keys(mp); Eterm *vs = map_get_values(mp); UINT32_HASH(size, HCONST_16); - if (size == 0) /* Empty Map */ + if (size == 0) { goto hash2_common; - + } + ESTACK_PUSH(s, hash_xor_values); + ESTACK_PUSH(s, hash_xor_keys); + ESTACK_PUSH(s, hash); + ESTACK_PUSH(s, HASH_MAP_TAIL); + hash = 0; + hash_xor_keys = 0; + hash_xor_values = 0; for (i = size - 1; i >= 0; i--) { tmp = vs[i]; + ESTACK_PUSH(s, HASH_MAP_VAL); ESTACK_PUSH(s, tmp); } /* We do not want to expose the tuple representation. * Do not push the keys as a tuple. */ - for (i = size - 1; i >= 1; i--) { + for (i = size - 1; i >= 0; i--) { tmp = ks[i]; + ESTACK_PUSH(s, HASH_MAP_KEY); ESTACK_PUSH(s, tmp); } - term = ks[0]; + goto hash2_common; } break; case EXPORT_SUBTAG: @@ -1427,15 +1442,47 @@ make_hash2(Eterm term) default: erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); hash2_common: + + /* Uint32 hash always has the hash value of the previous term, + * compounded or otherwise. + */ + if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); UnUseTmpHeapNoproc(2); return hash; } + term = ESTACK_POP(s); + + switch (term) { + case HASH_MAP_TAIL: { + hash = (Uint32) ESTACK_POP(s); + UINT32_HASH(hash_xor_keys, HCONST_16); + UINT32_HASH(hash_xor_values, HCONST_16); + hash_xor_keys = (Uint32) ESTACK_POP(s); + hash_xor_values = (Uint32) ESTACK_POP(s); + goto hash2_common; + } + case HASH_MAP_KEY: + hash_xor_keys ^= hash; + hash = 0; + goto hash2_common; + case HASH_MAP_VAL: + hash_xor_values ^= hash; + hash = 0; + goto hash2_common; + default: + break; + } } } } + +#undef HASH_MAP_TAIL +#undef HASH_MAP_KEY +#undef HASH_MAP_VAL + #undef UINT32_HASH_2 #undef UINT32_HASH #undef SINT32_HASH -- cgit v1.2.3 From 9344e8ccf846af62576657244e3d526f88fdeb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 18:01:58 +0100 Subject: erts: Update Maps erlang:phash2/1 tests --- erts/emulator/test/map_SUITE.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 70b5c33fb2..1f4476defa 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -584,22 +584,22 @@ t_erlang_hash(Config) when is_list(Config) -> t_bif_erlang_phash2() -> - 39679005 = erlang:phash2(#{}), - 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), - 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), - 87559846 = erlang:phash2(#{ 1 => a }), - 76038729 = erlang:phash2(#{ a => 1 }), + 39679005 = erlang:phash2(#{}), + 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 14363616 = erlang:phash2(#{ 1 => a }), + 51612236 = erlang:phash2(#{ a => 1 }), - 79950245 = erlang:phash2(#{{} => <<>>}), - 11244490 = erlang:phash2(#{<<>> => {}}), + 37468437 = erlang:phash2(#{{} => <<>>}), + 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, - 67968757 = erlang:phash2(M0), - 76038729 = erlang:phash2(M1), - 67968757 = erlang:phash2(M2), + 118679416 = erlang:phash2(M0), + 51612236 = erlang:phash2(M1), + 118679416 = erlang:phash2(M2), ok. t_bif_erlang_phash() -> -- cgit v1.2.3 From f00675d3c682f53824e23f1599cbb09ff2d57daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 16:37:58 +0200 Subject: stdlib: Deny variables as keys and disallow ':=' in map construction In the current iteration of Maps we should deny *any* variables in Map keys. --- erts/emulator/test/map_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 1f4476defa..327e9b8060 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -327,7 +327,7 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], ok. t_guard_fun(Config) when is_list(Config) -> -- cgit v1.2.3 From bbdec77e0749a81f5e1e94a00631249cbb420fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 19:40:25 +0200 Subject: erts: Remove erlang:size/1 test from map_SUITE --- erts/emulator/test/map_SUITE.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 327e9b8060..3b52295039 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -28,7 +28,8 @@ t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, - t_size/1, t_map_size/1, + %t_size/1, + t_map_size/1, %% Specific Map BIFs t_bif_map_get/1, @@ -67,7 +68,9 @@ all() -> [ t_bif_map_to_list, t_bif_map_from_list, %% erlang - t_erlang_hash, t_map_encode_decode + t_erlang_hash, t_map_encode_decode, + %t_size, + t_map_size ]. groups() -> []. @@ -115,14 +118,15 @@ t_build_and_match_literals(Config) when is_list(Config) -> ok. %% Tests size(Map). +%% not implemented, perhaps it shouldn't be either -t_size(Config) when is_list(Config) -> +%t_size(Config) when is_list(Config) -> % 0 = size(#{}), % 1 = size(#{a=>1}), % 1 = size(#{a=>#{a=>1}}), % 2 = size(#{a=>1, b=>2}), % 3 = size(#{a=>1, b=>2, b=>"3"}), - ok. +% ok. t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), @@ -132,10 +136,12 @@ t_map_size(Config) when is_list(Config) -> 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}, 2), + 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#{ "a" => 2}, 3), + false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), -- cgit v1.2.3 From 72146c6675aaff02b2452c2fd2026c111e641f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 11 Oct 2013 18:11:12 +0200 Subject: erts: Specs for Map BIFs --- erts/preloaded/src/erlang.erl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ee5bd3e515..ff0f9d0ead 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,6 +60,7 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). +-type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. @@ -104,10 +105,9 @@ -export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]). -export([list_to_integer/1, list_to_integer/2]). -export([list_to_pid/1, list_to_tuple/1, loaded/0]). --export([localtime/0, make_ref/0, match_spec_test/3, md5/1, md5_final/1]). +-export([localtime/0, make_ref/0, map_size/1, match_spec_test/3, md5/1, md5_final/1]). -export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]). --export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2 -]). +-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2]). -export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]). -export([pid_to_list/1, port_close/1, port_command/2, port_command/3]). -export([port_connect/2, port_control/3, port_get_data/1]). @@ -128,7 +128,7 @@ -export([abs/1, append/2, element/2, get_module_info/2, hd/1, is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1, is_float/1, is_function/1, is_function/2, is_integer/1, - is_list/1, is_number/1, is_pid/1, is_port/1, is_record/2, + is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2, is_record/3, is_reference/1, is_tuple/1, load_module/2, load_nif/2, localtime_to_universaltime/2, make_fun/3, make_tuple/2, make_tuple/3, nodes/1, open_port/2, @@ -1149,6 +1149,12 @@ localtime() -> make_ref() -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:map_size/1 +-spec map_size(Map) -> non_neg_integer() when + Map :: map(). +map_size(_Map) -> + erlang:nif_error(undefined). + %% match_spec_test/3 -spec erlang:match_spec_test(P1, P2, P3) -> TestResult when P1 :: [term()] | tuple(), @@ -1739,6 +1745,12 @@ is_number(_Term) -> is_pid(_Term) -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:is_map/1 +-spec is_map(Map) -> boolean() when + Map :: map(). +is_map(_Map) -> + erlang:nif_error(undefined). + %% Shadowed by erl_bif_types: erlang:is_port/1 -spec is_port(Term) -> boolean() when Term :: term(). -- cgit v1.2.3 From 457b29c9ddd2b338e80916a5e7bad8dfe4b36ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 16:28:50 +0200 Subject: erts,stdlib: Teach matchspec compiler map guards --- erts/emulator/beam/erl_db_util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index d903053aa4..3986ccd4d3 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -565,6 +565,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_is_map, + &is_map_1, + 1, + DBIF_ALL + }, { am_is_binary, &is_binary_1, @@ -631,6 +637,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_map_size, + &map_size_1, + 1, + DBIF_ALL + }, { am_bit_size, &bit_size_1, -- cgit v1.2.3 From e1095860776d76294c1577079b2f24e0688c8e04 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Oct 2013 14:38:50 +0200 Subject: Add tests for pditc, ets and tracing --- erts/emulator/test/map_SUITE.erl | 143 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 3b52295039..ea3483d09e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -45,13 +45,18 @@ %% erlang t_erlang_hash/1, - t_map_encode_decode/1 - ]). + t_map_encode_decode/1, --include_lib("test_server/include/test_server.hrl"). + %% misc + t_pdict/1, + t_ets/1, + t_dets/1, + t_tracing/1 + ]). +-include_lib("stdlib/include/ms_transform.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> []. all() -> [ t_build_and_match_literals, @@ -70,7 +75,12 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, %t_size, - t_map_size + t_map_size, + + %% Other functions + t_pdict, + t_ets, + t_tracing ]. groups() -> []. @@ -760,6 +770,129 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. +%% MISC +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 + ets:insert(Tid,{not_a_map,2}), + 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,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'],[{'or',{is_map,{element,1,'$1'}}, + {'==',{map_size,'$1'},2}}],[]}]), + id(#{ a => b }), + id({1,2}), + id({#{ a => b},2}), + id(#{ a => b, b => c}), + {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), + {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), + dbg:ctpl(), + + %% Test fun2ms, DOES NOT COMPILE!! + %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 50 -> + timeout + end. + +trace_collector(Msg,Parent) -> + io:format("~p~n",[Msg]), + Parent ! Msg, + Parent. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. -- cgit v1.2.3 From f8345dc86a1787c3c8deb06befbb697f37db1c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 17:34:19 +0200 Subject: erts: Fix map_SUITE match spec tests --- erts/emulator/test/map_SUITE.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index ea3483d09e..69b68c4b67 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -806,14 +806,17 @@ t_ets(_Config) -> [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), %% Test match with map of different size - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), - %% Test match with don't care value - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + %%% 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}), - 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])), + 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}], @@ -857,18 +860,22 @@ t_tracing(_Config) -> {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test map guard bifs - {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}}, - {'==',{map_size,'$1'},2}}],[]}]), + % 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}), - id(#{ a => b, b => c}), {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), - {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test fun2ms, DOES NOT COMPILE!! + {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 }), @@ -883,11 +890,7 @@ t_tracing(_Config) -> ok. getmsg(_Tracer) -> - receive - V -> V - after 50 -> - timeout - end. + receive V -> V after 100 -> timeout end. trace_collector(Msg,Parent) -> io:format("~p~n",[Msg]), -- cgit v1.2.3 From 6fdad74f41803089a0f9026c98f319daecda9a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 19:06:34 +0200 Subject: erts,stdlib: Change map module name to maps Name conforms to EEP. --- erts/emulator/beam/bif.tab | 24 ++-- erts/emulator/beam/erl_map.c | 82 ++++++------- erts/emulator/test/map_SUITE.erl | 248 +++++++++++++++++++-------------------- 3 files changed, 177 insertions(+), 177 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 306861a4c5..b623e47b9a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -580,18 +580,18 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 -bif map:to_list/1 -bif map:find/2 -bif map:get/2 -bif map:from_list/1 -bif map:is_key/2 -bif map:keys/1 -bif map:merge/2 -bif map:new/0 -bif map:put/3 -bif map:remove/2 -bif map:update/3 -bif map:values/1 +bif maps:to_list/1 +bif maps:find/2 +bif maps:get/2 +bif maps:from_list/1 +bif maps:is_key/2 +bif maps:keys/1 +bif maps:merge/2 +bif maps:new/0 +bif maps:put/3 +bif maps:remove/2 +bif maps:update/3 +bif maps:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6fd60f0689..27734276f9 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -38,25 +38,25 @@ * - erlang:is_map/1 * - erlang:map_size/1 * - * - map:find/2 - * - map:from_list/1 - * - map:get/2 - * - map:is_key/2 - * - map:keys/1 - * - map:merge/2 - * - map:new/0 - * - map:put/3 - * - map:remove/2 - * - map:to_list/1 - * - map:update/3 - * - map:values/1 + * - maps:find/2 + * - maps:from_list/1 + * - maps:get/2 + * - maps:is_key/2 + * - maps:keys/1 + * - maps:merge/2 + * - maps:new/0 + * - maps:put/3 + * - maps:remove/2 + * - maps:to_list/1 + * - maps:update/3 + * - maps:values/1 * * TODO: - * - map:foldl/3 - * - map:foldr/3 - * - map:map/3 - * - map:size/1 - * - map:without/2 + * - maps:foldl/3 + * - maps:foldr/3 + * - maps:map/3 + * - maps:size/1 + * - maps:without/2 * */ @@ -80,10 +80,10 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:to_list/1 +/* maps:to_list/1 */ -BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; Eterm* hp; @@ -107,11 +107,11 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:find/2 +/* maps:find/2 * return value if key *equals* a key in the map */ -BIF_RETTYPE map_find_2(BIF_ALIST_2) { +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, res; map_t *mp; @@ -137,12 +137,12 @@ BIF_RETTYPE map_find_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:get/2 +/* maps:get/2 * return value if key *matches* a key in the map * exception bad_key if none matches */ -BIF_RETTYPE map_get_2(BIF_ALIST_2) { +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, error; map_t *mp; @@ -184,11 +184,11 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:from_list/1 +/* maps:from_list/1 * List may be unsorted [{K,V}] */ -BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { Eterm *kv, item = BIF_ARG_1; Eterm *hp, *thp,*vs, *ks, keys, res; map_t *mp; @@ -305,10 +305,10 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:is_key/2 +/* maps:is_key/2 */ -BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { +BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *ks, key; map_t *mp; @@ -340,10 +340,10 @@ BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:keys/1 +/* maps:keys/1 */ -BIF_RETTYPE map_keys_1(BIF_ALIST_1) { +BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *ks, res = NIL; map_t *mp; @@ -366,10 +366,10 @@ BIF_RETTYPE map_keys_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } -/* map:merge/2 +/* maps:merge/2 */ -BIF_RETTYPE map_merge_2(BIF_ALIST_2) { +BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { Eterm *hp,*thp; Eterm tup; @@ -452,10 +452,10 @@ BIF_RETTYPE map_merge_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:new/2 +/* maps:new/2 */ -BIF_RETTYPE map_new_0(BIF_ALIST_0) { +BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; map_t *mp; @@ -472,10 +472,10 @@ BIF_RETTYPE map_new_0(BIF_ALIST_0) { BIF_RET(make_map(mp)); } -/* map:put/3 +/* maps:put/3 */ -BIF_RETTYPE map_put_3(BIF_ALIST_3) { +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint c = 0; @@ -582,10 +582,10 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } -/* map:remove/3 +/* maps:remove/3 */ -BIF_RETTYPE map_remove_2(BIF_ALIST_2) { +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Sint n; Sint found = 0; @@ -656,10 +656,10 @@ BIF_RETTYPE map_remove_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:update/3 +/* maps:update/3 */ -BIF_RETTYPE map_update_3(BIF_ALIST_3) { +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint found = 0; @@ -719,10 +719,10 @@ BIF_RETTYPE map_update_3(BIF_ALIST_3) { } -/* map:values/1 +/* maps:values/1 */ -BIF_RETTYPE map_values_1(BIF_ALIST_1) { +BIF_RETTYPE maps_values_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *vs, res = NIL; map_t *mp; diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 69b68c4b67..59fdb82f50 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -412,182 +412,182 @@ t_map_sort_literals(Config) when is_list(Config) -> %% BIFs t_bif_map_get(Config) when is_list(Config) -> - 1 = map:get(a, #{ a=> 1}), - 2 = map:get(b, #{ a=> 1, b => 2}), - "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), - "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + 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"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> - {ok, 1} = map:find(a, #{ a=> 1}), - {ok, 2} = map:find(b, #{ a=> 1, b => 2}), - {ok, "int"} = map:find(1, #{ 1 => "int"}), - {ok, "int"} = map:find(1.0, #{ 1 => "int"}), - {ok, "float"} = map:find(1, #{ 1.0 => "float"}), - {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + {ok, 1} = maps:find(a, #{ a=> 1}), + {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = maps:find(1, #{ 1 => "int"}), + {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), + {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), + {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), - {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - error = map:find(a,#{}), - error = map:find(a,#{b=>1, c=>2}), + error = maps:find(a,#{}), + error = maps:find(a,#{b=>1, c=>2}), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), ok. t_bif_map_is_key(Config) when is_list(Config) -> M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, - true = map:is_key("hi", M1), - true = map:is_key(int, M1), - true = map:is_key(<<"key">>, M1), - true = map:is_key(4, M1), - - false = map:is_key(5, M1), - false = map:is_key(<<"key2">>, M1), - false = map:is_key("h", M1), - false = map:is_key("hello", M1), - false = map:is_key(atom, M1), - - false = map:is_key("hi", map:remove("hi", M1)), - true = map:is_key("hi", M1), - true = map:is_key(1, map:put(1, "number", M1)), - false = map:is_key(1.0, map:put(1, "number", M1)), + 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("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)), ok. t_bif_map_keys(Config) when is_list(Config) -> - [] = map:keys(#{}), + [] = maps:keys(#{}), - [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = 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">>] = map:keys(M1), + [4,int,"hi",<<"key">>] = maps:keys(M1), %% error case - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), ok. t_bif_map_new(Config) when is_list(Config) -> - #{} = map:new(), - 0 = erlang:map_size(map:new()), + #{} = maps:new(), + 0 = erlang:map_size(maps:new()), ok. t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}), - ["hi"] = map:keys(M1), - ["hello"] = map:values(M1), + ["hi"] = maps:keys(M1), + ["hello"] = maps:values(M1), - M2 = #{ int := 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = maps:put(int, 3, M1), - [int,"hi"] = map:keys(M2), - [3,"hello"] = map:values(M2), + [int,"hi"] = maps:keys(M2), + [3,"hello"] = maps:values(M2), - M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2), - [int,"hi",<<"key">>] = map:keys(M3), - [3,"hello",<<"value">>] = map:values(M3), + [int,"hi",<<"key">>] = maps:keys(M3), + [3,"hello",<<"value">>] = maps:values(M3), - M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3), - [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), - [wat,3,"hello",<<"value">>] = map:values(M4), + [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4), + [wat,3,"hello",<<"value">>] = maps:values(M4), - M0 = #{ 4 := number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = maps:put(4, number, M4), - [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), - [number,wat,3,"hello",<<"value">>] = map:values(M5), + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), + [number,wat,3,"hello",<<"value">>] = maps:values(M5), %% error case - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), ok. t_bif_map_remove(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = map:remove("hi", M0), - [4,18446744073709551629,int,<<"key">>] = map:keys(M1), - [number,wat,3,<<"value">>] = map:values(M1), + M1 = maps:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = maps:keys(M1), + [number,wat,3,<<"value">>] = maps:values(M1), - M2 = map:remove(int, M1), - [4,18446744073709551629,<<"key">>] = map:keys(M2), - [number,wat,<<"value">>] = map:values(M2), + M2 = maps:remove(int, M1), + [4,18446744073709551629,<<"key">>] = maps:keys(M2), + [number,wat,<<"value">>] = maps:values(M2), - M3 = map:remove(<<"key">>, M2), - [4,18446744073709551629] = map:keys(M3), - [number,wat] = map:values(M3), + M3 = maps:remove(<<"key">>, M2), + [4,18446744073709551629] = maps:keys(M3), + [number,wat] = maps:values(M3), - M4 = map:remove(18446744073709551629, M3), - [4] = map:keys(M4), - [number] = map:values(M4), + M4 = maps:remove(18446744073709551629, M3), + [4] = maps:keys(M4), + [number] = maps:values(M4), - M5 = map:remove(4, M4), - [] = map:keys(M5), - [] = map:values(M5), + M5 = maps:remove(4, M4), + [] = maps:keys(M5), + [] = maps:values(M5), - M0 = map:remove(5,M0), - M0 = map:remove("hi there",M0), + M0 = maps:remove(5,M0), + M0 = maps:remove("hi there",M0), - #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. t_bif_map_values(Config) when is_list(Config) -> - [] = map:values(#{}), + [] = maps:values(#{}), - [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = maps:values(#{ 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}, M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, - [number,3,"hello2",<<"value2">>] = map:values(M2), - [number,3,"hello",<<"value">>] = map:values(M1), + [number,3,"hello2",<<"value2">>] = maps:values(M2), + [number,3,"hello",<<"value">>] = maps:values(M1), %% error case - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), ok. t_erlang_hash(Config) when is_list(Config) -> @@ -610,7 +610,7 @@ t_bif_erlang_phash2() -> 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 118679416 = erlang:phash2(M0), @@ -630,7 +630,7 @@ t_bif_erlang_phash() -> 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 3590546636 = erlang:phash(M0,Sz), @@ -650,7 +650,7 @@ t_bif_erlang_hash() -> 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 38260486 = erlang:hash(M0,Sz), @@ -706,7 +706,7 @@ t_map_encode_decode(Config) when is_list(Config) -> ok. map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> - M1 = map:put(K,V,M0), + M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it @@ -729,45 +729,45 @@ match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> t_bif_map_to_list(Config) when is_list(Config) -> - [] = map:to_list(#{}), - [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), - [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), - [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + [] = maps:to_list(#{}), + [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = 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}] = map:to_list(#{ + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = 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 - {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), ok. t_bif_map_from_list(Config) when is_list(Config) -> - #{} = map:from_list([]), - A = map:from_list([]), + #{} = maps:from_list([]), + A = maps:from_list([]), 0 = erlang:map_size(A), - #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), - #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), - #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + #{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} = map:from_list([{a,1},{a,3},{a,2}]), + #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]), #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = - map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + 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} = - map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + 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}]), %% error cases - {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. %% MISC -- cgit v1.2.3 From 7e42aefdecae6cc91bdaaef21924b4e7cb75861c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 16:09:29 +0100 Subject: erts: Add tests for non BIFs in Maps module * maps:without/2 * maps:foldl/3 * maps:foldr/3 * maps:map/2 * maps:size/1 --- erts/emulator/test/map_SUITE.erl | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 59fdb82f50..b6cfd668f1 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -47,6 +47,12 @@ t_erlang_hash/1, t_map_encode_decode/1, + %% maps module not bifs + t_maps_fold/1, + t_maps_map/1, + t_maps_size/1, + t_maps_without/1, + %% misc t_pdict/1, t_ets/1, @@ -74,9 +80,13 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, - %t_size, t_map_size, + %% maps module + t_maps_fold, t_maps_map, + t_maps_size, t_maps_without, + + %% Other functions t_pdict, t_ets, @@ -770,6 +780,49 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. +%% Maps module, not BIFs +t_maps_fold(_Config) -> + + Vs = lists:seq(1,100), + Rs = lists:reverse(Vs), + M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), + + %% foldl + 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), + Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), + + %% foldr + 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), + Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], 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 t_pdict(_Config) -> -- cgit v1.2.3 From 105dcb3eb7c6d5fd07cd76cd495230b0a1d1e7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 17:05:43 +0100 Subject: erts: Add tests for Map update on expressions (foo())#{ k1 := V1, k2 => V2 } --- erts/emulator/test/map_SUITE.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index b6cfd668f1..a06cb43ac7 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + t_update_map_expressions/1, t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, @@ -67,6 +68,7 @@ suite() -> []. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + t_update_map_expressions, t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, @@ -137,6 +139,7 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. + %% Tests size(Map). %% not implemented, perhaps it shouldn't be either @@ -163,7 +166,7 @@ t_map_size(Config) when is_list(Config) -> true = map_is_size(M#{ "a" => 2}, 2), false = map_is_size(M#{ "c" => 2}, 2), - %% Error cases. + %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), {'EXIT',{badarg,_}} = (catch map_size(1)), @@ -204,6 +207,22 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = 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 }, + + %% Error cases, FIXME: should be 'badmap'? + {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + ok. + + t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), -- cgit v1.2.3 From b313f455be0def48b85c905efd98308aefb5bbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 14:40:12 +0200 Subject: preloaded: Remove bogus map type --- erts/preloaded/src/erlang.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'erts') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ff0f9d0ead..fbc37bd955 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,7 +60,6 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). --type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. -- cgit v1.2.3 From 38047efbbb91b02bd8894654458350b5a9813798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 16:44:55 +0100 Subject: Update preloaded erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 98008 -> 98256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index f38377647c..eb696bb32f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- cgit v1.2.3 From e0eb6d5bafcebc1c24b0a538e50a1d55a3724f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 8 Nov 2013 15:34:05 +0100 Subject: erts: Add NIFs for Maps - int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM map) - int enif_get_map_size(ErlNifEnv *env, ERL_NIF_TERM, int*) - ERL_NIF_TERM enif_make_new_map(ErlNifEnv *env) - int enif_make_map_put(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_get_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_find_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_make_map_update(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_make_map_remove(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out) - int enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter) - void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value) --- erts/emulator/beam/erl_map.c | 443 ++++++++++++++++++--------------- erts/emulator/beam/erl_map.h | 6 + erts/emulator/beam/erl_nif.c | 209 ++++++++++++++++ erts/emulator/beam/erl_nif.h | 26 +- erts/emulator/beam/erl_nif_api_funcs.h | 33 +++ 5 files changed, 511 insertions(+), 206 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 27734276f9..98aeee634b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -111,28 +111,39 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { * return value if key *equals* a key in the map */ -BIF_RETTYPE maps_find_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, res; - map_t *mp; - Uint n,i; +int erts_maps_find(Eterm key, Eterm map, Eterm *value) { - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - ks = map_get_keys(mp); - vs = map_get_values(mp); + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { - hp = HAlloc(BIF_P, 3); - res = make_tuple(hp); - *hp++ = make_arityval(2); - *hp++ = am_ok; - *hp++ = vs[i]; - BIF_RET(res); - } + mp = (map_t*)map_val(map); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, value,res; + + if (erts_maps_find(BIF_ARG_1, BIF_ARG_2, &value)) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = value; + BIF_RET(res); } + BIF_RET(am_error); } BIF_ERROR(BIF_P, BADARG); @@ -142,43 +153,54 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) { * exception bad_key if none matches */ -BIF_RETTYPE maps_get_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, error; - map_t *mp; - Uint n,i; - char *s_error; - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) - goto error; +int erts_maps_get(Eterm key, Eterm map, Eterm *value) { + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - ks = map_get_keys(mp); - vs = map_get_values(mp); + mp = (map_t*)map_val(map); + n = map_get_size(mp); - if (is_immed(key)) { - for( i = 0; i < n; i++) { - if (ks[i] == key) { - BIF_RET(vs[i]); - } - } - } + if (n == 0) + return 0; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + if (is_immed(key)) { for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { - BIF_RET(vs[i]); + if (ks[i] == key) { + *value = vs[i]; + return 1; } } -error: + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp; + Eterm value, error; + char *s_error; + + if (erts_maps_get(BIF_ARG_1, BIF_ARG_2, &value)) { + BIF_RET(value); + } s_error = "bad_key"; error = am_atom_put(s_error, sys_strlen(s_error)); hp = HAlloc(BIF_P, 3); - BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1); BIF_ERROR(BIF_P, EXC_ERROR_2); } BIF_ERROR(BIF_P, BADARG); @@ -460,7 +482,7 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm tup; map_t *mp; - hp = HAlloc(BIF_P, (3 + 1)); + hp = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1)); tup = make_tuple(hp); *hp++ = make_arityval(0); @@ -475,183 +497,197 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { /* maps:put/3 */ -BIF_RETTYPE maps_put_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { - Sint n,i; - Sint c = 0; - Eterm* hp, *shp; - Eterm *ks,*vs, res, key, tup; - map_t *mp = (map_t*)map_val(BIF_ARG_3); - - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - hp = HAlloc(BIF_P, 4 + 2); - tup = make_tuple(hp); - *hp++ = make_arityval(1); - *hp++ = key; - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = 1; - *hp++ = tup; - *hp++ = BIF_ARG_2; +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(map); - BIF_RET(res); - } - - ks = map_get_keys(mp); - vs = map_get_values(mp); - /* only allocate for values, - * assume key-tuple will be intact - */ + n = map_get_size(mp); - hp = HAlloc(BIF_P, 3 + n); - shp = hp; /* save hp, used if optimistic update fails */ - res = make_map(hp); + if (n == 0) { + hp = HAlloc(p, MAP_HEADER_SIZE + 1 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); *hp++ = MAP_HEADER; - *hp++ = n; - *hp++ = mp->keys; + *hp++ = 1; + *hp++ = tup; + *hp++ = value; - if (is_immed(key)) { - for( i = 0; i < n; i ++) { - if (ks[i] == key) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + return res; + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(p, MAP_HEADER_SIZE + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } - } else { - for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } } + } - if (c) - BIF_RET(res); + if (c) + return res; - /* need to make a new tuple, - * use old hp since it needs to be recreated anyway. - */ - tup = make_tuple(shp); - *shp++ = make_arityval(n+1); + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); - hp = HAlloc(BIF_P, 3 + n + 1); - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = n + 1; - *hp++ = tup; + hp = HAlloc(p, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - ASSERT(n >= 0); + ASSERT(n >= 0); - /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { - *shp++ = *ks++; - *hp++ = *vs++; - n--; - } + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } - *shp++ = key; - *hp++ = BIF_ARG_2; + *shp++ = key; + *hp++ = value; - ASSERT(n >= 0); + ASSERT(n >= 0); - while(n--) { - *shp++ = *ks++; - *hp++ = *vs++; - } - /* we have one word remaining - * this will work out fine once we get the size word - * in the header. - */ - *shp = make_pos_bignum_header(0); - BIF_RET(res); + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + return res; +} +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3)); + } BIF_ERROR(BIF_P, BADARG); } /* maps:remove/3 */ -BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Sint n; - Sint found = 0; - Uint need; - Eterm *thp, *mhp; - Eterm *ks, *vs, res, key,tup; - map_t *mp = (map_t*)map_val(BIF_ARG_2); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, tup; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); + n = map_get_size(mp); - if (n == 0) - BIF_RET(BIF_ARG_2); + if (n == 0) { + *res = map; + return 1; + } - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - /* Assume key exists. - * Release allocated if it didn't. - * Allocate key tuple first. - */ + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ - need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(BIF_P, need); - mhp = thp + n; /* offset with tuple heap size */ + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(p, need); + mhp = thp + n; /* offset with tuple heap size */ - tup = make_tuple(thp); - *thp++ = make_arityval(n - 1); + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); - res = make_map(mhp); - *mhp++ = MAP_HEADER; - *mhp++ = n - 1; - *mhp++ = tup; + *res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; - if (is_immed(key)) { - while(n--) { - if (*ks == key) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } - } else { - while(n--) { - if (eq(*ks, key)) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } } + } - if (found) - BIF_RET(res); + if (found) { + return 1; + } - /* Not found, remove allocated memory - * and return previous map. - */ - HRelease(BIF_P, thp + need, thp); - BIF_RET(BIF_ARG_2); + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(p, thp + need, thp); + + *res = map; + return 1; +} + +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm res; + if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } @@ -659,19 +695,15 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { /* maps:update/3 */ -BIF_RETTYPE maps_update_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; Sint found = 0; Eterm* hp,*shp; - Eterm *ks,*vs, res, key; - map_t *mp = (map_t*)map_val(BIF_ARG_3); + Eterm *ks,*vs; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - BIF_ERROR(BIF_P, BADARG); + if ((n = map_get_size(mp)) == 0) { + return 0; } ks = map_get_keys(mp); @@ -681,9 +713,9 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { * assume key-tuple will be intact */ - hp = HAlloc(BIF_P, 3 + n); + hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - res = make_map(hp); + *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -691,7 +723,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -701,7 +733,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } else { for( i = 0; i < n; i ++) { if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -710,10 +742,19 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } } - if (found) - BIF_RET(res); + if (found) { + return 1; + } + HRelease(p, shp + MAP_HEADER_SIZE + n, shp); + return 0; +} - HRelease(BIF_P, shp + 3 + n, shp); +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Eterm res; + if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 4f0d26e100..616ecd24ce 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -62,5 +62,11 @@ typedef struct map_s { #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) #define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map); +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res); +int erts_maps_find(Eterm key, Eterm map, Eterm *value); +int erts_maps_get(Eterm key, Eterm map, Eterm *value); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); + #endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index e1e213c4eb..c683847aaa 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -31,6 +31,7 @@ #include "bif.h" #include "error.h" #include "big.h" +#include "erl_map.h" #include "beam_bp.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" @@ -1602,6 +1603,214 @@ enif_have_dirty_schedulers() #endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ +/* Maps */ + +int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) +{ + return is_map(term); +} + +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +{ + if (is_map(term)) { + map_t *mp; + mp = (map_t*)map_val(term); + *size = map_get_size(mp); + return 1; + } + return 0; +} + +ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env) +{ + Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1); + Eterm tup; + map_t *mp; + + tup = make_tuple(hp); + *hp++ = make_arityval(0); + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + return make_map(mp); +} + +int enif_make_map_put(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + *map_out = erts_maps_put(env->proc, key, value, map_in); + cache_env(env); + return 1; +} + +int enif_get_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_find_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_make_map_update(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + + flush_env(env); + res = erts_maps_update(env->proc, key, value, map_in, map_out); + cache_env(env); + return res; +} + +int enif_make_map_remove(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + res = erts_maps_remove(env->proc, key, map_in, map_out); + cache_env(env); + return res; +} + +int enif_map_iterator_create(ErlNifEnv *env, + Eterm map, + ErlNifMapIterator *iter, + ErlNifMapIteratorEntry entry) +{ + if (is_map(map)) { + map_t *mp = (map_t*)map_val(map); + size_t offset; + + switch (entry) { + case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break; + case ERL_NIF_MAP_ITERATOR_TAIL: offset = map_get_size(mp) - 1; break; + default: goto error; + } + + /* empty maps are ok but will leave the iterator + * in bad shape. + */ + + iter->map = map; + iter->ks = ((Eterm *)map_get_keys(mp)) + offset; + iter->vs = ((Eterm *)map_get_values(mp)) + offset; + iter->t_limit = map_get_size(mp) + 1; + iter->h_limit = 0; + iter->idx = offset + 1; + + return 1; + } + +error: + iter->map = THE_NON_VALUE; + return 0; +} + +void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + /* not used */ +} + +int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->t_limit)) { + return 1; + } + return 0; +} + +int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->h_limit)) { + return 1; + } + return 0; +} + + +int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx < iter->t_limit) { + iter->idx++; + if (iter->idx != iter->t_limit) { + iter->ks++; + iter->vs++; + } + return 1; + } + return 0; +} + +int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit ) { + iter->idx--; + if (iter->idx != iter->h_limit ) { + iter->ks--; + iter->vs--; + } + return 1; + } + return 0; +} + +int enif_map_iterator_get_pair(ErlNifEnv *env, + ErlNifMapIterator *iter, + Eterm *key, + Eterm *value) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && + iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && + iter->vs < (map_get_values(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + *key = *(iter->ks); + *value = *(iter->vs); + return 1; + } + return 0; +} + /*************************************************************************** ** load_nif/2 ** ***************************************************************************/ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index fb3c359ec9..3c1e13f8a4 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,14 +38,11 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice -** 2.5: R17 dirty schedulers +** 2.5: R17 Maps API additions +** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT #define ERL_NIF_MINOR_VERSION 5 -#else -#define ERL_NIF_MINOR_VERSION 4 -#endif #include @@ -168,6 +165,7 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; +<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -175,6 +173,24 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif +======= +typedef struct +{ + /* use a lot of memory, structure may change */ + ERL_NIF_TERM map; + ErlNifUInt64 h_limit; + ErlNifUInt64 t_limit; + ErlNifUInt64 idx; + ERL_NIF_TERM *ks; + ERL_NIF_TERM *vs; +} ErlNifMapIterator; + +typedef enum { + ERL_NIF_MAP_ITERATOR_HEAD = 1, + ERL_NIF_MAP_ITERATOR_TAIL = 2 +} ErlNifMapIteratorEntry; + +>>>>>>> erts: Add NIFs for Maps #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index f5b27dfdfa..2cabfd4ce1 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -149,6 +149,23 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif +ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); +ERL_NIF_API_FUNC_DECL(void, enif_map_iterator_destroy, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_head, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); + + /* ** Add new entries here to keep compatibility on Windows!!! */ @@ -281,6 +298,22 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); # define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif +# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) +# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) +# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) + /* ** Add new entries here */ -- cgit v1.2.3 From c71408f7184090fee077e35d0220fc021d2817fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 19 Nov 2013 11:38:36 +0100 Subject: erts: Test Maps with NIFs --- erts/emulator/test/nif_SUITE.erl | 25 +++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 59 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index affb66289b..5b7c310aa4 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -29,7 +29,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1, - types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + maps/1, api_macros/1, from_array/1, iolist_as_binary/1, resource/1, resource_binary/1, resource_takeover/1, @@ -58,7 +59,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, reload, upgrade, heap_frag, types, many_args, - binaries, get_string, get_atom, api_macros, from_array, + binaries, get_string, get_atom, maps, api_macros, from_array, iolist_as_binary, resource, resource_binary, resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, @@ -435,6 +436,21 @@ get_atom(Config) when is_list(Config) -> ?line {0, <<>>} = atom_to_bin('',0), ok. +maps(doc) -> ["Test NIF maps handling."]; +maps(suite) -> []; +maps(Config) when is_list(Config) -> + TmpMem = tmpmem(), + Pairs = [{adam, "bert"}] ++ + [{I,I}||I <- lists:seq(1,10)] ++ + [{a,value},{"a","value"},{<<"a">>,<<"value">>}], + ok = ensure_lib_loaded(Config, 1), + M = maps_from_list(Pairs), + R = {RIs,Is} = sorted_list_from_maps(M), + io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), + Is = lists:sort(Pairs), + Is = lists:reverse(RIs), + ok. + api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; api_macros(suite) -> []; api_macros(Config) when is_list(Config) -> @@ -1488,5 +1504,10 @@ otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. +%% maps +maps_from_list(_) -> ?nif_stub. +sorted_list_from_maps(_) -> ?nif_stub. + + nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 6f902e186d..a0316a7861 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,6 +1536,63 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +/* maps */ +static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM cell = argv[0]; + ERL_NIF_TERM map = enif_make_new_map(env); + ERL_NIF_TERM tuple; + const ERL_NIF_TERM *pair; + int arity = -1; + + if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env); + + /* assume sorted keys */ + + while (!enif_is_empty_list(env,cell)) { + if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env); + if (enif_get_tuple(env,tuple,&arity,&pair)) { + enif_make_map_put(env, map, pair[0], pair[1], &map); + } + } + + return map; +} + +static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + + ERL_NIF_TERM map = argv[0]; + ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM key, value; + ErlNifMapIterator iter; + + if (argc != 1 && !enif_is_map(env, map)) + return enif_make_badarg(env); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); + enif_map_iterator_next(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); + enif_map_iterator_prev(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + return enif_make_tuple2(env, list_f, list_b); +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1589,6 +1646,8 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif + {"maps_from_list", 1, maps_from_list}, + {"sorted_list_from_maps", 1, sorted_list_from_maps} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From faf92c5174c7b86d2b5829c928b187753b3918e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 29 Nov 2013 12:12:53 +0100 Subject: erts: NIFs Map API fixup --- erts/emulator/beam/erl_nif_api_funcs.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 2cabfd4ce1..4a5aacad48 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,19 +300,19 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) -# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) -# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) -# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) -# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) -# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) -# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) -# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) -# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) -# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) -# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) -# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) +# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_API_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) /* ** Add new entries here -- cgit v1.2.3 From d5c238473b9cec819d93faaef4ccc00ddb60465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 26 Oct 2013 21:01:41 +0200 Subject: erts: Add cmp_term to compare Uses total order of types meaning int < float --- erts/emulator/beam/erl_utils.h | 46 +++++++++++++++++++++++++++--------------- erts/emulator/beam/utils.c | 13 ++++++++++-- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 292d135946..c6247c7246 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -202,23 +202,37 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*); -#define CMP(A,B) cmp_rel(A,NULL,B,NULL) +Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) +#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) #else -Sint cmp(Eterm, Eterm); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B) -#define CMP(A,B) cmp(A,B) +Sint cmp(Eterm, Eterm, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1) +#define CMP(A,B) cmp(A,B,0) +#define CMP_TERM(A,B) cmp(A,B,1) #endif -#define cmp_lt(a,b) (CMP((a),(b)) < 0) -#define cmp_le(a,b) (CMP((a),(b)) <= 0) -#define cmp_eq(a,b) (CMP((a),(b)) == 0) -#define cmp_ne(a,b) (CMP((a),(b)) != 0) -#define cmp_ge(a,b) (CMP((a),(b)) >= 0) -#define cmp_gt(a,b) (CMP((a),(b)) > 0) - -#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) -#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) -#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) -#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define cmp_lt(a,b) (CMP((a),(b)) < 0) +#define cmp_le(a,b) (CMP((a),(b)) <= 0) +#define cmp_eq(a,b) (CMP((a),(b)) == 0) +#define cmp_ne(a,b) (CMP((a),(b)) != 0) +#define cmp_ge(a,b) (CMP((a),(b)) >= 0) +#define cmp_gt(a,b) (CMP((a),(b)) > 0) + +#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0) +#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0) +#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0) +#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0) + +#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) +#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) +#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) +#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b))) +#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b))) #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8958d334ae..bc4a05d385 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2425,10 +2425,14 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } +/* cmp(Eterm a, Eterm b, int exact) + * exact = 1 -> term-based compare + * exact = 0 -> arith-based compare + */ #if HALFWORD_HEAP -Sint cmp_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base) +Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) #else -Sint cmp(Eterm a, Eterm b) +Sint cmp(Eterm a, Eterm b, int exact) #endif { DECLARE_WSTACK(stack); @@ -2852,6 +2856,7 @@ tailrecur_ne: j = big_sign(aw) ? -1 : 1; break; case SMALL_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if (f2.fd < MAX_LOSSLESS_FLOAT && f2.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2877,12 +2882,14 @@ tailrecur_ne: #endif /* ERTS_SIZEOF_ETERM == 8 */ break; case FLOAT_BIG: + if (exact) goto exact_fall_through; { Wterm tmp = aw; aw = bw; bw = tmp; }/* fall through */ case BIG_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if ((f2.fd < (double) (MAX_SMALL + 1)) && (f2.fd > (double) (MIN_SMALL - 1))) { @@ -2912,6 +2919,7 @@ tailrecur_ne: } break; case FLOAT_SMALL: + if (exact) goto exact_fall_through; GET_DOUBLE(aw, f1); if (f1.fd < MAX_LOSSLESS_FLOAT && f1.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2936,6 +2944,7 @@ tailrecur_ne: } #endif /* ERTS_SIZEOF_ETERM == 8 */ break; +exact_fall_through: default: j = b_tag - a_tag; } -- cgit v1.2.3 From 76b8ea8ab1eb4ce099f88ccb8d1721c438d0ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 10:58:59 +0100 Subject: erts: Add BIF erts_internal:cmp_term/2 Compares terms where integer() < float(). --- erts/emulator/beam/bif.c | 11 +++++++++++ erts/emulator/beam/bif.tab | 2 ++ erts/preloaded/src/erts_internal.erl | 10 ++++++++++ 3 files changed, 23 insertions(+) (limited to 'erts') diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 61c1abedb5..9c4801041f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4615,6 +4615,17 @@ BIF_RETTYPE bump_reductions_1(BIF_ALIST_1) BIF_RET2(am_true, reds); } +BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) { + int res = CMP_TERM(BIF_ARG_1,BIF_ARG_2); + + /* ensure -1, 0, 1 result */ + if (res < 0) { + BIF_RET(make_small(-1)); + } else if (res > 0) { + BIF_RET(make_small(1)); + } + BIF_RET(make_small(0)); +} /* * Processes doing yield on return in a bif ends up in bif_return_trap(). */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index b623e47b9a..2d888862bf 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -593,6 +593,8 @@ bif maps:remove/2 bif maps:update/3 bif maps:values/1 +bif erts_internal:cmp_term/2 + # # Obsolete # diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index d6a185482e..88eb317f1d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -170,3 +170,13 @@ binary_to_term(_Binary) -> Opts :: [safe]. binary_to_term(_Binary, _Opts) -> erlang:nif_error(undefined). + +%% term compare where integer() < float() = true + +-spec cmp_term(A,B) -> Result when + A :: term(), + B :: term(), + Result :: -1 | 0 | 1. + +cmp_term(_A,_B) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From 862728a458729f4a71630f4a8fa93f1f26744c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:15:20 +0100 Subject: erts: Update maps BIFs to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/erl_map.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 98aeee634b..455ba25e11 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -108,7 +108,7 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { } /* maps:find/2 - * return value if key *equals* a key in the map + * return value if key *matches* a key in the map */ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { @@ -123,7 +123,7 @@ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { vs = map_get_values(mp); for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -178,7 +178,7 @@ int erts_maps_get(Eterm key, Eterm map, Eterm *value) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -283,7 +283,7 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { idx = size; - while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; } if (c == 0) { /* last compare was equal, @@ -353,7 +353,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { BIF_RET(am_true); } } @@ -425,7 +425,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { vs2 = map_get_values(mp2); while(i1 < n1 && i2 < n2) { - c = CMP(ks1[i1],ks2[i2]); + c = CMP_TERM(ks1[i1],ks2[i2]); if ( c == 0) { /* use righthand side arguments map value, * but advance both maps */ @@ -546,7 +546,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; c = 1; @@ -577,7 +577,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { ASSERT(n >= 0); /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { + while (n && ((c = CMP_TERM(*ks, key)) < 0)) { *shp++ = *ks++; *hp++ = *vs++; n--; @@ -658,7 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } else { while(n--) { - if (eq(*ks, key)) { + if (EQ(*ks, key)) { ks++; vs++; found = 1; @@ -732,7 +732,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; found = 1; -- cgit v1.2.3 From f3821597383fa20d1093dab70fa75b4a1018a6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:20:04 +0100 Subject: erts: Update maps instructions to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/beam_emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fe2e196785..89d9442526 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6316,7 +6316,7 @@ static int has_not_map_field(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(keys[i], key)) { + if (EQ(keys[i], key)) { return 0; } } @@ -6343,7 +6343,7 @@ static Eterm get_map_element(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { return vs[i]; } } @@ -6506,7 +6506,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = CMP(key, new_key)) < 0) { + if ((c = CMP_TERM(key, new_key)) < 0) { /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; -- cgit v1.2.3 From c334d4841d9d600237184233518ee13f7421f91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jan 2014 18:04:00 +0100 Subject: erts: Update maps serializing to use term order * erlang:term_to_binary/1 * erlang:binary_to_term/1 --- erts/emulator/beam/external.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a31b2731be..e8b77b9f37 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3803,7 +3803,7 @@ dec_term_atom_common: arity = arityval(*keys++); while(arity-- > 1) { - if (CMP(keys[arity-1],keys[arity]) >= 0) { + if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { goto error; } } -- cgit v1.2.3 From 0422247a9d8259ef1a28704500f07caf827f42f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 22:00:04 +0100 Subject: erts: Fix bug in erts_maps_remove HRelease was called with wrong arguments and left garbage on heap when key was not found. --- erts/emulator/beam/erl_map.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 455ba25e11..e68482be58 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -614,6 +614,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; Sint found = 0; Uint need; + Eterm *hp_start; Eterm *thp, *mhp; Eterm *ks, *vs, tup; map_t *mp = (map_t*)map_val(map); @@ -634,7 +635,8 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { */ need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(p, need); + hp_start = HAlloc(p, need); + thp = hp_start; mhp = thp + n; /* offset with tuple heap size */ tup = make_tuple(thp); @@ -676,7 +678,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { /* Not found, remove allocated memory * and return previous map. */ - HRelease(p, thp + need, thp); + HRelease(p, hp_start + need, hp_start); *res = map; return 1; -- cgit v1.2.3 From f1003b26524c2321b1eef36fb2d3997aaf0b9e10 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:03:33 +0100 Subject: erts: Optimize erts_map_update/remove to not continue comparing keys once it has been found. --- erts/emulator/beam/erl_map.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index e68482be58..57f156509c 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -612,7 +612,6 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) { int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; - Sint found = 0; Uint need; Eterm *hp_start; Eterm *thp, *mhp; @@ -650,9 +649,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { if (is_immed(key)) { while(n--) { if (*ks == key) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -661,9 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } else { while(n--) { if (EQ(*ks, key)) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -671,10 +666,6 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } - if (found) { - return 1; - } - /* Not found, remove allocated memory * and return previous map. */ @@ -682,6 +673,14 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { *res = map; return 1; + +found_key: + /* Copy rest of keys and values */ + if (n) { + sys_memcpy(mhp, vs+1, n*sizeof(Eterm)); + sys_memcpy(thp, ks+1, n*sizeof(Eterm)); + } + return 1; } BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { @@ -699,7 +698,6 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; - Sint found = 0; Eterm* hp,*shp; Eterm *ks,*vs; map_t *mp = (map_t*)map_val(map); @@ -717,7 +715,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -725,9 +722,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } @@ -735,20 +730,23 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } else { for( i = 0; i < n; i ++) { if (EQ(ks[i], key)) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } } } - if (found) { - return 1; - } HRelease(p, shp + MAP_HEADER_SIZE + n, shp); return 0; + +found_key: + *hp++ = value; + vs++; + if (++i < n) + sys_memcpy(hp, vs, (n - i)*sizeof(Eterm)); + *res = make_map(shp); + return 1; } BIF_RETTYPE maps_update_3(BIF_ALIST_3) { -- cgit v1.2.3 From 4db2458bba34884448d3b3752dce74c17d92c698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 13:24:20 +0100 Subject: Update map_SUITE to respect term order --- erts/emulator/test/map_SUITE.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a06cb43ac7..081e1b852e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -228,7 +228,7 @@ t_update_assoc(Config) when is_list(Config) -> 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.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + #{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, @@ -462,12 +462,10 @@ t_bif_map_find(Config) when is_list(Config) -> {ok, 1} = maps:find(a, #{ a=> 1}), {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), {ok, "int"} = maps:find(1, #{ 1 => "int"}), - {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), - {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), @@ -475,6 +473,10 @@ t_bif_map_find(Config) when is_list(Config) -> %% 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 + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), @@ -737,7 +739,7 @@ t_map_encode_decode(Config) when is_list(Config) -> map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), - Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), %% decode and match it -- cgit v1.2.3 From 39c35199f5118a59f337b695a934c6bfcbf0813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:23:56 +0100 Subject: erts: Let erlang:binary_to_term/1 handle unsorted Maps Maps may be encoded with keys in arbitrary order. This is fine, as long as keys are unique. --- erts/emulator/beam/external.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index e8b77b9f37..57251286c8 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3790,24 +3790,41 @@ dec_term_atom_common: } } - /* Iterate through all the maps and check for validity + /* Iterate through all the maps and check for validity and sort keys * - done here for when we know it is complete. */ while (maps_head) { - Eterm *keys; - Sint arity; + map_t *mp = (map_t*)maps_head; + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; next = (Eterm *)(EXPAND_POINTER(*maps_head)); - keys = tuple_val(*(maps_head + 2)); - arity = arityval(*keys++); - while(arity-- > 1) { - if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { - goto error; + /* sort */ + + for ( ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) goto error; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; } - } + } *maps_head = MAP_HEADER; maps_head = next; } -- cgit v1.2.3 From 228ee23186639c8b66be8ed26def7c8e9cb09059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:26:23 +0100 Subject: Update map_SUITE to test "unsorted" encoded maps --- erts/emulator/test/map_SUITE.erl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 081e1b852e..75381de556 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -711,19 +711,32 @@ t_map_encode_decode(Config) when is_list(Config) -> ], 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,100,0,1,97,97,2,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() + 100,0,1,97, % a :: atom() + 100,0,1,98, % b :: atom() + 107,0,5,118,97,108,117,101, % "value" :: list() + 97,33, % 33 :: integer() + 97,55 % 55 :: integer() + >>), + + %% 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 } - %% order violation - %% literally #{ b=>1, a=>1 } in the internal order (bad) - {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), - %% uniqueness violation - %% literally #{ a=>1, a=>1 } + %% literally #{ a=>1, "hi"=>"value", a=>2 } {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)), %% bad size (too large) {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch -- cgit v1.2.3 From c8ecb6962a923ceb0b7e599d22adef773d042e4a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:15:13 +0100 Subject: erts: Remove enif_find_map_value as it does the same thing as enif_get_map_value. Replace with placeholder to be ABI backward compatible on Windows as long as enif_find_map_value is not called. --- erts/emulator/beam/erl_nif.c | 13 +------------ erts/emulator/beam/erl_nif_api_funcs.h | 5 ++--- 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c683847aaa..1a539c730f 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1663,17 +1663,6 @@ int enif_get_map_value(ErlNifEnv* env, return erts_maps_get(key, map, value); } -int enif_find_map_value(ErlNifEnv* env, - Eterm map, - Eterm key, - Eterm *value) -{ - if (is_not_map(map)) { - return 0; - } - return erts_maps_get(key, map, value); -} - int enif_make_map_update(ErlNifEnv* env, Eterm map_in, Eterm key, diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4a5aacad48..71abb5dc8b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -154,7 +154,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); @@ -303,7 +303,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) # define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) # define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) -- cgit v1.2.3 From b8a7203f04a1ed6d2756e6b782cd4c95a8c7c491 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:22:34 +0100 Subject: erts: Increase version for NIF API and reject experimental v2.5 --- erts/emulator/beam/erl_nif.c | 3 ++- erts/emulator/beam/erl_nif.h | 9 ++++----- erts/emulator/beam/erl_nif_api_funcs.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1a539c730f..1eca7822eb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1996,7 +1996,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); } else if (entry->major != ERL_NIF_MAJOR_VERSION - || entry->minor > ERL_NIF_MINOR_VERSION) { + || entry->minor > ERL_NIF_MINOR_VERSION + || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).", entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 3c1e13f8a4..ae7f63f923 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,11 +38,13 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice +** 2.5: First experimental maps API additions (libs of this version is not compatible with any other VM) ** 2.5: R17 Maps API additions +** 2.6: R17 with maps ** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 5 +#define ERL_NIF_MINOR_VERSION 6 #include @@ -165,7 +167,6 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; -<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -173,7 +174,7 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif -======= + typedef struct { /* use a lot of memory, structure may change */ @@ -190,8 +191,6 @@ typedef enum { ERL_NIF_MAP_ITERATOR_TAIL = 2 } ErlNifMapIteratorEntry; ->>>>>>> erts: Add NIFs for Maps - #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 71abb5dc8b..9c386a4c41 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -154,7 +154,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); -- cgit v1.2.3 From f321ea89fecdb343e58e2485c057326d08fed69c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:16:40 +0100 Subject: erts: Do not allow map iterator created without map --- erts/emulator/beam/erl_nif.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1eca7822eb..ca82590a78 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1725,21 +1725,27 @@ int enif_map_iterator_create(ErlNifEnv *env, } error: +#ifdef DEBUG iter->map = THE_NON_VALUE; +#endif return 0; } void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) { /* not used */ +#ifdef DEBUG + iter->map = THE_NON_VALUE; +#endif + } int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->t_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1747,10 +1753,10 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->h_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->h_limit) { return 1; } return 0; @@ -1759,7 +1765,8 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx < iter->t_limit) { iter->idx++; if (iter->idx != iter->t_limit) { iter->ks++; @@ -1772,7 +1779,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx > iter->h_limit ) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit ) { iter->idx--; if (iter->idx != iter->h_limit ) { iter->ks--; @@ -1788,7 +1796,8 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *key, Eterm *value) { - if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From 609d835ce04a1f6320377780a11fb32382e1e419 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 11:25:34 +0100 Subject: erts: Let enif_map_iterator_next/prev return 0 to signal end of map. --- erts/emulator/beam/erl_nif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ca82590a78..def9a7efe1 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1771,8 +1771,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->t_limit) { iter->ks++; iter->vs++; + return 1; } - return 1; } return 0; } @@ -1785,8 +1785,8 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->h_limit ) { iter->ks--; iter->vs--; + return 1; } - return 1; } return 0; } -- cgit v1.2.3 From d39affeac223a7e2891b509395e7b19c931b5018 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:32:46 +0100 Subject: erts: Remove use of h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index def9a7efe1..94c2512650 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1718,7 +1718,6 @@ int enif_map_iterator_create(ErlNifEnv *env, iter->ks = ((Eterm *)map_get_keys(mp)) + offset; iter->vs = ((Eterm *)map_get_values(mp)) + offset; iter->t_limit = map_get_size(mp) + 1; - iter->h_limit = 0; iter->idx = offset + 1; return 1; @@ -1744,8 +1743,7 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->t_limit) { + if (iter->t_limit == 1 || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1755,8 +1753,7 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->h_limit) { + if (iter->t_limit == 1 || iter->idx == 0) { return 1; } return 0; @@ -1780,9 +1777,9 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit ) { + if (iter->idx > 0) { iter->idx--; - if (iter->idx != iter->h_limit ) { + if (iter->idx != 0) { iter->ks--; iter->vs--; return 1; @@ -1797,7 +1794,7 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *value) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + if (iter->idx > 0 && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From ada36d9024b4ceda974f32a78b5fe39b64f59319 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:55:10 +0100 Subject: erts: Simplify some map iterator code --- erts/emulator/beam/erl_nif.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 94c2512650..c6f7c8adb5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1743,20 +1743,14 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == iter->t_limit) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == iter->t_limit); } int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == 0) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == 0); } -- cgit v1.2.3 From 00f9be42e43913bce9b110382e55bfbdaa9406d0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:13:45 +0100 Subject: erts: Fix map iterator bug when reverting from end of map position and simplify code by ignoring h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 18 +++------ erts/emulator/test/nif_SUITE.erl | 5 ++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 57 ++++++++++++++++++++------- 3 files changed, 53 insertions(+), 27 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c6f7c8adb5..6fceb8a0ba 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1759,13 +1759,10 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx < iter->t_limit) { iter->idx++; - if (iter->idx != iter->t_limit) { - iter->ks++; - iter->vs++; - return 1; - } + iter->ks++; + iter->vs++; } - return 0; + return (iter->idx != iter->t_limit); } int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) @@ -1773,13 +1770,10 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx > 0) { iter->idx--; - if (iter->idx != 0) { - iter->ks--; - iter->vs--; - return 1; - } + iter->ks--; + iter->vs--; } - return 0; + return (iter->idx > 0); } int enif_map_iterator_get_pair(ErlNifEnv *env, diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 5b7c310aa4..a34f70c618 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -449,6 +449,9 @@ maps(Config) when is_list(Config) -> io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), + + #{} = maps_from_list([]), + {[],[]} = sorted_list_from_maps(#{}), ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index a0316a7861..8549d277de 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -1564,31 +1564,60 @@ static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NI ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ - ERL_NIF_TERM key, value; - ErlNifMapIterator iter; + ERL_NIF_TERM key, value, k2, v2; + ErlNifMapIterator iter_f; + ErlNifMapIterator iter_b; + int cnt, next_ret, prev_ret; if (argc != 1 && !enif_is_map(env, map)) - return enif_make_badarg(env); + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) - return enif_make_badarg(env); + if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_int(env, __LINE__); - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) { + if (cnt && !next_ret) + return enif_make_int(env, __LINE__); list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); - enif_map_iterator_next(env,&iter); + next_ret = enif_map_iterator_next(env,&iter_f); + cnt++; } + if (cnt && next_ret) + return enif_make_int(env, __LINE__); - enif_map_iterator_destroy(env, &iter); + if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) - return enif_make_badarg(env); + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) { + if (cnt && !prev_ret) + return enif_make_int(env, __LINE__); + + /* Test that iter_f can step "backwards" */ + if (!enif_map_iterator_prev(env,&iter_f) + || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2) + || k2 != key || v2 != value) { + return enif_make_int(env, __LINE__); + } - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); - enif_map_iterator_prev(env,&iter); + prev_ret = enif_map_iterator_prev(env,&iter_b); + } + + if (cnt) { + if (prev_ret || enif_map_iterator_prev(env,&iter_f)) + return enif_make_int(env, __LINE__); + + /* Test that iter_b can step "backwards" one step */ + if (!enif_map_iterator_next(env, &iter_b) + || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2) + || k2 != key || v2 != value) + return enif_make_int(env, __LINE__); } - enif_map_iterator_destroy(env, &iter); + enif_map_iterator_destroy(env, &iter_f); + enif_map_iterator_destroy(env, &iter_b); return enif_make_tuple2(env, list_f, list_b); } -- cgit v1.2.3 From a18853e6814ab42188e56343e6363ff94c794bb8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:21:26 +0100 Subject: erts: Optimize struct ErlNifMapIterator No need to use 64bit integers on 32bit machines. --- erts/emulator/beam/erl_nif.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index ae7f63f923..7613446f64 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -103,6 +103,8 @@ typedef unsigned long long ERL_NIF_TERM; # endif #endif +typedef ERL_NIF_TERM ERL_NIF_UINT; + struct enif_environment_t; typedef struct enif_environment_t ErlNifEnv; @@ -175,15 +177,14 @@ typedef enum }ErlNifDirtyTaskFlags; #endif -typedef struct +typedef struct /* All fields all internal and may change */ { - /* use a lot of memory, structure may change */ ERL_NIF_TERM map; - ErlNifUInt64 h_limit; - ErlNifUInt64 t_limit; - ErlNifUInt64 idx; + ERL_NIF_UINT t_limit; + ERL_NIF_UINT idx; ERL_NIF_TERM *ks; ERL_NIF_TERM *vs; + void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */ } ErlNifMapIterator; typedef enum { -- cgit v1.2.3 From e21989fdc834c9c9e75454bd06cb6ef29218bf9f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:07:01 +0100 Subject: erts: Add more tests for the NIF map API --- erts/emulator/test/nif_SUITE.erl | 49 +++++++++++++++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 55 +++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 10 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a34f70c618..bcc1f9e5af 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -444,14 +444,44 @@ maps(Config) when is_list(Config) -> [{I,I}||I <- lists:seq(1,10)] ++ [{a,value},{"a","value"},{<<"a">>,<<"value">>}], ok = ensure_lib_loaded(Config, 1), - M = maps_from_list(Pairs), - R = {RIs,Is} = sorted_list_from_maps(M), + M = maps_from_list_nif(Pairs), + R = {RIs,Is} = sorted_list_from_maps_nif(M), io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), - #{} = maps_from_list([]), - {[],[]} = sorted_list_from_maps(#{}), + #{} = maps_from_list_nif([]), + {[],[]} = sorted_list_from_maps_nif(#{}), + + 1 = is_map_nif(M), + 0 = is_map_nif("no map"), + + Msz = map_size(M), + {1,Msz} = get_map_size_nif(M), + {1,0} = get_map_size_nif(#{}), + {0,-123} = get_map_size_nif({#{}}), + + #{} = M0 = make_new_map_nif(), + + {1, #{key := value}=M1} = make_map_put_nif(M0, key, value), + {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"), + {0, undefined} = make_map_put_nif(666, key, value), + + {1, "value2"} = get_map_value_nif(M3,"key2"), + {0, undefined} = get_map_value_nif(M3,"key3"), + {0, undefined} = get_map_value_nif(false,key), + + {0, undefined} = make_map_update_nif(M0, key, value), + {0, undefined} = make_map_update_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"), + {0, undefined} = make_map_update_nif(666, key, value), + + {1, #{}} = make_map_remove_nif(M1, key), + {1, M1} = make_map_remove_nif(M2, "key2"), + {1, M2} = make_map_remove_nif(M2, "key3"), + {0, undefined} = make_map_remove_nif(self(), key), + ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; @@ -1508,8 +1538,15 @@ consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. %% maps -maps_from_list(_) -> ?nif_stub. -sorted_list_from_maps(_) -> ?nif_stub. +is_map_nif(_) -> ?nif_stub. +get_map_size_nif(_) -> ?nif_stub. +make_new_map_nif() -> ?nif_stub. +make_map_put_nif(_,_,_) -> ?nif_stub. +get_map_value_nif(_,_) -> ?nif_stub. +make_map_update_nif(_,_,_) -> ?nif_stub. +make_map_remove_nif(_,_) -> ?nif_stub. +maps_from_list_nif(_) -> ?nif_stub. +sorted_list_from_maps_nif(_) -> ?nif_stub. nif_stub_error(Line) -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 8549d277de..9ee89a49a4 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,8 +1536,48 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_int(env, enif_is_map(env,argv[0])); +} +static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int size = -123; + int ret = enif_get_map_size(env, argv[0], &size); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); +} +static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_new_map(env); +} +static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM value = enif_make_atom(env, "undefined"); + int ret = enif_get_map_value(env, argv[0], argv[1], &value); + return enif_make_tuple2(env, enif_make_int(env,ret), value); + +} +static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} + /* maps */ -static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM cell = argv[0]; ERL_NIF_TERM map = enif_make_new_map(env); @@ -1559,7 +1599,7 @@ static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM return map; } -static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ @@ -1675,8 +1715,15 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif - {"maps_from_list", 1, maps_from_list}, - {"sorted_list_from_maps", 1, sorted_list_from_maps} + {"is_map_nif", 1, is_map_nif}, + {"get_map_size_nif", 1, get_map_size_nif}, + {"make_new_map_nif", 0, make_new_map_nif}, + {"make_map_put_nif", 3, make_map_put_nif}, + {"get_map_value_nif", 2, get_map_value_nif}, + {"make_map_update_nif", 3, make_map_update_nif}, + {"make_map_remove_nif", 2, make_map_remove_nif}, + {"maps_from_list_nif", 1, maps_from_list_nif}, + {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 9eddc8eb4c7f4ef45b51650750334a787c5f9ef7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:45:42 +0100 Subject: erts: Fix compile error for halfword emulator --- erts/emulator/beam/erl_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index c6247c7246..5b81d814c6 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -204,9 +204,9 @@ int eq(Eterm, Eterm); #if HALFWORD_HEAP Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1) #else Sint cmp(Eterm, Eterm, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) -- cgit v1.2.3 From f2b9a5623ec5c9a314560add82ef20a03ba91235 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 17 Jan 2014 12:32:42 +0100 Subject: erts: Change 'size' argument of enif_get_map_size from int* to size_t* --- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6fceb8a0ba..c35f1fc2c6 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1610,7 +1610,7 @@ int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) return is_map(term); } -int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size) { if (is_map(term)) { map_t *mp; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 9c386a4c41..4121378e1a 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -150,7 +150,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); -ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 9ee89a49a4..b550d1f16d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1542,9 +1542,9 @@ static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - int size = -123; + size_t size = (size_t)-123; int ret = enif_get_map_size(env, argv[0], &size); - return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size)); } static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -- cgit v1.2.3 From 3645dc6b15d4fdd79768b92bb598c264005a8689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Jan 2014 12:30:51 +0100 Subject: preloaded: Fixup export cmp_term in erts_internal --- erts/preloaded/src/erts_internal.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'erts') diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 88eb317f1d..edcd50c77e 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -30,6 +30,7 @@ -export([await_port_send_result/3]). -export([binary_to_term/1, binary_to_term/2]). +-export([cmp_term/2]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). -- cgit v1.2.3 From bc34b5dafaebf07d7185900310246f5752c0a6c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 21 Jan 2014 12:21:11 +0100 Subject: erts: Add map construction to driver API erl_drv_output_term() and erl_drv_send_term() can send messages containing maps with the use of the new ERL_DRV_MAP. The driver API minor version is updated as new functionality is added. --- erts/doc/src/erl_driver.xml | 29 ++++++++- erts/emulator/beam/erl_driver.h | 4 +- erts/emulator/beam/erl_map.c | 31 ++++++++++ erts/emulator/beam/erl_map.h | 4 +- erts/emulator/beam/external.c | 31 +--------- erts/emulator/beam/io.c | 44 ++++++++++++- erts/emulator/test/send_term_SUITE.erl | 19 +++++- .../test/send_term_SUITE_data/send_term_drv.c | 72 ++++++++++++++++++++-- 8 files changed, 191 insertions(+), 43 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index c2f7fa4588..710c9b19cf 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@
- 20012013 + 20012014 Ericsson AB. All Rights Reserved. @@ -1742,15 +1742,19 @@ typedef struct ErlIOVec { term consists of one to four elements in the array. The term first has a term type, and then arguments. The port parameter specifies the sending port.

-

Tuple and lists (with the exception of strings, see below), +

Tuples, maps and lists (with the exception of strings, see below), are built in reverse polish notation, so that to build a tuple, the elements are given first, and then the tuple - term, with a count. Likewise for lists.

+ term, with a count. Likewise for lists and maps.

A tuple must be specified with the number of elements. (The elements precede the ERL_DRV_TUPLE term.)

A list must be specified with the number of elements, including the tail, which is the last term preceding ERL_DRV_LIST.

+

A map must be specified with the number of key-value pairs N. + The key-value pairs must precede the ERL_DRV_MAP in this order: + key1,value1,key2,value2,...,keyN,valueN. + Duplicate keys are not allowed.

The special term ERL_DRV_STRING_CONS is used to "splice" in a string in a list, a string given this way is not a list per se, but the elements are elements of the @@ -1774,6 +1778,7 @@ ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) ERL_DRV_STRING_CONS char *str, int len ERL_DRV_FLOAT double *dbl ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len +ERL_DRV_MAP int sz

The unsigned integer data type ErlDrvUInt and the signed integer data type ErlDrvSInt are 64 bits wide @@ -1856,6 +1861,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len }; erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]> + +

To build the map #{key1 => 100, key2 => {200, 300}}, the + following call could be made.

+ + +

If you want to pass a binary and don't already have the content of the binary in an ErlDrvBinary, you can benefit from using ERL_DRV_BUF2BINARY instead of creating an ErlDrvBinary diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 2bd3181bdc..ab9ee63104 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -605,6 +605,8 @@ EXTERN int null_func(void); #define ERL_DRV_INT64 ((ErlDrvTermData) 15) /* ErlDrvSInt64 * */ #define ERL_DRV_UINT64 ((ErlDrvTermData) 16) /* ErlDrvUInt64 * */ +#define ERL_DRV_MAP ((ErlDrvTermData) 17) /* ErlDrvUInt */ + #ifndef ERL_DRIVER_TYPES_ONLY /* make terms for driver_output_term and driver_send_term */ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 57f156509c..2fff7f9390 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -786,3 +786,34 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } + +int erts_validate_and_sort_map(map_t* mp) +{ + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; + + /* sort */ + + for (ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) return 0; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; + } + } + return 1; +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 616ecd24ce..cfacb2ec28 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -67,6 +67,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res int erts_maps_find(Eterm key, Eterm map, Eterm *value); int erts_maps_get(Eterm key, Eterm map, Eterm *value); int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); - +int erts_validate_and_sort_map(map_t* map); #endif diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 57251286c8..a4cc3435c3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3795,37 +3795,10 @@ dec_term_atom_common: */ while (maps_head) { - map_t *mp = (map_t*)maps_head; - Eterm *ks = map_get_keys(mp); - Eterm *vs = map_get_values(mp); - Uint sz = map_get_size(mp); - Uint ix,jx; - Eterm tmp; - int c; - next = (Eterm *)(EXPAND_POINTER(*maps_head)); - - /* sort */ - - for ( ix = 1; ix < sz; ix++) { - jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { - /* identical key -> error */ - if (c == 0) goto error; - - tmp = ks[jx]; - ks[jx] = ks[jx - 1]; - ks[jx - 1] = tmp; - - tmp = vs[jx]; - vs[jx] = vs[jx - 1]; - vs[jx - 1] = tmp; - - jx--; - } - - } *maps_head = MAP_HEADER; + if (!erts_validate_and_sort_map((map_t*)maps_head)) + goto error; maps_head = next; } diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 49af86b36a..3b16cdeb4a 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -46,6 +46,7 @@ #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" #include "dtrace-wrapper.h" +#include "erl_map.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -5293,6 +5294,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) depth++; break; } + case ERL_DRV_MAP: { /* int */ + ERTS_DDT_CHK_ENOUGH_ARGS(1); + if ((int) ptr[0] < 0) ERTS_DDT_FAIL; + need += MAP_HEADER_SIZE + 1 + 2*ptr[0]; + depth -= 2*ptr[0]; + if (depth < 0) ERTS_DDT_FAIL; + ptr++; + depth++; + break; + } + default: ERTS_DDT_FAIL; } @@ -5529,6 +5541,36 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ptr += 2; break; + case ERL_DRV_MAP: { /* int */ + int size = (int)ptr[0]; + Eterm* tp = hp; + Eterm* vp; + map_t *mp; + + *tp = make_arityval(size); + + hp += 1 + size; + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = size; + mp->keys = make_tuple(tp); + mess = make_map(mp); + + hp += MAP_HEADER_SIZE + size; /* advance "heap" pointer */ + + tp += size; /* point at last key */ + vp = hp - 1; /* point at last value */ + + while(size--) { + *vp-- = ESTACK_POP(stack); + *tp-- = ESTACK_POP(stack); + } + if (!erts_validate_and_sort_map(mp)) + ERTS_DDT_FAIL; + ptr++; + break; + } + } ESTACK_PUSH(stack, mess); } diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index b631f55a03..8e1f8df43a 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -62,7 +62,19 @@ basic(Config) when is_list(Config) -> ?line [] = term(P, 0), ?line Self = self(), - ?line {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self} = term(P, 1), + {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1), + + Map41 = maps:from_list([{blurf, 42}, + {[], [-42,{}|"abc"++P]}, + {"kalle", 3.1416}, + {Self, #{}}]), + Map41 = term(P, 41), + + Map42 = maps:from_list([{42, []}, + {[-42,{}|"abc"++P], "kalle"}, + {3.1416, Self}, + {#{}, blurf}]), + Map42 = term(P, 42), ?line Deep = lists:seq(0, 199), ?line Deep = term(P, 2), ?line {B1,B2} = term(P, 3), @@ -125,7 +137,8 @@ basic(Config) when is_list(Config) -> {-1, 36}, % ERL_DRV_INT64 {-4711, 37}, % ERL_DRV_INT64 {-20233590931456, 38}, % ERL_DRV_INT64 - {-9223372036854775808, 39}], % ERL_DRV_INT64 + {-9223372036854775808, 39}, + {#{}, 40}], % ERL_DRV_MAP ?line {Terms, Ops} = lists:unzip(Singles), ?line Terms = term(P,Ops), diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c index f8613487b0..381a4f20d5 100644 --- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c +++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c @@ -104,7 +104,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) double f = 3.1416; msg[0] = ERL_DRV_ATOM; - msg[1] = driver_mk_atom("blurf"), + msg[1] = driver_mk_atom("blurf"); msg[2] = ERL_DRV_INT; msg[3] = (ErlDrvTermData) 42; msg[4] = ERL_DRV_NIL; @@ -126,9 +126,11 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) msg[20] = (ErlDrvTermData) &f; msg[21] = ERL_DRV_PID; msg[22] = driver_connected(erlang_port); - msg[23] = ERL_DRV_TUPLE; - msg[24] = (ErlDrvTermData) 7; - msg += 25; + msg[23] = ERL_DRV_MAP; + msg[24] = (ErlDrvTermData) 0; + msg[25] = ERL_DRV_TUPLE; + msg[26] = (ErlDrvTermData) 8; + msg += 27; } break; @@ -481,6 +483,52 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) break; } + case 40: { + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + msg += 2; + break; + } + + case 41: /* Most term types inside a map */ + case 42: { + double f = 3.1416; + + if (buf[i] == 41) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)42; + *msg++ = ERL_DRV_NIL; + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)-42; + *msg++ = ERL_DRV_TUPLE; + *msg++ = (ErlDrvTermData)0; + *msg++ = ERL_DRV_PORT; + *msg++ = driver_mk_port(erlang_port); + *msg++ = ERL_DRV_STRING_CONS; + *msg++ = (ErlDrvTermData)"abc"; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_LIST; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_STRING; + *msg++ = (ErlDrvTermData)"kalle"; + *msg++ = (ErlDrvTermData)5; + *msg++ = ERL_DRV_FLOAT; + *msg++ = (ErlDrvTermData)&f; + *msg++ = ERL_DRV_PID; + *msg++ = driver_connected(erlang_port); + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)0; + if (buf[i] == 42) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)4; + break; + } case 127: /* Error cases */ { @@ -662,6 +710,22 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) FAIL_TERM(msg, 2); } + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + FAIL_TERM(msg, 1); + + /* map with duplicate key */ + msg[0] = ERL_DRV_ATOM; + msg[1] = driver_mk_atom("key"); + msg[2] = ERL_DRV_NIL; + msg[3] = ERL_DRV_ATOM; + msg[4] = driver_mk_atom("key"); + msg[5] = ERL_DRV_INT; + msg[6] = (ErlDrvTermData) -4711; + msg[7] = ERL_DRV_MAP; + msg[8] = 2; + FAIL_TERM(msg, 9); + /* Signal end of test case */ msg[0] = ERL_DRV_NIL; erl_drv_output_term(driver_mk_port(erlang_port), msg, 1); -- cgit v1.2.3 From dfce3c3ea8ed4064856c29a678c83fc277a05bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:18:14 +0100 Subject: erts: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 4096 -> 4276 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'erts') diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 12b36913a9..4a84b3945a 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 543074e7aab966a597e405bc52c94e57358663a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:47:42 +0100 Subject: erts: Fixup enif_make_map_put on windows --- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4121378e1a..d7c554e60b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,7 +300,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_make_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) -- cgit v1.2.3 From a7ff048b6b5d8649d1709a90f7514fccbb5e9235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 16:04:49 +0100 Subject: erts: Update maps_fold test to respect maps:fold/3 --- erts/emulator/test/map_SUITE.erl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 75381de556..faaafb33bc 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -816,18 +816,11 @@ t_bif_map_from_list(Config) when is_list(Config) -> %% Maps module, not BIFs t_maps_fold(_Config) -> - Vs = lists:seq(1,100), - Rs = lists:reverse(Vs), M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), - %% foldl - 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), - Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), - - %% foldr - 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), - Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M), + %% fold + 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M), ok. -- cgit v1.2.3 From 7849ea4d398717bd48bad76c551dcc5322fe39cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 17:39:37 +0100 Subject: erts: Strengthen map_SUITE tests * Add tests for maps:merge/2 * Add tests for maps:update/3 * Test more corner cases --- erts/emulator/test/map_SUITE.erl | 83 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index faaafb33bc..31c1486f1c 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -37,9 +37,11 @@ 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_update/1, t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, @@ -76,8 +78,10 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, - t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values, + t_bif_map_keys, t_bif_map_merge, t_bif_map_new, + t_bif_map_put, + t_bif_map_remove, t_bif_map_update, + t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, %% erlang @@ -478,8 +482,8 @@ t_bif_map_find(Config) when is_list(Config) -> error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), ok. @@ -496,11 +500,16 @@ t_bif_map_is_key(Config) when is_list(Config) -> 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 + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), ok. t_bif_map_keys(Config) when is_list(Config) -> @@ -526,6 +535,34 @@ t_bif_map_new(Config) when is_list(Config) -> 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), + + %% error case + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + + ok. + + t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, @@ -555,6 +592,11 @@ t_bif_map_put(Config) when is_list(Config) -> [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), [number,wat,3,"hello",<<"value">>] = maps:values(M5), + M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5), + + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6), + [number,wat,3,"hello",<<"other value">>] = maps:values(M6), + %% error case {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), @@ -564,6 +606,8 @@ t_bif_map_put(Config) when is_list(Config) -> ok. 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}, @@ -600,6 +644,33 @@ t_bif_map_remove(Config) when is_list(Config) -> {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. +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 + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), + + ok. + + t_bif_map_values(Config) when is_list(Config) -> @@ -810,6 +881,10 @@ t_bif_map_from_list(Config) when is_list(Config) -> {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% 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. -- cgit v1.2.3