1use flashraw_core::bits::{BitPumpMsb, BitRead};
23use flashraw_core::{Error, Result};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct OlympusParams {
28 pub extra_bits: u32,
30 pub grad_shift: u32,
32 pub edge_pred: i32,
34 pub low_bits: u32,
36 pub smooth_shift: u32,
38 pub small_shift: u32,
40 pub run_switch: i32,
42 pub run_offset: i32,
44 pub run_threshold: i32,
45 pub base_offset: i32,
47 pub base_threshold: i32,
48 pub max_wbits: i32,
50 pub max_high: u32,
52 pub header_len: u32,
54}
55
56impl Default for OlympusParams {
57 fn default() -> Self {
58 OlympusParams {
59 extra_bits: 0,
60 grad_shift: 5,
61 edge_pred: 0,
62 low_bits: 2,
63 smooth_shift: 5,
64 small_shift: 4,
65 run_switch: 3,
66 run_offset: 2,
67 run_threshold: 3,
68 base_offset: 4,
69 base_threshold: 7,
70 max_wbits: 15,
71 max_high: 12,
72 header_len: 5,
73 }
74 }
75}
76
77impl OlympusParams {
78 pub fn from_tags(t: &[Option<u32>; 14]) -> Self {
80 let d = Self::default();
81 let g = |i: usize, def: u32| t[i].unwrap_or(def);
82 OlympusParams {
83 extra_bits: g(0, d.extra_bits),
84 grad_shift: g(1, d.grad_shift),
85 edge_pred: g(2, d.edge_pred as u32) as i32,
86 low_bits: g(3, d.low_bits),
87 smooth_shift: g(4, d.smooth_shift),
88 small_shift: g(5, d.small_shift),
89 run_switch: g(6, d.run_switch as u32) as i32,
90 run_offset: g(7, d.run_offset as u32) as i32,
91 run_threshold: g(8, d.run_threshold as u32) as i32,
92 base_offset: g(9, d.base_offset as u32) as i32,
93 base_threshold: g(10, d.base_threshold as u32) as i32,
94 max_wbits: g(11, d.max_wbits as u32) as i32,
95 max_high: g(12, d.max_high).max(1),
96 header_len: g(13, d.header_len),
97 }
98 }
99
100 fn validate(&self) -> Result<()> {
101 let ok = self.extra_bits <= 4
102 && self.grad_shift <= 16
103 && self.low_bits <= 2
104 && self.small_shift <= 31
105 && self.max_high <= 24
106 && (1..=8).contains(&self.header_len)
107 && (0..=15).contains(&self.max_wbits);
108 if ok { Ok(()) } else { Err(Error::unsupported_compression("ORF", format!("coder parameters {self:?}"))) }
109 }
110}
111
112pub fn stream_start(data: &[u8], p: &OlympusParams) -> Result<usize> {
114 let n = p.header_len as usize;
115 let hdr = data.get(..n + 2).ok_or_else(|| Error::corrupt("ORF: compressed data too short"))?;
116 let v = hdr[..n].iter().fold(0u64, |a, &b| a << 8 | b as u64);
117 if v != 1 || hdr[n] != 0 || hdr[n + 1] != 0 {
118 return Err(Error::corrupt("ORF: invalid compressed stream header"));
119 }
120 Ok(n + 2)
121}
122
123#[derive(Clone, Copy, Default)]
125struct Chan {
126 prev_code: u32,
128 run: i32,
130 smooth: i32,
132 west: i32,
134}
135
136pub fn decode(data: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) -> Result<()> {
140 decode_with(data, p, out, width, height, false)
141}
142
143pub fn decode_with(
145 data: &[u8],
146 p: &OlympusParams,
147 out: &mut [u16],
148 width: usize,
149 height: usize,
150 parallel: bool,
151) -> Result<()> {
152 p.validate()?;
153 if out.len() < width * height {
154 return Err(Error::InvalidArgument("ORF: output buffer too small".into()));
155 }
156 let start = stream_start(data, p)?;
157 let standard = OlympusParams { header_len: p.header_len, ..OlympusParams::default() };
158 if *p == standard {
159 decode_standard(&data[start..], out, width, height, parallel)
160 } else {
161 decode_generic(&data[start..], p, out, width, height)
162 }
163}
164
165fn decode_standard(stream: &[u8], out: &mut [u16], width: usize, height: usize, parallel: bool) -> Result<()> {
175 const BAND: usize = 32;
176 let mut bits = PosBits::new(stream);
177 let mut ch = [EntropyChan::default(); 2];
178 let mut entropy_band = |y0: usize, codes: &mut Vec<i32>| -> Result<()> {
179 let y1 = (y0 + BAND).min(height);
180 codes.clear();
181 codes.resize((y1 - y0) * width, 0);
182 for (i, row) in codes.chunks_exact_mut(width).enumerate() {
183 standard_entropy_row(&mut bits, &mut ch, row);
184 if bits.exhausted() {
185 return Err(Error::corrupt(format!("ORF: compressed data ends at row {}/{height}", y0 + i)));
186 }
187 }
188 Ok(())
189 };
190 if width == 0 || height == 0 {
191 return Ok(());
192 }
193 #[cfg(all(feature = "rayon", target_arch = "wasm32"))]
197 if parallel && height > BAND {
198 let mut cur = Vec::new();
199 let mut prev: Option<(usize, Vec<i32>)> = None;
200 for y0 in (0..height).step_by(BAND) {
201 let (res, done) = rayon::join(
202 || entropy_band(y0, &mut cur).map(|_| ()),
203 || {
204 if let Some((py, codes)) = prev.take() {
205 predict_band(out, &codes, py, width);
206 Some(codes)
207 } else {
208 None
209 }
210 },
211 );
212 res?;
213 let recycled = done.unwrap_or_default();
214 prev = Some((y0, std::mem::replace(&mut cur, recycled)));
215 }
216 if let Some((py, codes)) = prev {
217 predict_band(out, &codes, py, width);
218 }
219 return Ok(());
220 }
221 #[cfg(all(feature = "rayon", not(target_arch = "wasm32")))]
223 if parallel && height > BAND {
224 use std::sync::mpsc;
225 return std::thread::scope(|s| {
226 let (tx, rx) = mpsc::sync_channel::<(usize, Vec<i32>)>(4);
227 let (back_tx, back_rx) = mpsc::channel::<Vec<i32>>();
228 let consumer = s.spawn(move || {
229 for (y0, codes) in rx {
230 predict_band(out, &codes, y0, width);
231 let _ = back_tx.send(codes);
232 }
233 });
234 let mut result = Ok(());
235 for y0 in (0..height).step_by(BAND) {
236 let mut buf = back_rx.try_recv().unwrap_or_default();
237 if let Err(e) = entropy_band(y0, &mut buf) {
238 result = Err(e);
239 break;
240 }
241 if tx.send((y0, buf)).is_err() {
242 break;
243 }
244 }
245 drop(tx);
246 let _ = consumer.join();
247 result
248 });
249 }
250 let _ = parallel;
251 let mut buf = Vec::new();
252 for y0 in (0..height).step_by(BAND) {
253 entropy_band(y0, &mut buf)?;
254 predict_band(out, &buf, y0, width);
255 }
256 Ok(())
257}
258
259struct PosBits<'a> {
263 data: &'a [u8],
264 pos: usize,
265}
266
267impl<'a> PosBits<'a> {
268 fn new(data: &'a [u8]) -> Self {
269 PosBits { data, pos: 0 }
270 }
271
272 #[inline(always)]
274 fn peek32(&self) -> u32 {
275 let byte = self.pos >> 3;
276 let w = match self.data.get(byte..byte + 8) {
277 Some(b) => u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
278 None => self.peek_tail(byte),
279 };
280 ((w << (self.pos & 7)) >> 32) as u32
281 }
282
283 #[cold]
284 fn peek_tail(&self, byte: usize) -> u64 {
285 let mut b = [0u8; 8];
286 if let Some(t) = self.data.get(byte..) {
287 b[..t.len().min(8)].copy_from_slice(&t[..t.len().min(8)]);
288 }
289 u64::from_be_bytes(b)
290 }
291
292 #[inline(always)]
293 fn skip(&mut self, n: u32) {
294 self.pos += n as usize;
295 }
296
297 fn exhausted(&self) -> bool {
299 self.pos > (self.data.len() + 8) * 8
300 }
301}
302
303#[derive(Clone, Copy, Default)]
305struct EntropyChan {
306 prev_code: u32,
307 run: i32,
308 smooth: i32,
309}
310
311#[inline(always)]
313fn standard_entropy_row(bits: &mut PosBits, ch: &mut [EntropyChan; 2], codes: &mut [i32]) {
314 let width = codes.len();
315 let [c0, c1] = ch;
316 codes[0] = standard_code::<true>(bits, c0);
317 if width > 1 {
318 codes[1] = standard_code::<true>(bits, c1);
319 }
320 let mut col = 2;
321 while col + 1 < width {
322 codes[col] = standard_code::<false>(bits, c0);
323 codes[col + 1] = standard_code::<false>(bits, c1);
324 col += 2;
325 }
326 if col < width {
327 codes[col] = standard_code::<false>(bits, c0);
328 }
329}
330
331#[inline(always)]
333fn standard_code<const EDGE: bool>(bits: &mut PosBits, c: &mut EntropyChan) -> i32 {
334 let wbits = if EDGE {
335 c.run = 0;
336 4
337 } else {
338 let run = if c.prev_code <= 16 { c.run + 1 } else { 0 };
339 c.run = run;
340 let hb = 32 - (c.prev_code & 0xffff).leading_zeros();
341 let w = if run >= 3 { hb.max(2) } else { hb.saturating_sub(2).max(4) };
342 w.min(15)
343 };
344 let v = bits.peek32();
345 let sign = v >> 31;
346 let low2 = ((v >> 29) & 3) as i32;
347 let rest = v << 3;
348 let (high, low, used) = if rest >> 20 != 0 {
349 let z = rest.leading_zeros();
350 let after = rest << (z + 1);
351 (z, after >> (32 - wbits), 4 + z + wbits)
352 } else {
353 let after = rest << 12;
355 let hn = 15 - wbits;
356 let high = if hn == 0 { 0 } else { after >> (32 - hn) };
357 let after2 = (after << hn) << 1;
358 (high, after2 >> (32 - wbits), 31)
359 };
360 bits.skip(used);
361 let glc = low | (high << wbits);
362 c.prev_code = glc;
363 let code = (glc ^ sign.wrapping_neg()) as i32;
364 let s = if EDGE { 0 } else { c.smooth };
365 let code = low2 + 4 * (s + code);
366 let vbits = ((code >> 1) & !1) + s + (code >> 2);
367 c.smooth = vbits >> 5;
368 code
369}
370
371fn predict_band(out: &mut [u16], codes: &[i32], y0: usize, width: usize) {
373 let rows = codes.len() / width;
374 let mut i = 0;
375 while i < rows {
376 let y = y0 + i;
377 if i + 1 < rows && y >= 2 {
378 let (above, cur) = out.split_at_mut(y * width);
380 let (r0, r1) = cur[..2 * width].split_at_mut(width);
381 let up0 = &above[(y - 2) * width..(y - 1) * width];
382 let up1 = &above[(y - 1) * width..y * width];
383 predict_two_rows(
384 &codes[i * width..(i + 1) * width],
385 &codes[(i + 1) * width..(i + 2) * width],
386 r0,
387 r1,
388 up0,
389 up1,
390 );
391 i += 2;
392 } else {
393 let (above, cur) = out.split_at_mut(y * width);
394 let up = if y >= 2 { &above[(y - 2) * width..(y - 1) * width] } else { &[][..] };
395 predict_row(&codes[i * width..(i + 1) * width], &mut cur[..width], up);
396 i += 1;
397 }
398 }
399}
400
401fn predict_row(codes: &[i32], cur: &mut [u16], up: &[u16]) {
403 let width = cur.len();
404 let mut west = [0i32; 2];
405 for col in 0..width {
406 let (n, nw) = match (up.get(col), col >= 2) {
407 (Some(&n), true) => (n as i32, up[col - 2] as i32),
408 (Some(&n), false) => (n as i32, 0),
409 (None, _) => (0, 0),
410 };
411 let w = if col >= 2 { west[col & 1] } else { 0 };
412 let v = codes[col].wrapping_add(predict(w, n, nw, 32));
413 west[col & 1] = v;
414 cur[col] = v as u16;
415 }
416}
417
418#[inline(never)]
420fn predict_two_rows(c0: &[i32], c1: &[i32], r0: &mut [u16], r1: &mut [u16], u0: &[u16], u1: &[u16]) {
421 let width = r0.len();
422 if width < 4 {
423 predict_row(c0, r0, u0);
424 predict_row(c1, r1, u1);
425 return;
426 }
427 let mut w = [0i32; 4];
429 for k in 0..2 {
430 let a = c0[k].wrapping_add(predict(0, u0[k] as i32, 0, 32));
431 let b = c1[k].wrapping_add(predict(0, u1[k] as i32, 0, 32));
432 w[k] = a;
433 w[2 + k] = b;
434 r0[k] = a as u16;
435 r1[k] = b as u16;
436 }
437 let mut col = 2;
438 while col + 1 < width {
439 let a0 = c0[col].wrapping_add(predict(w[0], u0[col] as i32, u0[col - 2] as i32, 32));
440 let a1 = c0[col + 1].wrapping_add(predict(w[1], u0[col + 1] as i32, u0[col - 1] as i32, 32));
441 let b0 = c1[col].wrapping_add(predict(w[2], u1[col] as i32, u1[col - 2] as i32, 32));
442 let b1 = c1[col + 1].wrapping_add(predict(w[3], u1[col + 1] as i32, u1[col - 1] as i32, 32));
443 w = [a0, a1, b0, b1];
444 r0[col] = a0 as u16;
445 r0[col + 1] = a1 as u16;
446 r1[col] = b0 as u16;
447 r1[col + 1] = b1 as u16;
448 col += 2;
449 }
450 if col < width {
451 let a0 = c0[col].wrapping_add(predict(w[0], u0[col] as i32, u0[col - 2] as i32, 32));
452 let b0 = c1[col].wrapping_add(predict(w[2], u1[col] as i32, u1[col - 2] as i32, 32));
453 r0[col] = a0 as u16;
454 r1[col] = b0 as u16;
455 }
456}
457
458#[inline(always)]
463fn predict(w: i32, n: i32, nw: i32, grad: i32) -> i32 {
464 let a = (w < n) | (nw < w);
465 let b = (nw <= n) & (w >= n);
466 let c = (nw < n) | (w >= n);
467 let d = (nw > w) | (w >= n);
468 let smooth = ((n - nw).abs() <= grad) & ((w - nw).abs() <= grad);
469 let g = if smooth { (n + w) >> 1 } else { n + w - nw };
470 let use_w = a & (b | !c);
471 let use_g = a & !b & c & d;
472 let r = if use_g { g } else { n };
473 if use_w { w } else { r }
474}
475
476#[cfg(test)]
478fn predict_reference(w: i32, n: i32, nw: i32, grad: i32) -> i32 {
479 let mut n = n;
480 if w < n || nw < w {
481 if nw <= n && w >= n {
482 n = w;
483 } else if nw < n || w >= n {
484 if nw > w || w >= n {
485 if (n - nw).abs() > grad {
486 n += w - nw;
487 } else if (w - nw).abs() <= grad {
488 n = (n + w) >> 1;
489 } else {
490 n = n + w - nw;
491 }
492 }
493 } else {
494 n = w;
495 }
496 }
497 n
498}
499
500fn decode_generic(stream: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) -> Result<()> {
502 let mut bits = BitPumpMsb::new(stream);
503 let small_limit = 1u32 << (p.small_shift & 31);
504 let grad = 1i32 << p.grad_shift;
505 let extra = p.extra_bits;
506 let mut ch = [Chan::default(); 2];
507 for row in 0..height {
508 let (above, cur) = out.split_at_mut(row * width);
509 let cur = &mut cur[..width];
510 let up = if row >= 2 { Some(&above[(row - 2) * width..(row - 1) * width]) } else { None };
511 for col in 0..width {
512 let c = &mut ch[col & 1];
513 let (wbits, run) = if col > 1 {
515 let run = if small_limit >= c.prev_code { c.run + 1 } else { 0 };
516 let hb = 32 - (c.prev_code & 0xffff).leading_zeros() as i32;
517 let (t1, t2) = if run >= p.run_switch {
518 (p.run_threshold, p.run_offset)
519 } else {
520 (p.base_threshold, p.base_offset)
521 };
522 (t2 + (hb - t1).max(-1) + 1, run)
523 } else {
524 (p.base_offset, 0)
525 };
526 let wbits = wbits.min(p.max_wbits);
527 if !(0..16).contains(&wbits) {
528 return Err(Error::corrupt("ORF: invalid code width"));
529 }
530 let wbits = wbits as u32;
531 c.run = run;
532
533 bits.fill();
535 let sign = bits.peek_no_fill(1);
536 bits.skip_no_fill(1);
537 let lowbits = if p.low_bits > 0 {
538 let v = bits.peek_no_fill(p.low_bits);
539 bits.skip_no_fill(p.low_bits);
540 v
541 } else {
542 0
543 };
544 let extrabits = if extra > 0 {
545 let v = bits.peek_no_fill(extra);
546 bits.skip_no_fill(extra);
547 v
548 } else {
549 0
550 };
551 let window = bits.peek_no_fill(p.max_high);
552 let high = if window != 0 {
553 let zeros = window.leading_zeros() - (32 - p.max_high);
554 bits.skip_no_fill(zeros + 1);
555 zeros
556 } else {
557 bits.skip_no_fill(p.max_high);
559 bits.fill();
560 let h = if wbits != 15 {
561 let n = 15 - wbits;
562 let v = bits.peek_no_fill(n);
563 bits.skip_no_fill(n);
564 v
565 } else {
566 0
567 };
568 bits.skip_no_fill(1);
569 h
570 };
571 let low = if wbits > 0 {
572 bits.fill();
573 let v = bits.peek_no_fill(wbits);
574 bits.skip_no_fill(wbits);
575 v
576 } else {
577 0
578 };
579 let glc = low | high.wrapping_shl(wbits);
580 let mut code = (glc ^ (sign.wrapping_neg())) as i32;
581 c.prev_code = glc;
582
583 let v = if col < 2 { 0 } else { c.smooth };
585 let vbits = match p.low_bits {
586 0 => 0,
587 1 => {
588 code = lowbits as i32 + 2 * (v + code);
589 v + (code >> 1)
590 }
591 _ => {
592 code = lowbits as i32 + 4 * (v + code);
593 ((code >> 1) & !1) + v + (code >> 2)
594 }
595 };
596 c.smooth = if p.smooth_shift >= 15 { 0 } else { vbits >> p.smooth_shift };
597
598 let w = if col < 2 { p.edge_pred } else { c.west };
600 let (mut n, nw) = match up {
601 Some(u) => ((u[col] >> extra) as i32, if col < 2 { p.edge_pred } else { (u[col - 2] >> extra) as i32 }),
602 None => (p.edge_pred, p.edge_pred),
603 };
604 if w < n || nw < w {
605 if nw <= n && w >= n {
606 n = w;
607 } else if nw < n || w >= n {
608 if nw > w || w >= n {
609 if (n - nw).abs() > grad {
610 n += w - nw;
611 } else if (w - nw).abs() <= grad {
612 n = (n + w) >> 1;
613 } else {
614 n = n + w - nw;
615 }
616 }
617 } else {
618 n = w;
619 }
620 }
621 let val = code.wrapping_add(n);
622 c.west = val;
623 cur[col] = (extrabits as i32).wrapping_add(val.wrapping_shl(extra)) as u16;
624 }
625 if bits.exhausted() {
626 return Err(Error::corrupt(format!("ORF: compressed data ends at row {row}/{height}")));
627 }
628 }
629 Ok(())
630}
631
632#[cfg(test)]
635pub(crate) fn decode_reference(data: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) {
636 let start = stream_start(data, p).unwrap();
637 let mut bitpos = start * 8;
638 let mut bit = || {
639 let b = data.get(bitpos / 8).map(|&b| (b >> (7 - bitpos % 8)) & 1).unwrap_or(0) as u32;
640 bitpos += 1;
641 b
642 };
643 let mut ctx = [0i32; 4];
644 let (mut lpred, mut pred1, mut glc, mut gcode_prev) = (0i32, 0i32, 0u32, 0u32);
645 for row in 0..height {
646 for col in 0..width {
647 let pcode = gcode_prev;
648 gcode_prev = glc;
649 let psel = if col > 1 && (1u32 << p.small_shift) >= pcode { ctx[3] + 1 } else { 0 };
650 ctx[3] = ctx[2];
651 ctx[2] = psel;
652 let mut wbits = if col > 1 {
653 let hb = 32 - (pcode & 0xffff).leading_zeros() as i32;
654 let (t1, t2) = if psel >= p.run_switch {
655 (p.run_threshold, p.run_offset)
656 } else {
657 (p.base_threshold, p.base_offset)
658 };
659 t2 + (hb - t1).max(-1) + 1
660 } else {
661 p.base_offset
662 };
663 wbits = wbits.min(p.max_wbits);
664 let wbits = wbits as u32;
665 let mut rd = |n: u32| (0..n).fold(0u32, |a, _| a << 1 | bit());
666 let sign = rd(1);
667 let t643 = rd(p.low_bits);
668 let t640 = rd(p.extra_bits);
669 let mut high = 0;
670 loop {
671 if rd(1) == 1 {
672 break;
673 }
674 high += 1;
675 if high == p.max_high {
676 high = if wbits != 15 { rd(15 - wbits) } else { 0 };
677 rd(1);
678 break;
679 }
680 }
681 let low = rd(wbits);
682 glc = low | (high << wbits);
683 let mut gcode = (glc ^ sign.wrapping_neg()) as i32;
684 let vbits = if p.low_bits == 2 {
685 let v = if col < 2 { 0 } else { pred1 };
686 gcode = t643 as i32 + 4 * (v + gcode);
687 ((gcode >> 1) & !1) + v + (gcode >> 2)
688 } else {
689 0
690 };
691 pred1 = ctx[0];
692 ctx[0] = vbits >> p.smooth_shift;
693 let w = if col < 2 { p.edge_pred } else { ctx[1] };
694 let mut n = if row < 2 { p.edge_pred } else { (out[(row - 2) * width + col] >> p.extra_bits) as i32 };
695 let nw = if row < 2 || col < 2 {
696 p.edge_pred
697 } else {
698 (out[(row - 2) * width + col - 2] >> p.extra_bits) as i32
699 };
700 ctx[1] = lpred;
701 if w < n || nw < w {
702 if nw <= n && w >= n {
703 n = w;
704 } else if nw < n || w >= n {
705 if nw > w || w >= n {
706 if (n - nw).abs() > 1 << p.grad_shift {
707 n += w - nw;
708 } else if (w - nw).abs() <= 1 << p.grad_shift {
709 n = (n + w) >> 1;
710 } else {
711 n = n + w - nw;
712 }
713 }
714 } else {
715 n = w;
716 }
717 }
718 lpred = gcode + n;
719 out[row * width + col] = (t640 as i32 + ((gcode + n) << p.extra_bits)) as u16;
720 }
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727
728 fn stream(seed: u32, len: usize, header_len: usize) -> Vec<u8> {
729 let mut v = vec![0u8; header_len - 1];
730 v.push(1);
731 v.extend([0, 0]);
732 let mut x = seed;
733 v.extend((0..len).map(|i| {
735 x = x.wrapping_mul(1664525).wrapping_add(1013904223);
736 let b = (x >> 24) as u8;
737 if i % 7 == 0 { b & 0x0f } else { b | 0x11 }
738 }));
739 v
740 }
741
742 #[test]
743 fn optimised_matches_reference_on_random_streams() {
744 for seed in 1..12 {
745 let p = OlympusParams::default();
746 let data = stream(seed, 40_000, 5);
747 let (w, h) = (66, 40);
748 let mut a = vec![0u16; w * h];
749 let mut b = vec![0u16; w * h];
750 let _ = decode(&data, &p, &mut a, w, h);
751 decode_reference(&data, &p, &mut b, w, h);
752 assert_eq!(a, b, "seed {seed}");
753 let mut c = vec![0u16; w * h];
755 let _ = decode_with(&data, &p, &mut c, w, h, true);
756 assert_eq!(c, b, "parallel seed {seed}");
757 }
758 }
759
760 #[test]
761 fn predictor_matches_specification() {
762 let vals = [-70, -33, -32, -31, -1, 0, 1, 2, 31, 32, 33, 64, 100, 4095];
763 for &w in &vals {
764 for &n in &vals {
765 for &nw in &vals {
766 assert_eq!(predict(w, n, nw, 32), predict_reference(w, n, nw, 32), "{w} {n} {nw}");
767 }
768 }
769 }
770 }
771
772 #[test]
773 fn generic_path_matches_reference() {
774 let p = OlympusParams { grad_shift: 4, ..OlympusParams::default() };
775 for seed in 1..6 {
776 let data = stream(seed, 40_000, 5);
777 let (w, h) = (34, 30);
778 let mut a = vec![0u16; w * h];
779 let mut b = vec![0u16; w * h];
780 let _ = decode(&data, &p, &mut a, w, h);
781 decode_reference(&data, &p, &mut b, w, h);
782 assert_eq!(a, b, "seed {seed}");
783 }
784 }
785
786 #[test]
787 fn header_and_truncation_checks() {
788 let p = OlympusParams::default();
789 let mut out = vec![0u16; 64 * 64];
790 assert!(decode(&[0, 0, 0, 0, 2, 0, 0, 0xff], &p, &mut out, 64, 64).is_err());
791 let data = stream(3, 200, 5);
792 assert!(decode(&data, &p, &mut out, 64, 64).is_err());
793 let bad = OlympusParams { max_wbits: 40, ..p };
794 assert!(decode(&stream(3, 100_000, 5), &bad, &mut out, 64, 64).is_err());
795 }
796}