286 lines
8.2 KiB
C
286 lines
8.2 KiB
C
/*
|
||
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);
|
||
}
|