webview_bundle/
decoder.rs1use crate::bundle::{
2 Bundle, FileDescriptors, Version, FILE_DESCRIPTORS_SIZE_BYTES_LENGTH, HEADER_MAGIC_BYTES,
3 VERSION_BYTES_LENGTH,
4};
5use bincode::{config, decode_from_slice};
6use std::hash::Hasher;
7use std::io::{Cursor, Read};
8use twox_hash::XxHash32;
9
10pub fn decode(buf: impl AsRef<[u8]>) -> crate::Result<Bundle> {
11 Decoder::new(&buf).decode()
12}
13
14struct Decoder<'a, T> {
15 buf: &'a T,
16 c: Cursor<&'a T>,
17}
18
19impl<'a, T> Decoder<'a, T> {
20 fn new(buf: &'a T) -> Self {
21 Self {
22 buf,
23 c: Cursor::new(buf),
24 }
25 }
26}
27
28impl<'a, T: AsRef<[u8]>> Decoder<'a, T> {
29 fn decode(&mut self) -> crate::Result<Bundle> {
30 self.read_magic_bytes()?;
31 let version = self.read_version()?;
32 let descriptors = self.read_file_descriptors()?;
33 self.read_header_checksum()?;
34 let data = self.read_data()?;
35 let bundle = Bundle {
36 version,
37 descriptors,
38 data,
39 };
40 Ok(bundle)
41 }
42
43 fn read_magic_bytes(&mut self) -> crate::Result<()> {
44 let mut buf = [0u8; HEADER_MAGIC_BYTES.len()];
45 self.c.read_exact(&mut buf)?;
46 if buf != HEADER_MAGIC_BYTES {
47 return Err(crate::Error::InvalidMagicNum);
48 }
49 Ok(())
50 }
51
52 fn read_version(&mut self) -> crate::Result<Version> {
53 let mut buf = [0u8; VERSION_BYTES_LENGTH];
54 self.c.read_exact(&mut buf)?;
55 if buf == Version::Version1.bytes() {
56 return Ok(Version::Version1);
57 }
58 Err(crate::Error::InvalidVersion)
59 }
60
61 fn read_file_descriptors(&mut self) -> crate::Result<FileDescriptors> {
62 let mut size_buf = [0u8; FILE_DESCRIPTORS_SIZE_BYTES_LENGTH];
63 self.c.read_exact(&mut size_buf)?;
64 let size = u32::from_be_bytes(AsRef::<[u8]>::as_ref(&size_buf).try_into().unwrap());
65
66 let mut descriptors_buf = vec![0u8; size as usize];
67 self.c.read_exact(&mut descriptors_buf)?;
68 let config = config::standard().with_big_endian();
69 let (descriptors, _): (FileDescriptors, _) = decode_from_slice(&descriptors_buf, config)
70 .map_err(|e| crate::Error::Decode {
71 error: e,
72 message: "fail to decode file descriptors".to_string(),
73 })?;
74 Ok(descriptors)
75 }
76
77 fn read_header_checksum(&mut self) -> crate::Result<u32> {
78 let mut buf = [0u8; size_of::<u32>()];
79 self.c.read_exact(&mut buf)?;
80 let checksum = u32::from_be_bytes(buf);
81
82 let mut hasher = XxHash32::with_seed(0);
83 let start = 0;
84 let end = self.c.position() as usize - 4;
85 hasher.write(&self.buf.as_ref()[start..end]);
86 let expected_checksum = hasher.finish() as u32;
87 if checksum != expected_checksum {
88 return Err(crate::Error::HeaderChecksumMismatch);
89 }
90 Ok(checksum)
91 }
92
93 fn read_data(&mut self) -> crate::Result<Vec<u8>> {
94 let total_len = self.buf.as_ref().len();
95 let data_len = total_len - self.c.position() as usize - size_of::<u32>();
96 let mut data = vec![0u8; data_len];
97 self.c.read_exact(&mut data)?;
98
99 let mut checksum_buf = [0u8; size_of::<u32>()];
100 self.c.read_exact(&mut checksum_buf)?;
101 let checksum = u32::from_be_bytes(checksum_buf);
102
103 let mut hasher = XxHash32::with_seed(0);
104 let start = 0;
105 let end = &self.buf.as_ref().len() - 4;
106 hasher.write(&self.buf.as_ref()[start..end]);
107 let expected_checksum = hasher.finish() as u32;
108 if checksum != expected_checksum {
109 return Err(crate::Error::ContentChecksumMismatch);
110 }
111 Ok(data)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::encoder::encode_bytes;
119
120 #[test]
121 fn encode_and_decode() {
122 let file = r#"const a = 10;"#;
123 let bundle = Bundle::builder()
124 .add_file("index.js", file.as_bytes())
125 .build();
126 let encoded = encode_bytes(&bundle).unwrap();
127 let decoded = decode(encoded).unwrap();
128 assert_eq!(bundle, decoded);
129 }
130
131 #[test]
132 fn invalid_magic() {
133 assert!(matches!(
134 decode(vec![0, 0, 0, 0, 0, 0, 0, 0]).unwrap_err(),
135 crate::Error::InvalidMagicNum,
136 ));
137 }
138}