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 let workers = if bits == 16 && cfg!(target_os = "macos") { 1 } else { par::memory_bound_workers() };
81 par::for_each_row_chunk_bounded(dst, dst_stride, workers, parallel, body);
82 Ok(())
83}
84
85pub fn unpack_row(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder, simd: bool) {
87 match (bits, order) {
88 (16, PackOrder::LsbBytes) => unpack_u16le(src, dst),
89 (16, PackOrder::MsbBytes) => unpack_u16be(src, dst),
90 (8, PackOrder::MsbBytes | PackOrder::LsbBytes) => unpack_u8(src, dst),
91 (12, PackOrder::MsbBytes) => unpack12_msb(src, dst, simd),
92 (12, PackOrder::LsbBytes) => unpack12_lsb(src, dst, simd),
93 (14, PackOrder::MsbBytes) => unpack14_msb(src, dst),
94 (14, PackOrder::LsbBytes) => unpack14_lsb(src, dst),
95 (10, PackOrder::MsbBytes) => unpack10_msb(src, dst),
96 (10, PackOrder::LsbBytes) => unpack10_lsb(src, dst),
97 _ => unpack_row_scalar(src, dst, bits, order),
98 }
99}
100
101pub fn complete_samples(len: usize, bits: u32, order: PackOrder) -> usize {
104 let usable = match order {
105 PackOrder::Msb16 => len / 2 * 2,
106 PackOrder::Msb32 => len / 4 * 4,
107 PackOrder::MsbBytes | PackOrder::LsbBytes => len,
108 };
109 if bits == 0 { 0 } else { usable * 8 / bits as usize }
110}
111
112pub fn unpack_row_partial(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder, simd: bool) -> usize {
116 let n = complete_samples(src.len(), bits, order).min(dst.len());
117 let (done, rest) = dst.split_at_mut(n);
118 unpack_row(src, done, bits, order, simd);
119 rest.fill(0);
120 n
121}
122
123pub fn unpack_row_scalar(src: &[u8], dst: &mut [u16], bits: u32, order: PackOrder) {
125 match order {
126 PackOrder::MsbBytes => {
127 let mut p = BitPumpMsb::new(src);
128 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
129 }
130 PackOrder::LsbBytes => {
131 let mut p = BitPumpLsb::new(src);
132 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
133 }
134 PackOrder::Msb16 => {
135 let mut p = BitPumpMsb16::new(src);
136 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
137 }
138 PackOrder::Msb32 => {
139 let mut p = BitPumpMsb32::new(src);
140 dst.iter_mut().for_each(|d| *d = p.get(bits) as u16);
141 }
142 }
143}
144
145fn unpack_u16le(src: &[u8], dst: &mut [u16]) {
146 let n = dst.len().min(src.len() / 2);
147 #[cfg(target_endian = "little")]
148 {
149 let bytes = unsafe { std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<u8>(), n * 2) };
152 bytes.copy_from_slice(&src[..n * 2]);
153 }
154 #[cfg(not(target_endian = "little"))]
155 for (d, s) in dst[..n].iter_mut().zip(src.chunks_exact(2)) {
156 *d = u16::from_le_bytes([s[0], s[1]]);
157 }
158 tail(src, dst, n, 16, PackOrder::LsbBytes);
159}
160
161fn unpack_u16be(src: &[u8], dst: &mut [u16]) {
162 let n = dst.len().min(src.len() / 2);
163 for (d, s) in dst[..n].iter_mut().zip(src.chunks_exact(2)) {
164 *d = u16::from_be_bytes([s[0], s[1]]);
165 }
166 tail(src, dst, n, 16, PackOrder::MsbBytes);
167}
168
169fn unpack_u8(src: &[u8], dst: &mut [u16]) {
170 let n = dst.len().min(src.len());
171 for (d, &s) in dst[..n].iter_mut().zip(src) {
172 *d = s as u16;
173 }
174 dst[n..].iter_mut().for_each(|d| *d = 0);
176}
177
178#[inline]
180fn tail(src: &[u8], dst: &mut [u16], done: usize, bits: u32, order: PackOrder) {
181 if done < dst.len() {
182 let byte = done * bits as usize / 8; unpack_row_scalar(src.get(byte..).unwrap_or(&[]), &mut dst[done..], bits, order);
184 }
185}
186
187fn unpack12_msb(src: &[u8], dst: &mut [u16], simd: bool) {
188 let mut done = 0;
189 if simd {
190 done = simd12::unpack(src, dst, true);
191 }
192 let groups = ((dst.len() - done) / 2).min(src.len().saturating_sub(done / 2 * 3) / 3);
193 for g in 0..groups {
194 let s = &src[(done / 2 + g) * 3..];
195 let (b0, b1, b2) = (s[0] as u16, s[1] as u16, s[2] as u16);
196 dst[done + 2 * g] = (b0 << 4) | (b1 >> 4);
197 dst[done + 2 * g + 1] = ((b1 & 0xF) << 8) | b2;
198 }
199 tail(src, dst, done + groups * 2, 12, PackOrder::MsbBytes);
200}
201
202fn unpack12_lsb(src: &[u8], dst: &mut [u16], simd: bool) {
203 let mut done = 0;
204 if simd {
205 done = simd12::unpack(src, dst, false);
206 }
207 let groups = ((dst.len() - done) / 2).min(src.len().saturating_sub(done / 2 * 3) / 3);
208 for g in 0..groups {
209 let s = &src[(done / 2 + g) * 3..];
210 let (b0, b1, b2) = (s[0] as u16, s[1] as u16, s[2] as u16);
211 dst[done + 2 * g] = b0 | ((b1 & 0xF) << 8);
212 dst[done + 2 * g + 1] = (b1 >> 4) | (b2 << 4);
213 }
214 tail(src, dst, done + groups * 2, 12, PackOrder::LsbBytes);
215}
216
217macro_rules! word_unpack {
218 ($name:ident, $bits:expr, $bytes:expr, $n:expr, $be:expr, $order:expr) => {
219 fn $name(src: &[u8], dst: &mut [u16]) {
220 let groups = (dst.len() / $n).min(src.len().saturating_sub(8 - $bytes) / $bytes);
222 for g in 0..groups {
223 let s = &src[g * $bytes..g * $bytes + 8];
224 let w = if $be {
225 u64::from_be_bytes(s.try_into().unwrap())
226 } else {
227 u64::from_le_bytes(s.try_into().unwrap())
228 };
229 let m = (1u64 << $bits) - 1;
230 for k in 0..$n {
231 dst[g * $n + k] =
232 if $be { (w >> (64 - $bits * (k + 1))) & m } else { (w >> ($bits * k)) & m } as u16;
233 }
234 }
235 tail(src, dst, groups * $n, $bits, $order);
236 }
237 };
238}
239
240word_unpack!(unpack14_msb, 14, 7, 4, true, PackOrder::MsbBytes);
241word_unpack!(unpack14_lsb, 14, 7, 4, false, PackOrder::LsbBytes);
242word_unpack!(unpack10_msb, 10, 5, 4, true, PackOrder::MsbBytes);
243word_unpack!(unpack10_lsb, 10, 5, 4, false, PackOrder::LsbBytes);
244
245#[allow(dead_code)] mod simd12 {
248 pub(super) const SHUF_MSB: [u8; 16] = [1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10];
250 pub(super) const SHUF_LSB: [u8; 16] = [0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11];
252
253 #[allow(unreachable_code)]
255 pub fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
256 #[cfg(target_arch = "x86_64")]
257 {
258 if std::arch::is_x86_feature_detected!("sse4.1") && std::arch::is_x86_feature_detected!("ssse3") {
259 return unsafe { x86::unpack(src, dst, msb) };
261 }
262 return 0;
263 }
264 #[cfg(target_arch = "aarch64")]
265 {
266 return unsafe { neon::unpack(src, dst, msb) };
268 }
269 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
270 {
271 return wasm::unpack(src, dst, msb);
272 }
273 let _ = (src, dst, msb);
274 0
275 }
276
277 #[inline]
279 pub(super) fn groups(src: &[u8], dst: &[u16]) -> usize {
280 (dst.len() / 8).min(src.len().saturating_sub(4) / 12)
281 }
282
283 #[cfg(target_arch = "x86_64")]
284 mod x86 {
285 use std::arch::x86_64::*;
286
287 #[target_feature(enable = "ssse3,sse4.1")]
290 pub unsafe fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
291 let n = super::groups(src, dst);
292 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
293 unsafe {
297 let shuf = _mm_loadu_si128(shuf.as_ptr() as *const __m128i);
298 let mask = _mm_set1_epi16(0x0FFF);
299 for g in 0..n {
300 let v = _mm_loadu_si128(src.as_ptr().add(g * 12) as *const __m128i);
301 let v = _mm_shuffle_epi8(v, shuf);
302 let shifted = _mm_srli_epi16(v, 4);
303 let masked = _mm_and_si128(v, mask);
304 let out = if msb {
306 _mm_blend_epi16(masked, shifted, 0b0101_0101)
307 } else {
308 _mm_blend_epi16(masked, shifted, 0b1010_1010)
309 };
310 _mm_storeu_si128(dst.as_mut_ptr().add(g * 8) as *mut __m128i, out);
311 }
312 }
313 n * 8
314 }
315 }
316
317 #[cfg(target_arch = "aarch64")]
318 mod neon {
319 use std::arch::aarch64::*;
320
321 #[target_feature(enable = "neon")]
324 pub unsafe fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
325 let n = super::groups(src, dst);
326 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
327 let even_sel: [u16; 8] = if msb {
328 [0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0]
329 } else {
330 [0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF]
331 };
332 unsafe {
334 let tbl = vld1q_u8(shuf.as_ptr());
335 let sel = vld1q_u16(even_sel.as_ptr());
336 let mask = vdupq_n_u16(0x0FFF);
337 for g in 0..n {
338 let v = vld1q_u8(src.as_ptr().add(g * 12));
339 let v = vreinterpretq_u16_u8(vqtbl1q_u8(v, tbl));
340 let shifted = vshrq_n_u16(v, 4);
341 let masked = vandq_u16(v, mask);
342 let out = vbslq_u16(sel, shifted, masked);
343 vst1q_u16(dst.as_mut_ptr().add(g * 8), out);
344 }
345 }
346 n * 8
347 }
348 }
349
350 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
351 mod wasm {
352 use core::arch::wasm32::*;
353
354 pub fn unpack(src: &[u8], dst: &mut [u16], msb: bool) -> usize {
355 let n = super::groups(src, dst);
356 let shuf = if msb { super::SHUF_MSB } else { super::SHUF_LSB };
357 let sel = if msb {
358 u16x8(0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0)
359 } else {
360 u16x8(0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF, 0, 0xFFFF)
361 };
362 unsafe {
364 let tbl = v128_load(shuf.as_ptr() as *const v128);
365 let mask = u16x8_splat(0x0FFF);
366 for g in 0..n {
367 let v = v128_load(src.as_ptr().add(g * 12) as *const v128);
368 let v = i8x16_swizzle(v, tbl);
369 let shifted = u16x8_shr(v, 4);
370 let masked = v128_and(v, mask);
371 v128_store(dst.as_mut_ptr().add(g * 8) as *mut v128, v128_bitselect(shifted, masked, sel));
372 }
373 }
374 n * 8
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 fn pseudo(n: usize, seed: u64) -> Vec<u8> {
384 let mut x = seed | 1;
385 (0..n)
386 .map(|_| {
387 x ^= x << 13;
388 x ^= x >> 7;
389 x ^= x << 17;
390 x as u8
391 })
392 .collect()
393 }
394
395 #[test]
396 fn fast_paths_match_reference() {
397 for bits in 1..=16u32 {
398 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes, PackOrder::Msb16, PackOrder::Msb32] {
399 for width in [1usize, 7, 8, 15, 16, 17, 63, 64, 333] {
400 let src = pseudo((width * bits as usize).div_ceil(8) + 3, bits as u64 * 1000 + width as u64);
401 let mut a = vec![0u16; width];
402 let mut b = vec![0u16; width];
403 let mut c = vec![0u16; width];
404 unpack_row_scalar(&src, &mut a, bits, order);
405 unpack_row(&src, &mut b, bits, order, true);
406 unpack_row(&src, &mut c, bits, order, false);
407 assert_eq!(a, b, "bits {bits} {order:?} width {width} simd");
408 assert_eq!(a, c, "bits {bits} {order:?} width {width} nosimd");
409 }
410 }
411 }
412 }
413
414 #[test]
415 fn short_sources_match_reference() {
416 for bits in 1..=16u32 {
418 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes, PackOrder::Msb16, PackOrder::Msb32] {
419 for len in 0..12usize {
420 let src = pseudo(len, bits as u64 + len as u64);
421 let mut a = vec![0xAAAAu16; 31];
422 let mut b = vec![0x5555u16; 31];
423 unpack_row_scalar(&src, &mut a, bits, order);
424 unpack_row(&src, &mut b, bits, order, true);
425 assert_eq!(a, b, "bits {bits} {order:?} len {len}");
426 }
427 }
428 }
429 }
430
431 #[test]
432 fn exact_length_sources() {
433 for bits in [10u32, 12, 14, 16] {
435 for width in [8usize, 16, 24, 32, 40, 48] {
436 let src = pseudo(width * bits as usize / 8, 9);
437 for order in [PackOrder::MsbBytes, PackOrder::LsbBytes] {
438 let mut a = vec![0u16; width];
439 let mut b = vec![0u16; width];
440 unpack_row_scalar(&src, &mut a, bits, order);
441 unpack_row(&src, &mut b, bits, order, true);
442 assert_eq!(a, b);
443 }
444 }
445 }
446 }
447
448 #[test]
449 fn whole_image_with_stride_and_truncation() {
450 let (w, h) = (100usize, 50usize);
451 let layout = PackedLayout { width: w, height: h, bits: 12, order: PackOrder::MsbBytes, row_stride: 160 };
452 let src = pseudo(160 * h, 5);
453 let mut dst = vec![0u16; w * h];
454 unpack(&src, layout, &mut dst, w, true, true, false).unwrap();
455 for y in 0..h {
456 let mut r = vec![0u16; w];
457 unpack_row_scalar(&src[y * 160..], &mut r, 12, PackOrder::MsbBytes);
458 assert_eq!(&dst[y * w..(y + 1) * w], &r[..]);
459 }
460 assert!(unpack(&src[..160 * 10], layout, &mut dst, w, true, true, false).is_err());
461 assert!(unpack(&src[..160 * 10], layout, &mut dst, w, true, true, true).is_ok());
462 let mut part = vec![0u16; w * h];
464 unpack(&src[..160 * 10 + 7], layout, &mut part, w, true, true, true).unwrap();
465 assert_eq!(&part[..w * 10], &dst[..w * 10]);
466 assert_eq!(&part[w * 10..w * 10 + 4], &dst[w * 10..w * 10 + 4]);
467 assert!(part[w * 10 + 4..].iter().all(|&v| v == 0));
468 }
469
470 #[test]
471 fn partial_rows_zero_incomplete_samples() {
472 let src = pseudo(16, 3);
473 for (bits, order, len, complete) in [
474 (12, PackOrder::MsbBytes, 5, 3),
475 (14, PackOrder::LsbBytes, 7, 4),
476 (14, PackOrder::MsbBytes, 6, 3),
477 (16, PackOrder::Msb16, 5, 2),
478 (12, PackOrder::Msb32, 7, 2),
479 ] {
480 let mut full = vec![0u16; 8];
481 unpack_row_scalar(&src, &mut full, bits, order);
482 let mut part = vec![0xFFFFu16; 8];
483 assert_eq!(unpack_row_partial(&src[..len], &mut part, bits, order, true), complete);
484 assert_eq!(&part[..complete], &full[..complete], "{bits} {order:?}");
485 assert!(part[complete..].iter().all(|&v| v == 0), "{bits} {order:?}");
486 }
487 }
488}