Skip to main content

jugar_probar/coverage/
memory.rs

1//! Zero-Copy WASM Memory View (Muda Elimination)
2//!
3//! Per spec ยง5.3: Zero-copy coverage collection eliminates serialization waste.
4//!
5//! Direct access to WASM linear memory for reading coverage counters
6//! without any copying or serialization overhead.
7
8use super::BlockId;
9
10/// Zero-copy WASM memory view for coverage (Muda elimination)
11///
12/// Provides direct access to coverage counters stored in WASM linear memory.
13/// Counters are stored as little-endian u64 values.
14#[derive(Debug)]
15pub struct CoverageMemoryView<'a> {
16    /// Direct pointer to WASM linear memory
17    memory: &'a [u8],
18    /// Counter array offset in bytes
19    counter_base: usize,
20    /// Number of blocks (counters)
21    block_count: usize,
22}
23
24impl<'a> CoverageMemoryView<'a> {
25    /// Create a new memory view
26    ///
27    /// # Arguments
28    ///
29    /// * `memory` - Reference to WASM linear memory
30    /// * `counter_base` - Byte offset where counters start
31    /// * `block_count` - Number of blocks/counters
32    #[must_use]
33    pub fn new(memory: &'a [u8], counter_base: usize, block_count: usize) -> Self {
34        Self {
35            memory,
36            counter_base,
37            block_count,
38        }
39    }
40
41    /// Read counter without copy (Genchi Genbutsu)
42    ///
43    /// Directly reads the u64 counter value for the given block.
44    #[inline]
45    #[must_use]
46    pub fn read_counter(&self, block: BlockId) -> u64 {
47        let idx = block.as_u32() as usize;
48        if idx >= self.block_count {
49            return 0;
50        }
51
52        let offset = self.counter_base + idx * 8;
53        if offset + 8 > self.memory.len() {
54            return 0;
55        }
56
57        let bytes = &self.memory[offset..offset + 8];
58        u64::from_le_bytes(bytes.try_into().unwrap_or([0; 8]))
59    }
60
61    /// SIMD batch read all counters
62    ///
63    /// Reads all counters at once. In a full implementation, this would
64    /// use Trueno's SIMD operations for acceleration.
65    #[must_use]
66    pub fn read_all_counters(&self) -> Vec<u64> {
67        let slice = &self.memory[self.counter_base..];
68        slice
69            .chunks_exact(8)
70            .take(self.block_count)
71            .map(|b| u64::from_le_bytes(b.try_into().unwrap_or([0; 8])))
72            .collect()
73    }
74
75    /// Get the number of blocks being tracked
76    #[inline]
77    #[must_use]
78    pub fn block_count(&self) -> usize {
79        self.block_count
80    }
81
82    /// Get the counter base offset
83    #[inline]
84    #[must_use]
85    pub fn counter_base(&self) -> usize {
86        self.counter_base
87    }
88
89    /// Get the total memory size
90    #[inline]
91    #[must_use]
92    pub fn memory_size(&self) -> usize {
93        self.memory.len()
94    }
95
96    /// Check if a block is covered (counter > 0)
97    #[inline]
98    #[must_use]
99    pub fn is_covered(&self, block: BlockId) -> bool {
100        self.read_counter(block) > 0
101    }
102
103    /// Count the number of covered blocks
104    #[must_use]
105    pub fn covered_count(&self) -> usize {
106        self.read_all_counters().iter().filter(|&&c| c > 0).count()
107    }
108
109    /// Calculate coverage percentage
110    #[must_use]
111    pub fn coverage_percent(&self) -> f64 {
112        if self.block_count == 0 {
113            return 100.0; // Vacuously true
114        }
115        (self.covered_count() as f64 / self.block_count as f64) * 100.0
116    }
117}