plotnik_bytecode/bytecode/
aligned_vec.rs

1//! 64-byte aligned storage for bytecode.
2//!
3//! Bytecode sections are 64-byte aligned internally. For this alignment to be
4//! meaningful at runtime, the buffer itself must start at a 64-byte boundary.
5//! Standard `Vec<u8>` provides no alignment guarantees for `u8`.
6
7use std::ops::Deref;
8
9/// Alignment for bytecode buffers (matches `SECTION_ALIGN`).
10pub const ALIGN: usize = 64;
11
12/// 64-byte aligned block for bytecode storage.
13#[repr(C, align(64))]
14#[derive(Clone, Copy)]
15struct Block([u8; 64]);
16
17/// Immutable 64-byte aligned byte storage.
18///
19/// Uses `Vec<Block>` internally — Vec guarantees element alignment,
20/// so the data starts at a 64-byte boundary. No custom allocator needed.
21pub struct AlignedVec {
22    blocks: Vec<Block>,
23    len: usize,
24}
25
26impl AlignedVec {
27    /// Copy bytes into aligned storage.
28    pub fn copy_from_slice(bytes: &[u8]) -> Self {
29        if bytes.is_empty() {
30            return Self {
31                blocks: Vec::new(),
32                len: 0,
33            };
34        }
35
36        let num_blocks = bytes.len().div_ceil(64);
37        let mut blocks = vec![Block([0; 64]); num_blocks];
38
39        // Copy block by block to stay safe
40        for (i, chunk) in bytes.chunks(64).enumerate() {
41            blocks[i].0[..chunk.len()].copy_from_slice(chunk);
42        }
43
44        Self {
45            blocks,
46            len: bytes.len(),
47        }
48    }
49
50    /// Read a file into aligned storage.
51    pub fn from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
52        let bytes = std::fs::read(path)?;
53        Ok(Self::copy_from_slice(&bytes))
54    }
55
56    /// Number of bytes stored.
57    pub fn len(&self) -> usize {
58        self.len
59    }
60
61    /// Check if empty.
62    pub fn is_empty(&self) -> bool {
63        self.len == 0
64    }
65
66    /// View as byte slice.
67    pub fn as_slice(&self) -> &[u8] {
68        if self.blocks.is_empty() {
69            return &[];
70        }
71        if self.len > self.blocks.len() * 64 {
72            panic!(
73                "AlignedVec invariant violated: len {} exceeds capacity {}",
74                self.len,
75                self.blocks.len() * 64
76            );
77        }
78        // SAFETY: Block is repr(C) with only [u8; 64], so pointer cast is valid.
79        // We only expose `len` bytes, which were initialized in copy_from_slice.
80        unsafe { std::slice::from_raw_parts(self.blocks.as_ptr() as *const u8, self.len) }
81    }
82}
83
84impl Deref for AlignedVec {
85    type Target = [u8];
86
87    fn deref(&self) -> &[u8] {
88        self.as_slice()
89    }
90}
91
92impl Clone for AlignedVec {
93    fn clone(&self) -> Self {
94        Self {
95            blocks: self.blocks.clone(),
96            len: self.len,
97        }
98    }
99}
100
101impl std::fmt::Debug for AlignedVec {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        f.debug_struct("AlignedVec")
104            .field("len", &self.len)
105            .field(
106                "aligned",
107                &(self.blocks.as_ptr() as usize).is_multiple_of(ALIGN),
108            )
109            .finish()
110    }
111}