# HG changeset patch # User vkempik # Date 1600871034 -10800 # Node ID 3a09ec20ba0d1795de650e5962f8435fccfb9b96 # Parent be18a085ddb000507727bfae376b654f265bb633 8240124: Better VM Interning Reviewed-by: bae, yan diff -r be18a085ddb0 -r 3a09ec20ba0d src/share/vm/classfile/altHashing.cpp --- a/src/share/vm/classfile/altHashing.cpp Tue Sep 22 13:09:39 2020 +0300 +++ b/src/share/vm/classfile/altHashing.cpp Wed Sep 23 17:23:54 2020 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,29 @@ * */ +/* + * halfsiphash code adapted from reference implementation + * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) + * which is distributed with the following copyright: + * + * SipHash reference C implementation + * + * Copyright (c) 2016 Jean-Philippe Aumasson + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along + * with this software. If not, see + * . + */ + #include "precompiled.hpp" #include "classfile/altHashing.hpp" -#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "oops/markOop.hpp" -#include "runtime/thread.hpp" +#include "runtime/os.hpp" // Get the hash code of the classes mirror if it exists, otherwise just // return a random number, which is one of the possible hash code used for @@ -39,266 +56,284 @@ } // Seed value used for each alternative hash calculated. -jint AltHashing::compute_seed() { - jlong nanos = os::javaTimeNanos(); - jlong now = os::javaTimeMillis(); - jint SEED_MATERIAL[8] = { - (jint) object_hash(SystemDictionary::String_klass()), - (jint) object_hash(SystemDictionary::System_klass()), - (jint) os::random(), // current thread isn't a java thread - (jint) (((julong)nanos) >> 32), - (jint) nanos, - (jint) (((julong)now) >> 32), - (jint) now, - (jint) (os::javaTimeNanos() >> 2) +uint64_t AltHashing::compute_seed() { + uint64_t nanos = os::javaTimeNanos(); + uint64_t now = os::javaTimeMillis(); + uint32_t SEED_MATERIAL[8] = { + (uint32_t) object_hash(SystemDictionary::String_klass()), + (uint32_t) object_hash(SystemDictionary::System_klass()), + (uint32_t) os::random(), // current thread isn't a java thread + (uint32_t) (((uint64_t)nanos) >> 32), + (uint32_t) nanos, + (uint32_t) (((uint64_t)now) >> 32), + (uint32_t) now, + (uint32_t) (os::javaTimeNanos() >> 2) }; - return murmur3_32(SEED_MATERIAL, 8); + return halfsiphash_64(SEED_MATERIAL, 8); +} + +// utility function copied from java/lang/Integer +static uint32_t Integer_rotateLeft(uint32_t i, int distance) { + return (i << distance) | (i >> (32 - distance)); } +static void halfsiphash_rounds(uint32_t v[4], int rounds) { + while (rounds-- > 0) { + v[0] += v[1]; + v[1] = Integer_rotateLeft(v[1], 5); + v[1] ^= v[0]; + v[0] = Integer_rotateLeft(v[0], 16); + v[2] += v[3]; + v[3] = Integer_rotateLeft(v[3], 8); + v[3] ^= v[2]; + v[0] += v[3]; + v[3] = Integer_rotateLeft(v[3], 7); + v[3] ^= v[0]; + v[2] += v[1]; + v[1] = Integer_rotateLeft(v[1], 13); + v[1] ^= v[2]; + v[2] = Integer_rotateLeft(v[2], 16); + } + } -// Murmur3 hashing for Symbol -jint AltHashing::murmur3_32(jint seed, const jbyte* data, int len) { - jint h1 = seed; +static void halfsiphash_adddata(uint32_t v[4], uint32_t newdata, int rounds) { + v[3] ^= newdata; + halfsiphash_rounds(v, rounds); + v[0] ^= newdata; +} + +static void halfsiphash_init32(uint32_t v[4], uint64_t seed) { + v[0] = seed & 0xffffffff; + v[1] = seed >> 32; + v[2] = 0x6c796765 ^ v[0]; + v[3] = 0x74656462 ^ v[1]; +} + +static void halfsiphash_init64(uint32_t v[4], uint64_t seed) { + halfsiphash_init32(v, seed); + v[1] ^= 0xee; +} + +static uint64_t halfsiphash_finish64(uint32_t v[4], int rounds) { + uint64_t rv; + v[2] ^= 0xee; + halfsiphash_rounds(v, rounds); + rv = v[1] ^ v[3]; + v[1] ^= 0xdd; + halfsiphash_rounds(v, rounds); + rv |= (uint64_t)(v[1] ^ v[3]) << 32; + return rv; +} + +// HalfSipHash-2-4 (64-bit output) for Symbols +uint64_t AltHashing::halfsiphash_64(uint64_t seed, const int8_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; + int off = 0; int count = len; - int offset = 0; + halfsiphash_init64(v, seed); // body while (count >= 4) { - jint k1 = (data[offset] & 0x0FF) - | (data[offset + 1] & 0x0FF) << 8 - | (data[offset + 2] & 0x0FF) << 16 - | data[offset + 3] << 24; + // Avoid sign extension with 0x0ff + newdata = (data[off] & 0x0FF) + | (data[off + 1] & 0x0FF) << 8 + | (data[off + 2] & 0x0FF) << 16 + | data[off + 3] << 24; count -= 4; - offset += 4; + off += 4; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail + newdata = ((uint32_t)len) << 24; // (Byte.SIZE / Byte.SIZE); if (count > 0) { - jint k1 = 0; - switch (count) { case 3: - k1 ^= (data[offset + 2] & 0xff) << 16; + newdata |= (data[off + 2] & 0x0ff) << 16; // fall through case 2: - k1 ^= (data[offset + 1] & 0xff) << 8; + newdata |= (data[off + 1] & 0x0ff) << 8; // fall through case 1: - k1 ^= (data[offset] & 0xff); + newdata |= (data[off] & 0x0ff); // fall through - default: - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; } } - // finalization - h1 ^= len; + halfsiphash_adddata(v, newdata, 2); - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; - - return h1; + // finalization + return halfsiphash_finish64(v, 4); } -// Murmur3 hashing for Strings -jint AltHashing::murmur3_32(jint seed, const jchar* data, int len) { - jint h1 = seed; - +// HalfSipHash-2-4 (64-bit output) for Strings +uint64_t AltHashing::halfsiphash_64(uint64_t seed, const uint16_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; int off = 0; int count = len; + halfsiphash_init64(v, seed); + // body while (count >= 2) { - jchar d1 = data[off++] & 0xFFFF; - jchar d2 = data[off++]; - jint k1 = (d1 | d2 << 16); + uint16_t d1 = data[off++] & 0x0FFFF; + uint16_t d2 = data[off++]; + newdata = (d1 | d2 << 16); count -= 2; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail - + newdata = ((uint32_t)len * 2) << 24; // (Character.SIZE / Byte.SIZE); if (count > 0) { - int k1 = data[off]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; + newdata |= (uint32_t)data[off]; } + halfsiphash_adddata(v, newdata, 2); // finalization - h1 ^= len * 2; // (Character.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; - - return h1; + return halfsiphash_finish64(v, 4); } -// Hash used for the seed. -jint AltHashing::murmur3_32(jint seed, const int* data, int len) { - jint h1 = seed; +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(uint64_t seed, const uint32_t* data, int len) { + uint32_t v[4]; int off = 0; int end = len; + halfsiphash_init64(v, seed); + // body while (off < end) { - jint k1 = data[off++]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, (uint32_t)data[off++], 2); } // tail (always empty, as body is always 32-bit chunks) // finalization - - h1 ^= len * 4; // (Integer.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((juint)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((juint)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((juint)h1) >> 16; - - return h1; + halfsiphash_adddata(v, ((uint32_t)len * 4) << 24, 2); // (Integer.SIZE / Byte.SIZE); + return halfsiphash_finish64(v, 4); } -jint AltHashing::murmur3_32(const int* data, int len) { - return murmur3_32(0, data, len); +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(const uint32_t* data, int len) { + return halfsiphash_64((uint64_t)0, data, len); } #ifndef PRODUCT -// Overloaded versions for internal test. -jint AltHashing::murmur3_32(const jbyte* data, int len) { - return murmur3_32(0, data, len); -} + void AltHashing::testHalfsiphash_64_ByteArray() { + // printf("testHalfsiphash_64_CharArray\n"); + const int factor = 4; -jint AltHashing::murmur3_32(const jchar* data, int len) { - return murmur3_32(0, data, len); -} + int8_t vector[256]; + int8_t hashes[factor * 256]; + + for (int i = 0; i < 256; i++) { + vector[i] = (int8_t) i; + } -// Internal test for alternate hashing. Translated from JDK version -// test/sun/misc/Hashing.java -static const jbyte ONE_BYTE[] = { (jbyte) 0x80}; -static const jbyte TWO_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81}; -static const jchar ONE_CHAR[] = { (jchar) 0x8180}; -static const jbyte THREE_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82}; -static const jbyte FOUR_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83}; -static const jchar TWO_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382}; -static const jint ONE_INT[] = { 0x83828180}; -static const jbyte SIX_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85}; -static const jchar THREE_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382, (jchar) 0x8584}; -static const jbyte EIGHT_BYTE[] = { - (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, - (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85, - (jbyte) 0x86, (jbyte) 0x87}; -static const jchar FOUR_CHAR[] = { - (jchar) 0x8180, (jchar) 0x8382, - (jchar) 0x8584, (jchar) 0x8786}; + // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} + for (int i = 0; i < 256; i++) { + uint64_t hash = AltHashing::halfsiphash_64(256 - i, vector, i); + hashes[i * factor] = (int8_t) hash; + hashes[i * factor + 1] = (int8_t)(hash >> 8); + hashes[i * factor + 2] = (int8_t)(hash >> 16); + hashes[i * factor + 3] = (int8_t)(hash >> 24); + } + + // hash to get const result. + uint64_t final_hash = AltHashing::halfsiphash_64(0, hashes, factor*256); -static const jint TWO_INT[] = { 0x83828180, 0x87868584}; + // Value found using reference implementation for the hashes array. + // halfsiphash((const uint8_t*)hashes, factor*256, (const uint8_t *)&k, + // (uint8_t*)&reference, 8); -static const juint MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3; + static const uint64_t HALFSIPHASH_64_BYTE_CHECK_VALUE = 0x15a7911e30917ee8; -void AltHashing::testMurmur3_32_ByteArray() { - // printf("testMurmur3_32_ByteArray\n"); - - jbyte* vector = new jbyte[256]; - jbyte* hashes = new jbyte[4 * 256]; - - for (int i = 0; i < 256; i++) { - vector[i] = (jbyte) i; + assert (HALFSIPHASH_64_BYTE_CHECK_VALUE == final_hash, + err_msg( + "Calculated hash result not as expected. Expected " UINT64_FORMAT " got " UINT64_FORMAT, + HALFSIPHASH_64_BYTE_CHECK_VALUE, + final_hash)); } - // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} - for (int i = 0; i < 256; i++) { - jint hash = murmur3_32(256 - i, vector, i); - hashes[i * 4] = (jbyte) hash; - hashes[i * 4 + 1] = (jbyte) (((juint)hash) >> 8); - hashes[i * 4 + 2] = (jbyte) (((juint)hash) >> 16); - hashes[i * 4 + 3] = (jbyte) (((juint)hash) >> 24); + void AltHashing::testHalfsiphash_64_CharArray() { + // printf("testHalfsiphash_64_CharArray\n"); + const int factor = 2; + + uint16_t vector[256]; + uint16_t hashes[factor * 256]; + + for (int i = 0; i < 256; i++) { + vector[i] = (uint16_t) i; + } + + // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} + for (int i = 0; i < 256; i++) { + uint64_t hash = AltHashing::halfsiphash_64(256 - i, vector, i); + hashes[i * factor] = (uint16_t) hash; + hashes[i * factor + 1] = (uint16_t)(hash >> 16); + } + + // hash to get const result. + uint64_t final_hash = AltHashing::halfsiphash_64(0, hashes, factor*256); + + // Value found using reference implementation for the hashes array. + // halfsiphash((const uint8_t*)hashes, 2*factor*256, (const uint8_t *)&k, + // (uint8_t*)&reference, 8); + static const uint64_t HALFSIPHASH_64_CHAR_CHECK_VALUE = 0xf392d8a6a9e24103; + + assert(HALFSIPHASH_64_CHAR_CHECK_VALUE == final_hash, + err_msg( + "Calculated hash result not as expected. Expected " UINT64_FORMAT " got " UINT64_FORMAT, + HALFSIPHASH_64_CHAR_CHECK_VALUE, + final_hash)); } - // hash to get const result. - juint final_hash = murmur3_32(hashes, 4*256); - - assert (MURMUR3_32_X86_CHECK_VALUE == final_hash, - err_msg( - "Calculated hash result not as expected. Expected %08X got %08X\n", - MURMUR3_32_X86_CHECK_VALUE, - final_hash)); -} + // Test against sample hashes published with the reference implementation: + // https://github.com/veorq/SipHash + void AltHashing::testHalfsiphash_64_FromReference() { + // printf("testHalfsiphash_64_FromReference\n"); -void AltHashing::testEquivalentHashes() { - jint jbytes, jchars, ints; - - // printf("testEquivalentHashes\n"); - - jbytes = murmur3_32(TWO_BYTE, 2); - jchars = murmur3_32(ONE_CHAR, 1); - assert (jbytes == jchars, - err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars)); + const uint64_t seed = 0x0706050403020100; + const uint64_t results[16] = { + 0xc83cb8b9591f8d21, 0xa12ee55b178ae7d5, + 0x8c85e4bc20e8feed, 0x99c7f5ae9f1fc77b, + 0xb5f37b5fd2aa3673, 0xdba7ee6f0a2bf51b, + 0xf1a63fae45107470, 0xb516001efb5f922d, + 0x6c6211d8469d7028, 0xdc7642ec407ad686, + 0x4caec8671cc8385b, 0x5ab1dc27adf3301e, + 0x3e3ea94bc0a8eaa9, 0xe150f598795a4402, + 0x1d5ff142f992a4a1, 0x60e426bf902876d6 + }; + uint32_t vector[16]; - jbytes = murmur3_32(FOUR_BYTE, 4); - jchars = murmur3_32(TWO_CHAR, 2); - ints = murmur3_32(ONE_INT, 1); - assert ((jbytes == jchars) && (jbytes == ints), - err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints)); - - jbytes = murmur3_32(SIX_BYTE, 6); - jchars = murmur3_32(THREE_CHAR, 3); - assert (jbytes == jchars, - err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars)); + for (int i = 0; i < 16; i++) + vector[i] = 0x03020100 + i * 0x04040404; - jbytes = murmur3_32(EIGHT_BYTE, 8); - jchars = murmur3_32(FOUR_CHAR, 4); - ints = murmur3_32(TWO_INT, 2); - assert ((jbytes == jchars) && (jbytes == ints), - err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints)); -} + for (int i = 0; i < 16; i++) { + uint64_t hash = AltHashing::halfsiphash_64(seed, vector, i); + assert(results[i] == hash, + err_msg( + "Calculated hash result not as expected. Round %d: " + "Expected " UINT64_FORMAT_X " got " UINT64_FORMAT_X "\n", + i, + results[i], + hash)); + } + } -// Returns true if the alternate hashcode is correct void AltHashing::test_alt_hash() { - testMurmur3_32_ByteArray(); - testEquivalentHashes(); + testHalfsiphash_64_ByteArray(); + testHalfsiphash_64_CharArray(); + testHalfsiphash_64_FromReference(); } #endif // PRODUCT diff -r be18a085ddb0 -r 3a09ec20ba0d src/share/vm/classfile/altHashing.hpp --- a/src/share/vm/classfile/altHashing.hpp Tue Sep 22 13:09:39 2020 +0300 +++ b/src/share/vm/classfile/altHashing.hpp Wed Sep 23 17:23:54 2020 +0300 @@ -26,37 +26,30 @@ #define SHARE_VM_CLASSFILE_ALTHASHING_HPP #include "prims/jni.h" -#include "classfile/symbolTable.hpp" +#include "memory/allocation.hpp" /** - * Hashing utilities. - * - * Implementation of Murmur3 hashing. - * This code was translated from src/share/classes/sun/misc/Hashing.java - * code in the JDK. + * Implementation of alternate more secure hashing. */ class AltHashing : AllStatic { - // utility function copied from java/lang/Integer - static jint Integer_rotateLeft(jint i, int distance) { - return (i << distance) | (((juint)i) >> (32-distance)); - } - static jint murmur3_32(const int* data, int len); - static jint murmur3_32(jint seed, const int* data, int len); + // For the seed computation + static uint64_t halfsiphash_64(const uint32_t* data, int len); + static uint64_t halfsiphash_64(uint64_t seed, const uint32_t* data, int len); + #ifndef PRODUCT + // Hashing functions used for internal testing + static void testHalfsiphash_64_ByteArray(); + static void testHalfsiphash_64_CharArray(); + static void testHalfsiphash_64_FromReference(); + #endif // PRODUCT + public: + static uint64_t compute_seed(); -#ifndef PRODUCT - // Hashing functions used for internal testing - static jint murmur3_32(const jbyte* data, int len); - static jint murmur3_32(const jchar* data, int len); - static void testMurmur3_32_ByteArray(); - static void testEquivalentHashes(); -#endif // PRODUCT - - public: - static jint compute_seed(); - static jint murmur3_32(jint seed, const jbyte* data, int len); - static jint murmur3_32(jint seed, const jchar* data, int len); + // For Symbols + static uint64_t halfsiphash_64(uint64_t seed, const int8_t* data, int len); + // For Strings + static uint64_t halfsiphash_64(uint64_t seed, const uint16_t* data, int len); NOT_PRODUCT(static void test_alt_hash();) }; #endif // SHARE_VM_CLASSFILE_ALTHASHING_HPP diff -r be18a085ddb0 -r 3a09ec20ba0d src/share/vm/classfile/symbolTable.cpp --- a/src/share/vm/classfile/symbolTable.cpp Tue Sep 22 13:09:39 2020 +0300 +++ b/src/share/vm/classfile/symbolTable.cpp Wed Sep 23 17:23:54 2020 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -216,7 +216,7 @@ // Pick hashing algorithm. unsigned int SymbolTable::hash_symbol(const char* s, int len) { return use_alternate_hashcode() ? - AltHashing::murmur3_32(seed(), (const jbyte*)s, len) : + AltHashing::halfsiphash_64(seed(), (const int8_t*)s, len) : java_lang_String::to_hash(s, len); } @@ -656,7 +656,7 @@ // Pick hashing algorithm unsigned int StringTable::hash_string(const jchar* s, int len) { - return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) : + return use_alternate_hashcode() ? AltHashing::halfsiphash_64(seed(), s, len) : java_lang_String::to_hash(s, len); } diff -r be18a085ddb0 -r 3a09ec20ba0d src/share/vm/utilities/hashtable.cpp --- a/src/share/vm/utilities/hashtable.cpp Tue Sep 22 13:09:39 2020 +0300 +++ b/src/share/vm/utilities/hashtable.cpp Wed Sep 23 17:23:54 2020 +0300 @@ -97,7 +97,7 @@ template unsigned int Hashtable::new_hash(Symbol* sym) { ResourceMark rm; // Use alternate hashing algorithm on this symbol. - return AltHashing::murmur3_32(seed(), (const jbyte*)sym->as_C_string(), sym->utf8_length()); + return AltHashing::halfsiphash_64(seed(), (const int8_t*)sym->as_C_string(), sym->utf8_length()); } template unsigned int Hashtable::new_hash(oop string) { @@ -105,7 +105,7 @@ int length; jchar* chars = java_lang_String::as_unicode_string(string, length); // Use alternate hashing algorithm on the string - return AltHashing::murmur3_32(seed(), chars, length); + return AltHashing::halfsiphash_64(seed(), chars, length); } // Create a new table and using alternate hash code, populate the new table