table: NameTree optimization
This commit adds platform-specific hash function selection and hash
table shrink function.
Change-Id: Iaaa0e5b86842d4e0582d3523f0a6b6836e152239
Refs: #1305
diff --git a/daemon/core/city-hash.cpp b/daemon/core/city-hash.cpp
new file mode 100644
index 0000000..e0bebb4
--- /dev/null
+++ b/daemon/core/city-hash.cpp
@@ -0,0 +1,628 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// This file provides CityHash64() and related functions.
+//
+// It's probably possible to create even faster hash functions by
+// writing a program that systematically explores some of the space of
+// possible hash functions, by using SIMD instructions, or by
+// compromising on hash quality.
+
+//#include "config.h"
+#include "city-hash.hpp"
+#include <algorithm>
+#include <string.h> // for memcpy and memset
+
+using namespace std;
+
+
+static uint64 UNALIGNED_LOAD64(const char *p) {
+ uint64 result;
+ memcpy(&result, p, sizeof(result));
+ return result;
+}
+
+static uint32 UNALIGNED_LOAD32(const char *p) {
+ uint32 result;
+ memcpy(&result, p, sizeof(result));
+ return result;
+}
+
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__NetBSD__)
+
+#include <sys/types.h>
+#include <machine/bswap.h>
+#if defined(__BSWAP_RENAME) && !defined(__bswap_32)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+
+#else
+
+#include <byteswap.h>
+
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define uint32_in_expected_order(x) (bswap_32(x))
+#define uint64_in_expected_order(x) (bswap_64(x))
+#else
+#define uint32_in_expected_order(x) (x)
+#define uint64_in_expected_order(x) (x)
+#endif
+
+#if !defined(LIKELY)
+#if HAVE_BUILTIN_EXPECT
+#define LIKELY(x) (__builtin_expect(!!(x), 1))
+#else
+#define LIKELY(x) (x)
+#endif
+#endif
+
+static uint64 Fetch64(const char *p) {
+ return uint64_in_expected_order(UNALIGNED_LOAD64(p));
+}
+
+static uint32 Fetch32(const char *p) {
+ return uint32_in_expected_order(UNALIGNED_LOAD32(p));
+}
+
+// Some primes between 2^63 and 2^64 for various uses.
+static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
+static const uint64 k1 = 0xb492b66fbe98f273ULL;
+static const uint64 k2 = 0x9ae16a3b2f90404fULL;
+
+// Magic numbers for 32-bit hashing. Copied from Murmur3.
+static const uint32_t c1 = 0xcc9e2d51;
+static const uint32_t c2 = 0x1b873593;
+
+// A 32-bit to 32-bit integer hash copied from Murmur3.
+static uint32 fmix(uint32 h)
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+}
+
+static uint32 Rotate32(uint32 val, int shift) {
+ // Avoid shifting by 32: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
+}
+
+#undef PERMUTE3
+#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0)
+
+static uint32 Mur(uint32 a, uint32 h) {
+ // Helper from Murmur3 for combining two 32-bit values.
+ a *= c1;
+ a = Rotate32(a, 17);
+ a *= c2;
+ h ^= a;
+ h = Rotate32(h, 19);
+ return h * 5 + 0xe6546b64;
+}
+
+static uint32 Hash32Len13to24(const char *s, size_t len) {
+ uint32 a = Fetch32(s - 4 + (len >> 1));
+ uint32 b = Fetch32(s + 4);
+ uint32 c = Fetch32(s + len - 8);
+ uint32 d = Fetch32(s + (len >> 1));
+ uint32 e = Fetch32(s);
+ uint32 f = Fetch32(s + len - 4);
+ uint32 h = len;
+
+ return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
+}
+
+static uint32 Hash32Len0to4(const char *s, size_t len) {
+ uint32 b = 0;
+ uint32 c = 9;
+ for (size_t i = 0; i < len; i++) {
+ signed char v = s[i];
+ b = b * c1 + v;
+ c ^= b;
+ }
+ return fmix(Mur(b, Mur(len, c)));
+}
+
+static uint32 Hash32Len5to12(const char *s, size_t len) {
+ uint32 a = len, b = len * 5, c = 9, d = b;
+ a += Fetch32(s);
+ b += Fetch32(s + len - 4);
+ c += Fetch32(s + ((len >> 1) & 4));
+ return fmix(Mur(c, Mur(b, Mur(a, d))));
+}
+
+uint32 CityHash32(const char *s, size_t len) {
+ if (len <= 24) {
+ return len <= 12 ?
+ (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :
+ Hash32Len13to24(s, len);
+ }
+
+ // len > 24
+ uint32 h = len, g = c1 * len, f = g;
+ uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
+ uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
+ uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
+ uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
+ uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
+ h ^= a0;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ h ^= a2;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a1;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ g ^= a3;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ f += a4;
+ f = Rotate32(f, 19);
+ f = f * 5 + 0xe6546b64;
+ size_t iters = (len - 1) / 20;
+ do {
+ uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2;
+ uint32 a1 = Fetch32(s + 4);
+ uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
+ uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
+ uint32 a4 = Fetch32(s + 16);
+ h ^= a0;
+ h = Rotate32(h, 18);
+ h = h * 5 + 0xe6546b64;
+ f += a1;
+ f = Rotate32(f, 19);
+ f = f * c1;
+ g += a2;
+ g = Rotate32(g, 18);
+ g = g * 5 + 0xe6546b64;
+ h ^= a3 + a1;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a4;
+ g = bswap_32(g) * 5;
+ h += a4 * 5;
+ h = bswap_32(h);
+ f += a0;
+ PERMUTE3(f, h, g);
+ s += 20;
+ } while (--iters != 0);
+ g = Rotate32(g, 11) * c1;
+ g = Rotate32(g, 17) * c1;
+ f = Rotate32(f, 11) * c1;
+ f = Rotate32(f, 17) * c1;
+ h = Rotate32(h + g, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ h = Rotate32(h + f, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ return h;
+}
+
+// Bitwise right rotate. Normally this will compile to a single
+// instruction, especially if the shift is a manifest constant.
+static uint64 Rotate(uint64 val, int shift) {
+ // Avoid shifting by 64: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
+}
+
+static uint64 ShiftMix(uint64 val) {
+ return val ^ (val >> 47);
+}
+
+static uint64 HashLen16(uint64 u, uint64 v) {
+ return Hash128to64(uint128(u, v));
+}
+
+static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
+ // Murmur-inspired hashing.
+ uint64 a = (u ^ v) * mul;
+ a ^= (a >> 47);
+ uint64 b = (v ^ a) * mul;
+ b ^= (b >> 47);
+ b *= mul;
+ return b;
+}
+
+static uint64 HashLen0to16(const char *s, size_t len) {
+ if (len >= 8) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) + k2;
+ uint64 b = Fetch64(s + len - 8);
+ uint64 c = Rotate(b, 37) * mul + a;
+ uint64 d = (Rotate(a, 25) + b) * mul;
+ return HashLen16(c, d, mul);
+ }
+ if (len >= 4) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch32(s);
+ return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
+ }
+ if (len > 0) {
+ uint8 a = s[0];
+ uint8 b = s[len >> 1];
+ uint8 c = s[len - 1];
+ uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
+ uint32 z = len + (static_cast<uint32>(c) << 2);
+ return ShiftMix(y * k2 ^ z * k0) * k2;
+ }
+ return k2;
+}
+
+// This probably works well for 16-byte strings as well, but it may be overkill
+// in that case.
+static uint64 HashLen17to32(const char *s, size_t len) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) * k1;
+ uint64 b = Fetch64(s + 8);
+ uint64 c = Fetch64(s + len - 8) * mul;
+ uint64 d = Fetch64(s + len - 16) * k2;
+ return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
+ a + Rotate(b + k2, 18) + c, mul);
+}
+
+// Return a 16-byte hash for 48 bytes. Quick and dirty.
+// Callers do best to use "random-looking" values for a and b.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(
+ uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) {
+ a += w;
+ b = Rotate(b + a + z, 21);
+ uint64 c = a;
+ a += x;
+ a += y;
+ b += Rotate(a, 44);
+ return make_pair(a + z, b + c);
+}
+
+// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(
+ const char* s, uint64 a, uint64 b) {
+ return WeakHashLen32WithSeeds(Fetch64(s),
+ Fetch64(s + 8),
+ Fetch64(s + 16),
+ Fetch64(s + 24),
+ a,
+ b);
+}
+
+// Return an 8-byte hash for 33 to 64 bytes.
+static uint64 HashLen33to64(const char *s, size_t len) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) * k2;
+ uint64 b = Fetch64(s + 8);
+ uint64 c = Fetch64(s + len - 24);
+ uint64 d = Fetch64(s + len - 32);
+ uint64 e = Fetch64(s + 16) * k2;
+ uint64 f = Fetch64(s + 24) * 9;
+ uint64 g = Fetch64(s + len - 8);
+ uint64 h = Fetch64(s + len - 16) * mul;
+ uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
+ uint64 v = ((a + g) ^ d) + f + 1;
+ uint64 w = bswap_64((u + v) * mul) + h;
+ uint64 x = Rotate(e + f, 42) + c;
+ uint64 y = (bswap_64((v + w) * mul) + g) * mul;
+ uint64 z = e + f + c;
+ a = bswap_64((x + z) * mul + y) + b;
+ b = ShiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+}
+
+uint64 CityHash64(const char *s, size_t len) {
+ if (len <= 32) {
+ if (len <= 16) {
+ return HashLen0to16(s, len);
+ } else {
+ return HashLen17to32(s, len);
+ }
+ } else if (len <= 64) {
+ return HashLen33to64(s, len);
+ }
+
+ // For strings over 64 bytes we hash the end first, and then as we
+ // loop we keep 56 bytes of state: v, w, x, y, and z.
+ uint64 x = Fetch64(s + len - 40);
+ uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
+ uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
+ pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
+ pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
+ x = x * k1 + Fetch64(s);
+
+ // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+ len = (len - 1) & ~static_cast<size_t>(63);
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 64;
+ } while (len != 0);
+ return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
+ HashLen16(v.second, w.second) + x);
+}
+
+uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) {
+ return CityHash64WithSeeds(s, len, k2, seed);
+}
+
+uint64 CityHash64WithSeeds(const char *s, size_t len,
+ uint64 seed0, uint64 seed1) {
+ return HashLen16(CityHash64(s, len) - seed0, seed1);
+}
+
+// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
+// of any length representable in signed long. Based on City and Murmur.
+static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
+ uint64 a = Uint128Low64(seed);
+ uint64 b = Uint128High64(seed);
+ uint64 c = 0;
+ uint64 d = 0;
+ signed long l = len - 16;
+ if (l <= 0) { // len <= 16
+ a = ShiftMix(a * k1) * k1;
+ c = b * k1 + HashLen0to16(s, len);
+ d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
+ } else { // len > 16
+ c = HashLen16(Fetch64(s + len - 8) + k1, a);
+ d = HashLen16(b + len, c + Fetch64(s + len - 16));
+ a += d;
+ do {
+ a ^= ShiftMix(Fetch64(s) * k1) * k1;
+ a *= k1;
+ b ^= a;
+ c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
+ c *= k1;
+ d ^= c;
+ s += 16;
+ l -= 16;
+ } while (l > 0);
+ }
+ a = HashLen16(a, c);
+ b = HashLen16(d, b);
+ return uint128(a ^ b, HashLen16(b, a));
+}
+
+uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
+ if (len < 128) {
+ return CityMurmur(s, len, seed);
+ }
+
+ // We expect len >= 128 to be the common case. Keep 56 bytes of state:
+ // v, w, x, y, and z.
+ pair<uint64, uint64> v, w;
+ uint64 x = Uint128Low64(seed);
+ uint64 y = Uint128High64(seed);
+ uint64 z = len * k1;
+ v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
+ v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
+ w.first = Rotate(y + z, 35) * k1 + x;
+ w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
+
+ // This is the same inner loop as CityHash64(), manually unrolled.
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 128;
+ } while (LIKELY(len >= 128));
+ x += Rotate(v.first + z, 49) * k0;
+ y = y * k0 + Rotate(w.second, 37);
+ z = z * k0 + Rotate(w.first, 27);
+ w.first *= 9;
+ v.first *= k0;
+ // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
+ for (size_t tail_done = 0; tail_done < len; ) {
+ tail_done += 32;
+ y = Rotate(x + y, 42) * k0 + v.second;
+ w.first += Fetch64(s + len - tail_done + 16);
+ x = x * k0 + w.first;
+ z += w.second + Fetch64(s + len - tail_done);
+ w.second += v.first;
+ v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);
+ v.first *= k0;
+ }
+ // At this point our 56 bytes of state should contain more than
+ // enough information for a strong 128-bit hash. We use two
+ // different 56-byte-to-8-byte hashes to get a 16-byte final result.
+ x = HashLen16(x, v.first);
+ y = HashLen16(y + z, w.first);
+ return uint128(HashLen16(x + v.second, w.second) + y,
+ HashLen16(x + w.second, y + v.second));
+}
+
+uint128 CityHash128(const char *s, size_t len) {
+ return len >= 16 ?
+ CityHash128WithSeed(s + 16, len - 16,
+ uint128(Fetch64(s), Fetch64(s + 8) + k0)) :
+ CityHash128WithSeed(s, len, uint128(k0, k1));
+}
+
+#ifdef __SSE4_2__
+#include <citycrc.h>
+#include <nmmintrin.h>
+
+// Requires len >= 240.
+static void CityHashCrc256Long(const char *s, size_t len,
+ uint32 seed, uint64 *result) {
+ uint64 a = Fetch64(s + 56) + k0;
+ uint64 b = Fetch64(s + 96) + k0;
+ uint64 c = result[0] = HashLen16(b, len);
+ uint64 d = result[1] = Fetch64(s + 120) * k0 + len;
+ uint64 e = Fetch64(s + 184) + seed;
+ uint64 f = 0;
+ uint64 g = 0;
+ uint64 h = c + d;
+ uint64 x = seed;
+ uint64 y = 0;
+ uint64 z = 0;
+
+ // 240 bytes of input per iter.
+ size_t iters = len / 240;
+ len -= iters * 240;
+ do {
+#undef CHUNK
+#define CHUNK(r) \
+ PERMUTE3(x, z, y); \
+ b += Fetch64(s); \
+ c += Fetch64(s + 8); \
+ d += Fetch64(s + 16); \
+ e += Fetch64(s + 24); \
+ f += Fetch64(s + 32); \
+ a += b; \
+ h += f; \
+ b += c; \
+ f += d; \
+ g += e; \
+ e += z; \
+ g += x; \
+ z = _mm_crc32_u64(z, b + g); \
+ y = _mm_crc32_u64(y, e + h); \
+ x = _mm_crc32_u64(x, f + a); \
+ e = Rotate(e, r); \
+ c += e; \
+ s += 40
+
+ CHUNK(0); PERMUTE3(a, h, c);
+ CHUNK(33); PERMUTE3(a, h, f);
+ CHUNK(0); PERMUTE3(b, h, f);
+ CHUNK(42); PERMUTE3(b, h, d);
+ CHUNK(0); PERMUTE3(b, h, e);
+ CHUNK(33); PERMUTE3(a, h, e);
+ } while (--iters > 0);
+
+ while (len >= 40) {
+ CHUNK(29);
+ e ^= Rotate(a, 20);
+ h += Rotate(b, 30);
+ g ^= Rotate(c, 40);
+ f += Rotate(d, 34);
+ PERMUTE3(c, h, g);
+ len -= 40;
+ }
+ if (len > 0) {
+ s = s + len - 40;
+ CHUNK(33);
+ e ^= Rotate(a, 43);
+ h += Rotate(b, 42);
+ g ^= Rotate(c, 41);
+ f += Rotate(d, 40);
+ }
+ result[0] ^= h;
+ result[1] ^= g;
+ g += h;
+ a = HashLen16(a, g + z);
+ x += y << 32;
+ b += x;
+ c = HashLen16(c, z) + h;
+ d = HashLen16(d, e + result[0]);
+ g += e;
+ h += HashLen16(x, f);
+ e = HashLen16(a, d) + g;
+ z = HashLen16(b, c) + a;
+ y = HashLen16(g, h) + c;
+ result[0] = e + z + y + x;
+ a = ShiftMix((a + y) * k0) * k0 + b;
+ result[1] += a + result[0];
+ a = ShiftMix(a * k0) * k0 + c;
+ result[2] = a + result[1];
+ a = ShiftMix((a + e) * k0) * k0;
+ result[3] = a + result[2];
+}
+
+// Requires len < 240.
+static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) {
+ char buf[240];
+ memcpy(buf, s, len);
+ memset(buf + len, 0, 240 - len);
+ CityHashCrc256Long(buf, 240, ~static_cast<uint32>(len), result);
+}
+
+void CityHashCrc256(const char *s, size_t len, uint64 *result) {
+ if (LIKELY(len >= 240)) {
+ CityHashCrc256Long(s, len, 0, result);
+ } else {
+ CityHashCrc256Short(s, len, result);
+ }
+}
+
+uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
+ if (len <= 900) {
+ return CityHash128WithSeed(s, len, seed);
+ } else {
+ uint64 result[4];
+ CityHashCrc256(s, len, result);
+ uint64 u = Uint128High64(seed) + result[0];
+ uint64 v = Uint128Low64(seed) + result[1];
+ return uint128(HashLen16(u, v + result[2]),
+ HashLen16(Rotate(v, 32), u * k0 + result[3]));
+ }
+}
+
+uint128 CityHashCrc128(const char *s, size_t len) {
+ if (len <= 900) {
+ return CityHash128(s, len);
+ } else {
+ uint64 result[4];
+ CityHashCrc256(s, len, result);
+ return uint128(result[2], result[3]);
+ }
+}
+
+#endif
diff --git a/daemon/core/city-hash.hpp b/daemon/core/city-hash.hpp
new file mode 100644
index 0000000..54a90cb
--- /dev/null
+++ b/daemon/core/city-hash.hpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// http://code.google.com/p/cityhash/
+//
+// This file provides a few functions for hashing strings. All of them are
+// high-quality functions in the sense that they pass standard tests such
+// as Austin Appleby's SMHasher. They are also fast.
+//
+// For 64-bit x86 code, on short strings, we don't know of anything faster than
+// CityHash64 that is of comparable quality. We believe our nearest competitor
+// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash
+// tables and most other hashing (excluding cryptography).
+//
+// For 64-bit x86 code, on long strings, the picture is more complicated.
+// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
+// CityHashCrc128 appears to be faster than all competitors of comparable
+// quality. CityHash128 is also good but not quite as fast. We believe our
+// nearest competitor is Bob Jenkins' Spooky. We don't have great data for
+// other 64-bit CPUs, but for long strings we know that Spooky is slightly
+// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
+// Note that CityHashCrc128 is declared in citycrc.h.
+//
+// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
+// is of comparable quality. We believe our nearest competitor is Murmur3A.
+// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
+//
+// Functions in the CityHash family are not suitable for cryptography.
+//
+// Please see CityHash's README file for more details on our performance
+// measurements and so on.
+//
+// WARNING: This code has been only lightly tested on big-endian platforms!
+// It is known to work well on little-endian platforms that have a small penalty
+// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
+// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
+// bug reports are welcome.
+//
+// By the way, for some hash functions, given strings a and b, the hash
+// of a+b is easily derived from the hashes of a and b. This property
+// doesn't hold for any hash functions in this file.
+
+#ifndef CITY_HASH_HPP
+#define CITY_HASH_HPP
+
+#include <stdlib.h> // for size_t.
+#include <stdint.h>
+#include <utility>
+
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+typedef std::pair<uint64, uint64> uint128;
+
+inline uint64 Uint128Low64(const uint128& x) { return x.first; }
+inline uint64 Uint128High64(const uint128& x) { return x.second; }
+
+// Hash function for a byte array.
+uint64 CityHash64(const char *buf, size_t len);
+
+// Hash function for a byte array. For convenience, a 64-bit seed is also
+// hashed into the result.
+uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed);
+
+// Hash function for a byte array. For convenience, two seeds are also
+// hashed into the result.
+uint64 CityHash64WithSeeds(const char *buf, size_t len,
+ uint64 seed0, uint64 seed1);
+
+// Hash function for a byte array.
+uint128 CityHash128(const char *s, size_t len);
+
+// Hash function for a byte array. For convenience, a 128-bit seed is also
+// hashed into the result.
+uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed);
+
+// Hash function for a byte array. Most useful in 32-bit binaries.
+uint32 CityHash32(const char *buf, size_t len);
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+inline uint64 Hash128to64(const uint128& x) {
+ // Murmur-inspired hashing.
+ const uint64 kMul = 0x9ddfea08eb382d69ULL;
+ uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
+ a ^= (a >> 47);
+ uint64 b = (Uint128High64(x) ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+#endif // CITY_HASH_H_
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index c6ad6b6..6b06d10 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -36,7 +36,6 @@
Forwarder::Forwarder()
: m_faceTable(*this)
- , m_nameTree(1024) // "1024" could be made as one configurable parameter of the forwarder.
, m_fib(m_nameTree)
, m_pit(m_nameTree)
, m_measurements(m_nameTree)
diff --git a/daemon/table/name-tree-entry.cpp b/daemon/table/name-tree-entry.cpp
index a3e4880..0e4d821 100644
--- a/daemon/table/name-tree-entry.cpp
+++ b/daemon/table/name-tree-entry.cpp
@@ -29,7 +29,9 @@
namespace nfd {
namespace name_tree {
-Node::Node() : m_prev(0), m_next(0)
+Node::Node()
+ : m_prev(0)
+ , m_next(0)
{
}
@@ -54,44 +56,6 @@
}
void
-Entry::setHash(uint32_t hash)
-{
- m_hash = hash;
-}
-
-void
-Entry::setParent(shared_ptr<Entry> parent)
-{
- m_parent = parent;
-}
-
-void
-Entry::setFibEntry(shared_ptr<fib::Entry> fibEntry)
-{
- if (static_cast<bool>(fibEntry)) {
- BOOST_ASSERT(!static_cast<bool>(fibEntry->m_nameTreeEntry));
- }
-
- if (static_cast<bool>(m_fibEntry)) {
- m_fibEntry->m_nameTreeEntry.reset();
- }
- m_fibEntry = fibEntry;
- if (static_cast<bool>(m_fibEntry)) {
- m_fibEntry->m_nameTreeEntry = this->shared_from_this();
- }
-}
-
-void
-Entry::insertPitEntry(shared_ptr<pit::Entry> pitEntry)
-{
- BOOST_ASSERT(static_cast<bool>(pitEntry));
- BOOST_ASSERT(!static_cast<bool>(pitEntry->m_nameTreeEntry));
-
- m_pitEntries.push_back(pitEntry);
- pitEntry->m_nameTreeEntry = this->shared_from_this();
-}
-
-void
Entry::erasePitEntry(shared_ptr<pit::Entry> pitEntry)
{
BOOST_ASSERT(static_cast<bool>(pitEntry));
@@ -106,37 +70,5 @@
pitEntry->m_nameTreeEntry.reset();
}
-void
-Entry::setMeasurementsEntry(shared_ptr<measurements::Entry> measurementsEntry)
-{
- if (static_cast<bool>(measurementsEntry)) {
- BOOST_ASSERT(!static_cast<bool>(measurementsEntry->m_nameTreeEntry));
- }
-
- if (static_cast<bool>(m_measurementsEntry)) {
- m_measurementsEntry->m_nameTreeEntry.reset();
- }
- m_measurementsEntry = measurementsEntry;
- if (static_cast<bool>(m_measurementsEntry)) {
- m_measurementsEntry->m_nameTreeEntry = this->shared_from_this();
- }
-}
-
-void
-Entry::setStrategyChoiceEntry(shared_ptr<strategy_choice::Entry> strategyChoiceEntry)
-{
- if (static_cast<bool>(strategyChoiceEntry)) {
- BOOST_ASSERT(!static_cast<bool>(strategyChoiceEntry->m_nameTreeEntry));
- }
-
- if (static_cast<bool>(m_strategyChoiceEntry)) {
- m_strategyChoiceEntry->m_nameTreeEntry.reset();
- }
- m_strategyChoiceEntry = strategyChoiceEntry;
- if (static_cast<bool>(m_strategyChoiceEntry)) {
- m_strategyChoiceEntry->m_nameTreeEntry = this->shared_from_this();
- }
-}
-
} // namespace name_tree
} // namespace nfd
diff --git a/daemon/table/name-tree-entry.hpp b/daemon/table/name-tree-entry.hpp
index 0a0e05e..ab2e307 100644
--- a/daemon/table/name-tree-entry.hpp
+++ b/daemon/table/name-tree-entry.hpp
@@ -75,9 +75,9 @@
getPrefix() const;
void
- setHash(uint32_t hash);
+ setHash(size_t hash);
- uint32_t
+ size_t
getHash() const;
void
@@ -126,7 +126,10 @@
getStrategyChoiceEntry() const;
private:
- uint32_t m_hash;
+ // Benefits of storing m_hash
+ // 1. m_hash is compared before m_prefix is compared
+ // 2. fast hash table resize support
+ size_t m_hash;
Name m_prefix;
shared_ptr<Entry> m_parent; // Pointing to the parent entry.
std::vector<shared_ptr<Entry> > m_children; // Children pointers.
@@ -137,6 +140,7 @@
// get the Name Tree Node that is associated with this Name Tree Entry
Node* m_node;
+
// Make private members accessible by Name Tree
friend class nfd::NameTree;
};
@@ -147,18 +151,30 @@
return m_prefix;
}
-inline uint32_t
+inline size_t
Entry::getHash() const
{
return m_hash;
}
+inline void
+Entry::setHash(size_t hash)
+{
+ m_hash = hash;
+}
+
inline shared_ptr<Entry>
Entry::getParent() const
{
return m_parent;
}
+inline void
+Entry::setParent(shared_ptr<Entry> parent)
+{
+ m_parent = parent;
+}
+
inline std::vector<shared_ptr<name_tree::Entry> >&
Entry::getChildren()
{
@@ -186,6 +202,22 @@
return m_fibEntry;
}
+inline void
+Entry::setFibEntry(shared_ptr<fib::Entry> fibEntry)
+{
+ if (static_cast<bool>(fibEntry)) {
+ BOOST_ASSERT(!static_cast<bool>(fibEntry->m_nameTreeEntry));
+ }
+
+ if (static_cast<bool>(m_fibEntry)) {
+ m_fibEntry->m_nameTreeEntry.reset();
+ }
+ m_fibEntry = fibEntry;
+ if (static_cast<bool>(m_fibEntry)) {
+ m_fibEntry->m_nameTreeEntry = this->shared_from_this();
+ }
+}
+
inline bool
Entry::hasPitEntries() const
{
@@ -198,18 +230,60 @@
return m_pitEntries;
}
+inline void
+Entry::insertPitEntry(shared_ptr<pit::Entry> pitEntry)
+{
+ BOOST_ASSERT(static_cast<bool>(pitEntry));
+ BOOST_ASSERT(!static_cast<bool>(pitEntry->m_nameTreeEntry));
+
+ m_pitEntries.push_back(pitEntry);
+ pitEntry->m_nameTreeEntry = this->shared_from_this();
+}
+
inline shared_ptr<measurements::Entry>
Entry::getMeasurementsEntry() const
{
return m_measurementsEntry;
}
+inline void
+Entry::setMeasurementsEntry(shared_ptr<measurements::Entry> measurementsEntry)
+{
+ if (static_cast<bool>(measurementsEntry)) {
+ BOOST_ASSERT(!static_cast<bool>(measurementsEntry->m_nameTreeEntry));
+ }
+
+ if (static_cast<bool>(m_measurementsEntry)) {
+ m_measurementsEntry->m_nameTreeEntry.reset();
+ }
+ m_measurementsEntry = measurementsEntry;
+ if (static_cast<bool>(m_measurementsEntry)) {
+ m_measurementsEntry->m_nameTreeEntry = this->shared_from_this();
+ }
+}
+
inline shared_ptr<strategy_choice::Entry>
Entry::getStrategyChoiceEntry() const
{
return m_strategyChoiceEntry;
}
+inline void
+Entry::setStrategyChoiceEntry(shared_ptr<strategy_choice::Entry> strategyChoiceEntry)
+{
+ if (static_cast<bool>(strategyChoiceEntry)) {
+ BOOST_ASSERT(!static_cast<bool>(strategyChoiceEntry->m_nameTreeEntry));
+ }
+
+ if (static_cast<bool>(m_strategyChoiceEntry)) {
+ m_strategyChoiceEntry->m_nameTreeEntry.reset();
+ }
+ m_strategyChoiceEntry = strategyChoiceEntry;
+ if (static_cast<bool>(m_strategyChoiceEntry)) {
+ m_strategyChoiceEntry->m_nameTreeEntry = this->shared_from_this();
+ }
+}
+
} // namespace name_tree
} // namespace nfd
diff --git a/daemon/table/name-tree.cpp b/daemon/table/name-tree.cpp
index 101014a..b42b240 100644
--- a/daemon/table/name-tree.cpp
+++ b/daemon/table/name-tree.cpp
@@ -26,8 +26,7 @@
#include "name-tree.hpp"
#include "core/logger.hpp"
-
-#include <boost/functional/hash.hpp>
+#include "core/city-hash.hpp"
namespace nfd {
@@ -35,20 +34,67 @@
namespace name_tree {
-// Interface of different hash functions
-uint32_t
-hashName(const Name& prefix)
+class Hash32
{
- // fixed value. Used for debugging only.
- uint32_t ret = 0;
+public:
+ static size_t
+ compute(const char* buffer, size_t length)
+ {
+ return static_cast<size_t>(CityHash32(buffer, length));
+ }
+};
- // Boost hash
- // requires the /boost/functional/hash.hpp header file
- // TODO: improve hash efficiency with Name type
- boost::hash<std::string> stringHash;
- ret = stringHash(prefix.toUri());
+class Hash64
+{
+public:
+ static size_t
+ compute(const char* buffer, size_t length)
+ {
+ return static_cast<size_t>(CityHash64(buffer, length));
+ }
+};
- return ret;
+typedef boost::mpl::if_c<sizeof(size_t) >= 8, Hash64, Hash32>::type CityHash;
+
+// Interface of different hash functions
+size_t
+computeHash(const Name& prefix)
+{
+ prefix.wireEncode(); // guarantees prefix's wire buffer is not empty
+
+ size_t hashValue = 0;
+ size_t hashUpdate = 0;
+
+ for (Name::const_iterator it = prefix.begin(); it != prefix.end(); it++)
+ {
+ const char* wireFormat = reinterpret_cast<const char*>( it->wire() );
+ hashUpdate = CityHash::compute(wireFormat, it->size());
+ hashValue ^= hashUpdate;
+ }
+
+ return hashValue;
+}
+
+std::vector<size_t>
+computeHashSet(const Name& prefix)
+{
+ prefix.wireEncode(); // guarantees prefix's wire buffer is not empty
+
+ size_t hashValue = 0;
+ size_t hashUpdate = 0;
+
+ std::vector<size_t> hashValueSet;
+ hashValueSet.push_back(hashValue);
+
+ for (Name::const_iterator it = prefix.begin(); it != prefix.end(); it++)
+ {
+ const char* wireFormat = reinterpret_cast<const char*>( it->wire() );
+ hashUpdate = CityHash::compute(wireFormat, it->size());
+ hashValue ^= hashUpdate;
+ hashValueSet.push_back(hashValue);
+ }
+
+ return hashValueSet;
}
} // namespace name_tree
@@ -56,11 +102,17 @@
NameTree::NameTree(size_t nBuckets)
: m_nItems(0)
, m_nBuckets(nBuckets)
- , m_loadFactor(0.5)
- , m_resizeFactor(2)
+ , m_minNBuckets(nBuckets)
+ , m_enlargeLoadFactor(0.5) // more than 50% buckets loaded
+ , m_enlargeFactor(2) // double the hash table size
+ , m_shrinkLoadFactor(0.1) // less than 10% buckets loaded
+ , m_shrinkFactor(0.5) // reduce the number of buckets by half
, m_endIterator(FULL_ENUMERATE_TYPE, *this, m_end)
{
- m_resizeThreshold = static_cast<size_t>(m_loadFactor *
+ m_enlargeThreshold = static_cast<size_t>(m_enlargeLoadFactor *
+ static_cast<double>(m_nBuckets));
+
+ m_shrinkThreshold = static_cast<size_t>(m_shrinkLoadFactor *
static_cast<double>(m_nBuckets));
// array of node pointers
@@ -74,8 +126,9 @@
{
for (size_t i = 0; i < m_nBuckets; i++)
{
- if (m_buckets[i] != 0)
+ if (m_buckets[i] != 0) {
delete m_buckets[i];
+ }
}
delete [] m_buckets;
@@ -87,8 +140,8 @@
{
NFD_LOG_TRACE("insert " << prefix);
- uint32_t hashValue = name_tree::hashName(prefix);
- uint32_t loc = hashValue % m_nBuckets;
+ size_t hashValue = name_tree::computeHash(prefix);
+ size_t loc = hashValue % m_nBuckets;
NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue << " location = " << loc);
@@ -152,7 +205,7 @@
if (ret.second == true)
{
- m_nItems++; /* Increase the counter */
+ m_nItems++; // Increase the counter
entry->m_parent = parent;
if (static_cast<bool>(parent))
@@ -161,9 +214,9 @@
}
}
- if (m_nItems > m_resizeThreshold)
+ if (m_nItems > m_enlargeThreshold)
{
- resize(m_resizeFactor * m_nBuckets);
+ resize(m_enlargeFactor * m_nBuckets);
}
parent = entry;
@@ -177,8 +230,8 @@
{
NFD_LOG_TRACE("findExactMatch " << prefix);
- uint32_t hashValue = name_tree::hashName(prefix);
- uint32_t loc = hashValue % m_nBuckets;
+ size_t hashValue = name_tree::computeHash(prefix);
+ size_t loc = hashValue % m_nBuckets;
NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue <<
" location = " << loc);
@@ -204,23 +257,42 @@
}
// Longest Prefix Match
-// Return the longest matching Entry address
-// start from the full name, and then remove 1 name comp each time
shared_ptr<name_tree::Entry>
NameTree::findLongestPrefixMatch(const Name& prefix, const name_tree::EntrySelector& entrySelector) const
{
NFD_LOG_TRACE("findLongestPrefixMatch " << prefix);
shared_ptr<name_tree::Entry> entry;
+ std::vector<size_t> hashValueSet = name_tree::computeHashSet(prefix);
- for (int i = prefix.size(); i >= 0; i--)
+ size_t hashValue = 0;
+ size_t loc = 0;
+
+ for (int i = static_cast<int>(prefix.size()); i >= 0; i--)
{
- entry = findExactMatch(prefix.getPrefix(i));
- if (static_cast<bool>(entry) && entrySelector(*entry))
- return entry;
+ hashValue = hashValueSet[i];
+ loc = hashValue % m_nBuckets;
+
+ name_tree::Node* node = 0;
+ for (node = m_buckets[loc]; node != 0; node = node->m_next)
+ {
+ entry = node->m_entry;
+ if (static_cast<bool>(entry))
+ {
+ // isPrefixOf() is used to avoid making a copy of the name
+ if (hashValue == entry->getHash() &&
+ entry->getPrefix().isPrefixOf(prefix) &&
+ entrySelector(*entry))
+ {
+ return entry;
+ }
+ } // if entry
+ } // for node
}
- return shared_ptr<name_tree::Entry>();
+ // if not found, the default value of entry (null pointer) will be returned
+ entry.reset();
+ return entry;
}
shared_ptr<name_tree::Entry>
@@ -301,6 +373,14 @@
if (static_cast<bool>(parent))
eraseEntryIfEmpty(parent);
+ size_t newNBuckets = static_cast<size_t>(m_shrinkFactor *
+ static_cast<double>(m_nBuckets));
+
+ if (newNBuckets >= m_minNBuckets && m_nItems < m_shrinkThreshold)
+ {
+ resize(newNBuckets);
+ }
+
return true;
} // if this entry is empty
@@ -434,10 +514,14 @@
name_tree::Node** oldBuckets = m_buckets;
m_buckets = newBuckets;
- delete oldBuckets;
+ delete [] oldBuckets;
m_nBuckets = newNBuckets;
- m_resizeThreshold = (int)(m_loadFactor * (double)m_nBuckets);
+
+ m_enlargeThreshold = static_cast<size_t>(m_enlargeLoadFactor *
+ static_cast<double>(m_nBuckets));
+ m_shrinkThreshold = static_cast<size_t>(m_shrinkLoadFactor *
+ static_cast<double>(m_nBuckets));
}
// For debugging
diff --git a/daemon/table/name-tree.hpp b/daemon/table/name-tree.hpp
index c210a76..4048137 100644
--- a/daemon/table/name-tree.hpp
+++ b/daemon/table/name-tree.hpp
@@ -34,14 +34,17 @@
namespace name_tree {
/**
- * \brief Compute the hash value of the given name prefix.
- * \todo 1) have another parameter that specifies the number of components
- * 2) optimize the hash function to return a list of have values for all
- * the (or a certain number of, like a batch operation) prefixes. 3) move
- * the hash-related code to a separate file in /core or ndn-cpp-dev lib.
+ * \brief Compute the hash value of the given name prefix's WIRE FORMAT
*/
-uint32_t
-hashName(const Name& prefix);
+size_t
+computeHash(const Name& prefix);
+
+/**
+ * \brief Incrementally compute hash values
+ * \return Return a vector of hash values, starting from the root prefix
+ */
+std::vector<size_t>
+computeHashSet(const Name& prefix);
/// a predicate to accept or reject an Entry in find operations
typedef function<bool (const Entry& entry)> EntrySelector;
@@ -80,7 +83,7 @@
class const_iterator;
explicit
- NameTree(size_t nBuckets);
+ NameTree(size_t nBuckets = 1024);
~NameTree();
@@ -254,9 +257,13 @@
private:
size_t m_nItems; // Number of items being stored
size_t m_nBuckets; // Number of hash buckets
- double m_loadFactor;
- size_t m_resizeThreshold;
- int m_resizeFactor;
+ size_t m_minNBuckets; // Minimum number of hash buckets
+ double m_enlargeLoadFactor;
+ size_t m_enlargeThreshold;
+ int m_enlargeFactor;
+ double m_shrinkLoadFactor;
+ size_t m_shrinkThreshold;
+ double m_shrinkFactor;
name_tree::Node** m_buckets; // Name Tree Buckets in the NPHT
shared_ptr<name_tree::Entry> m_end;
const_iterator m_endIterator;
diff --git a/tests/mgmt/fib-enumeration-publisher-common.hpp b/tests/mgmt/fib-enumeration-publisher-common.hpp
index b5ce20c..7507c5d 100644
--- a/tests/mgmt/fib-enumeration-publisher-common.hpp
+++ b/tests/mgmt/fib-enumeration-publisher-common.hpp
@@ -75,8 +75,7 @@
public:
FibEnumerationPublisherFixture()
- : m_nameTree(1024)
- , m_fib(m_nameTree)
+ : m_fib(m_nameTree)
, m_face(make_shared<InternalFace>())
, m_publisher(m_fib, m_face, "/localhost/nfd/FibEnumerationPublisherFixture")
, m_finished(false)
diff --git a/tests/table/fib.cpp b/tests/table/fib.cpp
index 368067d..629a10f 100644
--- a/tests/table/fib.cpp
+++ b/tests/table/fib.cpp
@@ -136,7 +136,7 @@
std::pair<shared_ptr<fib::Entry>, bool> insertRes;
shared_ptr<fib::Entry> entry;
- NameTree nameTree(1024);
+ NameTree nameTree;
Fib fib(nameTree);
// []
BOOST_CHECK_EQUAL(fib.size(), 0);
@@ -208,7 +208,7 @@
std::pair<shared_ptr<fib::Entry>, bool> insertRes;
shared_ptr<fib::Entry> entry;
- NameTree nameTree(1024);
+ NameTree nameTree;
Fib fib(nameTree);
// {}
@@ -262,7 +262,7 @@
BOOST_AUTO_TEST_CASE(FindExactMatch)
{
- NameTree nameTree(1024);
+ NameTree nameTree;
Fib fib(nameTree);
fib.insert("/A");
fib.insert("/A/B");
@@ -275,14 +275,14 @@
validateNoExactMatch(fib, "/does/not/exist");
- NameTree gapNameTree(1024);
+ NameTree gapNameTree;
Fib gapFib(nameTree);
fib.insert("/X");
fib.insert("/X/Y/Z");
validateNoExactMatch(gapFib, "/X/Y");
- NameTree emptyNameTree(1024);
+ NameTree emptyNameTree;
Fib emptyFib(emptyNameTree);
validateNoExactMatch(emptyFib, "/nothing/here");
}
@@ -301,7 +301,7 @@
BOOST_AUTO_TEST_CASE(Remove)
{
- NameTree emptyNameTree(1024);
+ NameTree emptyNameTree;
Fib emptyFib(emptyNameTree);
emptyFib.erase("/does/not/exist"); // crash test
@@ -310,7 +310,7 @@
emptyFib.erase("/still/does/not/exist"); // crash test
- NameTree nameTree(1024);
+ NameTree nameTree;
Fib fib(nameTree);
fib.insert("/");
fib.insert("/A");
@@ -335,7 +335,7 @@
validateRemove(fib, "/");
validateNoExactMatch(fib, "/");
- NameTree gapNameTree(1024);
+ NameTree gapNameTree;
Fib gapFib(gapNameTree);
gapFib.insert("/X");
gapFib.insert("/X/Y/Z");
@@ -347,7 +347,7 @@
BOOST_AUTO_TEST_CASE(Iterator)
{
- NameTree nameTree(1024);
+ NameTree nameTree;
Fib fib(nameTree);
Name nameA("/A");
Name nameAB("/A/B");
diff --git a/tests/table/measurements.cpp b/tests/table/measurements.cpp
index 3de39ca..98676b5 100644
--- a/tests/table/measurements.cpp
+++ b/tests/table/measurements.cpp
@@ -33,7 +33,7 @@
BOOST_AUTO_TEST_CASE(Get_Parent)
{
- NameTree nameTree(1024);
+ NameTree nameTree;
Measurements measurements(nameTree);
Name name0;
diff --git a/tests/table/name-tree.cpp b/tests/table/name-tree.cpp
index a652558..e5e9089 100644
--- a/tests/table/name-tree.cpp
+++ b/tests/table/name-tree.cpp
@@ -32,6 +32,19 @@
BOOST_FIXTURE_TEST_SUITE(TableNameTree, BaseFixture)
+BOOST_AUTO_TEST_CASE(Hash)
+{
+ Name root("/");
+ root.wireEncode();
+ size_t hashValue = name_tree::computeHash(root);
+ BOOST_CHECK_EQUAL(hashValue, static_cast<size_t>(0));
+
+ Name prefix("/nohello/world/ndn/research");
+ prefix.wireEncode();
+ std::vector<size_t> hashSet = name_tree::computeHashSet(prefix);
+ BOOST_CHECK_EQUAL(hashSet.size(), prefix.size() + 1);
+}
+
BOOST_AUTO_TEST_CASE(Entry)
{
Name prefix("ndn:/named-data/research/abc/def/ghi");
@@ -41,25 +54,25 @@
// examine all the get methods
- uint32_t hash = npe->getHash();
- BOOST_CHECK_EQUAL(hash, 0);
+ size_t hash = npe->getHash();
+ BOOST_CHECK_EQUAL(hash, static_cast<size_t>(0));
shared_ptr<name_tree::Entry> parent = npe->getParent();
BOOST_CHECK(!static_cast<bool>(parent));
std::vector<shared_ptr<name_tree::Entry> >& childList = npe->getChildren();
- BOOST_CHECK_EQUAL(childList.size(), 0);
+ BOOST_CHECK_EQUAL(childList.size(), static_cast<size_t>(0));
shared_ptr<fib::Entry> fib = npe->getFibEntry();
BOOST_CHECK(!static_cast<bool>(fib));
const std::vector< shared_ptr<pit::Entry> >& pitList = npe->getPitEntries();
- BOOST_CHECK_EQUAL(pitList.size(), 0);
+ BOOST_CHECK_EQUAL(pitList.size(), static_cast<size_t>(0));
// examine all the set method
- npe->setHash(12345);
- BOOST_CHECK_EQUAL(npe->getHash(), 12345);
+ npe->setHash(static_cast<size_t>(12345));
+ BOOST_CHECK_EQUAL(npe->getHash(), static_cast<size_t>(12345));
Name parentName("ndn:/named-data/research/abc/def");
parent = make_shared<name_tree::Entry>(parentName);
@@ -138,7 +151,6 @@
// Longest Prefix Matching
-
shared_ptr<name_tree::Entry> temp;
Name nameABCLPM("/a/b/c/def/asdf/nlf");
temp = nt.findLongestPrefixMatch(nameABCLPM);
@@ -228,8 +240,6 @@
BOOST_AUTO_TEST_CASE(NameTreeIteratorFullEnumerate)
{
- using namespace std;
-
size_t nBuckets = 1024;
NameTree nt(nBuckets);
@@ -378,7 +388,6 @@
BOOST_AUTO_TEST_CASE(NameTreeIteratorPartialEnumerate)
{
- using namespace std;
typedef NameTree::const_iterator const_iterator;
size_t nBuckets = 16;
@@ -680,8 +689,6 @@
BOOST_AUTO_TEST_CASE(NameTreeIteratorFindAllMatches)
{
- using namespace std;
-
size_t nBuckets = 16;
NameTree nt(nBuckets);
int counter = 0;
@@ -772,6 +779,22 @@
BOOST_CHECK_EQUAL(counter, 7);
}
+BOOST_AUTO_TEST_CASE(NameTreeHashTableResizeShrink)
+{
+ size_t nBuckets = 16;
+ NameTree nameTree(nBuckets);
+
+ Name prefix("/a/b/c/d/e/f/g/h"); // requires 9 buckets
+
+ shared_ptr<name_tree::Entry> entry = nameTree.lookup(prefix);
+ BOOST_CHECK_EQUAL(nameTree.size(), 9);
+ BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 32);
+
+ nameTree.eraseEntryIfEmpty(entry);
+ BOOST_CHECK_EQUAL(nameTree.size(), 0);
+ BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 16);
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests