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}