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            .as_chunks::<8>()
70            .0
71            .iter()
72            .take(self.block_count)
73            .map(|b| u64::from_le_bytes(*b))
74            .collect()
75    }
76
77    /// Get the number of blocks being tracked
78    #[inline]
79    #[must_use]
80    pub fn block_count(&self) -> usize {
81        self.block_count
82    }
83
84    /// Get the counter base offset
85    #[inline]
86    #[must_use]
87    pub fn counter_base(&self) -> usize {
88        self.counter_base
89    }
90
91    /// Get the total memory size
92    #[inline]
93    #[must_use]
94    pub fn memory_size(&self) -> usize {
95        self.memory.len()
96    }
97
98    /// Check if a block is covered (counter > 0)
99    #[inline]
100    #[must_use]
101    pub fn is_covered(&self, block: BlockId) -> bool {
102        self.read_counter(block) > 0
103    }
104
105    /// Count the number of covered blocks
106    #[must_use]
107    pub fn covered_count(&self) -> usize {
108        self.read_all_counters().iter().filter(|&&c| c > 0).count()
109    }
110
111    /// Calculate coverage percentage
112    #[must_use]
113    pub fn coverage_percent(&self) -> f64 {
114        if self.block_count == 0 {
115            return 100.0; // Vacuously true
116        }
117        (self.covered_count() as f64 / self.block_count as f64) * 100.0
118    }
119}