1use crate::advanced::{AdvancedConfig, FeatureDetector};
8use crate::error::{Result, SaferRingError};
9use crate::logging::LogLevel;
10use std::time::Duration;
11
12#[derive(Debug, Clone)]
17pub struct SaferRingConfig {
18 pub ring: RingConfig,
20 pub buffer: BufferConfig,
22 pub performance: PerformanceConfig,
24 pub logging: LoggingConfig,
26 pub advanced: AdvancedConfig,
28 pub error_handling: ErrorHandlingConfig,
30}
31
32#[derive(Debug, Clone)]
34pub struct RingConfig {
35 pub sq_entries: u32,
37 pub cq_entries: u32,
39 pub sq_poll: bool,
41 pub sq_thread_cpu: Option<u32>,
43 pub sq_thread_idle: Option<u32>,
45 pub cq_overflow: bool,
47 pub kernel_sq_thread: bool,
49}
50
51#[derive(Debug, Clone)]
53pub struct BufferConfig {
54 pub default_size: usize,
56 pub alignment: usize,
58 pub enable_pooling: bool,
60 pub pool_size: usize,
62 pub max_pooled_size: usize,
64 pub numa_aware: bool,
66 pub numa_node: i32,
68 pub pre_register: bool,
70}
71
72#[derive(Debug, Clone)]
74pub struct PerformanceConfig {
75 pub batch_submission: bool,
77 pub max_batch_size: usize,
79 pub batch_timeout: Duration,
81 pub zero_copy: bool,
83 pub vectored_io: bool,
85 pub operation_coalescing: bool,
87 pub coalescing_timeout: Duration,
89 pub adaptive_polling: bool,
91 pub poll_timeout: Duration,
93}
94
95#[derive(Debug, Clone)]
97pub struct LoggingConfig {
98 pub enabled: bool,
100 pub level: LogLevel,
102 pub metrics: bool,
104 pub tracing: bool,
106 pub log_file: Option<std::path::PathBuf>,
108 pub json_format: bool,
110 pub debug_assertions: bool,
112}
113
114#[derive(Debug, Clone)]
116pub struct ErrorHandlingConfig {
117 pub auto_retry: bool,
119 pub max_retries: u32,
121 pub retry_delay: Duration,
123 pub graceful_degradation: bool,
125 pub sync_fallback: bool,
127 pub panic_on_fatal: bool,
129}
130
131#[allow(clippy::derivable_impls)] impl Default for SaferRingConfig {
133 fn default() -> Self {
134 Self {
135 ring: RingConfig::default(),
136 buffer: BufferConfig::default(),
137 performance: PerformanceConfig::default(),
138 logging: LoggingConfig::default(),
139 advanced: AdvancedConfig::default(),
140 error_handling: ErrorHandlingConfig::default(),
141 }
142 }
143}
144
145impl Default for RingConfig {
146 fn default() -> Self {
147 Self {
148 sq_entries: 128,
149 cq_entries: 0, sq_poll: false,
151 sq_thread_cpu: None,
152 sq_thread_idle: None,
153 cq_overflow: true,
154 kernel_sq_thread: false,
155 }
156 }
157}
158
159impl Default for BufferConfig {
160 fn default() -> Self {
161 Self {
162 default_size: 4096,
163 alignment: 0, enable_pooling: true,
165 pool_size: 64,
166 max_pooled_size: 1024 * 1024, numa_aware: false,
168 numa_node: -1,
169 pre_register: false,
170 }
171 }
172}
173
174impl Default for PerformanceConfig {
175 fn default() -> Self {
176 Self {
177 batch_submission: true,
178 max_batch_size: 32,
179 batch_timeout: Duration::from_millis(1),
180 zero_copy: true,
181 vectored_io: true,
182 operation_coalescing: false,
183 coalescing_timeout: Duration::from_micros(100),
184 adaptive_polling: false,
185 poll_timeout: Duration::from_micros(10),
186 }
187 }
188}
189
190impl Default for LoggingConfig {
191 fn default() -> Self {
192 Self {
193 enabled: false,
194 level: LogLevel::Info,
195 metrics: false,
196 tracing: false,
197 log_file: None,
198 json_format: false,
199 debug_assertions: cfg!(debug_assertions),
200 }
201 }
202}
203
204impl Default for ErrorHandlingConfig {
205 fn default() -> Self {
206 Self {
207 auto_retry: false,
208 max_retries: 3,
209 retry_delay: Duration::from_millis(10),
210 graceful_degradation: true,
211 sync_fallback: false,
212 panic_on_fatal: true,
213 }
214 }
215}
216
217impl SaferRingConfig {
218 pub fn low_latency() -> Self {
223 Self {
224 ring: RingConfig {
225 sq_entries: 32,
226 cq_entries: 0,
227 sq_poll: true,
228 sq_thread_cpu: Some(0),
229 sq_thread_idle: Some(1),
230 cq_overflow: true,
231 kernel_sq_thread: true,
232 },
233 buffer: BufferConfig {
234 default_size: 1024,
235 alignment: 64, enable_pooling: true,
237 pool_size: 32,
238 max_pooled_size: 64 * 1024, numa_aware: true,
240 numa_node: -1,
241 pre_register: true,
242 },
243 performance: PerformanceConfig {
244 batch_submission: false, max_batch_size: 1,
246 batch_timeout: Duration::from_micros(1),
247 zero_copy: true,
248 vectored_io: false,
249 operation_coalescing: false,
250 coalescing_timeout: Duration::ZERO,
251 adaptive_polling: true,
252 poll_timeout: Duration::from_nanos(100),
253 },
254 logging: LoggingConfig {
255 enabled: false, level: LogLevel::Error,
257 metrics: false,
258 tracing: false,
259 log_file: None,
260 json_format: false,
261 debug_assertions: false,
262 },
263 advanced: AdvancedConfig {
264 buffer_selection: true,
265 multi_shot: false,
266 provided_buffers: true,
267 fast_poll: true,
268 sq_poll: true,
269 sq_thread_cpu: Some(0),
270 coop_taskrun: true,
271 defer_taskrun: true,
272 ..Default::default()
274 },
275 error_handling: ErrorHandlingConfig {
276 auto_retry: false,
277 max_retries: 0,
278 retry_delay: Duration::ZERO,
279 graceful_degradation: false,
280 sync_fallback: false,
281 panic_on_fatal: true,
282 },
283 }
284 }
285
286 pub fn high_throughput() -> Self {
291 Self {
292 ring: RingConfig {
293 sq_entries: 1024,
294 cq_entries: 2048,
295 sq_poll: false,
296 sq_thread_cpu: None,
297 sq_thread_idle: None,
298 cq_overflow: true,
299 kernel_sq_thread: false,
300 },
301 buffer: BufferConfig {
302 default_size: 64 * 1024, alignment: 4096, enable_pooling: true,
305 pool_size: 256,
306 max_pooled_size: 16 * 1024 * 1024, numa_aware: true,
308 numa_node: -1,
309 pre_register: true,
310 },
311 performance: PerformanceConfig {
312 batch_submission: true,
313 max_batch_size: 256,
314 batch_timeout: Duration::from_millis(10),
315 zero_copy: true,
316 vectored_io: true,
317 operation_coalescing: true,
318 coalescing_timeout: Duration::from_millis(1),
319 adaptive_polling: false,
320 poll_timeout: Duration::from_millis(1),
321 },
322 logging: LoggingConfig {
323 enabled: true,
324 level: LogLevel::Info,
325 metrics: true,
326 tracing: false,
327 log_file: None,
328 json_format: false,
329 debug_assertions: false,
330 },
331 advanced: AdvancedConfig {
332 buffer_selection: true,
333 multi_shot: true,
334 provided_buffers: true,
335 fast_poll: false,
336 sq_poll: false,
337 sq_thread_cpu: None,
338 coop_taskrun: true,
339 defer_taskrun: true,
340 ..Default::default()
342 },
343 error_handling: ErrorHandlingConfig {
344 auto_retry: true,
345 max_retries: 3,
346 retry_delay: Duration::from_millis(1),
347 graceful_degradation: true,
348 sync_fallback: true,
349 panic_on_fatal: false,
350 },
351 }
352 }
353
354 pub fn development() -> Self {
359 Self {
360 ring: RingConfig {
361 sq_entries: 64,
362 cq_entries: 0,
363 sq_poll: false,
364 sq_thread_cpu: None,
365 sq_thread_idle: None,
366 cq_overflow: true,
367 kernel_sq_thread: false,
368 },
369 buffer: BufferConfig {
370 default_size: 4096,
371 alignment: 0,
372 enable_pooling: true,
373 pool_size: 32,
374 max_pooled_size: 1024 * 1024,
375 numa_aware: false,
376 numa_node: -1,
377 pre_register: false,
378 },
379 performance: PerformanceConfig {
380 batch_submission: true,
381 max_batch_size: 16,
382 batch_timeout: Duration::from_millis(1),
383 zero_copy: true,
384 vectored_io: true,
385 operation_coalescing: false,
386 coalescing_timeout: Duration::from_millis(1),
387 adaptive_polling: false,
388 poll_timeout: Duration::from_millis(1),
389 },
390 logging: LoggingConfig {
391 enabled: true,
392 level: LogLevel::Debug,
393 metrics: true,
394 tracing: true,
395 log_file: Some("safer-ring.log".into()),
396 json_format: false,
397 debug_assertions: true,
398 },
399 advanced: AdvancedConfig::default(),
400 error_handling: ErrorHandlingConfig {
401 auto_retry: true,
402 max_retries: 1,
403 retry_delay: Duration::from_millis(10),
404 graceful_degradation: true,
405 sync_fallback: true,
406 panic_on_fatal: false,
407 },
408 }
409 }
410
411 pub fn production() -> Self {
416 Self {
417 ring: RingConfig {
418 sq_entries: 256,
419 cq_entries: 0,
420 sq_poll: false,
421 sq_thread_cpu: None,
422 sq_thread_idle: None,
423 cq_overflow: true,
424 kernel_sq_thread: false,
425 },
426 buffer: BufferConfig {
427 default_size: 8192,
428 alignment: 0,
429 enable_pooling: true,
430 pool_size: 128,
431 max_pooled_size: 4 * 1024 * 1024, numa_aware: true,
433 numa_node: -1,
434 pre_register: false,
435 },
436 performance: PerformanceConfig {
437 batch_submission: true,
438 max_batch_size: 64,
439 batch_timeout: Duration::from_millis(5),
440 zero_copy: true,
441 vectored_io: true,
442 operation_coalescing: false,
443 coalescing_timeout: Duration::from_millis(1),
444 adaptive_polling: false,
445 poll_timeout: Duration::from_millis(1),
446 },
447 logging: LoggingConfig {
448 enabled: true,
449 level: LogLevel::Warn,
450 metrics: true,
451 tracing: false,
452 log_file: Some("/var/log/safer-ring.log".into()),
453 json_format: true,
454 debug_assertions: false,
455 },
456 advanced: AdvancedConfig::default(),
457 error_handling: ErrorHandlingConfig {
458 auto_retry: true,
459 max_retries: 3,
460 retry_delay: Duration::from_millis(100),
461 graceful_degradation: true,
462 sync_fallback: true,
463 panic_on_fatal: false,
464 },
465 }
466 }
467
468 pub fn auto_detect() -> Result<Self> {
473 let detector = FeatureDetector::new()?;
474 let cpu_count = num_cpus::get();
475
476 let (pool_size, max_pooled_size) = if let Ok(memory_info) = Self::get_memory_info() {
478 let available_mb = memory_info.available / (1024 * 1024);
479 if available_mb > 1024 {
480 (256, 16 * 1024 * 1024)
481 } else if available_mb > 512 {
482 (128, 8 * 1024 * 1024)
483 } else {
484 (64, 1024 * 1024) }
486 } else {
487 (64, 1024 * 1024) };
489
490 Ok(Self {
491 advanced: detector.create_optimal_config(),
492 ring: RingConfig {
493 sq_entries: (cpu_count * 32).min(1024) as u32,
494 ..Default::default()
495 },
496 buffer: BufferConfig {
497 numa_aware: cpu_count > 16,
498 pool_size,
499 max_pooled_size,
500 ..Default::default()
501 },
502 ..Default::default()
503 })
504 }
505
506 pub fn validate(&self) -> Result<()> {
508 if self.ring.sq_entries == 0 {
510 return Err(SaferRingError::Io(std::io::Error::new(
511 std::io::ErrorKind::InvalidInput,
512 "SQ entries must be greater than 0",
513 )));
514 }
515
516 if self.ring.sq_entries > 4096 {
517 return Err(SaferRingError::Io(std::io::Error::new(
518 std::io::ErrorKind::InvalidInput,
519 "SQ entries should not exceed 4096",
520 )));
521 }
522
523 if self.buffer.default_size == 0 {
525 return Err(SaferRingError::Io(std::io::Error::new(
526 std::io::ErrorKind::InvalidInput,
527 "Default buffer size must be greater than 0",
528 )));
529 }
530
531 if self.buffer.pool_size == 0 && self.buffer.enable_pooling {
532 return Err(SaferRingError::Io(std::io::Error::new(
533 std::io::ErrorKind::InvalidInput,
534 "Pool size must be greater than 0 when pooling is enabled",
535 )));
536 }
537
538 if self.performance.max_batch_size == 0 && self.performance.batch_submission {
540 return Err(SaferRingError::Io(std::io::Error::new(
541 std::io::ErrorKind::InvalidInput,
542 "Max batch size must be greater than 0 when batch submission is enabled",
543 )));
544 }
545
546 if self.error_handling.auto_retry && self.error_handling.max_retries == 0 {
548 return Err(SaferRingError::Io(std::io::Error::new(
549 std::io::ErrorKind::InvalidInput,
550 "Max retries must be greater than 0 when auto retry is enabled",
551 )));
552 }
553
554 Ok(())
555 }
556
557 fn get_memory_info() -> Result<MemoryInfo> {
559 #[cfg(target_os = "linux")]
560 {
561 let meminfo = std::fs::read_to_string("/proc/meminfo").map_err(SaferRingError::Io)?;
562
563 let mut total = 0;
564 let mut available = 0;
565
566 for line in meminfo.lines() {
567 if line.starts_with("MemTotal:") {
568 if let Some(value) = line.split_whitespace().nth(1) {
569 total = value.parse::<u64>().unwrap_or(0) * 1024; }
571 } else if line.starts_with("MemAvailable:") {
572 if let Some(value) = line.split_whitespace().nth(1) {
573 available = value.parse::<u64>().unwrap_or(0) * 1024; }
575 }
576 }
577
578 Ok(MemoryInfo { total, available })
579 }
580
581 #[cfg(not(target_os = "linux"))]
582 {
583 Ok(MemoryInfo {
585 total: 8 * 1024 * 1024 * 1024, available: 4 * 1024 * 1024 * 1024, })
588 }
589 }
590}
591
592#[derive(Debug, Clone)]
594struct MemoryInfo {
595 #[allow(dead_code)]
597 total: u64,
598 available: u64,
600}
601
602#[derive(Debug)]
604pub struct ConfigBuilder {
605 config: SaferRingConfig,
606}
607
608impl ConfigBuilder {
609 pub fn new() -> Self {
611 Self {
612 config: SaferRingConfig::default(),
613 }
614 }
615
616 pub fn ring(mut self, ring: RingConfig) -> Self {
618 self.config.ring = ring;
619 self
620 }
621
622 pub fn buffer(mut self, buffer: BufferConfig) -> Self {
624 self.config.buffer = buffer;
625 self
626 }
627
628 pub fn performance(mut self, performance: PerformanceConfig) -> Self {
630 self.config.performance = performance;
631 self
632 }
633
634 pub fn logging(mut self, logging: LoggingConfig) -> Self {
636 self.config.logging = logging;
637 self
638 }
639
640 pub fn advanced(mut self, advanced: AdvancedConfig) -> Self {
642 self.config.advanced = advanced;
643 self
644 }
645
646 pub fn error_handling(mut self, error_handling: ErrorHandlingConfig) -> Self {
648 self.config.error_handling = error_handling;
649 self
650 }
651
652 pub fn build(self) -> Result<SaferRingConfig> {
654 self.config.validate()?;
655 Ok(self.config)
656 }
657}
658
659impl Default for ConfigBuilder {
660 fn default() -> Self {
661 Self::new()
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use super::*;
668
669 #[test]
670 fn test_default_config() {
671 let config = SaferRingConfig::default();
672 assert!(config.validate().is_ok());
673 }
674
675 #[test]
676 fn test_low_latency_config() {
677 let config = SaferRingConfig::low_latency();
678 assert!(config.validate().is_ok());
679 assert_eq!(config.ring.sq_entries, 32);
680 assert!(config.ring.sq_poll);
681 assert!(!config.performance.batch_submission);
682 }
683
684 #[test]
685 fn test_high_throughput_config() {
686 let config = SaferRingConfig::high_throughput();
687 assert!(config.validate().is_ok());
688 assert_eq!(config.ring.sq_entries, 1024);
689 assert!(config.performance.batch_submission);
690 assert_eq!(config.performance.max_batch_size, 256);
691 }
692
693 #[test]
694 fn test_development_config() {
695 let config = SaferRingConfig::development();
696 assert!(config.validate().is_ok());
697 assert!(config.logging.enabled);
698 assert_eq!(config.logging.level, LogLevel::Debug);
699 assert!(config.logging.tracing);
700 }
701
702 #[test]
703 fn test_production_config() {
704 let config = SaferRingConfig::production();
705 assert!(config.validate().is_ok());
706 assert!(config.logging.enabled);
707 assert_eq!(config.logging.level, LogLevel::Warn);
708 assert!(config.logging.json_format);
709 }
710
711 #[test]
712 fn test_config_validation() {
713 let mut config = SaferRingConfig::default();
714
715 assert!(config.validate().is_ok());
717
718 config.ring.sq_entries = 0;
720 assert!(config.validate().is_err());
721
722 config.ring.sq_entries = 128;
723 config.buffer.default_size = 0;
724 assert!(config.validate().is_err());
725 }
726
727 #[test]
728 fn test_config_builder() {
729 let config = ConfigBuilder::new()
730 .ring(RingConfig {
731 sq_entries: 64,
732 ..Default::default()
733 })
734 .buffer(BufferConfig {
735 default_size: 8192,
736 ..Default::default()
737 })
738 .build()
739 .unwrap();
740
741 assert_eq!(config.ring.sq_entries, 64);
742 assert_eq!(config.buffer.default_size, 8192);
743 }
744
745 #[test]
746 fn test_auto_detect_config() {
747 if let Ok(config) = SaferRingConfig::auto_detect() {
749 assert!(config.validate().is_ok());
750 assert!(config.ring.sq_entries > 0);
751 }
752 }
753}