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