1use flashraw_core::bits::{BitPumpLsb, BitPumpMsb, BitPumpMsb16, BitPumpMsb32, BitRead};
8use flashraw_core::{Error, PackOrder, Result, par};
9
10#[derive(Debug, Clone, Copy)]
12pub struct PackedLayout {
13 pub width: usize,
14 pub height: usize,
15 pub bits: u32,
17 pub order: PackOrder,
18 pub row_stride: usize,
20}
21
22impl PackedLayout {
23 pub fn tight(width: usize, height: usize, bits: u32, order: PackOrder) -> Self {
25 PackedLayout { width, height, bits, order, row_stride: (width * bits as usize).div_ceil(8) }
26 }
27 pub fn row_bytes(&self) -> usize {
28 (self.width * self.bits as usize).div_ceil(8)
29 }
30}
31
32pub fn unpack(
37 src: &[u8],
38 layout: PackedLayout,
39 dst: &mut [u16],
40 dst_stride: usize,
41 parallel: bool,
42 simd: bool,
43 lenient: bool,
44) -> Result<()> {
45 let PackedLayout { width, height, bits, row_stride, .. } = layout;
46 if bits == 0 || bits > 16 {
47 return Err(Error::corrupt(format!("unsupported packed bit depth {bits}")));
48 }
49 if dst_stride < width || dst.len() < dst_stride * height.saturating_sub(1) + width.min(dst.len()) {
50 return Err(Error::InvalidArgument("destination too small for packed unpack".into()));
51 }
52 let row_bytes = layout.row_bytes();
53 let avail_rows =
54 src.len().saturating_sub(row_bytes).checked_div(row_stride).map_or(height, |n| (n + 1).min(height));
55 let avail_rows = if src.len() < row_bytes { 0 } else { avail_rows };
56 if avail_rows < height && !lenient {
57 return Err(Error::corrupt(format!("truncated packed data: {avail_rows}/{height} rows present")));
58 }
59 let n = (dst_stride * height).min(dst.len());
60 let dst = &mut dst[..n];
61 let body = |first: usize, chunk: &mut [u16]| {
62 for (i, row) in chunk.chunks_mut(dst_stride).enumerate() {
63 let y = first + i;
64 if y >= avail_rows {
65 if let Some(s) = src.get(y * row_stride..).filter(|s| y == avail_rows && !s.is_empty()) {
67 unpack_row_partial(s, &mut row[..width], bits, layout.order, simd);
68 }
69 break;
70 }
71 let s = &src[y * row_stride..];
72 let s = &s[..row_bytes.min(s.len())];
73 unpack_row(s, &mut row[..width], bits, layout.order, simd);
74 }
75 };
76 par::for_each_row_chunk_bounded(dst, dst_stride, par::memory_bound_workers(), parallel, body);
78 Ok(())
79}
80
81pub fn unpack_row(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder, simd: bool) {
83 match (bits, order) {
84 (16, PackOrder::LsbBytes) => unpack_u16le(src, dst),
85 (16, PackOrder::MsbBytes) => unpack_u16be(src, dst),
86 (8, PackOrder::MsbBytes | PackOrder::LsbBytes) => unpack_u8(src, dst),
87 (12, PackOrder::MsbBytes) => unpack12_msb(src, dst, simd),
88 (12, PackOrder::LsbBytes) => unpack12_lsb(src, dst, simd),
89 (14, PackOrder::MsbBytes) => unpack14_msb(src, dst),
90 (14, PackOrder::LsbBytes) => unpack14_lsb(src, dst),
91 (10, PackOrder::MsbBytes) => unpack10_msb(src, dst),
92 (10, PackOrder::LsbBytes) => unpack10_lsb(src, dst),
93 _ => unpack_row_scalar(src, dst, bits, order),
94 }
95}
96
97pub fn complete_samples(len: usize, bits: u32, order: PackOrder) -> usize {
100 let usable = match order {
101 PackOrder::Msb16 => len / 2 * 2,
102 PackOrder::Msb32 => len / 4 * 4,
103 PackOrder::MsbBytes | PackOrder::LsbBytes => len,
104 };
105 if bits == 0 { 0 } else { usable * 8 / bits as usize }
106}
107
108pub fn unpack_row_partial(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder, simd: bool) -> usize {
112 let n = complete_samples(src.len(), bits, order).min(dst.len());
113 let (done, rest) = dst.split_at_mut(n);
114 unpack_row(src, done, bits, order, simd);
115 rest.fill(0);
116 n
117}
118
119pub fn unpack_row_scalar(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder) {
121 match order {
122 PackOrder::MsbBytes => {
123 let mut p = BitPumpMsb::new(src);
124 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
125 }
126 PackOrder::LsbBytes => {
127 let mut p = BitPumpLsb::new(src);
128 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
129 }
130 PackOrder::Msb16 => {
131 let mut p = BitPumpMsb16::new(src);
132 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
133 }
134 PackOrder::Msb32 => {
135 let mut p = BitPumpMsb32::new(src);
136 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
137 }
138 }
139}
140
141fn unpack_u16le(src: &[u8], dst: &mut [u16]) {
142 let n = dst.len().min(src.len() / 2);
143 for (d, s) in dst[..n].iter_mut().zip(src.chunks_exact(2)) {
144 *d = u16::from_le_bytes([s[0], s[1]]);
145 }
146 tail(src, dst, n, 16, PackOrder::LsbBytes);
147}
148
149fn unpack_u16be(src: &[u8], dst: &mut [u16]) {
150 let n = dst.len().min(src.len() / 2);
151 for (d, s) in dst[..n].iter_mut().zip(src.chunks_exact(2)) {
152 *d = u16::from_be_bytes([s[0], s[1]]);
153 }
154 tail(src, dst, n, 16, PackOrder::MsbBytes);
155}
156
157fn unpack_u8(src: &[u8], dst: &mut [u16]) {
158 let n = dst.len().min(src.len());
159 for (d, &s) in dst[..n].iter_mut().zip(src) {
160 *d = s as u16;
161 }
162 dst[n..].iter_mut().for_each(|d| *d = 0);
164}
165
166#[inline]
168fn tail(src: &[u8], dst: &mut [u16], done: usize, bits: u32, order: PackOrder) {
169 if done < dst.len() {
170 let byte = done * bits as usize / 8; unpack_row_scalar(src.get(byte..).unwrap_or(&[]), &mut dst[done..], bits, order);
172 }
173}
174
175fn unpack12_msb(src: &[u8], dst: &mut [u16], simd: bool) {
176 let mut done = 0;
177 if simd {
178 done = simd12::unpack(src, dst, true);
179 }
180 let groups = ((dst.len() - done) / 2).min(src.len().saturating_sub(done / 2 * 3) / 3);
181 for g in 0..groups {
182 let s = &src[(done / 2 + g) * 3..];
183 let (b0, b1, b2) = (s[0] as u16, s[1] as u16, s[2] as u16);
184 dst[done + 2 * g] = (b0 << 4) | (b1 >> 4);
185 dst[done + 2 * g + 1] = ((b1 & 0xF) << 8) | b2;
186 }
187 tail(src, dst, done + groups * 2, 12, PackOrder::MsbBytes);
188}
189
190fn unpack12_lsb(src: &[u8], dst: &mut [u16], simd: bool) {
191 let mut done = 0;
192 if simd {
193 done = simd12::unpack(src, dst, false);
194 }
195 let groups = ((dst.len() - done) / 2).min(src.len().saturating_sub(done / 2 * 3) / 3);
196 for g in 0..groups {
197 let s = &src[(done / 2 + g) * 3..];
198 let (b0, b1, b2) = (s[0] as u16, s[1] as u16, s[2] as u16);
199 dst[done + 2 * g] = b0 | ((b1 & 0xF) << 8);
200 dst[done + 2 * g + 1] = (b1 >> 4) | (b2 << 4);
201 }
202 tail(src, dst, done + groups * 2, 12, PackOrder::LsbBytes);
203}
204
205macro_rules! word_unpack {
206 ($name:ident, $bits:expr, $bytes:expr, $n:expr, $be:expr, $order:expr) => {
207 fn $name(src: &[u8], dst: &mut [u16]) {
208 let groups = (dst.len() / $n).min(src.len().saturating_sub(8 - $bytes) / $bytes);
210 for g in 0..groups {
211 let s = &src[g * $bytes..g * $bytes + 8];
212 let w = if $be {
213 u64::from_be_bytes(s.try_into().unwrap())
214 } else {
215 u64::from_le_bytes(s.try_into().unwrap())
216 };
217 let m = (1u64 << $bits) - 1;
218 for k in 0..$n {
219 dst[g * $n + k] =
220 if $be { (w >> (64 - $bits * (k + 1))) & m } else { (w >> ($bits * k)) & m } as u16;
221 }
222 }
223 tail(src, dst, groups * $n, $bits, $order);
224 }
225 };
226}
227
228word_unpack!(unpack14_msb, 14, 7, 4, true, PackOrder::MsbBytes);
229word_unpack!(unpack14_lsb, 14, 7, 4, false, PackOrder::LsbBytes);
230word_unpack!(unpack10_msb, 10, 5, 4, true, PackOrder::MsbBytes);
231word_unpack!(unpack10_lsb, 10, 5, 4, false, PackOrder::LsbBytes);
232
233#[allow(dead_code)] mod simd12 {
236 pub(super) const SHUF_MSB: [u8; 16] = [1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10];
238 pub(super) const SHUF_LSB: [u8; 16] = [0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11];
240
241 #[allow(unreachable_code)]
243 pub fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
244 #[cfg(target_arch = "x86_64")]
245 {
246 if std::arch::is_x86_feature_detected!("sse4.1") && std::arch::is_x86_feature_detected!("ssse3") {
247 return unsafe { x86::unpack(src, dst, msb) };
249 }
250 return 0;
251 }
252 #[cfg(target_arch = "aarch64")]
253 {
254 return unsafe { neon::unpack(src, dst, msb) };
256 }
257 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
258 {
259 return wasm::unpack(src, dst, msb);
260 }
261 let _ = (src, dst, msb);
262 0
263 }
264
265 #[inline]
267 pub(super) fn groups(src: &[u8], dst: &[u16]) -> usize {
268 (dst.len() / 8).min(src.len().saturating_sub(4) / 12)
269 }
270
271 #[cfg(target_arch = "x86_64")]
272 mod x86 {
273 use std::arch::x86_64::*;
274
275 #[target_feature(enable = "ssse3,sse4.1")]
278 pub unsafe fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
279 let n = super::groups(src, dst);
280 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
281 unsafe {
285 let shuf = _mm_loadu_si128(shuf.as_ptr() as *const __m128i);
286 let mask = _mm_set1_epi16(0x0FFF);
287 for g in 0..n {
288 let v = _mm_loadu_si128(src.as_ptr().add(g * 12) as *const __m128i);
289 let v = _mm_shuffle_epi8(v, shuf);
290 let shifted = _mm_srli_epi16(v, 4);
291 let masked = _mm_and_si128(v, mask);
292 let out = if msb {
294 _mm_blend_epi16(masked, shifted, 0b0101_0101)
295 } else {
296 _mm_blend_epi16(masked, shifted, 0b1010_1010)
297 };
298 _mm_storeu_si128(dst.as_mut_ptr().add(g * 8) as *mut __m128i, out);
299 }
300 }
301 n * 8
302 }
303 }
304
305 #[cfg(target_arch = "aarch64")]
306 mod neon {
307 use std::arch::aarch64::*;
308
309 #[target_feature(enable = "neon")]
312 pub unsafe fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
313 let n = super::groups(src, dst);
314 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
315 let even_sel: [u16; 8] = if msb {
316 [0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0]
317 } else {
318 [0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF]
319 };
320 unsafe {
322 let tbl = vld1q_u8(shuf.as_ptr());
323 let sel = vld1q_u16(even_sel.as_ptr());
324 let mask = vdupq_n_u16(0x0FFF);
325 for g in 0..n {
326 let v = vld1q_u8(src.as_ptr().add(g * 12));
327 let v = vreinterpretq_u16_u8(vqtbl1q_u8(v, tbl));
328 let shifted = vshrq_n_u16(v, 4);
329 let masked = vandq_u16(v, mask);
330 let out = vbslq_u16(sel, shifted, masked);
331 vst1q_u16(dst.as_mut_ptr().add(g * 8), out);
332 }
333 }
334 n * 8
335 }
336 }
337
338 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
339 mod wasm {
340 use core::arch::wasm32::*;
341
342 pub fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
343 let n = super::groups(src, dst);
344 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
345 let sel = if msb {
346 u16x8(0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0)
347 } else {
348 u16x8(0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF)
349 };
350 unsafe {
352 let tbl = v128_load(shuf.as_ptr() as *const v128);
353 let mask = u16x8_splat(0x0FFF);
354 for g in 0..n {
355 let v = v128_load(src.as_ptr().add(g * 12) as *const v128);
356 let v = i8x16_swizzle(v, tbl);
357 let shifted = u16x8_shr(v, 4);
358 let masked = v128_and(v, mask);
359 v128_store(dst.as_mut_ptr().add(g * 8) as *mut v128, v128_bitselect(shifted, masked, sel));
360 }
361 }
362 n * 8
363 }
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 fn pseudo(n: usize, seed: u64) -> Vec<u8> {
372 let mut x = seed | 1;
373 (0..n)
374 .map(|_| {
375 x ^= x << 13;
376 x ^= x >> 7;
377 x ^= x << 17;
378 x as u8
379 })
380 .collect()
381 }
382
383 #[test]
384 fn fast_paths_match_reference() {
385 for bits in 1..=16u32 {
386 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes, PackOrder::Msb16, PackOrder::Msb32] {
387 for width in [1usize, 7, 8, 15, 16, 17, 63, 64, 333] {
388 let src = pseudo((width * bits as usize).div_ceil(8) + 3, bits as u64 * 1000 + width as u64);
389 let mut a = vec![0u16; width];
390 let mut b = vec![0u16; width];
391 let mut c = vec![0u16; width];
392 unpack_row_scalar(&src, &mut a, bits, order);
393 unpack_row(&src, &mut b, bits, order, true);
394 unpack_row(&src, &mut c, bits, order, false);
395 assert_eq!(a, b, "bits {bits} {order:?} width {width} simd");
396 assert_eq!(a, c, "bits {bits} {order:?} width {width} nosimd");
397 }
398 }
399 }
400 }
401
402 #[test]
403 fn short_sources_match_reference() {
404 for bits in 1..=16u32 {
406 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes, PackOrder::Msb16, PackOrder::Msb32] {
407 for len in 0..12usize {
408 let src = pseudo(len, bits as u64 + len as u64);
409 let mut a = vec![0xAAAAu16; 31];
410 let mut b = vec![0x5555u16; 31];
411 unpack_row_scalar(&src, &mut a, bits, order);
412 unpack_row(&src, &mut b, bits, order, true);
413 assert_eq!(a, b, "bits {bits} {order:?} len {len}");
414 }
415 }
416 }
417 }
418
419 #[test]
420 fn exact_length_sources() {
421 for bits in [10u32, 12, 14, 16] {
423 for width in [8usize, 16, 24, 32, 40, 48] {
424 let src = pseudo(width * bits as usize / 8, 9);
425 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes] {
426 let mut a = vec![0u16; width];
427 let mut b = vec![0u16; width];
428 unpack_row_scalar(&src, &mut a, bits, order);
429 unpack_row(&src, &mut b, bits, order, true);
430 assert_eq!(a, b);
431 }
432 }
433 }
434 }
435
436 #[test]
437 fn whole_image_with_stride_and_truncation() {
438 let (w, h) = (100usize, 50usize);
439 let layout = PackedLayout { width: w, height: h, bits: 12, order: PackOrder::MsbBytes, row_stride: 160 };
440 let src = pseudo(160 * h, 5);
441 let mut dst = vec![0u16; w * h];
442 unpack(&src, layout, &mut dst, w, true, true, false).unwrap();
443 for y in 0..h {
444 let mut r = vec![0u16; w];
445 unpack_row_scalar(&src[y * 160..], &mut r, 12, PackOrder::MsbBytes);
446 assert_eq!(&dst[y * w..(y + 1) * w], &r[..]);
447 }
448 assert!(unpack(&src[..160 * 10], layout, &mut dst, w, true, true, false).is_err());
449 assert!(unpack(&src[..160 * 10], layout, &mut dst, w, true, true, true).is_ok());
450 let mut part = vec![0u16; w * h];
452 unpack(&src[..160 * 10 + 7], layout, &mut part, w, true, true, true).unwrap();
453 assert_eq!(&part[..w * 10], &dst[..w * 10]);
454 assert_eq!(&part[w * 10..w * 10 + 4], &dst[w * 10..w * 10 + 4]);
455 assert!(part[w * 10 + 4..].iter().all(|&v| v == 0));
456 }
457
458 #[test]
459 fn partial_rows_zero_incomplete_samples() {
460 let src = pseudo(16, 3);
461 for (bits, order, len, complete) in [
462 (12, PackOrder::MsbBytes, 5, 3),
463 (14, PackOrder::LsbBytes, 7, 4),
464 (14, PackOrder::MsbBytes, 6, 3),
465 (16, PackOrder::Msb16, 5, 2),
466 (12, PackOrder::Msb32, 7, 2),
467 ] {
468 let mut full = vec![0u16; 8];
469 unpack_row_scalar(&src, &mut full, bits, order);
470 let mut part = vec![0xFFFFu16; 8];
471 assert_eq!(unpack_row_partial(&src[..len], &mut part, bits, order, true), complete);
472 assert_eq!(&part[..complete], &full[..complete], "{bits} {order:?}");
473 assert!(part[complete..].iter().all(|&v| v == 0), "{bits} {order:?}");
474 }
475 }
476}