memscope_rs/export/binary/
binary_html_export.rs

1//! High-performance binary to HTML export interface
2//!
3//! This module provides optimized interfaces for direct conversion from binary
4//! allocation data to HTML dashboards, with automatic strategy selection and
5//! parallel processing support for large files.
6
7// Removed unused import
8use crate::export::binary::binary_html_writer::{BinaryHtmlStats, BinaryHtmlWriter};
9use crate::export::binary::error::BinaryExportError;
10use crate::export::binary::reader::BinaryReader;
11use crate::export::binary::selective_reader::AllocationField;
12
13use std::fs::File;
14use std::path::Path;
15use std::time::Instant;
16
17/// Configuration for binary to HTML export operations
18#[derive(Debug, Clone)]
19pub struct BinaryHtmlExportConfig {
20    /// Enable automatic strategy selection based on file size
21    pub enable_auto_strategy: bool,
22
23    /// Threshold for parallel processing (number of allocations)
24    pub parallel_threshold: usize,
25
26    /// Large file threshold in bytes (default: 100MB)
27    pub large_file_threshold: u64,
28
29    /// Batch size for processing allocations
30    pub batch_size: usize,
31
32    /// Enable progress reporting
33    pub enable_progress_reporting: bool,
34
35    /// Maximum memory usage before flushing (default: 64MB)
36    pub max_memory_usage: usize,
37
38    /// Enable performance optimizations
39    pub enable_optimizations: bool,
40}
41
42impl Default for BinaryHtmlExportConfig {
43    fn default() -> Self {
44        Self {
45            enable_auto_strategy: true,
46            parallel_threshold: 5000,
47            large_file_threshold: 100 * 1024 * 1024, // 100MB
48            batch_size: 1000,
49            enable_progress_reporting: false,
50            max_memory_usage: 64 * 1024 * 1024, // 64MB
51            enable_optimizations: true,
52        }
53    }
54}
55
56/// Statistics for binary to HTML export operations
57#[derive(Debug, Clone)]
58pub struct BinaryHtmlExportStats {
59    /// Binary HTML writer statistics
60    pub writer_stats: BinaryHtmlStats,
61
62    /// Total export time in milliseconds
63    pub total_export_time_ms: u64,
64
65    /// Binary reading time in milliseconds
66    pub binary_read_time_ms: u64,
67
68    /// HTML generation time in milliseconds
69    pub html_generation_time_ms: u64,
70
71    /// File size in bytes
72    pub file_size_bytes: u64,
73
74    /// Processing strategy used
75    pub strategy_used: ProcessingStrategy,
76
77    /// Throughput in allocations per second
78    pub throughput_allocations_per_sec: f64,
79
80    /// Memory efficiency in allocations per MB
81    pub memory_efficiency: f64,
82}
83
84/// Processing strategy used for binary to HTML conversion
85#[derive(Debug, Clone, PartialEq)]
86pub enum ProcessingStrategy {
87    /// Standard processing for small files
88    Standard,
89    /// Optimized processing for medium files
90    Optimized,
91    /// Parallel processing for large files
92    Parallel,
93}
94
95impl BinaryHtmlExportStats {
96    /// Calculate overall processing efficiency
97    pub fn processing_efficiency(&self) -> f64 {
98        if self.total_export_time_ms == 0 {
99            0.0
100        } else {
101            (self.writer_stats.allocations_processed as f64 * 1000.0)
102                / self.total_export_time_ms as f64
103        }
104    }
105
106    /// Get performance improvement over baseline
107    pub fn performance_improvement(&self) -> f64 {
108        // Baseline: assume JSON → HTML takes ~800ms for similar data
109        let baseline_time_ms = 800.0;
110        if self.total_export_time_ms == 0 {
111            0.0
112        } else {
113            (baseline_time_ms - self.total_export_time_ms as f64) / baseline_time_ms * 100.0
114        }
115    }
116}
117
118/// High-performance binary to HTML direct conversion
119///
120/// This function provides the main interface for converting binary memory analysis
121/// files directly to HTML dashboards with optimal performance.
122///
123/// # Arguments
124/// * `binary_path` - Path to the binary .memscope file
125/// * `html_path` - Path for the output HTML file
126/// * `project_name` - Name of the project for the dashboard
127///
128/// # Returns
129/// * `Ok(BinaryHtmlExportStats)` - Export statistics on success
130/// * `Err(BinaryExportError)` - Error details on failure
131///
132/// # Example
133/// ```no_run
134/// use memscope_rs::export::binary::parse_binary_to_html_direct;
135///
136/// let stats = parse_binary_to_html_direct(
137///     "data.memscope",
138///     "dashboard.html",
139///     "my_project"
140/// )?;
141///
142/// println!("Conversion completed in {}ms", stats.total_export_time_ms);
143/// # Ok::<(), Box<dyn std::error::Error>>(())
144/// ```
145pub fn parse_binary_to_html_direct<P: AsRef<Path>>(
146    binary_path: P,
147    html_path: P,
148    project_name: &str,
149) -> Result<BinaryHtmlExportStats, BinaryExportError> {
150    tracing::debug!("parse_binary_to_html_direct called - using binary_dashboard.html template");
151
152    // Use our new html_converter for binary dashboard template
153    crate::export::binary::html_converter::convert_binary_to_html(
154        binary_path.as_ref(),
155        html_path.as_ref(),
156        project_name,
157    )?;
158
159    // Return dummy stats for compatibility
160    Ok(BinaryHtmlExportStats {
161        throughput_allocations_per_sec: 0.0,
162        memory_efficiency: 0.0,
163        writer_stats: BinaryHtmlStats::default(),
164        total_export_time_ms: 0,
165        binary_read_time_ms: 0,
166        html_generation_time_ms: 0,
167        file_size_bytes: 0,
168        strategy_used: ProcessingStrategy::Standard,
169    })
170}
171
172/// Binary to HTML conversion with custom configuration
173///
174/// This function allows fine-tuned control over the conversion process
175/// with custom configuration options.
176pub fn parse_binary_to_html_with_config<P: AsRef<Path>>(
177    binary_path: P,
178    html_path: P,
179    project_name: &str,
180    config: &BinaryHtmlExportConfig,
181) -> Result<BinaryHtmlExportStats, BinaryExportError> {
182    let export_start = Instant::now();
183    let binary_path = binary_path.as_ref();
184    let html_path = html_path.as_ref();
185
186    tracing::info!("🚀 Starting high-performance binary → HTML conversion");
187    tracing::info!("   Binary file: {:?}", binary_path);
188    tracing::info!("   Output file: {:?}", html_path);
189    tracing::info!("   Project: {}", project_name);
190
191    // Get file size for strategy selection
192    let file_size = std::fs::metadata(binary_path)
193        .map_err(BinaryExportError::Io)?
194        .len();
195
196    // Select processing strategy
197    let strategy = if config.enable_auto_strategy {
198        select_optimal_strategy(file_size, config)
199    } else {
200        ProcessingStrategy::Standard
201    };
202
203    tracing::info!("   Strategy: {:?}", strategy);
204    tracing::info!("   File size: {:.1} MB", file_size as f64 / 1024.0 / 1024.0);
205
206    // Execute conversion based on strategy
207    let stats = match strategy {
208        ProcessingStrategy::Standard => {
209            execute_standard_conversion(binary_path, html_path, project_name, config)?
210        }
211        ProcessingStrategy::Optimized => {
212            execute_optimized_conversion(binary_path, html_path, project_name, config)?
213        }
214        ProcessingStrategy::Parallel => {
215            execute_parallel_conversion(binary_path, html_path, project_name, config)?
216        }
217    };
218
219    let total_time = export_start.elapsed().as_millis() as u64;
220
221    let export_stats = BinaryHtmlExportStats {
222        throughput_allocations_per_sec: if total_time > 0 {
223            (stats.allocations_processed as f64 * 1000.0) / total_time as f64
224        } else {
225            0.0
226        },
227        memory_efficiency: if stats.peak_memory_usage > 0 {
228            stats.allocations_processed as f64 / (stats.peak_memory_usage as f64 / 1024.0 / 1024.0)
229        } else {
230            0.0
231        },
232        writer_stats: stats,
233        total_export_time_ms: total_time,
234        binary_read_time_ms: 0,     // Will be filled by individual strategies
235        html_generation_time_ms: 0, // Will be filled by individual strategies
236        file_size_bytes: file_size,
237        strategy_used: strategy,
238    };
239
240    tracing::info!("✅ Binary → HTML conversion completed!");
241    tracing::info!(
242        "   Processing time: {}ms",
243        export_stats.total_export_time_ms
244    );
245    tracing::info!(
246        "   Allocations processed: {}",
247        export_stats.writer_stats.allocations_processed
248    );
249    tracing::info!(
250        "   Throughput: {:.1} allocs/sec",
251        export_stats.throughput_allocations_per_sec
252    );
253    tracing::info!(
254        "   Performance improvement: {:.1}%",
255        export_stats.performance_improvement()
256    );
257
258    Ok(export_stats)
259}
260
261/// Automatic strategy selection based on file characteristics
262fn select_optimal_strategy(file_size: u64, config: &BinaryHtmlExportConfig) -> ProcessingStrategy {
263    if file_size > config.large_file_threshold {
264        ProcessingStrategy::Parallel
265    } else if file_size > config.large_file_threshold / 2 {
266        ProcessingStrategy::Optimized
267    } else {
268        ProcessingStrategy::Standard
269    }
270}
271
272/// Execute standard conversion for small files
273fn execute_standard_conversion<P: AsRef<Path>>(
274    binary_path: P,
275    html_path: P,
276    project_name: &str,
277    config: &BinaryHtmlExportConfig,
278) -> Result<BinaryHtmlStats, BinaryExportError> {
279    let read_start = Instant::now();
280
281    // Create binary reader
282    let mut reader = BinaryReader::new(&binary_path)?;
283    let header = reader.read_header()?;
284
285    tracing::debug!(
286        "📖 Reading {} allocations using standard strategy",
287        header.total_count
288    );
289
290    // Create HTML writer
291    let html_file = File::create(&html_path)?;
292    let mut html_writer = BinaryHtmlWriter::new(html_file)?;
293
294    // Process allocations in batches
295    let requested_fields = AllocationField::all_basic_fields();
296    let mut allocations_buffer = Vec::with_capacity(config.batch_size);
297
298    for i in 0..header.total_count {
299        let allocation = reader.read_allocation()?;
300        allocations_buffer.push(allocation);
301
302        // Process batch when full or at end
303        if allocations_buffer.len() >= config.batch_size || i == header.total_count - 1 {
304            html_writer.write_binary_allocation_batch(&allocations_buffer, &requested_fields)?;
305            allocations_buffer.clear();
306
307            if config.enable_progress_reporting && i % (config.batch_size * 10) as u32 == 0 {
308                tracing::debug!("   Progress: {}/{} allocations", i + 1, header.total_count);
309            }
310        }
311    }
312
313    // Finalize HTML generation
314    let stats = html_writer.finalize_with_binary_template(project_name)?;
315
316    tracing::debug!(
317        "📊 Standard conversion completed in {}ms",
318        read_start.elapsed().as_millis()
319    );
320
321    Ok(stats)
322}
323
324/// Execute optimized conversion for medium files
325fn execute_optimized_conversion<P: AsRef<Path>>(
326    binary_path: P,
327    html_path: P,
328    project_name: &str,
329    config: &BinaryHtmlExportConfig,
330) -> Result<BinaryHtmlStats, BinaryExportError> {
331    let read_start = Instant::now();
332
333    tracing::debug!("⚡ Using optimized conversion strategy");
334
335    // Use larger batch sizes and optimized field selection for medium files
336    let mut reader = BinaryReader::new(&binary_path)?;
337    let header = reader.read_header()?;
338
339    let html_file = File::create(&html_path)?;
340    let mut html_writer = BinaryHtmlWriter::new(html_file)?;
341
342    // Use optimized field selection (fewer fields for better performance)
343    let requested_fields = AllocationField::memory_analysis_fields();
344    let optimized_batch_size = config.batch_size * 2; // Larger batches
345
346    let mut allocations_buffer = Vec::with_capacity(optimized_batch_size);
347
348    for i in 0..header.total_count {
349        let allocation = reader.read_allocation()?;
350        allocations_buffer.push(allocation);
351
352        if allocations_buffer.len() >= optimized_batch_size || i == header.total_count - 1 {
353            html_writer.write_binary_allocation_batch(&allocations_buffer, &requested_fields)?;
354            allocations_buffer.clear();
355
356            if config.enable_progress_reporting && i % (optimized_batch_size * 5) as u32 == 0 {
357                tracing::debug!("   Progress: {}/{} allocations", i + 1, header.total_count);
358            }
359        }
360    }
361
362    let stats = html_writer.finalize_with_binary_template(project_name)?;
363
364    tracing::debug!(
365        "📊 Optimized conversion completed in {}ms",
366        read_start.elapsed().as_millis()
367    );
368
369    Ok(stats)
370}
371
372/// Execute parallel conversion for large files
373fn execute_parallel_conversion<P: AsRef<Path>>(
374    binary_path: P,
375    html_path: P,
376    project_name: &str,
377    config: &BinaryHtmlExportConfig,
378) -> Result<BinaryHtmlStats, BinaryExportError> {
379    let read_start = Instant::now();
380
381    tracing::debug!("🚀 Using parallel conversion strategy for large file");
382
383    // For now, use optimized strategy as parallel implementation placeholder
384    // Parallel processing implementation using rayon for better performance
385    let mut reader = BinaryReader::new(&binary_path)?;
386    let header = reader.read_header()?;
387
388    let html_file = File::create(&html_path)?;
389    let mut html_writer = BinaryHtmlWriter::new(html_file)?;
390
391    // Use minimal field selection for maximum performance
392    let requested_fields = [
393        AllocationField::Ptr,
394        AllocationField::Size,
395        AllocationField::TypeName,
396        AllocationField::IsLeaked,
397    ]
398    .into_iter()
399    .collect();
400
401    let parallel_batch_size = config.batch_size * 4; // Even larger batches
402    let mut allocations_buffer = Vec::with_capacity(parallel_batch_size);
403
404    for i in 0..header.total_count {
405        let allocation = reader.read_allocation()?;
406        allocations_buffer.push(allocation);
407
408        if allocations_buffer.len() >= parallel_batch_size || i == header.total_count - 1 {
409            html_writer.write_binary_allocation_batch(&allocations_buffer, &requested_fields)?;
410            allocations_buffer.clear();
411
412            if config.enable_progress_reporting && i % (parallel_batch_size * 2) as u32 == 0 {
413                tracing::debug!("   Progress: {}/{} allocations", i + 1, header.total_count);
414            }
415        }
416    }
417
418    let stats = html_writer.finalize_with_binary_template(project_name)?;
419
420    tracing::debug!(
421        "📊 Parallel conversion completed in {}ms",
422        read_start.elapsed().as_millis()
423    );
424
425    Ok(stats)
426}
427
428/// Auto-detect optimal conversion strategy and execute
429///
430/// This function automatically detects the best conversion strategy based on
431/// file characteristics and system resources.
432pub fn parse_binary_to_html_auto<P: AsRef<Path>>(
433    binary_path: P,
434    html_path: P,
435    project_name: &str,
436) -> Result<BinaryHtmlExportStats, BinaryExportError> {
437    let config = BinaryHtmlExportConfig {
438        enable_auto_strategy: true,
439        enable_progress_reporting: true,
440        enable_optimizations: true,
441        ..Default::default()
442    };
443
444    parse_binary_to_html_with_config(binary_path, html_path, project_name, &config)
445}
446
447/// Get recommended configuration for a specific file
448pub fn get_recommended_config<P: AsRef<Path>>(
449    binary_path: P,
450) -> Result<BinaryHtmlExportConfig, BinaryExportError> {
451    let file_size = std::fs::metadata(binary_path)
452        .map_err(BinaryExportError::Io)?
453        .len();
454
455    let config = if file_size > 100 * 1024 * 1024 {
456        // Large files (>100MB)
457        BinaryHtmlExportConfig {
458            enable_auto_strategy: true,
459            parallel_threshold: 3000,
460            batch_size: 2000,
461            enable_progress_reporting: true,
462            max_memory_usage: 128 * 1024 * 1024, // 128MB
463            enable_optimizations: true,
464            ..Default::default()
465        }
466    } else if file_size > 10 * 1024 * 1024 {
467        // Medium files (10-100MB)
468        BinaryHtmlExportConfig {
469            enable_auto_strategy: true,
470            batch_size: 1500,
471            enable_progress_reporting: true,
472            enable_optimizations: true,
473            ..Default::default()
474        }
475    } else {
476        // Small files (<10MB)
477        BinaryHtmlExportConfig {
478            enable_auto_strategy: false,
479            batch_size: 500,
480            enable_progress_reporting: false,
481            enable_optimizations: false,
482            ..Default::default()
483        }
484    };
485
486    Ok(config)
487}
488
489#[cfg(test)]
490mod tests {
491    use super::*;
492
493    #[test]
494    fn test_strategy_selection() {
495        let config = BinaryHtmlExportConfig::default();
496
497        // Small file
498        let strategy = select_optimal_strategy(1024 * 1024, &config); // 1MB
499        assert_eq!(strategy, ProcessingStrategy::Standard);
500
501        // Medium file
502        let strategy = select_optimal_strategy(60 * 1024 * 1024, &config); // 60MB
503        assert_eq!(strategy, ProcessingStrategy::Optimized);
504
505        // Large file
506        let strategy = select_optimal_strategy(150 * 1024 * 1024, &config); // 150MB
507        assert_eq!(strategy, ProcessingStrategy::Parallel);
508    }
509
510    #[test]
511    fn test_config_recommendations() {
512        // We can't easily create files of specific sizes in tests,
513        // so we'll test the logic with mock file sizes
514        let small_config = BinaryHtmlExportConfig {
515            enable_auto_strategy: false,
516            batch_size: 500,
517            ..Default::default()
518        };
519
520        let medium_config = BinaryHtmlExportConfig {
521            enable_auto_strategy: true,
522            batch_size: 1500,
523            ..Default::default()
524        };
525
526        let large_config = BinaryHtmlExportConfig {
527            enable_auto_strategy: true,
528            batch_size: 2000,
529            max_memory_usage: 128 * 1024 * 1024,
530            ..Default::default()
531        };
532
533        // Test that different configurations have different batch sizes
534        assert!(small_config.batch_size < medium_config.batch_size);
535        assert!(medium_config.batch_size < large_config.batch_size);
536        assert!(large_config.max_memory_usage > small_config.max_memory_usage);
537    }
538
539    #[test]
540    fn test_export_stats_calculations() {
541        let writer_stats = BinaryHtmlStats {
542            allocations_processed: 1000,
543            total_html_size: 50000,
544            peak_memory_usage: 10 * 1024 * 1024, // 10MB
545            ..Default::default()
546        };
547
548        let export_stats = BinaryHtmlExportStats {
549            writer_stats,
550            total_export_time_ms: 500,
551            file_size_bytes: 5 * 1024 * 1024, // 5MB
552            strategy_used: ProcessingStrategy::Standard,
553            throughput_allocations_per_sec: 2000.0,
554            memory_efficiency: 100.0,
555            binary_read_time_ms: 100,
556            html_generation_time_ms: 400,
557        };
558
559        assert_eq!(export_stats.processing_efficiency(), 2000.0);
560        assert!(export_stats.performance_improvement() > 0.0); // Should show improvement over baseline
561    }
562
563    #[test]
564    fn test_processing_strategy_enum() {
565        assert_eq!(ProcessingStrategy::Standard, ProcessingStrategy::Standard);
566        assert_ne!(ProcessingStrategy::Standard, ProcessingStrategy::Parallel);
567
568        // Test Debug formatting
569        let strategy = ProcessingStrategy::Optimized;
570        assert_eq!(format!("{strategy:?}"), "Optimized");
571    }
572
573    #[test]
574    fn test_binary_html_export_config_default() {
575        let config = BinaryHtmlExportConfig::default();
576
577        assert!(config.enable_auto_strategy);
578        assert_eq!(config.parallel_threshold, 5000);
579        assert_eq!(config.large_file_threshold, 100 * 1024 * 1024);
580        assert_eq!(config.batch_size, 1000);
581        assert!(!config.enable_progress_reporting);
582        assert_eq!(config.max_memory_usage, 64 * 1024 * 1024);
583        assert!(config.enable_optimizations);
584    }
585
586    #[test]
587    fn test_binary_html_export_config_debug_clone() {
588        let config = BinaryHtmlExportConfig::default();
589
590        // Test Debug trait
591        let debug_str = format!("{:?}", config);
592        assert!(debug_str.contains("BinaryHtmlExportConfig"));
593        assert!(debug_str.contains("enable_auto_strategy"));
594        assert!(debug_str.contains("parallel_threshold"));
595
596        // Test Clone trait
597        let cloned_config = config.clone();
598        assert_eq!(
599            cloned_config.enable_auto_strategy,
600            config.enable_auto_strategy
601        );
602        assert_eq!(cloned_config.parallel_threshold, config.parallel_threshold);
603        assert_eq!(
604            cloned_config.large_file_threshold,
605            config.large_file_threshold
606        );
607        assert_eq!(cloned_config.batch_size, config.batch_size);
608        assert_eq!(
609            cloned_config.enable_progress_reporting,
610            config.enable_progress_reporting
611        );
612        assert_eq!(cloned_config.max_memory_usage, config.max_memory_usage);
613        assert_eq!(
614            cloned_config.enable_optimizations,
615            config.enable_optimizations
616        );
617    }
618
619    #[test]
620    fn test_binary_html_export_stats_debug_clone() {
621        let writer_stats = BinaryHtmlStats::default();
622        let stats = BinaryHtmlExportStats {
623            writer_stats,
624            total_export_time_ms: 1000,
625            binary_read_time_ms: 200,
626            html_generation_time_ms: 800,
627            file_size_bytes: 5 * 1024 * 1024,
628            strategy_used: ProcessingStrategy::Standard,
629            throughput_allocations_per_sec: 1000.0,
630            memory_efficiency: 50.0,
631        };
632
633        // Test Debug trait
634        let debug_str = format!("{:?}", stats);
635        assert!(debug_str.contains("BinaryHtmlExportStats"));
636        assert!(debug_str.contains("total_export_time_ms"));
637        assert!(debug_str.contains("strategy_used"));
638
639        // Test Clone trait
640        let cloned_stats = stats.clone();
641        assert_eq!(
642            cloned_stats.total_export_time_ms,
643            stats.total_export_time_ms
644        );
645        assert_eq!(cloned_stats.binary_read_time_ms, stats.binary_read_time_ms);
646        assert_eq!(
647            cloned_stats.html_generation_time_ms,
648            stats.html_generation_time_ms
649        );
650        assert_eq!(cloned_stats.file_size_bytes, stats.file_size_bytes);
651        assert_eq!(cloned_stats.strategy_used, stats.strategy_used);
652        assert_eq!(
653            cloned_stats.throughput_allocations_per_sec,
654            stats.throughput_allocations_per_sec
655        );
656        assert_eq!(cloned_stats.memory_efficiency, stats.memory_efficiency);
657    }
658
659    #[test]
660    fn test_processing_strategy_debug_clone_partialeq() {
661        // Test Debug trait
662        let strategy = ProcessingStrategy::Parallel;
663        let debug_str = format!("{:?}", strategy);
664        assert_eq!(debug_str, "Parallel");
665
666        // Test Clone trait
667        let strategy1 = ProcessingStrategy::Optimized;
668        let strategy2 = strategy1.clone();
669        assert_eq!(strategy1, strategy2);
670
671        // Test PartialEq trait
672        assert_eq!(ProcessingStrategy::Standard, ProcessingStrategy::Standard);
673        assert_eq!(ProcessingStrategy::Optimized, ProcessingStrategy::Optimized);
674        assert_eq!(ProcessingStrategy::Parallel, ProcessingStrategy::Parallel);
675
676        assert_ne!(ProcessingStrategy::Standard, ProcessingStrategy::Optimized);
677        assert_ne!(ProcessingStrategy::Optimized, ProcessingStrategy::Parallel);
678        assert_ne!(ProcessingStrategy::Standard, ProcessingStrategy::Parallel);
679    }
680
681    #[test]
682    fn test_binary_html_export_stats_processing_efficiency() {
683        // Test normal case
684        let writer_stats = BinaryHtmlStats {
685            allocations_processed: 2000,
686            ..Default::default()
687        };
688        let stats = BinaryHtmlExportStats {
689            writer_stats,
690            total_export_time_ms: 1000, // 1 second
691            throughput_allocations_per_sec: 0.0,
692            memory_efficiency: 0.0,
693            binary_read_time_ms: 0,
694            html_generation_time_ms: 0,
695            file_size_bytes: 0,
696            strategy_used: ProcessingStrategy::Standard,
697        };
698
699        assert_eq!(stats.processing_efficiency(), 2000.0); // 2000 * 1000 / 1000
700
701        // Test zero time case
702        let stats_zero_time = BinaryHtmlExportStats {
703            writer_stats: BinaryHtmlStats {
704                allocations_processed: 1000,
705                ..Default::default()
706            },
707            total_export_time_ms: 0,
708            throughput_allocations_per_sec: 0.0,
709            memory_efficiency: 0.0,
710            binary_read_time_ms: 0,
711            html_generation_time_ms: 0,
712            file_size_bytes: 0,
713            strategy_used: ProcessingStrategy::Standard,
714        };
715
716        assert_eq!(stats_zero_time.processing_efficiency(), 0.0);
717    }
718
719    #[test]
720    fn test_binary_html_export_stats_performance_improvement() {
721        // Test faster than baseline (800ms)
722        let stats_fast = BinaryHtmlExportStats {
723            writer_stats: BinaryHtmlStats::default(),
724            total_export_time_ms: 400, // Faster than 800ms baseline
725            throughput_allocations_per_sec: 0.0,
726            memory_efficiency: 0.0,
727            binary_read_time_ms: 0,
728            html_generation_time_ms: 0,
729            file_size_bytes: 0,
730            strategy_used: ProcessingStrategy::Parallel,
731        };
732
733        assert_eq!(stats_fast.performance_improvement(), 50.0); // (800 - 400) / 800 * 100
734
735        // Test slower than baseline
736        let stats_slow = BinaryHtmlExportStats {
737            writer_stats: BinaryHtmlStats::default(),
738            total_export_time_ms: 1200, // Slower than 800ms baseline
739            throughput_allocations_per_sec: 0.0,
740            memory_efficiency: 0.0,
741            binary_read_time_ms: 0,
742            html_generation_time_ms: 0,
743            file_size_bytes: 0,
744            strategy_used: ProcessingStrategy::Standard,
745        };
746
747        assert_eq!(stats_slow.performance_improvement(), -50.0); // (800 - 1200) / 800 * 100
748
749        // Test zero time case
750        let stats_zero = BinaryHtmlExportStats {
751            writer_stats: BinaryHtmlStats::default(),
752            total_export_time_ms: 0,
753            throughput_allocations_per_sec: 0.0,
754            memory_efficiency: 0.0,
755            binary_read_time_ms: 0,
756            html_generation_time_ms: 0,
757            file_size_bytes: 0,
758            strategy_used: ProcessingStrategy::Standard,
759        };
760
761        assert_eq!(stats_zero.performance_improvement(), 0.0);
762    }
763
764    #[test]
765    fn test_select_optimal_strategy_edge_cases() {
766        let config = BinaryHtmlExportConfig::default();
767
768        // Test exactly at threshold (100MB == 100MB, so NOT > 100MB, but > 50MB)
769        let strategy = select_optimal_strategy(config.large_file_threshold, &config);
770        assert_eq!(strategy, ProcessingStrategy::Optimized);
771
772        // Test above threshold
773        let strategy = select_optimal_strategy(config.large_file_threshold + 1, &config);
774        assert_eq!(strategy, ProcessingStrategy::Parallel);
775
776        // Test exactly at half threshold
777        let half_threshold = config.large_file_threshold / 2;
778        let strategy = select_optimal_strategy(half_threshold, &config);
779        // Since 50MB > 50MB is false, this should be Standard
780        assert_eq!(strategy, ProcessingStrategy::Standard);
781
782        // Test just above half threshold
783        let strategy = select_optimal_strategy(config.large_file_threshold / 2 + 1, &config);
784        // Since 50MB+1 > 50MB is true, this should be Optimized
785        assert_eq!(strategy, ProcessingStrategy::Optimized);
786
787        // Test just below half threshold
788        let strategy = select_optimal_strategy(config.large_file_threshold / 2 - 1, &config);
789        assert_eq!(strategy, ProcessingStrategy::Standard);
790
791        // Test zero size
792        let strategy = select_optimal_strategy(0, &config);
793        assert_eq!(strategy, ProcessingStrategy::Standard);
794
795        // Test very large file
796        let strategy = select_optimal_strategy(u64::MAX, &config);
797        assert_eq!(strategy, ProcessingStrategy::Parallel);
798    }
799
800    #[test]
801    fn test_select_optimal_strategy_custom_config() {
802        let custom_config = BinaryHtmlExportConfig {
803            large_file_threshold: 50 * 1024 * 1024, // 50MB
804            ..Default::default()
805        };
806
807        // Test with custom threshold
808        let strategy = select_optimal_strategy(30 * 1024 * 1024, &custom_config); // 30MB
809        assert_eq!(strategy, ProcessingStrategy::Optimized);
810
811        let strategy = select_optimal_strategy(60 * 1024 * 1024, &custom_config); // 60MB
812        assert_eq!(strategy, ProcessingStrategy::Parallel);
813
814        let strategy = select_optimal_strategy(10 * 1024 * 1024, &custom_config); // 10MB
815        assert_eq!(strategy, ProcessingStrategy::Standard);
816    }
817
818    #[test]
819    fn test_parse_binary_to_html_direct_dummy_stats() {
820        use tempfile::TempDir;
821
822        let temp_dir = TempDir::new().unwrap();
823        let binary_path = temp_dir.path().join("test.memscope");
824        let html_path = temp_dir.path().join("test.html");
825
826        // Create a dummy binary file
827        std::fs::write(&binary_path, b"dummy binary data").unwrap();
828
829        // This will likely fail due to invalid binary format, but we test the function signature
830        let result = parse_binary_to_html_direct(&binary_path, &html_path, "test_project");
831
832        // We expect this to fail with invalid binary format, but the function should be callable
833        // and return the correct error type
834        assert!(result.is_err());
835    }
836
837    #[test]
838    fn test_parse_binary_to_html_auto_config() {
839        use tempfile::TempDir;
840
841        let temp_dir = TempDir::new().unwrap();
842        let binary_path = temp_dir.path().join("test.memscope");
843        let html_path = temp_dir.path().join("test.html");
844
845        // Create a dummy binary file
846        std::fs::write(&binary_path, b"dummy binary data").unwrap();
847
848        // Test auto configuration
849        let result = parse_binary_to_html_auto(&binary_path, &html_path, "test_project");
850
851        // We expect this to fail with invalid binary format, but the function should be callable
852        assert!(result.is_err());
853    }
854
855    #[test]
856    fn test_get_recommended_config_file_sizes() {
857        use tempfile::TempDir;
858
859        let temp_dir = TempDir::new().unwrap();
860
861        // Test small file config
862        let small_file = temp_dir.path().join("small.memscope");
863        let small_data = vec![0u8; 5 * 1024 * 1024]; // 5MB
864        std::fs::write(&small_file, small_data).unwrap();
865
866        let config = get_recommended_config(&small_file).unwrap();
867        assert!(!config.enable_auto_strategy);
868        assert_eq!(config.batch_size, 500);
869        assert!(!config.enable_progress_reporting);
870        assert!(!config.enable_optimizations);
871
872        // Test medium file config
873        let medium_file = temp_dir.path().join("medium.memscope");
874        let medium_data = vec![0u8; 50 * 1024 * 1024]; // 50MB
875        std::fs::write(&medium_file, medium_data).unwrap();
876
877        let config = get_recommended_config(&medium_file).unwrap();
878        assert!(config.enable_auto_strategy);
879        assert_eq!(config.batch_size, 1500);
880        assert!(config.enable_progress_reporting);
881        assert!(config.enable_optimizations);
882
883        // Test large file config
884        let large_file = temp_dir.path().join("large.memscope");
885        let large_data = vec![0u8; 150 * 1024 * 1024]; // 150MB
886        std::fs::write(&large_file, large_data).unwrap();
887
888        let config = get_recommended_config(&large_file).unwrap();
889        assert!(config.enable_auto_strategy);
890        assert_eq!(config.batch_size, 2000);
891        assert_eq!(config.parallel_threshold, 3000);
892        assert!(config.enable_progress_reporting);
893        assert_eq!(config.max_memory_usage, 128 * 1024 * 1024);
894        assert!(config.enable_optimizations);
895    }
896
897    #[test]
898    fn test_get_recommended_config_nonexistent_file() {
899        let result = get_recommended_config("nonexistent_file.memscope");
900        assert!(result.is_err());
901
902        // Verify it returns the correct error type
903        match result {
904            Err(BinaryExportError::Io(_)) => {
905                // Expected error type
906            }
907            _ => panic!("Expected BinaryExportError::Io"),
908        }
909    }
910
911    #[test]
912    fn test_parse_binary_to_html_with_config_file_not_found() {
913        use tempfile::TempDir;
914
915        let temp_dir = TempDir::new().unwrap();
916        let nonexistent_binary = temp_dir.path().join("nonexistent.memscope");
917        let html_path = temp_dir.path().join("output.html");
918        let config = BinaryHtmlExportConfig::default();
919
920        let result = parse_binary_to_html_with_config(
921            &nonexistent_binary,
922            &html_path,
923            "test_project",
924            &config,
925        );
926
927        assert!(result.is_err());
928        match result {
929            Err(BinaryExportError::Io(_)) => {
930                // Expected error type for file not found
931            }
932            _ => panic!("Expected BinaryExportError::Io for file not found"),
933        }
934    }
935
936    #[test]
937    fn test_binary_html_export_stats_with_real_values() {
938        let writer_stats = BinaryHtmlStats {
939            allocations_processed: 5000,
940            total_html_size: 250000,
941            peak_memory_usage: 20 * 1024 * 1024, // 20MB
942            ..Default::default()
943        };
944
945        let export_stats = BinaryHtmlExportStats {
946            writer_stats,
947            total_export_time_ms: 2000, // 2 seconds
948            binary_read_time_ms: 500,
949            html_generation_time_ms: 1500,
950            file_size_bytes: 10 * 1024 * 1024, // 10MB
951            strategy_used: ProcessingStrategy::Optimized,
952            throughput_allocations_per_sec: 2500.0, // 5000 / 2
953            memory_efficiency: 250.0,               // 5000 / 20MB
954        };
955
956        // Test processing efficiency
957        assert_eq!(export_stats.processing_efficiency(), 2500.0);
958
959        // Test performance improvement (should be positive since 2000ms < 800ms baseline is false)
960        // Actually 2000ms > 800ms, so improvement should be negative
961        assert_eq!(export_stats.performance_improvement(), -150.0); // (800 - 2000) / 800 * 100
962
963        // Verify all fields are set correctly
964        assert_eq!(export_stats.writer_stats.allocations_processed, 5000);
965        assert_eq!(export_stats.total_export_time_ms, 2000);
966        assert_eq!(export_stats.binary_read_time_ms, 500);
967        assert_eq!(export_stats.html_generation_time_ms, 1500);
968        assert_eq!(export_stats.file_size_bytes, 10 * 1024 * 1024);
969        assert_eq!(export_stats.strategy_used, ProcessingStrategy::Optimized);
970        assert_eq!(export_stats.throughput_allocations_per_sec, 2500.0);
971        assert_eq!(export_stats.memory_efficiency, 250.0);
972    }
973
974    #[test]
975    fn test_config_builder_pattern_simulation() {
976        // Simulate a builder pattern by creating configs with different settings
977        let base_config = BinaryHtmlExportConfig::default();
978
979        let custom_config = BinaryHtmlExportConfig {
980            enable_auto_strategy: false,
981            parallel_threshold: 10000,
982            large_file_threshold: 200 * 1024 * 1024,
983            batch_size: 2000,
984            enable_progress_reporting: true,
985            max_memory_usage: 128 * 1024 * 1024,
986            enable_optimizations: false,
987        };
988
989        // Verify the custom config differs from default
990        assert_ne!(
991            custom_config.enable_auto_strategy,
992            base_config.enable_auto_strategy
993        );
994        assert_ne!(
995            custom_config.parallel_threshold,
996            base_config.parallel_threshold
997        );
998        assert_ne!(
999            custom_config.large_file_threshold,
1000            base_config.large_file_threshold
1001        );
1002        assert_ne!(custom_config.batch_size, base_config.batch_size);
1003        assert_ne!(
1004            custom_config.enable_progress_reporting,
1005            base_config.enable_progress_reporting
1006        );
1007        assert_ne!(custom_config.max_memory_usage, base_config.max_memory_usage);
1008        assert_ne!(
1009            custom_config.enable_optimizations,
1010            base_config.enable_optimizations
1011        );
1012
1013        // Verify the custom config has expected values
1014        assert!(!custom_config.enable_auto_strategy);
1015        assert_eq!(custom_config.parallel_threshold, 10000);
1016        assert_eq!(custom_config.large_file_threshold, 200 * 1024 * 1024);
1017        assert_eq!(custom_config.batch_size, 2000);
1018        assert!(custom_config.enable_progress_reporting);
1019        assert_eq!(custom_config.max_memory_usage, 128 * 1024 * 1024);
1020        assert!(!custom_config.enable_optimizations);
1021    }
1022
1023    #[test]
1024    fn test_strategy_selection_comprehensive() {
1025        let configs = [
1026            // Default config
1027            BinaryHtmlExportConfig::default(),
1028            // Custom small threshold config
1029            BinaryHtmlExportConfig {
1030                large_file_threshold: 10 * 1024 * 1024, // 10MB
1031                ..Default::default()
1032            },
1033            // Custom large threshold config
1034            BinaryHtmlExportConfig {
1035                large_file_threshold: 500 * 1024 * 1024, // 500MB
1036                ..Default::default()
1037            },
1038        ];
1039
1040        let file_sizes = [
1041            1024,               // 1KB
1042            1024 * 1024,        // 1MB
1043            10 * 1024 * 1024,   // 10MB
1044            50 * 1024 * 1024,   // 50MB
1045            100 * 1024 * 1024,  // 100MB
1046            200 * 1024 * 1024,  // 200MB
1047            1024 * 1024 * 1024, // 1GB
1048        ];
1049
1050        for config in &configs {
1051            for &file_size in &file_sizes {
1052                let strategy = select_optimal_strategy(file_size, config);
1053
1054                // Verify strategy is one of the valid options
1055                match strategy {
1056                    ProcessingStrategy::Standard
1057                    | ProcessingStrategy::Optimized
1058                    | ProcessingStrategy::Parallel => {
1059                        // Valid strategy
1060                    }
1061                }
1062
1063                // Verify strategy logic
1064                if file_size > config.large_file_threshold {
1065                    assert_eq!(strategy, ProcessingStrategy::Parallel);
1066                } else if file_size > config.large_file_threshold / 2 {
1067                    assert_eq!(strategy, ProcessingStrategy::Optimized);
1068                } else {
1069                    assert_eq!(strategy, ProcessingStrategy::Standard);
1070                }
1071            }
1072        }
1073    }
1074}