bytecoding/lib.rs
1//! Derive macro for encoding and decoding instructions and operands as bytecode.
2//!
3//! # Derive macro
4//!
5//! ## Enum attributes
6//!
7//! Attributes that can be used on enums.
8//!
9//! - `type = ...` (required)
10//!
11//! Sets which type is used for the instruction code. Possible values are `u8`, `u16`, `u32`,
12//! and `u64`.
13//!
14//! ## Variant attributes
15//!
16//! Attributes that can be used on enum variants.
17//!
18//! - `code = ...`
19//!
20//! Sets the instruction codes for this enum variant. A code has to be specified for every
21//! instruction this variant generates. If the variant only generates one instruction, then a
22//! single number can be used for the `code` attribute (instead of a list of numbers).
23//!
24//! ## Variant field attributes
25//!
26//! Attributes that can be used on fields of enum variants.
27//!
28//! - `flatten = [...]`
29//!
30//! Flattens the field into instruction codes for every specified value. This attribute is
31//! intended to be used for optimizing some values and falling back to using an operand for any
32//! non-specified value. If you want to flatten all values, use the `flatten_all` attribute
33//! instead.
34//!
35//! See the [example](#example) below for more information on how to use this attribute.
36//!
37//! - `flatten_all = [...]`
38//!
39//! Flattens the enum variant into instruction codes for every specified value. Note that every
40//! possible value has to be specified. If you only want to flatten some values, use the
41//! `flatten` attribute instead.
42//!
43//! See the [example](#example) below for more information on how to use this attribute.
44//!
45//! ## Field attributes
46//!
47//! Attributes that can be used on struct fields or fields of enum variants.
48//!
49//! - `skip`
50//!
51//! Skips encoding and decoding of the field. For decoding, the value from
52//! `std::default::Default` is used.
53//!
54//! # Example
55//!
56//! ```rust
57//! use bytecoding::Bytecode;
58//!
59//! #[derive(Debug, PartialEq, Eq, Bytecode)]
60//! struct Operand {
61//! value1: u8,
62//! value2: u16,
63//! }
64//!
65//! #[derive(Debug, PartialEq, Eq, Bytecode)]
66//! #[bytecode(type = u8)]
67//! enum Instruction {
68//! Add,
69//! Sub,
70//! Jump(u8),
71//! Foo(Operand),
72//!
73//! // This generates four instruction codes without an operand for the values 0 to 3, and one
74//! // instruction code with an operand for all remaining values
75//! Const {
76//! #[bytecode(flatten = [0, 1, 2, 3])]
77//! index: u16,
78//! },
79//!
80//! // This generates two instruction codes (without an operand):
81//! // - The first code is for the value `true`
82//! // - The second code is for the value `false`
83//! Bool(#[bytecode(flatten_all = [true, false])] bool),
84//! }
85//!
86//! # fn main() -> Result<(), bytecoding::DecodeError> {
87//! let instructions = vec![
88//! Instruction::Sub,
89//! Instruction::Add,
90//! Instruction::Foo(Operand { value1: 20, value2: 30 }),
91//! Instruction::Jump(42),
92//! Instruction::Const { index: 0 },
93//! Instruction::Const { index: 4 },
94//! Instruction::Bool(true),
95//! Instruction::Bool(false),
96//! ];
97//!
98//! // Encoding
99//! let mut buf = Vec::new();
100//! for instruction in &instructions {
101//! instruction.encode(&mut buf);
102//! }
103//! assert_eq!(buf, vec![
104//! 1, // Sub
105//! 0, // Add
106//! 3, 20, 0, 30, // Foo(Operand { value1: 20, value2: 30 })
107//! 2, 42, // Jump(42)
108//! 4, // Const { index: 0 }
109//! 8, 0, 4, // Const { index: 4 }
110//! 9, // Bool(true)
111//! 10, // Bool(false)
112//! ]);
113//!
114//! // Decoding
115//! let mut buf: &[u8] = &buf;
116//! let mut decoded_instructions = Vec::new();
117//! while !buf.is_empty() {
118//! decoded_instructions.push(Instruction::decode(&mut buf)?);
119//! }
120//! assert_eq!(decoded_instructions, instructions);
121//! # Ok(())
122//! # }
123
124use bytes::{Buf, BufMut};
125use core::fmt;
126use paste::paste;
127use std::error::Error;
128
129/// Derive macro for [`Bytecode`].
130///
131/// See the [module-level documentation](crate) for more information.
132pub use bytecoding_derive::Bytecode;
133
134#[doc(hidden)]
135pub use bytes;
136
137/// Error that can occur while decoding the bytecode.
138#[derive(Debug, Clone, PartialEq, Eq, Hash)]
139pub enum DecodeError {
140 InvalidValue(Box<[u8]>),
141 UnexpectedEnd,
142}
143
144impl Error for DecodeError {}
145
146impl fmt::Display for DecodeError {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self {
149 Self::InvalidValue(v) => write!(f, "Invalid value `{:?}`", v),
150 Self::UnexpectedEnd => f.write_str("Unexpected end"),
151 }
152 }
153}
154
155/// Encode and decode instructions and operands.
156pub trait Bytecode: Sized {
157 /// Encodes the instruction or the operand.
158 ///
159 /// # Example
160 ///
161 /// ```rust
162 /// use bytecoding::Bytecode;
163 ///
164 /// #[derive(Bytecode)]
165 /// #[bytecode(type = u8)]
166 /// enum Instruction {
167 /// Add,
168 /// Sub,
169 /// Jump(u8),
170 /// }
171 ///
172 /// let mut buf = Vec::new();
173 /// Instruction::Sub.encode(&mut buf);
174 /// Instruction::Add.encode(&mut buf);
175 /// Instruction::Jump(42).encode(&mut buf);
176 ///
177 /// assert_eq!(buf, vec![1, 0, 2, 42]);
178 /// ```
179 fn encode<B: BufMut>(&self, buf: &mut B);
180
181 /// Decodes a instruction or an operand from the given buffer.
182 ///
183 /// Advances the buffer to the next instruction.
184 ///
185 /// # Example
186 ///
187 /// ```rust
188 /// use bytecoding::Bytecode;
189 ///
190 /// #[derive(Debug, PartialEq, Eq, Bytecode)]
191 /// #[bytecode(type = u8)]
192 /// enum Instruction {
193 /// Add,
194 /// Sub,
195 /// Jump(u8),
196 /// }
197 ///
198 /// # fn main() -> Result<(), bytecoding::DecodeError> {
199 /// let mut buf: &[u8] = &[1, 0, 2, 42];
200 /// let instructions = [
201 /// Instruction::decode(&mut buf)?,
202 /// Instruction::decode(&mut buf)?,
203 /// Instruction::decode(&mut buf)?,
204 /// ];
205 ///
206 /// assert_eq!(instructions, [Instruction::Sub, Instruction::Add, Instruction::Jump(42)]);
207 /// # Ok(())
208 /// # }
209 /// ```
210 fn decode<B: Buf>(buf: &mut B) -> Result<Self, DecodeError>;
211}
212
213macro_rules! impl_integer_bytecode {
214 ($($type_ident:ident),*) => {
215 $(impl Bytecode for $type_ident {
216 fn encode<B: BufMut>(&self, buf: &mut B) {
217 paste! { buf.[<put_ $type_ident>](*self); }
218 }
219
220 fn decode<B: Buf>(buf: &mut B) -> Result<Self, DecodeError> {
221 const NUM_BYTES: usize = ($type_ident::BITS / 8) as usize;
222 if buf.remaining() < NUM_BYTES {
223 return Err(DecodeError::UnexpectedEnd);
224 }
225 Ok(paste! { buf.[<get_ $type_ident>]() })
226 }
227 })*
228 };
229}
230
231impl_integer_bytecode!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);