rustywallet_batch/
config.rs

1//! Configuration types for batch key generation.
2//!
3//! This module provides [`BatchConfig`] for customizing batch generation behavior.
4
5use crate::error::BatchError;
6
7/// Configuration for batch key generation.
8///
9/// Use the builder pattern or preset configurations for common use cases.
10///
11/// # Example
12///
13/// ```rust
14/// use rustywallet_batch::config::BatchConfig;
15///
16/// // Custom configuration
17/// let config = BatchConfig::default()
18///     .with_batch_size(100_000)
19///     .with_thread_count(Some(4))
20///     .with_chunk_size(1000);
21///
22/// // Or use presets
23/// let fast = BatchConfig::fast();
24/// let balanced = BatchConfig::balanced();
25/// let memory_efficient = BatchConfig::memory_efficient();
26/// ```
27#[derive(Debug, Clone)]
28pub struct BatchConfig {
29    /// Number of keys to generate in a batch.
30    pub batch_size: usize,
31
32    /// Number of threads to use for parallel processing.
33    /// None means auto-detect based on available CPU cores.
34    pub thread_count: Option<usize>,
35
36    /// Whether to use SIMD optimization when available.
37    pub use_simd: bool,
38
39    /// Size of chunks for streaming operations.
40    pub chunk_size: usize,
41
42    /// Maximum memory limit in bytes (optional).
43    pub memory_limit: Option<usize>,
44
45    /// Whether to use parallel processing.
46    pub parallel: bool,
47
48    /// Whether to maintain deterministic ordering in parallel mode.
49    pub deterministic_order: bool,
50}
51
52impl Default for BatchConfig {
53    fn default() -> Self {
54        Self {
55            batch_size: 10_000,
56            thread_count: None,
57            use_simd: true,
58            chunk_size: 1_000,
59            memory_limit: None,
60            parallel: false,
61            deterministic_order: false,
62        }
63    }
64}
65
66impl BatchConfig {
67    /// Create a new configuration with default values.
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    /// Preset configuration optimized for maximum speed.
73    ///
74    /// Uses parallel processing with all available cores and larger chunk sizes.
75    pub fn fast() -> Self {
76        Self {
77            batch_size: 100_000,
78            thread_count: None, // Auto-detect
79            use_simd: true,
80            chunk_size: 10_000,
81            memory_limit: None,
82            parallel: true,
83            deterministic_order: false,
84        }
85    }
86
87    /// Preset configuration balancing speed and memory usage.
88    ///
89    /// Uses parallel processing with moderate chunk sizes.
90    pub fn balanced() -> Self {
91        Self {
92            batch_size: 50_000,
93            thread_count: None,
94            use_simd: true,
95            chunk_size: 5_000,
96            memory_limit: Some(100 * 1024 * 1024), // 100MB
97            parallel: true,
98            deterministic_order: false,
99        }
100    }
101
102    /// Preset configuration optimized for minimal memory usage.
103    ///
104    /// Uses smaller chunk sizes and streaming to minimize memory footprint.
105    pub fn memory_efficient() -> Self {
106        Self {
107            batch_size: 10_000,
108            thread_count: Some(2),
109            use_simd: true,
110            chunk_size: 100,
111            memory_limit: Some(10 * 1024 * 1024), // 10MB
112            parallel: true,
113            deterministic_order: false,
114        }
115    }
116
117    /// Set the batch size.
118    pub fn with_batch_size(mut self, size: usize) -> Self {
119        self.batch_size = size;
120        self
121    }
122
123    /// Set the thread count for parallel processing.
124    pub fn with_thread_count(mut self, count: Option<usize>) -> Self {
125        self.thread_count = count;
126        self
127    }
128
129    /// Enable or disable SIMD optimization.
130    pub fn with_simd(mut self, enabled: bool) -> Self {
131        self.use_simd = enabled;
132        self
133    }
134
135    /// Set the chunk size for streaming operations.
136    pub fn with_chunk_size(mut self, size: usize) -> Self {
137        self.chunk_size = size;
138        self
139    }
140
141    /// Set the memory limit in bytes.
142    pub fn with_memory_limit(mut self, limit: Option<usize>) -> Self {
143        self.memory_limit = limit;
144        self
145    }
146
147    /// Enable or disable parallel processing.
148    pub fn with_parallel(mut self, enabled: bool) -> Self {
149        self.parallel = enabled;
150        self
151    }
152
153    /// Enable or disable deterministic ordering in parallel mode.
154    pub fn with_deterministic_order(mut self, enabled: bool) -> Self {
155        self.deterministic_order = enabled;
156        self
157    }
158
159    /// Validate the configuration.
160    ///
161    /// Returns an error if any configuration parameter is invalid.
162    pub fn validate(&self) -> Result<(), BatchError> {
163        if self.batch_size == 0 {
164            return Err(BatchError::invalid_config("batch_size must be greater than 0"));
165        }
166
167        if self.chunk_size == 0 {
168            return Err(BatchError::invalid_config("chunk_size must be greater than 0"));
169        }
170
171        if let Some(count) = self.thread_count {
172            if count == 0 {
173                return Err(BatchError::invalid_config("thread_count must be greater than 0"));
174            }
175        }
176
177        if let Some(limit) = self.memory_limit {
178            if limit == 0 {
179                return Err(BatchError::invalid_config("memory_limit must be greater than 0"));
180            }
181        }
182
183        Ok(())
184    }
185
186    /// Get the effective thread count.
187    ///
188    /// Returns the configured thread count or the number of available CPU cores.
189    pub fn effective_thread_count(&self) -> usize {
190        self.thread_count.unwrap_or_else(|| {
191            rayon::current_num_threads()
192        })
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_default_config() {
202        let config = BatchConfig::default();
203        assert_eq!(config.batch_size, 10_000);
204        assert!(config.thread_count.is_none());
205        assert!(config.use_simd);
206        assert_eq!(config.chunk_size, 1_000);
207        assert!(config.memory_limit.is_none());
208        assert!(!config.parallel);
209        assert!(!config.deterministic_order);
210    }
211
212    #[test]
213    fn test_fast_preset() {
214        let config = BatchConfig::fast();
215        assert_eq!(config.batch_size, 100_000);
216        assert!(config.parallel);
217        assert_eq!(config.chunk_size, 10_000);
218    }
219
220    #[test]
221    fn test_balanced_preset() {
222        let config = BatchConfig::balanced();
223        assert_eq!(config.batch_size, 50_000);
224        assert!(config.parallel);
225        assert!(config.memory_limit.is_some());
226    }
227
228    #[test]
229    fn test_memory_efficient_preset() {
230        let config = BatchConfig::memory_efficient();
231        assert_eq!(config.chunk_size, 100);
232        assert!(config.memory_limit.is_some());
233    }
234
235    #[test]
236    fn test_builder_pattern() {
237        let config = BatchConfig::new()
238            .with_batch_size(50_000)
239            .with_thread_count(Some(4))
240            .with_chunk_size(500)
241            .with_parallel(true);
242
243        assert_eq!(config.batch_size, 50_000);
244        assert_eq!(config.thread_count, Some(4));
245        assert_eq!(config.chunk_size, 500);
246        assert!(config.parallel);
247    }
248
249    #[test]
250    fn test_validation_valid() {
251        let config = BatchConfig::default();
252        assert!(config.validate().is_ok());
253    }
254
255    #[test]
256    fn test_validation_invalid_batch_size() {
257        let config = BatchConfig::default().with_batch_size(0);
258        assert!(config.validate().is_err());
259    }
260
261    #[test]
262    fn test_validation_invalid_chunk_size() {
263        let config = BatchConfig::default().with_chunk_size(0);
264        assert!(config.validate().is_err());
265    }
266
267    #[test]
268    fn test_validation_invalid_thread_count() {
269        let config = BatchConfig::default().with_thread_count(Some(0));
270        assert!(config.validate().is_err());
271    }
272}