waddling_errors_hash/algorithm.rs
1//! WDP-compliant hash algorithm
2//!
3//! This module provides the WDP-specified xxHash3 algorithm for error code hashing.
4//! Per WDP Part 5: Compact IDs, all error codes use xxHash3 with seed `0x000031762D706477`.
5
6#[cfg(not(feature = "std"))]
7extern crate alloc;
8
9/// Hash algorithm selection
10///
11/// WDP specifies xxHash3 as the standard algorithm for error code hashing.
12/// xxHash3 is optimized for small inputs (15-30 byte error codes), making it
13/// ideal for WDP's use case.
14///
15/// Note: This enum is kept for API compatibility but only xxHash3 is WDP-compliant.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum HashAlgorithm {
18 /// xxHash3 - WDP-specified algorithm (default)
19 ///
20 /// - Speed: ~30 GB/s
21 /// - Optimized for small inputs (perfect for error codes!)
22 /// - Security: NOT cryptographically secure (not needed for error codes)
23 /// - Cross-platform: C, Python, JS, Go, Java, Rust, etc.
24 /// - Standard: WDP Part 5 mandates xxHash3 with seed 0x000031762D706477
25 #[default]
26 XxHash3,
27}
28
29impl HashAlgorithm {
30 /// Get the algorithm name as a string
31 pub fn as_str(&self) -> &'static str {
32 match self {
33 HashAlgorithm::XxHash3 => "xxHash3",
34 }
35 }
36
37 /// Check if algorithm is WDP-compliant
38 ///
39 /// Returns true for xxHash3 (the only WDP-compliant algorithm).
40 pub fn is_wdp_compliant(&self) -> bool {
41 matches!(self, HashAlgorithm::XxHash3)
42 }
43
44 /// Check if algorithm is cross-language compatible
45 ///
46 /// xxHash3 is supported in C, Python, JS, Go, Java, Rust, and more.
47 pub fn is_cross_language(&self) -> bool {
48 true // xxHash3 is cross-language
49 }
50}
51
52/// Error type for parsing HashAlgorithm from string
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ParseHashAlgorithmError;
55
56impl core::fmt::Display for ParseHashAlgorithmError {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 write!(f, "unknown hash algorithm (only 'xxhash3' is supported)")
59 }
60}
61
62impl core::str::FromStr for HashAlgorithm {
63 type Err = ParseHashAlgorithmError;
64
65 /// Parse algorithm from string (case-insensitive)
66 ///
67 /// Only xxHash3 is WDP-compliant.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use waddling_errors_hash::HashAlgorithm;
73 /// use std::str::FromStr;
74 ///
75 /// assert_eq!(HashAlgorithm::from_str("xxHash3"), Ok(HashAlgorithm::XxHash3));
76 /// assert_eq!(HashAlgorithm::from_str("xxh3"), Ok(HashAlgorithm::XxHash3));
77 /// assert!(HashAlgorithm::from_str("unknown").is_err());
78 /// ```
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 match s.to_lowercase().as_str() {
81 "xxhash3" | "xxh3" | "xx3" | "xxhash" | "default" => Ok(HashAlgorithm::XxHash3),
82 _ => Err(ParseHashAlgorithmError),
83 }
84 }
85}
86
87/// WDP v1 seed as a numeric value
88///
89/// Per WDP Part 5: Compact IDs specification.
90/// ASCII bytes of "wdp-v1\0\0" interpreted as little-endian u64 = 0x000031762D706477
91pub const WDP_SEED: u64 = 0x000031762D706477;
92
93/// WDP v1 seed as a string (for display/documentation)
94pub const WDP_SEED_STR: &str = "wdp-v1";
95
96/// Hash configuration
97///
98/// For WDP compliance, use `HashConfig::wdp_compliant()` which uses
99/// xxHash3 with the standard WDP seed.
100#[derive(Debug, Clone)]
101pub struct HashConfig {
102 /// The hash algorithm to use (only xxHash3 for WDP)
103 pub algorithm: HashAlgorithm,
104
105 /// Seed for the hash
106 ///
107 /// Default is `0x000031762D706477` per WDP specification.
108 pub seed: u64,
109}
110
111impl Default for HashConfig {
112 fn default() -> Self {
113 Self::wdp_compliant()
114 }
115}
116
117impl HashConfig {
118 /// Create a new hash config with custom seed
119 pub fn new(seed: u64) -> Self {
120 Self {
121 algorithm: HashAlgorithm::XxHash3,
122 seed,
123 }
124 }
125
126 /// Create a WDP-compliant config (xxHash3 + WDP seed)
127 ///
128 /// This is the standard configuration per WDP Part 5.
129 pub fn wdp_compliant() -> Self {
130 Self {
131 algorithm: HashAlgorithm::XxHash3,
132 seed: WDP_SEED,
133 }
134 }
135
136 /// Create a config with a custom seed (for testing/isolation)
137 pub fn with_seed(seed: u64) -> Self {
138 Self {
139 algorithm: HashAlgorithm::XxHash3,
140 seed,
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_algorithm_from_str() {
151 use core::str::FromStr;
152 assert_eq!(
153 HashAlgorithm::from_str("xxHash3"),
154 Ok(HashAlgorithm::XxHash3)
155 );
156 assert_eq!(HashAlgorithm::from_str("xxh3"), Ok(HashAlgorithm::XxHash3));
157 assert_eq!(
158 HashAlgorithm::from_str("xxhash"),
159 Ok(HashAlgorithm::XxHash3)
160 );
161 assert!(HashAlgorithm::from_str("unknown").is_err());
162 }
163
164 #[test]
165 fn test_algorithm_is_wdp_compliant() {
166 assert!(HashAlgorithm::XxHash3.is_wdp_compliant());
167 }
168
169 #[test]
170 fn test_algorithm_is_cross_language() {
171 assert!(HashAlgorithm::XxHash3.is_cross_language());
172 }
173
174 #[test]
175 fn test_default_config() {
176 let config = HashConfig::default();
177 assert_eq!(config.algorithm, HashAlgorithm::XxHash3);
178 assert_eq!(config.seed, WDP_SEED);
179 }
180
181 #[test]
182 fn test_config_with_seed() {
183 let config = HashConfig::with_seed(0x12345678);
184 assert_eq!(config.algorithm, HashAlgorithm::XxHash3);
185 assert_eq!(config.seed, 0x12345678);
186 }
187
188 #[test]
189 fn test_config_wdp_compliant() {
190 let config = HashConfig::wdp_compliant();
191 assert_eq!(config.algorithm, HashAlgorithm::XxHash3);
192 assert_eq!(config.seed, 0x000031762D706477);
193 }
194
195 #[test]
196 fn test_wdp_seed_value() {
197 // Verify the seed is ASCII "wdp-v1\0\0" as little-endian u64
198 assert_eq!(WDP_SEED, 0x000031762D706477);
199 }
200}