181e3e7e5SSchrodinger ZHU Yifan //===-- Unittests for hash ------------------------------------------------===//
281e3e7e5SSchrodinger ZHU Yifan //
381e3e7e5SSchrodinger ZHU Yifan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481e3e7e5SSchrodinger ZHU Yifan // See https://llvm.org/LICENSE.txt for license information.
581e3e7e5SSchrodinger ZHU Yifan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681e3e7e5SSchrodinger ZHU Yifan //
781e3e7e5SSchrodinger ZHU Yifan //===----------------------------------------------------------------------===//
881e3e7e5SSchrodinger ZHU Yifan
981e3e7e5SSchrodinger ZHU Yifan #include "src/__support/CPP/new.h"
1081e3e7e5SSchrodinger ZHU Yifan #include "src/__support/hash.h"
1181e3e7e5SSchrodinger ZHU Yifan #include "src/stdlib/rand.h"
1281e3e7e5SSchrodinger ZHU Yifan #include "src/stdlib/srand.h"
1381e3e7e5SSchrodinger ZHU Yifan #include "src/string/memset.h"
1481e3e7e5SSchrodinger ZHU Yifan #include "test/UnitTest/Test.h"
1581e3e7e5SSchrodinger ZHU Yifan
1681e3e7e5SSchrodinger ZHU Yifan template <class T> struct AlignedMemory {
1781e3e7e5SSchrodinger ZHU Yifan T *data;
1881e3e7e5SSchrodinger ZHU Yifan size_t offset;
1981e3e7e5SSchrodinger ZHU Yifan std::align_val_t alignment;
AlignedMemoryAlignedMemory2081e3e7e5SSchrodinger ZHU Yifan AlignedMemory(size_t size, size_t alignment, size_t offset)
2181e3e7e5SSchrodinger ZHU Yifan : offset(offset), alignment{alignment} {
2281e3e7e5SSchrodinger ZHU Yifan size_t sz = size * sizeof(T);
2381e3e7e5SSchrodinger ZHU Yifan size_t aligned = sz + ((-sz) & (alignment - 1)) + alignment;
2481e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::AllocChecker ac;
2581e3e7e5SSchrodinger ZHU Yifan data = static_cast<T *>(operator new(aligned, this->alignment, ac));
2681e3e7e5SSchrodinger ZHU Yifan data += offset % alignment;
2781e3e7e5SSchrodinger ZHU Yifan }
~AlignedMemoryAlignedMemory2881e3e7e5SSchrodinger ZHU Yifan ~AlignedMemory() { operator delete(data - offset, alignment); }
2981e3e7e5SSchrodinger ZHU Yifan };
3081e3e7e5SSchrodinger ZHU Yifan
3181e3e7e5SSchrodinger ZHU Yifan size_t sizes[] = {0, 1, 23, 59, 1024, 5261};
32*e399a317SSchrodinger ZHU Yifan uint8_t values[] = {0, 1, 23, 59, 102, 255};
3381e3e7e5SSchrodinger ZHU Yifan
3481e3e7e5SSchrodinger ZHU Yifan // Hash value should not change with different alignments.
TEST(LlvmLibcHashTest,SanityCheck)3581e3e7e5SSchrodinger ZHU Yifan TEST(LlvmLibcHashTest, SanityCheck) {
3681e3e7e5SSchrodinger ZHU Yifan for (size_t sz : sizes) {
3781e3e7e5SSchrodinger ZHU Yifan for (uint8_t val : values) {
3881e3e7e5SSchrodinger ZHU Yifan uint64_t hash;
3981e3e7e5SSchrodinger ZHU Yifan {
4081e3e7e5SSchrodinger ZHU Yifan AlignedMemory<char> mem(sz, 64, 0);
4181e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::memset(mem.data, val, sz);
4281e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0x1234567890abcdef};
4381e3e7e5SSchrodinger ZHU Yifan state.update(mem.data, sz);
4481e3e7e5SSchrodinger ZHU Yifan hash = state.finish();
4581e3e7e5SSchrodinger ZHU Yifan }
4681e3e7e5SSchrodinger ZHU Yifan for (size_t offset = 1; offset < 64; ++offset) {
4781e3e7e5SSchrodinger ZHU Yifan AlignedMemory<char> mem(sz, 64, offset);
4881e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::memset(mem.data, val, sz);
4981e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0x1234567890abcdef};
5081e3e7e5SSchrodinger ZHU Yifan state.update(mem.data, sz);
5181e3e7e5SSchrodinger ZHU Yifan ASSERT_EQ(hash, state.finish());
5281e3e7e5SSchrodinger ZHU Yifan }
5381e3e7e5SSchrodinger ZHU Yifan }
5481e3e7e5SSchrodinger ZHU Yifan }
5581e3e7e5SSchrodinger ZHU Yifan }
5681e3e7e5SSchrodinger ZHU Yifan
popcnt(uint64_t x)5781e3e7e5SSchrodinger ZHU Yifan static inline size_t popcnt(uint64_t x) {
5881e3e7e5SSchrodinger ZHU Yifan size_t count = 0;
5981e3e7e5SSchrodinger ZHU Yifan while (x) {
6081e3e7e5SSchrodinger ZHU Yifan count += x & 1;
6181e3e7e5SSchrodinger ZHU Yifan x >>= 1;
6281e3e7e5SSchrodinger ZHU Yifan }
6381e3e7e5SSchrodinger ZHU Yifan return count;
6481e3e7e5SSchrodinger ZHU Yifan }
6581e3e7e5SSchrodinger ZHU Yifan
6681e3e7e5SSchrodinger ZHU Yifan // Mutate a single bit in a rather large input. The hash should change
6781e3e7e5SSchrodinger ZHU Yifan // significantly. At least one fifth of the bits should not match.
TEST(LlvmLibcHashTest,Avalanche)6881e3e7e5SSchrodinger ZHU Yifan TEST(LlvmLibcHashTest, Avalanche) {
6981e3e7e5SSchrodinger ZHU Yifan for (size_t sz : sizes) {
7081e3e7e5SSchrodinger ZHU Yifan for (uint8_t val : values) {
7181e3e7e5SSchrodinger ZHU Yifan uint64_t hash;
7281e3e7e5SSchrodinger ZHU Yifan AlignedMemory<char> mem(sz, 64, 0);
7381e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::memset(mem.data, val, sz);
7481e3e7e5SSchrodinger ZHU Yifan {
7581e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0xabcdef1234567890};
7681e3e7e5SSchrodinger ZHU Yifan state.update(mem.data, sz);
7781e3e7e5SSchrodinger ZHU Yifan hash = state.finish();
7881e3e7e5SSchrodinger ZHU Yifan }
7981e3e7e5SSchrodinger ZHU Yifan for (size_t i = 0; i < sz; ++i) {
8081e3e7e5SSchrodinger ZHU Yifan for (size_t j = 0; j < 8; ++j) {
8181e3e7e5SSchrodinger ZHU Yifan uint8_t mask = 1 << j;
8281e3e7e5SSchrodinger ZHU Yifan mem.data[i] ^= mask;
8381e3e7e5SSchrodinger ZHU Yifan {
8481e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0xabcdef1234567890};
8581e3e7e5SSchrodinger ZHU Yifan state.update(mem.data, sz);
8681e3e7e5SSchrodinger ZHU Yifan uint64_t new_hash = state.finish();
8781e3e7e5SSchrodinger ZHU Yifan ASSERT_GE(popcnt(hash ^ new_hash), size_t{13});
8881e3e7e5SSchrodinger ZHU Yifan }
8981e3e7e5SSchrodinger ZHU Yifan mem.data[i] ^= mask;
9081e3e7e5SSchrodinger ZHU Yifan }
9181e3e7e5SSchrodinger ZHU Yifan }
9281e3e7e5SSchrodinger ZHU Yifan }
9381e3e7e5SSchrodinger ZHU Yifan }
9481e3e7e5SSchrodinger ZHU Yifan }
9581e3e7e5SSchrodinger ZHU Yifan
9681e3e7e5SSchrodinger ZHU Yifan // Hash a random sequence of input. The LSB should be uniform enough such that
9781e3e7e5SSchrodinger ZHU Yifan // values spread across the entire range.
TEST(LlvmLibcHashTest,UniformLSB)9881e3e7e5SSchrodinger ZHU Yifan TEST(LlvmLibcHashTest, UniformLSB) {
9981e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::srand(0xffffffff);
10081e3e7e5SSchrodinger ZHU Yifan for (size_t sz : sizes) {
10181e3e7e5SSchrodinger ZHU Yifan AlignedMemory<size_t> counters(sz, sizeof(size_t), 0);
10281e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::memset(counters.data, 0, sz * sizeof(size_t));
10381e3e7e5SSchrodinger ZHU Yifan for (size_t i = 0; i < 200 * sz; ++i) {
10481e3e7e5SSchrodinger ZHU Yifan int randomness[8] = {LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(),
10581e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(),
10681e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(),
10781e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand()};
10881e3e7e5SSchrodinger ZHU Yifan {
10981e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0x1a2b3c4d5e6f7a8b};
11081e3e7e5SSchrodinger ZHU Yifan state.update(randomness, sizeof(randomness));
11181e3e7e5SSchrodinger ZHU Yifan uint64_t hash = state.finish();
11281e3e7e5SSchrodinger ZHU Yifan counters.data[hash % sz]++;
11381e3e7e5SSchrodinger ZHU Yifan }
11481e3e7e5SSchrodinger ZHU Yifan }
11581e3e7e5SSchrodinger ZHU Yifan for (size_t i = 0; i < sz; ++i) {
11681e3e7e5SSchrodinger ZHU Yifan ASSERT_GE(counters.data[i], size_t{140});
11781e3e7e5SSchrodinger ZHU Yifan ASSERT_LE(counters.data[i], size_t{260});
11881e3e7e5SSchrodinger ZHU Yifan }
11981e3e7e5SSchrodinger ZHU Yifan }
12081e3e7e5SSchrodinger ZHU Yifan }
12181e3e7e5SSchrodinger ZHU Yifan
12281e3e7e5SSchrodinger ZHU Yifan // Hash a low entropy sequence. The MSB should be uniform enough such that
12381e3e7e5SSchrodinger ZHU Yifan // there is no significant bias even if the value range is small.
12481e3e7e5SSchrodinger ZHU Yifan // Top 7 bits is examined because it will be used as a secondary key in
12581e3e7e5SSchrodinger ZHU Yifan // the hash table.
TEST(LlvmLibcHashTest,UniformMSB)12681e3e7e5SSchrodinger ZHU Yifan TEST(LlvmLibcHashTest, UniformMSB) {
12781e3e7e5SSchrodinger ZHU Yifan size_t sz = 1 << 7;
12881e3e7e5SSchrodinger ZHU Yifan AlignedMemory<size_t> counters(sz, sizeof(size_t), 0);
12981e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::memset(counters.data, 0, sz * sizeof(size_t));
13081e3e7e5SSchrodinger ZHU Yifan for (size_t i = 0; i < 200 * sz; ++i) {
13181e3e7e5SSchrodinger ZHU Yifan LIBC_NAMESPACE::internal::HashState state{0xa1b2c3d4e5f6a7b8};
13281e3e7e5SSchrodinger ZHU Yifan state.update(&i, sizeof(i));
13381e3e7e5SSchrodinger ZHU Yifan uint64_t hash = state.finish();
13481e3e7e5SSchrodinger ZHU Yifan counters.data[hash >> 57]++;
13581e3e7e5SSchrodinger ZHU Yifan }
13681e3e7e5SSchrodinger ZHU Yifan for (size_t i = 0; i < sz; ++i) {
13781e3e7e5SSchrodinger ZHU Yifan ASSERT_GE(counters.data[i], size_t{140});
13881e3e7e5SSchrodinger ZHU Yifan ASSERT_LE(counters.data[i], size_t{260});
13981e3e7e5SSchrodinger ZHU Yifan }
14081e3e7e5SSchrodinger ZHU Yifan }
141