aluvm/isa/
bytecode.rs

1// Reference rust implementation of AluVM (arithmetic logic unit virtual machine).
2// To find more on AluVM please check <https://aluvm.org>
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Designed in 2021-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7// Written in 2021-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
8//
9// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland.
10// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
11//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
12// Copyright (C) 2021-2025 Dr Maxim Orlovsky.
13// All rights under the above copyrights are reserved.
14//
15// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
16// in compliance with the License. You may obtain a copy of the License at
17//
18//        http://www.apache.org/licenses/LICENSE-2.0
19//
20// Unless required by applicable law or agreed to in writing, software distributed under the License
21// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
22// or implied. See the License for the specific language governing permissions and limitations under
23// the License.
24
25use core::fmt::Debug;
26use core::ops::RangeInclusive;
27
28use amplify::confinement::SmallBlob;
29use amplify::num::{u1, u2, u3, u4, u5, u6, u7};
30
31use crate::core::SiteId;
32
33/// Non-failing byte encoding for the instruction set.
34///
35/// We can't use `io` since (1) we are no_std, (2) it operates data with unlimited length (while we
36/// are bound by u16), (3) it provides too many fails in situations when we can't fail because of
37/// `u16`-bounding and exclusive in-memory encoding handling.
38pub trait Bytecode<Id: SiteId> {
39    /// Returns the range of instruction bytecodes covered by a set of operations.
40    fn op_range() -> RangeInclusive<u8>;
41
42    /// Returns byte representing instruction code (without its arguments).
43    fn opcode_byte(&self) -> u8;
44
45    /// Returns the number of bytes used by the instruction and its arguments when serialized into
46    /// the code segment.
47    fn code_byte_len(&self) -> u16;
48
49    /// If the instruction calls or references any external program, returns a reference to it.
50    fn external_ref(&self) -> Option<Id>;
51
52    /// Write an instruction as bytecode.
53    fn encode_instr<W>(&self, writer: &mut W) -> Result<(), W::Error>
54    where W: BytecodeWrite<Id> {
55        writer.write_byte(self.opcode_byte())?;
56        self.encode_operands(writer)?;
57        writer.check_aligned();
58        Ok(())
59    }
60
61    /// Writes an instruction operands as bytecode, omitting opcode byte.
62    fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
63    where W: BytecodeWrite<Id>;
64
65    /// Reads an instruction from bytecode.
66    fn decode_instr<R>(reader: &mut R) -> Result<Self, CodeEofError>
67    where
68        Self: Sized,
69        R: BytecodeRead<Id>,
70    {
71        let opcode = reader.read_byte()?;
72        let instr = Self::decode_operands(reader, opcode)?;
73        reader.check_aligned();
74        Ok(instr)
75    }
76
77    /// Reads an instruction operands from bytecode, provided the opcode byte.
78    fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
79    where
80        Self: Sized,
81        R: BytecodeRead<Id>;
82}
83
84/// Error indicating that an end-of-code segment boundary is reached during read or write operation.
85#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
86#[display("attempt to read or write outside of a code segment (i.e., at position > 0xFFFF)")]
87pub struct CodeEofError;
88
89/// Reader from a bytecode for instruction deserialization.
90pub trait BytecodeRead<Id: SiteId> {
91    /// Return the current byte offset of the cursor. Does not account for bits.
92    /// If the position is exactly at EOF, returns `None`.
93    fn pos(&self) -> u16;
94    /// Set the current cursor byte offset to the provided value if it is less than the underlying
95    /// buffer length.
96    ///
97    /// # Returns
98    ///
99    /// Previous position.
100    fn seek(&mut self, byte_pos: u16) -> Result<u16, CodeEofError>;
101    /// Return whether end of the bytecode is reached.
102    fn is_eof(&self) -> bool;
103    /// Peek a single byte without moving cursor.
104    fn peek_byte(&self) -> Result<u8, CodeEofError>;
105
106    /// Read single bit as a bool value.
107    fn read_bool(&mut self) -> Result<bool, CodeEofError> { Ok(self.read_1bit()? == u1::ONE) }
108    /// Read single bit.
109    fn read_1bit(&mut self) -> Result<u1, CodeEofError>;
110    /// Read two bits.
111    fn read_2bits(&mut self) -> Result<u2, CodeEofError>;
112    /// Read three bits.
113    fn read_3bits(&mut self) -> Result<u3, CodeEofError>;
114    /// Read four bits.
115    fn read_4bits(&mut self) -> Result<u4, CodeEofError>;
116    /// Read five bits.
117    fn read_5bits(&mut self) -> Result<u5, CodeEofError>;
118    /// Read six bits.
119    fn read_6bits(&mut self) -> Result<u6, CodeEofError>;
120    /// Read seven bits.
121    fn read_7bits(&mut self) -> Result<u7, CodeEofError>;
122
123    /// Read byte.
124    fn read_byte(&mut self) -> Result<u8, CodeEofError>;
125    /// Read word.
126    fn read_word(&mut self) -> Result<u16, CodeEofError>;
127
128    /// Read the fixed number of bytes and convert it into a result type.
129    ///
130    /// # Returns
131    ///
132    /// Resulting data type and a flag for `CK` registry indicating whether it was possible to read
133    /// all the data.
134    fn read_fixed<N, const LEN: usize>(
135        &mut self,
136        f: impl FnOnce([u8; LEN]) -> N,
137    ) -> Result<N, CodeEofError>;
138
139    /// Read variable-length byte string.
140    ///
141    /// # Returns
142    ///
143    /// Resulting data type and a flag for `CK` registry indicating whether it was possible to read
144    /// all the data.
145    fn read_bytes(&mut self) -> Result<(SmallBlob, bool), CodeEofError>;
146
147    /// Read external reference id.
148    fn read_ref(&mut self) -> Result<Id, CodeEofError>
149    where Id: Sized;
150
151    /// Check if the current cursor position is aligned to the next byte.
152    ///
153    /// # Panics
154    ///
155    /// If the position is not aligned, panics.
156    fn check_aligned(&self);
157}
158
159/// Writer converting instructions into a bytecode.
160pub trait BytecodeWrite<Id: SiteId> {
161    /// Error type returned during writing procedures.
162    type Error: Debug;
163
164    /// Write a single bit from a bool value.
165    fn write_bool(&mut self, data: bool) -> Result<(), Self::Error> {
166        self.write_1bit(if data { u1::ONE } else { u1::ZERO })
167    }
168
169    /// Write a single bit.
170    fn write_1bit(&mut self, data: u1) -> Result<(), Self::Error>;
171    /// Write two bits.
172    fn write_2bits(&mut self, data: u2) -> Result<(), Self::Error>;
173    /// Write three bits.
174    fn write_3bits(&mut self, data: u3) -> Result<(), Self::Error>;
175    /// Write four bits.
176    fn write_4bits(&mut self, data: u4) -> Result<(), Self::Error>;
177    /// Write five bits.
178    fn write_5bits(&mut self, data: u5) -> Result<(), Self::Error>;
179    /// Write six bits.
180    fn write_6bits(&mut self, data: u6) -> Result<(), Self::Error>;
181    /// Write seven bits.
182    fn write_7bits(&mut self, data: u7) -> Result<(), Self::Error>;
183
184    /// Write byte.
185    fn write_byte(&mut self, data: u8) -> Result<(), Self::Error>;
186    /// Write word.
187    fn write_word(&mut self, data: u16) -> Result<(), Self::Error>;
188
189    /// Write data representable as a fixed-length byte array.
190    fn write_fixed<const LEN: usize>(&mut self, data: [u8; LEN]) -> Result<(), Self::Error>;
191
192    /// Write variable-length byte string.
193    fn write_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error>;
194
195    /// Write external reference id.
196    fn write_ref(&mut self, id: Id) -> Result<(), Self::Error>;
197
198    /// Check if the current cursor position is aligned to the next byte.
199    ///
200    /// # Panics
201    ///
202    /// If the position is not aligned, panics.
203    fn check_aligned(&self);
204}