Skip to content

Instantly share code, notes, and snippets.

@tralamazza
Created May 22, 2026 23:32
Show Gist options
  • Select an option

  • Save tralamazza/3edacc7612427aec5958c4dd66670f94 to your computer and use it in GitHub Desktop.

Select an option

Save tralamazza/3edacc7612427aec5958c4dd66670f94 to your computer and use it in GitHub Desktop.
xxHash32 progressive: length-dispatched unkeyed 32-bit hash optimized for small inputs on embedded (Cortex-M)
#ifndef XXHASH_PROGRESSIVE_H
#define XXHASH_PROGRESSIVE_H
#include <stddef.h>
#include <stdint.h>
#define XXHP_PRIME32_1 UINT32_C(0x9e3779b1)
#define XXHP_PRIME32_2 UINT32_C(0x85ebca77)
#define XXHP_PRIME32_3 UINT32_C(0xc2b2ae3d)
#define XXHP_PRIME32_4 UINT32_C(0x27d4eb2f)
#define XXHP_PRIME32_5 UINT32_C(0x165667b1)
#define XXHP_TINY_MAX 31u
#define XXHP_MEDIUM_MAX 127u
static uint32_t xxhp_rotl32(uint32_t x, unsigned r) {
return (x << r) | (x >> (32u - r));
}
static uint32_t xxhp_readle32(const uint8_t *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static uint32_t xxhp_round(uint32_t acc, uint32_t input) {
acc += input * XXHP_PRIME32_2;
acc = xxhp_rotl32(acc, 13);
acc *= XXHP_PRIME32_1;
return acc;
}
static uint32_t xxhp_avalanche(uint32_t x) {
x ^= x >> 15;
x *= XXHP_PRIME32_2;
x ^= x >> 13;
x *= XXHP_PRIME32_3;
x ^= x >> 16;
return x;
}
static uint32_t xxhp_tiny(const uint8_t *p, size_t len, uint32_t seed) {
const uint8_t *end = p + len;
uint32_t h32 = seed + XXHP_PRIME32_5;
h32 += (uint32_t)len;
while (p + 4 <= end) {
h32 += xxhp_readle32(p) * XXHP_PRIME32_3;
h32 = xxhp_rotl32(h32, 17) * XXHP_PRIME32_4;
p += 4;
}
while (p < end) {
h32 += (uint32_t)*p * XXHP_PRIME32_5;
h32 = xxhp_rotl32(h32, 11) * XXHP_PRIME32_1;
p++;
}
return xxhp_avalanche(h32);
}
static uint32_t xxhp_large(const uint8_t *p, size_t len, uint32_t seed) {
const uint8_t *end = p + len;
const uint8_t *limit;
uint32_t h32;
uint32_t v1, v2, v3, v4;
if (len >= 16u) {
limit = end - 16u;
v1 = seed + XXHP_PRIME32_1 + XXHP_PRIME32_2;
v2 = seed + XXHP_PRIME32_2;
v3 = seed;
v4 = seed - XXHP_PRIME32_1;
do {
v1 = xxhp_round(v1, xxhp_readle32(p));
v2 = xxhp_round(v2, xxhp_readle32(p + 4));
v3 = xxhp_round(v3, xxhp_readle32(p + 8));
v4 = xxhp_round(v4, xxhp_readle32(p + 12));
p += 16;
} while (p <= limit);
h32 = xxhp_rotl32(v1, 1) + xxhp_rotl32(v2, 7) +
xxhp_rotl32(v3, 12) + xxhp_rotl32(v4, 18);
} else {
h32 = seed + XXHP_PRIME32_5;
}
h32 += (uint32_t)len;
while (p + 4 <= end) {
h32 += xxhp_readle32(p) * XXHP_PRIME32_3;
h32 = xxhp_rotl32(h32, 17) * XXHP_PRIME32_4;
p += 4;
}
while (p < end) {
h32 += (uint32_t)*p * XXHP_PRIME32_5;
h32 = xxhp_rotl32(h32, 11) * XXHP_PRIME32_1;
p++;
}
return xxhp_avalanche(h32);
}
static uint32_t xxhp_medium(const uint8_t *p, size_t len, uint32_t seed) {
const uint8_t *end = p + len;
const uint8_t *limit = end - 16u;
uint32_t h32;
uint32_t v1 = seed + XXHP_PRIME32_1 + XXHP_PRIME32_2;
uint32_t v2 = seed + XXHP_PRIME32_2;
uint32_t v3 = seed;
uint32_t v4 = seed - XXHP_PRIME32_1;
do {
v1 = xxhp_round(v1, xxhp_readle32(p));
v2 = xxhp_round(v2, xxhp_readle32(p + 4));
v3 = xxhp_round(v3, xxhp_readle32(p + 8));
v4 = xxhp_round(v4, xxhp_readle32(p + 12));
p += 16;
} while (p <= limit);
h32 = xxhp_rotl32(v1, 1) + xxhp_rotl32(v2, 7) +
xxhp_rotl32(v3, 12) + xxhp_rotl32(v4, 18);
h32 += (uint32_t)len;
while (p + 4 <= end) {
h32 += xxhp_readle32(p) * XXHP_PRIME32_3;
h32 = xxhp_rotl32(h32, 17) * XXHP_PRIME32_4;
p += 4;
}
while (p < end) {
h32 += (uint32_t)*p * XXHP_PRIME32_5;
h32 = xxhp_rotl32(h32, 11) * XXHP_PRIME32_1;
p++;
}
return xxhp_avalanche(h32);
}
static uint32_t xxh32_progressive(const void *input, size_t len, uint32_t seed) {
if (len == 0u) return seed + XXHP_PRIME32_5;
if (len <= XXHP_TINY_MAX) return xxhp_tiny((const uint8_t *)input, len, seed);
if (len <= XXHP_MEDIUM_MAX) return xxhp_medium((const uint8_t *)input, len, seed);
return xxhp_large((const uint8_t *)input, len, seed);
}
#endif /* XXHASH_PROGRESSIVE_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment