pub const PAGE_LEN: usize = 4096;
pub const HEAP_START: usize = 0x28;
const GROUP_BYTES: usize = 36;
const NONE: u32 = 0x03FF_FFFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HeaderStyle {
Normal,
Fixed,
}
#[derive(Debug, Clone)]
pub struct DataPage {
pub index: u32,
pub table: u32,
pub next: u32,
pub seq: u32,
pub style: HeaderStyle,
rows: Vec<Vec<u8>>,
heap_used: usize,
}
fn align4(n: usize) -> usize {
(n + 3) & !3
}
fn dir_bytes(rows: usize) -> usize {
let full = rows / 16;
let rest = rows % 16;
full * GROUP_BYTES + if rest > 0 { rest * 2 + 4 } else { 0 }
}
impl DataPage {
pub fn new(index: u32, table: u32, seq: u32, style: HeaderStyle) -> Self {
DataPage {
index,
table,
next: NONE,
seq,
style,
rows: Vec::new(),
heap_used: 0,
}
}
pub fn rows(&self) -> usize {
self.rows.len()
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn try_push(&mut self, row: &[u8]) -> bool {
let need = self.heap_used + align4(row.len()) + dir_bytes(self.rows.len() + 1);
if need > PAGE_LEN - HEAP_START || self.rows.len() >= 0x1fff {
return false;
}
self.heap_used += align4(row.len());
self.rows.push(row.to_vec());
true
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut p = vec![0u8; PAGE_LEN];
let n = self.rows.len();
let free = PAGE_LEN - HEAP_START - self.heap_used - dir_bytes(n);
let (u5, nrl) = match self.style {
HeaderStyle::Normal => (1u16, n.saturating_sub(1) as u16),
HeaderStyle::Fixed => (n as u16, 0),
};
p[0x04..0x08].copy_from_slice(&self.index.to_le_bytes());
p[0x08..0x0c].copy_from_slice(&self.table.to_le_bytes());
p[0x0c..0x10].copy_from_slice(&self.next.to_le_bytes());
p[0x10..0x14].copy_from_slice(&self.seq.to_le_bytes());
p[0x18] = (n & 0xff) as u8;
p[0x19] = (((n & 7) << 5) | ((n >> 8) & 0x1f)) as u8;
p[0x1a] = (n >> 3) as u8;
p[0x1b] = 0x24;
p[0x1c..0x1e].copy_from_slice(&(free as u16).to_le_bytes());
p[0x1e..0x20].copy_from_slice(&(self.heap_used as u16).to_le_bytes());
p[0x20..0x22].copy_from_slice(&u5.to_le_bytes());
p[0x22..0x24].copy_from_slice(&nrl.to_le_bytes());
let mut off = 0usize;
for (i, row) in self.rows.iter().enumerate() {
let start = HEAP_START + off;
p[start..start + row.len()].copy_from_slice(row);
let group = i / 16;
let slot = i % 16;
let base = PAGE_LEN - group * GROUP_BYTES;
let at = base - 6 - 2 * slot;
p[at..at + 2].copy_from_slice(&(off as u16).to_le_bytes());
let flags = base - 4;
let bit = 1u16 << slot;
let present = u16::from_le_bytes([p[flags], p[flags + 1]]) | bit;
p[flags..flags + 2].copy_from_slice(&present.to_le_bytes());
p[flags + 2..flags + 4].copy_from_slice(&present.to_le_bytes());
off += align4(row.len());
}
p
}
}
pub fn index_page(index: u32, table: u32, next: u32, first_data: Option<u32>) -> Vec<u8> {
let mut p = vec![0u8; PAGE_LEN];
p[0x04..0x08].copy_from_slice(&index.to_le_bytes());
p[0x08..0x0c].copy_from_slice(&table.to_le_bytes());
p[0x0c..0x10].copy_from_slice(&next.to_le_bytes());
p[0x10..0x14].copy_from_slice(&1u32.to_le_bytes());
p[0x1b] = 0x64;
p[0x20..0x22].copy_from_slice(&0x1fffu16.to_le_bytes());
p[0x22..0x24].copy_from_slice(&0x1fffu16.to_le_bytes());
p[0x24..0x26].copy_from_slice(&0x03ecu16.to_le_bytes());
let words = [index, first_data.unwrap_or(NONE), NONE, 0, 0x1FFF_0000];
let mut off = HEAP_START;
for w in words {
p[off..off + 4].copy_from_slice(&w.to_le_bytes());
off += 4;
}
for _ in 0..1004 {
p[off..off + 4].copy_from_slice(&0x1FFF_FFF8u32.to_le_bytes());
off += 4;
}
debug_assert_eq!(off, PAGE_LEN - 20);
p
}
pub fn header_page(
num_tables: u32,
next_unused: u32,
sequence: u32,
pointers: &[(u32, u32, u32, u32)],
) -> Vec<u8> {
let mut p = vec![0u8; PAGE_LEN];
p[0x04..0x08].copy_from_slice(&(PAGE_LEN as u32).to_le_bytes());
p[0x08..0x0c].copy_from_slice(&num_tables.to_le_bytes());
p[0x0c..0x10].copy_from_slice(&next_unused.to_le_bytes());
p[0x10..0x14].copy_from_slice(&1u32.to_le_bytes());
p[0x14..0x18].copy_from_slice(&sequence.to_le_bytes());
let mut off = 0x1c;
for &(ty, empty, first, last) in pointers {
for w in [ty, empty, first, last] {
p[off..off + 4].copy_from_slice(&w.to_le_bytes());
off += 4;
}
}
p
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn packed_row_count_matches_rekordbox() {
let mut pg = DataPage::new(18, 8, 4318, HeaderStyle::Normal);
for i in 0..284u32 {
assert!(pg.try_push(&[i.to_le_bytes(), [0; 4], [0; 4]].concat()));
}
let b = pg.to_bytes();
assert_eq!((b[0x18], b[0x19], b[0x1a]), (28, 129, 35));
assert_eq!(u16::from_le_bytes([b[0x1c], b[0x1d]]), 8);
assert_eq!(u16::from_le_bytes([b[0x1e], b[0x1f]]), 3408);
assert_eq!(u16::from_le_bytes([b[0x22], b[0x23]]), 283);
}
#[test]
fn page_fills_then_rejects() {
let mut pg = DataPage::new(1, 8, 1, HeaderStyle::Normal);
let mut n = 0;
while pg.try_push(&[0u8; 12]) {
n += 1;
}
assert_eq!(n, 284);
}
}