1use crate::export::binary::binary_html_writer::BinaryTemplateData;
7use crate::export::binary::error::BinaryExportError;
8use crate::export::binary::template_resource_manager::{
9 create_template_data, ResourceConfig, TemplateResourceManager,
10};
11
12use std::collections::HashMap;
13use std::time::Instant;
14
15#[derive(Debug, Clone)]
17pub struct BinaryTemplateEngineConfig {
18 pub enable_cache: bool,
20
21 pub enable_precompilation: bool,
23
24 pub enable_data_compression: bool,
26
27 pub max_cache_size_mb: usize,
29
30 pub processing_timeout_secs: u64,
32}
33
34impl Default for BinaryTemplateEngineConfig {
35 fn default() -> Self {
36 Self {
37 enable_cache: true,
38 enable_precompilation: true,
39 enable_data_compression: false,
40 max_cache_size_mb: 10,
41 processing_timeout_secs: 30,
42 }
43 }
44}
45
46pub struct BinaryTemplateEngine {
48 resource_manager: TemplateResourceManager,
50
51 resource_config: ResourceConfig,
53
54 config: BinaryTemplateEngineConfig,
56
57 last_render_time_ms: u64,
59
60 templates_processed: u64,
62
63 cache_hits: u64,
65}
66
67impl BinaryTemplateEngine {
68 pub fn new() -> Result<Self, BinaryExportError> {
70 Self::with_config(BinaryTemplateEngineConfig::default())
71 }
72
73 pub fn with_config(config: BinaryTemplateEngineConfig) -> Result<Self, BinaryExportError> {
75 let resource_manager = TemplateResourceManager::new("templates")?;
76 let resource_config = ResourceConfig {
77 embed_css: true,
78 embed_js: true,
79 embed_svg: true,
80 minify_resources: config.enable_data_compression,
81 custom_paths: HashMap::new(),
82 };
83
84 tracing::debug!(
85 "BinaryTemplateEngine configured with cache: {}, precompilation: {}",
86 config.enable_cache,
87 config.enable_precompilation
88 );
89
90 let engine = Self {
91 resource_manager,
92 resource_config,
93 config,
94 last_render_time_ms: 0,
95 templates_processed: 0,
96 cache_hits: 0,
97 };
98
99 Ok(engine)
100 }
101
102 pub fn render_binary_template(
104 &mut self,
105 template_data: &BinaryTemplateData,
106 ) -> Result<String, BinaryExportError> {
107 let render_start = Instant::now();
108
109 let optimized_data = self.optimize_template_data_for_size(template_data)?;
111
112 let json_data = self.serialize_template_data(&optimized_data)?;
114
115 tracing::info!(
117 "JSON data preview: {}",
118 &json_data[..json_data.len().min(500)]
119 );
120 tracing::info!("JSON data length: {} bytes", json_data.len());
121
122 let mut custom_data = HashMap::new();
124
125 custom_data.insert(
127 "PROCESSING_TIME".to_string(),
128 template_data.processing_time_ms.to_string(),
129 );
130 custom_data.insert("SVG_IMAGES".to_string(), self.load_svg_images()?);
131
132 if let Some(ref complex_types) = template_data.complex_types {
134 let complex_types_json = serde_json::to_string(complex_types).map_err(|e| {
135 BinaryExportError::SerializationError(format!(
136 "Complex types serialization failed: {e}",
137 ))
138 })?;
139 custom_data.insert("complex_types".to_string(), complex_types_json);
140 }
141
142 if let Some(ref unsafe_ffi) = template_data.unsafe_ffi {
143 let ffi_json = serde_json::to_string(unsafe_ffi).map_err(|e| {
144 BinaryExportError::SerializationError(format!(
145 "FFI safety serialization failed: {e}",
146 ))
147 })?;
148 custom_data.insert("unsafe_ffi".to_string(), ffi_json);
149 }
150
151 if let Some(ref variable_relationships) = template_data.variable_relationships {
152 let relationships_json =
153 serde_json::to_string(variable_relationships).map_err(|e| {
154 BinaryExportError::SerializationError(format!(
155 "Variable relationships serialization failed: {e}",
156 ))
157 })?;
158 custom_data.insert("variable_relationships".to_string(), relationships_json);
159 }
160
161 let mut resource_template_data =
162 create_template_data(&template_data.project_name, &json_data, custom_data);
163
164 resource_template_data.js_content = self._get_embedded_js();
166 resource_template_data.css_content = self._get_embedded_css();
167
168 let html_content = self.resource_manager.process_template(
170 "clean_dashboard.html",
171 &resource_template_data,
172 &self.resource_config,
173 )?;
174
175 self.last_render_time_ms = render_start.elapsed().as_millis() as u64;
177 self.templates_processed += 1;
178
179 Ok(html_content)
180 }
181
182 fn serialize_template_data(
184 &self,
185 data: &BinaryTemplateData,
186 ) -> Result<String, BinaryExportError> {
187 use serde_json::json;
188
189 let allocations_json: Vec<serde_json::Value> = data
191 .allocations
192 .iter()
193 .take(50) .map(|alloc| {
195 let ptr_str = format!("0x{:x}", alloc.ptr);
197 json!({
198 "id": alloc.id,
199 "size": alloc.size,
200 "type_name": alloc.type_name,
201 "scope_name": alloc.scope_name,
202 "var_name": alloc.var_name,
203 "ptr": ptr_str,
204 "timestamp_alloc": alloc.timestamp_alloc,
205 "is_active": alloc.is_active,
206 "thread_id": alloc.thread_id,
207 "borrow_count": alloc.borrow_count,
208 "is_leaked": alloc.is_leaked,
209 "lifetime_ms": alloc.lifetime_ms
210 })
211 })
212 .collect();
213
214 let memory_timeline = if data.allocations.len() > 1000 {
216 vec![
218 json!({"timestamp": 0, "memory_usage": 0, "allocation_count": 0}),
219 json!({"timestamp": 1000000, "memory_usage": data.total_memory_usage, "allocation_count": data.allocations.len()}),
220 ]
221 } else {
222 self.generate_fast_timeline_data(&data.allocations)
223 };
224
225 let size_distribution = self.generate_fast_size_distribution(&data.allocations);
226 let lifecycle_events = if data.allocations.len() > 500 {
227 vec![]
229 } else {
230 self.generate_fast_lifecycle_events(&data.allocations)
231 };
232
233 let mut dashboard_data = json!({
235 "memory_analysis": {
236 "allocations": allocations_json,
237 "stats": {
238 "total_allocations": data.allocations.len(),
239 "active_allocations": data.active_allocations_count,
240 "total_memory": data.total_memory_usage,
241 "active_memory": data.total_memory_usage
242 },
243 "memory_timeline": memory_timeline,
244 "size_distribution": size_distribution,
245 "fragmentation_analysis": {
246 "total_blocks": data.allocations.len(),
247 "fragmentation_score": 15,
248 "largest_block": data.allocations.iter().map(|a| a.size).max().unwrap_or(0),
249 "gaps": 0,
250 "total_gap_size": 0,
251 "analysis": "Low fragmentation detected"
252 },
253 "growth_trends": {
254 "peak_memory": data.peak_memory_usage,
255 "current_memory": data.total_memory_usage,
256 "growth_rate": 0,
257 "allocation_rate": data.allocations.len() as u64,
258 "time_points": memory_timeline,
259 "analysis": "Stable memory usage"
260 },
261 "visualization_ready": true
262 },
263 "lifetime": {
264 "lifecycle_events": lifecycle_events,
265 "variable_groups": [],
266 "user_variables_count": data.allocations.iter().filter(|a| a.var_name.is_some()).count(),
267 "visualization_ready": true
268 },
269 "performance": {
270 "memory_performance": {
271 "active_memory": data.total_memory_usage,
272 "peak_memory": data.peak_memory_usage,
273 "total_allocated": data.total_memory_usage
274 },
275 "allocation_distribution": {
276 "tiny": data.allocations.iter().filter(|a| a.size < 100).count(),
277 "small": data.allocations.iter().filter(|a| a.size >= 100 && a.size < 1024).count(),
278 "medium": data.allocations.iter().filter(|a| a.size >= 1024 && a.size < 10240).count(),
279 "large": data.allocations.iter().filter(|a| a.size >= 10240 && a.size < 102400).count(),
280 "massive": data.allocations.iter().filter(|a| a.size >= 102400).count()
281 }
282 }
283 });
284
285 let mut smart_pointers = Vec::new();
287 let mut collections = Vec::new();
288 let mut generic_types = Vec::new();
289 let mut primitive_types = Vec::new();
290
291 for alloc in &data.allocations {
292 let type_name = &alloc.type_name;
293 let type_info = json!({
294 "type_name": type_name,
295 "count": 1,
296 "total_size": alloc.size,
297 "complexity_score": if type_name.contains('<') { 3 } else { 1 }
298 });
299
300 if type_name.contains("Box<")
301 || type_name.contains("Rc<")
302 || type_name.contains("Arc<")
303 || type_name.contains("RefCell<")
304 {
305 smart_pointers.push(type_info);
306 } else if type_name.contains("Vec<")
307 || type_name.contains("HashMap<")
308 || type_name.contains("BTreeMap<")
309 || type_name.contains("HashSet<")
310 || type_name.contains("String")
311 {
312 collections.push(type_info);
313 } else if type_name.contains('<') && type_name.contains('>') {
314 generic_types.push(type_info);
315 } else if ![
316 "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64", "bool", "char",
317 ]
318 .contains(&type_name.as_str())
319 {
320 primitive_types.push(type_info);
321 }
322 }
323
324 dashboard_data["complex_types"] = json!({
325 "categorized_types": {
326 "smart_pointers": smart_pointers,
327 "collections": collections,
328 "generic_types": generic_types,
329 "trait_objects": [],
330 "primitive_types": primitive_types
331 },
332 "type_complexity": {},
333 "memory_usage_by_type": {},
334 "summary": {
335 "total_complex_types": smart_pointers.len() + collections.len() + generic_types.len(),
336 "smart_pointers_count": smart_pointers.len(),
337 "collections_count": collections.len(),
338 "generic_types_count": generic_types.len(),
339 "generic_type_count": generic_types.len()
340 }
341 });
342
343 let mut unsafe_operations = Vec::new();
345 let mut security_hotspots = Vec::new();
346 let mut enhanced_ffi_data = Vec::new();
347 let mut boundary_events = Vec::new();
348
349 for alloc in &data.allocations {
350 let is_unsafe = alloc.type_name.contains("*mut") ||
353 alloc.type_name.contains("*const") ||
354 alloc.type_name.contains("unsafe") ||
355 alloc.type_name.contains("libc") ||
356 alloc.type_name.contains("system_type") || alloc.type_name.contains("ffi") ||
358 alloc.type_name.contains("extern") ||
359 alloc.size > 1024*1024 || alloc.var_name.as_ref().is_some_and(|name| name.contains("ffi") || name.contains("unsafe")) ||
361 (alloc.size > 100*1024 && !alloc.type_name.contains("Vec") && !alloc.type_name.contains("String"));
363
364 if is_unsafe {
365 let risk_level = if alloc.size > 10 * 1024 * 1024 {
366 "High"
367 } else if alloc.size > 1024 * 1024 {
368 "Medium"
369 } else {
370 "Low"
371 };
372
373 let operation = json!({
374 "ptr": format!("0x{:x}", alloc.ptr),
375 "operation_type": if alloc.type_name.contains("*mut") { "Raw Pointer Mutation" }
376 else if alloc.type_name.contains("*const") { "Raw Pointer Access" }
377 else if alloc.type_name.contains("libc") { "FFI Call" }
378 else if alloc.size > 1024*1024 { "Large Allocation" }
379 else { "Unsafe Operation" },
380 "risk_level": risk_level,
381 "location": alloc.var_name.as_ref().unwrap_or(&"unknown".to_string()).clone(),
382 "timestamp": alloc.timestamp_alloc,
383 "size": alloc.size,
384 "safety_violations": if alloc.size > 10*1024*1024 { 3 }
385 else if alloc.size > 1024*1024 { 2 }
386 else { 1 }
387 });
388 unsafe_operations.push(operation.clone());
389
390 enhanced_ffi_data.push(json!({
392 "ptr": format!("0x{:x}", alloc.ptr),
393 "size": alloc.size,
394 "var_name": alloc.var_name,
395 "type_name": alloc.type_name,
396 "timestamp_alloc": alloc.timestamp_alloc,
397 "thread_id": alloc.thread_id,
398 "stack_trace": ["no_stack_trace_available"],
399 "runtime_state": {"status": "not_analyzed"},
400 "ffi_tracked": alloc.type_name.contains("libc") || alloc.type_name.contains("*"),
401 "safety_violations": if alloc.size > 10*1024*1024 { 3 }
402 else if alloc.size > 1024*1024 { 2 }
403 else { 1 }
404 }));
405
406 if alloc.type_name.contains("libc") || alloc.type_name.contains("*") {
408 boundary_events.push(json!({
409 "event_type": if alloc.type_name.contains("libc") { "FfiToRust" } else { "RustToFfi" },
410 "timestamp": alloc.timestamp_alloc,
411 "from_context": if alloc.type_name.contains("libc") { "libc" } else { "rust_main" },
412 "to_context": if alloc.type_name.contains("libc") { "rust_main" } else { "potential_ffi_target" },
413 "stack": [json!({
414 "function_name": "current_function",
415 "file_name": "src/unsafe_ffi_tracker.rs",
416 "line_number": 42,
417 "is_unsafe": true
418 })]
419 }));
420 }
421
422 if risk_level == "High" || alloc.size > 1024 * 1024 {
424 security_hotspots.push(json!({
425 "location": alloc.var_name.as_ref().unwrap_or(&"unknown".to_string()).clone(),
426 "description": format!("High-risk {} operation detected", operation["operation_type"]),
427 "violation_count": operation["safety_violations"],
428 "risk_score": if risk_level == "High" { 8.5 } else { 6.0 }
429 }));
430 }
431 }
432 }
433
434 let total_violations = unsafe_operations
435 .iter()
436 .map(|op| op["safety_violations"].as_u64().unwrap_or(0))
437 .sum::<u64>();
438
439 let risk_level = if total_violations > 20 {
440 "High"
441 } else if total_violations > 10 {
442 "Medium"
443 } else {
444 "Low"
445 };
446
447 dashboard_data["unsafe_ffi"] = json!({
448 "summary": {
449 "total_risk_items": enhanced_ffi_data.len(),
450 "unsafe_count": unsafe_operations.len(),
451 "ffi_count": enhanced_ffi_data.iter().filter(|item| item["ffi_tracked"].as_bool().unwrap_or(false)).count(),
452 "safety_violations": total_violations
453 },
454 "enhanced_ffi_data": enhanced_ffi_data,
455 "safety_violations": unsafe_operations,
456 "boundary_events": boundary_events,
457 "comprehensive_stats": {
458 "unsafe_allocations": unsafe_operations.len(),
459 "ffi_allocations": enhanced_ffi_data.iter().filter(|item| item["ffi_tracked"].as_bool().unwrap_or(false)).count(),
460 "boundary_crossings": boundary_events.len(),
461 "safety_violations": total_violations,
462 "unsafe_memory": enhanced_ffi_data.iter().map(|item| item["size"].as_u64().unwrap_or(0)).sum::<u64>()
463 },
464 "language_interactions": [],
465 "safety_analysis": {
466 "risk_level": risk_level,
467 "total_violations": total_violations,
468 "security_hotspots": security_hotspots
469 },
470 "visualization_ready": true
471 });
472
473 let allocations_sample = data.allocations.iter().take(20).collect::<Vec<_>>();
475 let mut nodes = Vec::new();
476 let mut links = Vec::new();
477
478 for (i, alloc) in allocations_sample.iter().enumerate() {
479 if let Some(var_name) = &alloc.var_name {
480 if !var_name.is_empty() && !var_name.starts_with("__") {
481 nodes.push(json!({
482 "id": format!("var_{i}"),
483 "name": var_name,
484 "type": &alloc.type_name,
485 "size": alloc.size,
486 "group": i % 4 + 1
487 }));
488
489 if i > 0 {
491 let prev_alloc = allocations_sample[i - 1];
492 if alloc.type_name == prev_alloc.type_name {
493 links.push(json!({
494 "source": format!("var_{}", i-1),
495 "target": format!("var_{i}"),
496 "strength": 0.8,
497 "type": "type_similarity"
498 }));
499 }
500 }
501 }
502 }
503 }
504
505 dashboard_data["variable_relationships"] = json!({
506 "nodes": nodes,
507 "edges": links,
508 "summary": {
509 "total_variables": nodes.len(),
510 "total_relationships": links.len(),
511 "relationship_density": if nodes.len() > 1 {
512 links.len() as f64 / (nodes.len() * (nodes.len() - 1) / 2) as f64
513 } else {
514 0.0
515 }
516 }
517 });
518
519 dashboard_data["security_violations"] = json!({
521 "metadata": {
522 "total_violations": total_violations
523 },
524 "violation_reports": unsafe_operations,
525 "security_summary": {
526 "security_analysis_summary": {
527 "total_violations": total_violations,
528 "severity_breakdown": {
529 "critical": unsafe_operations.iter().filter(|op| op["risk_level"] == "High").count(),
530 "high": unsafe_operations.iter().filter(|op| op["risk_level"] == "Medium").count(),
531 "medium": unsafe_operations.iter().filter(|op| op["risk_level"] == "Low").count(),
532 "low": 0,
533 "info": 0
534 }
535 }
536 }
537 });
538
539 serde_json::to_string(&dashboard_data).map_err(|e| {
540 BinaryExportError::SerializationError(format!("JSON serialization failed: {e}"))
541 })
542 }
543
544 fn _process_template_placeholders(
546 &self,
547 template: &str,
548 template_data: &BinaryTemplateData,
549 json_data: &str,
550 css_content: &str,
551 js_content: &str,
552 ) -> Result<String, BinaryExportError> {
553 let mut html_content = template.to_string();
554
555 html_content = html_content.replace("{{PROJECT_NAME}}", &template_data.project_name);
557 html_content = html_content.replace("{{BINARY_DATA}}", json_data);
558 html_content = html_content.replace("{{CSS_CONTENT}}", css_content);
559 html_content = html_content.replace("{{JS_CONTENT}}", js_content);
560 html_content = html_content.replace(
561 "{{GENERATION_TIME}}",
562 &chrono::Utc::now()
563 .format("%Y-%m-%d %H:%M:%S UTC")
564 .to_string(),
565 );
566 html_content = html_content.replace(
567 "{{PROCESSING_TIME}}",
568 &template_data.processing_time_ms.to_string(),
569 );
570
571 let throughput = self.calculate_throughput(template_data);
573 html_content = html_content.replace("{{THROUGHPUT}}", &throughput.to_string());
574
575 Ok(html_content)
576 }
577
578 #[allow(dead_code)]
580 fn calculate_throughput(&self, data: &BinaryTemplateData) -> f64 {
581 if data.processing_time_ms == 0 {
582 0.0
583 } else {
584 (data.allocations.len() as f64 * 1000.0) / data.processing_time_ms as f64
585 }
586 }
587
588 fn generate_fast_timeline_data(
590 &self,
591 allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
592 ) -> Vec<serde_json::Value> {
593 use serde_json::json;
594
595 if allocations.is_empty() {
597 return vec![];
598 }
599
600 let len = allocations.len();
601 let total_memory: u64 = allocations.iter().map(|a| a.size as u64).sum();
602
603 vec![
605 json!({"timestamp": 0, "memory_usage": 0, "allocation_count": 0}),
606 json!({"timestamp": 250000, "memory_usage": total_memory / 4, "allocation_count": len / 4}),
607 json!({"timestamp": 500000, "memory_usage": total_memory / 2, "allocation_count": len / 2}),
608 json!({"timestamp": 750000, "memory_usage": total_memory * 3 / 4, "allocation_count": len * 3 / 4}),
609 json!({"timestamp": 1000000, "memory_usage": total_memory, "allocation_count": len}),
610 ]
611 }
612
613 fn generate_fast_size_distribution(
615 &self,
616 allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
617 ) -> Vec<serde_json::Value> {
618 use serde_json::json;
619
620 if allocations.is_empty() {
621 return vec![];
622 }
623
624 let sample_size = allocations.len().min(20);
626 let mut small = 0u64;
627 let mut medium = 0u64;
628 let mut large = 0u64;
629 let mut huge = 0u64;
630
631 for alloc in allocations.iter().take(sample_size) {
632 match alloc.size {
633 0..=1024 => small += 1,
634 1025..=102400 => medium += 1,
635 102401..=1048576 => large += 1,
636 _ => huge += 1,
637 }
638 }
639
640 let scale_factor = allocations.len() as u64 / sample_size as u64;
642
643 vec![
644 json!({"size_range": "0-1KB", "count": small * scale_factor, "total_size": small * scale_factor * 512}),
645 json!({"size_range": "1-100KB", "count": medium * scale_factor, "total_size": medium * scale_factor * 50000}),
646 json!({"size_range": "100KB-1MB", "count": large * scale_factor, "total_size": large * scale_factor * 500000}),
647 json!({"size_range": ">1MB", "count": huge * scale_factor, "total_size": huge * scale_factor * 2000000}),
648 ]
649 }
650
651 fn generate_fast_lifecycle_events(
653 &self,
654 allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
655 ) -> Vec<serde_json::Value> {
656 use serde_json::json;
657
658 allocations
660 .iter()
661 .step_by(100)
662 .take(20)
663 .map(|alloc| {
664 json!({
665 "id": alloc.id,
666 "event_type": if alloc.is_active { "Allocation" } else { "Deallocation" },
667 "timestamp": alloc.timestamp_alloc,
668 "size": alloc.size
669 })
670 })
671 .collect()
672 }
673
674 #[allow(dead_code)]
676 fn count_unique_scopes(
677 &self,
678 allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
679 ) -> u64 {
680 use std::collections::HashSet;
681
682 let unique_scopes: HashSet<&str> = allocations
683 .iter()
684 .map(|alloc| alloc.scope_name.as_str())
685 .collect();
686
687 unique_scopes.len() as u64
688 }
689
690 #[allow(dead_code)]
692 fn calculate_average_scope_lifetime(
693 &self,
694 allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
695 ) -> f64 {
696 if allocations.is_empty() {
697 return 0.0;
698 }
699
700 let total_lifetime: u64 = allocations
701 .iter()
702 .filter_map(|alloc| alloc.lifetime_ms)
703 .sum();
704
705 let count = allocations
706 .iter()
707 .filter(|alloc| alloc.lifetime_ms.is_some())
708 .count();
709
710 if count == 0 {
711 0.0
712 } else {
713 total_lifetime as f64 / count as f64
714 }
715 }
716
717 #[allow(dead_code)]
719 fn calculate_memory_efficiency(&self, data: &BinaryTemplateData) -> f64 {
720 if data.peak_memory_usage == 0 {
721 0.0
722 } else {
723 (data.total_memory_usage as f64 / data.peak_memory_usage as f64) * 100.0
724 }
725 }
726
727 #[allow(dead_code)]
729 fn calculate_processing_speed(&self, data: &BinaryTemplateData) -> f64 {
730 if data.processing_time_ms == 0 {
731 0.0
732 } else {
733 let total_mb = data.total_memory_usage as f64 / (1024.0 * 1024.0);
734 let time_seconds = data.processing_time_ms as f64 / 1000.0;
735 total_mb / time_seconds
736 }
737 }
738
739 fn optimize_template_data_for_size(
741 &self,
742 data: &BinaryTemplateData,
743 ) -> Result<BinaryTemplateData, BinaryExportError> {
744 const MAX_ALLOCATIONS_ULTRA_FAST: usize = 200; let mut optimized_data = data.clone();
747
748 if data.allocations.len() > MAX_ALLOCATIONS_ULTRA_FAST {
750 tracing::info!(
751 "🚀 Ultra-fast optimization: {} → {} allocations",
752 data.allocations.len(),
753 MAX_ALLOCATIONS_ULTRA_FAST
754 );
755
756 optimized_data
758 .allocations
759 .truncate(MAX_ALLOCATIONS_ULTRA_FAST);
760 }
761
762 Ok(optimized_data)
763 }
764
765 fn load_svg_images(&self) -> Result<String, BinaryExportError> {
767 let mut svg_data = String::new();
768
769 let svg_files = [
771 ("memoryAnalysis", "images/memoryAnalysis.svg"),
772 ("lifecycleTimeline", "images/lifecycleTimeline.svg"),
773 ("unsafe_ffi_dashboard", "images/unsafe_ffi_dashboard.svg"),
774 ];
775
776 svg_data.push_str("<script>\n");
777 svg_data.push_str("// Embedded SVG images\n");
778 svg_data.push_str("window.svgImages = {\n");
779
780 for (name, path) in &svg_files {
781 if let Ok(svg_content) = std::fs::read_to_string(path) {
782 let escaped_svg = svg_content
784 .replace('\\', "\\\\")
785 .replace('`', "\\`")
786 .replace("${", "\\${");
787
788 svg_data.push_str(&format!(" {name}: `{escaped_svg}`,\n"));
789 } else {
790 svg_data.push_str(&format!(" {name}: `<svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100\" height=\"100\" fill=\"#f0f0f0\"/><text x=\"50\" y=\"50\" text-anchor=\"middle\" dy=\".3em\" font-family=\"Arial\" font-size=\"12\" fill=\"#666\">SVG Missing</text></svg>`,\n"));
792 }
793 }
794
795 svg_data.push_str("};\n");
796 svg_data.push_str("</script>\n");
797
798 Ok(svg_data)
799 }
800
801 fn _get_embedded_css(&self) -> String {
803 r#"
804 /* Binary Dashboard Specific Styles */
805 .binary-performance-indicator {
806 background: linear-gradient(45deg, #3b82f6, #1d4ed8);
807 color: white;
808 padding: 4px 12px;
809 border-radius: 16px;
810 font-size: 0.8rem;
811 font-weight: 600;
812 display: inline-flex;
813 align-items: center;
814 gap: 4px;
815 }
816
817 .binary-stats-grid {
818 display: grid;
819 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
820 gap: 1rem;
821 margin: 1rem 0;
822 }
823
824 .binary-stat-card {
825 background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
826 border: 1px solid #cbd5e0;
827 border-radius: 0.5rem;
828 padding: 1rem;
829 text-align: center;
830 transition: transform 0.2s ease;
831 }
832
833 .binary-stat-card:hover {
834 transform: translateY(-2px);
835 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
836 }
837
838 .binary-processing-badge {
839 background: linear-gradient(45deg, #10b981, #059669);
840 color: white;
841 padding: 2px 8px;
842 border-radius: 12px;
843 font-size: 0.75rem;
844 font-weight: 600;
845 text-transform: uppercase;
846 letter-spacing: 0.5px;
847 }
848
849 /* Dark mode adjustments for binary dashboard */
850 .dark .binary-stat-card {
851 background: linear-gradient(135deg, #374151 0%, #4b5563 100%);
852 border-color: #6b7280;
853 }
854
855 /* Performance indicators */
856 .performance-metric {
857 display: flex;
858 align-items: center;
859 justify-content: space-between;
860 padding: 0.5rem 0;
861 border-bottom: 1px solid #e5e7eb;
862 }
863
864 .performance-metric:last-child {
865 border-bottom: none;
866 }
867
868 .performance-value {
869 font-weight: 600;
870 color: #059669;
871 }
872
873 /* Binary data table enhancements */
874 .binary-table-row:hover {
875 background-color: rgba(59, 130, 246, 0.05);
876 }
877
878 .binary-pointer {
879 font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
880 font-size: 0.875rem;
881 color: #6366f1;
882 }
883
884 /* Responsive adjustments */
885 @media (max-width: 768px) {
886 .binary-stats-grid {
887 grid-template-columns: repeat(2, 1fr);
888 gap: 0.5rem;
889 }
890
891 .binary-stat-card {
892 padding: 0.75rem;
893 }
894 }
895 "#
896 .to_string()
897 }
898
899 fn _get_embedded_js(&self) -> String {
901 let script_js_content =
903 std::fs::read_to_string("templates/script.js").unwrap_or_else(|_| String::new());
904
905 let embedded_js = r#"
906 // Binary Dashboard Specific JavaScript
907
908 // Performance monitoring
909 function trackBinaryPerformance() {
910 const startTime = performance.now();
911
912 return {
913 end: function() {
914 const endTime = performance.now();
915 return endTime - startTime;
916 }
917 };
918 }
919
920 // Binary data processing utilities
921 function processBinaryData(data) {
922 if (!data || !data.memory_analysis) {
923 console.warn('No binary data available');
924 return null;
925 }
926
927 return {
928 allocations: data.memory_analysis.allocations || [],
929 summary: data.summary || {},
930 performance: data.performance_metrics || {}
931 };
932 }
933
934 // Enhanced table sorting for binary data
935 function sortBinaryTable(column, direction = 'asc') {
936 const table = document.getElementById('allocations-table');
937 if (!table) return;
938
939 const rows = Array.from(table.querySelectorAll('tr')).slice(1); // Skip header
940
941 rows.sort((a, b) => {
942 const aVal = a.cells[getColumnIndex(column)].textContent.trim();
943 const bVal = b.cells[getColumnIndex(column)].textContent.trim();
944
945 // Handle different data types
946 if (column === 'size') {
947 return direction === 'asc' ?
948 parseBytes(aVal) - parseBytes(bVal) :
949 parseBytes(bVal) - parseBytes(aVal);
950 } else if (column === 'ptr') {
951 const aPtr = parseInt(aVal.replace('0x', ''), 16);
952 const bPtr = parseInt(bVal.replace('0x', ''), 16);
953 return direction === 'asc' ? aPtr - bPtr : bPtr - aPtr;
954 } else {
955 return direction === 'asc' ?
956 aVal.localeCompare(bVal) :
957 bVal.localeCompare(aVal);
958 }
959 });
960
961 // Re-append sorted rows
962 rows.forEach(row => table.appendChild(row));
963 }
964
965 function getColumnIndex(column) {
966 const columns = { 'ptr': 0, 'variable': 1, 'type': 2, 'size': 3, 'status': 4 };
967 return columns[column] || 0;
968 }
969
970 function parseBytes(str) {
971 const match = str.match(/^([\d.]+)\s*([KMGT]?B)$/i);
972 if (!match) return 0;
973
974 const value = parseFloat(match[1]);
975 const unit = match[2].toUpperCase();
976
977 const multipliers = { 'B': 1, 'KB': 1024, 'MB': 1024*1024, 'GB': 1024*1024*1024 };
978 return value * (multipliers[unit] || 1);
979 }
980
981 // Binary-specific chart configurations
982 function createBinaryCharts() {
983 // Enhanced chart configurations for binary data
984 Chart.defaults.font.family = "'Inter', sans-serif";
985 Chart.defaults.color = '#6b7280';
986
987 // Add binary-specific chart plugins
988 Chart.register({
989 id: 'binaryDataPlugin',
990 beforeDraw: function(chart) {
991 if (chart.config.options.plugins?.binaryIndicator) {
992 const ctx = chart.ctx;
993 ctx.save();
994 ctx.fillStyle = '#3b82f6';
995 ctx.font = '12px Inter';
996 ctx.fillText('Binary Source', 10, 20);
997 ctx.restore();
998 }
999 }
1000 });
1001 }
1002
1003 // Initialize binary dashboard features
1004 function initializeBinaryFeatures() {
1005 // Add binary-specific event listeners
1006 document.addEventListener('keydown', function(e) {
1007 if (e.ctrlKey && e.key === 'b') {
1008 e.preventDefault();
1009 showBinaryInfo();
1010 }
1011 });
1012
1013 // Add performance monitoring
1014 const perfMonitor = trackBinaryPerformance();
1015
1016 // Setup binary data refresh
1017 setInterval(function() {
1018 updateBinaryMetrics();
1019 }, 5000);
1020
1021 console.log('Binary dashboard features initialized');
1022 }
1023
1024 function showBinaryInfo() {
1025 const info = {
1026 dataSource: 'Binary Direct',
1027 processingMode: 'Streaming',
1028 memoryEfficient: true,
1029 performanceOptimized: true
1030 };
1031
1032 console.table(info);
1033 }
1034
1035 function updateBinaryMetrics() {
1036 // Update real-time metrics if available
1037 if (window.analysisData && window.analysisData.performance_metrics) {
1038 const metrics = window.analysisData.performance_metrics;
1039
1040 // Update throughput display
1041 const throughputEl = document.getElementById('throughput');
1042 if (throughputEl && metrics.throughput_allocations_per_sec) {
1043 throughputEl.textContent = Math.round(metrics.throughput_allocations_per_sec).toLocaleString();
1044 }
1045 }
1046 }
1047
1048 // Safe element update function
1049 function safeUpdateElement(id, value) {
1050 const element = document.getElementById(id);
1051 if (element) {
1052 element.textContent = value;
1053 } else {
1054 console.warn('Element not found:', id);
1055 }
1056 }
1057
1058 // Destroy existing charts to prevent canvas reuse errors
1059 function destroyExistingCharts() {
1060 if (window.Chart && window.Chart.getChart) {
1061 const canvasIds = ['memory-distribution-chart', 'allocation-size-chart', 'memory-timeline-chart', 'lifecycle-timeline-chart'];
1062 canvasIds.forEach(canvasId => {
1063 const existingChart = window.Chart.getChart(canvasId);
1064 if (existingChart) {
1065 existingChart.destroy();
1066 console.log('Destroyed existing chart:', canvasId);
1067 }
1068 });
1069 }
1070 }
1071
1072 // Wait for all scripts to load, then override functions
1073 window.addEventListener('load', function() {
1074 console.log('All scripts loaded, applying safe overrides...');
1075
1076 // Destroy any existing charts first
1077 destroyExistingCharts();
1078
1079 // Override the problematic functions with safe versions
1080 window.updateSummaryStats = function(allocations) {
1081 if (!allocations) return;
1082
1083 const totalAllocations = allocations.length;
1084 const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
1085 const activeAllocations = allocations.filter(alloc => alloc.status === 'Active').length;
1086
1087 // Use safe updates for elements that exist
1088 safeUpdateElement('total-allocations', totalAllocations.toLocaleString());
1089 safeUpdateElement('total-memory', formatBytes(totalMemory));
1090 safeUpdateElement('active-allocations', activeAllocations.toLocaleString());
1091 safeUpdateElement('peak-memory', formatBytes(totalMemory));
1092
1093 // Update complex types stats safely
1094 if (window.analysisData && window.analysisData.complex_types) {
1095 const complexTypes = window.analysisData.complex_types;
1096 const summary = complexTypes.summary || {};
1097 safeUpdateElement('system-complex-types', summary.total_complex_types || 0);
1098 safeUpdateElement('system-smart-pointers', summary.smart_pointers_count || 0);
1099 safeUpdateElement('system-collections', summary.collections_count || 0);
1100 safeUpdateElement('system-generic-types', summary.generic_types_count || 0);
1101 }
1102 };
1103
1104 window.populateAllocationsTable = function(allocations) {
1105 const tableBody = document.getElementById('allocations-table');
1106 if (!tableBody) {
1107 console.warn('Allocations table not found');
1108 return;
1109 }
1110
1111 if (!allocations || allocations.length === 0) {
1112 tableBody.innerHTML = '<tr><td colspan="5" class="px-4 py-8 text-center text-gray-500">No allocation data available</td></tr>';
1113 return;
1114 }
1115
1116 // Update summary statistics safely
1117 window.updateSummaryStats(allocations);
1118
1119 // Populate table with first 100 allocations
1120 const displayAllocations = allocations.slice(0, 100);
1121 tableBody.innerHTML = displayAllocations.map(alloc => `
1122 <tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
1123 <td class="px-4 py-3 text-sm font-mono">0x${alloc.id ? alloc.id.toString(16) : 'N/A'}</td>
1124 <td class="px-4 py-3 text-sm">${alloc.location || alloc.var_name || 'unnamed'}</td>
1125 <td class="px-4 py-3 text-sm">${alloc.type_name || 'Unknown'}</td>
1126 <td class="px-4 py-3 text-sm text-right">${formatBytes(alloc.size || 0)}</td>
1127 <td class="px-4 py-3 text-sm text-right">
1128 <span class="px-2 py-1 text-xs rounded-full ${alloc.status === 'Active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}">
1129 ${alloc.status || 'Unknown'}
1130 </span>
1131 </td>
1132 </tr>
1133 `).join('');
1134 };
1135
1136 // Prevent multiple initializations
1137 if (window.dashboardInitialized) {
1138 console.log('Dashboard already initialized, skipping...');
1139 return;
1140 }
1141
1142 // Initialize user data metrics safely
1143 if (typeof initializeUserDataMetrics === 'function') {
1144 try {
1145 initializeUserDataMetrics();
1146 } catch (e) {
1147 console.warn('Error initializing user data metrics:', e);
1148 }
1149 }
1150
1151 if (typeof initializeLifecycleVisualization === 'function') {
1152 try {
1153 initializeLifecycleVisualization();
1154 } catch (e) {
1155 console.warn('Error initializing lifecycle visualization:', e);
1156 }
1157 }
1158
1159 if (typeof initializeSystemDataMetrics === 'function') {
1160 try {
1161 initializeSystemDataMetrics();
1162 } catch (e) {
1163 console.warn('Error initializing system data metrics:', e);
1164 }
1165 }
1166
1167 // Initialize allocations table if data is available
1168 if (window.analysisData && window.analysisData.memory_analysis) {
1169 const allocations = window.analysisData.memory_analysis.allocations || [];
1170 window.populateAllocationsTable(allocations);
1171 }
1172
1173 window.dashboardInitialized = true;
1174 console.log('Safe dashboard initialization complete');
1175 });
1176
1177 // Export binary dashboard utilities
1178 window.binaryDashboard = {
1179 trackPerformance: trackBinaryPerformance,
1180 processData: processBinaryData,
1181 sortTable: sortBinaryTable,
1182 createCharts: createBinaryCharts,
1183 initialize: initializeBinaryFeatures,
1184 safeUpdate: safeUpdateElement,
1185 updateStats: updateSummaryStats,
1186 populateTable: populateAllocationsTable
1187 };
1188 "#;
1189
1190 if !script_js_content.is_empty() {
1192 format!("{script_js_content}\n\n// === EMBEDDED SAFE OVERRIDES ===\n{embedded_js}")
1193 } else {
1194 embedded_js.to_string()
1195 }
1196 }
1197
1198 pub fn get_stats(&self) -> BinaryTemplateEngineStats {
1200 let cache_enabled = self.config.enable_cache;
1202 tracing::debug!(
1203 "Getting stats for engine with cache enabled: {}",
1204 cache_enabled
1205 );
1206
1207 BinaryTemplateEngineStats {
1208 templates_processed: self.templates_processed,
1209 last_render_time_ms: self.last_render_time_ms,
1210 cache_hits: self.cache_hits,
1211 cache_hit_rate: if self.templates_processed > 0 {
1212 (self.cache_hits as f64 / self.templates_processed as f64) * 100.0
1213 } else {
1214 0.0
1215 },
1216 cached_templates: 0, }
1218 }
1219
1220 pub fn last_render_time(&self) -> u64 {
1222 self.last_render_time_ms
1223 }
1224
1225 pub fn clear_cache(&mut self) {
1227 self.resource_manager.clear_cache();
1229 }
1230}
1231
1232impl Default for BinaryTemplateEngine {
1233 fn default() -> Self {
1234 Self::new().expect("Failed to create default BinaryTemplateEngine")
1235 }
1236}
1237
1238#[derive(Debug, Clone)]
1240pub struct BinaryTemplateEngineStats {
1241 pub templates_processed: u64,
1243
1244 pub last_render_time_ms: u64,
1246
1247 pub cache_hits: u64,
1249
1250 pub cache_hit_rate: f64,
1252
1253 pub cached_templates: usize,
1255}
1256
1257#[cfg(test)]
1258mod tests {
1259 use super::*;
1260 use crate::export::binary::binary_html_writer::{BinaryAllocationData, BinaryFieldValue};
1261 use std::collections::HashMap;
1262
1263 fn create_test_template_data() -> BinaryTemplateData {
1264 let mut optional_fields = HashMap::new();
1265 optional_fields.insert(
1266 "test_field".to_string(),
1267 BinaryFieldValue::String("test_value".to_string()),
1268 );
1269
1270 let allocation = BinaryAllocationData {
1271 id: 1,
1272 size: 1024,
1273 type_name: "Vec<u8>".to_string(),
1274 scope_name: "main".to_string(),
1275 timestamp_alloc: 1234567890,
1276 is_active: true,
1277 ptr: 0x1000,
1278 thread_id: "main".to_string(),
1279 var_name: Some("test_var".to_string()),
1280 borrow_count: 0,
1281 is_leaked: false,
1282 lifetime_ms: Some(1000),
1283 optional_fields,
1284 };
1285
1286 let allocations = vec![allocation];
1287 BinaryTemplateData {
1288 project_name: "test_project".to_string(),
1289 allocations: allocations.clone(),
1290 total_memory_usage: 1024,
1291 peak_memory_usage: 1024,
1292 active_allocations_count: 1,
1293 processing_time_ms: 100,
1294 data_source: "binary_direct".to_string(),
1295 complex_types: None, unsafe_ffi: None, variable_relationships: None, }
1299 }
1300
1301 #[test]
1302 fn test_binary_template_engine_creation() {
1303 let engine = BinaryTemplateEngine::new();
1304 assert!(engine.is_ok());
1305 }
1306
1307 #[test]
1308 fn test_template_data_serialization() {
1309 let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1310 let template_data = create_test_template_data();
1311
1312 let json_result = engine.serialize_template_data(&template_data);
1313 assert!(json_result.is_ok());
1314
1315 let json_str = json_result.expect("Test operation failed");
1316 assert!(json_str.contains("memory_analysis"));
1319 assert!(json_str.contains("Vec<u8>"));
1320 assert!(json_str.contains("allocations"));
1321 }
1322
1323 #[test]
1324 fn test_css_and_js_loading() {
1325 let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1326
1327 let css_content = engine._get_embedded_css();
1328 let js_content = engine._get_embedded_js();
1329
1330 assert!(!css_content.is_empty());
1332 assert!(!js_content.is_empty());
1333 }
1334
1335 #[test]
1336 fn test_placeholder_processing() {
1337 let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1338 let template_data = create_test_template_data();
1339
1340 let template = "Project: {{PROJECT_NAME}}, Time: {{PROCESSING_TIME}}ms";
1341 let json_data = "{}";
1342 let css_content = "";
1343 let js_content = "";
1344
1345 let result = engine._process_template_placeholders(
1346 template,
1347 &template_data,
1348 json_data,
1349 css_content,
1350 js_content,
1351 );
1352
1353 assert!(result.is_ok());
1354 let processed = result.expect("Test operation failed");
1355 assert!(processed.contains("test_project"));
1356 assert!(processed.contains("100ms"));
1357 }
1358
1359 #[test]
1360 fn test_throughput_calculation() {
1361 let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1362 let template_data = create_test_template_data();
1363
1364 let throughput = engine.calculate_throughput(&template_data);
1365 assert_eq!(throughput, 10.0); }
1367
1368 #[test]
1369 fn test_caching_functionality() {
1370 let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1371 enable_cache: true,
1372 ..Default::default()
1373 })
1374 .expect("Test operation failed");
1375
1376 let css1 = engine._get_embedded_css();
1381 let css2 = engine._get_embedded_css();
1382 assert_eq!(css1, css2); let js1 = engine._get_embedded_js();
1385 let js2 = engine._get_embedded_js();
1386 assert_eq!(js1, js2); let stats = engine.get_stats();
1393 assert_eq!(stats.cached_templates, 0); }
1395
1396 #[test]
1397 fn test_cache_hits_tracking() {
1398 let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1399 enable_cache: true,
1400 ..Default::default()
1401 })
1402 .expect("Test operation failed");
1403
1404 engine._get_embedded_css();
1407 engine._get_embedded_js();
1408
1409 engine._get_embedded_css();
1411 engine._get_embedded_js();
1412
1413 engine._get_embedded_css();
1415
1416 let stats = engine.get_stats();
1418 assert_eq!(stats.cache_hits, 0); }
1420
1421 #[test]
1422 fn test_cache_disabled() {
1423 let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1424 enable_cache: false,
1425 ..Default::default()
1426 })
1427 .expect("Test operation failed");
1428
1429 assert_eq!(engine.get_stats().cache_hits, 0);
1432
1433 engine._get_embedded_css();
1435 engine._get_embedded_js();
1436
1437 assert_eq!(engine.get_stats().cache_hits, 0);
1440 }
1441
1442 #[test]
1443 fn test_cache_clearing() {
1444 let mut engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1445 enable_cache: true,
1446 ..Default::default()
1447 })
1448 .expect("Test operation failed");
1449
1450 engine._get_embedded_css();
1452 engine._get_embedded_js();
1453
1454 engine.clear_cache();
1457
1458 let test_data = create_test_template_data();
1460 let result = engine.render_binary_template(&test_data);
1461 assert!(result.is_ok());
1462 }
1463
1464 #[test]
1465 fn test_binary_template_engine_config_default() {
1466 let config = BinaryTemplateEngineConfig::default();
1467
1468 assert!(config.enable_cache);
1469 assert!(config.enable_precompilation);
1470 assert!(!config.enable_data_compression);
1471 assert_eq!(config.max_cache_size_mb, 10);
1472 assert_eq!(config.processing_timeout_secs, 30);
1473 }
1474
1475 #[test]
1476 fn test_binary_template_engine_config_debug_clone() {
1477 let config = BinaryTemplateEngineConfig {
1478 enable_cache: false,
1479 enable_precompilation: false,
1480 enable_data_compression: true,
1481 max_cache_size_mb: 20,
1482 processing_timeout_secs: 60,
1483 };
1484
1485 let debug_str = format!("{:?}", config);
1487 assert!(debug_str.contains("BinaryTemplateEngineConfig"));
1488 assert!(debug_str.contains("enable_cache"));
1489 assert!(debug_str.contains("false")); let cloned_config = config.clone();
1493 assert_eq!(cloned_config.enable_cache, config.enable_cache);
1494 assert_eq!(
1495 cloned_config.enable_precompilation,
1496 config.enable_precompilation
1497 );
1498 assert_eq!(
1499 cloned_config.enable_data_compression,
1500 config.enable_data_compression
1501 );
1502 assert_eq!(cloned_config.max_cache_size_mb, config.max_cache_size_mb);
1503 assert_eq!(
1504 cloned_config.processing_timeout_secs,
1505 config.processing_timeout_secs
1506 );
1507 }
1508
1509 #[test]
1510 fn test_binary_template_engine_stats_debug_clone() {
1511 let stats = BinaryTemplateEngineStats {
1512 templates_processed: 10,
1513 last_render_time_ms: 150,
1514 cache_hits: 7,
1515 cache_hit_rate: 70.0,
1516 cached_templates: 5,
1517 };
1518
1519 let debug_str = format!("{:?}", stats);
1521 assert!(debug_str.contains("BinaryTemplateEngineStats"));
1522 assert!(debug_str.contains("templates_processed"));
1523 assert!(debug_str.contains("10"));
1524
1525 let cloned_stats = stats.clone();
1527 assert_eq!(cloned_stats.templates_processed, stats.templates_processed);
1528 assert_eq!(cloned_stats.last_render_time_ms, stats.last_render_time_ms);
1529 assert_eq!(cloned_stats.cache_hits, stats.cache_hits);
1530 assert_eq!(cloned_stats.cache_hit_rate, stats.cache_hit_rate);
1531 assert_eq!(cloned_stats.cached_templates, stats.cached_templates);
1532 }
1533
1534 #[test]
1535 fn test_binary_template_engine_with_custom_config() {
1536 let custom_config = BinaryTemplateEngineConfig {
1537 enable_cache: false,
1538 enable_precompilation: false,
1539 enable_data_compression: true,
1540 max_cache_size_mb: 50,
1541 processing_timeout_secs: 120,
1542 };
1543
1544 let engine = BinaryTemplateEngine::with_config(custom_config.clone());
1545 assert!(engine.is_ok());
1546
1547 let engine = engine.unwrap();
1548 assert_eq!(engine.config.enable_cache, custom_config.enable_cache);
1549 assert_eq!(
1550 engine.config.enable_precompilation,
1551 custom_config.enable_precompilation
1552 );
1553 assert_eq!(
1554 engine.config.enable_data_compression,
1555 custom_config.enable_data_compression
1556 );
1557 assert_eq!(
1558 engine.config.max_cache_size_mb,
1559 custom_config.max_cache_size_mb
1560 );
1561 assert_eq!(
1562 engine.config.processing_timeout_secs,
1563 custom_config.processing_timeout_secs
1564 );
1565 }
1566
1567 #[test]
1568 fn test_binary_template_engine_default_trait() {
1569 let engine1 = BinaryTemplateEngine::new().unwrap();
1570 let engine2 = BinaryTemplateEngine::default();
1571
1572 assert_eq!(engine1.config.enable_cache, engine2.config.enable_cache);
1574 assert_eq!(
1575 engine1.config.enable_precompilation,
1576 engine2.config.enable_precompilation
1577 );
1578 assert_eq!(
1579 engine1.config.enable_data_compression,
1580 engine2.config.enable_data_compression
1581 );
1582 assert_eq!(
1583 engine1.config.max_cache_size_mb,
1584 engine2.config.max_cache_size_mb
1585 );
1586 assert_eq!(
1587 engine1.config.processing_timeout_secs,
1588 engine2.config.processing_timeout_secs
1589 );
1590 }
1591
1592 #[test]
1593 fn test_render_binary_template_full_workflow() {
1594 let mut engine = BinaryTemplateEngine::new().unwrap();
1595 let template_data = create_test_template_data();
1596
1597 let result = engine.render_binary_template(&template_data);
1598 assert!(result.is_ok());
1599
1600 let html_content = result.unwrap();
1601 assert!(!html_content.is_empty());
1602
1603 let stats = engine.get_stats();
1605 assert_eq!(stats.templates_processed, 1);
1606 assert!(stats.last_render_time_ms > 0);
1607 }
1608
1609 #[test]
1610 fn test_render_binary_template_with_large_dataset() {
1611 let mut engine = BinaryTemplateEngine::new().unwrap();
1612
1613 let mut large_allocations = Vec::new();
1615 for i in 0..1000 {
1616 let mut optional_fields = HashMap::new();
1617 optional_fields.insert(
1618 "test_field".to_string(),
1619 BinaryFieldValue::String(format!("test_value_{}", i)),
1620 );
1621
1622 large_allocations.push(BinaryAllocationData {
1623 id: i as u64,
1624 size: 1024 + i as usize,
1625 type_name: format!("Type{}", i % 10),
1626 scope_name: format!("scope_{}", i % 5),
1627 timestamp_alloc: 1234567890 + i as u64,
1628 is_active: i % 2 == 0,
1629 ptr: 0x1000 + i as usize,
1630 thread_id: format!("thread_{}", i % 3),
1631 var_name: Some(format!("var_{}", i)),
1632 borrow_count: (i % 5) as usize,
1633 is_leaked: i % 10 == 0,
1634 lifetime_ms: Some(1000 + i as u64),
1635 optional_fields,
1636 });
1637 }
1638
1639 let large_template_data = BinaryTemplateData {
1640 project_name: "large_test_project".to_string(),
1641 allocations: large_allocations,
1642 total_memory_usage: 1024000,
1643 peak_memory_usage: 1024000,
1644 active_allocations_count: 500,
1645 processing_time_ms: 1000,
1646 data_source: "binary_direct".to_string(),
1647 complex_types: None,
1648 unsafe_ffi: None,
1649 variable_relationships: None,
1650 };
1651
1652 let result = engine.render_binary_template(&large_template_data);
1653 assert!(result.is_ok());
1654
1655 let html_content = result.unwrap();
1656 assert!(!html_content.is_empty());
1657
1658 assert!(!html_content.is_empty());
1661
1662 assert!(
1664 html_content.contains("html")
1665 || html_content.contains("HTML")
1666 || html_content.len() > 1000
1667 ); }
1669
1670 #[test]
1671 fn test_optimize_template_data_for_size() {
1672 let engine = BinaryTemplateEngine::new().unwrap();
1673
1674 let mut many_allocations = Vec::new();
1676 for i in 0..500 {
1677 many_allocations.push(BinaryAllocationData {
1678 id: i as u64,
1679 size: 1024,
1680 type_name: "TestType".to_string(),
1681 scope_name: "test_scope".to_string(),
1682 timestamp_alloc: 1234567890,
1683 is_active: true,
1684 ptr: 0x1000 + i as usize,
1685 thread_id: "main".to_string(),
1686 var_name: Some(format!("var_{}", i)),
1687 borrow_count: 0,
1688 is_leaked: false,
1689 lifetime_ms: Some(1000),
1690 optional_fields: HashMap::new(),
1691 });
1692 }
1693
1694 let large_data = BinaryTemplateData {
1695 project_name: "test_project".to_string(),
1696 allocations: many_allocations,
1697 total_memory_usage: 512000,
1698 peak_memory_usage: 512000,
1699 active_allocations_count: 500,
1700 processing_time_ms: 100,
1701 data_source: "binary_direct".to_string(),
1702 complex_types: None,
1703 unsafe_ffi: None,
1704 variable_relationships: None,
1705 };
1706
1707 let result = engine.optimize_template_data_for_size(&large_data);
1708 assert!(result.is_ok());
1709
1710 let optimized_data = result.unwrap();
1711 assert_eq!(optimized_data.allocations.len(), 200); assert_eq!(optimized_data.project_name, large_data.project_name);
1713 assert_eq!(
1714 optimized_data.total_memory_usage,
1715 large_data.total_memory_usage
1716 );
1717 }
1718
1719 #[test]
1720 fn test_generate_fast_timeline_data() {
1721 let engine = BinaryTemplateEngine::new().unwrap();
1722
1723 let empty_allocations = vec![];
1725 let timeline = engine.generate_fast_timeline_data(&empty_allocations);
1726 assert!(timeline.is_empty());
1727
1728 let allocations = vec![
1730 BinaryAllocationData {
1731 id: 1,
1732 size: 1000,
1733 type_name: "Type1".to_string(),
1734 scope_name: "scope1".to_string(),
1735 timestamp_alloc: 1234567890,
1736 is_active: true,
1737 ptr: 0x1000,
1738 thread_id: "main".to_string(),
1739 var_name: Some("var1".to_string()),
1740 borrow_count: 0,
1741 is_leaked: false,
1742 lifetime_ms: Some(1000),
1743 optional_fields: HashMap::new(),
1744 },
1745 BinaryAllocationData {
1746 id: 2,
1747 size: 2000,
1748 type_name: "Type2".to_string(),
1749 scope_name: "scope2".to_string(),
1750 timestamp_alloc: 1234567900,
1751 is_active: true,
1752 ptr: 0x2000,
1753 thread_id: "main".to_string(),
1754 var_name: Some("var2".to_string()),
1755 borrow_count: 0,
1756 is_leaked: false,
1757 lifetime_ms: Some(2000),
1758 optional_fields: HashMap::new(),
1759 },
1760 ];
1761
1762 let timeline = engine.generate_fast_timeline_data(&allocations);
1763 assert_eq!(timeline.len(), 5); assert!(timeline[0]["timestamp"].as_u64().unwrap() == 0);
1767 assert!(timeline[4]["timestamp"].as_u64().unwrap() == 1000000);
1768 assert!(timeline[4]["memory_usage"].as_u64().unwrap() == 3000); assert!(timeline[4]["allocation_count"].as_u64().unwrap() == 2);
1770 }
1771
1772 #[test]
1773 fn test_generate_fast_size_distribution() {
1774 let engine = BinaryTemplateEngine::new().unwrap();
1775
1776 let empty_allocations = vec![];
1778 let distribution = engine.generate_fast_size_distribution(&empty_allocations);
1779 assert!(distribution.is_empty());
1780
1781 let allocations = vec![
1783 BinaryAllocationData {
1784 id: 1,
1785 size: 512, type_name: "SmallType".to_string(),
1787 scope_name: "scope1".to_string(),
1788 timestamp_alloc: 1234567890,
1789 is_active: true,
1790 ptr: 0x1000,
1791 thread_id: "main".to_string(),
1792 var_name: Some("small_var".to_string()),
1793 borrow_count: 0,
1794 is_leaked: false,
1795 lifetime_ms: Some(1000),
1796 optional_fields: HashMap::new(),
1797 },
1798 BinaryAllocationData {
1799 id: 2,
1800 size: 50000, type_name: "MediumType".to_string(),
1802 scope_name: "scope2".to_string(),
1803 timestamp_alloc: 1234567900,
1804 is_active: true,
1805 ptr: 0x2000,
1806 thread_id: "main".to_string(),
1807 var_name: Some("medium_var".to_string()),
1808 borrow_count: 0,
1809 is_leaked: false,
1810 lifetime_ms: Some(2000),
1811 optional_fields: HashMap::new(),
1812 },
1813 BinaryAllocationData {
1814 id: 3,
1815 size: 500000, type_name: "LargeType".to_string(),
1817 scope_name: "scope3".to_string(),
1818 timestamp_alloc: 1234567910,
1819 is_active: true,
1820 ptr: 0x3000,
1821 thread_id: "main".to_string(),
1822 var_name: Some("large_var".to_string()),
1823 borrow_count: 0,
1824 is_leaked: false,
1825 lifetime_ms: Some(3000),
1826 optional_fields: HashMap::new(),
1827 },
1828 BinaryAllocationData {
1829 id: 4,
1830 size: 2000000, type_name: "HugeType".to_string(),
1832 scope_name: "scope4".to_string(),
1833 timestamp_alloc: 1234567920,
1834 is_active: true,
1835 ptr: 0x4000,
1836 thread_id: "main".to_string(),
1837 var_name: Some("huge_var".to_string()),
1838 borrow_count: 0,
1839 is_leaked: false,
1840 lifetime_ms: Some(4000),
1841 optional_fields: HashMap::new(),
1842 },
1843 ];
1844
1845 let distribution = engine.generate_fast_size_distribution(&allocations);
1846 assert_eq!(distribution.len(), 4); let size_ranges: Vec<&str> = distribution
1850 .iter()
1851 .map(|item| item["size_range"].as_str().unwrap())
1852 .collect();
1853 assert!(size_ranges.contains(&"0-1KB"));
1854 assert!(size_ranges.contains(&"1-100KB"));
1855 assert!(size_ranges.contains(&"100KB-1MB"));
1856 assert!(size_ranges.contains(&">1MB"));
1857 }
1858
1859 #[test]
1860 fn test_generate_fast_lifecycle_events() {
1861 let engine = BinaryTemplateEngine::new().unwrap();
1862
1863 let mut many_allocations = Vec::new();
1865 for i in 0..1000 {
1866 many_allocations.push(BinaryAllocationData {
1867 id: i as u64,
1868 size: 1024,
1869 type_name: "TestType".to_string(),
1870 scope_name: "test_scope".to_string(),
1871 timestamp_alloc: 1234567890 + i as u64,
1872 is_active: i % 2 == 0,
1873 ptr: 0x1000 + i as usize,
1874 thread_id: "main".to_string(),
1875 var_name: Some(format!("var_{}", i)),
1876 borrow_count: 0,
1877 is_leaked: false,
1878 lifetime_ms: Some(1000),
1879 optional_fields: HashMap::new(),
1880 });
1881 }
1882
1883 let lifecycle_events = engine.generate_fast_lifecycle_events(&many_allocations);
1884 assert!(lifecycle_events.len() <= 20); if !lifecycle_events.is_empty() {
1888 let first_event = &lifecycle_events[0];
1889 assert!(first_event.get("id").is_some());
1890 assert!(first_event.get("event_type").is_some());
1891 assert!(first_event.get("timestamp").is_some());
1892 assert!(first_event.get("size").is_some());
1893
1894 let event_type = first_event["event_type"].as_str().unwrap();
1895 assert!(event_type == "Allocation" || event_type == "Deallocation");
1896 }
1897 }
1898
1899 #[test]
1900 fn test_count_unique_scopes() {
1901 let engine = BinaryTemplateEngine::new().unwrap();
1902
1903 let allocations = vec![
1904 BinaryAllocationData {
1905 id: 1,
1906 size: 1024,
1907 type_name: "Type1".to_string(),
1908 scope_name: "scope1".to_string(),
1909 timestamp_alloc: 1234567890,
1910 is_active: true,
1911 ptr: 0x1000,
1912 thread_id: "main".to_string(),
1913 var_name: Some("var1".to_string()),
1914 borrow_count: 0,
1915 is_leaked: false,
1916 lifetime_ms: Some(1000),
1917 optional_fields: HashMap::new(),
1918 },
1919 BinaryAllocationData {
1920 id: 2,
1921 size: 2048,
1922 type_name: "Type2".to_string(),
1923 scope_name: "scope1".to_string(), timestamp_alloc: 1234567900,
1925 is_active: true,
1926 ptr: 0x2000,
1927 thread_id: "main".to_string(),
1928 var_name: Some("var2".to_string()),
1929 borrow_count: 0,
1930 is_leaked: false,
1931 lifetime_ms: Some(2000),
1932 optional_fields: HashMap::new(),
1933 },
1934 BinaryAllocationData {
1935 id: 3,
1936 size: 4096,
1937 type_name: "Type3".to_string(),
1938 scope_name: "scope2".to_string(), timestamp_alloc: 1234567910,
1940 is_active: true,
1941 ptr: 0x3000,
1942 thread_id: "main".to_string(),
1943 var_name: Some("var3".to_string()),
1944 borrow_count: 0,
1945 is_leaked: false,
1946 lifetime_ms: Some(3000),
1947 optional_fields: HashMap::new(),
1948 },
1949 ];
1950
1951 let unique_scopes = engine.count_unique_scopes(&allocations);
1952 assert_eq!(unique_scopes, 2); }
1954
1955 #[test]
1956 fn test_calculate_average_scope_lifetime() {
1957 let engine = BinaryTemplateEngine::new().unwrap();
1958
1959 let empty_allocations = vec![];
1961 let avg_lifetime = engine.calculate_average_scope_lifetime(&empty_allocations);
1962 assert_eq!(avg_lifetime, 0.0);
1963
1964 let allocations = vec![
1966 BinaryAllocationData {
1967 id: 1,
1968 size: 1024,
1969 type_name: "Type1".to_string(),
1970 scope_name: "scope1".to_string(),
1971 timestamp_alloc: 1234567890,
1972 is_active: true,
1973 ptr: 0x1000,
1974 thread_id: "main".to_string(),
1975 var_name: Some("var1".to_string()),
1976 borrow_count: 0,
1977 is_leaked: false,
1978 lifetime_ms: Some(1000),
1979 optional_fields: HashMap::new(),
1980 },
1981 BinaryAllocationData {
1982 id: 2,
1983 size: 2048,
1984 type_name: "Type2".to_string(),
1985 scope_name: "scope2".to_string(),
1986 timestamp_alloc: 1234567900,
1987 is_active: true,
1988 ptr: 0x2000,
1989 thread_id: "main".to_string(),
1990 var_name: Some("var2".to_string()),
1991 borrow_count: 0,
1992 is_leaked: false,
1993 lifetime_ms: Some(2000),
1994 optional_fields: HashMap::new(),
1995 },
1996 BinaryAllocationData {
1997 id: 3,
1998 size: 4096,
1999 type_name: "Type3".to_string(),
2000 scope_name: "scope3".to_string(),
2001 timestamp_alloc: 1234567910,
2002 is_active: true,
2003 ptr: 0x3000,
2004 thread_id: "main".to_string(),
2005 var_name: Some("var3".to_string()),
2006 borrow_count: 0,
2007 is_leaked: false,
2008 lifetime_ms: None, optional_fields: HashMap::new(),
2010 },
2011 ];
2012
2013 let avg_lifetime = engine.calculate_average_scope_lifetime(&allocations);
2014 assert_eq!(avg_lifetime, 1500.0); }
2016
2017 #[test]
2018 fn test_calculate_memory_efficiency() {
2019 let engine = BinaryTemplateEngine::new().unwrap();
2020
2021 let zero_peak_data = BinaryTemplateData {
2023 project_name: "test".to_string(),
2024 allocations: vec![],
2025 total_memory_usage: 1000,
2026 peak_memory_usage: 0,
2027 active_allocations_count: 0,
2028 processing_time_ms: 100,
2029 data_source: "binary_direct".to_string(),
2030 complex_types: None,
2031 unsafe_ffi: None,
2032 variable_relationships: None,
2033 };
2034
2035 let efficiency = engine.calculate_memory_efficiency(&zero_peak_data);
2036 assert_eq!(efficiency, 0.0);
2037
2038 let normal_data = BinaryTemplateData {
2040 project_name: "test".to_string(),
2041 allocations: vec![],
2042 total_memory_usage: 800,
2043 peak_memory_usage: 1000,
2044 active_allocations_count: 0,
2045 processing_time_ms: 100,
2046 data_source: "binary_direct".to_string(),
2047 complex_types: None,
2048 unsafe_ffi: None,
2049 variable_relationships: None,
2050 };
2051
2052 let efficiency = engine.calculate_memory_efficiency(&normal_data);
2053 assert_eq!(efficiency, 80.0); }
2055
2056 #[test]
2057 fn test_calculate_processing_speed() {
2058 let engine = BinaryTemplateEngine::new().unwrap();
2059
2060 let zero_time_data = BinaryTemplateData {
2062 project_name: "test".to_string(),
2063 allocations: vec![],
2064 total_memory_usage: 1024 * 1024, peak_memory_usage: 1024 * 1024,
2066 active_allocations_count: 0,
2067 processing_time_ms: 0,
2068 data_source: "binary_direct".to_string(),
2069 complex_types: None,
2070 unsafe_ffi: None,
2071 variable_relationships: None,
2072 };
2073
2074 let speed = engine.calculate_processing_speed(&zero_time_data);
2075 assert_eq!(speed, 0.0);
2076
2077 let normal_data = BinaryTemplateData {
2079 project_name: "test".to_string(),
2080 allocations: vec![],
2081 total_memory_usage: 2 * 1024 * 1024, peak_memory_usage: 2 * 1024 * 1024,
2083 active_allocations_count: 0,
2084 processing_time_ms: 1000, data_source: "binary_direct".to_string(),
2086 complex_types: None,
2087 unsafe_ffi: None,
2088 variable_relationships: None,
2089 };
2090
2091 let speed = engine.calculate_processing_speed(&normal_data);
2092 assert_eq!(speed, 2.0); }
2094
2095 #[test]
2096 fn test_load_svg_images() {
2097 let engine = BinaryTemplateEngine::new().unwrap();
2098
2099 let svg_result = engine.load_svg_images();
2100 assert!(svg_result.is_ok());
2101
2102 let svg_content = svg_result.unwrap();
2103 assert!(!svg_content.is_empty());
2104 assert!(svg_content.contains("window.svgImages"));
2105 assert!(svg_content.contains("memoryAnalysis"));
2106 assert!(svg_content.contains("lifecycleTimeline"));
2107 assert!(svg_content.contains("unsafe_ffi_dashboard"));
2108 }
2109
2110 #[test]
2111 fn test_get_stats_and_last_render_time() {
2112 let mut engine = BinaryTemplateEngine::new().unwrap();
2113
2114 let initial_stats = engine.get_stats();
2116 assert_eq!(initial_stats.templates_processed, 0);
2117 assert_eq!(initial_stats.last_render_time_ms, 0);
2118 assert_eq!(initial_stats.cache_hits, 0);
2119 assert_eq!(initial_stats.cache_hit_rate, 0.0);
2120 assert_eq!(initial_stats.cached_templates, 0);
2121
2122 assert_eq!(engine.last_render_time(), 0);
2124
2125 let template_data = create_test_template_data();
2127 let result = engine.render_binary_template(&template_data);
2128 assert!(result.is_ok());
2129
2130 let updated_stats = engine.get_stats();
2132 assert_eq!(updated_stats.templates_processed, 1);
2133 assert!(updated_stats.last_render_time_ms > 0);
2134 assert!(engine.last_render_time() > 0);
2135 assert_eq!(engine.last_render_time(), updated_stats.last_render_time_ms);
2136 }
2137
2138 #[test]
2139 fn test_throughput_calculation_edge_cases() {
2140 let engine = BinaryTemplateEngine::new().unwrap();
2141
2142 let zero_time_data = BinaryTemplateData {
2144 project_name: "test".to_string(),
2145 allocations: vec![create_test_template_data().allocations[0].clone()],
2146 total_memory_usage: 1024,
2147 peak_memory_usage: 1024,
2148 active_allocations_count: 1,
2149 processing_time_ms: 0,
2150 data_source: "binary_direct".to_string(),
2151 complex_types: None,
2152 unsafe_ffi: None,
2153 variable_relationships: None,
2154 };
2155
2156 let throughput = engine.calculate_throughput(&zero_time_data);
2157 assert_eq!(throughput, 0.0);
2158
2159 let normal_data = BinaryTemplateData {
2161 project_name: "test".to_string(),
2162 allocations: vec![
2163 create_test_template_data().allocations[0].clone(),
2164 create_test_template_data().allocations[0].clone(),
2165 ],
2166 total_memory_usage: 2048,
2167 peak_memory_usage: 2048,
2168 active_allocations_count: 2,
2169 processing_time_ms: 500,
2170 data_source: "binary_direct".to_string(),
2171 complex_types: None,
2172 unsafe_ffi: None,
2173 variable_relationships: None,
2174 };
2175
2176 let throughput = engine.calculate_throughput(&normal_data);
2177 assert_eq!(throughput, 4.0); }
2179}