From: Timo Sirainen Date: Mon, 13 Apr 2026 10:30:03 +0000 (+0200) Subject: [PATCH 01/12] lib: Add XXH64 hash implementation X-Git-Tag: archive/raspbian/1%2.4.1+dfsg1-6+rpi1+deb13u7^2~41 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=4aae229d991a35b1b88eb14dd01a9f83c40ccd66;p=dovecot.git [PATCH 01/12] lib: Add XXH64 hash implementation Adds a streaming XXH64 (non-cryptographic) hash with init/loop/result API, a one-shot xxh64_data(), and an xxh64_to_32() XOR-fold inline. Registered in hash_methods[] for use via the generic hash_method API. Gbp-Pq: Name 0001-lib-Add-XXH64-hash-implementation.patch --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 6da8a30..eb9b9dd 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -177,6 +177,7 @@ liblib_la_SOURCES = \ sha1.c \ sha2.c \ sha3.c \ + xxh64.c \ sleep.c \ sort.c \ stats-dist.c \ @@ -337,6 +338,7 @@ headers = \ sha1.h \ sha2.h \ sha3.h \ + xxh64.h \ sleep.h \ sort.h \ stats-dist.h \ diff --git a/src/lib/hash-method.c b/src/lib/hash-method.c index d7a6bed..bdeefba 100644 --- a/src/lib/hash-method.c +++ b/src/lib/hash-method.c @@ -6,6 +6,7 @@ #include "sha1.h" #include "sha2.h" #include "sha3.h" +#include "xxh64.h" #include "hash-method.h" const struct hash_method *hash_method_lookup(const char *name) @@ -93,6 +94,7 @@ const struct hash_method *hash_methods[] = { &hash_method_sha512, &hash_method_sha3_256, &hash_method_sha3_512, + &hash_method_xxh64, &hash_method_size, NULL }; diff --git a/src/lib/test-hash-method.c b/src/lib/test-hash-method.c index 6592ec9..6384dc9 100644 --- a/src/lib/test-hash-method.c +++ b/src/lib/test-hash-method.c @@ -425,6 +425,39 @@ static void test_hash_methods_fips() { "\x6d\xf6\x54\x5a\x1c\xe8\xba\x00", 512 / 8, }, + /* xxh64 reference vectors (seed=0) */ + { "xxh64", + "", + 0, + 1, + "\xef\x46\xdb\x37\x51\xd8\xe9\x99", + 64 / 8 + }, + { "xxh64", + "abc", + 3, + 1, + "\x44\xbc\x2c\xf5\xad\x77\x09\x99", + 64 / 8 + }, + /* 62 bytes: exercises the >= 32-byte accumulator path */ + { "xxh64", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" + "ghijklmnopqrstuvwxyz0123456789", + 62, + 1, + "\xba\x76\x1a\x9a\xa8\x0e\x0d\x36", + 64 / 8 + }, + /* 16 bytes × 3 rounds: exercises chunked input across the 32-byte block boundary */ + { "xxh64", + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + 16, + 3, + "\x32\x0d\xcd\xab\xd8\x29\x7a\x9e", + 64 / 8 + }, }; for(size_t i = 0; i < N_ELEMENTS(test_vectors); i++) { diff --git a/src/lib/xxh64.c b/src/lib/xxh64.c new file mode 100644 index 0000000..1c17833 --- /dev/null +++ b/src/lib/xxh64.c @@ -0,0 +1,184 @@ +/* Copyright (c) 2025 Dovecot authors, see the included COPYING file */ + +/* XXHash64 - Fast non-cryptographic hash function. + * Based on Yann Collet's public domain XXHash64 algorithm. + * Reference: https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md + */ + +#include "lib.h" +#include "xxh64.h" + +#define XXH64_PRIME1 UINT64_C(0x9E3779B185EBCA87) +#define XXH64_PRIME2 UINT64_C(0xC2B2AE3D27D4EB4F) +#define XXH64_PRIME3 UINT64_C(0x165667B19E3779F9) +#define XXH64_PRIME4 UINT64_C(0x85EBCA77C2B27589) +#define XXH64_PRIME5 UINT64_C(0x27D4EB2F165667C5) + +#define XXH64_ROTL(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) + +static inline uint64_t xxh64_read64(const void *p) +{ + uint64_t v; + memcpy(&v, p, sizeof(v)); + return v; +} + +static inline uint32_t xxh64_read32(const void *p) +{ + uint32_t v; + memcpy(&v, p, sizeof(v)); + return v; +} + +static uint64_t ATTR_UNSIGNED_WRAPS xxh64_round(uint64_t acc, uint64_t input) +{ + acc += input * XXH64_PRIME2; + acc = XXH64_ROTL(acc, 31); + acc *= XXH64_PRIME1; + return acc; +} + +static uint64_t ATTR_UNSIGNED_WRAPS xxh64_merge_acc(uint64_t h64, uint64_t acc) +{ + acc = xxh64_round(0, acc); + h64 ^= acc; + h64 = h64 * XXH64_PRIME1 + XXH64_PRIME4; + return h64; +} + +void ATTR_UNSIGNED_WRAPS xxh64_init(struct xxh64_context *ctx, uint64_t seed) +{ + ctx->seed = seed; + ctx->v1 = seed + XXH64_PRIME1 + XXH64_PRIME2; + ctx->v2 = seed + XXH64_PRIME2; + ctx->v3 = seed; + ctx->v4 = seed - XXH64_PRIME1; + ctx->total_len = 0; + ctx->buf_used = 0; +} + +void ATTR_UNSIGNED_WRAPS xxh64_loop(struct xxh64_context *ctx, const void *data, size_t size) +{ + const unsigned char *p = data; + const unsigned char *end = p + size; + + ctx->total_len += size; + + if (ctx->buf_used + size < 32) { + memcpy(ctx->buf + ctx->buf_used, p, size); + ctx->buf_used += size; + return; + } + + if (ctx->buf_used > 0) { + size_t fill = 32 - ctx->buf_used; + memcpy(ctx->buf + ctx->buf_used, p, fill); + p += fill; + ctx->v1 = xxh64_round(ctx->v1, xxh64_read64(ctx->buf)); + ctx->v2 = xxh64_round(ctx->v2, xxh64_read64(ctx->buf + 8)); + ctx->v3 = xxh64_round(ctx->v3, xxh64_read64(ctx->buf + 16)); + ctx->v4 = xxh64_round(ctx->v4, xxh64_read64(ctx->buf + 24)); + ctx->buf_used = 0; + } + + while (p + 32 <= end) { + ctx->v1 = xxh64_round(ctx->v1, xxh64_read64(p)); + ctx->v2 = xxh64_round(ctx->v2, xxh64_read64(p + 8)); + ctx->v3 = xxh64_round(ctx->v3, xxh64_read64(p + 16)); + ctx->v4 = xxh64_round(ctx->v4, xxh64_read64(p + 24)); + p += 32; + } + + if (p < end) { + ctx->buf_used = (unsigned int)(end - p); + memcpy(ctx->buf, p, ctx->buf_used); + } +} + +uint64_t ATTR_UNSIGNED_WRAPS xxh64_result(struct xxh64_context *ctx) +{ + const unsigned char *p = ctx->buf; + const unsigned char *end = p + ctx->buf_used; + uint64_t h64; + + if (ctx->total_len >= 32) { + h64 = XXH64_ROTL(ctx->v1, 1) + XXH64_ROTL(ctx->v2, 7) + + XXH64_ROTL(ctx->v3, 12) + XXH64_ROTL(ctx->v4, 18); + h64 = xxh64_merge_acc(h64, ctx->v1); + h64 = xxh64_merge_acc(h64, ctx->v2); + h64 = xxh64_merge_acc(h64, ctx->v3); + h64 = xxh64_merge_acc(h64, ctx->v4); + } else { + h64 = ctx->seed + XXH64_PRIME5; + } + + h64 += ctx->total_len; + + while (p + 8 <= end) { + h64 ^= xxh64_round(0, xxh64_read64(p)); + h64 = XXH64_ROTL(h64, 27) * XXH64_PRIME1 + XXH64_PRIME4; + p += 8; + } + if (p + 4 <= end) { + h64 ^= (uint64_t)xxh64_read32(p) * XXH64_PRIME1; + h64 = XXH64_ROTL(h64, 23) * XXH64_PRIME2 + XXH64_PRIME3; + p += 4; + } + while (p < end) { + h64 ^= (uint64_t)*p * XXH64_PRIME5; + h64 = XXH64_ROTL(h64, 11) * XXH64_PRIME1; + p++; + } + + /* Avalanche */ + h64 ^= h64 >> 33; + h64 *= XXH64_PRIME2; + h64 ^= h64 >> 29; + h64 *= XXH64_PRIME3; + h64 ^= h64 >> 32; + + return h64; +} + +uint64_t xxh64_data(const void *data, size_t size, uint64_t seed) +{ + struct xxh64_context ctx; + + xxh64_init(&ctx, seed); + xxh64_loop(&ctx, data, size); + return xxh64_result(&ctx); +} + +static void xxh64_hash_init(void *context) +{ + xxh64_init(context, 0); +} + +static void xxh64_hash_loop(void *context, const void *data, size_t size) +{ + xxh64_loop(context, data, size); +} + +static void xxh64_hash_result(void *context, unsigned char *digest_r) +{ + uint64_t hash = xxh64_result(context); + + digest_r[0] = (hash >> 56) & 0xff; + digest_r[1] = (hash >> 48) & 0xff; + digest_r[2] = (hash >> 40) & 0xff; + digest_r[3] = (hash >> 32) & 0xff; + digest_r[4] = (hash >> 24) & 0xff; + digest_r[5] = (hash >> 16) & 0xff; + digest_r[6] = (hash >> 8) & 0xff; + digest_r[7] = (hash >> 0) & 0xff; +} + +const struct hash_method hash_method_xxh64 = { + .name = "xxh64", + .block_size = 32, + .context_size = sizeof(struct xxh64_context), + .digest_size = XXH64_RESULTLEN, + .init = xxh64_hash_init, + .loop = xxh64_hash_loop, + .result = xxh64_hash_result, +}; diff --git a/src/lib/xxh64.h b/src/lib/xxh64.h new file mode 100644 index 0000000..7beb4ca --- /dev/null +++ b/src/lib/xxh64.h @@ -0,0 +1,30 @@ +#ifndef XXH64_H +#define XXH64_H + +#include "hash-method.h" + +#define XXH64_RESULTLEN 8 + +struct xxh64_context { + uint64_t seed; + uint64_t v1, v2, v3, v4; + uint64_t total_len; + unsigned char buf[32]; + unsigned int buf_used; +}; + +void xxh64_init(struct xxh64_context *ctx, uint64_t seed); +void xxh64_loop(struct xxh64_context *ctx, const void *data, size_t size); +uint64_t xxh64_result(struct xxh64_context *ctx); + +uint64_t xxh64_data(const void *data, size_t size, uint64_t seed) ATTR_PURE; + +/* XOR-fold the 64-bit hash to 32 bits */ +static inline uint32_t xxh64_to_32(uint64_t hash) +{ + return (uint32_t)(hash ^ (hash >> 32)); +} + +extern const struct hash_method hash_method_xxh64; + +#endif