1pub const PAGE_LEN: usize = 4096;
9pub const HEAP_START: usize = 0x28;
10const GROUP_BYTES: usize = 36;
11const NONE: u32 = 0x03FF_FFFF;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum HeaderStyle {
16 Normal,
18 Fixed,
20}
21
22#[derive(Debug, Clone)]
23pub struct DataPage {
24 pub index: u32,
25 pub table: u32,
26 pub next: u32,
27 pub seq: u32,
28 pub style: HeaderStyle,
29 rows: Vec<Vec<u8>>,
30 heap_used: usize,
31}
32
33fn align4(n: usize) -> usize {
34 (n + 3) & !3
35}
36
37fn dir_bytes(rows: usize) -> usize {
38 let full = rows / 16;
39 let rest = rows % 16;
40 full * GROUP_BYTES + if rest > 0 { rest * 2 + 4 } else { 0 }
41}
42
43impl DataPage {
44 pub fn new(index: u32, table: u32, seq: u32, style: HeaderStyle) -> Self {
45 DataPage {
46 index,
47 table,
48 next: NONE,
49 seq,
50 style,
51 rows: Vec::new(),
52 heap_used: 0,
53 }
54 }
55
56 pub fn rows(&self) -> usize {
57 self.rows.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
61 self.rows.is_empty()
62 }
63
64 pub fn try_push(&mut self, row: &[u8]) -> bool {
66 let need = self.heap_used + align4(row.len()) + dir_bytes(self.rows.len() + 1);
67 if need > PAGE_LEN - HEAP_START || self.rows.len() >= 0x1fff {
68 return false;
69 }
70 self.heap_used += align4(row.len());
71 self.rows.push(row.to_vec());
72 true
73 }
74
75 pub fn to_bytes(&self) -> Vec<u8> {
76 let mut p = vec![0u8; PAGE_LEN];
77 let n = self.rows.len();
78 let free = PAGE_LEN - HEAP_START - self.heap_used - dir_bytes(n);
79 let (u5, nrl) = match self.style {
80 HeaderStyle::Normal => (1u16, n.saturating_sub(1) as u16),
81 HeaderStyle::Fixed => (n as u16, 0),
82 };
83 p[0x04..0x08].copy_from_slice(&self.index.to_le_bytes());
84 p[0x08..0x0c].copy_from_slice(&self.table.to_le_bytes());
85 p[0x0c..0x10].copy_from_slice(&self.next.to_le_bytes());
86 p[0x10..0x14].copy_from_slice(&self.seq.to_le_bytes());
87 p[0x18] = (n & 0xff) as u8;
88 p[0x19] = (((n & 7) << 5) | ((n >> 8) & 0x1f)) as u8;
89 p[0x1a] = (n >> 3) as u8;
90 p[0x1b] = 0x24;
91 p[0x1c..0x1e].copy_from_slice(&(free as u16).to_le_bytes());
92 p[0x1e..0x20].copy_from_slice(&(self.heap_used as u16).to_le_bytes());
93 p[0x20..0x22].copy_from_slice(&u5.to_le_bytes());
94 p[0x22..0x24].copy_from_slice(&nrl.to_le_bytes());
95
96 let mut off = 0usize;
97 for (i, row) in self.rows.iter().enumerate() {
98 let start = HEAP_START + off;
99 p[start..start + row.len()].copy_from_slice(row);
100 let group = i / 16;
101 let slot = i % 16;
102 let base = PAGE_LEN - group * GROUP_BYTES;
103 let at = base - 6 - 2 * slot;
104 p[at..at + 2].copy_from_slice(&(off as u16).to_le_bytes());
105 let flags = base - 4;
106 let bit = 1u16 << slot;
107 let present = u16::from_le_bytes([p[flags], p[flags + 1]]) | bit;
108 p[flags..flags + 2].copy_from_slice(&present.to_le_bytes());
109 p[flags + 2..flags + 4].copy_from_slice(&present.to_le_bytes());
110 off += align4(row.len());
111 }
112 p
113 }
114}
115
116pub fn index_page(index: u32, table: u32, next: u32, first_data: Option<u32>) -> Vec<u8> {
119 let mut p = vec![0u8; PAGE_LEN];
120 p[0x04..0x08].copy_from_slice(&index.to_le_bytes());
121 p[0x08..0x0c].copy_from_slice(&table.to_le_bytes());
122 p[0x0c..0x10].copy_from_slice(&next.to_le_bytes());
123 p[0x10..0x14].copy_from_slice(&1u32.to_le_bytes());
124 p[0x1b] = 0x64;
125 p[0x20..0x22].copy_from_slice(&0x1fffu16.to_le_bytes());
126 p[0x22..0x24].copy_from_slice(&0x1fffu16.to_le_bytes());
127 p[0x24..0x26].copy_from_slice(&0x03ecu16.to_le_bytes());
128 let words = [index, first_data.unwrap_or(NONE), NONE, 0, 0x1FFF_0000];
129 let mut off = HEAP_START;
130 for w in words {
131 p[off..off + 4].copy_from_slice(&w.to_le_bytes());
132 off += 4;
133 }
134 for _ in 0..1004 {
135 p[off..off + 4].copy_from_slice(&0x1FFF_FFF8u32.to_le_bytes());
136 off += 4;
137 }
138 debug_assert_eq!(off, PAGE_LEN - 20);
139 p
140}
141
142pub fn header_page(
144 num_tables: u32,
145 next_unused: u32,
146 sequence: u32,
147 pointers: &[(u32, u32, u32, u32)],
148) -> Vec<u8> {
149 let mut p = vec![0u8; PAGE_LEN];
150 p[0x04..0x08].copy_from_slice(&(PAGE_LEN as u32).to_le_bytes());
151 p[0x08..0x0c].copy_from_slice(&num_tables.to_le_bytes());
152 p[0x0c..0x10].copy_from_slice(&next_unused.to_le_bytes());
153 p[0x10..0x14].copy_from_slice(&1u32.to_le_bytes());
154 p[0x14..0x18].copy_from_slice(&sequence.to_le_bytes());
155 let mut off = 0x1c;
156 for &(ty, empty, first, last) in pointers {
157 for w in [ty, empty, first, last] {
158 p[off..off + 4].copy_from_slice(&w.to_le_bytes());
159 off += 4;
160 }
161 }
162 p
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn packed_row_count_matches_rekordbox() {
171 let mut pg = DataPage::new(18, 8, 4318, HeaderStyle::Normal);
173 for i in 0..284u32 {
174 assert!(pg.try_push(&[i.to_le_bytes(), [0; 4], [0; 4]].concat()));
175 }
176 let b = pg.to_bytes();
177 assert_eq!((b[0x18], b[0x19], b[0x1a]), (28, 129, 35));
178 assert_eq!(u16::from_le_bytes([b[0x1c], b[0x1d]]), 8);
179 assert_eq!(u16::from_le_bytes([b[0x1e], b[0x1f]]), 3408);
180 assert_eq!(u16::from_le_bytes([b[0x22], b[0x23]]), 283);
181 }
182
183 #[test]
184 fn page_fills_then_rejects() {
185 let mut pg = DataPage::new(1, 8, 1, HeaderStyle::Normal);
186 let mut n = 0;
187 while pg.try_push(&[0u8; 12]) {
188 n += 1;
189 }
190 assert_eq!(n, 284);
191 }
192}