Skip to main content

reifydb_runtime/
hash.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! Hash types and functions for ReifyDB.
5//!
6//! Provides xxHash3 hashing using pure Rust implementation that works
7//! on both native and WASM targets.
8
9use core::hash::{Hash, Hasher};
10
11use serde::{Deserialize, Serialize};
12use xxhash_rust::xxh3;
13
14#[repr(transparent)]
15#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct Hash64(pub u64);
18
19impl From<u64> for Hash64 {
20	fn from(value: u64) -> Self {
21		Hash64(value)
22	}
23}
24
25impl From<Hash64> for u64 {
26	fn from(hash: Hash64) -> Self {
27		hash.0
28	}
29}
30
31impl Hash for Hash64 {
32	fn hash<H: Hasher>(&self, state: &mut H) {
33		state.write_u64(self.0)
34	}
35}
36
37#[repr(transparent)]
38#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
39#[serde(transparent)]
40pub struct Hash128(pub u128);
41
42impl From<u128> for Hash128 {
43	fn from(value: u128) -> Self {
44		Hash128(value)
45	}
46}
47
48impl From<Hash128> for u128 {
49	fn from(hash: Hash128) -> Self {
50		hash.0
51	}
52}
53
54impl Hash for Hash128 {
55	fn hash<H: Hasher>(&self, state: &mut H) {
56		state.write_u128(self.0)
57	}
58}
59
60impl Hash128 {
61	/// Convert to a 32-character lowercase hex string (no prefix).
62	#[inline]
63	pub fn to_hex_string(self) -> String {
64		const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
65		let mut buf = String::with_capacity(32);
66		let val = self.0;
67		for i in (0..32).rev() {
68			let nibble = ((val >> (i * 4)) & 0xf) as usize;
69			buf.push(HEX_DIGITS[nibble] as char);
70		}
71		buf
72	}
73
74	/// Convert to a 34-character lowercase hex string with "0x" prefix.
75	#[inline]
76	pub fn to_hex_string_prefixed(self) -> String {
77		const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
78		let mut buf = String::with_capacity(34);
79		buf.push_str("0x");
80		let val = self.0;
81		for i in (0..32).rev() {
82			let nibble = ((val >> (i * 4)) & 0xf) as usize;
83			buf.push(HEX_DIGITS[nibble] as char);
84		}
85		buf
86	}
87}
88
89/// Compute xxHash3 64-bit hash of data.
90#[inline]
91pub fn xxh3_64(data: &[u8]) -> Hash64 {
92	Hash64(xxh3::xxh3_64(data))
93}
94
95/// Compute xxHash3 128-bit hash of data.
96#[inline]
97pub fn xxh3_128(data: &[u8]) -> Hash128 {
98	Hash128(xxh3::xxh3_128(data))
99}
100
101#[cfg(test)]
102mod tests {
103	use super::*;
104
105	#[test]
106	fn test_xxh3_64() {
107		let data = b"hello world";
108		let hash = xxh3_64(data);
109		// xxh3_64 should be deterministic
110		assert_eq!(hash, xxh3_64(data));
111		assert_ne!(hash, xxh3_64(b"different data"));
112	}
113
114	#[test]
115	fn test_xxh3_128() {
116		let data = b"hello world";
117		let hash = xxh3_128(data);
118		// xxh3_128 should be deterministic
119		assert_eq!(hash, xxh3_128(data));
120		assert_ne!(hash, xxh3_128(b"different data"));
121	}
122
123	#[test]
124	fn test_hash64_conversions() {
125		let value: u64 = 12345;
126		let hash = Hash64::from(value);
127		assert_eq!(u64::from(hash), value);
128	}
129
130	#[test]
131	fn test_hash128_conversions() {
132		let value: u128 = 123456789;
133		let hash = Hash128::from(value);
134		assert_eq!(u128::from(hash), value);
135	}
136}