Skip to main content

reifydb_runtime/
hash.rs

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