use std::convert::TryFrom; pub struct Package { pub _type: u8, pub version: u8, pub value: u64, pub children: Vec, } pub struct PackageReader { pub buffer: Vec, position: usize, } impl PackageReader { pub fn read(buffer: Vec) -> Result { let mut reader = Self { buffer: buffer, position: 0, }; let package = reader.read_package()?; reader.check_empty()?; return Ok(package); } fn read_u8(&mut self, size: usize) -> Result { assert!(size < 8); return Ok(u8::try_from(self.read_u64(size)?).unwrap()); } fn read_u64(&mut self, size: usize) -> Result { // FIXME: lazily read from input file assert!(size < 64); let start_i = self.position / 4; let end_i = (self.position + size) / 4; let end_j = (self.position + size) % 4; if end_i >= self.buffer.len() && end_j > 0 { return Err(String::from("out of bounds read")); } let mut x = 0; for i in start_i..=end_i { if i != end_i { x = (x << 4) | u64::from(self.buffer[i]); } else if end_j != 0 { x = (x << end_j) | (u64::from(self.buffer[i]) >> (4 - end_j)); } } x &= (1 << size) - 1; self.position += size; return Ok(x); } fn read_package(&mut self) -> Result { let mut package = Package { version: self.read_u8(3)?, _type: self.read_u8(3)?, value: 0, children: vec![], }; if package._type == 4 { loop { let c = self.read_u8(1)?; package.value <<= 4; package.value |= self.read_u64(4)?; if c == 0 { break; } } } else { let t = self.read_u8(1)?; if t == 0 { let len = self.read_u64(15)?; let end = self.position + usize::try_from(len).unwrap(); while self.position < end { package.children.push(self.read_package()?); } if self.position != end { return Err(String::from("invalid subpackage length")); } } else { let count = self.read_u64(11)?; for _ in 0..count { package.children.push(self.read_package()?); } } } return Ok(package); } fn check_empty(&mut self) -> Result<(), String> { let remaining = self.buffer.len() * 4 - self.position; if self.read_u8(remaining)? == 0 { return Ok(()); } else { return Err(String::from("remaining bits not 0")); } } }