const FKP_PAGE_SIZE: usize = 512;
use crate::ole::binary::read_u32_le;
#[derive(Debug, Clone)]
pub struct FkpEntry {
pub fc: u32,
pub grpprl: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ChpxFkp {
entries: Vec<FkpEntry>,
}
impl ChpxFkp {
pub fn parse(page_data: &[u8], _data_stream: &[u8]) -> Option<Self> {
if page_data.len() != FKP_PAGE_SIZE {
return None;
}
let crun = page_data[511] as usize;
if crun == 0 || crun > 101 {
return None;
}
let mut entries = Vec::with_capacity(crun);
let mut fcs = Vec::with_capacity(crun + 1);
for i in 0..=crun {
let offset = i * 4;
if offset + 4 > page_data.len() {
return None;
}
let fc = read_u32_le(page_data, offset).unwrap_or(0);
fcs.push(fc);
}
let bx_offset = (crun + 1) * 4;
for (i, fc_start) in fcs.iter().enumerate().take(crun) {
let bx_index = bx_offset + i;
if bx_index >= page_data.len() {
return None;
}
let bx = page_data[bx_index] as usize;
if bx == 0 {
entries.push(FkpEntry {
fc: *fc_start,
grpprl: Vec::new(),
});
continue;
}
let grpprl_offset = bx * 2;
if grpprl_offset >= FKP_PAGE_SIZE {
return None;
}
if grpprl_offset >= page_data.len() {
return None;
}
let cb = page_data[grpprl_offset] as usize;
if entries.len() < 2 && cb > 0 {
eprintln!("DEBUG: FKP entry {}: bx={}, grpprl_offset={}, cb={}", i, bx, grpprl_offset, cb);
}
let grpprl_start = grpprl_offset + 1;
let grpprl_end = grpprl_start + cb;
if grpprl_end > FKP_PAGE_SIZE {
return None;
}
let grpprl = page_data[grpprl_start..grpprl_end].to_vec();
entries.push(FkpEntry {
fc: *fc_start,
grpprl,
});
}
Some(Self { entries })
}
#[inline]
pub fn count(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn entry(&self, index: usize) -> Option<&FkpEntry> {
self.entries.get(index)
}
#[inline]
pub fn entries(&self) -> &[FkpEntry] {
&self.entries
}
}
#[derive(Debug, Clone)]
pub struct PapxFkp {
entries: Vec<FkpEntry>,
}
impl PapxFkp {
pub fn parse(page_data: &[u8], _data_stream: &[u8]) -> Option<Self> {
if page_data.len() != FKP_PAGE_SIZE {
return None;
}
let cpara = page_data[511] as usize;
if cpara == 0 || cpara > 101 {
return None;
}
let mut entries = Vec::with_capacity(cpara);
let mut fcs = Vec::with_capacity(cpara + 1);
for i in 0..=cpara {
let offset = i * 4;
if offset + 4 > page_data.len() {
return None;
}
let fc = read_u32_le(page_data, offset).unwrap_or(0);
fcs.push(fc);
}
let bx_offset = (cpara + 1) * 4;
for (i, fc_start) in fcs.iter().enumerate().take(cpara) {
let bx_index = bx_offset + (i * 13);
if bx_index + 13 > page_data.len() {
return None;
}
entries.push(FkpEntry {
fc: *fc_start,
grpprl: Vec::new(),
});
}
Some(Self { entries })
}
#[inline]
pub fn count(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn entry(&self, index: usize) -> Option<&FkpEntry> {
self.entries.get(index)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fkp_page_size() {
assert_eq!(FKP_PAGE_SIZE, 512);
}
#[test]
fn test_invalid_page_size() {
let small_page = vec![0u8; 100];
assert!(ChpxFkp::parse(&small_page, &[]).is_none());
let large_page = vec![0u8; 1000];
assert!(ChpxFkp::parse(&large_page, &[]).is_none());
}
#[test]
fn test_invalid_crun() {
let mut page = vec![0u8; 512];
page[511] = 0; assert!(ChpxFkp::parse(&page, &[]).is_none());
page[511] = 200; assert!(ChpxFkp::parse(&page, &[]).is_none());
}
}