1use std::ops::Index;
12
13use bitvec::order::Msb0;
14use bitvec::slice::BitSlice;
15use bitvec::view::BitView;
16use num_complex::Complex;
17
18use crate::data::U16_OFFSET;
19use crate::data::U32_OFFSET;
20use crate::data::U64_OFFSET;
21use crate::data::UnsignedView;
22use crate::endian::decode_be;
23use crate::error::FitsError;
24use crate::error::Result;
25use crate::header::Header;
26use crate::keyword::key;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum TformKind {
32 Logical,
34 Bit,
36 Byte,
38 I16,
40 I32,
42 I64,
44 Char,
46 F32,
48 F64,
50 ComplexF32,
52 ComplexF64,
54 ArrayDesc32,
56 ArrayDesc64,
58}
59
60impl TformKind {
61 fn from_code(code: u8) -> Option<TformKind> {
62 Some(match code {
63 b'L' => TformKind::Logical,
64 b'X' => TformKind::Bit,
65 b'B' => TformKind::Byte,
66 b'I' => TformKind::I16,
67 b'J' => TformKind::I32,
68 b'K' => TformKind::I64,
69 b'A' => TformKind::Char,
70 b'E' => TformKind::F32,
71 b'D' => TformKind::F64,
72 b'C' => TformKind::ComplexF32,
73 b'M' => TformKind::ComplexF64,
74 b'P' => TformKind::ArrayDesc32,
75 b'Q' => TformKind::ArrayDesc64,
76 _ => return None,
77 })
78 }
79
80 pub fn code(self) -> char {
82 match self {
83 TformKind::Logical => 'L',
84 TformKind::Bit => 'X',
85 TformKind::Byte => 'B',
86 TformKind::I16 => 'I',
87 TformKind::I32 => 'J',
88 TformKind::I64 => 'K',
89 TformKind::Char => 'A',
90 TformKind::F32 => 'E',
91 TformKind::F64 => 'D',
92 TformKind::ComplexF32 => 'C',
93 TformKind::ComplexF64 => 'M',
94 TformKind::ArrayDesc32 => 'P',
95 TformKind::ArrayDesc64 => 'Q',
96 }
97 }
98
99 pub(crate) fn elem_size(self) -> usize {
102 match self {
103 TformKind::Logical | TformKind::Bit | TformKind::Byte | TformKind::Char => 1,
104 TformKind::I16 => 2,
105 TformKind::I32 | TformKind::F32 => 4,
106 TformKind::I64 | TformKind::F64 | TformKind::ComplexF32 | TformKind::ArrayDesc32 => 8,
107 TformKind::ComplexF64 | TformKind::ArrayDesc64 => 16,
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct Tform {
117 pub repeat: usize,
118 pub kind: TformKind,
119 pub vla_elem: Option<TformKind>,
122}
123
124impl Tform {
125 pub fn parse(value: &str) -> Result<Tform> {
127 let s = value.trim();
128 let invalid = || FitsError::InvalidTform {
129 tform: value.to_string(),
130 };
131 let pos = s
132 .bytes()
133 .position(|b| b.is_ascii_alphabetic())
134 .ok_or_else(invalid)?;
135 let repeat = if pos == 0 {
136 1
137 } else {
138 s[..pos].parse().map_err(|_| invalid())?
139 };
140 let kind = TformKind::from_code(s.as_bytes()[pos]).ok_or_else(invalid)?;
141 let vla_elem = if matches!(kind, TformKind::ArrayDesc32 | TformKind::ArrayDesc64) {
143 let elem = s.as_bytes().get(pos + 1).copied().ok_or_else(invalid)?;
144 if repeat > 1 {
146 return Err(invalid());
147 }
148 Some(TformKind::from_code(elem).ok_or_else(invalid)?)
149 } else {
150 None
151 };
152 Ok(Tform {
153 repeat,
154 kind,
155 vla_elem,
156 })
157 }
158
159 pub fn byte_width(self) -> usize {
161 match self.kind {
162 TformKind::Bit => self.repeat.div_ceil(8),
163 _ => self.repeat.saturating_mul(self.kind.elem_size()),
167 }
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum TDispKind {
174 Char,
176 Logical,
178 Integer,
180 Binary,
182 Octal,
184 Hex,
186 Float,
188 Exponential,
190 Engineering,
192 Scientific,
194 General,
196 Double,
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub struct TDisp {
204 pub kind: TDispKind,
205 pub width: usize,
206 pub decimals: Option<usize>,
207 pub exponent: Option<usize>,
208}
209
210impl TDisp {
211 pub fn parse(s: &str) -> Option<TDisp> {
214 let s = s.trim().to_ascii_uppercase();
215 let (kind, rest) = if let Some(r) = s.strip_prefix("EN") {
216 (TDispKind::Engineering, r)
217 } else if let Some(r) = s.strip_prefix("ES") {
218 (TDispKind::Scientific, r)
219 } else {
220 let kind = match s.bytes().next()? {
221 b'A' => TDispKind::Char,
222 b'L' => TDispKind::Logical,
223 b'I' => TDispKind::Integer,
224 b'B' => TDispKind::Binary,
225 b'O' => TDispKind::Octal,
226 b'Z' => TDispKind::Hex,
227 b'F' => TDispKind::Float,
228 b'E' => TDispKind::Exponential,
229 b'G' => TDispKind::General,
230 b'D' => TDispKind::Double,
231 _ => return None,
232 };
233 (kind, &s[1..])
234 };
235 let (main, exponent) = match rest.split_once('E') {
237 Some((m, e)) => (m, Some(e.parse().ok()?)),
238 None => (rest, None),
239 };
240 let (width, decimals) = match main.split_once('.') {
241 Some((w, d)) => (w, Some(d.parse().ok()?)),
242 None => (main, None),
243 };
244 Some(TDisp {
245 kind,
246 width: width.parse().ok()?,
247 decimals,
248 exponent,
249 })
250 }
251}
252
253#[derive(Debug, Clone)]
256pub struct Column {
257 pub name: Option<String>,
258 pub unit: Option<String>,
259 pub tform: Tform,
260 pub tscale: f64,
262 pub tzero: f64,
264 pub tnull: Option<i64>,
266 pub tdim: Option<Vec<usize>>,
269 pub tdisp: Option<TDisp>,
271 pub byte_offset: usize,
273}
274
275#[derive(Debug, Clone, PartialEq)]
280pub enum ColumnData {
281 Logical(Vec<Option<bool>>),
283 Bytes(Vec<u8>),
285 I16(Vec<i16>),
286 I32(Vec<i32>),
287 I64(Vec<i64>),
288 F32(Vec<f32>),
289 F64(Vec<f64>),
290 ComplexF32(Vec<Complex<f32>>),
291 ComplexF64(Vec<Complex<f64>>),
292 Text(Vec<String>),
294}
295
296impl ColumnData {
297 pub fn element_count(&self) -> usize {
299 match self {
300 ColumnData::Logical(v) => v.len(),
301 ColumnData::Bytes(v) => v.len(),
302 ColumnData::I16(v) => v.len(),
303 ColumnData::I32(v) => v.len(),
304 ColumnData::I64(v) => v.len(),
305 ColumnData::F32(v) => v.len(),
306 ColumnData::F64(v) => v.len(),
307 ColumnData::ComplexF32(v) => v.len(),
308 ColumnData::ComplexF64(v) => v.len(),
309 ColumnData::Text(v) => v.len(),
310 }
311 }
312}
313
314#[derive(Debug, Clone)]
316pub struct BinTable {
317 pub nrows: usize,
318 pub columns: Vec<Column>,
319 pub(crate) row_len: usize,
320 heap_offset: usize,
322 heap_end: usize,
325 bytes: Vec<u8>,
329}
330
331impl BinTable {
332 pub(crate) fn from_data(header: &Header, data: Vec<u8>) -> Result<BinTable> {
335 let row_len = header
336 .get_integer("NAXIS1")
337 .ok_or(FitsError::MissingKeyword { name: "NAXIS1" })?
338 .max(0) as usize;
339 let nrows = header
340 .get_integer("NAXIS2")
341 .ok_or(FitsError::MissingKeyword { name: "NAXIS2" })?
342 .max(0) as usize;
343 let tfields = match header.get_integer("TFIELDS") {
346 Some(t) if (0..=999).contains(&t) => t as usize,
347 Some(_) => return Err(FitsError::KeywordOutOfRange { name: "TFIELDS" }),
348 None => return Err(FitsError::MissingKeyword { name: "TFIELDS" }),
349 };
350
351 let mut columns = Vec::with_capacity(tfields);
352 let mut offset = 0;
353 for n in 1..=tfields {
354 let tform_value = header
355 .get_text(key!("TFORM{n}").as_str())
356 .ok_or(FitsError::MissingKeyword { name: "TFORMn" })?;
357 let tform = Tform::parse(tform_value)?;
358 let tdim = header
359 .get_text(key!("TDIM{n}").as_str())
360 .and_then(parse_tdim);
361 let is_vla = matches!(tform.kind, TformKind::ArrayDesc32 | TformKind::ArrayDesc64);
367 if let Some(dims) = &tdim
368 && !is_vla
369 && dims.iter().try_fold(1usize, |a, &x| a.checked_mul(x)) != Some(tform.repeat)
370 {
371 return Err(FitsError::KeywordOutOfRange { name: "TDIMn" });
372 }
373 columns.push(Column {
374 name: header
375 .get_text(key!("TTYPE{n}").as_str())
376 .map(str::to_string)
377 .filter(|s| !s.is_empty()),
378 unit: header
379 .get_text(key!("TUNIT{n}").as_str())
380 .map(str::to_string)
381 .filter(|s| !s.is_empty()),
382 tform,
383 tscale: header.get_real(key!("TSCAL{n}").as_str()).unwrap_or(1.0),
384 tzero: header.get_real(key!("TZERO{n}").as_str()).unwrap_or(0.0),
385 tnull: header.get_integer(key!("TNULL{n}").as_str()),
386 tdim,
387 tdisp: header
388 .get_text(key!("TDISP{n}").as_str())
389 .and_then(TDisp::parse),
390 byte_offset: offset,
391 });
392 offset = offset.saturating_add(tform.byte_width());
393 }
394 if offset != row_len {
395 return Err(FitsError::RowWidthMismatch {
396 computed: offset,
397 declared: row_len,
398 });
399 }
400
401 let main_table = nrows.checked_mul(row_len).ok_or(FitsError::UnexpectedEof)?;
404 if data.len() < main_table {
405 return Err(FitsError::UnexpectedEof);
406 }
407 let heap_offset = header
408 .get_integer("THEAP")
409 .map_or(main_table, |t| t.max(0) as usize);
410 if heap_offset < main_table {
412 return Err(FitsError::KeywordOutOfRange { name: "THEAP" });
413 }
414 let pcount = header
417 .get_integer("PCOUNT")
418 .map_or(0, |p| p.max(0) as usize);
419 let heap_end = main_table
420 .checked_add(pcount)
421 .ok_or(FitsError::UnexpectedEof)?
422 .min(data.len());
423 Ok(BinTable {
424 nrows,
425 columns,
426 row_len,
427 heap_offset,
428 heap_end,
429 bytes: data,
430 })
431 }
432
433 #[cfg(feature = "compression")]
435 pub(crate) fn raw_rows(&self) -> &[u8] {
436 &self.bytes[..self.nrows * self.row_len]
437 }
438
439 pub fn column_index(&self, name: &str) -> Option<usize> {
442 self.columns.iter().position(|c| {
443 c.name
444 .as_deref()
445 .is_some_and(|n| n.eq_ignore_ascii_case(name))
446 })
447 }
448
449 fn column_index_checked(&self, name: &str) -> Result<usize> {
450 self.column_index(name)
451 .ok_or_else(|| FitsError::ColumnNotFound {
452 name: name.to_string(),
453 })
454 }
455
456 pub fn column_by_idx(&self, index: usize) -> Result<ColumnReader<'_>> {
461 if index >= self.columns.len() {
462 return Err(FitsError::ColumnIndexOutOfBounds {
463 index,
464 len: self.columns.len(),
465 });
466 }
467 Ok(ColumnReader { table: self, index })
468 }
469
470 pub fn column_by_name(&self, name: &str) -> Result<ColumnReader<'_>> {
473 let index = self.column_index_checked(name)?;
474 Ok(ColumnReader { table: self, index })
475 }
476
477 fn bounded_heap(&self, offset: usize, nbytes: usize) -> Result<&[u8]> {
481 let start = self
482 .heap_offset
483 .checked_add(offset)
484 .ok_or(FitsError::UnexpectedEof)?;
485 let end = start.checked_add(nbytes).ok_or(FitsError::UnexpectedEof)?;
486 if end > self.heap_end {
487 return Err(FitsError::UnexpectedEof);
488 }
489 self.bytes.get(start..end).ok_or(FitsError::UnexpectedEof)
490 }
491
492 fn cell(&self, col: &Column, r: usize) -> &[u8] {
494 let start = r * self.row_len + col.byte_offset;
495 &self.bytes[start..start + col.tform.byte_width()]
496 }
497
498 fn flatten(&self, col: &Column) -> Vec<u8> {
500 let mut out = Vec::with_capacity(self.nrows * col.tform.byte_width());
501 for r in 0..self.nrows {
502 out.extend_from_slice(self.cell(col, r));
503 }
504 out
505 }
506}
507
508#[derive(Debug, Clone, Copy)]
516pub struct ColumnReader<'a> {
517 table: &'a BinTable,
518 index: usize,
519}
520
521impl<'a> ColumnReader<'a> {
522 pub fn descriptor(&self) -> &'a Column {
525 &self.table.columns[self.index]
526 }
527
528 pub fn raw(&self) -> Result<ColumnData> {
533 let col = self.descriptor();
534 if matches!(
535 col.tform.kind,
536 TformKind::ArrayDesc32 | TformKind::ArrayDesc64
537 ) {
538 return Err(FitsError::VariableLengthColumn {
539 code: col.tform.kind.code(),
540 });
541 }
542 Ok(if col.tform.kind == TformKind::Char {
543 ColumnData::Text(
544 (0..self.table.nrows)
545 .map(|r| trim_text(self.table.cell(col, r)))
546 .collect(),
547 )
548 } else {
549 decode_array(col.tform.kind, &self.table.flatten(col))
550 })
551 }
552
553 pub fn physical(&self) -> Result<Vec<f64>> {
557 let col = self.descriptor();
558 column_data_physical(
559 &self.raw()?,
560 col.tform.kind,
561 col.tscale,
562 col.tzero,
563 col.tnull,
564 )
565 }
566
567 pub fn unsigned(&self) -> Result<Option<UnsignedView>> {
573 let col = self.descriptor();
574 if col.tscale != 1.0 || col.tnull.is_some() {
575 return Ok(None);
576 }
577 let tzero = col.tzero;
578 Ok(match (self.raw()?, col.tform.kind) {
579 (ColumnData::Bytes(v), TformKind::Byte) if tzero == -128.0 => {
580 Some(UnsignedView::from_signed_byte(&v))
581 }
582 (ColumnData::I16(v), _) if tzero == U16_OFFSET => {
583 Some(UnsignedView::from_offset_i16(&v))
584 }
585 (ColumnData::I32(v), _) if tzero == U32_OFFSET => {
586 Some(UnsignedView::from_offset_i32(&v))
587 }
588 (ColumnData::I64(v), _) if tzero == U64_OFFSET => {
589 Some(UnsignedView::from_offset_i64(&v))
590 }
591 _ => None,
592 })
593 }
594
595 pub fn complex(&self) -> Result<Vec<Complex<f64>>> {
598 let col = self.descriptor();
599 let scale = |re: f64, im: f64| Complex {
600 re: col.tzero + col.tscale * re,
601 im: col.tzero + col.tscale * im,
602 };
603 Ok(match self.raw()? {
604 ColumnData::ComplexF32(v) => v
605 .iter()
606 .map(|&Complex { re, im }| scale(re as f64, im as f64))
607 .collect(),
608 ColumnData::ComplexF64(v) => {
609 v.iter().map(|&Complex { re, im }| scale(re, im)).collect()
610 }
611 _ => {
612 return Err(FitsError::NotAComplexColumn {
613 code: col.tform.kind.code(),
614 });
615 }
616 })
617 }
618
619 pub fn bits(&self) -> Result<BitColumn<'a>> {
623 let col = self.descriptor();
624 if col.tform.kind != TformKind::Bit {
625 return Err(FitsError::NotABitColumn {
626 code: col.tform.kind.code(),
627 });
628 }
629 Ok(BitColumn {
630 table: self.table,
631 index: self.index,
632 })
633 }
634
635 pub fn vla(&self) -> Result<Vec<ColumnData>> {
639 let col = self.descriptor();
640 let (elem, wide) = match (col.tform.kind, col.tform.vla_elem) {
641 (TformKind::ArrayDesc32, Some(e)) => (e, false),
642 (TformKind::ArrayDesc64, Some(e)) => (e, true),
643 _ => {
644 return Err(FitsError::NotAVla {
645 code: col.tform.kind.code(),
646 });
647 }
648 };
649 let mut out = Vec::with_capacity(self.table.nrows);
650 for r in 0..self.table.nrows {
651 let d = decode_descriptor(self.table.cell(col, r), wide);
652 let nbytes = match elem {
653 TformKind::Bit => d.nelem.div_ceil(8),
654 _ => d
655 .nelem
656 .checked_mul(elem.elem_size())
657 .ok_or(FitsError::UnexpectedEof)?,
658 };
659 out.push(decode_array(
660 elem,
661 self.table.bounded_heap(d.offset, nbytes)?,
662 ));
663 }
664 Ok(out)
665 }
666
667 pub fn vla_physical(&self) -> Result<Vec<Vec<f64>>> {
671 let rows = self.vla()?; let col = self.descriptor();
673 let elem = col
674 .tform
675 .vla_elem
676 .expect("vla() succeeded ⇒ vla_elem is Some");
677 rows.iter()
678 .map(|row| column_data_physical(row, elem, col.tscale, col.tzero, col.tnull))
679 .collect()
680 }
681
682 pub fn vla_bits(&self) -> Result<BitColumn<'a>> {
687 let col = self.descriptor();
688 let wide = match (col.tform.kind, col.tform.vla_elem) {
689 (TformKind::ArrayDesc32, Some(TformKind::Bit)) => false,
690 (TformKind::ArrayDesc64, Some(TformKind::Bit)) => true,
691 _ => {
692 return Err(FitsError::NotABitColumn {
693 code: col.tform.kind.code(),
694 });
695 }
696 };
697 for r in 0..self.table.nrows {
700 let d = decode_descriptor(self.table.cell(col, r), wide);
701 self.table.bounded_heap(d.offset, d.nelem.div_ceil(8))?;
702 }
703 Ok(BitColumn {
704 table: self.table,
705 index: self.index,
706 })
707 }
708}
709
710#[derive(Debug, Clone, Copy)]
728pub struct BitColumn<'a> {
729 table: &'a BinTable,
730 index: usize,
731}
732
733impl<'a> BitColumn<'a> {
734 pub fn nrows(&self) -> usize {
736 self.table.nrows
737 }
738
739 pub fn is_empty(&self) -> bool {
741 self.table.nrows == 0
742 }
743
744 pub fn row(&self, r: usize) -> &'a BitSlice<u8, Msb0> {
748 assert!(
749 r < self.table.nrows,
750 "row {r} out of bounds ({} rows)",
751 self.table.nrows
752 );
753 let col = &self.table.columns[self.index];
754 if col.tform.kind == TformKind::Bit {
755 &self.table.cell(col, r).view_bits::<Msb0>()[..col.tform.repeat]
757 } else {
758 let wide = col.tform.kind == TformKind::ArrayDesc64;
761 let d = decode_descriptor(self.table.cell(col, r), wide);
762 let cell = self
763 .table
764 .bounded_heap(d.offset, d.nelem.div_ceil(8))
765 .expect("vla_bits validated every heap span");
766 &cell.view_bits::<Msb0>()[..d.nelem]
767 }
768 }
769
770 pub fn get(&self, row: usize, col: usize) -> Option<bool> {
772 if row >= self.table.nrows {
773 return None;
774 }
775 let bits = self.row(row);
776 (col < bits.len()).then(|| bits[col])
777 }
778
779 pub fn iter(&self) -> impl ExactSizeIterator<Item = &'a BitSlice<u8, Msb0>> + '_ {
781 (0..self.table.nrows).map(move |r| self.row(r))
782 }
783}
784
785impl Index<usize> for BitColumn<'_> {
788 type Output = BitSlice<u8, Msb0>;
789
790 fn index(&self, row: usize) -> &BitSlice<u8, Msb0> {
791 self.row(row)
792 }
793}
794
795impl Index<(usize, usize)> for BitColumn<'_> {
798 type Output = bool;
799
800 fn index(&self, (row, col): (usize, usize)) -> &bool {
801 &self.row(row)[col]
802 }
803}
804
805fn parse_tdim(value: &str) -> Option<Vec<usize>> {
807 let inner = value.trim().strip_prefix('(')?.strip_suffix(')')?;
808 inner
809 .split(',')
810 .map(|s| s.trim().parse::<usize>().ok())
811 .collect()
812}
813
814fn column_data_physical(
819 data: &ColumnData,
820 kind: TformKind,
821 tscale: f64,
822 tzero: f64,
823 tnull: Option<i64>,
824) -> Result<Vec<f64>> {
825 let scale = |x: f64| tzero + tscale * x;
826 let scaled_int = |xi: i64| {
827 if tnull == Some(xi) {
828 f64::NAN
829 } else {
830 scale(xi as f64)
831 }
832 };
833 Ok(match data {
834 ColumnData::Bytes(v) if kind == TformKind::Byte => {
835 v.iter().map(|&b| scaled_int(b as i64)).collect()
836 }
837 ColumnData::I16(v) => v.iter().map(|&x| scaled_int(x as i64)).collect(),
838 ColumnData::I32(v) => v.iter().map(|&x| scaled_int(x as i64)).collect(),
839 ColumnData::I64(v) => v.iter().map(|&x| scaled_int(x)).collect(),
840 ColumnData::F32(v) => v.iter().map(|&x| scale(x as f64)).collect(),
841 ColumnData::F64(v) => v.iter().map(|&x| scale(x)).collect(),
842 _ => return Err(FitsError::NonNumericColumn { code: kind.code() }),
843 })
844}
845
846fn decode_array(kind: TformKind, bytes: &[u8]) -> ColumnData {
849 match kind {
850 TformKind::Logical => ColumnData::Logical(
851 bytes
852 .iter()
853 .map(|&b| match b {
854 b'T' => Some(true),
855 b'F' => Some(false),
856 _ => None, })
858 .collect(),
859 ),
860 TformKind::Byte | TformKind::Bit => ColumnData::Bytes(bytes.to_vec()),
861 TformKind::Char => ColumnData::Text(vec![trim_text(bytes)]),
862 TformKind::I16 => ColumnData::I16(decode_be(bytes, i16::from_be_bytes)),
863 TformKind::I32 => ColumnData::I32(decode_be(bytes, i32::from_be_bytes)),
864 TformKind::I64 => ColumnData::I64(decode_be(bytes, i64::from_be_bytes)),
865 TformKind::F32 => ColumnData::F32(decode_be(bytes, f32::from_be_bytes)),
866 TformKind::F64 => ColumnData::F64(decode_be(bytes, f64::from_be_bytes)),
867 TformKind::ComplexF32 => ColumnData::ComplexF32(decode_be(bytes, |b: [u8; 8]| Complex {
868 re: f32::from_be_bytes([b[0], b[1], b[2], b[3]]),
869 im: f32::from_be_bytes([b[4], b[5], b[6], b[7]]),
870 })),
871 TformKind::ComplexF64 => ColumnData::ComplexF64(decode_be(bytes, |b: [u8; 16]| Complex {
872 re: f64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
873 im: f64::from_be_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]),
874 })),
875 TformKind::ArrayDesc32 | TformKind::ArrayDesc64 => ColumnData::Bytes(bytes.to_vec()),
877 }
878}
879
880fn trim_text(cell: &[u8]) -> String {
883 let nul = cell.iter().position(|&b| b == 0).unwrap_or(cell.len());
884 let head = &cell[..nul];
885 let end = head.iter().rposition(|&b| b != b' ').map_or(0, |i| i + 1);
886 String::from_utf8_lossy(&head[..end]).into_owned()
887}
888
889#[derive(Debug, Clone, Copy)]
892struct Descriptor {
893 nelem: usize,
894 offset: usize,
895}
896
897fn decode_descriptor(desc: &[u8], wide: bool) -> Descriptor {
900 if wide {
901 Descriptor {
902 nelem: be_u64(&desc[0..8]),
903 offset: be_u64(&desc[8..16]),
904 }
905 } else {
906 Descriptor {
907 nelem: be_u32(&desc[0..4]),
908 offset: be_u32(&desc[4..8]),
909 }
910 }
911}
912
913fn be_u32(b: &[u8]) -> usize {
917 u32::from_be_bytes([b[0], b[1], b[2], b[3]]) as usize
918}
919
920fn be_u64(b: &[u8]) -> usize {
921 usize::try_from(u64::from_be_bytes([
925 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
926 ]))
927 .unwrap_or(usize::MAX)
928}
929
930#[cfg(test)]
931mod tests;