Skip to main content

baedeker_core/binary/
functionsec.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Function section parsing.
5//!
6//! Decodes function declarations from the function section.
7//! See [Spec §5.5.6](https://webassembly.github.io/spec/core/binary/modules.html#function-section).
8
9use alloc::vec::Vec;
10
11use crate::binary::leb128::{self, Cursor};
12use crate::binary::section::RawSection;
13use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
14use crate::types::TypeIdx;
15
16pub fn parse_function_section(section: &RawSection<'_>) -> Result<Vec<TypeIdx>, DecodeError> {
17    let mut cursor = Cursor::new(section.data);
18    let count = leb128::decode_u32(&mut cursor).map_err(|mut e| {
19        e.context = DecodeContext::FunctionSection;
20        e.offset = ByteOffset(section.offset + e.offset.0);
21        e
22    })?;
23
24    let mut functions = Vec::with_capacity(cursor.capacity_hint(count));
25    for _ in 0..count {
26        let type_idx = leb128::decode_u32(&mut cursor).map_err(|mut e| {
27            e.context = DecodeContext::FunctionSection;
28            e.offset = ByteOffset(section.offset + e.offset.0);
29            e
30        })?;
31        functions.push(TypeIdx(type_idx));
32    }
33
34    if !cursor.is_empty() {
35        return Err(DecodeError {
36            offset: ByteOffset(section.offset + cursor.position()),
37            context: DecodeContext::FunctionSection,
38            kind: DecodeErrorKind::SectionSizeMismatch {
39                expected: section.data.len() as u32,
40                consumed: cursor.position() as u32,
41            },
42        });
43    }
44
45    Ok(functions)
46}
47
48#[cfg(test)]
49mod tests {
50    use alloc::vec;
51
52    use super::*;
53    use crate::binary::section::SectionId;
54
55    fn raw_function_section(data: &[u8]) -> RawSection<'_> {
56        RawSection {
57            id: SectionId::Function,
58            offset: 30,
59            data,
60        }
61    }
62
63    #[test]
64    fn parse_empty_function_section() {
65        let section = raw_function_section(&[0x00]);
66        let functions = parse_function_section(&section).unwrap();
67        assert!(functions.is_empty());
68    }
69
70    #[test]
71    fn parse_function_type_indices() {
72        let section = raw_function_section(&[0x03, 0x00, 0x02, 0x7F]);
73        let functions = parse_function_section(&section).unwrap();
74        assert_eq!(functions, vec![TypeIdx(0), TypeIdx(2), TypeIdx(127)]);
75    }
76
77    #[test]
78    fn reject_trailing_bytes() {
79        let section = raw_function_section(&[0x00, 0xFF]);
80        let err = parse_function_section(&section).unwrap_err();
81        assert_eq!(
82            err.kind,
83            DecodeErrorKind::SectionSizeMismatch {
84                expected: 2,
85                consumed: 1,
86            }
87        );
88    }
89}