Skip to main content

compression/
aa_header.rs

1use crate::{
2    aa_byte_stream::ArchiveFlags,
3    aa_field_key::{FieldKey, FieldKeySet},
4    ffi, util, CompressionError, Result,
5};
6use std::ffi::{c_void, CStr};
7use std::ptr::{null_mut, NonNull};
8
9/// Wraps AppleArchive hash function identifiers.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
11pub enum HashFunction {
12    /// Wraps the `Crc32` variant of `HashFunction`.
13    Crc32 = 1,
14    /// Wraps the `Sha1` variant of `HashFunction`.
15    Sha1 = 2,
16    /// Wraps the `Sha256` variant of `HashFunction`.
17    Sha256 = 3,
18    /// Wraps the `Sha384` variant of `HashFunction`.
19    Sha384 = 4,
20    /// Wraps the `Sha512` variant of `HashFunction`.
21    Sha512 = 5,
22}
23
24impl HashFunction {
25    /// Wraps the digest sizes produced by `AAHeaderGetFieldHash`.
26    pub const fn digest_len(self) -> usize {
27        match self {
28            Self::Crc32 => 4,
29            Self::Sha1 => 20,
30            Self::Sha256 => 32,
31            Self::Sha384 => 48,
32            Self::Sha512 => 64,
33        }
34    }
35
36    const fn from_raw(raw: u32) -> Option<Self> {
37        match raw {
38            1 => Some(Self::Crc32),
39            2 => Some(Self::Sha1),
40            3 => Some(Self::Sha256),
41            4 => Some(Self::Sha384),
42            5 => Some(Self::Sha512),
43            _ => None,
44        }
45    }
46
47    const fn as_raw(self) -> u32 {
48        self as u32
49    }
50}
51
52/// Wraps AppleArchive entry type identifiers.
53#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
54pub enum EntryType {
55    /// Wraps the `RegularFile` variant of `EntryType`.
56    RegularFile = 'F' as isize,
57    /// Wraps the `Directory` variant of `EntryType`.
58    Directory = 'D' as isize,
59    /// Wraps the `SymbolicLink` variant of `EntryType`.
60    SymbolicLink = 'L' as isize,
61    /// Wraps the `Fifo` variant of `EntryType`.
62    Fifo = 'P' as isize,
63    /// Wraps the `CharacterDevice` variant of `EntryType`.
64    CharacterDevice = 'C' as isize,
65    /// Wraps the `BlockDevice` variant of `EntryType`.
66    BlockDevice = 'B' as isize,
67    /// Wraps the `Socket` variant of `EntryType`.
68    Socket = 'S' as isize,
69    /// Wraps the `Whiteout` variant of `EntryType`.
70    Whiteout = 'W' as isize,
71    /// Wraps the `Door` variant of `EntryType`.
72    Door = 'R' as isize,
73    /// Wraps the `Port` variant of `EntryType`.
74    Port = 'T' as isize,
75    /// Wraps the `Metadata` variant of `EntryType`.
76    Metadata = 'M' as isize,
77}
78
79impl EntryType {
80    /// Wraps raw AppleArchive entry type values.
81    pub fn from_raw(raw: u32) -> Option<Self> {
82        match raw {
83            x if x == Self::RegularFile as u32 => Some(Self::RegularFile),
84            x if x == Self::Directory as u32 => Some(Self::Directory),
85            x if x == Self::SymbolicLink as u32 => Some(Self::SymbolicLink),
86            x if x == Self::Fifo as u32 => Some(Self::Fifo),
87            x if x == Self::CharacterDevice as u32 => Some(Self::CharacterDevice),
88            x if x == Self::BlockDevice as u32 => Some(Self::BlockDevice),
89            x if x == Self::Socket as u32 => Some(Self::Socket),
90            x if x == Self::Whiteout as u32 => Some(Self::Whiteout),
91            x if x == Self::Door as u32 => Some(Self::Door),
92            x if x == Self::Port as u32 => Some(Self::Port),
93            x if x == Self::Metadata as u32 => Some(Self::Metadata),
94            _ => None,
95        }
96    }
97}
98
99/// Wraps AppleArchive header field type identifiers.
100#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
101pub enum FieldType {
102    /// Wraps the `Flag` variant of `FieldType`.
103    Flag = 0,
104    /// Wraps the `UInt` variant of `FieldType`.
105    UInt = 1,
106    /// Wraps the `String` variant of `FieldType`.
107    String = 2,
108    /// Wraps the `Hash` variant of `FieldType`.
109    Hash = 3,
110    /// Wraps the `Timespec` variant of `FieldType`.
111    Timespec = 4,
112    /// Wraps the `Blob` variant of `FieldType`.
113    Blob = 5,
114}
115
116impl FieldType {
117    fn from_raw(raw: i32) -> Option<Self> {
118        match raw {
119            0 => Some(Self::Flag),
120            1 => Some(Self::UInt),
121            2 => Some(Self::String),
122            3 => Some(Self::Hash),
123            4 => Some(Self::Timespec),
124            5 => Some(Self::Blob),
125            _ => None,
126        }
127    }
128}
129
130/// Wraps `timespec` values stored in `AAHeader` fields.
131#[repr(C)]
132#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
133pub struct Timespec {
134    /// Wraps the `seconds` field of `Timespec`.
135    pub seconds: i64,
136    /// Wraps the `nanoseconds` field of `Timespec`.
137    pub nanoseconds: i64,
138}
139
140/// Wraps hash values returned by `AAHeaderGetFieldHash`.
141#[derive(Clone, Debug, Eq, PartialEq)]
142pub struct HashValue {
143    /// Wraps the `function` field of `HashValue`.
144    pub function: HashFunction,
145    /// Wraps the `bytes` field of `HashValue`.
146    pub bytes: Vec<u8>,
147}
148
149/// Wraps blob descriptors returned by `AAHeaderGetFieldBlob`.
150#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
151pub struct BlobDescription {
152    /// Wraps the `size` field of `BlobDescription`.
153    pub size: u64,
154    /// Wraps the `offset` field of `BlobDescription`.
155    pub offset: u64,
156}
157
158/// Wraps typed `AAHeader` field values.
159#[derive(Clone, Debug, Eq, PartialEq)]
160pub enum HeaderFieldValue {
161    /// Wraps the `Flag` payload variant returned by `AAHeader` accessors.
162    Flag,
163    /// Wraps the `UInt` payload variant returned by `AAHeader` accessors.
164    UInt(u64),
165    /// Wraps the `String` payload variant returned by `AAHeader` accessors.
166    String(String),
167    /// Wraps the `Hash` payload variant returned by `AAHeader` accessors.
168    Hash(HashValue),
169    /// Wraps the `Timespec` payload variant returned by `AAHeader` accessors.
170    Timespec(Timespec),
171    /// Wraps the `Blob` payload variant returned by `AAHeader` accessors.
172    Blob(BlobDescription),
173}
174
175/// Wraps an `AAHeader` handle.
176#[derive(Debug)]
177pub struct Header {
178    handle: NonNull<c_void>,
179}
180
181impl Header {
182    pub(crate) fn from_handle(handle: *mut c_void, operation: &'static str) -> Result<Self> {
183        Ok(Self {
184            handle: util::nonnull_handle(handle, operation)?,
185        })
186    }
187
188    /// Wraps `AAHeaderCreate`.
189    pub fn new() -> Result<Self> {
190        let handle = unsafe { ffi::aa_header::compression_rs_aa_header_create() };
191        Self::from_handle(handle, "AAHeaderCreate")
192    }
193
194    /// Wraps `AAHeaderCreateWithEncodedData`.
195    pub fn from_encoded_data(data: &[u8]) -> Result<Self> {
196        let handle = unsafe {
197            ffi::aa_header::compression_rs_aa_header_create_with_encoded_data(
198                data.len(),
199                data.as_ptr(),
200            )
201        };
202        Self::from_handle(handle, "AAHeaderCreateWithEncodedData")
203    }
204
205    /// Wraps `AAHeaderCreateWithPath`.
206    pub fn from_path(
207        key_set: &FieldKeySet,
208        dir: &str,
209        path: &str,
210        flags: ArchiveFlags,
211    ) -> Result<Self> {
212        let dir = util::cstring("dir", dir)?;
213        let path = util::cstring("path", path)?;
214        let handle = unsafe {
215            ffi::aa_header::compression_rs_aa_header_create_with_path(
216                key_set.as_ptr(),
217                dir.as_ptr(),
218                path.as_ptr(),
219                flags.bits(),
220            )
221        };
222        Self::from_handle(handle, "AAHeaderCreateWithPath")
223    }
224
225    pub(crate) fn as_ptr(&self) -> *mut c_void {
226        self.handle.as_ptr()
227    }
228
229    /// Wraps `AAHeaderAssign`.
230    pub fn assign(&mut self, other: &Self) -> Result<()> {
231        let status = unsafe {
232            ffi::aa_header::compression_rs_aa_header_assign(self.as_ptr(), other.as_ptr())
233        };
234        util::status_result("AAHeaderAssign", status)
235    }
236
237    /// Wraps `AAHeaderGetKeyIndex`.
238    pub fn field_count(&self) -> u32 {
239        unsafe { ffi::aa_header::compression_rs_aa_header_get_field_count(self.as_ptr()) }
240    }
241
242    /// Wraps `AAHeaderGetKeyIndex`.
243    pub fn is_empty(&self) -> bool {
244        self.field_count() == 0
245    }
246
247    /// Wraps `AAHeaderGetKeyIndex`.
248    pub fn key_index(&self, key: FieldKey) -> Result<Option<u32>> {
249        match unsafe {
250            ffi::aa_header::compression_rs_aa_header_get_key_index(self.as_ptr(), key.raw())
251        } {
252            -1 => Ok(None),
253            value if value < -1 => Err(CompressionError::OperationFailed {
254                operation: "AAHeaderGetKeyIndex",
255                code: value,
256            }),
257            value => Ok(Some(value.unsigned_abs())),
258        }
259    }
260
261    /// Wraps `AAHeaderGetFieldType`.
262    pub fn field_type(&self, index: u32) -> Result<FieldType> {
263        let raw = unsafe {
264            ffi::aa_header::compression_rs_aa_header_get_field_type(self.as_ptr(), index)
265        };
266        if raw < 0 {
267            return Err(CompressionError::OperationFailed {
268                operation: "AAHeaderGetFieldType",
269                code: raw,
270            });
271        }
272        FieldType::from_raw(raw).ok_or(CompressionError::OperationFailed {
273            operation: "AAHeaderGetFieldType",
274            code: raw,
275        })
276    }
277
278    /// Wraps `AAHeaderGetFieldKey`.
279    pub fn field_key(&self, index: u32) -> Result<FieldKey> {
280        if index >= self.field_count() {
281            return Err(CompressionError::OperationFailed {
282                operation: "AAHeaderGetFieldKey",
283                code: -1,
284            });
285        }
286
287        Ok(FieldKey::from_raw(unsafe {
288            ffi::aa_header::compression_rs_aa_header_get_field_key(self.as_ptr(), index)
289        }))
290    }
291
292    /// Wraps `AAHeaderRemoveField`.
293    pub fn payload_size(&self) -> u64 {
294        unsafe { ffi::aa_header::compression_rs_aa_header_get_payload_size(self.as_ptr()) }
295    }
296
297    /// Wraps `AAHeaderRemoveField`.
298    pub fn remove_field(&mut self, index: u32) -> Result<()> {
299        let status =
300            unsafe { ffi::aa_header::compression_rs_aa_header_remove_field(self.as_ptr(), index) };
301        util::status_result("AAHeaderRemoveField", status)
302    }
303
304    /// Wraps `AAHeaderClear`.
305    pub fn clear(&mut self) -> Result<()> {
306        let status = unsafe { ffi::aa_header::compression_rs_aa_header_clear(self.as_ptr()) };
307        util::status_result("AAHeaderClear", status)
308    }
309
310    /// Wraps `AAHeaderSetFieldFlag`.
311    pub fn set_field_flag(&mut self, index: u32, key: FieldKey) -> Result<()> {
312        let status = unsafe {
313            ffi::aa_header::compression_rs_aa_header_set_field_flag(self.as_ptr(), index, key.raw())
314        };
315        util::status_result("AAHeaderSetFieldFlag", status)
316    }
317
318    /// Wraps `AAHeaderSetFieldUInt`.
319    pub fn set_field_uint(&mut self, index: u32, key: FieldKey, value: u64) -> Result<()> {
320        let status = unsafe {
321            ffi::aa_header::compression_rs_aa_header_set_field_uint(
322                self.as_ptr(),
323                index,
324                key.raw(),
325                value,
326            )
327        };
328        util::status_result("AAHeaderSetFieldUInt", status)
329    }
330
331    /// Wraps `AAHeaderSetFieldString`.
332    pub fn set_field_string(&mut self, index: u32, key: FieldKey, value: &str) -> Result<()> {
333        let value = util::cstring("value", value)?;
334        let status = unsafe {
335            ffi::aa_header::compression_rs_aa_header_set_field_string(
336                self.as_ptr(),
337                index,
338                key.raw(),
339                value.as_ptr(),
340                value.as_bytes().len(),
341            )
342        };
343        util::status_result("AAHeaderSetFieldString", status)
344    }
345
346    /// Wraps `AAHeaderSetFieldHash`.
347    pub fn set_field_hash(
348        &mut self,
349        index: u32,
350        key: FieldKey,
351        function: HashFunction,
352        value: &[u8],
353    ) -> Result<()> {
354        if value.len() != function.digest_len() {
355            return Err(CompressionError::InvalidHashLength {
356                expected: function.digest_len(),
357                actual: value.len(),
358            });
359        }
360        let status = unsafe {
361            ffi::aa_header::compression_rs_aa_header_set_field_hash(
362                self.as_ptr(),
363                index,
364                key.raw(),
365                function.as_raw(),
366                value.as_ptr(),
367            )
368        };
369        util::status_result("AAHeaderSetFieldHash", status)
370    }
371
372    /// Wraps `AAHeaderSetFieldTimespec`.
373    pub fn set_field_timespec(&mut self, index: u32, key: FieldKey, value: Timespec) -> Result<()> {
374        let status = unsafe {
375            ffi::aa_header::compression_rs_aa_header_set_field_timespec(
376                self.as_ptr(),
377                index,
378                key.raw(),
379                value.seconds,
380                value.nanoseconds,
381            )
382        };
383        util::status_result("AAHeaderSetFieldTimespec", status)
384    }
385
386    /// Wraps `AAHeaderSetFieldBlob`.
387    pub fn set_field_blob(&mut self, index: u32, key: FieldKey, size: u64) -> Result<()> {
388        let status = unsafe {
389            ffi::aa_header::compression_rs_aa_header_set_field_blob(
390                self.as_ptr(),
391                index,
392                key.raw(),
393                size,
394            )
395        };
396        util::status_result("AAHeaderSetFieldBlob", status)
397    }
398
399    /// Wraps `AAHeaderGetFieldUInt`.
400    pub fn append_field_flag(&mut self, key: FieldKey) -> Result<()> {
401        self.set_field_flag(u32::MAX, key)
402    }
403
404    /// Wraps `AAHeaderGetFieldUInt`.
405    pub fn append_field_uint(&mut self, key: FieldKey, value: u64) -> Result<()> {
406        self.set_field_uint(u32::MAX, key, value)
407    }
408
409    /// Wraps `AAHeaderGetFieldUInt`.
410    pub fn append_field_string(&mut self, key: FieldKey, value: &str) -> Result<()> {
411        self.set_field_string(u32::MAX, key, value)
412    }
413
414    /// Wraps `AAHeaderGetFieldUInt`.
415    pub fn append_field_hash(
416        &mut self,
417        key: FieldKey,
418        function: HashFunction,
419        value: &[u8],
420    ) -> Result<()> {
421        self.set_field_hash(u32::MAX, key, function, value)
422    }
423
424    /// Wraps `AAHeaderGetFieldUInt`.
425    pub fn append_field_timespec(&mut self, key: FieldKey, value: Timespec) -> Result<()> {
426        self.set_field_timespec(u32::MAX, key, value)
427    }
428
429    /// Wraps `AAHeaderGetFieldUInt`.
430    pub fn append_field_blob(&mut self, key: FieldKey, size: u64) -> Result<()> {
431        self.set_field_blob(u32::MAX, key, size)
432    }
433
434    /// Wraps `AAHeaderGetFieldUInt`.
435    pub fn field_uint(&self, index: u32) -> Result<u64> {
436        let mut value = 0_u64;
437        let status = unsafe {
438            ffi::aa_header::compression_rs_aa_header_get_field_uint(
439                self.as_ptr(),
440                index,
441                &mut value,
442            )
443        };
444        util::status_result("AAHeaderGetFieldUInt", status)?;
445        Ok(value)
446    }
447
448    /// Wraps `AAHeaderGetFieldString`.
449    pub fn field_string(&self, index: u32) -> Result<String> {
450        let mut length = 0_usize;
451        let status = unsafe {
452            ffi::aa_header::compression_rs_aa_header_get_field_string(
453                self.as_ptr(),
454                index,
455                0,
456                null_mut(),
457                &mut length,
458            )
459        };
460        util::status_result("AAHeaderGetFieldString", status)?;
461
462        let mut buffer = vec![0_i8; length.saturating_add(1)];
463        let status = unsafe {
464            ffi::aa_header::compression_rs_aa_header_get_field_string(
465                self.as_ptr(),
466                index,
467                buffer.len(),
468                buffer.as_mut_ptr(),
469                &mut length,
470            )
471        };
472        util::status_result("AAHeaderGetFieldString", status)?;
473
474        let value = unsafe { CStr::from_ptr(buffer.as_ptr()) }
475            .to_str()
476            .map_err(|_| CompressionError::Utf8Error {
477                operation: "AAHeaderGetFieldString",
478            })?;
479        Ok(value.to_string())
480    }
481
482    /// Wraps `AAHeaderGetFieldHash`.
483    pub fn field_hash(&self, index: u32) -> Result<HashValue> {
484        let mut function = 0_u32;
485        let mut bytes = vec![0_u8; HashFunction::Sha512.digest_len()];
486        let status = unsafe {
487            ffi::aa_header::compression_rs_aa_header_get_field_hash(
488                self.as_ptr(),
489                index,
490                bytes.len(),
491                &mut function,
492                bytes.as_mut_ptr(),
493            )
494        };
495        util::status_result("AAHeaderGetFieldHash", status)?;
496        let function =
497            HashFunction::from_raw(function).ok_or(CompressionError::OperationFailed {
498                operation: "AAHeaderGetFieldHash",
499                code: -1,
500            })?;
501        bytes.truncate(function.digest_len());
502        Ok(HashValue { function, bytes })
503    }
504
505    /// Wraps `AAHeaderGetFieldTimespec`.
506    pub fn field_timespec(&self, index: u32) -> Result<Timespec> {
507        let mut seconds = 0_i64;
508        let mut nanoseconds = 0_i64;
509        let status = unsafe {
510            ffi::aa_header::compression_rs_aa_header_get_field_timespec(
511                self.as_ptr(),
512                index,
513                &mut seconds,
514                &mut nanoseconds,
515            )
516        };
517        util::status_result("AAHeaderGetFieldTimespec", status)?;
518        Ok(Timespec {
519            seconds,
520            nanoseconds,
521        })
522    }
523
524    /// Wraps `AAHeaderGetFieldBlob`.
525    pub fn field_blob(&self, index: u32) -> Result<BlobDescription> {
526        let mut size = 0_u64;
527        let mut offset = 0_u64;
528        let status = unsafe {
529            ffi::aa_header::compression_rs_aa_header_get_field_blob(
530                self.as_ptr(),
531                index,
532                &mut size,
533                &mut offset,
534            )
535        };
536        util::status_result("AAHeaderGetFieldBlob", status)?;
537        Ok(BlobDescription { size, offset })
538    }
539
540    /// Wraps the `field_value` convenience for `Header`.
541    pub fn field_value(&self, index: u32) -> Result<HeaderFieldValue> {
542        match self.field_type(index)? {
543            FieldType::Flag => Ok(HeaderFieldValue::Flag),
544            FieldType::UInt => self.field_uint(index).map(HeaderFieldValue::UInt),
545            FieldType::String => self.field_string(index).map(HeaderFieldValue::String),
546            FieldType::Hash => self.field_hash(index).map(HeaderFieldValue::Hash),
547            FieldType::Timespec => self.field_timespec(index).map(HeaderFieldValue::Timespec),
548            FieldType::Blob => self.field_blob(index).map(HeaderFieldValue::Blob),
549        }
550    }
551
552    /// Wraps the `uint_with_key` convenience for `Header`.
553    pub fn uint_with_key(&self, key: FieldKey) -> Result<Option<u64>> {
554        self.key_index(key)?
555            .map(|index| self.field_uint(index))
556            .transpose()
557    }
558
559    /// Wraps the `string_with_key` convenience for `Header`.
560    pub fn string_with_key(&self, key: FieldKey) -> Result<Option<String>> {
561        self.key_index(key)?
562            .map(|index| self.field_string(index))
563            .transpose()
564    }
565
566    /// Wraps `AAHeaderGetEncodedSize`.
567    pub fn hash_with_key(&self, key: FieldKey) -> Result<Option<HashValue>> {
568        self.key_index(key)?
569            .map(|index| self.field_hash(index))
570            .transpose()
571    }
572
573    /// Wraps `AAHeaderGetEncodedSize`.
574    pub fn timespec_with_key(&self, key: FieldKey) -> Result<Option<Timespec>> {
575        self.key_index(key)?
576            .map(|index| self.field_timespec(index))
577            .transpose()
578    }
579
580    /// Wraps `AAHeaderGetEncodedSize`.
581    pub fn blob_with_key(&self, key: FieldKey) -> Result<Option<BlobDescription>> {
582        self.key_index(key)?
583            .map(|index| self.field_blob(index))
584            .transpose()
585    }
586
587    /// Wraps `AAHeaderGetEncodedData`.
588    pub fn value_with_key(&self, key: FieldKey) -> Result<Option<HeaderFieldValue>> {
589        self.key_index(key)?
590            .map(|index| self.field_value(index))
591            .transpose()
592    }
593
594    /// Wraps `AAHeaderGetEncodedData`.
595    pub fn entry_type(&self) -> Result<Option<EntryType>> {
596        Ok(self
597            .uint_with_key(FieldKey::TYP)?
598            .and_then(|raw| u32::try_from(raw).ok())
599            .and_then(EntryType::from_raw))
600    }
601
602    /// Wraps `AAHeaderGetEncodedData`.
603    pub fn path(&self) -> Result<Option<String>> {
604        self.string_with_key(FieldKey::PAT)
605    }
606
607    /// Wraps `AAHeaderGetEncodedData`.
608    pub fn encoded_size(&self) -> usize {
609        unsafe { ffi::aa_header::compression_rs_aa_header_get_encoded_size(self.as_ptr()) }
610    }
611
612    /// Wraps `AAHeaderGetEncodedData`.
613    pub fn encoded_data(&self) -> Result<Vec<u8>> {
614        let size = self.encoded_size();
615        let mut data = vec![0_u8; size];
616        if size == 0 {
617            return Ok(data);
618        }
619        let copied = unsafe {
620            ffi::aa_header::compression_rs_aa_header_copy_encoded_data(
621                self.as_ptr(),
622                data.as_mut_ptr(),
623            )
624        };
625        if copied {
626            Ok(data)
627        } else {
628            Err(CompressionError::OperationFailed {
629                operation: "AAHeaderGetEncodedData",
630                code: -1,
631            })
632        }
633    }
634}
635
636impl Clone for Header {
637    fn clone(&self) -> Self {
638        let handle = unsafe { ffi::aa_header::compression_rs_aa_header_clone(self.as_ptr()) };
639        Self::from_handle(handle, "AAHeaderClone").expect("AAHeaderClone returned null")
640    }
641}
642
643impl Drop for Header {
644    fn drop(&mut self) {
645        unsafe { ffi::aa_header::compression_rs_aa_header_release(self.as_ptr()) };
646    }
647}
648
649impl Header {
650    pub(crate) fn from_raw_clone(raw: *mut c_void, operation: &'static str) -> Result<Self> {
651        let handle = unsafe { ffi::aa_header::compression_rs_aa_header_clone_from_raw(raw) };
652        Self::from_handle(handle, operation)
653    }
654
655    pub(crate) fn clone_raw(&self) -> Result<*mut c_void> {
656        let raw = unsafe { ffi::aa_header::compression_rs_aa_header_clone_raw(self.as_ptr()) };
657        if raw.is_null() {
658            Err(CompressionError::NullHandle {
659                operation: "AAHeaderClone",
660            })
661        } else {
662            Ok(raw)
663        }
664    }
665}