Skip to main content

flashraw_codecs/
sony_arw6.rs

1//! Sony ARW "compressed" format 6 (ILCE-7M5 "Compressed"/"Compressed HQ").
2//!
3//! The raw strip holds one or more tile streams. Each stream is a 3-level 5/3
4//! integer wavelet pyramid of three planes at half resolution — the green average
5//! of each 2x2 quad and red/blue residuals against it — plus a final level that
6//! restores the two greens of every quad. Coefficients are coded in packets; every
7//! packet record (a band row, or a few) is an independent MSB-first bit stream of
8//! groups of four coefficients with an adaptive bit width, zero runs and sign bits.
9//! Decoded 12-bit codes map to 16-bit linear values through a fixed curve
10//! ([`crate::sony_arw6_curve::LLVC3_CURVE`]).
11//!
12//! Layout of a stream (all big-endian): `"A000"`/`"0000"` magic; width at 0x08,
13//! half height at 0x0A, format words at 0x0C/0x0E; five 24-bit group sizes at
14//! 0x10; per-group packet directories from 0x30; packets from 0x80.
15//!
16//! Tiles are independent and decode in parallel; within a tile the packet records
17//! decode in parallel and the three planes synthesise in parallel.
18
19use crate::sony_arw6_curve::LLVC3_CURVE;
20use flashraw_core::{Error, Result, par};
21
22const STREAM_OFFSET: usize = 0x200;
23const BIAS: i32 = 2048;
24const MAX_PLANE_SAMPLES: usize = 128 * 1024 * 1024;
25
26fn corrupt(what: &str) -> Error {
27    Error::corrupt(format!("ARW6: {what}"))
28}
29
30fn require(cond: bool, what: &str) -> Result<()> {
31    if cond { Ok(()) } else { Err(corrupt(what)) }
32}
33
34fn be16(d: &[u8], p: usize) -> Result<u32> {
35    d.get(p..p + 2).map(|b| u16::from_be_bytes([b[0], b[1]]) as u32).ok_or_else(|| corrupt("truncated header"))
36}
37
38fn be32(d: &[u8], p: usize) -> Result<u32> {
39    d.get(p..p + 4).map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]])).ok_or_else(|| corrupt("truncated header"))
40}
41
42fn le32(d: &[u8], p: usize) -> Result<u32> {
43    d.get(p..p + 4).map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]])).ok_or_else(|| corrupt("truncated header"))
44}
45
46/// A 2-D plane of signed coefficients.
47#[derive(Clone, Default)]
48struct Plane {
49    rows: usize,
50    cols: usize,
51    data: Vec<i32>,
52}
53
54impl Plane {
55    fn new(rows: usize, cols: usize) -> Result<Self> {
56        let n = rows.checked_mul(cols).filter(|&n| n <= MAX_PLANE_SAMPLES).ok_or_else(|| corrupt("plane too large"))?;
57        Ok(Plane { rows, cols, data: vec![0; n] })
58    }
59    #[inline(always)]
60    fn at(&self, r: usize, c: usize) -> i32 {
61        self.data[r * self.cols + c]
62    }
63    #[inline(always)]
64    fn set(&mut self, r: usize, c: usize, v: i32) {
65        self.data[r * self.cols + c] = v;
66    }
67    fn row(&self, r: usize) -> &[i32] {
68        &self.data[r * self.cols..(r + 1) * self.cols]
69    }
70    fn row_mut(&mut self, r: usize) -> &mut [i32] {
71        &mut self.data[r * self.cols..(r + 1) * self.cols]
72    }
73    /// First `rows` rows.
74    fn head(&self, rows: usize) -> Result<Plane> {
75        require(rows <= self.rows, "plane row count")?;
76        Ok(Plane { rows, cols: self.cols, data: self.data[..rows * self.cols].to_vec() })
77    }
78}
79
80/// Arithmetic shift = floor division by a power of two.
81#[inline(always)]
82fn fshr(x: i32, bits: u32) -> i32 {
83    x >> bits
84}
85
86/// Sign-extend the low 16 bits.
87#[inline(always)]
88fn sign16(x: i32) -> i32 {
89    x as u16 as i16 as i32
90}
91
92/// Boundary detail reconstruction used at stripe edges.
93#[inline(always)]
94fn edge_detail(x: i32, odd_mode: bool) -> i32 {
95    let half = x.signum();
96    let odd = x % 2 != 0;
97    let use_half = if odd_mode { odd } else { x != 0 && !odd };
98    x.wrapping_mul(2).wrapping_add(if use_half { half } else { 0 })
99}
100
101// ---------------------------------------------------------------------------
102// Bit readers
103
104/// MSB-first reader for packet headers (exact-bound checked).
105struct HeaderBits<'a> {
106    data: &'a [u8],
107    pos: usize,
108}
109
110impl HeaderBits<'_> {
111    fn read(&mut self, n: u32) -> Result<u32> {
112        require(self.pos + n as usize <= self.data.len() * 8, "packet header beyond data")?;
113        let mut out = 0u32;
114        for _ in 0..n {
115            let b = (self.data[self.pos >> 3] >> (7 - (self.pos & 7))) & 1;
116            out = (out << 1) | b as u32;
117            self.pos += 1;
118        }
119        Ok(out)
120    }
121}
122
123/// Coefficient bit stream of one packet record: MSB-first, with an allowance of
124/// whole 64-bit word pairs; reading past it yields zeros and sets `status` (the
125/// decoder then zero-fills, as the format intends). Bytes past the end of the
126/// packet read as zero.
127struct CoeffBits<'a> {
128    data: &'a [u8],
129    /// Next byte to load into the cache.
130    next: usize,
131    /// Valid bits, left-aligned.
132    cache: u64,
133    nbits: u32,
134    /// Bits still within the allowance.
135    remaining: u64,
136    status: u8,
137}
138
139impl<'a> CoeffBits<'a> {
140    fn new(packet: &'a [u8], payload_offset: usize, byte_length: usize) -> Self {
141        let words = ((byte_length.div_ceil(8)) + 1) & !1;
142        CoeffBits { data: packet, next: payload_offset, cache: 0, nbits: 0, remaining: words as u64 * 64, status: 0 }
143    }
144
145    /// Top up the cache with whole bytes (to at least 56 bits).
146    #[inline(always)]
147    fn refill(&mut self) {
148        if self.nbits > 55 {
149            return;
150        }
151        let take = (63 - self.nbits) >> 3;
152        let w = match self.data.get(self.next..self.next + 8) {
153            Some(b) => u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
154            None => {
155                let mut b = [0u8; 8];
156                if let Some(t) = self.data.get(self.next..) {
157                    let k = t.len().min(8);
158                    b[..k].copy_from_slice(&t[..k]);
159                }
160                u64::from_be_bytes(b)
161            }
162        };
163        // Only the `take` whole bytes that fit are added.
164        let bits = take * 8;
165        self.cache |= (w >> (64 - bits)) << (64 - bits - self.nbits);
166        self.next += take as usize;
167        self.nbits += bits;
168    }
169
170    #[inline(always)]
171    fn consume(&mut self, n: u32) {
172        self.cache <<= n;
173        self.nbits -= n;
174        self.remaining -= n as u64;
175    }
176
177    /// Up to 32 bits.
178    #[inline(always)]
179    fn read(&mut self, n: u32) -> u32 {
180        if n == 0 {
181            return 0;
182        }
183        if n as u64 > self.remaining {
184            return self.read_past_limit(n);
185        }
186        if self.nbits < n {
187            self.refill();
188        }
189        let v = (self.cache >> (64 - n)) as u32;
190        self.consume(n);
191        v
192    }
193
194    #[cold]
195    fn read_past_limit(&mut self, n: u32) -> u32 {
196        // Bits up to the limit are real; the rest read as zero.
197        let avail = self.remaining as u32;
198        let head = if avail > 0 { self.read(avail) } else { 0 };
199        self.status = 2;
200        self.remaining = 0;
201        // A full 32-bit read at the limit shifts by 32 (head is then 0).
202        head.checked_shl(n - avail).unwrap_or(0)
203    }
204
205    #[inline(always)]
206    fn bit(&mut self) -> u32 {
207        self.read(1)
208    }
209
210    /// Count zeros up to and including the terminating one (returns zeros + 1).
211    fn unary_plus_one(&mut self) -> i32 {
212        let mut zeros = 0i32;
213        loop {
214            if self.status != 0 {
215                return zeros + 1;
216            }
217            if self.remaining == 0 {
218                // The next bit lies past the allowance.
219                self.status = 2;
220                return zeros + 1;
221            }
222            self.refill();
223            let avail = (self.nbits as u64).min(self.remaining) as u32;
224            let lz = self.cache.leading_zeros();
225            if lz < avail {
226                self.consume(lz + 1);
227                return zeros + lz as i32 + 1;
228            }
229            self.consume(avail);
230            zeros += avail as i32;
231        }
232    }
233
234    /// Position of the first one bit within the next `k` bits (consuming it), or
235    /// `None` after consuming `k` zero bits.
236    #[inline(always)]
237    fn leading_one_within(&mut self, k: u32) -> Option<u32> {
238        if k as u64 <= self.remaining && k <= 32 {
239            self.refill();
240            let lz = self.cache.leading_zeros();
241            if lz < k {
242                self.consume(lz + 1);
243                return Some(lz);
244            }
245            self.consume(k);
246            return None;
247        }
248        (0..k).find(|_| self.bit() != 0)
249    }
250}
251
252/// Adapt the coefficient bit width: `0` keep, `10` grow by a unary count, `11`
253/// shrink by the position of the next one bit (or to zero).
254#[inline(always)]
255fn update_width(br: &mut CoeffBits, w: i32) -> i32 {
256    if br.bit() == 0 || br.status != 0 {
257        return w;
258    }
259    if br.bit() == 0 || br.status != 0 {
260        return w + br.unary_plus_one();
261    }
262    match br.leading_one_within((w - 1).max(0) as u32) {
263        Some(z) => w - (z as i32 + 1),
264        None => 0,
265    }
266}
267
268/// Length of a run of all-zero groups (Elias-gamma-like, capped at `remaining`).
269fn zero_run(br: &mut CoeffBits, remaining: i32) -> i32 {
270    if remaining <= 1 {
271        return remaining;
272    }
273    let max_prefix = 32 - ((remaining - 1) as u32).leading_zeros() as i32;
274    let mut zeros = 0;
275    while zeros < max_prefix {
276        if br.bit() != 0 {
277            break;
278        }
279        if br.status != 0 {
280            return remaining;
281        }
282        zeros += 1;
283    }
284    if zeros >= max_prefix {
285        return remaining;
286    }
287    let base = 1 << zeros;
288    if base >= remaining {
289        return remaining;
290    }
291    let extra = if zeros > 0 { br.read(zeros as u32) as i32 } else { 0 };
292    (base + extra).min(remaining)
293}
294
295/// Decode one band row of `groups` four-coefficient groups into `dst`.
296fn decode_component(br: &mut CoeffBits, groups: usize, shift: u32, dst: &mut [i32]) {
297    let row_width = dst.len();
298    let mut width = update_width(br, 0);
299    let mut gi = 0usize;
300    let mut out = 0usize;
301    while gi < groups {
302        if br.status != 0 || width > 0x13 {
303            if br.status == 0 {
304                br.status = 1;
305            }
306            dst[out..].fill(0);
307            return;
308        }
309        if width == 0 {
310            let run = zero_run(br, (groups - gi) as i32) as usize;
311            let n = (run * 4).min(row_width - out);
312            dst[out..out + n].fill(0);
313            out += n;
314            gi += run;
315            if gi >= groups {
316                break;
317            }
318            width = br.unary_plus_one();
319            continue;
320        }
321        let mut vals = [0i32; 4];
322        for v in vals.iter_mut() {
323            let x = br.read(width as u32);
324            *v = if shift > 0 && x > 0 {
325                (((2 * x as u64 + 1) << (shift - 1)) - (x as u64 & 1)) as i32
326            } else {
327                x as i32
328            };
329        }
330        let next = if gi + 1 < groups { update_width(br, width) } else { width };
331        for v in vals.iter_mut() {
332            if *v > 0 {
333                let s = br.bit() as i32;
334                *v = v.wrapping_sub(2i32.wrapping_mul(*v).wrapping_mul(s));
335            }
336        }
337        for v in vals {
338            if out < row_width {
339                dst[out] = v;
340                out += 1;
341            }
342        }
343        width = next;
344        gi += 1;
345    }
346    dst[out..].fill(0);
347}
348
349// ---------------------------------------------------------------------------
350// Stream structure
351
352#[derive(Debug, Clone, Copy)]
353struct DirEntry {
354    group: usize,
355    index: usize,
356    start: usize,
357    length: usize,
358}
359
360/// A tile stream inside the strip.
361#[derive(Debug, Clone, Copy)]
362pub struct TileStream {
363    pub offset: usize,
364    pub length: usize,
365    pub x: usize,
366    pub y: usize,
367    pub width: usize,
368    pub height: usize,
369}
370
371/// `(coded width, half height)` if `stream` starts with a valid stream header.
372fn parse_stream_header(stream: &[u8]) -> Option<(usize, usize)> {
373    if stream.len() < 0x80 || !(stream.starts_with(b"A000") || stream.starts_with(b"0000")) {
374        return None;
375    }
376    let wc = be16(stream, 0x0c).ok()?;
377    let we = be16(stream, 0x0e).ok()?;
378    if (wc >> 4) & 0x3f != 16 || we >> 13 != 3 || (we >> 10) & 3 != 3 {
379        return None;
380    }
381    let w = be16(stream, 8).ok()? as usize;
382    let hh = be16(stream, 0x0a).ok()? as usize;
383    if w == 0 || hh == 0 || w % 16 != 0 {
384        return None;
385    }
386    Some((w, hh))
387}
388
389/// Five 24-bit group sizes (in 16-byte units) starting at `p`.
390fn group_sizes(stream: &[u8], p: usize) -> Result<[usize; 5]> {
391    let mut g = [0usize; 5];
392    for (i, v) in g.iter_mut().enumerate() {
393        *v = ((be32(stream, p + 3 * i)? >> 4) & 0x0fff_fff0) as usize;
394    }
395    Ok(g)
396}
397
398fn stream_length(stream: &[u8]) -> Result<usize> {
399    require(stream.len() >= 0x80, "stream too short")?;
400    let payload: usize = group_sizes(stream, 0x10)?.iter().sum();
401    require(payload <= stream.len() - 0x80, "stream payload beyond strip")?;
402    Ok(0x80 + payload)
403}
404
405/// Locate the tile streams of a strip covering a `full_w x full_h` frame.
406pub fn find_streams(strip: &[u8], full_w: usize, full_h: usize) -> Vec<TileStream> {
407    let mut streams = Vec::new();
408    if full_w == 0 || full_h == 0 || strip.len() < STREAM_OFFSET + 0x80 {
409        return streams;
410    }
411    let count = le32(strip, 0).unwrap_or(0) as usize;
412    if (1..=16).contains(&count) {
413        'tiles: for index in 0..count {
414            let entry = 0x08 + index * 0x18;
415            let fields: Option<Vec<u32>> = (0..6).map(|k| le32(strip, entry + 4 * k).ok()).collect();
416            let Some(f) = fields else {
417                streams.clear();
418                break;
419            };
420            let (table_offset, tx, ty, tw, th) = (f[0] as usize, f[2] as usize, f[3] as usize, f[4], f[5]);
421            let start = if table_offset != 0 { table_offset } else { STREAM_OFFSET };
422            if start > strip.len() - 0x80 {
423                streams.clear();
424                break;
425            }
426            let end = (strip.len() - 0x80).min(start.saturating_add(0x1000));
427            let mut cand = start;
428            while cand <= end {
429                if let Some((w, hh)) = parse_stream_header(&strip[cand..]) {
430                    if let Ok(len) = stream_length(&strip[cand..]) {
431                        let tile_w = if tw != 0 { tw as usize } else { w };
432                        let tile_h = if th != 0 { th as usize } else { hh * 2 };
433                        if len > 0x80
434                            && cand + len <= strip.len()
435                            && tile_w == w
436                            && tile_h == hh * 2
437                            && tx + tile_w <= full_w
438                            && ty + tile_h <= full_h
439                        {
440                            streams.push(TileStream {
441                                offset: cand,
442                                length: len,
443                                x: tx,
444                                y: ty,
445                                width: w,
446                                height: hh * 2,
447                            });
448                            continue 'tiles;
449                        }
450                    }
451                }
452                cand += 0x10;
453            }
454            streams.clear();
455            break;
456        }
457        if streams.len() == count {
458            return streams;
459        }
460        streams.clear();
461    }
462    if let Some((w, hh)) = parse_stream_header(&strip[STREAM_OFFSET..]) {
463        if let Ok(len) = stream_length(&strip[STREAM_OFFSET..]) {
464            if w == full_w && hh * 2 == full_h {
465                streams.push(TileStream { offset: STREAM_OFFSET, length: len, x: 0, y: 0, width: w, height: hh * 2 });
466            }
467        }
468    }
469    streams
470}
471
472fn parse_directory(stream: &[u8]) -> Result<Vec<DirEntry>> {
473    let groups = group_sizes(stream, 0x10)?;
474    let mut entries = Vec::new();
475    let mut pos = 0x30usize;
476    let mut base = 0usize;
477    for (group, &group_len) in groups.iter().enumerate() {
478        require(pos + 0x10 <= stream.len(), "directory beyond stream")?;
479        let n = (stream[pos] & 0x0f) as usize;
480        require(n <= 9, "directory entry count")?;
481        let mut values = [0usize; 9];
482        for (i, v) in values.iter_mut().take(5).enumerate() {
483            *v = ((be32(stream, pos + 3 * i)? & 0x00ff_ffff) << 4) as usize;
484        }
485        let mut consumed = 0x10;
486        if n >= 5 {
487            require(pos + 0x20 <= stream.len(), "directory beyond stream")?;
488            for i in 0..4 {
489                values[5 + i] = ((be32(stream, pos + 0x10 + 3 * i)? & 0x00ff_ffff) << 4) as usize;
490            }
491            consumed = 0x20;
492        }
493        let mut local = 0usize;
494        for (index, &length) in values.iter().take(n).enumerate() {
495            let start = 0x80 + base + local;
496            require(start <= stream.len() && length <= stream.len() - start, "packet beyond stream")?;
497            entries.push(DirEntry { group, index, start, length });
498            local += length;
499        }
500        require(local <= group_len, "group size mismatch")?;
501        base += group_len;
502        pos += consumed;
503    }
504    Ok(entries)
505}
506
507struct Record {
508    byte_length: usize,
509    selectors: [u8; 3],
510    payload_offset: usize,
511}
512
513struct Packet<'a> {
514    kind: usize,
515    data: &'a [u8],
516    records: Vec<Record>,
517}
518
519fn parse_packet(data: &[u8]) -> Result<Packet<'_>> {
520    require(data.len() >= 16, "packet too short")?;
521    let mut br = HeaderBits { data, pos: 0 };
522    let control_count = br.read(16)? as usize;
523    let extra_count = br.read(24)? as usize;
524    let tag4 = br.read(4)?;
525    let reserved4 = br.read(4)?;
526    let kind = br.read(2)? as usize;
527    let control_words = br.read(6)?;
528    let block_count = br.read(16)? as usize;
529    let width_marker = br.read(8)?;
530    let reserved8 = br.read(8)?;
531    for _ in 0..5 {
532        require(br.read(8)? == 0, "packet header padding")?;
533    }
534    require(tag4 == 4 && reserved4 == 0 && control_words == 0, "packet header tag")?;
535    require(kind == 1 || kind == 3, "packet type")?;
536    require(block_count > 0 && block_count <= 30000, "packet block count")?;
537    require(width_marker == 0x10 && reserved8 == 0, "packet width marker")?;
538    let control_bytes = (control_count + 1) << 4;
539    let total = (control_count + 1 + extra_count) << 4;
540    require(total == data.len() && control_bytes <= data.len(), "packet size")?;
541    let mut cursor = control_bytes;
542    let mut records = Vec::with_capacity(block_count);
543    for _ in 0..block_count {
544        let byte_length = br.read(16)? as usize;
545        let mut selectors = [0u8; 3];
546        for s in selectors.iter_mut().take(kind) {
547            *s = br.read(4)? as u8;
548        }
549        require(byte_length <= data.len() && cursor <= data.len() - byte_length, "packet record beyond packet")?;
550        records.push(Record { byte_length, selectors, payload_offset: cursor });
551        cursor += byte_length;
552    }
553    Ok(Packet { kind, data, records })
554}
555
556fn row_multiplier(group: usize) -> usize {
557    match group {
558        4 => 8,
559        0 => 1,
560        g => 1 << (g - 1),
561    }
562}
563
564/// Decode the coefficient planes of packet `(group, index)`.
565fn packet_planes(
566    stream: &[u8],
567    dir: &[DirEntry],
568    mosaic_w: usize,
569    coded_h: usize,
570    group: usize,
571    index: usize,
572    parallel: bool,
573) -> Result<Vec<Plane>> {
574    let e = dir.iter().find(|e| e.group == group && e.index == index).ok_or_else(|| corrupt("missing packet"))?;
575    let p = parse_packet(&stream[e.start..e.start + e.length])?;
576    let comps = if p.kind == 1 { 1 } else { 3 };
577    let row_width = match (p.kind, group) {
578        (1, 4) => mosaic_w / 2,
579        (1, _) => mosaic_w / 16,
580        (3, 1..=3) => mosaic_w >> (5 - group),
581        _ => return Err(corrupt("invalid packet type or group")),
582    };
583    let rm = row_multiplier(group);
584    let rows = p.records.len() * rm;
585    require(coded_h > 0 && coded_h <= 0x7fff, "coded height")?;
586    let expected = (coded_h.div_ceil(16) + 1) * rm;
587    let groups_per_row = row_width.div_ceil(4);
588    require(row_width > 0 && groups_per_row > 0 && rows > 0, "packet geometry")?;
589    if rows != expected {
590        require(coded_h % 16 != 0 && expected.checked_sub(rows) == Some(rm), "packet row count")?;
591    }
592    require(p.records.len() <= stream.len() / 16 + 1, "packet record count")?;
593    let mut planes: Vec<Plane> = (0..comps).map(|_| Plane::new(expected, row_width)).collect::<Result<_>>()?;
594    // Records are independent bit streams: decode them in parallel, each into its
595    // own rows of every component plane.
596    let record_rows = rm * row_width;
597    let mut bufs: Vec<Vec<i32>> = Vec::new();
598    let decode_record = |ri: usize| -> Vec<i32> {
599        let rec = &p.records[ri];
600        let mut buf = vec![0i32; comps * record_rows];
601        if rec.byte_length > 0 {
602            let mut bits = CoeffBits::new(p.data, rec.payload_offset, rec.byte_length);
603            for ci in 0..comps {
604                let shift = if ci < p.kind { rec.selectors[ci] as u32 } else { 0 };
605                for r in 0..rm {
606                    let off = ci * record_rows + r * row_width;
607                    decode_component(&mut bits, groups_per_row, shift, &mut buf[off..off + row_width]);
608                }
609            }
610        }
611        buf
612    };
613    bufs.extend(par::map((0..p.records.len()).collect(), parallel, decode_record));
614    for (ri, buf) in bufs.iter().enumerate() {
615        for (ci, plane) in planes.iter_mut().enumerate() {
616            let dst = &mut plane.data[ri * record_rows..(ri + 1) * record_rows];
617            dst.copy_from_slice(&buf[ci * record_rows..(ci + 1) * record_rows]);
618        }
619    }
620    Ok(planes)
621}
622
623// ---------------------------------------------------------------------------
624// Wavelet synthesis
625
626/// Horizontal DPCM of the band-0 rows (sign-extended 16-bit deltas).
627fn integrate(coeffs: &Plane, rows: usize, start: usize) -> Result<Plane> {
628    require(rows <= coeffs.rows && start <= rows, "band-0 rows")?;
629    let mut out = Plane::new(rows - start, coeffs.cols)?;
630    for y in start..rows {
631        let src = coeffs.row(y);
632        let dst = out.row_mut(y - start);
633        let mut acc = 0i32;
634        for (d, &s) in dst.iter_mut().zip(src) {
635            acc = acc.wrapping_add(sign16(s).wrapping_mul(2));
636            *d = acc / 2;
637        }
638    }
639    Ok(out)
640}
641
642/// Rows per parallel work item of the lifting passes.
643const LIFT_ROWS: usize = 16;
644
645/// Inverse 5/3 lifting along rows (vertical): `low`/`high` interleave to 2x rows.
646fn inv53_vertical(low: &Plane, high: &Plane, parallel: bool) -> Result<Plane> {
647    require(low.rows == high.rows && low.cols == high.cols, "vertical lifting shapes")?;
648    let (h, w) = (low.rows, low.cols);
649    let mut lo2 = Plane::new(h, w)?;
650    par::for_each_row_chunk(&mut lo2.data, w, LIFT_ROWS, parallel, |first, chunk| {
651        for (i, d) in chunk.chunks_mut(w).enumerate() {
652            let y = first + i;
653            let hp = high.row(y.saturating_sub(1));
654            let hc = high.row(y);
655            let l = low.row(y);
656            for (x, d) in d.iter_mut().enumerate() {
657                *d = l[x].wrapping_sub(fshr(hp[x].wrapping_add(hc[x]).wrapping_add(2), 2));
658            }
659        }
660    });
661    let mut out = Plane::new(h * 2, w)?;
662    let lo2 = &lo2;
663    par::for_each_row_chunk(&mut out.data, 2 * w, LIFT_ROWS, parallel, |first, chunk| {
664        for (i, pair) in chunk.chunks_mut(2 * w).enumerate() {
665            let y = first + i;
666            let ln = lo2.row(if y + 1 < h { y + 1 } else { y });
667            let lc = lo2.row(y);
668            let hc = high.row(y);
669            let (r0, r1) = pair.split_at_mut(w);
670            r0.copy_from_slice(lc);
671            for (x, d) in r1.iter_mut().enumerate() {
672                *d = hc[x].wrapping_add(fshr(lc[x].wrapping_add(ln[x]), 1));
673            }
674        }
675    });
676    Ok(out)
677}
678
679/// Inverse 5/3 lifting along columns (horizontal): interleaves to 2x columns.
680fn inv53_horizontal(low: &Plane, high: &Plane, parallel: bool) -> Result<Plane> {
681    require(low.rows == high.rows && low.cols == high.cols, "horizontal lifting shapes")?;
682    let (h, w) = (low.rows, low.cols);
683    let mut out = Plane::new(h, w * 2)?;
684    if w == 0 {
685        return Ok(out);
686    }
687    par::for_each_row_chunk(&mut out.data, 2 * w, LIFT_ROWS, parallel, |first, chunk| {
688        let mut lo2 = vec![0i32; w];
689        for (i, o) in chunk.chunks_mut(2 * w).enumerate() {
690            let y = first + i;
691            let l = low.row(y);
692            let hi = high.row(y);
693            lo2[0] = l[0].wrapping_sub(fshr(hi[0].wrapping_add(hi[0]).wrapping_add(2), 2));
694            for x in 1..w {
695                lo2[x] = l[x].wrapping_sub(fshr(hi[x - 1].wrapping_add(hi[x]).wrapping_add(2), 2));
696            }
697            for x in 0..w {
698                let ln = lo2[if x + 1 < w { x + 1 } else { x }];
699                o[2 * x] = lo2[x];
700                o[2 * x + 1] = hi[x].wrapping_add(fshr(lo2[x].wrapping_add(ln), 1));
701            }
702        }
703    });
704    Ok(out)
705}
706
707/// Vertical lifting where the high band leads (odd-phase stripe boundaries).
708fn inv53_vertical_high_leading(low: &Plane, high: &Plane) -> Result<Plane> {
709    require(low.cols == high.cols, "lifting shapes")?;
710    require(high.rows == low.rows || high.rows == low.rows + 1, "lifting row counts")?;
711    require(low.rows > 0, "empty lifting band")?;
712    let (lh, hh, w) = (low.rows, high.rows, low.cols);
713    let mut lo2 = Plane::new(lh, w)?;
714    let mut hi2 = Plane::new(hh, w)?;
715    for y in 0..lh {
716        let next = if hh == lh + 1 || y + 1 < hh { y + 1 } else { y };
717        let next = next.min(hh - 1);
718        for x in 0..w {
719            let v = high.at(y, x).wrapping_add(high.at(next, x)).wrapping_add(2);
720            lo2.set(y, x, low.at(y, x).wrapping_sub(fshr(v, 2)));
721        }
722    }
723    if hh == lh + 1 {
724        for x in 0..w {
725            hi2.set(0, x, high.at(0, x).wrapping_add(lo2.at(0, x)));
726            hi2.set(hh - 1, x, high.at(hh - 1, x).wrapping_add(lo2.at(lh - 1, x)));
727        }
728        for y in 1..hh - 1 {
729            for x in 0..w {
730                let v = fshr(lo2.at(y - 1, x).wrapping_add(lo2.at(y, x)), 1);
731                hi2.set(y, x, high.at(y, x).wrapping_add(v));
732            }
733        }
734        let mut out = Plane::new(lh * 2 + 1, w)?;
735        for y in 0..lh {
736            out.row_mut(2 * y).copy_from_slice(hi2.row(y));
737            out.row_mut(2 * y + 1).copy_from_slice(lo2.row(y));
738        }
739        let last = out.rows - 1;
740        out.row_mut(last).copy_from_slice(hi2.row(hh - 1));
741        return Ok(out);
742    }
743    for x in 0..w {
744        hi2.set(0, x, high.at(0, x).wrapping_add(lo2.at(0, x)));
745    }
746    for y in 1..hh {
747        for x in 0..w {
748            let v = fshr(lo2.at(y - 1, x).wrapping_add(lo2.at(y, x)), 1);
749            hi2.set(y, x, high.at(y, x).wrapping_add(v));
750        }
751    }
752    let mut out = Plane::new(lh * 2, w)?;
753    for y in 0..lh {
754        out.row_mut(2 * y).copy_from_slice(hi2.row(y));
755        out.row_mut(2 * y + 1).copy_from_slice(lo2.row(y));
756    }
757    Ok(out)
758}
759
760/// One synthesis level without stripe guard rows.
761fn synth_level(ll: &Plane, s0: &Plane, s1: &Plane, s2: &Plane, parallel: bool) -> Result<Plane> {
762    let (h, w) = (ll.rows, ll.cols);
763    require(s0.cols == w && s1.cols == w && s2.cols == w, "subband widths")?;
764    require(s0.rows > h && s1.rows > h && s2.rows > h && h > 0, "subband rows")?;
765    let mut lh = Plane::new(h, w)?;
766    let mut hh = Plane::new(h, w)?;
767    for y in 0..h - 1 {
768        lh.row_mut(y).copy_from_slice(s1.row(y + 1));
769        hh.row_mut(y).copy_from_slice(s2.row(y + 1));
770    }
771    lh.row_mut(h - 1).copy_from_slice(s0.row(h));
772    hh.row_mut(h - 1).copy_from_slice(s1.row(h));
773    let lo = inv53_vertical(ll, &lh, parallel)?;
774    let hi = inv53_vertical(&s0.head(h)?, &hh, parallel)?;
775    inv53_horizontal(&lo, &hi, parallel)
776}
777
778/// Level with `edge` stripe guard rows at the top and bottom of the subbands.
779fn synth_level_stride(
780    ll: &Plane,
781    s0: &Plane,
782    s1: &Plane,
783    s2: &Plane,
784    edge: usize,
785    odd: bool,
786    parallel: bool,
787) -> Result<Plane> {
788    if edge == 0 {
789        return synth_level(ll, s0, s1, s2, parallel);
790    }
791    let (h, w) = (ll.rows, ll.cols);
792    require(s0.cols == w && s1.cols == w && s2.cols == w, "subband widths")?;
793    require(h >= 2 * edge && s0.rows >= h + 2 * edge && s1.rows >= h + edge && s2.rows >= h, "subband rows")?;
794    let mut hl = Plane::new(h, w)?;
795    let mut lh = Plane::new(h, w)?;
796    let mut hh = Plane::new(h, w)?;
797    for y in 0..edge {
798        hl.row_mut(y).copy_from_slice(s0.row(y));
799        lh.row_mut(y).copy_from_slice(s0.row(y + edge));
800        for x in 0..w {
801            hh.set(y, x, edge_detail(s1.at(y, x), odd));
802        }
803    }
804    for y in edge..h - edge {
805        hl.row_mut(y).copy_from_slice(s0.row(y + edge));
806        lh.row_mut(y).copy_from_slice(s1.row(y + edge));
807        hh.row_mut(y).copy_from_slice(s2.row(y + edge));
808    }
809    for y in h - edge..h {
810        let k = y - (h - edge);
811        hl.row_mut(y).copy_from_slice(s0.row(h + k));
812        lh.row_mut(y).copy_from_slice(s0.row(h + edge + k));
813        for x in 0..w {
814            hh.set(y, x, edge_detail(s1.at(h + k, x), odd));
815        }
816    }
817    let lo = inv53_vertical(ll, &lh, parallel)?;
818    let hi = inv53_vertical(&hl, &hh, parallel)?;
819    inv53_horizontal(&lo, &hi, parallel)
820}
821
822/// Guarded (height not a multiple of 16) level 1.
823fn synth_guard1(ll: &Plane, s0: &Plane, s1: &Plane, s2: &Plane, parallel: bool) -> Result<Plane> {
824    let (h, w) = (ll.rows, ll.cols);
825    require(s0.cols == w && s1.cols == w && s2.cols == w, "subband widths")?;
826    require(s0.rows >= h + 2 && s1.rows >= h + 2 && s2.rows >= h + 2, "subband rows")?;
827    let mut lh = Plane::new(h + 1, w)?;
828    let mut hh = Plane::new(h + 1, w)?;
829    let mut hl = Plane::new(h, w)?;
830    for y in 0..h {
831        hl.row_mut(y).copy_from_slice(s0.row(y + 1));
832        lh.row_mut(y).copy_from_slice(s1.row(y + 1));
833        hh.row_mut(y).copy_from_slice(s2.row(y + 1));
834    }
835    lh.row_mut(h).copy_from_slice(s0.row(h + 1));
836    hh.row_mut(h).copy_from_slice(s1.row(h + 1));
837    let lo = inv53_vertical_high_leading(ll, &lh)?;
838    let hi = inv53_vertical_high_leading(&hl, &hh)?;
839    inv53_horizontal(&lo, &hi, parallel)
840}
841
842/// Guarded level 2.
843fn synth_guard2(ll: &Plane, s0: &Plane, s1: &Plane, s2: &Plane, odd: bool, parallel: bool) -> Result<Plane> {
844    let (h, w) = (ll.rows, ll.cols);
845    require(s0.cols == w && s1.cols == w && s2.cols == w, "subband widths")?;
846    require(h > 0 && s0.rows >= h + 3 && s1.rows > h && s2.rows > h, "subband rows")?;
847    let mut hl = Plane::new(h, w)?;
848    let mut lh = Plane::new(h, w)?;
849    let mut hh = Plane::new(h, w)?;
850    lh.row_mut(0).copy_from_slice(s0.row(0));
851    for x in 0..w {
852        hh.set(0, x, edge_detail(s0.at(1, x), odd));
853    }
854    for y in 0..h {
855        hl.row_mut(y).copy_from_slice(s0.row(y + 2));
856    }
857    for y in 1..h {
858        lh.row_mut(y).copy_from_slice(s1.row(y + 1));
859        hh.row_mut(y).copy_from_slice(s2.row(y + 1));
860    }
861    let lo = inv53_vertical_high_leading(ll, &lh)?;
862    let hi = inv53_vertical_high_leading(&hl, &hh)?;
863    inv53_horizontal(&lo, &hi, parallel)
864}
865
866/// Guarded level 3.
867fn synth_guard3(ll: &Plane, s0: &Plane, s1: &Plane, s2: &Plane, odd: bool, parallel: bool) -> Result<Plane> {
868    let (h, w) = (ll.rows, ll.cols);
869    require(s0.cols == w && s1.cols == w && s2.cols == w, "subband widths")?;
870    require(h >= 2 && s0.rows >= h + 5 && s1.rows >= h + 2 && s2.rows >= h + 2, "subband rows")?;
871    let mut hl = Plane::new(h, w)?;
872    let mut lh = Plane::new(h, w)?;
873    let mut hh = Plane::new(h, w)?;
874    hl.row_mut(0).copy_from_slice(s0.row(0));
875    lh.row_mut(0).copy_from_slice(s0.row(1));
876    lh.row_mut(h - 1).copy_from_slice(s0.row(h + 3));
877    for x in 0..w {
878        hh.set(0, x, edge_detail(s0.at(2, x), odd));
879        hh.set(h - 1, x, edge_detail(s0.at(h + 4, x), odd));
880    }
881    for y in 1..h {
882        hl.row_mut(y).copy_from_slice(s0.row(y + 3));
883    }
884    for y in 1..h - 1 {
885        lh.row_mut(y).copy_from_slice(s1.row(y + 3));
886        hh.row_mut(y).copy_from_slice(s2.row(y + 3));
887    }
888    let lo = inv53_vertical(ll, &lh, parallel)?;
889    let hi = inv53_vertical(&hl, &hh, parallel)?;
890    inv53_horizontal(&lo, &hi, parallel)
891}
892
893/// Final level: restore the two greens of each quad from their average (`ll`) and
894/// the band-4 detail.
895fn final_green(ll: &Plane, detail: &Plane, top_rows: usize, parallel: bool) -> Result<Plane> {
896    let (h, w) = (ll.rows, ll.cols);
897    require(top_rows <= 8 && detail.cols == w && detail.rows >= 8 + h.saturating_sub(top_rows), "green detail")?;
898    if h == 0 || w == 0 {
899        return Plane::new(h, w * 2);
900    }
901    // Band-4 rows used for each output row (the guard rows are skipped).
902    let sel = |y: usize| detail.row(if y < top_rows { y } else { y + 8 - top_rows });
903    let mut odd = Plane::new(h, w)?;
904    par::for_each_row_chunk(&mut odd.data, w, LIFT_ROWS, parallel, |first, chunk| {
905        for (i, o) in chunk.chunks_mut(w).enumerate() {
906            let y = first + i;
907            let (cur, prev, l) = (sel(y), sel(y.saturating_sub(1)), ll.row(y));
908            for x in 0..w {
909                let pred = if x + 1 < w {
910                    fshr(cur[x + 1].wrapping_add(prev[x]).wrapping_add(cur[x]).wrapping_add(prev[x + 1]), 2)
911                } else {
912                    fshr(prev[x].wrapping_add(cur[x]).wrapping_mul(2), 2)
913                };
914                o[x] = ((2 * l[x] as i64 - pred as i64) >> 1) as i32;
915            }
916        }
917    });
918    let mut out = Plane::new(h, w * 2)?;
919    let odd = &odd;
920    par::for_each_row_chunk(&mut out.data, 2 * w, LIFT_ROWS, parallel, |first, chunk| {
921        for (i, o) in chunk.chunks_mut(2 * w).enumerate() {
922            let y = first + i;
923            let (od, on, sl) = (odd.row(y), odd.row(if y + 1 < h { y + 1 } else { y }), sel(y));
924            let v = (od[0].wrapping_add(on[0]) as i64 * 2) >> 2;
925            o[0] = sl[0].wrapping_add(v as i32);
926            o[1] = od[0];
927            for x in 1..w {
928                let s = od[x - 1].wrapping_add(on[x - 1]).wrapping_add(on[x]).wrapping_add(od[x]);
929                o[2 * x] = sl[x].wrapping_add(fshr(s, 2));
930                o[2 * x + 1] = od[x];
931            }
932        }
933    });
934    Ok(out)
935}
936
937/// Decoded planes of one tile (half resolution except `full_green`).
938struct DecodedTile {
939    full_green: Plane,
940    red: Plane,
941    blue: Plane,
942}
943
944/// Decode one tile stream.
945fn decode_stream(stream: &[u8], parallel: bool) -> Result<DecodedTile> {
946    let (width, half_h) = parse_stream_header(stream).ok_or_else(|| corrupt("invalid stream header"))?;
947    let dir = parse_directory(stream)?;
948    let coded_h = half_h * 2;
949    let padded = coded_h.div_ceil(16) * 16;
950    let guarded = coded_h != padded;
951    let low_rows = padded / 16;
952    let low_start = guarded as usize;
953
954    // Three planes (green average, red and blue residuals) are independent until
955    // the final green level.
956    let plane = |index: usize| -> Result<Plane> {
957        let b0 = packet_planes(stream, &dir, width, coded_h, 0, index, parallel)?;
958        let mut p = integrate(&b0[0], low_rows, low_start)?;
959        for group in 1..=3 {
960            let edge = [0, 0, 1, 2][group];
961            let odd = group == 3 && index != 0;
962            let s = packet_planes(stream, &dir, width, coded_h, group, index, parallel)?;
963            require(s.len() == 3, "detail packet with one component")?;
964            p = if guarded {
965                match group {
966                    1 => synth_guard1(&p, &s[0], &s[1], &s[2], parallel)?,
967                    2 => synth_guard2(&p, &s[0], &s[1], &s[2], false, parallel)?,
968                    _ => synth_guard3(&p, &s[0], &s[1], &s[2], odd, parallel)?,
969                }
970            } else {
971                synth_level_stride(&p, &s[0], &s[1], &s[2], edge, odd, parallel)?
972            };
973        }
974        Ok(p)
975    };
976    let mut planes = par::map(vec![0usize, 1, 2], parallel, plane).into_iter();
977    let (Some(green), Some(red), Some(blue)) = (planes.next(), planes.next(), planes.next()) else {
978        return Err(corrupt("missing colour plane"));
979    };
980    let (green, red, blue) = (green?, red?, blue?);
981    let g4 = packet_planes(stream, &dir, width, coded_h, 4, 0, parallel)?;
982    let full_green = final_green(&green, &g4[0], if guarded { 2 } else { 4 }, parallel)?;
983    Ok(DecodedTile { full_green, red, blue })
984}
985
986#[inline(always)]
987fn clamp_code(x: i32) -> i32 {
988    x.saturating_add(BIAS).clamp(0, 4095) - BIAS
989}
990
991#[inline(always)]
992fn sample(x: i32) -> u16 {
993    LLVC3_CURVE[x.saturating_add(BIAS).clamp(0, 4095) as usize]
994}
995
996/// Decode a whole ARW6 strip into `out` (`raw_w x raw_h`, row-major).
997pub fn decode(strip: &[u8], out: &mut [u16], raw_w: usize, raw_h: usize, parallel: bool) -> Result<()> {
998    require(raw_w > 0 && raw_h > 0 && raw_w % 16 == 0 && raw_h % 2 == 0, "frame size")?;
999    require(out.len() >= raw_w * raw_h, "output buffer")?;
1000    require(strip.len() >= STREAM_OFFSET + 0x80, "strip too short")?;
1001    let streams = find_streams(strip, raw_w, raw_h);
1002    require(!streams.is_empty(), "no tile streams")?;
1003    let tiles: Vec<Result<DecodedTile>> =
1004        par::map(streams.clone(), parallel, |s| decode_stream(&strip[s.offset..s.offset + s.length], parallel));
1005    for (s, tile) in streams.iter().zip(tiles) {
1006        let t = tile?;
1007        let (half_h, half_w) = (s.height / 2, s.width / 2);
1008        require(
1009            t.full_green.rows >= half_h
1010                && t.full_green.cols == s.width
1011                && t.red.rows >= half_h
1012                && t.red.cols == half_w
1013                && t.blue.rows >= half_h
1014                && t.blue.cols == half_w,
1015            "tile plane sizes",
1016        )?;
1017        // Each output row pair of the tile is independent.
1018        let band = &mut out[s.y * raw_w..(s.y + s.height) * raw_w];
1019        par::for_each_row_chunk(band, 2 * raw_w, 8, parallel, |first, chunk| {
1020            for (i, pair) in chunk.chunks_mut(2 * raw_w).enumerate() {
1021                let y = first + i;
1022                let (r0, r1) = pair.split_at_mut(raw_w);
1023                let (r0, r1) = (&mut r0[s.x..s.x + s.width], &mut r1[s.x..s.x + s.width]);
1024                let (fg, red, blue) = (t.full_green.row(y), t.red.row(y), t.blue.row(y));
1025                for x in 0..half_w {
1026                    let g0 = clamp_code(fg[2 * x]);
1027                    let g1 = clamp_code(fg[2 * x + 1]);
1028                    let gavg = ((g0 as i64 + g1 as i64) >> 1) as i32;
1029                    r0[2 * x] = sample(gavg.wrapping_add(red[x].wrapping_mul(2)));
1030                    r0[2 * x + 1] = sample(fg[2 * x + 1]);
1031                    r1[2 * x] = sample(fg[2 * x]);
1032                    r1[2 * x + 1] = sample(gavg.wrapping_add(blue[x].wrapping_mul(2)));
1033                }
1034            }
1035        });
1036    }
1037    Ok(())
1038}
1039
1040#[cfg(test)]
1041mod tests {
1042    use super::*;
1043
1044    #[test]
1045    fn helpers_match_definitions() {
1046        for x in -9..=9 {
1047            assert_eq!(fshr(x, 1), (x as f64 / 2.0).floor() as i32);
1048            assert_eq!(fshr(x, 2), (x as f64 / 4.0).floor() as i32);
1049        }
1050        assert_eq!(edge_detail(3, true), 7);
1051        assert_eq!(edge_detail(3, false), 6);
1052        assert_eq!(edge_detail(-4, false), -9);
1053        assert_eq!(edge_detail(-4, true), -8);
1054        assert_eq!(edge_detail(0, false), 0);
1055        assert_eq!(sign16(0xffff), -1);
1056        assert_eq!(sample(-5000), LLVC3_CURVE[0]);
1057        assert_eq!(sample(5000), 39002);
1058    }
1059
1060    #[test]
1061    fn coefficient_bits_and_limits() {
1062        let data = [0b1010_0000u8, 0xff, 0, 0, 0, 0, 0, 0, 0x80];
1063        let mut b = CoeffBits::new(&data, 0, 1);
1064        assert_eq!(b.read(3), 0b101);
1065        assert_eq!(b.read(8), 0b0000_0111);
1066        // allowance is two words (128 bits); bits past it read as zero with status set
1067        let mut b = CoeffBits::new(&data, 0, 1);
1068        b.read(32);
1069        b.read(32);
1070        b.read(32);
1071        b.read(24);
1072        assert_eq!(b.read(16), 0);
1073        assert_eq!(b.status, 2);
1074    }
1075
1076    #[test]
1077    fn inverse_lifting_reconstructs_forward_53() {
1078        // Forward 5/3 on a 1-D signal, then our horizontal inverse.
1079        let sig: Vec<i32> = vec![10, 13, 7, 2, 8, 30, 31, 29, 5, 6];
1080        let n = sig.len() / 2;
1081        let mut high = vec![0; n];
1082        let mut low = vec![0; n];
1083        for i in 0..n {
1084            let r = if 2 * i + 2 < sig.len() { sig[2 * i + 2] } else { sig[2 * i] };
1085            high[i] = sig[2 * i + 1] - fshr(sig[2 * i] + r, 1);
1086        }
1087        for i in 0..n {
1088            let hp = high[i.saturating_sub(1)];
1089            low[i] = sig[2 * i] + fshr(hp + high[i] + 2, 2);
1090        }
1091        let lo = Plane { rows: 1, cols: n, data: low };
1092        let hi = Plane { rows: 1, cols: n, data: high };
1093        assert_eq!(inv53_horizontal(&lo, &hi, false).unwrap().data, sig);
1094    }
1095
1096    /// Bit-at-a-time reference of the coefficient stream semantics.
1097    struct RefBits<'a> {
1098        data: &'a [u8],
1099        start: usize,
1100        pos: usize,
1101        limit: usize,
1102        status: u8,
1103    }
1104
1105    impl RefBits<'_> {
1106        fn bit(&mut self) -> u32 {
1107            if self.pos >= self.limit {
1108                self.status = 2;
1109                return 0;
1110            }
1111            let byte = self.start + self.pos / 8;
1112            let b = self.data.get(byte).map(|&b| (b >> (7 - self.pos % 8)) & 1).unwrap_or(0) as u32;
1113            self.pos += 1;
1114            b
1115        }
1116        fn read(&mut self, n: u32) -> u32 {
1117            (0..n).fold(0, |a, _| a << 1 | self.bit())
1118        }
1119        fn unary_plus_one(&mut self) -> i32 {
1120            let mut zeros = 0;
1121            while self.status == 0 {
1122                let b = self.bit();
1123                if self.status != 0 {
1124                    break;
1125                }
1126                if b != 0 {
1127                    return zeros + 1;
1128                }
1129                zeros += 1;
1130            }
1131            zeros + 1
1132        }
1133        fn leading_one_within(&mut self, k: u32) -> Option<u32> {
1134            (0..k).find(|_| self.bit() != 0)
1135        }
1136    }
1137
1138    #[test]
1139    fn coefficient_reader_matches_bit_reference() {
1140        let mut x = 99u32;
1141        let data: Vec<u8> = (0..300)
1142            .map(|i| {
1143                x = x.wrapping_mul(1664525).wrapping_add(1013904223);
1144                let b = (x >> 24) as u8;
1145                if i % 5 == 0 { 0 } else { b & (b >> 1) }
1146            })
1147            .collect();
1148        for (off, len) in [(0usize, 40usize), (3, 100), (17, 7), (250, 60)] {
1149            let mut a = CoeffBits::new(&data, off, len);
1150            let words = ((len.div_ceil(8)) + 1) & !1;
1151            let mut b = RefBits { data: &data, start: off, pos: 0, limit: words * 64, status: 0 };
1152            let mut y = off as u32 + 7;
1153            for step in 0..400 {
1154                y = y.wrapping_mul(1103515245).wrapping_add(12345);
1155                match (y >> 16) % 3 {
1156                    0 => {
1157                        let n = (y >> 8) % 33;
1158                        assert_eq!(a.read(n), b.read(n), "read {n} step {step}");
1159                    }
1160                    1 => assert_eq!(a.unary_plus_one(), b.unary_plus_one(), "unary step {step}"),
1161                    _ => {
1162                        let k = (y >> 8) % 20;
1163                        assert_eq!(a.leading_one_within(k), b.leading_one_within(k), "lead {k} step {step}");
1164                    }
1165                }
1166                assert_eq!(a.status != 0, b.status != 0, "status step {step}");
1167            }
1168        }
1169    }
1170
1171    #[test]
1172    fn garbage_strips_are_rejected() {
1173        let mut out = vec![0u16; 64 * 32];
1174        assert!(decode(&[0u8; 100], &mut out, 64, 32, false).is_err());
1175        let mut strip = vec![0u8; 0x400];
1176        strip[0x200..0x204].copy_from_slice(b"A000");
1177        assert!(decode(&strip, &mut out, 64, 32, false).is_err());
1178    }
1179}