diff options
Diffstat (limited to 'erts/emulator/beam/utils.c')
-rw-r--r-- | erts/emulator/beam/utils.c | 202 |
1 files changed, 176 insertions, 26 deletions
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 7f8bdcb2ca..bc4a05d385 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__ @@ -734,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) @@ -785,10 +788,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_TERM_ARRAY_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 @@ -877,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; @@ -967,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); @@ -976,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); @@ -1070,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 @@ -1087,6 +1110,11 @@ make_hash2(Eterm term) #define HCONST_13 0x08d12e65UL #define HCONST_14 0xa708a81eUL #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 { \ @@ -1182,11 +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); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + UINT32_HASH(size, HCONST_16); + 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 >= 0; i--) { + tmp = ks[i]; + ESTACK_PUSH(s, HASH_MAP_KEY); + ESTACK_PUSH(s, tmp); + } + goto hash2_common; } break; case EXPORT_SUBTAG: @@ -1380,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 @@ -1490,7 +1584,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; @@ -1603,6 +1697,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); @@ -1612,7 +1724,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); @@ -1640,7 +1752,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 } @@ -2007,6 +2119,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 +2405,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. * @@ -2301,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); @@ -2464,7 +2592,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; @@ -2688,11 +2834,6 @@ tailrecur_ne: { FloatDef f1, f2; Eterm big; -#if HEAP_ON_C_STACK - Eterm big_buf[CMP_TMP_HEAP_SIZE]; /* If HEAP_ON_C_STACK */ -#else - Eterm *big_buf = erts_get_scheduler_data()->cmp_tmp_heap; -#endif #if HALFWORD_HEAP Wterm aw = is_immed(a) ? a : rterm2wterm(a,a_base); Wterm bw = is_immed(b) ? b : rterm2wterm(b,b_base); @@ -2703,6 +2844,8 @@ tailrecur_ne: #define MAX_LOSSLESS_FLOAT ((double)((1LL << 53) - 2)) #define MIN_LOSSLESS_FLOAT ((double)(((1LL << 53) - 2)*-1)) #define BIG_ARITY_FLOAT_MAX (1024 / D_EXP) /* arity of max float as a bignum */ + Eterm big_buf[BIG_NEED_SIZE(BIG_ARITY_FLOAT_MAX)]; + b_tag = tag_val_def(bw); switch(_NUMBER_CODE(a_tag, b_tag)) { @@ -2713,13 +2856,15 @@ 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 */ f1.fd = signed_val(aw); j = float_comp(f1.fd, f2.fd); + } #if ERTS_SIZEOF_ETERM == 8 - } else if (f2.fd > (double) (MAX_SMALL + 1)) { + else if (f2.fd > (double) (MAX_SMALL + 1)) { /* Float is a positive bignum, i.e. bigger */ j = -1; } else if (f2.fd < (double) (MIN_SMALL - 1)) { @@ -2730,19 +2875,21 @@ tailrecur_ne: j = signed_val(aw) - (Sint) f2.fd; } #else - } else { + else { /* If float is positive it is bigger than small */ j = (f2.fd > 0.0) ? -1 : 1; } #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))) { @@ -2764,21 +2911,23 @@ tailrecur_ne: j = float_comp(f1.fd, f2.fd); } } else { - big = double_to_big(f2.fd, big_buf); - j = big_comp(aw, big); + big = double_to_big(f2.fd, big_buf, sizeof(big_buf)/sizeof(Eterm)); + j = big_comp(aw, rterm2wterm(big,big_buf)); } if (_NUMBER_CODE(a_tag, b_tag) == FLOAT_BIG) { j = -j; } 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 */ f2.fd = signed_val(bw); j = float_comp(f1.fd, f2.fd); + } #if ERTS_SIZEOF_ETERM == 8 - } else if (f1.fd > (double) (MAX_SMALL + 1)) { + else if (f1.fd > (double) (MAX_SMALL + 1)) { /* Float is a positive bignum, i.e. bigger */ j = 1; } else if (f1.fd < (double) (MIN_SMALL - 1)) { @@ -2789,12 +2938,13 @@ tailrecur_ne: j = (Sint) f1.fd - signed_val(bw); } #else - } else { + else { /* If float is positive it is bigger than small */ j = (f1.fd > 0.0) ? 1 : -1; } #endif /* ERTS_SIZEOF_ETERM == 8 */ break; +exact_fall_through: default: j = b_tag - a_tag; } |