/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2009 by Trifork * * 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. **/ package erjang; /** * Utility functions and constants for erlang hashing. * * This class should replicate the hashing logic in beam/util.c * */ public class EHash { static final int BINARY_DEF = 0x0; static final int LIST_DEF = 0x1; static final int NIL_DEF = 0x2; static final int TUPLE_DEF = 0x3; static final int PID_DEF = 0x4; static final int EXTERNAL_PID_DEF = 0x5; static final int PORT_DEF = 0x6; static final int EXTERNAL_PORT_DEF = 0x7; static final int EXPORT_DEF = 0x8; static final int FUN_DEF = 0x9; static final int REF_DEF = 0xa; static final int EXTERNAL_REF_DEF = 0xb; static final int ATOM_DEF = 0xc; static final int FLOAT_DEF = 0xd; static final int BIG_DEF = 0xe; static final int SMALL_DEF = 0xf; /* some prime numbers just above 2 ^ 28 */ static final int FUNNY_NUMBER1 = 268440163; static final int FUNNY_NUMBER2 = 268439161; static final int FUNNY_NUMBER3 = 268435459; static final int FUNNY_NUMBER4 = 268436141; static final int FUNNY_NUMBER5 = 268438633; static final int FUNNY_NUMBER6 = 268437017; static final int FUNNY_NUMBER7 = 268438039; static final int FUNNY_NUMBER8 = 268437511; static final int FUNNY_NUMBER9 = 268439627; static final int FUNNY_NUMBER10 = 268440479; static final int FUNNY_NUMBER11 = 268440577; static final int FUNNY_NUMBER12 = 268440581; static final int HCONST = 0x9e3779b9; /** from util.c:934 */ int block_hash(byte[] k, int length, int initval) { int a, b, c; int len; /* Set up the internal state */ len = length; a = b = HCONST; c = initval; /* the previous hash value */ int p = 0; while (len >= 12) { a += (uint(k[p + 0]) + uint(k[p + 1]) << 8 + uint(k[p + 2]) << 16 + uint(k[p + 3]) << 24); b += (uint(k[p + 4]) + uint(k[p + 5]) << 8 + uint(k[p + 6]) << 16 + uint(k[p + 7]) << 24); c += (uint(k[p + 8]) + uint(k[p + 9]) << 8 + uint(k[p + 10]) << 16 + uint(k[p + 11]) << 24); // inlined MIX because we don't have macros 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); p += 12; len -= 12; } c += length; switch (len) /* all the case statements fall through */ { case 11: c += uint(k[p + 10]) << 24; case 10: c += uint(k[p + 9]) << 16; case 9: c += uint(k[p + 8]) << 8; /* the first byte of c is reserved for the length */ case 8: b += uint(k[p + 7]) << 24; case 7: b += uint(k[p + 6]) << 16; case 6: b += uint(k[p + 5]) << 8; case 5: b += uint(k[4]); case 4: a += uint(k[p + 3]) << 24; case 3: a += uint(k[p + 2]) << 16; case 2: a += uint(k[p + 1]) << 8; case 1: a += uint(k[p + 0]); /* case 0: nothing left to add */ } c = mix(a, b, c); return c; } static final int uint(byte b) { return ((int) b) & 0xff; } static final int mix(int a, int b, int c) { 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); return c; } /* (HCONST * {2, ..., 14}) mod 2^32 */ static final int HCONST_2 = 0x3c6ef372; static final int HCONST_3 = 0xdaa66d2b; static final int HCONST_4 = 0x78dde6e4; static final int HCONST_5 = 0x1715609d; static final int HCONST_6 = 0xb54cda56; static final int HCONST_7 = 0x5384540f; static final int HCONST_8 = 0xf1bbcdc8; static final int HCONST_9 = 0x8ff34781; static final int HCONST_10 = 0x2e2ac13a; static final int HCONST_11 = 0xcc623af3; static final int HCONST_12 = 0x6a99b4ac; static final int HCONST_13 = 0x08d12e65; static final int HCONST_14 = 0xa708a81e; static final int HCONST_15 = 0x454021d7; static final int HCONST_NIL = (int) 3468870702L; static final int UINT32_HASH_2(int expr1, int expr2, int aconst, int hash) { int a, b; a = aconst + expr1; b = aconst + expr2; return mix(a, b, hash); } static final int UINT32_HASH(int expr, int aconst, int hash) { return UINT32_HASH_2(expr, 0, aconst, hash); } static final int SINT32_HASH(int expr, int aconst, int hash) { if (expr < 0) { return UINT32_HASH(-expr, aconst, hash); } else { return UINT32_HASH(expr, aconst, hash); } } public static int hash2(EAtom val, int hash) { if (hash == 0) { return val.hash; } else { return UINT32_HASH(val.hash, HCONST_3, hash); } } public static int hash2(ESmall val, int hash) { return SINT32_HASH(val.value, HCONST, hash); } public static int hash2(EString val, int hash) { int c = 0; int sh = 0; for (int p = 0; p < val.length(); p++) { sh = (sh << 8) + val.charAt(p); if (c == 3) { hash = UINT32_HASH(sh, HCONST_4, hash); c = sh = 0; } else { c++; } } if (c > 0) { hash = UINT32_HASH(sh, HCONST_4, hash); } return hash; } public static int hash2(ERef ref, int hash) { /* Only 15 bits are hashed. */ return UINT32_HASH(ref.internal_ref_numbers()[0], HCONST_7, hash); } public static int hash2(EInternalPort port, int hash) { /* Only 15 bits are hashed. */ return UINT32_HASH(port.internal_port_number(), HCONST_6, hash); } public static int hash2(EInternalPID pid, int hash) { /* Only 15 bits are hashed. */ return UINT32_HASH(pid.internal_pid_number(), HCONST_5, hash); } public static int hash2(ENil nil, int hash) { if (hash == 0) { return HCONST_NIL; } else { return UINT32_HASH(NIL_DEF, HCONST_2, hash); } } }