rune/runtime/unit/
storage.rs

1use core::fmt;
2use core::iter;
3use core::mem::size_of;
4use core::slice;
5
6use crate as rune;
7use crate::alloc::prelude::*;
8use crate::alloc::{self, Vec};
9
10#[cfg(feature = "byte-code")]
11use musli::storage;
12#[cfg(feature = "musli")]
13use musli::{Decode, Encode};
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::runtime::Inst;
18
19mod sealed {
20    pub trait Sealed {}
21
22    #[cfg(feature = "byte-code")]
23    impl Sealed for crate::runtime::unit::ByteCodeUnit {}
24    impl Sealed for crate::runtime::unit::ArrayUnit {}
25}
26
27/// Builder trait for unit storage.
28pub trait UnitEncoder: self::sealed::Sealed {
29    /// Current offset in storage, which also corresponds to the instruction
30    /// pointer being built.
31    #[doc(hidden)]
32    fn offset(&self) -> usize;
33
34    /// Encode an instruction into the current storage.
35    #[doc(hidden)]
36    fn encode(&mut self, inst: Inst) -> Result<(), EncodeError>;
37
38    /// Indicate that the given number of offsets have been added.
39    #[doc(hidden)]
40    fn extend_offsets(&mut self, extra: usize) -> alloc::Result<usize>;
41
42    /// Mark that the given offset index is at the current offset.
43    #[doc(hidden)]
44    fn mark_offset(&mut self, index: usize);
45
46    /// Calculate label jump.
47    #[doc(hidden)]
48    fn label_jump(&self, base: usize, offset: usize, jump: usize) -> usize;
49}
50
51/// Instruction storage used by a [`Unit`][super::Unit].
52pub trait UnitStorage: self::sealed::Sealed + fmt::Debug + Default {
53    /// Iterator over instructions and their corresponding instruction offsets.
54    #[doc(hidden)]
55    type Iter<'this>: Iterator<Item = (usize, Inst)>
56    where
57        Self: 'this;
58
59    /// Size of unit storage. This can be seen as the instruction pointer which
60    /// is just beyond the last instruction.
61    #[doc(hidden)]
62    fn end(&self) -> usize;
63
64    /// Get the number of bytes which is used to store unit bytecode.
65    #[doc(hidden)]
66    fn bytes(&self) -> usize;
67
68    /// Iterate over all instructions.
69    #[doc(hidden)]
70    fn iter(&self) -> Self::Iter<'_>;
71
72    /// Get the instruction at the given instruction pointer.
73    #[doc(hidden)]
74    fn get(&self, ip: usize) -> Result<Option<(Inst, usize)>, BadInstruction>;
75
76    /// Translate the given jump offset.
77    #[doc(hidden)]
78    fn translate(&self, jump: usize) -> Result<usize, BadJump>;
79}
80
81/// Unit stored as array of instructions.
82#[derive(Debug, TryClone, Default)]
83#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
84#[cfg_attr(feature = "musli", derive(Encode, Decode))]
85pub struct ArrayUnit {
86    instructions: Vec<Inst>,
87}
88
89impl UnitEncoder for ArrayUnit {
90    #[inline]
91    fn offset(&self) -> usize {
92        self.instructions.len()
93    }
94
95    #[inline]
96    fn encode(&mut self, inst: Inst) -> Result<(), EncodeError> {
97        self.instructions.try_push(inst)?;
98        Ok(())
99    }
100
101    #[inline]
102    fn extend_offsets(&mut self, _: usize) -> alloc::Result<usize> {
103        Ok(self.instructions.len())
104    }
105
106    #[inline]
107    fn mark_offset(&mut self, _: usize) {}
108
109    #[inline]
110    fn label_jump(&self, base: usize, offset: usize, _: usize) -> usize {
111        base.wrapping_add(offset)
112    }
113}
114
115impl UnitStorage for ArrayUnit {
116    type Iter<'this> = iter::Enumerate<iter::Copied<slice::Iter<'this, Inst>>>;
117
118    #[inline]
119    fn end(&self) -> usize {
120        self.instructions.len()
121    }
122
123    #[inline]
124    fn bytes(&self) -> usize {
125        self.instructions.len().wrapping_mul(size_of::<Inst>())
126    }
127
128    #[inline]
129    fn iter(&self) -> Self::Iter<'_> {
130        self.instructions.iter().copied().enumerate()
131    }
132
133    #[inline]
134    fn get(&self, ip: usize) -> Result<Option<(Inst, usize)>, BadInstruction> {
135        let Some(inst) = self.instructions.get(ip) else {
136            return Ok(None);
137        };
138
139        Ok(Some((*inst, 1)))
140    }
141
142    #[inline]
143    fn translate(&self, jump: usize) -> Result<usize, BadJump> {
144        Ok(jump)
145    }
146}
147
148/// Error indicating that encoding failed.
149#[derive(Debug)]
150#[doc(hidden)]
151pub struct EncodeError {
152    kind: EncodeErrorKind,
153}
154
155#[cfg(feature = "byte-code")]
156impl From<storage::Error> for EncodeError {
157    #[inline]
158    fn from(error: storage::Error) -> Self {
159        Self {
160            kind: EncodeErrorKind::StorageError { error },
161        }
162    }
163}
164
165impl From<alloc::Error> for EncodeError {
166    #[inline]
167    fn from(error: alloc::Error) -> Self {
168        Self {
169            kind: EncodeErrorKind::AllocError { error },
170        }
171    }
172}
173
174impl fmt::Display for EncodeError {
175    #[inline]
176    fn fmt(
177        &self,
178        #[cfg_attr(not(feature = "byte-code"), allow(unused))] f: &mut fmt::Formatter<'_>,
179    ) -> fmt::Result {
180        match &self.kind {
181            #[cfg(feature = "byte-code")]
182            EncodeErrorKind::StorageError { .. } => write!(f, "Storage error"),
183            EncodeErrorKind::AllocError { error } => error.fmt(f),
184        }
185    }
186}
187
188impl core::error::Error for EncodeError {
189    #[inline]
190    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
191        match &self.kind {
192            #[cfg(all(feature = "byte-code", feature = "std"))]
193            EncodeErrorKind::StorageError { error } => Some(error),
194            _ => None,
195        }
196    }
197}
198
199#[derive(Debug)]
200enum EncodeErrorKind {
201    #[cfg(feature = "byte-code")]
202    StorageError {
203        #[cfg_attr(not(feature = "std"), allow(dead_code))]
204        error: storage::Error,
205    },
206    AllocError {
207        error: alloc::Error,
208    },
209}
210
211/// Error indicating that a bad instruction was located at the given instruction
212/// pointer.
213#[derive(Debug)]
214#[cfg_attr(test, derive(PartialEq))]
215pub struct BadInstruction {
216    pub(crate) ip: usize,
217}
218
219impl fmt::Display for BadInstruction {
220    #[inline]
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        write!(f, "Bad instruction at instruction {}", self.ip)
223    }
224}
225
226impl core::error::Error for BadInstruction {}
227
228/// Error indicating that a bad instruction was located at the given instruction
229/// pointer.
230#[derive(Debug)]
231#[cfg_attr(test, derive(PartialEq))]
232pub struct BadJump {
233    pub(crate) jump: usize,
234}
235
236impl fmt::Display for BadJump {
237    #[inline]
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        write!(f, "Bad jump index {}", self.jump)
240    }
241}
242
243impl core::error::Error for BadJump {}