1use crate::core::types::AllocationInfo;
8use crate::export::binary::complex_type_analyzer::{ComplexTypeAnalysis, ComplexTypeAnalyzer};
9use crate::export::binary::error::BinaryExportError;
10use crate::export::binary::ffi_safety_analyzer::{FfiSafetyAnalysis, FfiSafetyAnalyzer};
11use crate::export::binary::selective_reader::AllocationField;
12use crate::export::binary::variable_relationship_analyzer::{
13 VariableRelationshipAnalysis, VariableRelationshipAnalyzer,
14};
15
16use std::collections::{HashMap, HashSet};
17use std::io::{BufWriter, Write};
18use std::time::Instant;
19
20#[derive(Debug, Clone)]
22pub struct BinaryHtmlWriterConfig {
23 pub buffer_size: usize,
25
26 pub max_memory_before_flush: usize,
28
29 pub chunk_size: usize,
31
32 pub enable_intelligent_buffering: bool,
34
35 pub enable_data_compression: bool,
37
38 pub parallel_threshold: usize,
40}
41
42impl Default for BinaryHtmlWriterConfig {
43 fn default() -> Self {
44 Self {
45 buffer_size: 256 * 1024, max_memory_before_flush: 32 * 1024 * 1024, chunk_size: 1000,
48 enable_intelligent_buffering: true,
49 enable_data_compression: false,
50 parallel_threshold: 5000,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Default)]
57pub struct BinaryHtmlStats {
58 pub allocations_processed: u64,
60
61 pub total_html_size: usize,
63
64 pub template_render_time_ms: u64,
66
67 pub data_processing_time_ms: u64,
69
70 pub peak_memory_usage: usize,
72
73 pub buffer_flushes: u32,
75
76 pub total_processing_time_ms: u64,
78
79 pub avg_processing_speed: f64,
81
82 pub memory_efficiency: f64,
84}
85
86impl BinaryHtmlStats {
87 pub fn processing_throughput(&self) -> f64 {
89 if self.total_processing_time_ms == 0 {
90 0.0
91 } else {
92 (self.allocations_processed as f64 * 1000.0) / self.total_processing_time_ms as f64
93 }
94 }
95
96 pub fn memory_efficiency_ratio(&self) -> f64 {
98 if self.peak_memory_usage == 0 {
99 0.0
100 } else {
101 (self.allocations_processed as f64) / (self.peak_memory_usage as f64 / 1024.0 / 1024.0)
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
108pub struct BinaryAllocationData {
109 pub id: u64,
110 pub size: usize,
111 pub type_name: String,
112 pub scope_name: String,
113 pub timestamp_alloc: u64,
114 pub is_active: bool,
115 pub ptr: usize,
116 pub thread_id: String,
117 pub var_name: Option<String>,
118 pub borrow_count: usize,
119 pub is_leaked: bool,
120 pub lifetime_ms: Option<u64>,
121 pub optional_fields: HashMap<String, BinaryFieldValue>,
123}
124
125#[derive(Debug, Clone)]
127pub enum BinaryFieldValue {
128 String(String),
129 Number(u64),
130 Boolean(bool),
131 Array(Vec<String>),
132 Optional(Option<Box<BinaryFieldValue>>),
133}
134
135impl BinaryAllocationData {
136 pub fn from_allocation(
138 allocation: &AllocationInfo,
139 requested_fields: &HashSet<AllocationField>,
140 ) -> Result<Self, BinaryExportError> {
141 let mut optional_fields = HashMap::new();
142
143 if requested_fields.contains(&AllocationField::StackTrace) {
145 if let Some(ref stack_trace) = allocation.stack_trace {
146 optional_fields.insert(
147 "stack_trace".to_string(),
148 BinaryFieldValue::Array(stack_trace.clone()),
149 );
150 }
151 }
152
153 if requested_fields.contains(&AllocationField::TimestampDealloc) {
154 if let Some(timestamp_dealloc) = allocation.timestamp_dealloc {
155 optional_fields.insert(
156 "timestamp_dealloc".to_string(),
157 BinaryFieldValue::Number(timestamp_dealloc),
158 );
159 }
160 }
161
162 if requested_fields.contains(&AllocationField::SmartPointerInfo)
164 && allocation.smart_pointer_info.is_some()
165 {
166 optional_fields.insert(
167 "smart_pointer_info".to_string(),
168 BinaryFieldValue::String("present".to_string()),
169 );
170 }
171
172 Ok(Self {
173 id: allocation.ptr as u64,
174 size: allocation.size,
175 type_name: allocation
176 .type_name
177 .clone()
178 .unwrap_or_else(|| "Unknown".to_string()),
179 scope_name: allocation
180 .scope_name
181 .clone()
182 .unwrap_or_else(|| "global".to_string()),
183 timestamp_alloc: allocation.timestamp_alloc,
184 is_active: allocation.timestamp_dealloc.is_none(),
185 ptr: allocation.ptr,
186 thread_id: allocation.thread_id.clone(),
187 var_name: allocation.var_name.clone(),
188 borrow_count: allocation.borrow_count,
189 is_leaked: allocation.is_leaked,
190 lifetime_ms: allocation.lifetime_ms,
191 optional_fields,
192 })
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct BinaryTemplateData {
199 pub project_name: String,
200 pub allocations: Vec<BinaryAllocationData>,
201 pub total_memory_usage: u64,
202 pub peak_memory_usage: u64,
203 pub active_allocations_count: usize,
204 pub processing_time_ms: u64,
205 pub data_source: String,
206 pub complex_types: Option<ComplexTypeAnalysis>,
207 pub unsafe_ffi: Option<FfiSafetyAnalysis>,
208 pub variable_relationships: Option<VariableRelationshipAnalysis>,
209}
210
211#[derive(Debug)]
213struct IntelligentBuffer {
214 current_usage: usize,
216 target_size: usize,
218 writes_since_flush: u32,
220 avg_write_size: f64,
222 last_flush_time: Instant,
224}
225
226impl IntelligentBuffer {
227 fn new(target_size: usize) -> Self {
228 let buffer = Self {
229 current_usage: 0,
230 target_size,
231 writes_since_flush: 0,
232 avg_write_size: 0.0,
233 last_flush_time: Instant::now(),
234 };
235
236 tracing::debug!(
238 "Created IntelligentBuffer with target size: {}",
239 buffer.target_size
240 );
241 buffer
242 }
243
244 fn reset_after_flush(&mut self) {
245 self.current_usage = 0;
246 self.writes_since_flush = 0;
247 self.avg_write_size = 0.0;
248 self.last_flush_time = Instant::now();
249 }
250}
251
252pub struct BinaryHtmlWriter<W: Write> {
254 writer: BufWriter<W>,
256
257 config: BinaryHtmlWriterConfig,
259
260 stats: BinaryHtmlStats,
262
263 start_time: Instant,
265
266 current_memory_usage: usize,
268
269 allocation_buffer: Vec<BinaryAllocationData>,
271
272 all_allocations: Vec<AllocationInfo>,
274
275 intelligent_buffer: IntelligentBuffer,
277}
278
279impl<W: Write> BinaryHtmlWriter<W> {
280 pub fn new(writer: W) -> Result<Self, BinaryExportError> {
282 Self::with_config(writer, BinaryHtmlWriterConfig::default())
283 }
284
285 pub fn with_config(
287 writer: W,
288 config: BinaryHtmlWriterConfig,
289 ) -> Result<Self, BinaryExportError> {
290 let start_time = Instant::now();
291
292 let buffered_writer = BufWriter::with_capacity(config.buffer_size, writer);
294
295 let stats = BinaryHtmlStats::default();
296
297 Ok(Self {
298 writer: buffered_writer,
299 config: config.clone(),
300 stats,
301 start_time,
302 current_memory_usage: 0,
303 allocation_buffer: Vec::with_capacity(config.chunk_size),
304 all_allocations: Vec::new(),
305 intelligent_buffer: IntelligentBuffer::new(config.buffer_size / 4),
306 })
307 }
308
309 pub fn write_binary_allocation(
311 &mut self,
312 allocation: &AllocationInfo,
313 requested_fields: &HashSet<AllocationField>,
314 ) -> Result<(), BinaryExportError> {
315 let write_start = Instant::now();
316
317 self.all_allocations.push(allocation.clone());
319
320 let binary_data = BinaryAllocationData::from_allocation(allocation, requested_fields)?;
322
323 self.allocation_buffer.push(binary_data);
325
326 self.current_memory_usage += std::mem::size_of::<BinaryAllocationData>();
328
329 if self.current_memory_usage >= self.config.max_memory_before_flush {
331 self.flush_allocation_buffer()?;
332 }
333
334 self.stats.allocations_processed += 1;
335 self.stats.data_processing_time_ms += write_start.elapsed().as_millis() as u64;
336
337 Ok(())
338 }
339
340 pub fn write_binary_allocation_batch(
342 &mut self,
343 allocations: &[AllocationInfo],
344 requested_fields: &HashSet<AllocationField>,
345 ) -> Result<(), BinaryExportError> {
346 let batch_start = Instant::now();
347
348 if allocations.len() >= self.config.parallel_threshold {
350 self.write_allocation_batch_parallel(allocations, requested_fields)?;
351 } else {
352 self.write_allocation_batch_serial(allocations, requested_fields)?;
353 }
354
355 self.stats.data_processing_time_ms += batch_start.elapsed().as_millis() as u64;
356 Ok(())
357 }
358
359 fn write_allocation_batch_serial(
361 &mut self,
362 allocations: &[AllocationInfo],
363 requested_fields: &HashSet<AllocationField>,
364 ) -> Result<(), BinaryExportError> {
365 for allocation in allocations {
366 self.write_binary_allocation(allocation, requested_fields)?;
367 }
368 Ok(())
369 }
370
371 fn write_allocation_batch_parallel(
373 &mut self,
374 allocations: &[AllocationInfo],
375 requested_fields: &HashSet<AllocationField>,
376 ) -> Result<(), BinaryExportError> {
377 self.write_allocation_batch_serial(allocations, requested_fields)
380 }
381
382 fn flush_allocation_buffer(&mut self) -> Result<(), BinaryExportError> {
384 self.allocation_buffer.clear();
387 self.current_memory_usage = 0;
388 self.stats.buffer_flushes += 1;
389 self.intelligent_buffer.reset_after_flush();
390 Ok(())
391 }
392
393 pub fn finalize_with_binary_template(
395 &mut self,
396 project_name: &str,
397 ) -> Result<BinaryHtmlStats, BinaryExportError> {
398 let finalize_start = Instant::now();
399
400 let template_data = self.build_binary_template_data(project_name)?;
402
403 let html_content = self.render_binary_template(&template_data)?;
405
406 self.writer.write_all(html_content.as_bytes())?;
408 self.writer.flush()?;
409
410 self.stats.total_html_size = html_content.len();
412 self.stats.total_processing_time_ms = self.start_time.elapsed().as_millis() as u64;
413 self.stats.template_render_time_ms = finalize_start.elapsed().as_millis() as u64;
414 self.stats.avg_processing_speed = self.stats.processing_throughput();
415 self.stats.memory_efficiency = self.stats.memory_efficiency_ratio();
416
417 Ok(self.stats.clone())
418 }
419
420 fn build_binary_template_data(
422 &self,
423 project_name: &str,
424 ) -> Result<BinaryTemplateData, BinaryExportError> {
425 let total_memory: u64 = self.allocation_buffer.iter().map(|a| a.size as u64).sum();
426 let peak_memory = total_memory; let active_count = self
428 .allocation_buffer
429 .iter()
430 .filter(|a| a.is_active)
431 .count();
432
433 let complex_types = if !self.all_allocations.is_empty() {
435 match ComplexTypeAnalyzer::analyze_allocations(&self.all_allocations) {
436 Ok(analysis) => Some(analysis),
437 Err(e) => {
438 tracing::warn!("Complex type analysis failed: {}", e);
439 None
440 }
441 }
442 } else {
443 None
444 };
445
446 let unsafe_ffi = if !self.all_allocations.is_empty() {
448 match FfiSafetyAnalyzer::analyze_allocations(&self.all_allocations) {
449 Ok(analysis) => Some(analysis),
450 Err(e) => {
451 tracing::warn!("FFI safety analysis failed: {}", e);
452 None
453 }
454 }
455 } else {
456 None
457 };
458
459 let variable_relationships = if !self.all_allocations.is_empty() {
461 match VariableRelationshipAnalyzer::analyze_allocations(&self.all_allocations) {
462 Ok(analysis) => Some(analysis),
463 Err(e) => {
464 tracing::warn!("Variable relationship analysis failed: {}", e);
465 None
466 }
467 }
468 } else {
469 None
470 };
471
472 Ok(BinaryTemplateData {
473 project_name: project_name.to_string(),
474 allocations: self.allocation_buffer.clone(),
475 total_memory_usage: total_memory,
476 peak_memory_usage: peak_memory,
477 active_allocations_count: active_count,
478 processing_time_ms: self.stats.data_processing_time_ms,
479 data_source: "binary_direct".to_string(),
480 complex_types,
481 unsafe_ffi,
482 variable_relationships,
483 })
484 }
485
486 fn render_binary_template(
488 &self,
489 data: &BinaryTemplateData,
490 ) -> Result<String, BinaryExportError> {
491 use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
492
493 let mut template_engine = BinaryTemplateEngine::new().map_err(|e| {
495 BinaryExportError::CorruptedData(format!("Failed to create template engine: {e}"))
496 })?;
497
498 template_engine.render_binary_template(data)
500 }
501
502 fn _convert_to_json_format(
504 &self,
505 data: &BinaryTemplateData,
506 ) -> Result<String, BinaryExportError> {
507 use serde_json::json;
508
509 let allocations_json: Vec<serde_json::Value> = data
510 .allocations
511 .iter()
512 .map(|alloc| {
513 json!({
514 "id": alloc.id,
515 "size": alloc.size,
516 "type_name": alloc.type_name,
517 "scope_name": alloc.scope_name,
518 "timestamp_alloc": alloc.timestamp_alloc,
519 "is_active": alloc.is_active,
520 "ptr": format!("0x{:x}", alloc.ptr),
521 "thread_id": alloc.thread_id,
522 "var_name": alloc.var_name,
523 "borrow_count": alloc.borrow_count,
524 "is_leaked": alloc.is_leaked,
525 "lifetime_ms": alloc.lifetime_ms
526 })
527 })
528 .collect();
529
530 let mut dashboard_data = json!({
531 "project_name": data.project_name,
532 "data_source": data.data_source,
533 "summary": {
534 "total_allocations": data.allocations.len(),
535 "total_memory": data.total_memory_usage,
536 "peak_memory": data.peak_memory_usage,
537 "active_allocations": data.active_allocations_count
538 },
539 "memory_analysis": {
540 "allocations": allocations_json,
541 "memory_timeline": [],
542 "size_distribution": []
543 },
544 "performance_metrics": {
545 "export_time_ms": data.processing_time_ms,
546 "data_source": "binary_direct",
547 "throughput_allocations_per_sec": self.stats.processing_throughput()
548 }
549 });
550
551 if let Some(ref complex_types) = data.complex_types {
553 dashboard_data["complex_types"] = serde_json::to_value(complex_types).map_err(|e| {
554 BinaryExportError::SerializationError(format!(
555 "Complex types serialization failed: {e}"
556 ))
557 })?;
558 }
559
560 if let Some(ref unsafe_ffi) = data.unsafe_ffi {
562 dashboard_data["unsafe_ffi"] = serde_json::to_value(unsafe_ffi).map_err(|e| {
563 BinaryExportError::SerializationError(format!(
564 "FFI safety analysis serialization failed: {e}"
565 ))
566 })?;
567 }
568
569 if let Some(ref variable_relationships) = data.variable_relationships {
571 dashboard_data["variable_relationships"] = serde_json::to_value(variable_relationships)
572 .map_err(|e| {
573 BinaryExportError::SerializationError(format!(
574 "Variable relationship analysis serialization failed: {e}",
575 ))
576 })?;
577 }
578
579 serde_json::to_string_pretty(&dashboard_data).map_err(|e| {
580 BinaryExportError::SerializationError(format!("JSON serialization failed: {e}"))
581 })
582 }
583
584 pub fn get_stats(&self) -> &BinaryHtmlStats {
586 &self.stats
587 }
588
589 fn _update_peak_memory_usage(&mut self) {
591 if self.current_memory_usage > self.stats.peak_memory_usage {
592 self.stats.peak_memory_usage = self.current_memory_usage;
593 }
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use super::*;
600 use std::io::Cursor;
601
602 fn create_test_allocation() -> AllocationInfo {
603 AllocationInfo {
604 ptr: 0x1000,
605 size: 1024,
606 var_name: Some("test_var".to_string()),
607 type_name: Some("Vec<u8>".to_string()),
608 scope_name: Some("main".to_string()),
609 timestamp_alloc: 1234567890,
610 timestamp_dealloc: None,
611 thread_id: "main".to_string(),
612 borrow_count: 0,
613 stack_trace: Some(vec!["frame1".to_string(), "frame2".to_string()]),
614 is_leaked: false,
615 lifetime_ms: Some(1000),
616 borrow_info: None,
617 clone_info: None,
618 ownership_history_available: false,
619 smart_pointer_info: None,
620 memory_layout: None,
621 generic_info: None,
622 dynamic_type_info: None,
623 runtime_state: None,
624 stack_allocation: None,
625 temporary_object: None,
626 fragmentation_analysis: None,
627 generic_instantiation: None,
628 type_relationships: None,
629 type_usage: None,
630 function_call_tracking: None,
631 lifecycle_tracking: None,
632 access_tracking: None,
633 drop_chain_analysis: None,
634 }
635 }
636
637 #[test]
638 fn test_binary_html_writer_creation() {
639 let buffer = Vec::new();
640 let cursor = Cursor::new(buffer);
641 let writer = BinaryHtmlWriter::new(cursor);
642 assert!(writer.is_ok());
643 }
644
645 #[test]
646 fn test_binary_allocation_data_conversion() {
647 let allocation = create_test_allocation();
648 let fields = AllocationField::all_basic_fields();
649
650 let binary_data = BinaryAllocationData::from_allocation(&allocation, &fields);
651 assert!(binary_data.is_ok());
652
653 let data = binary_data.expect("Failed to get test value");
654 assert_eq!(data.size, 1024);
655 assert_eq!(data.type_name, "Vec<u8>");
656 assert!(data.is_active);
657 }
658
659 #[test]
660 fn test_write_binary_allocation() {
661 let buffer = Vec::new();
662 let cursor = Cursor::new(buffer);
663 let mut writer = BinaryHtmlWriter::new(cursor).expect("Failed to get test value");
664
665 let allocation = create_test_allocation();
666 let fields = AllocationField::all_basic_fields();
667
668 let result = writer.write_binary_allocation(&allocation, &fields);
669 assert!(result.is_ok());
670 assert_eq!(writer.stats.allocations_processed, 1);
671 }
672
673 #[test]
674 fn test_batch_processing() {
675 let buffer = Vec::new();
676 let cursor = Cursor::new(buffer);
677 let mut writer = BinaryHtmlWriter::new(cursor).expect("Failed to get test value");
678
679 let allocations = vec![create_test_allocation(); 5];
680 let fields = AllocationField::all_basic_fields();
681
682 let result = writer.write_binary_allocation_batch(&allocations, &fields);
683 assert!(result.is_ok());
684 assert_eq!(writer.stats.allocations_processed, 5);
685 }
686
687 #[test]
688 fn test_finalize_with_template() {
689 let buffer = Vec::new();
690 let cursor = Cursor::new(buffer);
691 let mut writer = BinaryHtmlWriter::new(cursor).expect("Failed to get test value");
692
693 let allocation = create_test_allocation();
694 let fields = AllocationField::all_basic_fields();
695
696 writer
697 .write_binary_allocation(&allocation, &fields)
698 .expect("Test operation failed");
699 let stats = writer
700 .finalize_with_binary_template("test_project")
701 .expect("Test operation failed");
702
703 assert_eq!(stats.allocations_processed, 1);
704 assert!(stats.total_html_size > 0);
705 assert!(stats.total_processing_time_ms > 0);
706 }
707
708 #[test]
709 fn test_stats_calculation() {
710 let stats = BinaryHtmlStats {
711 allocations_processed: 1000,
712 total_processing_time_ms: 500,
713 peak_memory_usage: 1024 * 1024, ..Default::default()
715 };
716
717 assert_eq!(stats.processing_throughput(), 2000.0); assert_eq!(stats.memory_efficiency_ratio(), 1000.0); }
720}