use crate::parser::{read_u16, read_u8};
use crate::Error;
#[derive(Debug, Clone)]
pub(crate) struct Index<'a> {
pub(crate) bytes: &'a [u8],
pub(crate) count: u32,
pub(crate) end: usize,
off_size: u8,
offsets_at: usize,
data_at: usize,
}
impl<'a> Index<'a> {
pub(crate) fn parse(bytes: &'a [u8], at: usize) -> Result<Self, Error> {
if at > bytes.len() {
return Err(Error::UnexpectedEof);
}
let count = read_u16(bytes, at)? as u32;
if count == 0 {
return Ok(Self {
bytes,
count: 0,
end: at + 2,
off_size: 1,
offsets_at: at + 2,
data_at: at + 2,
});
}
let off_size = read_u8(bytes, at + 2)?;
if !(1..=4).contains(&off_size) {
return Err(Error::Cff("INDEX offSize out of range"));
}
let offsets_at = at + 3;
let offsets_len = (count as usize + 1) * off_size as usize;
let data_at = offsets_at + offsets_len;
if data_at > bytes.len() {
return Err(Error::UnexpectedEof);
}
let last = read_offset(bytes, offsets_at, off_size, count as usize)?;
if last < 1 {
return Err(Error::Cff("INDEX last offset == 0"));
}
let data_len = (last - 1) as usize;
let end = data_at + data_len;
if end > bytes.len() {
return Err(Error::UnexpectedEof);
}
Ok(Self {
bytes,
count,
end,
off_size,
offsets_at,
data_at,
})
}
pub(crate) fn entry(&self, i: u32) -> Result<&'a [u8], Error> {
if i >= self.count {
return Err(Error::Cff("INDEX entry out of range"));
}
let start = read_offset(self.bytes, self.offsets_at, self.off_size, i as usize)?;
let end_off = read_offset(self.bytes, self.offsets_at, self.off_size, i as usize + 1)?;
if start < 1 || end_off < start {
return Err(Error::Cff("malformed INDEX offsets"));
}
let s = self.data_at + (start - 1) as usize;
let e = self.data_at + (end_off - 1) as usize;
if e > self.bytes.len() {
return Err(Error::UnexpectedEof);
}
Ok(&self.bytes[s..e])
}
}
fn read_offset(bytes: &[u8], offsets_at: usize, off_size: u8, i: usize) -> Result<u32, Error> {
let off = offsets_at + i * off_size as usize;
let s = bytes
.get(off..off + off_size as usize)
.ok_or(Error::UnexpectedEof)?;
Ok(match off_size {
1 => s[0] as u32,
2 => u16::from_be_bytes([s[0], s[1]]) as u32,
3 => ((s[0] as u32) << 16) | ((s[1] as u32) << 8) | s[2] as u32,
4 => u32::from_be_bytes([s[0], s[1], s[2], s[3]]),
_ => unreachable!("off_size validated to 1..=4 in parse()"),
})
}
#[cfg(test)]
mod tests {
use super::*;
fn build_three_entry_index() -> Vec<u8> {
vec![
0x00, 0x03, 0x01, 0x01, 0x04, 0x06, 0x06, b'a', b'b', b'c', b'd', b'e',
]
}
#[test]
fn parses_three_entries() {
let v = build_three_entry_index();
let idx = Index::parse(&v, 0).expect("parse");
assert_eq!(idx.count, 3);
assert_eq!(idx.entry(0).unwrap(), b"abc");
assert_eq!(idx.entry(1).unwrap(), b"de");
assert_eq!(idx.entry(2).unwrap(), b"");
assert!(idx.entry(3).is_err());
}
#[test]
fn empty_index_is_two_bytes() {
let v = vec![0u8, 0]; let idx = Index::parse(&v, 0).expect("parse");
assert_eq!(idx.count, 0);
assert_eq!(idx.end, 2);
assert!(idx.entry(0).is_err());
}
#[test]
fn end_field_points_past_data() {
let v = build_three_entry_index();
let idx = Index::parse(&v, 0).expect("parse");
assert_eq!(idx.end, v.len());
}
#[test]
fn rejects_truncated_data() {
let mut v = build_three_entry_index();
v.pop();
assert!(Index::parse(&v, 0).is_err());
}
#[test]
fn handles_off_size_2() {
let v = vec![
0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x05, b'W', b'X', b'Y', b'Z',
];
let idx = Index::parse(&v, 0).expect("parse");
assert_eq!(idx.entry(0).unwrap(), b"WXYZ");
}
}