1const FKP_SIZE: usize = 512;
6
7#[derive(Debug, Clone)]
9pub struct ChpxRun {
10 pub fc_start: u32,
11 pub fc_end: u32,
12 pub grpprl: Vec<u8>,
13}
14
15#[derive(Debug, Clone)]
17pub struct PapxRun {
18 pub fc_start: u32,
19 pub fc_end: u32,
20 pub istd: u16,
21 pub grpprl: Vec<u8>,
22}
23
24fn read_u32(bytes: &[u8], offset: usize) -> Option<u32> {
25 bytes
26 .get(offset..offset + 4)
27 .map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
28}
29
30pub fn parse_bin_table(plc: &[u8]) -> Vec<(u32, u32, u32)> {
33 if plc.len() < 4 || !(plc.len() - 4).is_multiple_of(8) {
34 return Vec::new();
35 }
36 let count = (plc.len() - 4) / 8;
37 let mut entries = Vec::with_capacity(count);
38 for index in 0..count {
39 let Some(fc_start) = read_u32(plc, index * 4) else { break };
40 let Some(fc_end) = read_u32(plc, (index + 1) * 4) else { break };
41 let Some(page) = read_u32(plc, (count + 1) * 4 + index * 4) else { break };
42 entries.push((fc_start, fc_end, page));
43 }
44 entries
45}
46
47fn fkp_page(word_stream: &[u8], page_number: u32) -> Option<&[u8]> {
48 let start = page_number as usize * FKP_SIZE;
49 word_stream.get(start..start + FKP_SIZE)
50}
51
52pub fn parse_chpx_fkp(word_stream: &[u8], page_number: u32) -> Vec<ChpxRun> {
54 let Some(page) = fkp_page(word_stream, page_number) else {
55 return Vec::new();
56 };
57 let crun = page[FKP_SIZE - 1] as usize;
58 if crun == 0 || (crun + 1) * 4 + crun > FKP_SIZE - 1 {
59 return Vec::new();
60 }
61 let mut runs = Vec::with_capacity(crun);
62 for index in 0..crun {
63 let Some(fc_start) = read_u32(page, index * 4) else { break };
64 let Some(fc_end) = read_u32(page, (index + 1) * 4) else { break };
65 let offset_byte = page[(crun + 1) * 4 + index] as usize;
66 let grpprl = if offset_byte == 0 {
67 Vec::new()
68 } else {
69 let chpx_offset = offset_byte * 2;
70 let cb = page.get(chpx_offset).copied().unwrap_or(0) as usize;
71 page.get(chpx_offset + 1..chpx_offset + 1 + cb)
72 .map(|slice| slice.to_vec())
73 .unwrap_or_default()
74 };
75 runs.push(ChpxRun {
76 fc_start,
77 fc_end,
78 grpprl,
79 });
80 }
81 runs
82}
83
84pub fn parse_papx_fkp(word_stream: &[u8], page_number: u32) -> Vec<PapxRun> {
88 const BX_SIZE: usize = 13;
89 let Some(page) = fkp_page(word_stream, page_number) else {
90 return Vec::new();
91 };
92 let crun = page[FKP_SIZE - 1] as usize;
93 if crun == 0 || (crun + 1) * 4 + crun * BX_SIZE > FKP_SIZE - 1 {
94 return Vec::new();
95 }
96 let mut runs = Vec::with_capacity(crun);
97 for index in 0..crun {
98 let Some(fc_start) = read_u32(page, index * 4) else { break };
99 let Some(fc_end) = read_u32(page, (index + 1) * 4) else { break };
100 let bx_offset = (crun + 1) * 4 + index * BX_SIZE;
101 let offset_byte = page[bx_offset] as usize;
102 let (istd, grpprl) = if offset_byte == 0 {
103 (0u16, Vec::new())
104 } else {
105 parse_papx_in_fkp(page, offset_byte * 2)
106 };
107 runs.push(PapxRun {
108 fc_start,
109 fc_end,
110 istd,
111 grpprl,
112 });
113 }
114 runs
115}
116
117fn parse_papx_in_fkp(page: &[u8], offset: usize) -> (u16, Vec<u8>) {
120 let Some(&cb) = page.get(offset) else {
121 return (0, Vec::new());
122 };
123 let (payload_start, payload_len) = if cb == 0 {
124 let Some(&cb2) = page.get(offset + 1) else {
125 return (0, Vec::new());
126 };
127 (offset + 2, cb2 as usize * 2)
128 } else {
129 (offset + 1, cb as usize * 2 - 1)
130 };
131 let Some(payload) = page.get(payload_start..payload_start + payload_len) else {
132 return (0, Vec::new());
133 };
134 if payload.len() < 2 {
135 return (0, Vec::new());
136 }
137 let istd = u16::from_le_bytes([payload[0], payload[1]]);
138 (istd, payload[2..].to_vec())
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn parses_chpx_fkp_page() {
147 let mut page = vec![0u8; FKP_SIZE];
148 page[FKP_SIZE - 1] = 2;
150 page[0..4].copy_from_slice(&100u32.to_le_bytes());
151 page[4..8].copy_from_slice(&110u32.to_le_bytes());
152 page[8..12].copy_from_slice(&120u32.to_le_bytes());
153 page[12] = 50;
155 page[13] = 0;
156 page[100] = 3; page[101..104].copy_from_slice(&[0x35, 0x08, 0x01]); let mut stream = vec![0u8; FKP_SIZE * 2];
160 stream[FKP_SIZE..].copy_from_slice(&page);
161 let runs = parse_chpx_fkp(&stream, 1);
162 assert_eq!(runs.len(), 2);
163 assert_eq!(runs[0].fc_start, 100);
164 assert_eq!(runs[0].grpprl, vec![0x35, 0x08, 0x01]);
165 assert!(runs[1].grpprl.is_empty());
166 }
167
168 #[test]
169 fn parses_papx_fkp_page() {
170 let mut page = vec![0u8; FKP_SIZE];
171 page[FKP_SIZE - 1] = 1;
172 page[0..4].copy_from_slice(&200u32.to_le_bytes());
173 page[4..8].copy_from_slice(&230u32.to_le_bytes());
174 let bx_offset = 8;
175 page[bx_offset] = 60; page[120] = 3;
178 page[121..123].copy_from_slice(&1u16.to_le_bytes()); page[123..126].copy_from_slice(&[0x05, 0x24, 0x01]); let mut stream = vec![0u8; FKP_SIZE * 2];
182 stream[FKP_SIZE..].copy_from_slice(&page);
183 let runs = parse_papx_fkp(&stream, 1);
184 assert_eq!(runs.len(), 1);
185 assert_eq!(runs[0].istd, 1);
186 assert_eq!(runs[0].grpprl, vec![0x05, 0x24, 0x01]);
187 }
188
189 #[test]
190 fn parses_papx_large_grpprl_escape() {
191 let mut page = vec![0u8; FKP_SIZE];
192 page[FKP_SIZE - 1] = 1;
193 page[0..4].copy_from_slice(&10u32.to_le_bytes());
194 page[4..8].copy_from_slice(&20u32.to_le_bytes());
195 page[8] = 60;
196 page[120] = 0;
198 page[121] = 3;
199 page[122..124].copy_from_slice(&5u16.to_le_bytes());
200 page[124..128].copy_from_slice(&[0x03, 0x24, 0x01, 0x00]);
201
202 let mut stream = vec![0u8; FKP_SIZE * 2];
203 stream[FKP_SIZE..].copy_from_slice(&page);
204 let runs = parse_papx_fkp(&stream, 1);
205 assert_eq!(runs[0].istd, 5);
206 assert_eq!(runs[0].grpprl.len(), 4);
207 }
208}