ivm_compile/
options.rs

1/// The current compile feature version of this build.
2///
3/// The compile feature version is incremented whenever a new feature is added to the ivmc compiler,
4/// or a change was implemented to the bytecode format.
5///
6/// The VM shall be backwards compatible between small version changes, but should not be held to
7/// the standard of forward compatibility.
8///
9/// However, this does not mean that the VM will never deprecate features and/or mark them for
10/// removal.
11pub const CCFV: u32 = 1;
12
13pub mod header_format_doc {
14    //! This module's purpose is purely for documentation.
15    //!
16    //! The documentation within this module declares the ivmc bytecode header format.
17    //!
18    //! # Format (pseudo)
19    //! ```txt
20    //! /// 4 bytes: little endian u32.
21    //! /// **required - since CFV 1**
22    //! CompileFeatureVersion: [u8; 4],
23    //!
24    //! /// Will be mapped to the MemoryPointerLength enum.
25    //! /// See [MemoryPointerLength::get_byte_identifier].
26    //! /// **required - since CFV 1**
27    //! MemoryPointerLength: MemoryPointerLength#get_byte_identifier()
28    //! ```
29}
30
31/// An enum deciding the amount of bytes required to point to a location in memory.
32/// If the extra memory is not needed, using a smaller MemoryPointerLength can be file size
33/// optimization.
34///
35/// # Pointing to the max memory index
36/// ```txt
37/// X32b => [0xFF, 0xFF, 0xFF, 0xFF]
38/// X64b => [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
39/// ```
40pub enum MemoryPointerLength {
41    /// 32 bit memory pointers - (4 bytes).
42    X32b,
43
44    /// 64 bit memory pointers - (8 bytes).
45    X64b,
46}
47
48impl MemoryPointerLength {
49    /// Extract a usize from the memory pool at the given start index, until this
50    /// MemoryPointerLength's [span](Self::get_span()) is satisfied.
51    ///
52    /// See [Self::get_span()], [Self::to_usize(&\[u8\])].
53    pub fn extract(&self, index: usize, pool: &[u8]) -> usize {
54        self.to_usize(&pool[index..][..self.get_span()])
55    }
56
57    /// Convert the given input to a usize.
58    ///
59    /// Panics if this length cannot be fit into a usize.
60    pub fn to_usize(&self, input: &[u8]) -> usize {
61        debug_assert_eq!(self.get_span(), input.len());
62
63        match self {
64            Self::X32b => u32::from_le_bytes(input.try_into().unwrap()) as usize,
65            Self::X64b => u64::from_le_bytes(input.try_into().unwrap()) as usize,
66        }
67    }
68
69    /// Convert a memory pointer index to its little-endian byte representation.
70    pub fn fit(&self, mem_ptr_index: usize) -> Vec<u8> {
71        mem_ptr_index.to_le_bytes()[..self.get_span()].to_vec()
72    }
73
74    /// Get the byte size of this memory size.
75    ///
76    /// `X32b => 4 bytes, X64b => 8 bytes.`
77    pub fn get_span(&self) -> usize {
78        match self {
79            Self::X32b => 4,
80            Self::X64b => 8,
81        }
82    }
83
84    /// Get the this memory pointer length's byte identifier.
85    /// This will be placed into the bytecode header.
86    ///
87    /// See [options::header_format_doc] for a full guide regarding the ivm bytecode format.
88    pub fn get_byte_identifier(&self) -> u8 {
89        match self {
90            Self::X32b => 0,
91            Self::X64b => 1,
92        }
93    }
94
95    /// Match the memory pointer length from the given byte.
96    ///
97    /// See [MemoryPointerLength::get_byte_identifier()].
98    pub fn from_byte_identifier(byte: u8) -> Option<Self> {
99        match byte {
100            0 => Some(Self::X32b),
101            1 => Some(Self::X64b),
102            _ => None,
103        }
104    }
105}
106
107/// A struct containing the required options for the VM.
108/// This struct represents an ivmc bytecode header.
109///
110/// See [ProgramOptions::write_bytecode()].
111pub struct ProgramOptions {
112    pub cfv: u32,
113    pub ptr_len: MemoryPointerLength,
114}
115
116impl ProgramOptions {
117    /// Write these options as a bytecode header into the given [Vec].
118    pub fn write_bytecode(&self, output: &mut Vec<u8>) {
119        output.extend(CCFV.to_le_bytes());
120        output.push(self.ptr_len.get_byte_identifier());
121    }
122
123    /// Create a new ProgramOptions.
124    pub fn new(cfv: u32, ptr_len: MemoryPointerLength) -> Self {
125        Self { cfv, ptr_len }
126    }
127}
128
129pub enum InvalidHeaderCause {
130    /// The header format was not fulfilled.
131    /// For example, the header did not specify a CFV, and/or the memory pointer length.
132    FormatNotFulfilled,
133
134    /// The value was not recognized.
135    UnrecognizedValue,
136}
137
138impl InvalidHeaderCause {
139    pub fn get_help(&self) -> &[&str] {
140        const DOC_HELP: &str =
141            "see [ivm_compile::options::header_format_doc] for documentation on matching the ivmc b\
142            ytecode header format";
143
144        match self {
145            Self::FormatNotFulfilled => &[DOC_HELP],
146            Self::UnrecognizedValue => &[
147                "this bytecode input may have been compiled by a later version of ivmc",
148                DOC_HELP,
149            ],
150        }
151    }
152}
153
154/// An error returned when the header of a bytecode input did not meet the official ivmc bytecode
155/// header format.
156pub struct InvalidHeaderError {
157    cause: InvalidHeaderCause,
158    message: String,
159}
160
161impl InvalidHeaderError {
162    /// Get the cause of this error.
163    pub fn get_cause(&self) -> &InvalidHeaderCause {
164        &self.cause
165    }
166
167    /// Get the message of this error.
168    pub fn get_message(&self) -> &String {
169        &self.message
170    }
171
172    pub fn new(cause: InvalidHeaderCause, message: String) -> Self {
173        Self { cause, message }
174    }
175
176    pub fn from(cause: InvalidHeaderCause, message: &str) -> Self {
177        Self::new(cause, message.to_string())
178    }
179}