From 8f5604dde6489f48f1c79c14699552507b7e1302 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 3 Nov 2020 11:43:35 +1000 Subject: [PATCH] hash: add halfsipmix --- CMakeLists.txt | 3 + hash/halfsipmix.cpp | 1 + hash/halfsipmix.hpp | 147 +++++++++++++++++++++++++++++++++++++++ test/hash/halfsipmix.cpp | 98 ++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 hash/halfsipmix.cpp create mode 100644 hash/halfsipmix.hpp create mode 100644 test/hash/halfsipmix.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e1e94e8c..488821f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,6 +383,8 @@ list ( hash/fletcher.hpp hash/fnv1a.cpp hash/fnv1a.hpp + hash/halfsipmix.cpp + hash/halfsipmix.hpp hash/murmur/common.cpp hash/murmur/common.hpp hash/murmur.hpp @@ -698,6 +700,7 @@ if (TESTS) hash/crc hash/fasthash hash/fnv1a + hash/halfsipmix hash/murmur hash/siphash hash/table diff --git a/hash/halfsipmix.cpp b/hash/halfsipmix.cpp new file mode 100644 index 00000000..e5082aa0 --- /dev/null +++ b/hash/halfsipmix.cpp @@ -0,0 +1 @@ +#include "./halfsipmix.hpp" \ No newline at end of file diff --git a/hash/halfsipmix.hpp b/hash/halfsipmix.hpp new file mode 100644 index 00000000..c2cf8f91 --- /dev/null +++ b/hash/halfsipmix.hpp @@ -0,0 +1,147 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2020, Danny Robson + * + * Derived from the CC0 reference implementation by: + * Jean-Philippe Aumasson + */ + +#pragma once + +#include "../std.hpp" + +#include +#include + + +namespace cruft::hash { + template < + typename ResultT, + int CRoundsV, + int DRoundsV, + typename = std::enable_if_t || std::is_same_v> + > + struct halfsiphash { + u32 rotl (u32 x, u32 b) + { + return ((x) << (b)) | ((x) >> (32 - (b))); + } + + u32 v0, v1, v2, v3; + + constexpr halfsiphash (u32 k0, u32 k1) + { + v0 = 0 ^ k0; + v1 = 0 ^ k1; + v2 = 0x6c796765 ^ k0; + v3 = 0x74656462 ^ k1; + + if constexpr (std::is_same_v) + v1 ^= 0xee; + } + + constexpr void round (void) + { + v0 += v1; + v1 = rotl (v1, 5); + v1 ^= v0; + v0 = rotl (v0, 16); + v2 += v3; + v3 = rotl (v3, 8); + v3 ^= v2; + v0 += v3; + v3 = rotl (v3, 7); + v3 ^= v0; + v2 += v1; + v1 = rotl (v1, 13); + v1 ^= v2; + v2 = rotl (v2, 16); + } + + constexpr void put (u32 word) + { + v3 ^= word; + for (int i = 0; i < CRoundsV; ++i) + round (); + v0 ^= word; + } + + constexpr ResultT + operator() ( + std::span in + ) { + for (auto const &m: in) + put (m); + + return finish (u32 (in.size ())); + } + + template + requires (std::is_same_v, u32> && ...) + constexpr ResultT + operator() (ArgsT &&...vals) + { + (put (vals), ...); + return finish (sizeof ...(vals)); + } + + constexpr ResultT + finish (u32 count) + { + u32 const b = u32(count * sizeof (u32)) << 24; + + v3 ^= b; + + for (int i = 0; i < CRoundsV; ++i) + round (); + + v0 ^= b; + + if (std::is_same_v) + v2 ^= 0xee; + else + v2 ^= 0xff; + + for (int i = 0; i < DRoundsV; ++i) + round (); + + ResultT res = v1 ^ v3; + + if constexpr (std::is_same_v) + return res; + + v1 ^= 0xdd; + + for (int i = 0; i < DRoundsV; ++i) + round (); + + res |= u64(v1 ^ v3) << 32; + + return res; + } + }; + + + template < + int CRoundsV, + int DRoundsV, + typename ...ValuesT + > + requires (std::is_same_v, u32> && ...) + inline constexpr + u32 halfsipmix (u32 key0, u32 key1, ValuesT &&...vals) + { + return halfsiphash (key0, key1) (vals...); + } + + + template + decltype(auto) + halfsipmix24 (ValueT &&...vals) + { + return halfsipmix<2, 4> (vals...); + } +}; diff --git a/test/hash/halfsipmix.cpp b/test/hash/halfsipmix.cpp new file mode 100644 index 00000000..31a560e5 --- /dev/null +++ b/test/hash/halfsipmix.cpp @@ -0,0 +1,98 @@ +#include "hash/halfsipmix.hpp" +#include "tap.hpp" + +#include + + +static constexpr u32 +vectors_hsip32[64] = { + 0x5b9f35a9, + 0x89466e2a, + 0x8f84b8d0, + 0x2f87f057, + 0x84acb5d9, + 0x45104de3, + 0x630c4027, + 0x004064b0, + 0x1221e7fe, + 0xecc65ce7, + 0x4018bcb6, + 0x48d50577, + 0xe32ce169, + 0xd4f3853b, + 0x40b9c5a5, + 0x736ce7d4, +}; + + +static constexpr u64 +vectors_hsip64[64] = { + 0xc83cb8b9591f8d21, + 0xa12ee55b178ae7d5, + 0x8c85e4bc20e8feed, + 0x99c7f5ae9f1fc77b, + 0xb5f37b5fd2aa3673, + 0xdba7ee6f0a2bf51b, + 0xf1a63fae45107470, + 0xb516001efb5f922d, + 0x6c6211d8469d7028, + 0xdc7642ec407ad686, + 0x4caec8671cc8385b, + 0x5ab1dc27adf3301e, + 0x3e3ea94bc0a8eaa9, + 0xe150f598795a4402, + 0x1d5ff142f992a4a1, + 0x60e426bf902876d6, +}; + + +static constexpr +u32 SRC[64] { + 0x03020100, + 0x07060504, + 0x0b0a0908, + 0x0f0e0d0c, + 0x13121110, + 0x17161514, + 0x1b1a1918, + 0x1f1e1d1c, + 0x23222120, + 0x27262524, + 0x2b2a2928, + 0x2f2e2d2c, + 0x33323130, + 0x37363534, + 0x3b3a3938, + 0x3f3e3d3c, +}; + + +template +int siphash_test (ValueT const *v) { + bool any_failed = false; + int fails = 0; + + u32 const k0 = 0x03020100; + u32 const k1 = 0x07060504; + + for (int i = 0; i < 16; ++i) { + ValueT res = cruft::hash::halfsiphash{k0, k1} (std::span (SRC, SRC + i)); + + if (v[i] != res) { + fails++; + any_failed = true; + } + } + + return any_failed; +} + + +int main () +{ + cruft::TAP::logger tap; + tap.expect_eq (siphash_test (vectors_hsip32), 0, "siphash-2-4: u32"); + tap.expect_eq (siphash_test (vectors_hsip64), 0, "siphash-2-4: u64"); + + return tap.status (); +} \ No newline at end of file