use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Cursor, Read, Write};
use crate::error::{Error, Result};
use crate::structures::signatures;
#[derive(Debug, Clone, Copy)]
pub struct IndexLeafElement {
pub key_node_offset: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct FastLeafElement {
pub key_node_offset: u32,
pub name_hint: [u8; 4],
}
impl FastLeafElement {
pub fn create_name_hint(name: &str) -> [u8; 4] {
let mut hint = [0u8; 4];
for (i, c) in name.chars().take(4).enumerate() {
let code = c as u32;
if code <= 255 {
hint[i] = code as u8;
} else {
hint[0] = 0;
break;
}
}
hint
}
pub fn hint_as_string(&self) -> String {
self.name_hint
.iter()
.take_while(|&&b| b != 0)
.map(|&b| b as char)
.collect()
}
pub fn hint_matches(&self, name: &str) -> bool {
let target_hint = Self::create_name_hint(name);
if self.name_hint[0] == 0 || target_hint[0] == 0 {
return true; }
self.name_hint == target_hint
}
}
#[derive(Debug, Clone, Copy)]
pub struct HashLeafElement {
pub key_node_offset: u32,
pub name_hash: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct IndexRootElement {
pub subkeys_list_offset: u32,
}
#[derive(Debug, Clone)]
pub struct IndexLeaf {
pub signature: [u8; 2],
pub num_elements: u16,
pub elements: Vec<IndexLeafElement>,
}
impl IndexLeaf {
pub const HEADER_SIZE: usize = 4;
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < Self::HEADER_SIZE {
return Err(Error::BufferTooSmall {
needed: Self::HEADER_SIZE,
available: data.len(),
});
}
let mut cursor = Cursor::new(data);
let mut signature = [0u8; 2];
cursor.read_exact(&mut signature)?;
if &signature != signatures::INDEX_LEAF {
return Err(Error::InvalidSignature {
expected: "li".to_string(),
found: String::from_utf8_lossy(&signature).to_string(),
});
}
let num_elements = cursor.read_u16::<LittleEndian>()?;
let needed = Self::HEADER_SIZE + (num_elements as usize * 4);
if data.len() < needed {
return Err(Error::BufferTooSmall {
needed,
available: data.len(),
});
}
let mut elements = Vec::with_capacity(num_elements as usize);
for _ in 0..num_elements {
let key_node_offset = cursor.read_u32::<LittleEndian>()?;
elements.push(IndexLeafElement { key_node_offset });
}
Ok(Self {
signature,
num_elements,
elements,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.signature)?;
writer.write_u16::<LittleEndian>(self.num_elements)?;
for elem in &self.elements {
writer.write_u32::<LittleEndian>(elem.key_node_offset)?;
}
Ok(())
}
pub fn new() -> Self {
Self {
signature: *signatures::INDEX_LEAF,
num_elements: 0,
elements: Vec::new(),
}
}
pub fn total_size(&self) -> usize {
Self::HEADER_SIZE + (self.elements.len() * 4)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
self.write(&mut buffer).unwrap();
buffer
}
}
impl Default for IndexLeaf {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct FastLeaf {
pub signature: [u8; 2],
pub num_elements: u16,
pub elements: Vec<FastLeafElement>,
}
impl FastLeaf {
pub const HEADER_SIZE: usize = 4;
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < Self::HEADER_SIZE {
return Err(Error::BufferTooSmall {
needed: Self::HEADER_SIZE,
available: data.len(),
});
}
let mut cursor = Cursor::new(data);
let mut signature = [0u8; 2];
cursor.read_exact(&mut signature)?;
if &signature != signatures::FAST_LEAF {
return Err(Error::InvalidSignature {
expected: "lf".to_string(),
found: String::from_utf8_lossy(&signature).to_string(),
});
}
let num_elements = cursor.read_u16::<LittleEndian>()?;
let needed = Self::HEADER_SIZE + (num_elements as usize * 8);
if data.len() < needed {
return Err(Error::BufferTooSmall {
needed,
available: data.len(),
});
}
let mut elements = Vec::with_capacity(num_elements as usize);
for _ in 0..num_elements {
let key_node_offset = cursor.read_u32::<LittleEndian>()?;
let mut name_hint = [0u8; 4];
cursor.read_exact(&mut name_hint)?;
elements.push(FastLeafElement {
key_node_offset,
name_hint,
});
}
Ok(Self {
signature,
num_elements,
elements,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.signature)?;
writer.write_u16::<LittleEndian>(self.num_elements)?;
for elem in &self.elements {
writer.write_u32::<LittleEndian>(elem.key_node_offset)?;
writer.write_all(&elem.name_hint)?;
}
Ok(())
}
pub fn new() -> Self {
Self {
signature: *signatures::FAST_LEAF,
num_elements: 0,
elements: Vec::new(),
}
}
pub fn total_size(&self) -> usize {
Self::HEADER_SIZE + (self.elements.len() * 8)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
self.write(&mut buffer).unwrap();
buffer
}
}
impl Default for FastLeaf {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct HashLeaf {
pub signature: [u8; 2],
pub num_elements: u16,
pub elements: Vec<HashLeafElement>,
}
impl HashLeaf {
pub const HEADER_SIZE: usize = 4;
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < Self::HEADER_SIZE {
return Err(Error::BufferTooSmall {
needed: Self::HEADER_SIZE,
available: data.len(),
});
}
let mut cursor = Cursor::new(data);
let mut signature = [0u8; 2];
cursor.read_exact(&mut signature)?;
if &signature != signatures::HASH_LEAF {
return Err(Error::InvalidSignature {
expected: "lh".to_string(),
found: String::from_utf8_lossy(&signature).to_string(),
});
}
let num_elements = cursor.read_u16::<LittleEndian>()?;
let needed = Self::HEADER_SIZE + (num_elements as usize * 8);
if data.len() < needed {
return Err(Error::BufferTooSmall {
needed,
available: data.len(),
});
}
let mut elements = Vec::with_capacity(num_elements as usize);
for _ in 0..num_elements {
let key_node_offset = cursor.read_u32::<LittleEndian>()?;
let name_hash = cursor.read_u32::<LittleEndian>()?;
elements.push(HashLeafElement {
key_node_offset,
name_hash,
});
}
Ok(Self {
signature,
num_elements,
elements,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.signature)?;
writer.write_u16::<LittleEndian>(self.num_elements)?;
for elem in &self.elements {
writer.write_u32::<LittleEndian>(elem.key_node_offset)?;
writer.write_u32::<LittleEndian>(elem.name_hash)?;
}
Ok(())
}
pub fn new() -> Self {
Self {
signature: *signatures::HASH_LEAF,
num_elements: 0,
elements: Vec::new(),
}
}
pub fn total_size(&self) -> usize {
Self::HEADER_SIZE + (self.elements.len() * 8)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
self.write(&mut buffer).unwrap();
buffer
}
}
impl Default for HashLeaf {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct IndexRoot {
pub signature: [u8; 2],
pub num_elements: u16,
pub elements: Vec<IndexRootElement>,
}
impl IndexRoot {
pub const HEADER_SIZE: usize = 4;
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < Self::HEADER_SIZE {
return Err(Error::BufferTooSmall {
needed: Self::HEADER_SIZE,
available: data.len(),
});
}
let mut cursor = Cursor::new(data);
let mut signature = [0u8; 2];
cursor.read_exact(&mut signature)?;
if &signature != signatures::INDEX_ROOT {
return Err(Error::InvalidSignature {
expected: "ri".to_string(),
found: String::from_utf8_lossy(&signature).to_string(),
});
}
let num_elements = cursor.read_u16::<LittleEndian>()?;
let needed = Self::HEADER_SIZE + (num_elements as usize * 4);
if data.len() < needed {
return Err(Error::BufferTooSmall {
needed,
available: data.len(),
});
}
let mut elements = Vec::with_capacity(num_elements as usize);
for _ in 0..num_elements {
let subkeys_list_offset = cursor.read_u32::<LittleEndian>()?;
elements.push(IndexRootElement { subkeys_list_offset });
}
Ok(Self {
signature,
num_elements,
elements,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.signature)?;
writer.write_u16::<LittleEndian>(self.num_elements)?;
for elem in &self.elements {
writer.write_u32::<LittleEndian>(elem.subkeys_list_offset)?;
}
Ok(())
}
pub fn new() -> Self {
Self {
signature: *signatures::INDEX_ROOT,
num_elements: 0,
elements: Vec::new(),
}
}
pub fn total_size(&self) -> usize {
Self::HEADER_SIZE + (self.elements.len() * 4)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
self.write(&mut buffer).unwrap();
buffer
}
}
impl Default for IndexRoot {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum SubkeysList {
IndexLeaf(IndexLeaf),
FastLeaf(FastLeaf),
HashLeaf(HashLeaf),
IndexRoot(IndexRoot),
}
impl SubkeysList {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 2 {
return Err(Error::BufferTooSmall {
needed: 2,
available: data.len(),
});
}
let sig: [u8; 2] = [data[0], data[1]];
match &sig {
b"li" => Ok(SubkeysList::IndexLeaf(IndexLeaf::parse(data)?)),
b"lf" => Ok(SubkeysList::FastLeaf(FastLeaf::parse(data)?)),
b"lh" => Ok(SubkeysList::HashLeaf(HashLeaf::parse(data)?)),
b"ri" => Ok(SubkeysList::IndexRoot(IndexRoot::parse(data)?)),
_ => Err(Error::UnknownCellType(sig)),
}
}
pub fn get_offsets(&self) -> Vec<u32> {
match self {
SubkeysList::IndexLeaf(il) => il.elements.iter().map(|e| e.key_node_offset).collect(),
SubkeysList::FastLeaf(fl) => fl.elements.iter().map(|e| e.key_node_offset).collect(),
SubkeysList::HashLeaf(hl) => hl.elements.iter().map(|e| e.key_node_offset).collect(),
SubkeysList::IndexRoot(ir) => ir.elements.iter().map(|e| e.subkeys_list_offset).collect(),
}
}
pub fn is_index_root(&self) -> bool {
matches!(self, SubkeysList::IndexRoot(_))
}
pub fn to_bytes(&self) -> Vec<u8> {
match self {
SubkeysList::IndexLeaf(il) => il.to_bytes(),
SubkeysList::FastLeaf(fl) => fl.to_bytes(),
SubkeysList::HashLeaf(hl) => hl.to_bytes(),
SubkeysList::IndexRoot(ir) => ir.to_bytes(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_index_leaf() {
let mut il = IndexLeaf::new();
il.elements.push(IndexLeafElement { key_node_offset: 100 });
il.elements.push(IndexLeafElement { key_node_offset: 200 });
il.num_elements = il.elements.len() as u16;
let bytes = il.to_bytes();
let parsed = IndexLeaf::parse(&bytes).unwrap();
assert_eq!(parsed.num_elements, 2);
assert_eq!(parsed.elements[0].key_node_offset, 100);
assert_eq!(parsed.elements[1].key_node_offset, 200);
}
#[test]
fn test_hash_leaf() {
let mut hl = HashLeaf::new();
hl.elements.push(HashLeafElement {
key_node_offset: 100,
name_hash: 12345,
});
hl.num_elements = hl.elements.len() as u16;
let bytes = hl.to_bytes();
let parsed = HashLeaf::parse(&bytes).unwrap();
assert_eq!(parsed.num_elements, 1);
assert_eq!(parsed.elements[0].key_node_offset, 100);
assert_eq!(parsed.elements[0].name_hash, 12345);
}
}