baedeker_core/binary/
exportsec.rs1use alloc::{borrow::ToOwned, string::String, vec::Vec};
10
11use crate::binary::leb128::{self, Cursor};
12use crate::binary::section::RawSection;
13use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
14use crate::types::{Export, ExportDesc, FuncIdx, GlobalIdx, MemIdx, TableIdx};
15
16pub fn parse_export_section(section: &RawSection<'_>) -> Result<Vec<Export>, DecodeError> {
17 let mut cursor = Cursor::new(section.data);
18 let count = decode_u32_in_section(&mut cursor, section.offset)?;
19
20 let mut exports = Vec::with_capacity(cursor.capacity_hint(count));
21 for _ in 0..count {
22 let name = parse_name(&mut cursor, section.offset)?;
23 let desc = parse_export_desc(&mut cursor, section.offset)?;
24 exports.push(Export { name, desc });
25 }
26
27 if !cursor.is_empty() {
28 return Err(DecodeError {
29 offset: ByteOffset(section.offset + cursor.position()),
30 context: DecodeContext::ExportSection,
31 kind: DecodeErrorKind::SectionSizeMismatch {
32 expected: section.data.len() as u32,
33 consumed: cursor.position() as u32,
34 },
35 });
36 }
37
38 Ok(exports)
39}
40
41fn parse_name(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<String, DecodeError> {
42 let length = decode_u32_in_section(cursor, base_offset)? as usize;
43 let offset = cursor.position();
44 let bytes = cursor.read_bytes(length).map_err(|_| DecodeError {
45 offset: ByteOffset(base_offset + offset),
46 context: DecodeContext::ExportSection,
47 kind: DecodeErrorKind::UnexpectedEof,
48 })?;
49
50 core::str::from_utf8(bytes)
51 .map(str::to_owned)
52 .map_err(|_| DecodeError {
53 offset: ByteOffset(base_offset + offset),
54 context: DecodeContext::ExportSection,
55 kind: DecodeErrorKind::InvalidUtf8,
56 })
57}
58
59fn parse_export_desc(
60 cursor: &mut Cursor<'_>,
61 base_offset: usize,
62) -> Result<ExportDesc, DecodeError> {
63 let offset = cursor.position();
64 let byte = cursor.read_byte().map_err(|_| DecodeError {
65 offset: ByteOffset(base_offset + offset),
66 context: DecodeContext::ExportSection,
67 kind: DecodeErrorKind::UnexpectedEof,
68 })?;
69
70 let idx = decode_u32_in_section(cursor, base_offset)?;
71 match byte {
72 0x00 => Ok(ExportDesc::Func(FuncIdx(idx))),
73 0x01 => Ok(ExportDesc::Table(TableIdx(idx))),
74 0x02 => Ok(ExportDesc::Mem(MemIdx(idx))),
75 0x03 => Ok(ExportDesc::Global(GlobalIdx(idx))),
76 _ => Err(DecodeError {
77 offset: ByteOffset(base_offset + offset),
78 context: DecodeContext::ExportSection,
79 kind: DecodeErrorKind::UnknownExportDesc { byte },
80 }),
81 }
82}
83
84fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
85 leb128::decode_u32(cursor).map_err(|mut e| {
86 e.context = DecodeContext::ExportSection;
87 e.offset = ByteOffset(base_offset + e.offset.0);
88 e
89 })
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::binary::section::SectionId;
96
97 fn raw_export_section(data: &[u8]) -> RawSection<'_> {
98 RawSection {
99 id: SectionId::Export,
100 offset: 28,
101 data,
102 }
103 }
104
105 #[test]
106 fn parse_empty_export_section() {
107 let section = raw_export_section(&[0x00]);
108 let exports = parse_export_section(§ion).unwrap();
109 assert!(exports.is_empty());
110 }
111
112 #[test]
113 fn parse_function_and_memory_exports() {
114 let section = raw_export_section(&[
115 0x02, 0x03, b'a', b'd', b'd', 0x00, 0x01, 0x03, b'm', b'e', b'm', 0x02, 0x00,
116 ]);
117
118 let exports = parse_export_section(§ion).unwrap();
119 assert_eq!(exports.len(), 2);
120 assert_eq!(exports[0].name, "add");
121 assert_eq!(exports[0].desc, ExportDesc::Func(FuncIdx(1)));
122 assert_eq!(exports[1].name, "mem");
123 assert_eq!(exports[1].desc, ExportDesc::Mem(MemIdx(0)));
124 }
125
126 #[test]
127 fn reject_unknown_export_descriptor() {
128 let section = raw_export_section(&[0x01, 0x01, b'x', 0x04, 0x00]);
129 let err = parse_export_section(§ion).unwrap_err();
130 assert!(matches!(
131 err.kind,
132 DecodeErrorKind::UnknownExportDesc { byte: 0x04 }
133 ));
134 }
135}