aboutsummaryrefslogtreecommitdiff
path: root/lib/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hash.c')
-rw-r--r--lib/hash.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/lib/hash.c b/lib/hash.c
new file mode 100644
index 0000000..6a969a8
--- /dev/null
+++ b/lib/hash.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "impl.h"
+
+#define rotl(v, x) (((v) << (x)) | ((v) >> (64 - (x))))
+
+#define ROUND do { \
+ v0 += v1; \
+ v2 += v3; \
+ v1 = rotl(v1, 13); \
+ v3 = rotl(v3, 16); \
+ v1 ^= v0; \
+ v3 ^= v2; \
+ v0 = rotl(v0, 32); \
+ v2 += v1; \
+ v0 += v3; \
+ v1 = rotl(v1, 17); \
+ v3 = rotl(v3, 21); \
+ v1 ^= v2; \
+ v3 ^= v0; \
+ v2 = rotl(v2, 32); \
+} while(0)
+
+uint64_t hkvs_hash(const char *hk, const char *data, size_t data_size) {
+ uint64_t hk0 = hkvs_read_u64(hk + 0);
+ uint64_t hk1 = hkvs_read_u64(hk + 8);
+ uint64_t v0 = (uint64_t)0x736f6d6570736575 ^ hk0;
+ uint64_t v1 = (uint64_t)0x646f72616e646f6d ^ hk1;
+ uint64_t v2 = (uint64_t)0x6c7967656e657261 ^ hk0;
+ uint64_t v3 = (uint64_t)0x7465646279746573 ^ hk1;
+ uint64_t last = (uint64_t)data_size << 56;
+ while(data_size >= 8) {
+ uint64_t m = hkvs_read_u64(data);
+ v3 ^= m;
+ ROUND;
+ v0 ^= m;
+ data += 8;
+ data_size -= 8;
+ }
+
+ switch(data_size) {
+ case 7:
+ last |= (uint64_t)(uint8_t)data[6] << 48;
+ HKVS_FALLTHROUGH;
+ case 6:
+ last |= (uint64_t)(uint8_t)data[5] << 40;
+ HKVS_FALLTHROUGH;
+ case 5:
+ last |= (uint64_t)(uint8_t)data[4] << 32;
+ HKVS_FALLTHROUGH;
+ case 4:
+ last |= (uint64_t)(uint8_t)data[3] << 24;
+ HKVS_FALLTHROUGH;
+ case 3:
+ last |= (uint64_t)(uint8_t)data[2] << 16;
+ HKVS_FALLTHROUGH;
+ case 2:
+ last |= (uint64_t)(uint8_t)data[1] << 8;
+ HKVS_FALLTHROUGH;
+ case 1:
+ last |= (uint64_t)(uint8_t)data[0];
+ HKVS_FALLTHROUGH;
+ case 0:
+ break;
+ default:
+ __builtin_unreachable();
+ }
+
+ v3 ^= last;
+ ROUND;
+ v0 ^= last;
+
+ v2 ^= 0xff;
+ ROUND;
+ ROUND;
+ ROUND;
+ return v0 ^ v1 ^ v2 ^ v3;
+}