Files
StarForth/src/starkernel/hash/xxhash64.c
T

286 lines
8.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This 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.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* xxhash64.c - Freestanding xxHash64 Implementation
*
* Based on xxHash by Yann Collet (BSD-2-Clause).
* Adapted for freestanding kernel use - no libc dependency.
*
* xxHash is an extremely fast non-cryptographic hash algorithm.
* https://github.com/Cyan4973/xxHash
*/
#include "starkernel/xxhash64.h"
/*===========================================================================
* xxHash64 Constants
*===========================================================================*/
#define XXHASH64_PRIME1 0x9E3779B185EBCA87ULL
#define XXHASH64_PRIME2 0xC2B2AE3D27D4EB4FULL
#define XXHASH64_PRIME3 0x165667B19E3779F9ULL
#define XXHASH64_PRIME4 0x85EBCA77C2B2AE63ULL
#define XXHASH64_PRIME5 0x27D4EB2F165667C5ULL
/*===========================================================================
* Helper Functions
*===========================================================================*/
/**
* Rotate left 64-bit
*/
static inline uint64_t xxh64_rotl(uint64_t x, int r) {
return (x << r) | (x >> (64 - r));
}
/**
* Read 64-bit little-endian (unaligned safe)
*/
static inline uint64_t xxh64_read64(const uint8_t *p) {
return ((uint64_t)p[0]) |
((uint64_t)p[1] << 8) |
((uint64_t)p[2] << 16) |
((uint64_t)p[3] << 24) |
((uint64_t)p[4] << 32) |
((uint64_t)p[5] << 40) |
((uint64_t)p[6] << 48) |
((uint64_t)p[7] << 56);
}
/**
* Read 32-bit little-endian (unaligned safe)
*/
static inline uint32_t xxh64_read32(const uint8_t *p) {
return ((uint32_t)p[0]) |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
}
/**
* Round function
*/
static inline uint64_t xxh64_round(uint64_t acc, uint64_t input) {
acc += input * XXHASH64_PRIME2;
acc = xxh64_rotl(acc, 31);
acc *= XXHASH64_PRIME1;
return acc;
}
/**
* Merge accumulator
*/
static inline uint64_t xxh64_merge_round(uint64_t acc, uint64_t val) {
val = xxh64_round(0, val);
acc ^= val;
acc = acc * XXHASH64_PRIME1 + XXHASH64_PRIME4;
return acc;
}
/**
* Avalanche finalization
*/
static inline uint64_t xxh64_avalanche(uint64_t h) {
h ^= h >> 33;
h *= XXHASH64_PRIME2;
h ^= h >> 29;
h *= XXHASH64_PRIME3;
h ^= h >> 32;
return h;
}
/*===========================================================================
* One-Shot Hashing
*===========================================================================*/
uint64_t xxhash64(const void *data, size_t len, uint64_t seed) {
const uint8_t *p = (const uint8_t *)data;
const uint8_t *end = p + len;
uint64_t h64;
if (len >= 32) {
const uint8_t *limit = end - 32;
uint64_t v1 = seed + XXHASH64_PRIME1 + XXHASH64_PRIME2;
uint64_t v2 = seed + XXHASH64_PRIME2;
uint64_t v3 = seed + 0;
uint64_t v4 = seed - XXHASH64_PRIME1;
do {
v1 = xxh64_round(v1, xxh64_read64(p));
p += 8;
v2 = xxh64_round(v2, xxh64_read64(p));
p += 8;
v3 = xxh64_round(v3, xxh64_read64(p));
p += 8;
v4 = xxh64_round(v4, xxh64_read64(p));
p += 8;
} while (p <= limit);
h64 = xxh64_rotl(v1, 1) + xxh64_rotl(v2, 7) +
xxh64_rotl(v3, 12) + xxh64_rotl(v4, 18);
h64 = xxh64_merge_round(h64, v1);
h64 = xxh64_merge_round(h64, v2);
h64 = xxh64_merge_round(h64, v3);
h64 = xxh64_merge_round(h64, v4);
} else {
h64 = seed + XXHASH64_PRIME5;
}
h64 += (uint64_t)len;
/* Process remaining 8-byte chunks */
while (p + 8 <= end) {
uint64_t k1 = xxh64_round(0, xxh64_read64(p));
h64 ^= k1;
h64 = xxh64_rotl(h64, 27) * XXHASH64_PRIME1 + XXHASH64_PRIME4;
p += 8;
}
/* Process remaining 4-byte chunk */
if (p + 4 <= end) {
h64 ^= (uint64_t)xxh64_read32(p) * XXHASH64_PRIME1;
h64 = xxh64_rotl(h64, 23) * XXHASH64_PRIME2 + XXHASH64_PRIME3;
p += 4;
}
/* Process remaining bytes */
while (p < end) {
h64 ^= (uint64_t)(*p) * XXHASH64_PRIME5;
h64 = xxh64_rotl(h64, 11) * XXHASH64_PRIME1;
p++;
}
return xxh64_avalanche(h64);
}
/*===========================================================================
* Streaming API
*===========================================================================*/
void xxhash64_reset(XXHash64State *state, uint64_t seed) {
state->total_len = 0;
state->v1 = seed + XXHASH64_PRIME1 + XXHASH64_PRIME2;
state->v2 = seed + XXHASH64_PRIME2;
state->v3 = seed + 0;
state->v4 = seed - XXHASH64_PRIME1;
state->buffer_size = 0;
state->seed = seed;
}
void xxhash64_update(XXHash64State *state, const void *data, size_t len) {
const uint8_t *p = (const uint8_t *)data;
const uint8_t *end = p + len;
state->total_len += len;
/* Fill buffer if we have leftover data */
if (state->buffer_size + len < 32) {
/* Not enough to process, just buffer it */
while (p < end) {
state->buffer[state->buffer_size++] = *p++;
}
return;
}
/* Process buffered data first */
if (state->buffer_size > 0) {
uint32_t fill = 32 - state->buffer_size;
for (uint32_t i = 0; i < fill; i++) {
state->buffer[state->buffer_size + i] = p[i];
}
p += fill;
state->v1 = xxh64_round(state->v1, xxh64_read64(state->buffer));
state->v2 = xxh64_round(state->v2, xxh64_read64(state->buffer + 8));
state->v3 = xxh64_round(state->v3, xxh64_read64(state->buffer + 16));
state->v4 = xxh64_round(state->v4, xxh64_read64(state->buffer + 24));
state->buffer_size = 0;
}
/* Process 32-byte blocks */
if (p + 32 <= end) {
const uint8_t *limit = end - 32;
do {
state->v1 = xxh64_round(state->v1, xxh64_read64(p));
p += 8;
state->v2 = xxh64_round(state->v2, xxh64_read64(p));
p += 8;
state->v3 = xxh64_round(state->v3, xxh64_read64(p));
p += 8;
state->v4 = xxh64_round(state->v4, xxh64_read64(p));
p += 8;
} while (p <= limit);
}
/* Buffer remaining bytes */
while (p < end) {
state->buffer[state->buffer_size++] = *p++;
}
}
uint64_t xxhash64_digest(const XXHash64State *state) {
const uint8_t *p = state->buffer;
const uint8_t *end = p + state->buffer_size;
uint64_t h64;
if (state->total_len >= 32) {
h64 = xxh64_rotl(state->v1, 1) + xxh64_rotl(state->v2, 7) +
xxh64_rotl(state->v3, 12) + xxh64_rotl(state->v4, 18);
h64 = xxh64_merge_round(h64, state->v1);
h64 = xxh64_merge_round(h64, state->v2);
h64 = xxh64_merge_round(h64, state->v3);
h64 = xxh64_merge_round(h64, state->v4);
} else {
h64 = state->seed + XXHASH64_PRIME5;
}
h64 += state->total_len;
/* Process remaining 8-byte chunks */
while (p + 8 <= end) {
uint64_t k1 = xxh64_round(0, xxh64_read64(p));
h64 ^= k1;
h64 = xxh64_rotl(h64, 27) * XXHASH64_PRIME1 + XXHASH64_PRIME4;
p += 8;
}
/* Process remaining 4-byte chunk */
if (p + 4 <= end) {
h64 ^= (uint64_t)xxh64_read32(p) * XXHASH64_PRIME1;
h64 = xxh64_rotl(h64, 23) * XXHASH64_PRIME2 + XXHASH64_PRIME3;
p += 4;
}
/* Process remaining bytes */
while (p < end) {
h64 ^= (uint64_t)(*p) * XXHASH64_PRIME5;
h64 = xxh64_rotl(h64, 11) * XXHASH64_PRIME1;
p++;
}
return xxh64_avalanche(h64);
}