rtvm_primitives/bytecode/eof/
header.rs

1use super::{
2    decode_helpers::{consume_u16, consume_u8},
3    EofDecodeError,
4};
5use std::vec::Vec;
6
7/// EOF Header containing
8#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct EofHeader {
11    /// Size of EOF types section.
12    /// types section includes num of input and outputs and max stack size.
13    pub types_size: u16,
14    /// Sizes of EOF code section.
15    /// Code size can't be zero.
16    pub code_sizes: Vec<u16>,
17    /// EOF Container size.
18    /// Container size can be zero.
19    pub container_sizes: Vec<u16>,
20    /// EOF data size.
21    pub data_size: u16,
22    /// sum code sizes
23    pub sum_code_sizes: usize,
24    /// sum container sizes
25    pub sum_container_sizes: usize,
26}
27
28const KIND_TERMINAL: u8 = 0;
29const KIND_TYPES: u8 = 1;
30const KIND_CODE: u8 = 2;
31const KIND_CONTAINER: u8 = 3;
32const KIND_DATA: u8 = 4;
33
34#[inline]
35fn consume_header_section_size(input: &[u8]) -> Result<(&[u8], Vec<u16>, usize), EofDecodeError> {
36    // num_sections	2 bytes	0x0001-0xFFFF
37    // 16-bit unsigned big-endian integer denoting the number of the sections
38    let (input, num_sections) = consume_u16(input)?;
39    if num_sections == 0 {
40        return Err(EofDecodeError::NonSizes);
41    }
42    let byte_size = (num_sections * 2) as usize;
43    if input.len() < byte_size {
44        return Err(EofDecodeError::ShortInputForSizes);
45    }
46    let mut sizes = Vec::with_capacity(num_sections as usize);
47    let mut sum = 0;
48    for i in 0..num_sections as usize {
49        // size	2 bytes	0x0001-0xFFFF
50        // 16-bit unsigned big-endian integer denoting the length of the section content
51        let code_size = u16::from_be_bytes([input[i * 2], input[i * 2 + 1]]);
52        if code_size == 0 {
53            return Err(EofDecodeError::ZeroSize);
54        }
55        sum += code_size as usize;
56        sizes.push(code_size);
57    }
58
59    Ok((&input[byte_size..], sizes, sum))
60}
61
62impl EofHeader {
63    /// Length of the header in bytes.
64    ///
65    /// Length is calculated as:
66    /// magic 2 byte +
67    /// version 1 byte +
68    /// types section 3 bytes +
69    /// code section 3 bytes +
70    /// num_code_sections * 2 +
71    /// if num_container_sections != 0 { container section 3 bytes} +
72    /// num_container_sections * 2 +
73    /// data section 3 bytes +
74    /// terminator 1 byte
75    ///
76    /// It is minimum 15 bytes (there is at least one code section).
77    pub fn size(&self) -> usize {
78        let optional_container_sizes = if self.container_sizes.is_empty() {
79            0
80        } else {
81            3 + self.container_sizes.len() * 2
82        };
83        13 + self.code_sizes.len() * 2 + optional_container_sizes
84    }
85
86    /// Returns number of types.
87    pub fn types_count(&self) -> usize {
88        self.types_size as usize / 4
89    }
90
91    /// Returns body size. It is sum of code sizes, container sizes and data size.
92    pub fn body_size(&self) -> usize {
93        self.sum_code_sizes + self.sum_container_sizes + self.data_size as usize
94    }
95
96    /// Returns raw size of the EOF.
97    pub fn eof_size(&self) -> usize {
98        self.size() + self.body_size()
99    }
100
101    /// Encodes EOF header into binary form.
102    pub fn encode(&self, buffer: &mut Vec<u8>) {
103        // magic	2 bytes	0xEF00	EOF prefix
104        buffer.extend_from_slice(&0xEF00u16.to_be_bytes());
105        // version	1 byte	0x01	EOF version
106        buffer.push(0x01);
107        // kind_types	1 byte	0x01	kind marker for types size section
108        buffer.push(KIND_TYPES);
109        // types_size	2 bytes	0x0004-0xFFFF
110        buffer.extend_from_slice(&self.types_size.to_be_bytes());
111        // kind_code	1 byte	0x02	kind marker for code size section
112        buffer.push(KIND_CODE);
113        // code_sections_sizes
114        buffer.extend_from_slice(&(self.code_sizes.len() as u16).to_be_bytes());
115        for size in &self.code_sizes {
116            buffer.extend_from_slice(&size.to_be_bytes());
117        }
118        // kind_container_or_data	1 byte	0x03 or 0x04	kind marker for container size section or data size section
119        if self.container_sizes.is_empty() {
120            buffer.push(KIND_DATA);
121        } else {
122            buffer.push(KIND_CONTAINER);
123            // container_sections_sizes
124            buffer.extend_from_slice(&(self.container_sizes.len() as u16).to_be_bytes());
125            for size in &self.container_sizes {
126                buffer.extend_from_slice(&size.to_be_bytes());
127            }
128            // kind_data	1 byte	0x04	kind marker for data size section
129            buffer.push(KIND_DATA);
130        }
131        // data_size	2 bytes	0x0000-0xFFFF	16-bit unsigned big-endian integer denoting the length of the data section content
132        buffer.extend_from_slice(&self.data_size.to_be_bytes());
133        // terminator	1 byte	0x00	marks the end of the EofHeader
134        buffer.push(KIND_TERMINAL);
135    }
136
137    /// Decodes EOF header from binary form.
138    pub fn decode(input: &[u8]) -> Result<(Self, &[u8]), EofDecodeError> {
139        let mut header = EofHeader::default();
140
141        // magic	2 bytes	0xEF00	EOF prefix
142        let (input, kind) = consume_u16(input)?;
143        if kind != 0xEF00 {
144            return Err(EofDecodeError::InvalidEOFMagicNumber);
145        }
146
147        // version	1 byte	0x01	EOF version
148        let (input, version) = consume_u8(input)?;
149        if version != 0x01 {
150            return Err(EofDecodeError::InvalidEOFVersion);
151        }
152
153        // kind_types	1 byte	0x01	kind marker for types size section
154        let (input, kind_types) = consume_u8(input)?;
155        if kind_types != KIND_TYPES {
156            return Err(EofDecodeError::InvalidTypesKind);
157        }
158
159        // types_size	2 bytes	0x0004-0xFFFF
160        // 16-bit unsigned big-endian integer denoting the length of the type section content
161        let (input, types_size) = consume_u16(input)?;
162        header.types_size = types_size;
163
164        if header.types_size % 4 != 0 {
165            return Err(EofDecodeError::InvalidTypesSection);
166        }
167
168        // kind_code	1 byte	0x02	kind marker for code size section
169        let (input, kind_types) = consume_u8(input)?;
170        if kind_types != KIND_CODE {
171            return Err(EofDecodeError::InvalidCodeKind);
172        }
173
174        // code_sections_sizes
175        let (input, sizes, sum) = consume_header_section_size(input)?;
176
177        if sizes.len() > 1024 {
178            return Err(EofDecodeError::TooManyCodeSections);
179        }
180
181        if sizes.is_empty() {
182            return Err(EofDecodeError::ZeroCodeSections);
183        }
184
185        if sizes.len() != (types_size / 4) as usize {
186            return Err(EofDecodeError::MismatchCodeAndTypesSize);
187        }
188
189        header.code_sizes = sizes;
190        header.sum_code_sizes = sum;
191
192        let (input, kind_container_or_data) = consume_u8(input)?;
193
194        let input = match kind_container_or_data {
195            KIND_CONTAINER => {
196                // container_sections_sizes
197                let (input, sizes, sum) = consume_header_section_size(input)?;
198                // the number of container sections must not exceed 256
199                if sizes.len() > 256 {
200                    return Err(EofDecodeError::TooManyContainerSections);
201                }
202                header.container_sizes = sizes;
203                header.sum_container_sizes = sum;
204                let (input, kind_data) = consume_u8(input)?;
205                if kind_data != KIND_DATA {
206                    return Err(EofDecodeError::InvalidDataKind);
207                }
208                input
209            }
210            KIND_DATA => input,
211            _ => return Err(EofDecodeError::InvalidKindAfterCode),
212        };
213
214        // data_size	2 bytes	0x0000-0xFFFF	16-bit
215        // unsigned big-endian integer denoting the length
216        // of the data section content (for not yet deployed
217        // containers this can be more than the actual content, see Data Section Lifecycle)
218        let (input, data_size) = consume_u16(input)?;
219        header.data_size = data_size;
220
221        // terminator	1 byte	0x00	marks the end of the EofHeader
222        let (input, terminator) = consume_u8(input)?;
223        if terminator != KIND_TERMINAL {
224            return Err(EofDecodeError::InvalidTerminalByte);
225        }
226
227        Ok((header, input))
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::hex;
235
236    #[test]
237    fn sanity_header_decode() {
238        let input = hex!("ef000101000402000100010400000000800000fe");
239        let (header, _) = EofHeader::decode(&input).unwrap();
240        assert_eq!(header.types_size, 4);
241        assert_eq!(header.code_sizes, vec![1]);
242        assert_eq!(header.container_sizes, vec![]);
243        assert_eq!(header.data_size, 0);
244    }
245
246    #[test]
247    fn decode_header_not_terminated() {
248        let input = hex!("ef0001010004");
249        assert_eq!(EofHeader::decode(&input), Err(EofDecodeError::MissingInput));
250    }
251
252    #[test]
253    fn failing_test() {
254        let input = hex!("ef00010100040200010006030001001404000200008000016000e0000000ef000101000402000100010400000000800000fe");
255        let _ = EofHeader::decode(&input).unwrap();
256    }
257}