Added MD5, SHA256, CityHash hash function for MultiLevelHashMap testing
authorkhizmax <libcds.dev@gmail.com>
Sat, 5 Sep 2015 17:24:21 +0000 (20:24 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sat, 5 Sep 2015 17:24:21 +0000 (20:24 +0300)
13 files changed:
doxygen/image/multilevel_hashset.png
projects/Win/vc12/unit-prerequisites.vcxproj
projects/source.test-common.mk
tests/CMakeLists.txt
tests/hashing/city.cc [new file with mode: 0644]
tests/hashing/city.h [new file with mode: 0644]
tests/hashing/hash_func.h [new file with mode: 0644]
tests/hashing/md5.cpp [new file with mode: 0644]
tests/hashing/md5.h [new file with mode: 0644]
tests/hashing/sha256.cpp [new file with mode: 0644]
tests/hashing/sha256.h [new file with mode: 0644]
tests/unit/map2/map_defs.h
tests/unit/map2/map_type_multilevel_hashmap.h

index 2df82c0b386261ce44e9e857775e14ab923569bc..63b213bc770ae326449da331171f8326710904e0 100644 (file)
Binary files a/doxygen/image/multilevel_hashset.png and b/doxygen/image/multilevel_hashset.png differ
index 4d40f1a899568d9e9accad3ba8efd391a3cca741..848ab3a8b882e07fee9b8dee341bca9718fdaa8f 100644 (file)
     <ClInclude Include="..\..\..\tests\cppunit\cppunit_mini.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\cppunit_proxy.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\file_reporter.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\cppunit_mini.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\cppunit_proxy.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\file_reporter.h" />\r
+    <ClInclude Include="..\..\..\tests\hashing\city.h" />\r
+    <ClInclude Include="..\..\..\tests\hashing\hash_func.h" />\r
+    <ClInclude Include="..\..\..\tests\hashing\md5.h" />\r
+    <ClInclude Include="..\..\..\tests\hashing\sha256.h" />\r
     <ClInclude Include="..\..\..\tests\unit\ellen_bintree_update_desc_pool.h" />\r
     <ClInclude Include="..\..\..\tests\unit\michael_alloc.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\test_beans.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\thread.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\tests\unit\ellen_bintree_update_desc_pool.h" />\r
     <ClInclude Include="..\..\..\tests\unit\michael_alloc.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\test_beans.h" />\r
     <ClInclude Include="..\..\..\tests\cppunit\thread.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="..\..\..\tests\hashing\city.cc" />\r
+    <ClCompile Include="..\..\..\tests\hashing\md5.cpp" />\r
+    <ClCompile Include="..\..\..\tests\hashing\sha256.cpp" />\r
     <ClCompile Include="..\..\..\tests\unit\ellen_bintree_update_desc_pool.cpp" />\r
     <ClCompile Include="..\..\..\tests\unit\michael_alloc.cpp" />\r
     <ClCompile Include="..\..\..\tests\cppunit\test_main.cpp">\r
     <ClCompile Include="..\..\..\tests\unit\ellen_bintree_update_desc_pool.cpp" />\r
     <ClCompile Include="..\..\..\tests\unit\michael_alloc.cpp" />\r
     <ClCompile Include="..\..\..\tests\cppunit\test_main.cpp">\r
index a867a5297d0bc7ab0ec2014793dda17d178b4c72..99b66930e70efb9b2d43e96e79a1e0b7c9d1bd0e 100644 (file)
@@ -3,4 +3,9 @@ CDS_TESTCOMMON_SOURCES := \
     tests/cppunit/test_main.cpp \
     tests/cppunit/thread.cpp \
     tests/unit/michael_alloc.cpp \
     tests/cppunit/test_main.cpp \
     tests/cppunit/thread.cpp \
     tests/unit/michael_alloc.cpp \
-    tests/unit/ellen_bintree_update_desc_pool.cpp
+    tests/unit/ellen_bintree_update_desc_pool.cpp \
+    tests/hashing/city.cc \
+    tests/hashing/md5.cpp \
+    tests/hashing/sha256.cpp
+
+
index af28e734f65f53fb507adf7b742a1461306b1f05..3c77e1afde74f970a8cd27d59d6b8e9ca3011d41 100644 (file)
@@ -5,7 +5,10 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 set(SOURCES cppunit/test_main.cpp
             cppunit/thread.cpp
             unit/michael_alloc.cpp
 set(SOURCES cppunit/test_main.cpp
             cppunit/thread.cpp
             unit/michael_alloc.cpp
-            unit/ellen_bintree_update_desc_pool.cpp)
+            unit/ellen_bintree_update_desc_pool.cpp
+            hashing/city.cc
+            hashing/md5.cpp
+            hashing/sha256.cpp)
 
 add_library(${TEST_COMMON} OBJECT ${SOURCES})
 
 
 add_library(${TEST_COMMON} OBJECT ${SOURCES})
 
diff --git a/tests/hashing/city.cc b/tests/hashing/city.cc
new file mode 100644 (file)
index 0000000..c33aec0
--- /dev/null
@@ -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.h"
+
+#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 (int 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/tests/hashing/city.h b/tests/hashing/city.h
new file mode 100644 (file)
index 0000000..94499ce
--- /dev/null
@@ -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_H_
+#define CITY_HASH_H_
+
+#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/tests/hashing/hash_func.h b/tests/hashing/hash_func.h
new file mode 100644 (file)
index 0000000..3ea5ce9
--- /dev/null
@@ -0,0 +1,110 @@
+//$$CDS-header$$
+
+#ifndef CDSUNIT_HASH_FUNC_H
+#define CDSUNIT_HASH_FUNC_H
+
+#include "hashing/sha256.h"
+#include "hashing/md5.h"
+#include "hashing/city.h"
+
+namespace hashing {
+
+    template <class Hasher>
+    class hasher {
+        typedef Hasher hasher_type;
+        hasher_type m_hasher;
+    public:
+        struct hash_type {
+            uint8_t h[hasher_type::HashBytes];
+        };
+
+        hash_type operator()( void const * pBuf, size_t len )
+        {
+            m_hasher.reset();
+            m_hasher.add(pBuf, len);
+            hash_type result;
+            m_hasher.getHash( result.h );
+            return result;
+        }
+
+        hash_type operator()( std::string const& s )
+        {
+            return operator()( reinterpret_cast<void const *>(s.c_str()), s.length());
+        }
+
+        template <typename T>
+        hash_type operator()( T const& s )
+        {
+            return operator()( reinterpret_cast<void const *>(&s), sizeof(s));
+        }
+    };
+
+    typedef hasher<SHA256> sha256;
+    typedef hasher<MD5> md5;
+
+    class city32 {
+    public:
+        typedef uint32_t hash_type;
+
+        hash_type operator()( void const * pBuf, size_t len )
+        {
+            return CityHash32( reinterpret_cast<char const *>( pBuf ), len );
+        }
+
+        hash_type operator()( std::string const& s )
+        {
+            return CityHash32( s.c_str(), s.length() );
+        }
+
+        template <typename T>
+        hash_type operator()( T const& s )
+        {
+            return CityHash32( reinterpret_cast<char const *>( &s ), sizeof(s));
+        }
+    };
+
+    class city64 {
+    public:
+        typedef uint64_t hash_type;
+
+        hash_type operator()( void const * pBuf, size_t len )
+        {
+            return CityHash64( reinterpret_cast<char const *>( pBuf ), len );
+        }
+
+        hash_type operator()( std::string const& s )
+        {
+            return CityHash64( s.c_str(), s.length() );
+        }
+
+        template <typename T>
+        hash_type operator()( T const& s )
+        {
+            return CityHash64( reinterpret_cast<char const *>( &s ), sizeof(s));
+        }
+    };
+
+    class city128 {
+    public:
+        typedef uint128 hash_type;
+
+        hash_type operator()( void const * pBuf, size_t len )
+        {
+            return CityHash128( reinterpret_cast<char const *>( pBuf ), len );
+        }
+
+        hash_type operator()( std::string const& s )
+        {
+            return CityHash128( s.c_str(), s.length() );
+        }
+
+        template <typename T>
+        hash_type operator()( T const& s )
+        {
+            return CityHash128( reinterpret_cast<char const *>( &s ), sizeof(s));
+        }
+    };
+
+} // namespace hashing
+
+#endif // #ifndef CDSUNIT_HASH_FUNC_H
diff --git a/tests/hashing/md5.cpp b/tests/hashing/md5.cpp
new file mode 100644 (file)
index 0000000..2743319
--- /dev/null
@@ -0,0 +1,380 @@
+// //////////////////////////////////////////////////////////
+// md5.cpp
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "md5.h"
+
+#ifndef _MSC_VER
+#include <endian.h>
+#endif
+
+
+/// same as reset()
+MD5::MD5()
+{
+  reset();
+}
+
+
+/// restart
+void MD5::reset()
+{
+  m_numBytes   = 0;
+  m_bufferSize = 0;
+
+  // according to RFC 1321
+  m_hash[0] = 0x67452301;
+  m_hash[1] = 0xefcdab89;
+  m_hash[2] = 0x98badcfe;
+  m_hash[3] = 0x10325476;
+}
+
+
+namespace
+{
+  // mix functions for processBlock()
+  inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d)
+  {
+    return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d);
+  }
+
+  inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d)
+  {
+    return c ^ (d & (b ^ c)); // original: f = (b & d) | (c & (~d));
+  }
+
+  inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d)
+  {
+    return b ^ c ^ d;
+  }
+
+  inline uint32_t f4(uint32_t b, uint32_t c, uint32_t d)
+  {
+    return c ^ (b | ~d);
+  }
+
+  inline uint32_t rotate(uint32_t a, uint32_t c)
+  {
+    return (a << c) | (a >> (32 - c));
+  }
+
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+  inline uint32_t swap(uint32_t x)
+  {
+#if defined(__GNUC__) || defined(__clang__)
+    return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+    return _byteswap_ulong(x);
+#endif
+
+    return (x >> 24) |
+          ((x >>  8) & 0x0000FF00) |
+          ((x <<  8) & 0x00FF0000) |
+           (x << 24);
+  }
+#endif
+}
+
+
+/// process 64 bytes
+void MD5::processBlock(const void* data)
+{
+  // get last hash
+  uint32_t a = m_hash[0];
+  uint32_t b = m_hash[1];
+  uint32_t c = m_hash[2];
+  uint32_t d = m_hash[3];
+
+  // data represented as 16x 32-bit words
+  const uint32_t* words = (uint32_t*) data;
+
+  // computations are little endian, swap data if necessary
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+#define LITTLEENDIAN(x) swap(x)
+#else
+#define LITTLEENDIAN(x) (x)
+#endif
+
+  // first round
+  uint32_t word0  = LITTLEENDIAN(words[ 0]);
+  a = rotate(a + f1(b,c,d) + word0  + 0xd76aa478,  7) + b;
+  uint32_t word1  = LITTLEENDIAN(words[ 1]);
+  d = rotate(d + f1(a,b,c) + word1  + 0xe8c7b756, 12) + a;
+  uint32_t word2  = LITTLEENDIAN(words[ 2]);
+  c = rotate(c + f1(d,a,b) + word2  + 0x242070db, 17) + d;
+  uint32_t word3  = LITTLEENDIAN(words[ 3]);
+  b = rotate(b + f1(c,d,a) + word3  + 0xc1bdceee, 22) + c;
+
+  uint32_t word4  = LITTLEENDIAN(words[ 4]);
+  a = rotate(a + f1(b,c,d) + word4  + 0xf57c0faf,  7) + b;
+  uint32_t word5  = LITTLEENDIAN(words[ 5]);
+  d = rotate(d + f1(a,b,c) + word5  + 0x4787c62a, 12) + a;
+  uint32_t word6  = LITTLEENDIAN(words[ 6]);
+  c = rotate(c + f1(d,a,b) + word6  + 0xa8304613, 17) + d;
+  uint32_t word7  = LITTLEENDIAN(words[ 7]);
+  b = rotate(b + f1(c,d,a) + word7  + 0xfd469501, 22) + c;
+
+  uint32_t word8  = LITTLEENDIAN(words[ 8]);
+  a = rotate(a + f1(b,c,d) + word8  + 0x698098d8,  7) + b;
+  uint32_t word9  = LITTLEENDIAN(words[ 9]);
+  d = rotate(d + f1(a,b,c) + word9  + 0x8b44f7af, 12) + a;
+  uint32_t word10 = LITTLEENDIAN(words[10]);
+  c = rotate(c + f1(d,a,b) + word10 + 0xffff5bb1, 17) + d;
+  uint32_t word11 = LITTLEENDIAN(words[11]);
+  b = rotate(b + f1(c,d,a) + word11 + 0x895cd7be, 22) + c;
+
+  uint32_t word12 = LITTLEENDIAN(words[12]);
+  a = rotate(a + f1(b,c,d) + word12 + 0x6b901122,  7) + b;
+  uint32_t word13 = LITTLEENDIAN(words[13]);
+  d = rotate(d + f1(a,b,c) + word13 + 0xfd987193, 12) + a;
+  uint32_t word14 = LITTLEENDIAN(words[14]);
+  c = rotate(c + f1(d,a,b) + word14 + 0xa679438e, 17) + d;
+  uint32_t word15 = LITTLEENDIAN(words[15]);
+  b = rotate(b + f1(c,d,a) + word15 + 0x49b40821, 22) + c;
+
+  // second round
+  a = rotate(a + f2(b,c,d) + word1  + 0xf61e2562,  5) + b;
+  d = rotate(d + f2(a,b,c) + word6  + 0xc040b340,  9) + a;
+  c = rotate(c + f2(d,a,b) + word11 + 0x265e5a51, 14) + d;
+  b = rotate(b + f2(c,d,a) + word0  + 0xe9b6c7aa, 20) + c;
+
+  a = rotate(a + f2(b,c,d) + word5  + 0xd62f105d,  5) + b;
+  d = rotate(d + f2(a,b,c) + word10 + 0x02441453,  9) + a;
+  c = rotate(c + f2(d,a,b) + word15 + 0xd8a1e681, 14) + d;
+  b = rotate(b + f2(c,d,a) + word4  + 0xe7d3fbc8, 20) + c;
+
+  a = rotate(a + f2(b,c,d) + word9  + 0x21e1cde6,  5) + b;
+  d = rotate(d + f2(a,b,c) + word14 + 0xc33707d6,  9) + a;
+  c = rotate(c + f2(d,a,b) + word3  + 0xf4d50d87, 14) + d;
+  b = rotate(b + f2(c,d,a) + word8  + 0x455a14ed, 20) + c;
+
+  a = rotate(a + f2(b,c,d) + word13 + 0xa9e3e905,  5) + b;
+  d = rotate(d + f2(a,b,c) + word2  + 0xfcefa3f8,  9) + a;
+  c = rotate(c + f2(d,a,b) + word7  + 0x676f02d9, 14) + d;
+  b = rotate(b + f2(c,d,a) + word12 + 0x8d2a4c8a, 20) + c;
+
+  // third round
+  a = rotate(a + f3(b,c,d) + word5  + 0xfffa3942,  4) + b;
+  d = rotate(d + f3(a,b,c) + word8  + 0x8771f681, 11) + a;
+  c = rotate(c + f3(d,a,b) + word11 + 0x6d9d6122, 16) + d;
+  b = rotate(b + f3(c,d,a) + word14 + 0xfde5380c, 23) + c;
+
+  a = rotate(a + f3(b,c,d) + word1  + 0xa4beea44,  4) + b;
+  d = rotate(d + f3(a,b,c) + word4  + 0x4bdecfa9, 11) + a;
+  c = rotate(c + f3(d,a,b) + word7  + 0xf6bb4b60, 16) + d;
+  b = rotate(b + f3(c,d,a) + word10 + 0xbebfbc70, 23) + c;
+
+  a = rotate(a + f3(b,c,d) + word13 + 0x289b7ec6,  4) + b;
+  d = rotate(d + f3(a,b,c) + word0  + 0xeaa127fa, 11) + a;
+  c = rotate(c + f3(d,a,b) + word3  + 0xd4ef3085, 16) + d;
+  b = rotate(b + f3(c,d,a) + word6  + 0x04881d05, 23) + c;
+
+  a = rotate(a + f3(b,c,d) + word9  + 0xd9d4d039,  4) + b;
+  d = rotate(d + f3(a,b,c) + word12 + 0xe6db99e5, 11) + a;
+  c = rotate(c + f3(d,a,b) + word15 + 0x1fa27cf8, 16) + d;
+  b = rotate(b + f3(c,d,a) + word2  + 0xc4ac5665, 23) + c;
+
+  // fourth round
+  a = rotate(a + f4(b,c,d) + word0  + 0xf4292244,  6) + b;
+  d = rotate(d + f4(a,b,c) + word7  + 0x432aff97, 10) + a;
+  c = rotate(c + f4(d,a,b) + word14 + 0xab9423a7, 15) + d;
+  b = rotate(b + f4(c,d,a) + word5  + 0xfc93a039, 21) + c;
+
+  a = rotate(a + f4(b,c,d) + word12 + 0x655b59c3,  6) + b;
+  d = rotate(d + f4(a,b,c) + word3  + 0x8f0ccc92, 10) + a;
+  c = rotate(c + f4(d,a,b) + word10 + 0xffeff47d, 15) + d;
+  b = rotate(b + f4(c,d,a) + word1  + 0x85845dd1, 21) + c;
+
+  a = rotate(a + f4(b,c,d) + word8  + 0x6fa87e4f,  6) + b;
+  d = rotate(d + f4(a,b,c) + word15 + 0xfe2ce6e0, 10) + a;
+  c = rotate(c + f4(d,a,b) + word6  + 0xa3014314, 15) + d;
+  b = rotate(b + f4(c,d,a) + word13 + 0x4e0811a1, 21) + c;
+
+  a = rotate(a + f4(b,c,d) + word4  + 0xf7537e82,  6) + b;
+  d = rotate(d + f4(a,b,c) + word11 + 0xbd3af235, 10) + a;
+  c = rotate(c + f4(d,a,b) + word2  + 0x2ad7d2bb, 15) + d;
+  b = rotate(b + f4(c,d,a) + word9  + 0xeb86d391, 21) + c;
+
+  // update hash
+  m_hash[0] += a;
+  m_hash[1] += b;
+  m_hash[2] += c;
+  m_hash[3] += d;
+}
+
+
+/// add arbitrary number of bytes
+void MD5::add(const void* data, size_t numBytes)
+{
+  const uint8_t* current = (const uint8_t*) data;
+
+  if (m_bufferSize > 0)
+  {
+    while (numBytes > 0 && m_bufferSize < BlockSize)
+    {
+      m_buffer[m_bufferSize++] = *current++;
+      numBytes--;
+    }
+  }
+
+  // full buffer
+  if (m_bufferSize == BlockSize)
+  {
+    processBlock(m_buffer);
+    m_numBytes  += BlockSize;
+    m_bufferSize = 0;
+  }
+
+  // no more data ?
+  if (numBytes == 0)
+    return;
+
+  // process full blocks
+  while (numBytes >= BlockSize)
+  {
+    processBlock(current);
+    current    += BlockSize;
+    m_numBytes += BlockSize;
+    numBytes   -= BlockSize;
+  }
+
+  // keep remaining bytes in buffer
+  while (numBytes > 0)
+  {
+    m_buffer[m_bufferSize++] = *current++;
+    numBytes--;
+  }
+}
+
+
+/// process final block, less than 64 bytes
+void MD5::processBuffer()
+{
+  // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
+
+  // - append "1" bit to message
+  // - append "0" bits until message length in bit mod 512 is 448
+  // - append length as 64 bit integer
+
+  // number of bits
+  size_t paddedLength = m_bufferSize * 8;
+
+  // plus one bit set to 1 (always appended)
+  paddedLength++;
+
+  // number of bits must be (numBits % 512) = 448
+  size_t lower11Bits = paddedLength & 511;
+  if (lower11Bits <= 448)
+    paddedLength +=       448 - lower11Bits;
+  else
+    paddedLength += 512 + 448 - lower11Bits;
+  // convert from bits to bytes
+  paddedLength /= 8;
+
+  // only needed if additional data flows over into a second block
+  unsigned char extra[BlockSize];
+
+  // append a "1" bit, 128 => binary 10000000
+  if (m_bufferSize < BlockSize)
+    m_buffer[m_bufferSize] = 128;
+  else
+    extra[0] = 128;
+
+  size_t i;
+  for (i = m_bufferSize + 1; i < BlockSize; i++)
+    m_buffer[i] = 0;
+  for (; i < paddedLength; i++)
+    extra[i - BlockSize] = 0;
+
+  // add message length in bits as 64 bit number
+  uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
+  // find right position
+  unsigned char* addLength;
+  if (paddedLength < BlockSize)
+    addLength = m_buffer + paddedLength;
+  else
+    addLength = extra + paddedLength - BlockSize;
+
+  // must be little endian
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF; msgBits >>= 8;
+  *addLength++ = msgBits & 0xFF;
+
+  // process blocks
+  processBlock(m_buffer);
+  // flowed over into a second block ?
+  if (paddedLength > BlockSize)
+    processBlock(extra);
+}
+
+
+/// return latest hash as 32 hex characters
+std::string MD5::getHash()
+{
+  // compute hash (as raw bytes)
+  unsigned char rawHash[HashBytes];
+  getHash(rawHash);
+
+  // convert to hex string
+  std::string result;
+  result.reserve(2 * HashBytes);
+  for (int i = 0; i < HashBytes; i++)
+  {
+    static const char dec2hex[16+1] = "0123456789abcdef";
+    result += dec2hex[(rawHash[i] >> 4) & 15];
+    result += dec2hex[ rawHash[i]       & 15];
+  }
+
+  return result;
+}
+
+
+/// return latest hash as bytes
+void MD5::getHash(unsigned char buffer[MD5::HashBytes])
+{
+  // save old hash if buffer is partially filled
+  uint32_t oldHash[HashValues];
+  for (int i = 0; i < HashValues; i++)
+    oldHash[i] = m_hash[i];
+
+  // process remaining bytes
+  processBuffer();
+
+  unsigned char* current = buffer;
+  for (int i = 0; i < HashValues; i++)
+  {
+    *current++ =  m_hash[i]        & 0xFF;
+    *current++ = (m_hash[i] >>  8) & 0xFF;
+    *current++ = (m_hash[i] >> 16) & 0xFF;
+    *current++ = (m_hash[i] >> 24) & 0xFF;
+
+    // restore old hash
+    m_hash[i] = oldHash[i];
+  }
+}
+
+
+/// compute MD5 of a memory block
+std::string MD5::operator()(const void* data, size_t numBytes)
+{
+  reset();
+  add(data, numBytes);
+  return getHash();
+}
+
+
+/// compute MD5 of a string, excluding final zero
+std::string MD5::operator()(const std::string& text)
+{
+  reset();
+  add(text.c_str(), text.size());
+  return getHash();
+}
diff --git a/tests/hashing/md5.h b/tests/hashing/md5.h
new file mode 100644 (file)
index 0000000..f53e2d9
--- /dev/null
@@ -0,0 +1,78 @@
+// //////////////////////////////////////////////////////////
+// md5.h
+// Copyright (c) 2014 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include <string>
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8  uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+// GCC
+#include <stdint.h>
+#endif
+
+
+/// compute MD5 hash
+/** Usage:
+    MD5 md5;
+    std::string myHash  = md5("Hello World");     // std::string
+    std::string myHash2 = md5("How are you", 11); // arbitrary data, 11 bytes
+
+    // or in a streaming fashion:
+
+    MD5 md5;
+    while (more data available)
+      md5.add(pointer to fresh data, number of new bytes);
+    std::string myHash3 = md5.getHash();
+  */
+class MD5 //: public Hash
+{
+public:
+  /// split into 64 byte blocks (=> 512 bits), hash is 16 bytes long
+  enum { BlockSize = 512 / 8, HashBytes = 16 };
+
+  /// same as reset()
+  MD5();
+
+  /// compute MD5 of a memory block
+  std::string operator()(const void* data, size_t numBytes);
+  /// compute MD5 of a string, excluding final zero
+  std::string operator()(const std::string& text);
+
+  /// add arbitrary number of bytes
+  void add(const void* data, size_t numBytes);
+
+  /// return latest hash as 32 hex characters
+  std::string getHash();
+  /// return latest hash as bytes
+  void        getHash(unsigned char buffer[HashBytes]);
+
+  /// restart
+  void reset();
+
+private:
+  /// process 64 bytes
+  void processBlock(const void* data);
+  /// process everything left in the internal buffer
+  void processBuffer();
+
+  /// size of processed data in bytes
+  uint64_t m_numBytes;
+  /// valid bytes in m_buffer
+  size_t   m_bufferSize;
+  /// bytes not processed yet
+  uint8_t  m_buffer[BlockSize];
+
+  enum { HashValues = HashBytes / 4 };
+  /// hash, stored as integers
+  uint32_t m_hash[HashValues];
+};
diff --git a/tests/hashing/sha256.cpp b/tests/hashing/sha256.cpp
new file mode 100644 (file)
index 0000000..0c6138c
--- /dev/null
@@ -0,0 +1,411 @@
+// //////////////////////////////////////////////////////////
+// sha256.cpp
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "sha256.h"
+
+// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
+#ifndef _MSC_VER
+#include <endian.h>
+#endif
+
+
+/// same as reset()
+SHA256::SHA256()
+{
+  reset();
+}
+
+
+/// restart
+void SHA256::reset()
+{
+  m_numBytes   = 0;
+  m_bufferSize = 0;
+
+  // according to RFC 1321
+  m_hash[0] = 0x6a09e667;
+  m_hash[1] = 0xbb67ae85;
+  m_hash[2] = 0x3c6ef372;
+  m_hash[3] = 0xa54ff53a;
+  m_hash[4] = 0x510e527f;
+  m_hash[5] = 0x9b05688c;
+  m_hash[6] = 0x1f83d9ab;
+  m_hash[7] = 0x5be0cd19;
+}
+
+
+namespace
+{
+  inline uint32_t rotate(uint32_t a, uint32_t c)
+  {
+    return (a >> c) | (a << (32 - c));
+  }
+
+  inline uint32_t swap(uint32_t x)
+  {
+#if defined(__GNUC__) || defined(__clang__)
+    return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+    return _byteswap_ulong(x);
+#endif
+
+    return (x >> 24) |
+          ((x >>  8) & 0x0000FF00) |
+          ((x <<  8) & 0x00FF0000) |
+           (x << 24);
+  }
+
+  // mix functions for processBlock()
+  inline uint32_t f1(uint32_t e, uint32_t f, uint32_t g)
+  {
+    uint32_t term1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
+    uint32_t term2 = (e & f) ^ (~e & g); //(g ^ (e & (f ^ g)))
+    return term1 + term2;
+  }
+
+  inline uint32_t f2(uint32_t a, uint32_t b, uint32_t c)
+  {
+    uint32_t term1 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22);
+    uint32_t term2 = ((a | b) & c) | (a & b); //(a & (b ^ c)) ^ (b & c);
+    return term1 + term2;
+  }
+}
+
+
+/// process 64 bytes
+void SHA256::processBlock(const void* data)
+{
+  // get last hash
+  uint32_t a = m_hash[0];
+  uint32_t b = m_hash[1];
+  uint32_t c = m_hash[2];
+  uint32_t d = m_hash[3];
+  uint32_t e = m_hash[4];
+  uint32_t f = m_hash[5];
+  uint32_t g = m_hash[6];
+  uint32_t h = m_hash[7];
+
+  // data represented as 16x 32-bit words
+  const uint32_t* input = (uint32_t*) data;
+  // convert to big endian
+  uint32_t words[64];
+  int i;
+  for (i = 0; i < 16; i++)
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+    words[i] =      input[i];
+#else
+    words[i] = swap(input[i]);
+#endif
+
+  uint32_t x,y; // temporaries
+
+  // first round
+  x = h + f1(e,f,g) + 0x428a2f98 + words[ 0]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0x71374491 + words[ 1]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0xb5c0fbcf + words[ 2]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0xe9b5dba5 + words[ 3]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x3956c25b + words[ 4]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0x59f111f1 + words[ 5]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x923f82a4 + words[ 6]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0xab1c5ed5 + words[ 7]; y = f2(b,c,d); e += x; a = x + y;
+
+  // secound round
+  x = h + f1(e,f,g) + 0xd807aa98 + words[ 8]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0x12835b01 + words[ 9]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0x243185be + words[10]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0x550c7dc3 + words[11]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x72be5d74 + words[12]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0x80deb1fe + words[13]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x9bdc06a7 + words[14]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0xc19bf174 + words[15]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 24 words
+  for (; i < 24; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // third round
+  x = h + f1(e,f,g) + 0xe49b69c1 + words[16]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0xefbe4786 + words[17]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0x0fc19dc6 + words[18]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0x240ca1cc + words[19]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x2de92c6f + words[20]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0x4a7484aa + words[21]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x5cb0a9dc + words[22]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0x76f988da + words[23]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 32 words
+  for (; i < 32; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // fourth round
+  x = h + f1(e,f,g) + 0x983e5152 + words[24]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0xa831c66d + words[25]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0xb00327c8 + words[26]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0xbf597fc7 + words[27]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0xc6e00bf3 + words[28]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0xd5a79147 + words[29]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x06ca6351 + words[30]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0x14292967 + words[31]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 40 words
+  for (; i < 40; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // fifth round
+  x = h + f1(e,f,g) + 0x27b70a85 + words[32]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0x2e1b2138 + words[33]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0x4d2c6dfc + words[34]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0x53380d13 + words[35]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x650a7354 + words[36]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0x766a0abb + words[37]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x81c2c92e + words[38]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0x92722c85 + words[39]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 48 words
+  for (; i < 48; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // sixth round
+  x = h + f1(e,f,g) + 0xa2bfe8a1 + words[40]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0xa81a664b + words[41]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0xc24b8b70 + words[42]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0xc76c51a3 + words[43]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0xd192e819 + words[44]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0xd6990624 + words[45]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0xf40e3585 + words[46]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0x106aa070 + words[47]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 56 words
+  for (; i < 56; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // seventh round
+  x = h + f1(e,f,g) + 0x19a4c116 + words[48]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0x1e376c08 + words[49]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0x2748774c + words[50]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0x34b0bcb5 + words[51]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x391c0cb3 + words[52]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0x4ed8aa4a + words[53]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0x5b9cca4f + words[54]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0x682e6ff3 + words[55]; y = f2(b,c,d); e += x; a = x + y;
+
+  // extend to 64 words
+  for (; i < 64; i++)
+    words[i] = words[i-16] +
+               (rotate(words[i-15],  7) ^ rotate(words[i-15], 18) ^ (words[i-15] >>  3)) +
+               words[i-7] +
+               (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
+
+  // eigth round
+  x = h + f1(e,f,g) + 0x748f82ee + words[56]; y = f2(a,b,c); d += x; h = x + y;
+  x = g + f1(d,e,f) + 0x78a5636f + words[57]; y = f2(h,a,b); c += x; g = x + y;
+  x = f + f1(c,d,e) + 0x84c87814 + words[58]; y = f2(g,h,a); b += x; f = x + y;
+  x = e + f1(b,c,d) + 0x8cc70208 + words[59]; y = f2(f,g,h); a += x; e = x + y;
+  x = d + f1(a,b,c) + 0x90befffa + words[60]; y = f2(e,f,g); h += x; d = x + y;
+  x = c + f1(h,a,b) + 0xa4506ceb + words[61]; y = f2(d,e,f); g += x; c = x + y;
+  x = b + f1(g,h,a) + 0xbef9a3f7 + words[62]; y = f2(c,d,e); f += x; b = x + y;
+  x = a + f1(f,g,h) + 0xc67178f2 + words[63]; y = f2(b,c,d); e += x; a = x + y;
+
+  // update hash
+  m_hash[0] += a;
+  m_hash[1] += b;
+  m_hash[2] += c;
+  m_hash[3] += d;
+  m_hash[4] += e;
+  m_hash[5] += f;
+  m_hash[6] += g;
+  m_hash[7] += h;
+}
+
+
+/// add arbitrary number of bytes
+void SHA256::add(const void* data, size_t numBytes)
+{
+  const uint8_t* current = (const uint8_t*) data;
+
+  if (m_bufferSize > 0)
+  {
+    while (numBytes > 0 && m_bufferSize < BlockSize)
+    {
+      m_buffer[m_bufferSize++] = *current++;
+      numBytes--;
+    }
+  }
+
+  // full buffer
+  if (m_bufferSize == BlockSize)
+  {
+    processBlock(m_buffer);
+    m_numBytes  += BlockSize;
+    m_bufferSize = 0;
+  }
+
+  // no more data ?
+  if (numBytes == 0)
+    return;
+
+  // process full blocks
+  while (numBytes >= BlockSize)
+  {
+    processBlock(current);
+    current    += BlockSize;
+    m_numBytes += BlockSize;
+    numBytes   -= BlockSize;
+  }
+
+  // keep remaining bytes in buffer
+  while (numBytes > 0)
+  {
+    m_buffer[m_bufferSize++] = *current++;
+    numBytes--;
+  }
+}
+
+
+/// process final block, less than 64 bytes
+void SHA256::processBuffer()
+{
+  // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
+
+  // - append "1" bit to message
+  // - append "0" bits until message length in bit mod 512 is 448
+  // - append length as 64 bit integer
+
+  // number of bits
+  size_t paddedLength = m_bufferSize * 8;
+
+  // plus one bit set to 1 (always appended)
+  paddedLength++;
+
+  // number of bits must be (numBits % 512) = 448
+  size_t lower11Bits = paddedLength & 511;
+  if (lower11Bits <= 448)
+    paddedLength +=       448 - lower11Bits;
+  else
+    paddedLength += 512 + 448 - lower11Bits;
+  // convert from bits to bytes
+  paddedLength /= 8;
+
+  // only needed if additional data flows over into a second block
+  unsigned char extra[BlockSize];
+
+  // append a "1" bit, 128 => binary 10000000
+  if (m_bufferSize < BlockSize)
+    m_buffer[m_bufferSize] = 128;
+  else
+    extra[0] = 128;
+
+  size_t i;
+  for (i = m_bufferSize + 1; i < BlockSize; i++)
+    m_buffer[i] = 0;
+  for (; i < paddedLength; i++)
+    extra[i - BlockSize] = 0;
+
+  // add message length in bits as 64 bit number
+  uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
+  // find right position
+  unsigned char* addLength;
+  if (paddedLength < BlockSize)
+    addLength = m_buffer + paddedLength;
+  else
+    addLength = extra + paddedLength - BlockSize;
+
+  // must be big endian
+  *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
+  *addLength++ = (unsigned char)((msgBits >>  8) & 0xFF);
+  *addLength   = (unsigned char)( msgBits        & 0xFF);
+
+  // process blocks
+  processBlock(m_buffer);
+  // flowed over into a second block ?
+  if (paddedLength > BlockSize)
+    processBlock(extra);
+}
+
+
+/// return latest hash as 64 hex characters
+std::string SHA256::getHash()
+{
+  // compute hash (as raw bytes)
+  unsigned char rawHash[HashBytes];
+  getHash(rawHash);
+
+  // convert to hex string
+  std::string result;
+  result.reserve(2 * HashBytes);
+  for (int i = 0; i < HashBytes; i++)
+  {
+    static const char dec2hex[16+1] = "0123456789abcdef";
+    result += dec2hex[(rawHash[i] >> 4) & 15];
+    result += dec2hex[ rawHash[i]       & 15];
+  }
+
+  return result;
+}
+
+
+/// return latest hash as bytes
+void SHA256::getHash(unsigned char buffer[SHA256::HashBytes])
+{
+  // save old hash if buffer is partially filled
+  uint32_t oldHash[HashValues];
+  for (int i = 0; i < HashValues; i++)
+    oldHash[i] = m_hash[i];
+
+  // process remaining bytes
+  processBuffer();
+
+  unsigned char* current = buffer;
+  for (int i = 0; i < HashValues; i++)
+  {
+    *current++ = (m_hash[i] >> 24) & 0xFF;
+    *current++ = (m_hash[i] >> 16) & 0xFF;
+    *current++ = (m_hash[i] >>  8) & 0xFF;
+    *current++ =  m_hash[i]        & 0xFF;
+
+    // restore old hash
+    m_hash[i] = oldHash[i];
+  }
+}
+
+
+/// compute SHA256 of a memory block
+std::string SHA256::operator()(const void* data, size_t numBytes)
+{
+  reset();
+  add(data, numBytes);
+  return getHash();
+}
+
+
+/// compute SHA256 of a string, excluding final zero
+std::string SHA256::operator()(const std::string& text)
+{
+  reset();
+  add(text.c_str(), text.size());
+  return getHash();
+}
diff --git a/tests/hashing/sha256.h b/tests/hashing/sha256.h
new file mode 100644 (file)
index 0000000..aeaf314
--- /dev/null
@@ -0,0 +1,78 @@
+// //////////////////////////////////////////////////////////
+// sha256.h
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include <string>
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8  uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+// GCC
+#include <stdint.h>
+#endif
+
+
+/// compute SHA256 hash
+/** Usage:
+    SHA256 sha256;
+    std::string myHash  = sha256("Hello World");     // std::string
+    std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes
+
+    // or in a streaming fashion:
+
+    SHA256 sha256;
+    while (more data available)
+      sha256.add(pointer to fresh data, number of new bytes);
+    std::string myHash3 = sha256.getHash();
+  */
+class SHA256 //: public Hash
+{
+public:
+  /// split into 64 byte blocks (=> 512 bits), hash is 32 bytes long
+  enum { BlockSize = 512 / 8, HashBytes = 32 };
+
+  /// same as reset()
+  SHA256();
+
+  /// compute SHA256 of a memory block
+  std::string operator()(const void* data, size_t numBytes);
+  /// compute SHA256 of a string, excluding final zero
+  std::string operator()(const std::string& text);
+
+  /// add arbitrary number of bytes
+  void add(const void* data, size_t numBytes);
+
+  /// return latest hash as 64 hex characters
+  std::string getHash();
+  /// return latest hash as bytes
+  void        getHash(unsigned char buffer[HashBytes]);
+
+  /// restart
+  void reset();
+
+private:
+  /// process 64 bytes
+  void processBlock(const void* data);
+  /// process everything left in the internal buffer
+  void processBuffer();
+
+  /// size of processed data in bytes
+  uint64_t m_numBytes;
+  /// valid bytes in m_buffer
+  size_t   m_bufferSize;
+  /// bytes not processed yet
+  uint8_t  m_buffer[BlockSize];
+
+  enum { HashValues = HashBytes / 4 };
+  /// hash, stored as integers
+  uint32_t m_hash[HashValues];
+};
index 47c17bbe2e9fa1c1861a612e7f2ce92d77529be7..644f9cb6d48c7836eca89a930450f9b7581e5787 100644 (file)
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_stdhash) \
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_stdhash_stat) \
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_stdhash) \
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_stdhash) \
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_stdhash_stat) \
     TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_stdhash) \
-    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_stdhash_stat)
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_stdhash_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_md5) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_md5_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_md5) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_md5_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_sha256) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_sha256_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_sha256) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_sha256_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_city64) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_city64_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_city64) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_city64_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_city128) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_hp_city128_stat) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_city128) \
+    TEST_CASE(tag_MultiLevelHashMap, MultiLevelHashMap_dhp_city128_stat) \
+
 
 #undef CDSUNIT_TEST_MultiLevelHashMap
 #define CDSUNIT_TEST_MultiLevelHashMap  \
     CPPUNIT_TEST(MultiLevelHashMap_hp_stdhash) \
     CPPUNIT_TEST(MultiLevelHashMap_hp_stdhash_stat) \
     CPPUNIT_TEST(MultiLevelHashMap_dhp_stdhash) \
 
 #undef CDSUNIT_TEST_MultiLevelHashMap
 #define CDSUNIT_TEST_MultiLevelHashMap  \
     CPPUNIT_TEST(MultiLevelHashMap_hp_stdhash) \
     CPPUNIT_TEST(MultiLevelHashMap_hp_stdhash_stat) \
     CPPUNIT_TEST(MultiLevelHashMap_dhp_stdhash) \
-    CPPUNIT_TEST(MultiLevelHashMap_dhp_stdhash_stat)
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_stdhash_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_md5) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_md5_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_md5) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_md5_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_sha256) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_sha256_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_sha256) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_sha256_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_city64) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_city64_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_city64) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_city64_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_city128) \
+    CPPUNIT_TEST(MultiLevelHashMap_hp_city128_stat) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_city128) \
+    CPPUNIT_TEST(MultiLevelHashMap_dhp_city128_stat) \
 
 
index 2c25a8e950bb11d80bb03968f33942ecce6ed19e..307ebc1a9b4a433858f1f848761d213fb97d5e2b 100644 (file)
@@ -9,6 +9,7 @@
 #include <cds/container/multilevel_hashmap_dhp.h>
 
 #include "print_multilevel_hashset_stat.h"
 #include <cds/container/multilevel_hashmap_dhp.h>
 
 #include "print_multilevel_hashset_stat.h"
+#include "hashing/hash_func.h"
 
 namespace map2 {
 
 
 namespace map2 {
 
@@ -46,6 +47,66 @@ namespace map2 {
 
         typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_stat >    MultiLevelHashMap_hp_stdhash_stat;
         typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_stat >    MultiLevelHashMap_dhp_stdhash_stat;
 
         typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_stat >    MultiLevelHashMap_hp_stdhash_stat;
         typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_stat >    MultiLevelHashMap_dhp_stdhash_stat;
+
+        // SHA256
+        struct traits_MultiLevelHashMap_sha256 : public cc::multilevel_hashmap::traits
+        {
+            typedef ::hashing::sha256 hash;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_sha256 >    MultiLevelHashMap_hp_sha256;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_sha256 >    MultiLevelHashMap_dhp_sha256;
+
+        struct traits_MultiLevelHashMap_sha256_stat : public traits_MultiLevelHashMap_sha256
+        {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_sha256_stat >    MultiLevelHashMap_hp_sha256_stat;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_sha256_stat >    MultiLevelHashMap_dhp_sha256_stat;
+
+        //MD5
+        struct traits_MultiLevelHashMap_md5 : public cc::multilevel_hashmap::traits
+        {
+            typedef ::hashing::md5 hash;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_md5 >    MultiLevelHashMap_hp_md5;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_md5 >    MultiLevelHashMap_dhp_md5;
+
+        struct traits_MultiLevelHashMap_md5_stat : public traits_MultiLevelHashMap_md5
+        {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_md5_stat >    MultiLevelHashMap_hp_md5_stat;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_md5_stat >    MultiLevelHashMap_dhp_md5_stat;
+
+        // CityHash
+        struct traits_MultiLevelHashMap_city64 : public cc::multilevel_hashmap::traits
+        {
+            typedef ::hashing::city64 hash;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_city64 >    MultiLevelHashMap_hp_city64;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_city64 >    MultiLevelHashMap_dhp_city64;
+
+        struct traits_MultiLevelHashMap_city64_stat : public traits_MultiLevelHashMap_city64
+        {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_city64_stat >    MultiLevelHashMap_hp_city64_stat;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_city64_stat >    MultiLevelHashMap_dhp_city64_stat;
+
+        struct traits_MultiLevelHashMap_city128 : public cc::multilevel_hashmap::traits
+        {
+            typedef ::hashing::city128 hash;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_city128 >    MultiLevelHashMap_hp_city128;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_city128 >    MultiLevelHashMap_dhp_city128;
+
+        struct traits_MultiLevelHashMap_city128_stat : public traits_MultiLevelHashMap_city128
+        {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef MultiLevelHashMap< cds::gc::HP,  Key, Value, traits_MultiLevelHashMap_city128_stat >    MultiLevelHashMap_hp_city128_stat;
+        typedef MultiLevelHashMap< cds::gc::DHP, Key, Value, traits_MultiLevelHashMap_city128_stat >    MultiLevelHashMap_dhp_city128_stat;
+
     };
 
     template <typename GC, typename K, typename T, typename Traits >
     };
 
     template <typename GC, typename K, typename T, typename Traits >