aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilherme Andrade <[email protected]>2017-04-22 17:22:32 +0100
committerGuilherme Andrade <[email protected]>2017-04-22 17:25:13 +0100
commit21e1d879faaae2278a68200fe1085d7e73791fa0 (patch)
treec711ec5ac1659532787af3c0f44b764426fc88e5
parentd8756f8665a42effa2f3515369058cff4441abeb (diff)
downloadotp-21e1d879faaae2278a68200fe1085d7e73791fa0.tar.gz
otp-21e1d879faaae2278a68200fe1085d7e73791fa0.tar.bz2
otp-21e1d879faaae2278a68200fe1085d7e73791fa0.zip
erts: Support custom salt in enif_hash
-rw-r--r--erts/doc/src/erl_nif.xml19
-rw-r--r--erts/emulator/beam/bif.c4
-rw-r--r--erts/emulator/beam/erl_bif_info.c2
-rw-r--r--erts/emulator/beam/erl_db_hash.c2
-rw-r--r--erts/emulator/beam/erl_map.h2
-rw-r--r--erts/emulator/beam/erl_nif.c6
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h2
-rw-r--r--erts/emulator/beam/erl_process_dict.c2
-rw-r--r--erts/emulator/beam/erl_trace.c2
-rw-r--r--erts/emulator/beam/erl_utils.h4
-rw-r--r--erts/emulator/beam/utils.c40
-rw-r--r--erts/emulator/hipe/hipe_bif0.c2
-rw-r--r--erts/emulator/test/nif_SUITE.erl6
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c11
14 files changed, 57 insertions, 47 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 53b6fdeaa1..03c96dd3ca 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -827,11 +827,13 @@ typedef enum {
</item>
<tag><c>ERL_NIF_PHASH2</c></tag>
<item>
- <p>Portable hash function that gives the same hash for the same Erlang term
- regardless of machine architecture and ERTS version. Corresponds to
- <seealso marker="erlang#phash2-1"><c>erlang:phash2/1</c></seealso>.</p>
- <p>Slower than <c>ERL_NIF_INTERNAL_HASH.</c></p>
+ <p>Portable hash function that gives the same hash for the
+ same Erlang term regardless of machine architecture and ERTS version.</p>
<p>Generated hash values within <c>0..2^27-1</c>.</p>
+ <p>Slower than <c>ERL_NIF_INTERNAL_HASH.</c>
+ When used with <c>salt=0</c>,
+ it corresponds to
+ <seealso marker="erlang#phash2-1"><c>erlang:phash2/1</c></seealso>.</p>
</item>
</taglist>
</item>
@@ -1411,12 +1413,15 @@ typedef enum {
<func>
<name>
<ret>unsigned long</ret>
- <nametext>enif_hash(ErlNifHash type, ERL_NIF_TERM term)</nametext>
+ <nametext>
+ enif_hash(ErlNifHash type, ERL_NIF_TERM term, unsigned long salt)
+ </nametext>
</name>
<fsummary>Hash terms.</fsummary>
<desc>
- <p>Hash terms according to the specified
- <seealso marker="#ErlNifHash"><c>ErlNifHash</c></seealso> type.</p>
+ <p>Hash <c>term</c> according to the specified
+ <seealso marker="#ErlNifHash"><c>ErlNifHash</c></seealso> <c>type</c>
+ and using the least significant 32 bits of <c>salt</c>.</p>
<p>Range of returned value depends on the hash type.</p>
</desc>
</func>
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 214de3652f..7473a8e43f 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -4881,7 +4881,7 @@ BIF_RETTYPE phash2_1(BIF_ALIST_1)
{
Uint32 hash;
- hash = make_hash2(BIF_ARG_1);
+ hash = make_hash2(BIF_ARG_1, 0);
BIF_RET(make_small(hash & ((1L << 27) - 1)));
}
@@ -4901,7 +4901,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
}
range = (Uint32) u;
}
- hash = make_hash2(BIF_ARG_1);
+ hash = make_hash2(BIF_ARG_1, 0);
if (range) {
final_hash = hash % range; /* [0..range-1] */
} else {
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 3a8687dc59..f2863f5d5b 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3950,7 +3950,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(erts_debug_reader_groups_map(BIF_P, (int) groups));
}
else if (ERTS_IS_ATOM_STR("internal_hash", tp[1])) {
- Uint hash = (Uint) make_internal_hash(tp[2]);
+ Uint hash = (Uint) make_internal_hash(tp[2], 0);
Uint hsz = 0;
Eterm* hp;
erts_bld_uint(NULL, &hsz, hash);
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 7ab27df00c..07f6da7b6d 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -189,7 +189,7 @@ static ERTS_INLINE int add_fixed_deletion(DbTableHash* tb, int ix,
/* optimised version of make_hash (normal case? atomic key) */
#define MAKE_HASH(term) \
((is_atom(term) ? (atom_tab(atom_val(term))->slot.bucket.hvalue) : \
- make_internal_hash(term)) % MAX_HASH)
+ make_internal_hash(term, 0)) % MAX_HASH)
#ifdef ERTS_SMP
# define DB_HASH_LOCK_MASK (DB_HASH_LOCK_CNT-1)
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 61a841f7f0..f7d0413685 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -57,7 +57,7 @@ typedef struct flatmap_s {
#define hashmap_size(x) (((hashmap_head_t*) hashmap_val(x))->size)
-#define hashmap_make_hash(Key) make_internal_hash(Key)
+#define hashmap_make_hash(Key) make_internal_hash(Key, 0)
#define hashmap_restore_hash(Heap,Lvl,Key) \
(((Lvl) < 8) ? hashmap_make_hash(Key) >> (4*(Lvl)) : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), (Key))) >> (4*((Lvl) & 7)))
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 3470cf3fd5..bdfb7f3b8e 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1214,7 +1214,7 @@ int enif_compare(Eterm lhs, Eterm rhs)
return result;
}
-unsigned long enif_hash(ErlNifHash type, Eterm term)
+unsigned long enif_hash(ErlNifHash type, Eterm term, unsigned long salt)
{
#if SIZEOF_LONG < 4
/* This *really* shouldn't happen */
@@ -1222,9 +1222,9 @@ unsigned long enif_hash(ErlNifHash type, Eterm term)
#endif
switch (type) {
case ERL_NIF_INTERNAL_HASH:
- return make_internal_hash(term);
+ return make_internal_hash(term, salt);
case ERL_NIF_PHASH2:
- return make_hash2(term) & ((1 << 27) - 1);
+ return make_hash2(term, salt) & ((1 << 27) - 1);
default:
return 0;
}
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index f52c841ed7..6b9f78d46b 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -47,7 +47,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_get_list_cell,(ErlNifEnv* env, ERL_NIF_TERM term,
ERL_NIF_API_FUNC_DECL(int,enif_get_tuple,(ErlNifEnv* env, ERL_NIF_TERM tpl, int* arity, const ERL_NIF_TERM** array));
ERL_NIF_API_FUNC_DECL(int,enif_is_identical,(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs));
ERL_NIF_API_FUNC_DECL(int,enif_compare,(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs));
-ERL_NIF_API_FUNC_DECL(unsigned long,enif_hash,(ErlNifHash type, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(unsigned long,enif_hash,(ErlNifHash type, ERL_NIF_TERM term, unsigned long salt));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_binary,(ErlNifEnv* env, ErlNifBinary* bin));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_badarg,(ErlNifEnv* env));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_int,(ErlNifEnv* env, int i));
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index 7cfdf20341..01e240c65d 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -57,7 +57,7 @@
((is_small(Term)) ? (Uint32) unsigned_val(Term) : \
((is_atom(Term)) ? \
(Uint32) atom_val(Term) : \
- make_internal_hash(Term)))
+ make_internal_hash(Term, 0)))
#define PD_SZ2BYTES(Sz) (sizeof(ProcDict) + ((Sz) - 1)*sizeof(Eterm))
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 04f3160d42..8855449ca1 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -3230,7 +3230,7 @@ static int tracer_cmp_fun(void* a, void* b)
static HashValue tracer_hash_fun(void* obj)
{
- return make_internal_hash(((ErtsTracerNif*)obj)->module);
+ return make_internal_hash(((ErtsTracerNif*)obj)->module, 0);
}
static void *tracer_alloc_fun(void* tmpl)
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 47289a0af1..b38beaf93d 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -118,9 +118,9 @@ int erts_fit_in_bits_uint(Uint);
Sint erts_list_length(Eterm);
int erts_is_builtin(Eterm, Eterm, int);
Uint32 block_hash(byte *, unsigned, Uint32);
-Uint32 make_hash2(Eterm);
+Uint32 make_hash2(Eterm, Uint32 salt);
Uint32 make_hash(Eterm);
-Uint32 make_internal_hash(Eterm);
+Uint32 make_internal_hash(Eterm, Uint32 salt);
void erts_save_emu_args(int argc, char **argv);
Eterm erts_get_emu_args(struct process *c_p);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 8f3f48f38f..2378c057dd 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1003,7 +1003,7 @@ tail_recur:
break;
}
case MAP_DEF:
- hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
+ hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term, 0);
break;
case TUPLE_DEF:
{
@@ -1109,7 +1109,7 @@ block_hash(byte *k, unsigned length, Uint32 initval)
}
Uint32
-make_hash2(Eterm term)
+make_hash2(Eterm term, Uint32 salt)
{
Uint32 hash;
Uint32 hash_xor_pairs;
@@ -1179,7 +1179,7 @@ make_hash2(Eterm term)
switch (term & _TAG_IMMED2_MASK) {
case _TAG_IMMED2_ATOM:
/* Fast, but the poor hash value should be mixed. */
- return atom_tab(atom_val(term))->slot.bucket.hvalue;
+ return atom_tab(atom_val(term))->slot.bucket.hvalue ^ salt;
}
break;
case _TAG_IMMED1_SMALL:
@@ -1190,7 +1190,7 @@ make_hash2(Eterm term)
term = small_to_big(x, tmp_big);
break;
}
- hash = 0;
+ hash = salt;
SINT32_HASH(x, HCONST);
return hash;
}
@@ -1201,7 +1201,7 @@ make_hash2(Eterm term)
DECLARE_ESTACK(s);
UseTmpHeapNoproc(2);
- hash = 0;
+ hash = salt;
for (;;) {
switch (primary_tag(term)) {
case TAG_PRIMARY_LIST:
@@ -1277,7 +1277,7 @@ make_hash2(Eterm term)
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
+ hash = salt;
hash_xor_pairs = 0;
for (i = size - 1; i >= 0; i--) {
ESTACK_PUSH(s, HASH_MAP_PAIR);
@@ -1296,7 +1296,7 @@ make_hash2(Eterm term)
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
+ hash = salt;
hash_xor_pairs = 0;
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
@@ -1471,7 +1471,7 @@ make_hash2(Eterm term)
break;
default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
+ erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X, %lu)\n", term, salt);
}
}
break;
@@ -1488,21 +1488,21 @@ make_hash2(Eterm term)
case _TAG_IMMED1_IMMED2:
switch (term & _TAG_IMMED2_MASK) {
case _TAG_IMMED2_ATOM:
- if (hash == 0)
+ if (hash == salt)
/* Fast, but the poor hash value should be mixed. */
- hash = atom_tab(atom_val(term))->slot.bucket.hvalue;
+ hash = atom_tab(atom_val(term))->slot.bucket.hvalue ^ salt;
else
UINT32_HASH(atom_tab(atom_val(term))->slot.bucket.hvalue,
HCONST_3);
goto hash2_common;
case _TAG_IMMED2_NIL:
- if (hash == 0)
- hash = 3468870702UL;
+ if (hash == salt)
+ hash = 3468870702UL ^ salt;
else
UINT32_HASH(NIL_DEF, HCONST_2);
goto hash2_common;
default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
+ erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X, %lu)\n", term, salt);
}
case _TAG_IMMED1_SMALL:
{
@@ -1518,7 +1518,7 @@ make_hash2(Eterm term)
}
break;
default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
+ erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X, %lu)\n", term, salt);
hash2_common:
/* Uint32 hash always has the hash value of the previous term,
@@ -1542,7 +1542,7 @@ make_hash2(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = salt;
goto hash2_common;
default:
break;
@@ -1578,7 +1578,7 @@ do { /* Lightweight mixing of constant (type info) */ \
} while (0)
Uint32
-make_internal_hash(Eterm term)
+make_internal_hash(Eterm term, Uint32 salt)
{
Uint32 hash;
Uint32 hash_xor_pairs;
@@ -1587,7 +1587,7 @@ make_internal_hash(Eterm term)
/* Optimization. Simple cases before declaration of estack. */
if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
- hash = 0;
+ hash = salt;
#if ERTS_SIZEOF_ETERM == 8
UINT32_HASH_2((Uint32)term, (Uint32)(term >> 32), HCONST);
#elif ERTS_SIZEOF_ETERM == 4
@@ -1601,7 +1601,7 @@ make_internal_hash(Eterm term)
Eterm tmp;
DECLARE_ESTACK(s);
- hash = 0;
+ hash = salt;
for (;;) {
switch (primary_tag(term)) {
case TAG_PRIMARY_LIST:
@@ -1894,7 +1894,7 @@ make_internal_hash(Eterm term)
goto pop_next;
}
default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
+ erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_internal_hash(0x%X, %lu)\n", term, salt);
}
}
break;
@@ -1907,7 +1907,7 @@ make_internal_hash(Eterm term)
goto pop_next;
default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
+ erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_internal_hash(0x%X, %lu)\n", term, salt);
pop_next:
if (ESTACK_ISEMPTY(s)) {
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 8b420b9e9b..b7297043e5 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -496,7 +496,7 @@ static ErlOffHeap const_term_table_off_heap;
static HashValue const_term_hash(void *tmpl)
{
- return make_hash2((Eterm)tmpl);
+ return make_hash2((Eterm)tmpl, 0);
}
static int const_term_cmp(void *tmpl, void *bucket)
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index e177fa4963..36af68be60 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -2628,7 +2628,7 @@ nif_internal_hash(Config) ->
OnesPerBit =
lists:foldl(
fun (Term, Acc) ->
- NifHashValue = hash_nif(internal, Term),
+ NifHashValue = hash_nif(internal, Term, 0),
lists:foldl(
fun (BitIndex, AccB) ->
BitValue = (NifHashValue band (1 bsl BitIndex)) bsr BitIndex,
@@ -2661,7 +2661,7 @@ nif_phash2(Config) ->
lists:foreach(
fun (Term) ->
HashValue = erlang:phash2(Term),
- NifHashValue = hash_nif(phash2, Term),
+ NifHashValue = hash_nif(phash2, Term, 0),
(HashValue =:= NifHashValue
orelse ct:fail("Expected: ~p\nActual: ~p",
[HashValue, NifHashValue]))
@@ -2714,7 +2714,7 @@ type_test() -> ?nif_stub.
tuple_2_list(_) -> ?nif_stub.
is_identical(_,_) -> ?nif_stub.
compare(_,_) -> ?nif_stub.
-hash_nif(_, _) -> ?nif_stub.
+hash_nif(_Type, _Term, _Salt) -> ?nif_stub.
many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
clone_bin(_) -> ?nif_stub.
make_sub_bin(_,_,_) -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 9d65b9c09b..2a80d48b62 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -689,7 +689,7 @@ static ERL_NIF_TERM compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- if (argc != 2) {
+ if (argc != 3) {
return enif_make_badarg(env);
}
@@ -704,7 +704,12 @@ static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
return enif_make_badarg(env);
}
- return enif_make_ulong(env, enif_hash(type, argv[1]));
+ unsigned long salt;
+ if (! enif_get_ulong(env, argv[2], &salt)) {
+ return enif_make_badarg(env);
+ }
+
+ return enif_make_ulong(env, enif_hash(type, argv[1], salt));
}
static ERL_NIF_TERM many_args_100(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -2884,7 +2889,7 @@ static ErlNifFunc nif_funcs[] =
{"tuple_2_list", 1, tuple_2_list},
{"is_identical",2,is_identical},
{"compare",2,compare},
- {"hash_nif",2,hash_nif},
+ {"hash_nif",3,hash_nif},
{"many_args_100", 100, many_args_100},
{"clone_bin", 1, clone_bin},
{"make_sub_bin", 3, make_sub_bin},