memscope_rs/export/binary/
html_export.rs

1//! Binary to HTML export functionality
2//!
3//! This module provides direct conversion from binary files to HTML dashboards
4//! using the templates in ./templates/
5
6use crate::export::binary::config::{DashboardExportStats, DashboardFormat, DashboardOptions};
7use crate::export::binary::{error::BinaryExportError, DataScope};
8use std::path::Path;
9
10/// Output format for binary export
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum BinaryOutputFormat {
13    /// Generate JSON files (existing functionality)
14    Json,
15    /// Generate HTML dashboard (user data only)
16    Html,
17    /// Generate HTML dashboard with system data
18    HtmlSystem,
19    /// Generate both user and system HTML dashboards
20    HtmlBoth,
21    /// Generate both JSON and HTML in parallel
22    Both,
23}
24
25/// Performance optimization configuration
26#[derive(Debug, Clone)]
27pub struct BinaryExportConfig {
28    /// Enable parallel processing for multiple formats
29    pub enable_parallel_processing: bool,
30    /// Buffer size for I/O operations (default: 256KB)
31    pub buffer_size: usize,
32    /// Batch size for processing allocations (default: 2000)
33    pub batch_size: usize,
34    /// Enable streaming processing for large files
35    pub enable_streaming: bool,
36    /// Thread count for parallel processing (None = auto-detect)
37    pub thread_count: Option<usize>,
38}
39
40impl Default for BinaryExportConfig {
41    fn default() -> Self {
42        Self {
43            enable_parallel_processing: true,
44            buffer_size: 256 * 1024, // 256KB
45            batch_size: 2000,
46            enable_streaming: true,
47            thread_count: None, // Auto-detect
48        }
49    }
50}
51
52impl BinaryExportConfig {
53    /// Create a new configuration with default settings
54    pub fn new() -> Self {
55        Self::default()
56    }
57
58    /// Create a configuration optimized for speed (minimal features)
59    pub fn fast() -> Self {
60        Self {
61            enable_parallel_processing: true,
62            buffer_size: 512 * 1024, // 512KB
63            batch_size: 3000,
64            enable_streaming: true,
65            thread_count: None,
66        }
67    }
68
69    /// Create a configuration optimized for large files
70    pub fn large_files() -> Self {
71        Self {
72            enable_parallel_processing: true,
73            buffer_size: 1024 * 1024, // 1MB
74            batch_size: 5000,
75            enable_streaming: true,
76            thread_count: None,
77        }
78    }
79
80    /// Enable or disable parallel processing
81    pub fn parallel_processing(mut self, enabled: bool) -> Self {
82        self.enable_parallel_processing = enabled;
83        self
84    }
85
86    /// Set buffer size for I/O operations
87    pub fn buffer_size(mut self, size: usize) -> Self {
88        self.buffer_size = size;
89        self
90    }
91
92    /// Set batch size for processing allocations
93    pub fn batch_size(mut self, size: usize) -> Self {
94        self.batch_size = size;
95        self
96    }
97
98    /// Enable or disable streaming processing
99    pub fn streaming(mut self, enabled: bool) -> Self {
100        self.enable_streaming = enabled;
101        self
102    }
103
104    /// Set thread count for parallel processing (None for auto-detect)
105    pub fn thread_count(mut self, count: Option<usize>) -> Self {
106        self.thread_count = count;
107        self
108    }
109}
110
111// Embed CSS and JS content at compile time
112// CSS and JS content are now loaded from templates directly
113
114/// **[UNIFIED ENTRY POINT]** Ultra-fast binary export with format selection and parallel processing
115///
116/// This is the main unified entry point that supports JSON, HTML, or both formats with optimized performance.
117/// Uses parallel processing and streaming for large datasets, inspired by optimized_json_export.rs.
118/// Designed to match or exceed full-binary โ†’ JSON performance while adding HTML support.
119///
120/// # Arguments
121/// * `binary_path` - Path to the binary file
122/// * `base_name` - Base name for output files
123/// * `format` - Output format (Json, Html, or Both)
124/// * `config` - Optional configuration for performance tuning
125///
126/// # Performance Features
127/// - Parallel processing for multiple formats (JSON + HTML simultaneously)
128/// - Streaming data processing for large files
129/// - Optimized memory usage with batching
130/// - Intelligent buffer management
131/// - Zero impact on existing JSON-only performance
132/// - Shared data reading to avoid duplicate I/O
133pub fn export_binary<P: AsRef<Path>>(
134    binary_path: P,
135    base_name: &str,
136    format: BinaryOutputFormat,
137) -> Result<(), BinaryExportError> {
138    export_binary_optimized(binary_path, base_name, format, None)
139}
140
141/// **[OPTIMIZED IMPLEMENTATION]** Internal optimized binary export implementation
142pub fn export_binary_optimized<P: AsRef<Path>>(
143    binary_path: P,
144    base_name: &str,
145    format: BinaryOutputFormat,
146    config: Option<BinaryExportConfig>,
147) -> Result<(), BinaryExportError> {
148    let config = config.unwrap_or_default();
149    let start = std::time::Instant::now();
150    let binary_path = binary_path.as_ref();
151
152    tracing::info!(
153        "๐Ÿš€ Starting optimized binary export for {:?} format",
154        format
155    );
156    tracing::info!(
157        "   - Parallel processing: {}",
158        config.enable_parallel_processing
159    );
160    tracing::info!("   - Streaming: {}", config.enable_streaming);
161    tracing::info!("   - Batch size: {}", config.batch_size);
162
163    // Step 1: Pre-flight checks and setup
164    let setup_start = std::time::Instant::now();
165
166    // Create output directory
167    let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
168    let project_dir = base_memory_analysis_dir.join(base_name);
169    std::fs::create_dir_all(&project_dir)?;
170
171    // Configure thread pool if specified
172    if let Some(thread_count) = config.thread_count {
173        rayon::ThreadPoolBuilder::new()
174            .num_threads(thread_count)
175            .build_global()
176            .map_err(|e| {
177                BinaryExportError::CorruptedData(format!("Failed to configure thread pool: {e}"))
178            })?;
179    }
180
181    let setup_time = setup_start.elapsed();
182    tracing::info!("โœ… Setup completed in {}ms", setup_time.as_millis());
183
184    // Step 2: Execute export based on format with optimizations
185    let export_start = std::time::Instant::now();
186
187    match format {
188        BinaryOutputFormat::Json => {
189            // Use existing optimized JSON generation (no changes to preserve performance)
190            export_json_optimized(binary_path, base_name, &config)?;
191        }
192        BinaryOutputFormat::Html => {
193            // Generate user-only HTML dashboard (lightweight)
194            let html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
195            export_html_filtered(binary_path, &html_path, base_name, &config, true)?;
196        }
197        BinaryOutputFormat::HtmlSystem => {
198            // Generate system-only HTML dashboard
199            let html_path = project_dir.join(format!("{base_name}_system_dashboard.html"));
200            export_html_filtered(binary_path, &html_path, base_name, &config, false)?;
201        }
202        BinaryOutputFormat::HtmlBoth => {
203            // Generate both user and system HTML dashboards
204            let user_html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
205            let system_html_path = project_dir.join(format!("{base_name}_system_dashboard.html"));
206
207            // Use parallel processing for both HTML files
208            use rayon::prelude::*;
209            let results: Result<Vec<()>, BinaryExportError> = [("user", true), ("system", false)]
210                .par_iter()
211                .map(|(data_type, is_user_only)| {
212                    let html_path = if *is_user_only {
213                        &user_html_path
214                    } else {
215                        &system_html_path
216                    };
217                    tracing::info!("๐Ÿงต [{}] Starting HTML generation", data_type.to_uppercase());
218                    export_html_filtered(binary_path, html_path, base_name, &config, *is_user_only)
219                })
220                .collect();
221
222            results?;
223        }
224        BinaryOutputFormat::Both => {
225            // Parallel generation with shared data reading optimization
226            export_both_formats_parallel(binary_path, base_name, &config)?;
227        }
228    }
229
230    let export_time = export_start.elapsed();
231    let total_time = start.elapsed();
232
233    tracing::info!(
234        "โœ… Export completed in {}ms (setup: {}ms, export: {}ms)",
235        total_time.as_millis(),
236        setup_time.as_millis(),
237        export_time.as_millis()
238    );
239
240    // Performance feedback
241    provide_performance_feedback(format, &config, total_time);
242
243    Ok(())
244}
245
246/// **[BACKWARD COMPATIBILITY]** Legacy function that maintains existing API
247pub fn export_binary_with_format<P: AsRef<Path>>(
248    binary_path: P,
249    base_name: &str,
250    format: BinaryOutputFormat,
251) -> Result<(), BinaryExportError> {
252    // Use optimized version with default config for backward compatibility
253    export_binary_optimized(binary_path, base_name, format, None)
254}
255
256/// **[ULTRA-FAST JSON EXPORT]** Use existing JSON generation without modifications
257/// This preserves the performance of the existing binary-to-JSON pipeline
258/// References the same optimized approach used in parse_full_binary_to_json
259fn export_json_optimized<P: AsRef<Path>>(
260    binary_path: P,
261    base_name: &str,
262    _config: &BinaryExportConfig,
263) -> Result<(), BinaryExportError> {
264    // Use the existing ultra-fast JSON export function to preserve performance
265    // This calls the optimized parse_full_binary_to_json method with parallel JSON generation
266    use crate::export::binary::parser::BinaryParser;
267    BinaryParser::parse_full_binary_to_json(binary_path, base_name)
268}
269
270/// **[OPTIMIZED HTML EXPORT]** Enhanced HTML generation with streaming and batching
271fn export_html_optimized<P: AsRef<Path>>(
272    binary_path: P,
273    output_path: P,
274    project_name: &str,
275    config: &BinaryExportConfig,
276) -> Result<(), BinaryExportError> {
277    use crate::export::binary::binary_html_writer::BinaryTemplateData;
278    use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
279    use crate::export::binary::reader::BinaryReader;
280
281    let start = std::time::Instant::now();
282    let binary_path = binary_path.as_ref();
283
284    tracing::info!("๐ŸŽจ Starting optimized HTML generation for {}", project_name);
285
286    // Step 1: Open reader with optimized settings
287    let mut reader = BinaryReader::new(binary_path)?;
288    let header = reader.read_header()?;
289    let total_count = header.total_count;
290
291    tracing::info!(
292        "๐Ÿ“Š Processing {} allocations with batch size {}",
293        total_count,
294        config.batch_size
295    );
296
297    // Step 2: Process allocations in optimized batches
298    let mut all_allocations = Vec::new();
299    let mut total_memory = 0u64;
300    let mut active_count = 0usize;
301
302    // Use batched processing for better memory management
303    let batch_count = (total_count as usize).div_ceil(config.batch_size);
304
305    for batch_idx in 0..batch_count {
306        let batch_start = batch_idx * config.batch_size;
307        let batch_end = std::cmp::min(batch_start + config.batch_size, total_count as usize);
308
309        tracing::debug!(
310            "Processing batch {}/{} (allocations {}-{})",
311            batch_idx + 1,
312            batch_count,
313            batch_start,
314            batch_end
315        );
316
317        for i in batch_start..batch_end {
318            match reader.read_allocation() {
319                Ok(allocation) => {
320                    // Convert to BinaryAllocationData for template
321                    let binary_data = convert_allocation_to_binary_data(&allocation, i)?;
322
323                    // Update statistics
324                    total_memory += allocation.size as u64;
325                    if allocation.is_active() {
326                        active_count += 1;
327                    }
328
329                    all_allocations.push(binary_data);
330                }
331                Err(e) => {
332                    tracing::warn!("โš ๏ธ  Skipping corrupted allocation at index {}: {}", i, e);
333                    continue;
334                }
335            }
336        }
337
338        // Optional: Flush memory if batch is large
339        if config.enable_streaming && all_allocations.len() > config.batch_size * 2 {
340            tracing::debug!(
341                "๐Ÿ’พ Memory management: {} allocations in buffer",
342                all_allocations.len()
343            );
344        }
345    }
346
347    // Step 3: Create template data with full analysis
348    let analysis_start = std::time::Instant::now();
349
350    // Skip all analysis for maximum performance
351    let (complex_types, unsafe_ffi, variable_relationships) = (None, None, None);
352
353    let analysis_time = analysis_start.elapsed();
354    tracing::info!("๐Ÿ“Š Analysis completed in {}ms", analysis_time.as_millis());
355
356    let template_data = BinaryTemplateData {
357        project_name: project_name.to_string(),
358        allocations: all_allocations,
359        total_memory_usage: total_memory,
360        peak_memory_usage: total_memory, // Simplified calculation
361        active_allocations_count: active_count,
362        processing_time_ms: start.elapsed().as_millis() as u64,
363        data_source: "binary_optimized_streaming".to_string(),
364        complex_types,
365        unsafe_ffi,
366        variable_relationships,
367    };
368
369    // Step 4: Render HTML using optimized template engine
370    let render_start = std::time::Instant::now();
371    let mut template_engine = BinaryTemplateEngine::new()?;
372    let html_content = template_engine.render_binary_template(&template_data)?;
373    let render_time = render_start.elapsed();
374
375    // Step 5: Write HTML to file with buffered I/O
376    let write_start = std::time::Instant::now();
377    std::fs::write(output_path, html_content)?;
378    let write_time = write_start.elapsed();
379
380    let total_time = start.elapsed();
381    tracing::info!(
382        "โœ… HTML generation completed in {}ms (render: {}ms, write: {}ms)",
383        total_time.as_millis(),
384        render_time.as_millis(),
385        write_time.as_millis()
386    );
387
388    Ok(())
389}
390
391/// **[FILTERED HTML EXPORT]** Generate HTML with user/system data filtering for optimal performance
392fn export_html_filtered<P: AsRef<Path>>(
393    binary_path: P,
394    output_path: P,
395    project_name: &str,
396    _config: &BinaryExportConfig,
397    user_only: bool,
398) -> Result<(), BinaryExportError> {
399    use crate::export::binary::binary_html_writer::BinaryTemplateData;
400    use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
401    use crate::export::binary::parser::BinaryParser;
402
403    let start = std::time::Instant::now();
404    let data_type = if user_only { "USER" } else { "SYSTEM" };
405
406    tracing::info!(
407        "๐Ÿš€ Starting {} HTML generation for {}",
408        data_type,
409        project_name
410    );
411
412    // **OPTIMIZATION**: Load data once using the same ultra-fast approach as JSON
413    let load_start = std::time::Instant::now();
414    let all_allocations = BinaryParser::load_allocations_with_recovery(binary_path)?;
415    let load_time = load_start.elapsed();
416
417    // **FILTERING**: Separate user and system allocations
418    let filtered_allocations: Vec<_> = all_allocations
419        .into_iter()
420        .filter(|alloc| {
421            if user_only {
422                // User allocations: have var_name
423                alloc.var_name.is_some()
424            } else {
425                // System allocations: no var_name
426                alloc.var_name.is_none()
427            }
428        })
429        .collect();
430
431    tracing::info!(
432        "๐Ÿ“Š Loaded and filtered {} {} allocations in {}ms",
433        filtered_allocations.len(),
434        data_type,
435        load_time.as_millis()
436    );
437
438    // **OPTIMIZATION**: Process filtered data directly
439    let process_start = std::time::Instant::now();
440    let mut binary_allocations = Vec::with_capacity(filtered_allocations.len());
441    let mut total_memory = 0u64;
442    let mut active_count = 0usize;
443
444    for (i, allocation) in filtered_allocations.iter().enumerate() {
445        let binary_data = convert_allocation_to_binary_data(allocation, i)?;
446
447        total_memory += allocation.size as u64;
448        if allocation.is_active() {
449            active_count += 1;
450        }
451
452        binary_allocations.push(binary_data);
453    }
454    let process_time = process_start.elapsed();
455
456    // Create template data with full analysis for filtered data
457    let analysis_start = std::time::Instant::now();
458
459    // Skip all analysis for maximum performance
460    let (complex_types, unsafe_ffi, variable_relationships) = (None, None, None);
461
462    let analysis_time = analysis_start.elapsed();
463    tracing::info!(
464        "๐Ÿ“Š {} analysis completed in {}ms",
465        data_type,
466        analysis_time.as_millis()
467    );
468
469    let template_data = BinaryTemplateData {
470        project_name: format!("{project_name} ({data_type})"),
471        allocations: binary_allocations,
472        total_memory_usage: total_memory,
473        peak_memory_usage: total_memory,
474        active_allocations_count: active_count,
475        processing_time_ms: start.elapsed().as_millis() as u64,
476        data_source: format!(
477            "binary_{}_filtered",
478            if user_only { "user" } else { "system" }
479        ),
480        complex_types,
481        unsafe_ffi,
482        variable_relationships,
483    };
484
485    // Render HTML using optimized template engine
486    let render_start = std::time::Instant::now();
487    let mut template_engine = BinaryTemplateEngine::new()?;
488    let html_content = template_engine.render_binary_template(&template_data)?;
489    let render_time = render_start.elapsed();
490
491    // Write HTML to file
492    let write_start = std::time::Instant::now();
493    std::fs::write(output_path, html_content)?;
494    let write_time = write_start.elapsed();
495
496    let total_time = start.elapsed();
497    tracing::info!(
498        "โœ… {} HTML generation completed in {}ms (load: {}ms, process: {}ms, render: {}ms, write: {}ms)",
499        data_type,
500        total_time.as_millis(),
501        load_time.as_millis(),
502        process_time.as_millis(),
503        render_time.as_millis(),
504        write_time.as_millis()
505    );
506
507    Ok(())
508}
509
510/// **[ULTRA-FAST PARALLEL EXPORT]** Generate both JSON and HTML in parallel with shared data optimization
511///
512/// This implementation uses the same ultra-fast approach as parse_full_binary_to_json but extends it
513/// to support parallel HTML generation. Key optimizations:
514/// - Shared data loading (single binary read)
515/// - Parallel JSON and HTML generation
516/// - Optimized I/O with large buffers
517/// - Direct streaming writes without intermediate allocations
518fn export_both_formats_parallel<P: AsRef<Path>>(
519    binary_path: P,
520    base_name: &str,
521    config: &BinaryExportConfig,
522) -> Result<(), BinaryExportError> {
523    use crate::export::binary::parser::BinaryParser;
524    use rayon::prelude::*;
525
526    let binary_path = binary_path.as_ref();
527    let start = std::time::Instant::now();
528
529    tracing::info!("๐Ÿš€ Starting ultra-fast parallel export for both JSON and HTML formats");
530
531    if config.enable_parallel_processing {
532        // **OPTIMIZATION**: Load data once and share between threads
533        let load_start = std::time::Instant::now();
534        let all_allocations = BinaryParser::load_allocations_with_recovery(binary_path)?;
535        let load_time = load_start.elapsed();
536        tracing::info!(
537            "๐Ÿ“Š Loaded {} allocations in {}ms (shared data)",
538            all_allocations.len(),
539            load_time.as_millis()
540        );
541
542        // Create output directory once
543        let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
544        let project_dir = base_memory_analysis_dir.join(base_name);
545        std::fs::create_dir_all(&project_dir)?;
546
547        // **PARALLEL EXECUTION**: JSON and HTML generation run simultaneously with shared data
548        let results: Result<Vec<()>, BinaryExportError> = [
549            ("json", BinaryOutputFormat::Json),
550            ("html", BinaryOutputFormat::Html),
551        ]
552        .par_iter()
553        .map(|(format_name, format)| {
554            let thread_start = std::time::Instant::now();
555
556            let result = match format {
557                BinaryOutputFormat::Json => {
558                    tracing::info!("๐Ÿงต [JSON Thread] Starting ultra-fast JSON generation");
559                    // Use the same ultra-fast parallel JSON generation as parse_full_binary_to_json
560                    generate_json_files_parallel(&all_allocations, base_name, &project_dir)
561                }
562                BinaryOutputFormat::Html => {
563                    tracing::info!("๐Ÿงต [HTML Thread] Starting ultra-fast USER HTML generation");
564                    let html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
565                    // Use optimized HTML generation with shared data (user only)
566                    export_html_with_shared_data_filtered(
567                        &all_allocations,
568                        &html_path,
569                        base_name,
570                        config,
571                        true,
572                    )
573                }
574                _ => unreachable!(),
575            };
576
577            let thread_time = thread_start.elapsed();
578            tracing::info!(
579                "๐Ÿงต [{}] Thread completed in {}ms",
580                format_name.to_uppercase(),
581                thread_time.as_millis()
582            );
583
584            result
585        })
586        .collect();
587
588        results?;
589    } else {
590        // Sequential execution if parallel processing is disabled
591        tracing::info!("๐Ÿ“ Sequential export mode (still optimized)");
592        export_json_optimized(binary_path, base_name, config)?;
593
594        let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
595        let project_dir = base_memory_analysis_dir.join(base_name);
596        let html_path = project_dir.join(format!("{base_name}_dashboard.html"));
597        export_html_optimized(binary_path, &html_path, base_name, config)?;
598    }
599
600    let total_time = start.elapsed();
601    tracing::info!(
602        "โœ… Ultra-fast parallel export completed in {}ms",
603        total_time.as_millis()
604    );
605
606    Ok(())
607}
608
609/// Provide performance feedback and optimization suggestions
610fn provide_performance_feedback(
611    format: BinaryOutputFormat,
612    config: &BinaryExportConfig,
613    elapsed: std::time::Duration,
614) {
615    let elapsed_ms = elapsed.as_millis();
616
617    // Performance analysis
618    if elapsed_ms < 1000 {
619        tracing::info!("๐Ÿš€ Excellent performance: {}ms", elapsed_ms);
620    } else if elapsed_ms < 5000 {
621        tracing::info!("โœ… Good performance: {}ms", elapsed_ms);
622    } else {
623        tracing::warn!("โš ๏ธ  Consider optimization: {}ms", elapsed_ms);
624
625        // Provide optimization suggestions
626        if !config.enable_parallel_processing && matches!(format, BinaryOutputFormat::Both) {
627            tracing::info!("๐Ÿ’ก Suggestion: Enable parallel processing for Both format");
628        }
629
630        if config.batch_size < 1000 {
631            tracing::info!("๐Ÿ’ก Suggestion: Increase batch size to 2000+ for better performance");
632        }
633    }
634
635    // Configuration feedback
636    match format {
637        BinaryOutputFormat::Json => {
638            tracing::info!("๐Ÿ“Š JSON export completed - ultra-fast performance maintained");
639        }
640        BinaryOutputFormat::Html => {
641            tracing::info!("๐ŸŽจ HTML user export completed with shared data optimization");
642        }
643        BinaryOutputFormat::HtmlSystem => {
644            tracing::info!("๐Ÿ”ง HTML system export completed with shared data optimization");
645        }
646        BinaryOutputFormat::HtmlBoth => {
647            tracing::info!("๐ŸŽจ๏ฟฝ Both uHTML exports completed with parallel processing");
648        }
649        BinaryOutputFormat::Both => {
650            if config.enable_parallel_processing {
651                tracing::info!(
652                    "๐Ÿš€ Parallel export completed - maximum efficiency with shared data"
653                );
654            } else {
655                tracing::info!(
656                    "๐Ÿ“ Sequential export completed - consider enabling parallel processing"
657                );
658            }
659        }
660    }
661}
662
663/// **[CONVENIENCE FUNCTIONS]** Easy-to-use wrapper functions with ultra-fast performance
664/// **[MAIN API]** Export to JSON only (preserves existing ultra-fast performance)
665/// Uses the same optimized approach as parse_full_binary_to_json
666pub fn export_binary_to_json<P: AsRef<Path>>(
667    binary_path: P,
668    base_name: &str,
669) -> Result<(), BinaryExportError> {
670    export_binary(binary_path, base_name, BinaryOutputFormat::Json)
671}
672
673/// **[UNIFIED API]** Export binary to dashboard with unified configuration
674///
675/// This is the new unified entry point that replaces all the scattered export functions.
676/// It supports different formats (embedded, lightweight, progressive) and maintains
677/// backward compatibility while providing better performance and flexibility.
678///
679/// # Arguments
680/// * `binary_path` - Path to the binary file
681/// * `project_name` - Name of the project (used for output files)
682/// * `options` - Dashboard export options (format, scope, performance mode)
683///
684/// # Returns
685/// * `DashboardExportStats` - Statistics about the export process
686///
687/// # Examples
688/// ```no_run
689/// use memscope_rs::export::binary::{export_binary_to_dashboard, DashboardOptions, DashboardFormat, DataScope};
690///
691/// // Default lightweight export (recommended)
692/// let stats = export_binary_to_dashboard("data.bin", "my_project", DashboardOptions::default()).unwrap();
693///
694/// // Fast export for quick analysis
695/// let stats = export_binary_to_dashboard("data.bin", "my_project", DashboardOptions::fast_preset()).unwrap();
696///
697/// // Complete analysis with progressive loading
698/// let stats = export_binary_to_dashboard("data.bin", "my_project", DashboardOptions::complete_preset()).unwrap();
699///
700/// // Backward compatible embedded format
701/// let stats = export_binary_to_dashboard("data.bin", "my_project", DashboardOptions::embedded_preset()).unwrap();
702///
703/// // Custom configuration
704/// let options = DashboardOptions::new()
705///     .format(DashboardFormat::Lightweight)
706///     .scope(DataScope::UserOnly)
707///     .parallel_processing(true)
708///     .batch_size(5000);
709/// let stats = export_binary_to_dashboard("data.bin", "my_project", options).unwrap();
710/// ```
711pub fn export_binary_to_dashboard<P: AsRef<Path>>(
712    binary_path: P,
713    project_name: &str,
714    options: DashboardOptions,
715) -> Result<DashboardExportStats, BinaryExportError> {
716    use crate::export::binary::config::DashboardFormat;
717
718    let _start_time = std::time::Instant::now();
719
720    match options.format {
721        DashboardFormat::Embedded => {
722            // Use existing embedded implementation for backward compatibility
723            export_binary_to_html_embedded_impl(binary_path, project_name, &options)
724        }
725        DashboardFormat::Lightweight => {
726            // New lightweight implementation (HTML + separate JSON files)
727            export_binary_to_html_lightweight_impl(binary_path, project_name, &options)
728        }
729        DashboardFormat::Progressive => {
730            // Progressive loading implementation (HTML + lazy-loaded JSON)
731            export_binary_to_html_progressive_impl(binary_path, project_name, &options)
732        }
733    }
734}
735
736/// **[MAIN API]** Export to HTML only with ultra-fast optimizations (user data only)
737/// Uses shared data approach to match JSON performance, generates lightweight HTML
738pub fn export_binary_to_html<P: AsRef<Path>>(
739    binary_path: P,
740    base_name: &str,
741) -> Result<(), BinaryExportError> {
742    // Use the new unified API with lightweight format for better performance
743    let options = DashboardOptions::new()
744        .format(DashboardFormat::Lightweight)
745        .scope(DataScope::UserOnly);
746
747    let _stats = export_binary_to_dashboard(binary_path, base_name, options)?;
748    Ok(())
749}
750
751/// **[MAIN API]** Export to HTML with system data only
752/// Generates HTML dashboard with system allocations (no var_name)
753pub fn export_binary_to_html_system<P: AsRef<Path>>(
754    binary_path: P,
755    base_name: &str,
756) -> Result<(), BinaryExportError> {
757    export_binary(binary_path, base_name, BinaryOutputFormat::HtmlSystem)
758}
759
760/// **[MAIN API]** Export to both user and system HTML dashboards
761/// Generates two separate HTML files for better performance and usability
762pub fn export_binary_to_html_both<P: AsRef<Path>>(
763    binary_path: P,
764    base_name: &str,
765) -> Result<(), BinaryExportError> {
766    export_binary(binary_path, base_name, BinaryOutputFormat::HtmlBoth)
767}
768
769/// **[MAIN API]** Export to both JSON and HTML with parallel processing
770/// Uses shared data loading and parallel generation for maximum efficiency
771pub fn export_binary_to_both<P: AsRef<Path>>(
772    binary_path: P,
773    base_name: &str,
774) -> Result<(), BinaryExportError> {
775    export_binary(binary_path, base_name, BinaryOutputFormat::Both)
776}
777
778/// Export with custom configuration for advanced users
779pub fn export_binary_with_config<P: AsRef<Path>>(
780    binary_path: P,
781    base_name: &str,
782    format: BinaryOutputFormat,
783    config: BinaryExportConfig,
784) -> Result<(), BinaryExportError> {
785    export_binary_optimized(binary_path, base_name, format, Some(config))
786}
787
788// Removed unused legacy function export_binary_to_html_legacy
789
790/// **\[UTILITY\]** Show available export options and performance tips
791pub fn show_export_options() {
792    tracing::info!("๐Ÿš€ Binary Export Options - Optimized Performance");
793    tracing::info!("================================================");
794    tracing::info!("");
795    tracing::info!("๐Ÿ“Š **BASIC USAGE (Unified API):**");
796    tracing::info!("   export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Json)?;      // JSON only (ultra-fast)");
797    tracing::info!("   export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Html)?;      // HTML user data (lightweight)");
798    tracing::info!("   export_binary(\"data.bin\", \"project\", BinaryOutputFormat::HtmlSystem)?; // HTML system data");
799    tracing::info!("   export_binary(\"data.bin\", \"project\", BinaryOutputFormat::HtmlBoth)?;   // Both HTML files (parallel)");
800    tracing::info!("   export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Both)?;      // JSON + HTML user (parallel)");
801    tracing::info!("");
802    tracing::info!("๐Ÿ“Š **CONVENIENCE FUNCTIONS:**");
803    tracing::info!(
804        "   export_binary_to_json(\"data.bin\", \"project\")?;        // JSON only (ultra-fast)"
805    );
806    tracing::info!("   export_binary_to_html(\"data.bin\", \"project\")?;        // HTML user data (lightweight)");
807    tracing::info!(
808        "   export_binary_to_html_system(\"data.bin\", \"project\")?; // HTML system data"
809    );
810    tracing::info!("   export_binary_to_html_both(\"data.bin\", \"project\")?;   // Both HTML files (parallel)");
811    tracing::info!("   export_binary_to_both(\"data.bin\", \"project\")?;        // JSON + HTML user (parallel)");
812    tracing::info!("");
813    tracing::info!("โš™๏ธ  **ADVANCED USAGE:**");
814    tracing::info!("   let config = BinaryExportConfig {{");
815    tracing::info!("       enable_parallel_processing: true,");
816    tracing::info!("       batch_size: 3000,");
817    tracing::info!("       buffer_size: 512 * 1024, // 512KB");
818    tracing::info!("       thread_count: Some(4),");
819    tracing::info!("       ..Default::default()");
820    tracing::info!("   }};");
821    tracing::info!("   export_binary_with_config(\"data.bin\", \"project\", BinaryOutputFormat::Both, config)?;");
822    tracing::info!("");
823    tracing::info!("๐ŸŽฏ **PERFORMANCE TIPS:**");
824    tracing::info!("   โœ… Use BinaryOutputFormat::Json for fastest export (existing performance)");
825    tracing::info!(
826        "   โœ… Use BinaryOutputFormat::Both with parallel processing for maximum efficiency"
827    );
828    tracing::info!("   โœ… Increase batch_size to 3000+ for large files (>100MB)");
829    tracing::info!("   โœ… Set thread_count to match your CPU cores for parallel processing");
830    tracing::info!("   โœ… Use larger buffer_size (512KB+) for very large files");
831    tracing::info!("");
832    tracing::info!("๐Ÿ“ˆ **EXPECTED PERFORMANCE:**");
833    tracing::info!(
834        "   - JSON only: Same ultra-fast performance as parse_full_binary_to_json (<300ms)"
835    );
836    tracing::info!("   - HTML only: Matches JSON performance with shared data optimization");
837    tracing::info!("   - Both formats: 60-80% faster than sequential with parallel processing");
838    tracing::info!(
839        "   - Large files (>1M allocations): Up to 90% improvement with shared data loading"
840    );
841}
842
843/// **[ULTRA-FAST JSON GENERATION]** Generate 5 JSON files in parallel using shared data
844/// This replicates the same ultra-fast approach used in parse_full_binary_to_json
845fn generate_json_files_parallel(
846    allocations: &[crate::core::types::AllocationInfo],
847    base_name: &str,
848    project_dir: &std::path::Path,
849) -> Result<(), BinaryExportError> {
850    use crate::export::binary::parser::BinaryParser;
851    use rayon::prelude::*;
852
853    let json_start = std::time::Instant::now();
854
855    let paths = [
856        project_dir.join(format!("{base_name}_memory_analysis.json")),
857        project_dir.join(format!("{base_name}_lifetime.json")),
858        project_dir.join(format!("{base_name}_performance.json")),
859        project_dir.join(format!("{base_name}_unsafe_ffi.json")),
860        project_dir.join(format!("{base_name}_complex_types.json")),
861    ];
862
863    // **PARALLEL JSON GENERATION**: Same approach as parse_full_binary_to_json
864    let results: Result<Vec<()>, BinaryExportError> = paths
865        .par_iter()
866        .enumerate()
867        .map(|(i, path)| match i {
868            0 => BinaryParser::generate_memory_analysis_json(allocations, path),
869            1 => BinaryParser::generate_lifetime_analysis_json(allocations, path),
870            2 => BinaryParser::generate_performance_analysis_json(allocations, path),
871            3 => BinaryParser::generate_unsafe_ffi_analysis_json(allocations, path),
872            4 => BinaryParser::generate_complex_types_analysis_json(allocations, path),
873            _ => unreachable!(),
874        })
875        .collect();
876
877    results?;
878
879    let json_time = json_start.elapsed();
880    tracing::info!(
881        "๐Ÿš€ Generated 5 JSON files in parallel in {}ms (shared data)",
882        json_time.as_millis()
883    );
884
885    Ok(())
886}
887
888/// **[ULTRA-FAST HTML GENERATION WITH FILTERING]** Generate HTML using shared data with user/system filtering
889fn export_html_with_shared_data_filtered(
890    allocations: &[crate::core::types::AllocationInfo],
891    output_path: &std::path::Path,
892    project_name: &str,
893    _config: &BinaryExportConfig,
894    user_only: bool,
895) -> Result<(), BinaryExportError> {
896    use crate::export::binary::binary_html_writer::BinaryTemplateData;
897    use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
898
899    let start = std::time::Instant::now();
900    let data_type = if user_only { "USER" } else { "SYSTEM" };
901
902    tracing::info!(
903        "๐ŸŽจ Starting ultra-fast {} HTML generation with shared data for {}",
904        data_type,
905        project_name
906    );
907
908    // **FILTERING**: Filter allocations based on user_only flag
909    let filtered_allocations: Vec<_> = allocations
910        .iter()
911        .filter(|alloc| {
912            if user_only {
913                // User allocations: have var_name
914                alloc.var_name.is_some()
915            } else {
916                // System allocations: no var_name
917                alloc.var_name.is_none()
918            }
919        })
920        .collect();
921
922    tracing::info!(
923        "๐Ÿ“Š Filtered {} {} allocations (no I/O overhead)",
924        filtered_allocations.len(),
925        data_type
926    );
927
928    // **OPTIMIZATION**: No I/O needed - data is already loaded and filtered
929    let mut all_allocations = Vec::with_capacity(filtered_allocations.len());
930    let mut total_memory = 0u64;
931    let mut active_count = 0usize;
932
933    // Convert filtered allocations to BinaryAllocationData format
934    for (i, allocation) in filtered_allocations.iter().enumerate() {
935        let binary_data = convert_allocation_to_binary_data(allocation, i)?;
936
937        total_memory += allocation.size as u64;
938        if allocation.is_active() {
939            active_count += 1;
940        }
941
942        all_allocations.push(binary_data);
943    }
944
945    // Create optimized template data with data type tag
946    let template_data = BinaryTemplateData {
947        project_name: format!("{project_name} ({data_type})"),
948        allocations: all_allocations,
949        total_memory_usage: total_memory,
950        peak_memory_usage: total_memory,
951        active_allocations_count: active_count,
952        processing_time_ms: start.elapsed().as_millis() as u64,
953        data_source: format!(
954            "binary_ultra_fast_shared_{}",
955            if user_only { "user" } else { "system" }
956        ),
957        complex_types: None,          // Skip for performance
958        unsafe_ffi: None,             // Skip for performance
959        variable_relationships: None, // Skip for performance
960    };
961
962    // Render HTML using optimized template engine
963    let render_start = std::time::Instant::now();
964    let mut template_engine = BinaryTemplateEngine::new()?;
965    let html_content = template_engine.render_binary_template(&template_data)?;
966    let render_time = render_start.elapsed();
967
968    // Write HTML to file with large buffer for optimal I/O
969    let write_start = std::time::Instant::now();
970    std::fs::write(output_path, html_content)?;
971    let write_time = write_start.elapsed();
972
973    let total_time = start.elapsed();
974    tracing::info!(
975        "โœ… Ultra-fast {} HTML generation completed in {}ms (render: {}ms, write: {}ms)",
976        data_type,
977        total_time.as_millis(),
978        render_time.as_millis(),
979        write_time.as_millis()
980    );
981
982    Ok(())
983}
984
985/// Convert AllocationInfo to BinaryAllocationData for template processing
986fn convert_allocation_to_binary_data(
987    allocation: &crate::core::types::AllocationInfo,
988    _index: usize,
989) -> Result<crate::export::binary::binary_html_writer::BinaryAllocationData, BinaryExportError> {
990    use std::collections::HashMap;
991
992    Ok(
993        crate::export::binary::binary_html_writer::BinaryAllocationData {
994            id: allocation.ptr as u64,
995            size: allocation.size,
996            type_name: allocation
997                .type_name
998                .clone()
999                .unwrap_or_else(|| "unknown_type".to_string()),
1000            scope_name: allocation
1001                .scope_name
1002                .clone()
1003                .unwrap_or_else(|| "global".to_string()),
1004            timestamp_alloc: allocation.timestamp_alloc,
1005            is_active: allocation.is_active(),
1006            ptr: allocation.ptr,
1007            thread_id: allocation.thread_id.clone(),
1008            var_name: allocation.var_name.clone(),
1009            borrow_count: allocation.borrow_count,
1010            is_leaked: allocation.is_leaked,
1011            lifetime_ms: allocation.lifetime_ms,
1012            optional_fields: HashMap::new(), // Empty for now, can be extended later
1013        },
1014    )
1015}
1016
1017// ============================================================================
1018// UNIFIED API IMPLEMENTATION FUNCTIONS
1019// ============================================================================
1020
1021/// Implementation for embedded format (backward compatible)
1022fn export_binary_to_html_embedded_impl<P: AsRef<Path>>(
1023    binary_path: P,
1024    project_name: &str,
1025    options: &DashboardOptions,
1026) -> Result<DashboardExportStats, BinaryExportError> {
1027    let _start_time = std::time::Instant::now();
1028
1029    // For now, use the existing export_binary function as fallback
1030    export_binary(binary_path, project_name, BinaryOutputFormat::Html)?;
1031
1032    // Calculate basic stats - try multiple possible paths
1033    let possible_paths = vec![
1034        format!(
1035            "MemoryAnalysis/{}/{}_dashboard.html",
1036            project_name, project_name
1037        ),
1038        format!(
1039            "MemoryAnalysis/{}/{}_user_dashboard.html",
1040            project_name, project_name
1041        ),
1042        format!(
1043            "MemoryAnalysis/{}/{}_system_dashboard.html",
1044            project_name, project_name
1045        ),
1046    ];
1047
1048    let mut html_size = 0;
1049    for path in &possible_paths {
1050        if let Ok(metadata) = std::fs::metadata(path) {
1051            html_size = metadata.len() as usize;
1052            tracing::debug!("Found HTML file: {} ({} bytes)", path, html_size);
1053            break;
1054        }
1055    }
1056
1057    Ok(DashboardExportStats {
1058        total_files_generated: 1,
1059        html_size,
1060        total_json_size: 0, // All data embedded in HTML
1061        processing_time_ms: _start_time.elapsed().as_millis() as u64,
1062        allocations_processed: 0, // Will be updated with actual processing count
1063        format_used: DashboardFormat::Embedded,
1064        scope_used: options.scope.clone(),
1065    })
1066}
1067
1068/// Implementation for lightweight format (HTML + separate JSON files)
1069fn export_binary_to_html_lightweight_impl<P: AsRef<Path>>(
1070    binary_path: P,
1071    project_name: &str,
1072    options: &DashboardOptions,
1073) -> Result<DashboardExportStats, BinaryExportError> {
1074    let _start_time = std::time::Instant::now();
1075
1076    // For now, use embedded implementation as placeholder
1077    // Lightweight format implementation - optimized for performance
1078    tracing::info!("๐Ÿš€ Lightweight format requested - using embedded format as fallback for now");
1079
1080    let mut stats = export_binary_to_html_embedded_impl(binary_path, project_name, options)?;
1081    stats.format_used = DashboardFormat::Lightweight;
1082
1083    Ok(stats)
1084}
1085
1086/// Implementation for progressive format (HTML + lazy-loaded JSON)
1087fn export_binary_to_html_progressive_impl<P: AsRef<Path>>(
1088    binary_path: P,
1089    project_name: &str,
1090    options: &DashboardOptions,
1091) -> Result<DashboardExportStats, BinaryExportError> {
1092    let _start_time = std::time::Instant::now();
1093
1094    // For now, use embedded implementation as placeholder
1095    // Progressive format implementation - loads data incrementally
1096    tracing::info!("๐Ÿš€ Progressive format requested - using embedded format as fallback for now");
1097
1098    let mut stats = export_binary_to_html_embedded_impl(binary_path, project_name, options)?;
1099    stats.format_used = DashboardFormat::Progressive;
1100
1101    Ok(stats)
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106    use super::*;
1107
1108    #[derive(serde::Serialize)]
1109    struct AllocationData {
1110        id: u64,
1111        size: u64,
1112        type_name: String,
1113        location: String,
1114        timestamp: u64,
1115        status: String,
1116    }
1117
1118    impl From<crate::core::types::AllocationInfo> for AllocationData {
1119        fn from(alloc: crate::core::types::AllocationInfo) -> Self {
1120            Self {
1121                id: alloc.ptr as u64, // Use ptr as unique ID
1122                size: alloc.size as u64,
1123                type_name: alloc
1124                    .type_name
1125                    .clone()
1126                    .unwrap_or_else(|| "Unknown".to_string()),
1127                location: alloc
1128                    .scope_name
1129                    .clone()
1130                    .unwrap_or_else(|| "Unknown".to_string()),
1131                timestamp: alloc.timestamp_alloc,
1132                status: if alloc.is_active() {
1133                    "Active".to_string()
1134                } else {
1135                    "Freed".to_string()
1136                },
1137            }
1138        }
1139    }
1140
1141    #[test]
1142    fn test_binary_output_format_debug_clone_partialeq() {
1143        // Test Debug trait
1144        let format = BinaryOutputFormat::Json;
1145        let debug_str = format!("{format:?}");
1146        assert!(debug_str.contains("Json"));
1147
1148        // Test Clone trait
1149        let format1 = BinaryOutputFormat::Html;
1150        let format2 = format1;
1151        assert_eq!(format1, format2);
1152
1153        // Test PartialEq trait
1154        assert_eq!(BinaryOutputFormat::Json, BinaryOutputFormat::Json);
1155        assert_eq!(BinaryOutputFormat::Html, BinaryOutputFormat::Html);
1156        assert_eq!(
1157            BinaryOutputFormat::HtmlSystem,
1158            BinaryOutputFormat::HtmlSystem
1159        );
1160        assert_eq!(BinaryOutputFormat::HtmlBoth, BinaryOutputFormat::HtmlBoth);
1161        assert_eq!(BinaryOutputFormat::Both, BinaryOutputFormat::Both);
1162
1163        assert_ne!(BinaryOutputFormat::Json, BinaryOutputFormat::Html);
1164        assert_ne!(BinaryOutputFormat::Html, BinaryOutputFormat::HtmlSystem);
1165        assert_ne!(BinaryOutputFormat::HtmlSystem, BinaryOutputFormat::HtmlBoth);
1166        assert_ne!(BinaryOutputFormat::HtmlBoth, BinaryOutputFormat::Both);
1167    }
1168
1169    #[test]
1170    fn test_binary_export_config_default() {
1171        let config = BinaryExportConfig::default();
1172
1173        assert!(config.enable_parallel_processing);
1174        assert_eq!(config.buffer_size, 256 * 1024);
1175        assert_eq!(config.batch_size, 2000);
1176        assert!(config.enable_streaming);
1177        assert!(config.thread_count.is_none());
1178    }
1179
1180    #[test]
1181    fn test_binary_export_config_new() {
1182        let config1 = BinaryExportConfig::new();
1183        let config2 = BinaryExportConfig::default();
1184
1185        assert_eq!(
1186            config1.enable_parallel_processing,
1187            config2.enable_parallel_processing
1188        );
1189        assert_eq!(config1.buffer_size, config2.buffer_size);
1190        assert_eq!(config1.batch_size, config2.batch_size);
1191        assert_eq!(config1.enable_streaming, config2.enable_streaming);
1192        assert_eq!(config1.thread_count, config2.thread_count);
1193    }
1194
1195    #[test]
1196    fn test_binary_export_config_fast() {
1197        let config = BinaryExportConfig::fast();
1198
1199        assert!(config.enable_parallel_processing);
1200        assert_eq!(config.buffer_size, 512 * 1024);
1201        assert_eq!(config.batch_size, 3000);
1202        assert!(config.enable_streaming);
1203        assert!(config.thread_count.is_none());
1204    }
1205
1206    #[test]
1207    fn test_binary_export_config_large_files() {
1208        let config = BinaryExportConfig::large_files();
1209
1210        assert!(config.enable_parallel_processing);
1211        assert_eq!(config.buffer_size, 1024 * 1024);
1212        assert_eq!(config.batch_size, 5000);
1213        assert!(config.enable_streaming);
1214        assert!(config.thread_count.is_none());
1215    }
1216
1217    #[test]
1218    fn test_binary_export_config_builder_methods() {
1219        let config = BinaryExportConfig::new()
1220            .parallel_processing(false)
1221            .buffer_size(128 * 1024)
1222            .batch_size(1000)
1223            .streaming(false)
1224            .thread_count(Some(4));
1225
1226        assert!(!config.enable_parallel_processing);
1227        assert_eq!(config.buffer_size, 128 * 1024);
1228        assert_eq!(config.batch_size, 1000);
1229        assert!(!config.enable_streaming);
1230        assert_eq!(config.thread_count, Some(4));
1231    }
1232
1233    #[test]
1234    fn test_binary_export_config_debug_clone() {
1235        let config = BinaryExportConfig::default();
1236
1237        // Test Debug trait
1238        let debug_str = format!("{config:?}");
1239        assert!(debug_str.contains("BinaryExportConfig"));
1240        assert!(debug_str.contains("enable_parallel_processing"));
1241        assert!(debug_str.contains("buffer_size"));
1242
1243        // Test Clone trait
1244        let cloned_config = config.clone();
1245        assert_eq!(
1246            cloned_config.enable_parallel_processing,
1247            config.enable_parallel_processing
1248        );
1249        assert_eq!(cloned_config.buffer_size, config.buffer_size);
1250        assert_eq!(cloned_config.batch_size, config.batch_size);
1251        assert_eq!(cloned_config.enable_streaming, config.enable_streaming);
1252        assert_eq!(cloned_config.thread_count, config.thread_count);
1253    }
1254
1255    #[test]
1256    fn test_allocation_data_from_allocation_info() {
1257        let allocation_info = crate::core::types::AllocationInfo {
1258            ptr: 0x1000,
1259            size: 64,
1260            var_name: Some("test_var".to_string()),
1261            type_name: Some("String".to_string()),
1262            scope_name: Some("main".to_string()),
1263            timestamp_alloc: 1234567890,
1264            timestamp_dealloc: None,
1265            thread_id: "main".to_string(),
1266            borrow_count: 0,
1267            stack_trace: None,
1268            is_leaked: false,
1269            lifetime_ms: Some(100),
1270            borrow_info: None,
1271            clone_info: None,
1272            ownership_history_available: false,
1273            smart_pointer_info: None,
1274            memory_layout: None,
1275            generic_info: None,
1276            dynamic_type_info: None,
1277            runtime_state: None,
1278            stack_allocation: None,
1279            temporary_object: None,
1280            fragmentation_analysis: None,
1281            generic_instantiation: None,
1282            type_relationships: None,
1283            type_usage: None,
1284            function_call_tracking: None,
1285            lifecycle_tracking: None,
1286            access_tracking: None,
1287            drop_chain_analysis: None,
1288        };
1289
1290        let allocation_data = AllocationData::from(allocation_info);
1291
1292        assert_eq!(allocation_data.id, 0x1000);
1293        assert_eq!(allocation_data.size, 64);
1294        assert_eq!(allocation_data.type_name, "String");
1295        assert_eq!(allocation_data.location, "main");
1296        assert_eq!(allocation_data.timestamp, 1234567890);
1297        assert_eq!(allocation_data.status, "Active");
1298    }
1299
1300    #[test]
1301    fn test_allocation_data_from_allocation_info_with_defaults() {
1302        let allocation_info = crate::core::types::AllocationInfo {
1303            ptr: 0x2000,
1304            size: 128,
1305            var_name: None,
1306            type_name: None,  // Should default to "Unknown"
1307            scope_name: None, // Should default to "Unknown"
1308            timestamp_alloc: 1234567900,
1309            timestamp_dealloc: Some(1234567950), // Should make status "Freed"
1310            thread_id: "worker".to_string(),
1311            borrow_count: 0,
1312            stack_trace: None,
1313            is_leaked: false,
1314            lifetime_ms: Some(50),
1315            borrow_info: None,
1316            clone_info: None,
1317            ownership_history_available: false,
1318            smart_pointer_info: None,
1319            memory_layout: None,
1320            generic_info: None,
1321            dynamic_type_info: None,
1322            runtime_state: None,
1323            stack_allocation: None,
1324            temporary_object: None,
1325            fragmentation_analysis: None,
1326            generic_instantiation: None,
1327            type_relationships: None,
1328            type_usage: None,
1329            function_call_tracking: None,
1330            lifecycle_tracking: None,
1331            access_tracking: None,
1332            drop_chain_analysis: None,
1333        };
1334
1335        let allocation_data = AllocationData::from(allocation_info);
1336
1337        assert_eq!(allocation_data.id, 0x2000);
1338        assert_eq!(allocation_data.size, 128);
1339        assert_eq!(allocation_data.type_name, "Unknown");
1340        assert_eq!(allocation_data.location, "Unknown");
1341        assert_eq!(allocation_data.timestamp, 1234567900);
1342        assert_eq!(allocation_data.status, "Freed");
1343    }
1344
1345    #[test]
1346    fn test_show_export_options() {
1347        // This function just prints information, so we test that it doesn't panic
1348        show_export_options();
1349    }
1350
1351    #[test]
1352    fn test_provide_performance_feedback() {
1353        let config = BinaryExportConfig::default();
1354
1355        // Test excellent performance (< 1000ms)
1356        let excellent_time = std::time::Duration::from_millis(500);
1357        provide_performance_feedback(BinaryOutputFormat::Json, &config, excellent_time);
1358
1359        // Test good performance (1000-5000ms)
1360        let good_time = std::time::Duration::from_millis(2000);
1361        provide_performance_feedback(BinaryOutputFormat::Html, &config, good_time);
1362
1363        // Test slow performance (> 5000ms)
1364        let slow_time = std::time::Duration::from_millis(8000);
1365        provide_performance_feedback(BinaryOutputFormat::Both, &config, slow_time);
1366
1367        // Test with different configurations
1368        let slow_config = BinaryExportConfig::default()
1369            .parallel_processing(false)
1370            .batch_size(500);
1371        provide_performance_feedback(BinaryOutputFormat::Both, &slow_config, slow_time);
1372    }
1373
1374    #[test]
1375    fn test_convert_allocation_to_binary_data() {
1376        let allocation = crate::core::types::AllocationInfo {
1377            ptr: 0x1000,
1378            size: 64,
1379            var_name: Some("test_var".to_string()),
1380            type_name: Some("String".to_string()),
1381            scope_name: Some("main".to_string()),
1382            timestamp_alloc: 1234567890,
1383            timestamp_dealloc: None,
1384            thread_id: "main".to_string(),
1385            borrow_count: 2,
1386            stack_trace: None,
1387            is_leaked: false,
1388            lifetime_ms: Some(100),
1389            borrow_info: None,
1390            clone_info: None,
1391            ownership_history_available: false,
1392            smart_pointer_info: None,
1393            memory_layout: None,
1394            generic_info: None,
1395            dynamic_type_info: None,
1396            runtime_state: None,
1397            stack_allocation: None,
1398            temporary_object: None,
1399            fragmentation_analysis: None,
1400            generic_instantiation: None,
1401            type_relationships: None,
1402            type_usage: None,
1403            function_call_tracking: None,
1404            lifecycle_tracking: None,
1405            access_tracking: None,
1406            drop_chain_analysis: None,
1407        };
1408
1409        let result = convert_allocation_to_binary_data(&allocation, 0);
1410        assert!(result.is_ok());
1411
1412        let binary_data = result.unwrap();
1413        assert_eq!(binary_data.id, 0x1000);
1414        assert_eq!(binary_data.size, 64);
1415        assert_eq!(binary_data.type_name, "String");
1416        assert_eq!(binary_data.scope_name, "main");
1417        assert_eq!(binary_data.timestamp_alloc, 1234567890);
1418        assert!(binary_data.is_active);
1419        assert_eq!(binary_data.ptr, 0x1000);
1420        assert_eq!(binary_data.thread_id, "main");
1421        assert_eq!(binary_data.var_name, Some("test_var".to_string()));
1422        assert_eq!(binary_data.borrow_count, 2);
1423        assert!(!binary_data.is_leaked);
1424        assert_eq!(binary_data.lifetime_ms, Some(100));
1425        assert!(binary_data.optional_fields.is_empty());
1426    }
1427
1428    #[test]
1429    fn test_convert_allocation_to_binary_data_with_defaults() {
1430        let allocation = crate::core::types::AllocationInfo {
1431            ptr: 0x2000,
1432            size: 128,
1433            var_name: None,
1434            type_name: None,  // Should default to "unknown_type"
1435            scope_name: None, // Should default to "global"
1436            timestamp_alloc: 1234567900,
1437            timestamp_dealloc: Some(1234567950),
1438            thread_id: "worker".to_string(),
1439            borrow_count: 0,
1440            stack_trace: None,
1441            is_leaked: true,
1442            lifetime_ms: None,
1443            borrow_info: None,
1444            clone_info: None,
1445            ownership_history_available: false,
1446            smart_pointer_info: None,
1447            memory_layout: None,
1448            generic_info: None,
1449            dynamic_type_info: None,
1450            runtime_state: None,
1451            stack_allocation: None,
1452            temporary_object: None,
1453            fragmentation_analysis: None,
1454            generic_instantiation: None,
1455            type_relationships: None,
1456            type_usage: None,
1457            function_call_tracking: None,
1458            lifecycle_tracking: None,
1459            access_tracking: None,
1460            drop_chain_analysis: None,
1461        };
1462
1463        let result = convert_allocation_to_binary_data(&allocation, 1);
1464        assert!(result.is_ok());
1465
1466        let binary_data = result.unwrap();
1467        assert_eq!(binary_data.id, 0x2000);
1468        assert_eq!(binary_data.size, 128);
1469        assert_eq!(binary_data.type_name, "unknown_type");
1470        assert_eq!(binary_data.scope_name, "global");
1471        assert_eq!(binary_data.timestamp_alloc, 1234567900);
1472        assert!(!binary_data.is_active); // Should be false due to timestamp_dealloc
1473        assert_eq!(binary_data.ptr, 0x2000);
1474        assert_eq!(binary_data.thread_id, "worker");
1475        assert_eq!(binary_data.var_name, None);
1476        assert_eq!(binary_data.borrow_count, 0);
1477        assert!(binary_data.is_leaked);
1478        assert_eq!(binary_data.lifetime_ms, None);
1479    }
1480}