Skip to main content

baedeker_core/binary/
memorysec.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Memory section parsing.
5//!
6//! Decodes defined memories from the memory section.
7//! See [Spec §5.5.7](https://webassembly.github.io/spec/core/binary/modules.html#memory-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::{Limits, MemType};
15
16pub fn parse_memory_section(section: &RawSection<'_>) -> Result<Vec<MemType>, DecodeError> {
17    let mut cursor = Cursor::new(section.data);
18    let count = decode_u32_in_section(&mut cursor, section.offset)?;
19
20    let mut memories = Vec::with_capacity(cursor.capacity_hint(count));
21    for _ in 0..count {
22        memories.push(MemType {
23            limits: parse_limits(&mut cursor, section.offset)?,
24        });
25    }
26
27    if !cursor.is_empty() {
28        return Err(DecodeError {
29            offset: ByteOffset(section.offset + cursor.position()),
30            context: DecodeContext::MemorySection,
31            kind: DecodeErrorKind::SectionSizeMismatch {
32                expected: section.data.len() as u32,
33                consumed: cursor.position() as u32,
34            },
35        });
36    }
37
38    Ok(memories)
39}
40
41fn parse_limits(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<Limits, DecodeError> {
42    let tag_offset = cursor.position();
43    let tag = cursor.read_byte().map_err(|_| DecodeError {
44        offset: ByteOffset(base_offset + tag_offset),
45        context: DecodeContext::MemorySection,
46        kind: DecodeErrorKind::UnexpectedEof,
47    })?;
48
49    match tag {
50        0x00 => {
51            let min = decode_u32_in_section(cursor, base_offset)?;
52            Ok(Limits { min, max: None })
53        }
54        0x01 => {
55            let min = decode_u32_in_section(cursor, base_offset)?;
56            let max = decode_u32_in_section(cursor, base_offset)?;
57            Ok(Limits {
58                min,
59                max: Some(max),
60            })
61        }
62        _ => Err(DecodeError {
63            offset: ByteOffset(base_offset + tag_offset),
64            context: DecodeContext::MemorySection,
65            kind: DecodeErrorKind::UnexpectedByte {
66                expected: 0x00,
67                found: tag,
68            },
69        }),
70    }
71}
72
73fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
74    leb128::decode_u32(cursor).map_err(|mut e| {
75        e.context = DecodeContext::MemorySection;
76        e.offset = ByteOffset(base_offset + e.offset.0);
77        e
78    })
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::binary::section::SectionId;
85
86    fn raw_memory_section(data: &[u8]) -> RawSection<'_> {
87        RawSection {
88            id: SectionId::Memory,
89            offset: 24,
90            data,
91        }
92    }
93
94    #[test]
95    fn parse_empty_memory_section() {
96        let section = raw_memory_section(&[0x00]);
97        let memories = parse_memory_section(&section).unwrap();
98        assert!(memories.is_empty());
99    }
100
101    #[test]
102    fn parse_single_min_only_memory() {
103        let section = raw_memory_section(&[0x01, 0x00, 0x02]);
104        let memories = parse_memory_section(&section).unwrap();
105        assert_eq!(memories.len(), 1);
106        assert_eq!(memories[0].limits.min, 2);
107        assert_eq!(memories[0].limits.max, None);
108    }
109
110    #[test]
111    fn parse_single_bounded_memory() {
112        let section = raw_memory_section(&[0x01, 0x01, 0x01, 0x03]);
113        let memories = parse_memory_section(&section).unwrap();
114        assert_eq!(memories.len(), 1);
115        assert_eq!(memories[0].limits.min, 1);
116        assert_eq!(memories[0].limits.max, Some(3));
117    }
118
119    #[test]
120    fn reject_invalid_limits_tag() {
121        let section = raw_memory_section(&[0x01, 0x02, 0x01]);
122        let err = parse_memory_section(&section).unwrap_err();
123        assert!(matches!(
124            err.kind,
125            DecodeErrorKind::UnexpectedByte {
126                expected: 0x00,
127                found: 0x02
128            }
129        ));
130    }
131}