1#[cfg(test)]
2mod test;
3
4use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
6use std::string::FromUtf8Error;
7use thiserror::Error;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12const IDENTIFIER: u32 = 0x57494C44;
13
14#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Default, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct Section {
17 pub id: u8,
18 pub flags: u32,
19 pub checksum: u32,
20 pub body: Vec<u8>,
21}
22
23#[derive(Debug, Error)]
24pub enum Error {
25 #[error("invalid identifier: {0:#X}. required: 0x57494C44")]
26 InvalidIdentifier(u32),
27 #[error("read error occurred")]
28 Io(#[from] io::Error),
29 #[error("string was not UTF-8")]
30 NotUtf8(#[from] FromUtf8Error),
31}
32
33const HEADER_IDENTIFIER: usize = 4;
34const HEADER_SECTIONS_LEN: usize = 1;
35const HEADER_SECTIONS: usize = 1 + 8;
36
37const SECTION_FLAGS: usize = 4;
38const SECTION_CHECKSUM: usize = 4;
39const SECTION_BODY_LEN: usize = 8;
40
41pub fn encode(writer: &mut impl Write, sections: Vec<Section>) -> Result<(), Error> {
42 writer.write_u32::<BigEndian>(IDENTIFIER)?;
43
44 let sections_len = sections.len() as u8;
45 writer.write_u8(sections_len)?;
46
47 let header_offset =
48 HEADER_IDENTIFIER + HEADER_SECTIONS_LEN + (sections.len() * HEADER_SECTIONS);
49
50 let mut encoded_sections = Cursor::new(vec![]);
51
52 for idx in 0..sections_len {
53 let section = §ions[idx as usize];
54 let section_offset =
55 SECTION_FLAGS + SECTION_CHECKSUM + SECTION_BODY_LEN + section.body.len();
56 let offset = header_offset + (section_offset * idx as usize);
57
58 writer.write_u8(section.id)?;
59 writer.write_u64::<BigEndian>(offset as u64)?;
60
61 encoded_sections.write_u32::<BigEndian>(section.flags)?;
62 encoded_sections.write_u32::<BigEndian>(section.checksum)?;
63 encoded_sections.write_u64::<BigEndian>(section.body.len() as u64)?;
64 encoded_sections.write_all(§ion.body)?;
65 }
66
67 writer.write_all(&encoded_sections.into_inner())?;
68
69 Ok(())
70}
71
72#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug, Hash)]
73pub struct ContainerDecoder<R: Read> {
74 reader: R,
75 idx: usize,
76 pub sections: Vec<(u8, u64)>,
77}
78
79impl<R: Read> ContainerDecoder<R> {
80 pub fn new(mut reader: R) -> Result<Self, Error> {
81 let identifier = reader.read_u32::<BigEndian>()?;
82 if identifier != IDENTIFIER {
83 return Err(Error::InvalidIdentifier(identifier));
84 }
85
86 let sections_len = reader.read_u8()?;
87 let mut sections = vec![];
88
89 for _ in 0..sections_len {
90 let id = reader.read_u8()?;
91 let addr = reader.read_u64::<BigEndian>()?;
92 sections.push((id, addr));
93 }
94
95 Ok(Self {
96 reader,
97 idx: 0,
98 sections,
99 })
100 }
101}
102
103impl<R: Read + Seek> Iterator for ContainerDecoder<R> {
104 type Item = Section;
105
106 fn next(&mut self) -> Option<Self::Item> {
107 if self.idx == self.sections.len() {
108 return None;
109 }
110
111 let (id, addr) = self.sections[self.idx];
112 self.idx += 1;
113
114 self.reader.seek(SeekFrom::Start(addr)).ok()?;
115
116 let flags = self.reader.read_u32::<BigEndian>().ok()?;
117 let checksum = self.reader.read_u32::<BigEndian>().ok()?;
118 let body_len = self.reader.read_u64::<BigEndian>().ok()?;
119
120 let mut body = vec![0; body_len as usize];
121 self.reader.read_exact(&mut body).ok()?;
122
123 Some(Section {
124 id,
125 flags,
126 checksum,
127 body,
128 })
129 }
130}