rune/runtime/unit/
byte_code.rs

1use core::mem::size_of;
2
3use serde::{Deserialize, Serialize};
4
5use crate as rune;
6use crate::alloc::prelude::*;
7use crate::alloc::{self, Vec};
8use crate::runtime::unit::{BadInstruction, BadJump, EncodeError, UnitEncoder, UnitStorage};
9use crate::runtime::Inst;
10
11/// Unit stored as byte code, which is a more compact representation than
12/// `ArrayUnit`, but takes more time to execute since it needs to be decoded as
13/// it's being executed.
14#[derive(Debug, TryClone, Default, Serialize, Deserialize)]
15pub struct ByteCodeUnit {
16    /// The instructions contained in the source file.
17    #[try_clone(with = Clone::clone)]
18    bytes: rust_alloc::vec::Vec<u8>,
19    /// Known jump offsets.
20    offsets: Vec<usize>,
21}
22
23/// Iterator for [`ByteCodeUnit`].
24pub struct ByteCodeUnitIter<'a> {
25    address: &'a [u8],
26    len: usize,
27}
28
29impl Iterator for ByteCodeUnitIter<'_> {
30    type Item = (usize, Inst);
31
32    #[inline]
33    fn next(&mut self) -> Option<Self::Item> {
34        if self.address.is_empty() {
35            return None;
36        }
37
38        let ip = self.len.checked_sub(self.address.len())?;
39        let inst = musli::storage::decode(self.address).ok()?;
40        Some((ip, inst))
41    }
42}
43
44impl UnitEncoder for ByteCodeUnit {
45    #[inline]
46    fn offset(&self) -> usize {
47        self.bytes.len()
48    }
49
50    #[inline]
51    fn encode(&mut self, inst: Inst) -> Result<(), EncodeError> {
52        musli::storage::encode(&mut self.bytes, &inst)?;
53        Ok(())
54    }
55
56    #[inline]
57    fn extend_offsets(&mut self, extra: usize) -> alloc::Result<usize> {
58        let base = self.offsets.len();
59        self.offsets.try_extend((0..extra).map(|_| 0))?;
60        Ok(base)
61    }
62
63    #[inline]
64    fn mark_offset(&mut self, index: usize) {
65        if let Some(o) = self.offsets.get_mut(index) {
66            *o = self.bytes.len();
67        }
68    }
69
70    #[inline]
71    fn label_jump(&self, base: usize, _: usize, jump: usize) -> usize {
72        base.wrapping_add(jump)
73    }
74}
75
76impl UnitStorage for ByteCodeUnit {
77    type Iter<'this> = ByteCodeUnitIter<'this>;
78
79    #[inline]
80    fn end(&self) -> usize {
81        self.bytes.len()
82    }
83
84    #[inline]
85    fn bytes(&self) -> usize {
86        self.bytes
87            .len()
88            .wrapping_add(self.offsets.len().wrapping_mul(size_of::<usize>()))
89    }
90
91    #[inline]
92    fn iter(&self) -> Self::Iter<'_> {
93        ByteCodeUnitIter {
94            address: &self.bytes[..],
95            len: self.bytes.len(),
96        }
97    }
98
99    fn get(&self, ip: usize) -> Result<Option<(Inst, usize)>, BadInstruction> {
100        let Some(bytes) = self.bytes.get(ip..) else {
101            return Ok(None);
102        };
103
104        let start = bytes.as_ptr();
105        let inst: Inst = musli::storage::decode(bytes).map_err(|_| BadInstruction { ip })?;
106        let len = (bytes.as_ptr() as usize).wrapping_sub(start as usize);
107        Ok(Some((inst, len)))
108    }
109
110    #[inline]
111    fn translate(&self, jump: usize) -> Result<usize, BadJump> {
112        let Some(&offset) = self.offsets.get(jump) else {
113            return Err(BadJump { jump });
114        };
115
116        Ok(offset)
117    }
118}