Skip to main content

baedeker_core/binary/
datasec.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Data section parsing.
5//!
6//! Decodes defined data segments and their initialization payloads.
7//! See [Spec §5.5.14](https://webassembly.github.io/spec/core/binary/modules.html#data-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::{DataMode, DataSegment, MemIdx};
15
16pub fn parse_data_section<'a>(
17    section: &RawSection<'a>,
18) -> Result<Vec<DataSegment<'a>>, DecodeError> {
19    let mut cursor = Cursor::new(section.data);
20    let count = decode_u32_in_section(&mut cursor, section.offset)?;
21
22    let mut segments = Vec::with_capacity(cursor.capacity_hint(count));
23    for _ in 0..count {
24        segments.push(parse_data_segment(&mut cursor, section.offset)?);
25    }
26
27    if !cursor.is_empty() {
28        return Err(DecodeError {
29            offset: ByteOffset(section.offset + cursor.position()),
30            context: DecodeContext::DataSection,
31            kind: DecodeErrorKind::SectionSizeMismatch {
32                expected: section.data.len() as u32,
33                consumed: cursor.position() as u32,
34            },
35        });
36    }
37
38    Ok(segments)
39}
40
41pub fn parse_data_count_section(section: &RawSection<'_>) -> Result<u32, DecodeError> {
42    let mut cursor = Cursor::new(section.data);
43    let count = leb128::decode_u32(&mut cursor).map_err(|mut e| {
44        e.context = DecodeContext::DataCountSection;
45        e.offset = ByteOffset(section.offset + e.offset.0);
46        e
47    })?;
48
49    if !cursor.is_empty() {
50        return Err(DecodeError {
51            offset: ByteOffset(section.offset + cursor.position()),
52            context: DecodeContext::DataCountSection,
53            kind: DecodeErrorKind::SectionSizeMismatch {
54                expected: section.data.len() as u32,
55                consumed: cursor.position() as u32,
56            },
57        });
58    }
59
60    Ok(count)
61}
62
63fn parse_data_segment<'a>(
64    cursor: &mut Cursor<'a>,
65    base_offset: usize,
66) -> Result<DataSegment<'a>, DecodeError> {
67    let flag = decode_u32_in_section(cursor, base_offset)?;
68    match flag {
69        0 => {
70            let offset_offset = base_offset + cursor.position();
71            let offset_expr = parse_init_expr(cursor, base_offset)?;
72            let init = parse_byte_vec(cursor, base_offset)?;
73            let init_offset = base_offset + cursor.position() - init.len();
74            Ok(DataSegment {
75                mode: DataMode::Active {
76                    memory: MemIdx(0),
77                    offset_expr,
78                    offset_offset,
79                },
80                init,
81                init_offset,
82            })
83        }
84        1 => {
85            let init = parse_byte_vec(cursor, base_offset)?;
86            let init_offset = base_offset + cursor.position() - init.len();
87            Ok(DataSegment {
88                mode: DataMode::Passive,
89                init,
90                init_offset,
91            })
92        }
93        2 => {
94            let memory = MemIdx(decode_u32_in_section(cursor, base_offset)?);
95            let offset_offset = base_offset + cursor.position();
96            let offset_expr = parse_init_expr(cursor, base_offset)?;
97            let init = parse_byte_vec(cursor, base_offset)?;
98            let init_offset = base_offset + cursor.position() - init.len();
99            Ok(DataSegment {
100                mode: DataMode::Active {
101                    memory,
102                    offset_expr,
103                    offset_offset,
104                },
105                init,
106                init_offset,
107            })
108        }
109        _ => Err(DecodeError {
110            offset: ByteOffset(base_offset),
111            context: DecodeContext::DataSection,
112            kind: DecodeErrorKind::UnexpectedByte {
113                expected: 0x00,
114                found: flag as u8,
115            },
116        }),
117    }
118}
119
120fn parse_init_expr<'a>(
121    cursor: &mut Cursor<'a>,
122    base_offset: usize,
123) -> Result<&'a [u8], DecodeError> {
124    let start = cursor.position();
125    loop {
126        let pos = cursor.position();
127        let byte = cursor.read_byte().map_err(|_| DecodeError {
128            offset: ByteOffset(base_offset + pos),
129            context: DecodeContext::DataSection,
130            kind: DecodeErrorKind::UnexpectedEof,
131        })?;
132        if byte == 0x0B {
133            return Ok(&cursor.original()[start..cursor.position()]);
134        }
135    }
136}
137
138fn parse_byte_vec<'a>(
139    cursor: &mut Cursor<'a>,
140    base_offset: usize,
141) -> Result<&'a [u8], DecodeError> {
142    let len = decode_u32_in_section(cursor, base_offset)? as usize;
143    let pos = cursor.position();
144    cursor.read_bytes(len).map_err(|_| DecodeError {
145        offset: ByteOffset(base_offset + pos),
146        context: DecodeContext::DataSection,
147        kind: DecodeErrorKind::UnexpectedEof,
148    })
149}
150
151fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
152    leb128::decode_u32(cursor).map_err(|mut e| {
153        e.context = DecodeContext::DataSection;
154        e.offset = ByteOffset(base_offset + e.offset.0);
155        e
156    })
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use crate::binary::section::SectionId;
163
164    fn raw_data_section(data: &[u8]) -> RawSection<'_> {
165        RawSection {
166            id: SectionId::Data,
167            offset: 40,
168            data,
169        }
170    }
171
172    #[test]
173    fn parse_passive_data_segment() {
174        let section = raw_data_section(&[0x01, 0x01, 0x03, b'a', b'b', b'c']);
175        let segments = parse_data_section(&section).unwrap();
176        assert_eq!(segments.len(), 1);
177        assert!(matches!(segments[0].mode, DataMode::Passive));
178        assert_eq!(segments[0].init, b"abc");
179    }
180
181    #[test]
182    fn parse_active_data_segment() {
183        let section = raw_data_section(&[0x01, 0x00, 0x41, 0x00, 0x0B, 0x02, 0xAA, 0xBB]);
184        let segments = parse_data_section(&section).unwrap();
185        assert_eq!(segments.len(), 1);
186        match &segments[0].mode {
187            DataMode::Active {
188                memory,
189                offset_expr,
190                ..
191            } => {
192                assert_eq!(*memory, MemIdx(0));
193                assert_eq!(*offset_expr, &[0x41, 0x00, 0x0B]);
194            }
195            DataMode::Passive => panic!("expected active"),
196        }
197        assert_eq!(segments[0].init, &[0xAA, 0xBB]);
198    }
199}