/erts/emulator/beam/utils.c
C | 4791 lines | 3943 code | 486 blank | 362 comment | 788 complexity | c4cdd7b93ea09f08d9dfeb5b2b4c4923 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2018. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
- #ifdef HAVE_CONFIG_H
- # include "config.h"
- #endif
- #define ERTS_DO_INCL_GLB_INLINE_FUNC_DEF
- #include "sys.h"
- #include "erl_vm.h"
- #include "global.h"
- #include "erl_process.h"
- #include "big.h"
- #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__
- #include "erl_db.h"
- #include "erl_threads.h"
- #include "register.h"
- #include "dist.h"
- #include "erl_printf.h"
- #include "erl_threads.h"
- #include "erl_lock_count.h"
- #include "erl_time.h"
- #include "erl_thr_progress.h"
- #include "erl_thr_queue.h"
- #include "erl_sched_spec_pre_alloc.h"
- #include "beam_bp.h"
- #include "erl_ptab.h"
- #include "erl_check_io.h"
- #include "erl_bif_unique.h"
- #include "erl_io_queue.h"
- #define ERTS_WANT_TIMER_WHEEL_API
- #include "erl_time.h"
- #ifdef HIPE
- # include "hipe_mode_switch.h"
- #endif
- #define ERTS_WANT_NFUNC_SCHED_INTERNALS__
- #include "erl_nfunc_sched.h"
- #include "erl_proc_sig_queue.h"
- #undef M_TRIM_THRESHOLD
- #undef M_TOP_PAD
- #undef M_MMAP_THRESHOLD
- #undef M_MMAP_MAX
- #if defined(__GLIBC__) && defined(HAVE_MALLOC_H)
- #include <malloc.h>
- #endif
- #if !defined(HAVE_MALLOPT)
- #undef HAVE_MALLOPT
- #define HAVE_MALLOPT 0
- #endif
- Eterm*
- erts_heap_alloc(Process* p, Uint need, Uint xtra)
- {
- ErlHeapFragment* bp;
- Uint n;
- #if defined(DEBUG) || defined(CHECK_FOR_HOLES)
- Uint i;
- #endif
- #ifdef FORCE_HEAP_FRAGS
- if (p->space_verified && p->space_verified_from!=NULL
- && HEAP_TOP(p) >= p->space_verified_from
- && HEAP_TOP(p) + need <= p->space_verified_from + p->space_verified
- && HEAP_LIMIT(p) - HEAP_TOP(p) >= need) {
- Uint consumed = need + (HEAP_TOP(p) - p->space_verified_from);
- ASSERT(consumed <= p->space_verified);
- p->space_verified -= consumed;
- p->space_verified_from += consumed;
- HEAP_TOP(p) = p->space_verified_from;
- return HEAP_TOP(p) - need;
- }
- p->space_verified = 0;
- p->space_verified_from = NULL;
- #endif /* FORCE_HEAP_FRAGS */
- n = need + xtra;
- bp = MBUF(p);
- if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) {
- Eterm* ret = bp->mem + bp->used_size;
- bp->used_size += need;
- p->mbuf_sz += need;
- return ret;
- }
- #ifdef DEBUG
- n++;
- #endif
- bp = (ErlHeapFragment*)
- ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(n));
- #if defined(DEBUG) || defined(CHECK_FOR_HOLES)
- for (i = 0; i < n; i++) {
- bp->mem[i] = ERTS_HOLE_MARKER;
- }
- #endif
- #ifdef DEBUG
- n--;
- #endif
- bp->next = MBUF(p);
- MBUF(p) = bp;
- bp->alloc_size = n;
- bp->used_size = need;
- MBUF_SIZE(p) += need;
- bp->off_heap.first = NULL;
- bp->off_heap.overhead = 0;
- return bp->mem;
- }
- #ifdef CHECK_FOR_HOLES
- Eterm*
- erts_set_hole_marker(Eterm* ptr, Uint sz)
- {
- Eterm* p = ptr;
- Uint i;
- for (i = 0; i < sz; i++) {
- *p++ = ERTS_HOLE_MARKER;
- }
- return ptr;
- }
- #endif
- /*
- * Helper function for the ESTACK macros defined in global.h.
- */
- void
- erl_grow_estack(ErtsEStack* s, Uint need)
- {
- Uint old_size = (s->end - s->start);
- Uint new_size;
- Uint sp_offs = s->sp - s->start;
- if (need < old_size)
- new_size = 2*old_size;
- else
- new_size = ((need / old_size) + 2) * old_size;
- if (s->start != s->edefault) {
- s->start = erts_realloc(s->alloc_type, s->start,
- new_size*sizeof(Eterm));
- } else {
- Eterm* new_ptr = erts_alloc(s->alloc_type, new_size*sizeof(Eterm));
- sys_memcpy(new_ptr, s->start, old_size*sizeof(Eterm));
- s->start = new_ptr;
- }
- s->end = s->start + new_size;
- s->sp = s->start + sp_offs;
- }
- /*
- * Helper function for the WSTACK macros defined in global.h.
- */
- void
- erl_grow_wstack(ErtsWStack* s, Uint need)
- {
- Uint old_size = (s->wend - s->wstart);
- Uint new_size;
- Uint sp_offs = s->wsp - s->wstart;
- if (need < old_size)
- new_size = 2 * old_size;
- else
- new_size = ((need / old_size) + 2) * old_size;
- if (s->wstart != s->wdefault) {
- s->wstart = erts_realloc(s->alloc_type, s->wstart,
- new_size*sizeof(UWord));
- } else {
- UWord* new_ptr = erts_alloc(s->alloc_type, new_size*sizeof(UWord));
- sys_memcpy(new_ptr, s->wstart, old_size*sizeof(UWord));
- s->wstart = new_ptr;
- }
- s->wend = s->wstart + new_size;
- s->wsp = s->wstart + sp_offs;
- }
- /*
- * Helper function for the PSTACK macros defined in global.h.
- */
- void
- erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes)
- {
- Uint old_size = s->size;
- Uint new_size;
- if (need_bytes < old_size)
- new_size = 2 * old_size;
- else
- new_size = ((need_bytes / old_size) + 2) * old_size;
- if (s->pstart != default_pstack) {
- s->pstart = erts_realloc(s->alloc_type, s->pstart, new_size);
- } else {
- byte* new_ptr = erts_alloc(s->alloc_type, new_size);
- sys_memcpy(new_ptr, s->pstart, old_size);
- s->pstart = new_ptr;
- }
- s->size = new_size;
- }
- /*
- * Helper function for the EQUEUE macros defined in global.h.
- */
- void
- erl_grow_equeue(ErtsEQueue* q, Eterm* default_equeue)
- {
- Uint old_size = (q->end - q->start);
- Uint new_size = old_size * 2;
- Uint first_part = (q->end - q->front);
- Uint second_part = (q->back - q->start);
- Eterm* new_ptr = erts_alloc(q->alloc_type, new_size*sizeof(Eterm));
- ASSERT(q->back == q->front); // of course the queue is full now!
- if (first_part > 0)
- sys_memcpy(new_ptr, q->front, first_part*sizeof(Eterm));
- if (second_part > 0)
- sys_memcpy(new_ptr+first_part, q->start, second_part*sizeof(Eterm));
- if (q->start != default_equeue)
- erts_free(q->alloc_type, q->start);
- q->start = new_ptr;
- q->end = q->start + new_size;
- q->front = q->start;
- q->back = q->start + old_size;
- }
- /* CTYPE macros */
- #define LATIN1
- #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
- #ifdef LATIN1
- #define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z') \
- || ((c) >= 128+95 && (c) <= 255 && (c) != 247))
- #define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z') \
- || ((c) >= 128+64 && (c) <= 128+94 && (c) != 247-32))
- #else
- #define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
- #define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
- #endif
- #define IS_ALNUM(c) (IS_DIGIT(c) || IS_LOWER(c) || IS_UPPER(c))
- /* We don't include 160 (non-breaking space). */
- #define IS_SPACE(c) (c == ' ' || c == '\n' || c == '\t' || c == '\r')
- #ifdef LATIN1
- #define IS_CNTRL(c) ((c) < ' ' || (c) == 127 \
- || ((c) >= 128 && (c) < 128+32))
- #else
- /* Treat all non-ASCII as control characters */
- #define IS_CNTRL(c) ((c) < ' ' || (c) >= 127)
- #endif
- #define IS_PRINT(c) (!IS_CNTRL(c))
- /*
- * Calculate length of a list.
- * Returns -1 if not a proper list (i.e. not terminated with NIL)
- */
- Sint
- erts_list_length(Eterm list)
- {
- Sint i = 0;
- while(is_list(list)) {
- i++;
- list = CDR(list_val(list));
- }
- if (is_not_nil(list)) {
- return -1;
- }
- return i;
- }
- static const struct {
- Sint64 mask;
- int bits;
- } fib_data[] = {{ERTS_I64_LITERAL(0x2), 1},
- {ERTS_I64_LITERAL(0xc), 2},
- {ERTS_I64_LITERAL(0xf0), 4},
- {ERTS_I64_LITERAL(0xff00), 8},
- {ERTS_I64_LITERAL(0xffff0000), 16},
- {ERTS_I64_LITERAL(0xffffffff00000000), 32}};
- static ERTS_INLINE int
- fit_in_bits(Sint64 value, int start)
- {
- int bits = 0;
- int i;
- for (i = start; i >= 0; i--) {
- if (value & fib_data[i].mask) {
- value >>= fib_data[i].bits;
- bits |= fib_data[i].bits;
- }
- }
- bits++;
- return bits;
- }
- int erts_fit_in_bits_int64(Sint64 value)
- {
- return fit_in_bits(value, 5);
- }
- int erts_fit_in_bits_int32(Sint32 value)
- {
- return fit_in_bits((Sint64) (Uint32) value, 4);
- }
- int erts_fit_in_bits_uint(Uint value)
- {
- #if ERTS_SIZEOF_ETERM == 4
- return fit_in_bits((Sint64) (Uint32) value, 4);
- #elif ERTS_SIZEOF_ETERM == 8
- return fit_in_bits(value, 5);
- #else
- # error "No way, Jose"
- #endif
- }
- int
- erts_print(fmtfn_t to, void *arg, char *format, ...)
- {
- int res;
- va_list arg_list;
- va_start(arg_list, format);
- {
- switch ((UWord)to) {
- case (UWord)ERTS_PRINT_STDOUT:
- res = erts_vprintf(format, arg_list);
- break;
- case (UWord)ERTS_PRINT_STDERR:
- res = erts_vfprintf(stderr, format, arg_list);
- break;
- case (UWord)ERTS_PRINT_FILE:
- res = erts_vfprintf((FILE *) arg, format, arg_list);
- break;
- case (UWord)ERTS_PRINT_SBUF:
- res = erts_vsprintf((char *) arg, format, arg_list);
- break;
- case (UWord)ERTS_PRINT_SNBUF:
- res = erts_vsnprintf(((erts_print_sn_buf *) arg)->buf,
- ((erts_print_sn_buf *) arg)->size,
- format,
- arg_list);
- break;
- case (UWord)ERTS_PRINT_DSBUF:
- res = erts_vdsprintf((erts_dsprintf_buf_t *) arg, format, arg_list);
- break;
- case (UWord)ERTS_PRINT_FD:
- res = erts_vfdprintf((int)(SWord) arg, format, arg_list);
- break;
- default:
- res = erts_vcbprintf(to, arg, format, arg_list);
- break;
- }
- }
- va_end(arg_list);
- return res;
- }
- int
- erts_putc(fmtfn_t to, void *arg, char c)
- {
- return erts_print(to, arg, "%c", c);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
- * Some Erlang term building utility functions (to be used when performance *
- * isn't critical). *
- * *
- * Add more functions like these here (and function prototypes in global.h) *
- * when needed. *
- * *
- \* */
- Eterm
- erts_bld_atom(Uint **hpp, Uint *szp, char *str)
- {
- if (hpp)
- return erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
- else
- return THE_NON_VALUE;
- }
- Eterm
- erts_bld_uint(Uint **hpp, Uint *szp, Uint ui)
- {
- Eterm res = THE_NON_VALUE;
- if (IS_USMALL(0, ui)) {
- if (hpp)
- res = make_small(ui);
- }
- else {
- if (szp)
- *szp += BIG_UINT_HEAP_SIZE;
- if (hpp) {
- res = uint_to_big(ui, *hpp);
- *hpp += BIG_UINT_HEAP_SIZE;
- }
- }
- return res;
- }
- /*
- * Erts_bld_uword is more or less similar to erts_bld_uint, but a pointer
- * can safely be passed.
- */
- Eterm
- erts_bld_uword(Uint **hpp, Uint *szp, UWord uw)
- {
- Eterm res = THE_NON_VALUE;
- if (IS_USMALL(0, uw)) {
- if (hpp)
- res = make_small((Uint) uw);
- }
- else {
- if (szp)
- *szp += BIG_UWORD_HEAP_SIZE(uw);
- if (hpp) {
- res = uword_to_big(uw, *hpp);
- *hpp += BIG_UWORD_HEAP_SIZE(uw);
- }
- }
- return res;
- }
- Eterm
- erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64)
- {
- Eterm res = THE_NON_VALUE;
- if (IS_USMALL(0, ui64)) {
- if (hpp)
- res = make_small((Uint) ui64);
- }
- else {
- if (szp)
- *szp += ERTS_UINT64_HEAP_SIZE(ui64);
- if (hpp)
- res = erts_uint64_to_big(ui64, hpp);
- }
- return res;
- }
- Eterm
- erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64)
- {
- Eterm res = THE_NON_VALUE;
- if (IS_SSMALL(si64)) {
- if (hpp)
- res = make_small((Sint) si64);
- }
- else {
- if (szp)
- *szp += ERTS_SINT64_HEAP_SIZE(si64);
- if (hpp)
- res = erts_sint64_to_big(si64, hpp);
- }
- return res;
- }
- Eterm
- erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr)
- {
- Eterm res = THE_NON_VALUE;
- if (szp)
- *szp += 2;
- if (hpp) {
- res = CONS(*hpp, car, cdr);
- *hpp += 2;
- }
- return res;
- }
- Eterm
- erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...)
- {
- Eterm res = THE_NON_VALUE;
- ASSERT(arity < (((Uint)1) << (sizeof(Uint)*8 - _HEADER_ARITY_OFFS)));
- if (szp)
- *szp += arity + 1;
- if (hpp) {
- res = make_tuple(*hpp);
- *((*hpp)++) = make_arityval(arity);
- if (arity > 0) {
- Uint i;
- va_list argp;
- va_start(argp, arity);
- for (i = 0; i < arity; i++) {
- *((*hpp)++) = va_arg(argp, Eterm);
- }
- va_end(argp);
- }
- }
- return res;
- }
- Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[])
- {
- Eterm res = THE_NON_VALUE;
- /*
- * Note callers expect that 'terms' is *not* accessed if hpp == NULL.
- */
- ASSERT(arity < (((Uint)1) << (sizeof(Uint)*8 - _HEADER_ARITY_OFFS)));
- if (szp)
- *szp += arity + 1;
- if (hpp) {
- res = make_tuple(*hpp);
- *((*hpp)++) = make_arityval(arity);
- if (arity > 0) {
- Uint i;
- for (i = 0; i < arity; i++)
- *((*hpp)++) = terms[i];
- }
- }
- return res;
- }
- Eterm
- erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len)
- {
- Eterm res = THE_NON_VALUE;
- Sint i = len;
- if (szp)
- *szp += len*2;
- if (hpp) {
- res = NIL;
- while (--i >= 0) {
- res = CONS(*hpp, make_small((byte) str[i]), res);
- *hpp += 2;
- }
- }
- return res;
- }
- Eterm
- erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[])
- {
- Eterm list = THE_NON_VALUE;
- if (szp)
- *szp += 2*length;
- if (hpp) {
- Sint i = length;
- list = NIL;
- while (--i >= 0) {
- list = CONS(*hpp, terms[i], list);
- *hpp += 2;
- }
- }
- return list;
- }
- Eterm
- erts_bld_2tup_list(Uint **hpp, Uint *szp,
- Sint length, Eterm terms1[], Uint terms2[])
- {
- Eterm res = THE_NON_VALUE;
- if (szp)
- *szp += 5*length;
- if (hpp) {
- Sint i = length;
- res = NIL;
- while (--i >= 0) {
- res = CONS(*hpp+3, TUPLE2(*hpp, terms1[i], terms2[i]), res);
- *hpp += 5;
- }
- }
- return res;
- }
- Eterm
- erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp,
- Sint length, Eterm atoms[], UWord uints[])
- {
- Sint i;
- Eterm res = THE_NON_VALUE;
- if (szp) {
- *szp += 5*length;
- i = length;
- while (--i >= 0) {
- if (!IS_USMALL(0, uints[i]))
- *szp += BIG_UINT_HEAP_SIZE;
- }
- }
- if (hpp) {
- i = length;
- res = NIL;
- while (--i >= 0) {
- Eterm ui;
- if (IS_USMALL(0, uints[i]))
- ui = make_small(uints[i]);
- else {
- ui = uint_to_big(uints[i], *hpp);
- *hpp += BIG_UINT_HEAP_SIZE;
- }
- res = CONS(*hpp+3, TUPLE2(*hpp, atoms[i], ui), res);
- *hpp += 5;
- }
- }
- return res;
- }
- Eterm
- erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
- Eterm atoms[], Uint uints1[], Uint uints2[])
- {
- Sint i;
- Eterm res = THE_NON_VALUE;
- if (szp) {
- *szp += 6*length;
- i = length;
- while (--i >= 0) {
- if (!IS_USMALL(0, uints1[i]))
- *szp += BIG_UINT_HEAP_SIZE;
- if (!IS_USMALL(0, uints2[i]))
- *szp += BIG_UINT_HEAP_SIZE;
- }
- }
- if (hpp) {
- i = length;
- res = NIL;
- while (--i >= 0) {
- Eterm ui1;
- Eterm ui2;
- if (IS_USMALL(0, uints1[i]))
- ui1 = make_small(uints1[i]);
- else {
- ui1 = uint_to_big(uints1[i], *hpp);
- *hpp += BIG_UINT_HEAP_SIZE;
- }
- if (IS_USMALL(0, uints2[i]))
- ui2 = make_small(uints2[i]);
- else {
- ui2 = uint_to_big(uints2[i], *hpp);
- *hpp += BIG_UINT_HEAP_SIZE;
- }
- res = CONS(*hpp+4, TUPLE3(*hpp, atoms[i], ui1, ui2), res);
- *hpp += 6;
- }
- }
- return res;
- }
- /* *\
- * *
- \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /* make a hash index from an erlang term */
- /*
- ** There are two hash functions.
- **
- ** make_hash: A hash function that will give the same values for the same
- ** terms regardless of the internal representation. Small integers are
- ** hashed using the same algorithm as bignums and bignums are hashed
- ** independent of the CPU endianess.
- ** Make_hash also hashes pids, ports and references like 32 bit numbers
- ** (but with different constants).
- ** make_hash() is called from the bif erlang:phash/2
- **
- ** The idea behind the hash algorithm is to produce values suitable for
- ** linear dynamic hashing. We cannot choose the range at all while hashing
- ** (it's not even supplied to the hashing functions). The good old algorithm
- ** [H = H*C+X mod M, where H is the hash value, C is a "random" constant(or M),
- ** M is the range, preferably a prime, and X is each byte value] is therefore
- ** modified to:
- ** H = H*C+X mod 2^32, where C is a large prime. This gives acceptable
- ** "spreading" of the hashes, so that later modulo calculations also will give
- ** acceptable "spreading" in the range.
- ** We really need to hash on bytes, otherwise the
- ** upper bytes of a word will be less significant than the lower ones. That's
- ** not acceptable at all. For internal use one could maybe optimize by using
- ** another hash function, that is less strict but faster. That is, however, not
- ** implemented.
- **
- ** Short semi-formal description of make_hash:
- **
- ** In make_hash, the number N is treated like this:
- ** Abs(N) is hashed bytewise with the least significant byte, B(0), first.
- ** The number of bytes (J) to calculate hash on in N is
- ** (the number of _32_ bit words needed to store the unsigned
- ** value of abs(N)) * 4.
- ** X = FUNNY_NUMBER2
- ** If N < 0, Y = FUNNY_NUMBER4 else Y = FUNNY_NUMBER3.
- ** The hash value is Y*h(J) mod 2^32 where h(J) is calculated like
- ** h(0) = <initial hash>
- ** h(i) = h(i-1)*X + B(i-1)
- ** The above should hold regardless of internal representation.
- ** Pids are hashed like small numbers but with differrent constants, as are
- ** ports.
- ** References are hashed like ports but only on the least significant byte.
- ** Binaries are hashed on all bytes (not on the 15 first as in
- ** make_broken_hash()).
- ** Bytes in lists (possibly text strings) use a simpler multiplication inlined
- ** in the handling of lists, that is an optimization.
- ** Everything else is like in the old hash (make_broken_hash()).
- **
- ** make_hash2() is faster than make_hash, in particular for bignums
- ** and binaries, and produces better hash values.
- */
- /* some prime numbers just above 2 ^ 28 */
- #define FUNNY_NUMBER1 268440163
- #define FUNNY_NUMBER2 268439161
- #define FUNNY_NUMBER3 268435459
- #define FUNNY_NUMBER4 268436141
- #define FUNNY_NUMBER5 268438633
- #define FUNNY_NUMBER6 268437017
- #define FUNNY_NUMBER7 268438039
- #define FUNNY_NUMBER8 268437511
- #define FUNNY_NUMBER9 268439627
- #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)
- {
- byte* ptr;
- Uint bitoffs;
- Uint bitsize;
- ERTS_GET_BINARY_BYTES(bin, ptr, bitoffs, bitsize);
- if (bitoffs == 0) {
- while (sz--) {
- hash = hash*FUNNY_NUMBER1 + *ptr++;
- }
- if (bitsize > 0) {
- byte b = *ptr;
- b >>= 8 - bitsize;
- hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize;
- }
- } else {
- Uint previous = *ptr++;
- Uint b;
- Uint lshift = bitoffs;
- Uint rshift = 8 - lshift;
- while (sz--) {
- b = (previous << lshift) & 0xFF;
- previous = *ptr++;
- b |= previous >> rshift;
- hash = hash*FUNNY_NUMBER1 + b;
- }
- if (bitsize > 0) {
- b = (previous << lshift) & 0xFF;
- previous = *ptr++;
- b |= previous >> rshift;
- b >>= 8 - bitsize;
- hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize;
- }
- }
- return hash;
- }
- Uint32 make_hash(Eterm term_arg)
- {
- DECLARE_WSTACK(stack);
- Eterm term = term_arg;
- Eterm hash = 0;
- unsigned op;
- #define MAKE_HASH_TUPLE_OP (FIRST_VACANT_TAG_DEF)
- #define MAKE_HASH_TERM_ARRAY_OP (FIRST_VACANT_TAG_DEF+1)
- #define MAKE_HASH_CDR_PRE_OP (FIRST_VACANT_TAG_DEF+2)
- #define MAKE_HASH_CDR_POST_OP (FIRST_VACANT_TAG_DEF+3)
- /*
- ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit
- ** integer.
- ** If the endianess is known, we could be smarter here,
- ** but that gives no significant speedup (on a sparc at least)
- */
- #define UINT32_HASH_STEP(Expr, Prime1) \
- do { \
- Uint32 x = (Uint32) (Expr); \
- hash = \
- (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
- ((x >> 8) & 0xFF)) * (Prime1) + \
- ((x >> 16) & 0xFF)) * (Prime1) + \
- (x >> 24)); \
- } while(0)
- #define UINT32_HASH_RET(Expr, Prime1, Prime2) \
- UINT32_HASH_STEP(Expr, Prime1); \
- hash = hash * (Prime2); \
- break
- /*
- * Significant additions needed for real 64 bit port with larger fixnums.
- */
- /*
- * Note, for the simple 64bit port, not utilizing the
- * larger word size this function will work without modification.
- */
- tail_recur:
- op = tag_val_def(term);
- for (;;) {
- switch (op) {
- case NIL_DEF:
- hash = hash*FUNNY_NUMBER3 + 1;
- break;
- case ATOM_DEF:
- hash = hash*FUNNY_NUMBER1 +
- (atom_tab(atom_val(term))->slot.bucket.hvalue);
- break;
- case SMALL_DEF:
- {
- Sint y1 = signed_val(term);
- Uint y2 = y1 < 0 ? -(Uint)y1 : y1;
- UINT32_HASH_STEP(y2, FUNNY_NUMBER2);
- #if defined(ARCH_64)
- if (y2 >> 32)
- UINT32_HASH_STEP(y2 >> 32, FUNNY_NUMBER2);
- #endif
- hash *= (y1 < 0 ? FUNNY_NUMBER4 : FUNNY_NUMBER3);
- break;
- }
- case BINARY_DEF:
- {
- Uint sz = binary_size(term);
- hash = hash_binary_bytes(term, sz, hash);
- hash = hash*FUNNY_NUMBER4 + sz;
- break;
- }
- case EXPORT_DEF:
- {
- Export* ep = *((Export **) (export_val(term) + 1));
- hash = hash * FUNNY_NUMBER11 + ep->info.mfa.arity;
- hash = hash*FUNNY_NUMBER1 +
- (atom_tab(atom_val(ep->info.mfa.module))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
- (atom_tab(atom_val(ep->info.mfa.function))->slot.bucket.hvalue);
- break;
- }
- case FUN_DEF:
- {
- ErlFunThing* funp = (ErlFunThing *) fun_val(term);
- Uint num_free = funp->num_free;
- hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
- (atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
- 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_TERM_ARRAY_OP);
- }
- term = funp->env[0];
- goto tail_recur;
- }
- break;
- }
- case PID_DEF:
- UINT32_HASH_RET(internal_pid_number(term),FUNNY_NUMBER5,FUNNY_NUMBER6);
- case EXTERNAL_PID_DEF:
- UINT32_HASH_RET(external_pid_number(term),FUNNY_NUMBER5,FUNNY_NUMBER6);
- case PORT_DEF:
- UINT32_HASH_RET(internal_port_number(term),FUNNY_NUMBER9,FUNNY_NUMBER10);
- case EXTERNAL_PORT_DEF:
- UINT32_HASH_RET(external_port_number(term),FUNNY_NUMBER9,FUNNY_NUMBER10);
- case REF_DEF:
- UINT32_HASH_RET(internal_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
- case EXTERNAL_REF_DEF:
- UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
- case FLOAT_DEF:
- {
- FloatDef ff;
- GET_DOUBLE(term, ff);
- if (ff.fd == 0.0f) {
- /* ensure positive 0.0 */
- ff.fd = erts_get_positive_zero_float();
- }
- hash = hash*FUNNY_NUMBER6 + (ff.fw[0] ^ ff.fw[1]);
- break;
- }
- case MAKE_HASH_CDR_PRE_OP:
- term = (Eterm) WSTACK_POP(stack);
- if (is_not_list(term)) {
- WSTACK_PUSH(stack, (UWord) MAKE_HASH_CDR_POST_OP);
- goto tail_recur;
- }
- /* fall through */
- case LIST_DEF:
- {
- Eterm* list = list_val(term);
- while(is_byte(*list)) {
- /* Optimization for strings.
- ** Note that this hash is different from a 'small' hash,
- ** as multiplications on a Sparc is so slow.
- */
- hash = hash*FUNNY_NUMBER2 + unsigned_val(*list);
- if (is_not_list(CDR(list))) {
- WSTACK_PUSH(stack, MAKE_HASH_CDR_POST_OP);
- term = CDR(list);
- goto tail_recur;
- }
- list = list_val(CDR(list));
- }
- WSTACK_PUSH2(stack, CDR(list), MAKE_HASH_CDR_PRE_OP);
- term = CAR(list);
- goto tail_recur;
- }
- case MAKE_HASH_CDR_POST_OP:
- hash *= FUNNY_NUMBER8;
- break;
- case BIG_DEF:
- /* Note that this is the exact same thing as the hashing of smalls.*/
- {
- Eterm* ptr = big_val(term);
- Uint n = BIG_SIZE(ptr);
- Uint k = n-1;
- ErtsDigit d;
- int is_neg = BIG_SIGN(ptr);
- Uint i;
- int j;
- for (i = 0; i < k; i++) {
- d = BIG_DIGIT(ptr, i);
- for(j = 0; j < sizeof(ErtsDigit); ++j) {
- hash = (hash*FUNNY_NUMBER2) + (d & 0xff);
- d >>= 8;
- }
- }
- d = BIG_DIGIT(ptr, k);
- k = sizeof(ErtsDigit);
- #if defined(ARCH_64)
- if (!(d >> 32))
- k /= 2;
- #endif
- for(j = 0; j < (int)k; ++j) {
- hash = (hash*FUNNY_NUMBER2) + (d & 0xff);
- d >>= 8;
- }
- hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3;
- break;
- }
- case MAP_DEF:
- hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
- break;
- case TUPLE_DEF:
- {
- Eterm* ptr = tuple_val(term);
- Uint arity = arityval(*ptr);
- WSTACK_PUSH3(stack, (UWord) arity, (UWord)(ptr+1), (UWord) arity);
- op = MAKE_HASH_TUPLE_OP;
- }/*fall through*/
- case MAKE_HASH_TUPLE_OP:
- case MAKE_HASH_TERM_ARRAY_OP:
- {
- Uint i = (Uint) WSTACK_POP(stack);
- Eterm* ptr = (Eterm*) WSTACK_POP(stack);
- if (i != 0) {
- term = *ptr;
- WSTACK_PUSH3(stack, (UWord)(ptr+1), (UWord) i-1, (UWord) op);
- goto tail_recur;
- }
- if (op == MAKE_HASH_TUPLE_OP) {
- Uint32 arity = (Uint32) WSTACK_POP(stack);
- hash = hash*FUNNY_NUMBER9 + arity;
- }
- break;
- }
- default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op);
- return 0;
- }
- if (WSTACK_ISEMPTY(stack)) break;
- op = WSTACK_POP(stack);
- }
- DESTROY_WSTACK(stack);
- return hash;
- #undef MAKE_HASH_TUPLE_OP
- #undef MAKE_HASH_TERM_ARRAY_OP
- #undef MAKE_HASH_CDR_PRE_OP
- #undef MAKE_HASH_CDR_POST_OP
- #undef UINT32_HASH_STEP
- #undef UINT32_HASH_RET
- }
- /* Hash function suggested by Bob Jenkins. */
- #define MIX(a,b,c) \
- do { \
- a -= b; a -= c; a ^= (c>>13); \
- b -= c; b -= a; b ^= (a<<8); \
- c -= a; c -= b; c ^= (b>>13); \
- a -= b; a -= c; a ^= (c>>12); \
- b -= c; b -= a; b ^= (a<<16); \
- c -= a; c -= b; c ^= (b>>5); \
- a -= b; a -= c; a ^= (c>>3); \
- b -= c; b -= a; b ^= (a<<10); \
- c -= a; c -= b; c ^= (b>>15); \
- } while(0)
- #define HCONST 0x9e3779b9UL /* the golden ratio; an arbitrary value */
- static Uint32
- block_hash(byte *k, Uint length, Uint32 initval)
- {
- Uint32 a,b,c;
- Uint len;
- /* Set up the internal state */
- len = length;
- a = b = HCONST;
- c = initval; /* the previous hash value */
- while (len >= 12)
- {
- a += (k[0] +((Uint32)k[1]<<8) +((Uint32)k[2]<<16) +((Uint32)k[3]<<24));
- b += (k[4] +((Uint32)k[5]<<8) +((Uint32)k[6]<<16) +((Uint32)k[7]<<24));
- c += (k[8] +((Uint32)k[9]<<8) +((Uint32)k[10]<<16)+((Uint32)k[11]<<24));
- MIX(a,b,c);
- k += 12; len -= 12;
- }
- c += length;
- switch(len) /* all the case statements fall through */
- {
- case 11: c+=((Uint32)k[10]<<24);
- case 10: c+=((Uint32)k[9]<<16);
- case 9 : c+=((Uint32)k[8]<<8);
- /* the first byte of c is reserved for the length */
- case 8 : b+=((Uint32)k[7]<<24);
- case 7 : b+=((Uint32)k[6]<<16);
- case 6 : b+=((Uint32)k[5]<<8);
- case 5 : b+=k[4];
- case 4 : a+=((Uint32)k[3]<<24);
- case 3 : a+=((Uint32)k[2]<<16);
- case 2 : a+=((Uint32)k[1]<<8);
- case 1 : a+=k[0];
- /* case 0: nothing left to add */
- }
- MIX(a,b,c);
- return c;
- }
- Uint32
- make_hash2(Eterm term)
- {
- Uint32 hash;
- Uint32 hash_xor_pairs;
- DeclareTmpHeapNoproc(tmp_big,2);
- ERTS_UNDEF(hash_xor_pairs, 0);
- /* (HCONST * {2, ..., 22}) mod 2^32 */
- #define HCONST_2 0x3c6ef372UL
- #define HCONST_3 0xdaa66d2bUL
- #define HCONST_4 0x78dde6e4UL
- #define HCONST_5 0x1715609dUL
- #define HCONST_6 0xb54cda56UL
- #define HCONST_7 0x5384540fUL
- #define HCONST_8 0xf1bbcdc8UL
- #define HCONST_9 0x8ff34781UL
- #define HCONST_10 0x2e2ac13aUL
- #define HCONST_11 0xcc623af3UL
- #define HCONST_12 0x6a99b4acUL
- #define HCONST_13 0x08d12e65UL
- #define HCONST_14 0xa708a81eUL
- #define HCONST_15 0x454021d7UL
- #define HCONST_16 0xe3779b90UL
- #define HCONST_17 0x81af1549UL
- #define HCONST_18 0x1fe68f02UL
- #define HCONST_19 0xbe1e08bbUL
- #define HCONST_20 0x5c558274UL
- #define HCONST_21 0xfa8cfc2dUL
- #define HCONST_22 0x98c475e6UL
- #define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF))
- #define HASH_MAP_PAIR (_make_header(2,_TAG_HEADER_REF))
- #define HASH_CDR (_make_header(3,_TAG_HEADER_REF))
- #define UINT32_HASH_2(Expr1, Expr2, AConst) \
- do { \
- Uint32 a,b; \
- a = AConst + (Uint32) (Expr1); \
- b = AConst + (Uint32) (Expr2); \
- MIX(a,b,hash); \
- } while(0)
- #define UINT32_HASH(Expr, AConst) UINT32_HASH_2(Expr, 0, AConst)
- #define SINT32_HASH(Expr, AConst) \
- do { \
- Sint32 y = (Sint32) (Expr); \
- if (y < 0) { \
- UINT32_HASH(-y, AConst); \
- /* Negative numbers are unnecessarily mixed twice. */ \
- } \
- UINT32_HASH(y, AConst); \
- } while(0)
- #define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
- #ifdef ARCH_64
- # define POINTER_HASH(Ptr, AConst) UINT32_HASH_2((Uint32)(UWord)(Ptr), (((UWord)(Ptr)) >> 32), AConst)
- #else
- # define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, AConst)
- #endif
- /* Optimization. Simple cases before declaration of estack. */
- if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
- switch (term & _TAG_IMMED1_MASK) {
- case _TAG_IMMED1_IMMED2:
- 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;
- }
- break;
- case _TAG_IMMED1_SMALL:
- {
- Sint x = signed_val(term);
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
- }
- hash = 0;
- SINT32_HASH(x, HCONST);
- return hash;
- }
- }
- };
- {
- Eterm tmp;
- DECLARE_ESTACK(s);
- UseTmpHeapNoproc(2);
- hash = 0;
- for (;;) {
- switch (primary_tag(term)) {
- case TAG_PRIMARY_LIST:
- {
- int c = 0;
- Uint32 sh = 0;
- Eterm* ptr = list_val(term);
- while (is_byte(*ptr)) {
- /* Optimization for strings. */
- sh = (sh << 8) + unsigned_val(*ptr);
- if (c == 3) {
- UINT32_HASH(sh, HCONST_4);
- c = sh = 0;
- } else {
- c++;
- }
- term = CDR(ptr);
- if (is_not_list(term))
- break;
- ptr = list_val(term);
- }
- if (c > 0)
- UINT32_HASH(sh, HCONST_4);
- if (is_list(term)) {
- tmp = CDR(ptr);
- ESTACK_PUSH(s, tmp);
- term = CAR(ptr);
- }
- }
- break;
- case TAG_PRIMARY_BOXED:
- {
- Eterm hdr = *boxed_val(term);
- ASSERT(is_header(hdr));
- switch (hdr & _TAG_HEADER_MASK) {
- case ARITYVAL_SUBTAG:
- {
- int i;
- int arity = header_arity(hdr);
- Eterm* elem = tuple_val(term);
- UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
- goto hash2_common;
- for (i = arity; ; i--) {
- term = elem[i];
- if (i == 1)
- break;
- ESTACK_PUSH(s, term);
- }
- }
- break;
- case MAP_SUBTAG:
- {
- Eterm* ptr = boxed_val(term) + 1;
- Uint size;
- int i;
- switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_FLATMAP:
- {
- flatmap_t *mp = (flatmap_t *)flatmap_val(term);
- Eterm *ks = flatmap_get_keys(mp);
- Eterm *vs = flatmap_get_values(mp);
- size = flatmap_get_size(mp);
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
- goto hash2_common;
- /* We want a portable hash function that is *independent* of
- * the order in which keys and values are encountered.
- * We therefore calculate context independent hashes for all .
- * key-value pairs and then xor them together.
- */
- ESTACK_PUSH(s, hash_xor_pairs);
- ESTACK_PUSH(s, hash);
- ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
- hash_xor_pairs = 0;
- for (i = size - 1; i >= 0; i--) {
- ESTACK_PUSH(s, HASH_MAP_PAIR);
- ESTACK_PUSH(s, vs[i]);
- ESTACK_PUSH(s, ks[i]);
- }
- goto hash2_common;
- }
- case HAMT_SUBTAG_HEAD_ARRAY:
- case HAMT_SUBTAG_HEAD_BITMAP:
- size = *ptr++;
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
- goto hash2_common;
- ESTACK_PUSH(s, hash_xor_pairs);
- ESTACK_PUSH(s, hash);
- ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
- hash_xor_pairs = 0;
- }
- switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_ARRAY:
- i = 16;
- break;
- case HAMT_SUBTAG_HEAD_BITMAP:
- case HAMT_SUBTAG_NODE_BITMAP:
- i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
- break;
- default:
- erts_exit(ERTS_ERROR_EXIT, "bad header");
- }
- while (i) {
- if (is_list(*ptr)) {
- Eterm* cons = list_val(*ptr);
- ESTACK_PUSH(s, HASH_MAP_PAIR);
- ESTACK_PUSH(s, CDR(cons));
- ESTACK_PUSH(s, CAR(cons));
- }
- else {
- ASSERT(is_boxed(*ptr));
- ESTACK_PUSH(s, *ptr);
- }
- i--; ptr++;
- }
- goto hash2_common;
- }
- break;
- case EXPORT_SUBTAG:
- {
- Export* ep = *((Export **) (export_val(term) + 1));
- UINT32_HASH_2
- (ep->info.mfa.arity,
- atom_tab(atom_val(ep->info.mfa.module))->slot.bucket.hvalue,
- HCONST);
- UINT32_HASH
- (atom_tab(atom_val(ep->info.mfa.function))->slot.bucket.hvalue,
- HCONST_14);
- goto hash2_common;
- }
- case FUN_SUBTAG:
- {
- ErlFunThing* funp = (ErlFunThing *) fun_val(term);
- Uint num_free = funp->num_free;
- UINT32_HASH_2
- (num_free,
- atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
- HCONST);
- UINT32_HASH_2
- (funp->fe->old_index, funp->fe->old_uniq, HCONST);
- if (num_free == 0) {
- goto hash2_common;
- } else {
- Eterm* bptr = funp->env + num_free - 1;
- while (num_free-- > 1) {
- term = *bptr--;
- ESTACK_PUSH(s, term);
- }
- term = *bptr;
- }
- }
- break;
- case REFC_BINARY_SUBTAG:
- case HEAP_BINARY_SUBTAG:
- case SUB_BINARY_SUBTAG:
- {
- byte* bptr;
- unsigned sz = binary_size(term);
- Uint32 con = HCONST_13 + hash;
- Uint bitoffs;
- Uint bitsize;
- ERTS_GET_BINARY_BYTES(term, bptr, bitoffs, bitsize);
- if (sz == 0 && bitsize == 0) {
- hash = con;
- } else {
- if (bitoffs == 0) {
- hash = block_hash(bptr, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (bptr[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- } else {
- byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP,
- sz + (bitsize != 0));
- erts_copy_bits(bptr, bitoffs, 1, buf, 0, 1, sz*8+bitsize);
- hash = block_hash(buf, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (buf[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- }
- }
- goto hash2_common;
- }
- break;
- case POS_BIG_SUBTAG:
- case NEG_BIG_SUBTAG:
- {
- Eterm* ptr = big_val(term);
- Uint i = 0;
- Uint n = BIG_SIZE(ptr);
- Uint32 con = BIG_SIGN(ptr) ? HCONST_10 : HCONST_11;
- #if D_EXP == 16
- do {
- Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- x += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #elif D_EXP == 32
- do {
- Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #elif D_EXP == 64
- do {
- Uint t;
- Uint32 x, y;
- ASSERT(i < n);
- t = BIG_DIGIT(ptr, i++);
- x = t & 0xffffffff;
- y = t >> 32;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #else
- #error "unsupported D_EXP size"
- #endif
- goto hash2_common;
- }
- break;
- case REF_SUBTAG:
- /* All parts of the ref should be hashed. */
- UINT32_HASH(internal_ref_numbers(term)[0], HCONST_7);
- goto hash2_common;
- break;
- case EXTERNAL_REF_SUBTAG:
- /* All parts of the ref should be hashed. */
- UINT32_HASH(external_ref_numbers(term)[0], HCONST_7);
- goto hash2_common;
- break;
- case EXTERNAL_PID_SUBTAG:
- /* Only 15 bits are hashed. */
- UINT32_HASH(external_pid_number(term), HCONST_5);
- goto hash2_common;
- case EXTERNAL_PORT_SUBTAG:
- /* Only 15 bits are hashed. */
- UINT32_HASH(external_port_number(term), HCONST_6);
- goto hash2_common;
- case FLOAT_SUBTAG:
- {
- FloatDef ff;
- GET_DOUBLE(term, ff);
- if (ff.fd == 0.0f) {
- /* ensure positive 0.0 */
- ff.fd = erts_get_positive_zero_float();
- }
- #if defined(WORDS_BIGENDIAN) || defined(DOUBLE_MIDDLE_ENDIAN)
- UINT32_HASH_2(ff.fw[0], ff.fw[1], HCONST_12);
- #else
- UINT32_HASH_2(ff.fw[1], ff.fw[0], HCONST_12);
- #endif
- goto hash2_common;
- }
- break;
- default:
- erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
- }
- }
- break;
- case TAG_PRIMARY_IMMED1:
- switch (term & _TAG_IMMED1_MASK) {
- case _TAG_IMMED1_PID:
- /* Only 15 bits are hashed. */
- UINT32_HASH(internal_pid_number(term), HCONST_5);
- goto hash2_common;
- case _TAG_IMMED1_PORT:
- /* Only 15 bits are hashed. */
- UINT32_HASH(internal_port_number(term), HCONST_6);
- goto hash2_common;
- case _TAG_IMMED1_IMMED2:
- switch (term & _TAG_IMMED2_MASK) {
- case _TAG_IMMED2_ATOM:
- if (hash == 0)
- /* Fast, but the poor hash value should be mixed. */
- hash = atom_tab(atom_val(term))->slot.bucket.hvalue;
- 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;
- 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);
- }
- case _TAG_IMMED1_SMALL:
- {
- Sint x = signed_val(term);
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
- }
- SINT32_HASH(x, HCONST);
- goto hash2_common;
- }
- }
- break;
- default:
- erts_exit(ERTS_ERROR_EXIT, "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_pairs, HCONST_19);
- hash_xor_pairs = (Uint32) ESTACK_POP(s);
- goto hash2_common;
- }
- case HASH_MAP_PAIR:
- hash_xor_pairs ^= hash;
- hash = 0;
- goto hash2_common;
- default:
- break;
- }
- }
- }
- }
- }
- /* Term hash function for internal use.
- *
- * Limitation #1: Is not "portable" in any way between different VM instances.
- *
- * Limitation #2: The hash value is only valid as long as the term exists
- * somewhere in the VM. Why? Because external pids, ports and refs are hashed
- * by mixing the node *pointer* value. If a node disappears and later reappears
- * with a new ErlNode struct, externals from that node will hash different than
- * before.
- *
- * One IMPORTANT property must hold (for hamt).
- * EVERY BIT of the term that is significant for equality (see EQ)
- * MUST BE USED AS INPUT FOR THE HASH. Two different terms must always have a
- * chance of hashing different when salted: hash([Salt|A]) vs hash([Salt|B]).
- *
- * This is why we cannot use cached hash values for atoms for example.
- *
- */
- #define CONST_HASH(AConst) \
- do { /* Lightweight mixing of constant (type info) */ \
- hash ^= AConst; \
- hash = (hash << 17) ^ (hash >> (32-17)); \
- } while (0)
- Uint32
- make_internal_hash(Eterm term, Uint32 salt)
- {
- Uint32 hash;
- /* Optimization. Simple cases before declaration of estack. */
- if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
- hash = salt;
- #if ERTS_SIZEOF_ETERM == 8
- UINT32_HASH_2((Uint32)term, (Uint32)(term >> 32), HCONST);
- #elif ERTS_SIZEOF_ETERM == 4
- UINT32_HASH(term, HCONST);
- #else
- # error "No you don't"
- #endif
- return hash;
- }
- {
- Eterm tmp;
- DECLARE_ESTACK(s);
- hash = salt;
- for (;;) {
- switch (primary_tag(term)) {
- case TAG_PRIMARY_LIST:
- {
- int c = 0;
- Uint32 sh = 0;
- Eterm* ptr = list_val(term);
- while (is_byte(*ptr)) {
- /* Optimization for strings. */
- sh = (sh << 8) + unsigned_val(*ptr);
- if (c == 3) {
- UINT32_HASH(sh, HCONST_4);
- c = sh = 0;
- } else {
- c++;
- }
- term = CDR(ptr);
- if (is_not_list(term))
- break;
- ptr = list_val(term);
- }
- if (c > 0)
- UINT32_HASH_2(sh, (Uint32)c, HCONST_22);
- if (is_list(term)) {
- tmp = CDR(ptr);
- CONST_HASH(HCONST_17); /* Hash CAR in cons cell */
- ESTACK_PUSH(s, tmp);
- if (is_not_list(tmp)) {
- ESTACK_PUSH(s, HASH_CDR);
- }
- term = CAR(ptr);
- }
- }
- break;
- case TAG_PRIMARY_BOXED:
- {
- Eterm hdr = *boxed_val(term);
- ASSERT(is_header(hdr));
- switch (hdr & _TAG_HEADER_MASK) {
- case ARITYVAL_SUBTAG:
- {
- int i;
- int arity = header_arity(hdr);
- Eterm* elem = tuple_val(term);
- UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
- goto pop_next;
- for (i = arity; ; i--) {
- term = elem[i];
- if (i == 1)
- break;
- ESTACK_PUSH(s, term);
- }
- }
- break;
- case MAP_SUBTAG:
- {
- Eterm* ptr = boxed_val(term) + 1;
- Uint size;
- int i;
- /*
- * We rely on key-value iteration order being constant
- * for identical maps (in this VM instance).
- */
- switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_FLATMAP:
- {
- flatmap_t *mp = (flatmap_t *)flatmap_val(term);
- Eterm *ks = flatmap_get_keys(mp);
- Eterm *vs = flatmap_get_values(mp);
- size = flatmap_get_size(mp);
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
- goto pop_next;
- for (i = size - 1; i >= 0; i--) {
- ESTACK_PUSH(s, vs[i]);
- ESTACK_PUSH(s, ks[i]);
- }
- goto pop_next;
- }
- case HAMT_SUBTAG_HEAD_ARRAY:
- case HAMT_SUBTAG_HEAD_BITMAP:
- size = *ptr++;
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
- goto pop_next;
- }
- switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_ARRAY:
- i = 16;
- break;
- case HAMT_SUBTAG_HEAD_BITMAP:
- case HAMT_SUBTAG_NODE_BITMAP:
- i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
- break;
- default:
- erts_exit(ERTS_ERROR_EXIT, "bad header");
- }
- while (i) {
- if (is_list(*ptr)) {
- Eterm* cons = list_val(*ptr);
- ESTACK_PUSH(s, CDR(cons));
- ESTACK_PUSH(s, CAR(cons));
- }
- else {
- ASSERT(is_boxed(*ptr));
- ESTACK_PUSH(s, *ptr);
- }
- i--; ptr++;
- }
- goto pop_next;
- }
- break;
- case EXPORT_SUBTAG:
- {
- Export* ep = *((Export **) (export_val(term) + 1));
- /* Assumes Export entries never move */
- POINTER_HASH(ep, HCONST_14);
- goto pop_next;
- }
- case FUN_SUBTAG:
- {
- ErlFunThing* funp = (ErlFunThing *) fun_val(term);
- Uint num_free = funp->num_free;
- UINT32_HASH_2(num_free, funp->fe->module, HCONST_20);
- UINT32_HASH_2(funp->fe->old_index, funp->fe->old_uniq, HCONST_21);
- if (num_free == 0) {
- goto pop_next;
- } else {
- Eterm* bptr = funp->env + num_free - 1;
- while (num_free-- > 1) {
- term = *bptr--;
- ESTACK_PUSH(s, term);
- }
- term = *bptr;
- }
- }
- break;
- case REFC_BINARY_SUBTAG:
- case HEAP_BINARY_SUBTAG:
- case SUB_BINARY_SUBTAG:
- {
- byte* bptr;
- Uint sz = binary_size(term);
- Uint32 con = HCONST_13 + hash;
- Uint bitoffs;
- Uint bitsize;
- ERTS_GET_BINARY_BYTES(term, bptr, bitoffs, bitsize);
- if (sz == 0 && bitsize == 0) {
- hash = con;
- } else {
- if (bitoffs == 0) {
- hash = block_hash(bptr, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (bptr[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- } else {
- byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP,
- sz + (bitsize != 0));
- erts_copy_bits(bptr, bitoffs, 1, buf, 0, 1, sz*8+bitsize);
- hash = block_hash(buf, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (buf[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- }
- }
- goto pop_next;
- }
- break;
- case POS_BIG_SUBTAG:
- case NEG_BIG_SUBTAG:
- {
- Eterm* ptr = big_val(term);
- Uint i = 0;
- Uint n = BIG_SIZE(ptr);
- Uint32 con = BIG_SIGN(ptr) ? HCONST_10 : HCONST_11;
- #if D_EXP == 16
- do {
- Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- x += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #elif D_EXP == 32
- do {
- Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #elif D_EXP == 64
- do {
- Uint t;
- Uint32 x, y;
- ASSERT(i < n);
- t = BIG_DIGIT(ptr, i++);
- x = t & 0xffffffff;
- y = t >> 32;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
- #else
- #error "unsupported D_EXP size"
- #endif
- goto pop_next;
- }
- break;
- case REF_SUBTAG:
- UINT32_HASH(internal_ref_numbers(term)[0], HCONST_7);
- ASSERT(internal_ref_no_numbers(term) == 3);
- UINT32_HASH_2(internal_ref_numbers(term)[1],
- internal_ref_numbers(term)[2], HCONST_8);
- goto pop_next;
- case EXTERNAL_REF_SUBTAG:
- {
- ExternalThing* thing = external_thing_ptr(term);
- ASSERT(external_thing_ref_no_numbers(thing) == 3);
- /* See limitation #2 */
- #ifdef ARCH_64
- POINTER_HASH(thing->node, HCONST_7);
- UINT32_HASH(external_thing_ref_numbers(thing)[0], HCONST_7);
- #else
- UINT32_HASH_2(thing->node,
- external_thing_ref_numbers(thing)[0], HCONST_7);
- #endif
- UINT32_HASH_2(external_thing_ref_numbers(thing)[1],
- external_thing_ref_numbers(thing)[2], HCONST_8);
- goto pop_next;
- }
- case EXTERNAL_PID_SUBTAG: {
- ExternalThing* thing = external_thing_ptr(term);
- /* See limitation #2 */
- #ifdef ARCH_64
- POINTER_HASH(thing->node, HCONST_5);
- UINT32_HASH(thing->data.ui[0], HCONST_5);
- #else
- UINT32_HASH_2(thing->node, thing->data.ui[0], HCONST_5);
- #endif
- goto pop_next;
- }
- case EXTERNAL_PORT_SUBTAG: {
- ExternalThing* thing = external_thing_ptr(term);
- /* See limitation #2 */
- #ifdef ARCH_64
- POINTER_HASH(thing->node, HCONST_6);
- UINT32_HASH(thing->data.ui[0], HCONST_6);
- #else
- UINT32_HASH_2(thing->node, thing->data.ui[0], HCONST_6);
- #endif
- goto pop_next;
- }
- case FLOAT_SUBTAG:
- {
- FloatDef ff;
- GET_DOUBLE(term, ff);
- if (ff.fd == 0.0f) {
- /* ensure positive 0.0 */
- ff.fd = erts_get_positive_zero_float();
- }
- U…
Large files files are truncated, but you can click here to view the full file