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}