beamcode/
lib.rs

1//! Erlang BEAM instructions encoding / decoding library.
2//!
3//! # References
4//!
5//! - [The BEAM Book - Generic BEAM Instructions](https://blog.stenmans.org/theBeamBook/#CH-Instructions)
6use crate::instruction::Instruction;
7use crate::term::TermKind;
8use beamcode_derive::{Decode, Encode};
9use byteorder::ReadBytesExt as _;
10use num::BigInt;
11use std::io::{Read, Write};
12
13pub mod instruction;
14pub mod term;
15
16/// This trait allows decoding an object from a byte sequence.
17pub trait Decode: Sized {
18    fn decode<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
19        let tag = reader.read_u8()?;
20        Self::decode_with_tag(reader, tag)
21    }
22
23    fn decode_with_tag<R: Read>(reader: &mut R, tag: u8) -> Result<Self, DecodeError>;
24}
25
26/// This trait allows encoding an object into a byte sequence.
27pub trait Encode {
28    fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>;
29}
30
31/// Decoding errors.
32#[derive(Debug, thiserror::Error)]
33pub enum DecodeError {
34    #[error("unknown term tag: {tag}")]
35    UnknownTermTag { tag: u8 },
36
37    #[error("unexpected term: expected={expected:?}, actual={actual:?}")]
38    UnexpectedTerm {
39        expected: Vec<TermKind>,
40        actual: TermKind,
41    },
42
43    #[error("unknown allocation list item tag: {tag}")]
44    UnknownAllocationListItemTag { tag: usize },
45
46    #[error("unknown opcode: {opcode}")]
47    UnknownOpcode { opcode: u8 },
48
49    #[error("invalid Unicode codepoint: {value}")]
50    InvalidUnicodeCodepoint { value: u32 },
51
52    #[error(transparent)]
53    NumError(#[from] num::bigint::TryFromBigIntError<BigInt>),
54
55    #[error(transparent)]
56    IoError(#[from] std::io::Error),
57}
58
59/// Encoding errors.
60#[derive(Debug, thiserror::Error)]
61pub enum EncodeError {
62    #[error(transparent)]
63    IoError(#[from] std::io::Error),
64}
65
66/// Decodes BEAM instructions.
67pub fn decode_instructions(bytecode: &[u8]) -> Result<Vec<Instruction>, DecodeError> {
68    let mut reader = bytecode;
69    let mut instructions = Vec::new();
70    while !reader.is_empty() {
71        let instruction = Instruction::decode(&mut reader)?;
72        instructions.push(instruction);
73    }
74    Ok(instructions)
75}
76
77/// Encodes BEAM instructions.
78pub fn encode_instructions(instructions: &[Instruction]) -> Result<Vec<u8>, EncodeError> {
79    let mut buf = Vec::new();
80    for instruction in instructions {
81        instruction.encode(&mut buf)?;
82    }
83    Ok(buf)
84}