webview_bundle/
decoder.rs

1use 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}