Skip to main content

compression/
aa_archive_stream.rs

1use crate::{
2    aa_byte_stream::{ArchiveFlags, ByteStream},
3    aa_entry_blob::{EntryAclBlob, EntryXatBlob},
4    aa_entry_stream::{EntryAttributes, EntryMessage, PathList},
5    aa_field_key::{FieldKey, FieldKeySet},
6    aa_header::Header,
7    ffi, util, CompressionError, Result,
8};
9use std::ffi::{c_char, c_void, CStr};
10use std::ptr::NonNull;
11
12#[allow(dead_code)]
13#[derive(Debug)]
14enum ArchiveStreamUpstream {
15    Byte(Box<ByteStream>),
16    Archive(Box<ArchiveStream>),
17}
18
19/// Wraps an `AAArchiveStream` handle.
20#[derive(Debug)]
21pub struct ArchiveStream {
22    handle: NonNull<c_void>,
23    _upstream: Option<ArchiveStreamUpstream>,
24    closed: bool,
25    _message_handler: Option<Box<ArchiveMessageState>>,
26}
27
28impl ArchiveStream {
29    /// Wraps `AAExtractArchiveOutputStreamOpen`.
30    pub fn extract_output(dir: &str, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
31        let dir = util::cstring("dir", dir)?;
32        let handle = unsafe {
33            ffi::aa_archive_stream::compression_rs_aa_extract_archive_output_stream_open(
34                dir.as_ptr(),
35                flags.bits(),
36                n_threads,
37            )
38        };
39        Ok(Self {
40            handle: util::nonnull_handle(handle, "AAExtractArchiveOutputStreamOpen")?,
41            _upstream: None,
42            closed: false,
43            _message_handler: None,
44        })
45    }
46
47    /// Wraps `AAEncodeArchiveOutputStreamOpen`.
48    pub fn encode_output(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
49        let handle = unsafe {
50            ffi::aa_archive_stream::compression_rs_aa_encode_archive_output_stream_open(
51                stream.as_ptr(),
52                flags.bits(),
53                n_threads,
54            )
55        };
56        Ok(Self {
57            handle: util::nonnull_handle(handle, "AAEncodeArchiveOutputStreamOpen")?,
58            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
59            closed: false,
60            _message_handler: None,
61        })
62    }
63
64    /// Wraps `AADecodeArchiveInputStreamOpen`.
65    pub fn decode_input(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
66        let handle = unsafe {
67            ffi::aa_archive_stream::compression_rs_aa_decode_archive_input_stream_open(
68                stream.as_ptr(),
69                flags.bits(),
70                n_threads,
71            )
72        };
73        Ok(Self {
74            handle: util::nonnull_handle(handle, "AADecodeArchiveInputStreamOpen")?,
75            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
76            closed: false,
77            _message_handler: None,
78        })
79    }
80
81    /// Wraps `AAConvertArchiveOutputStreamOpen`.
82    pub fn convert_output(
83        stream: ArchiveStream,
84        insert_key_set: &FieldKeySet,
85        remove_key_set: &FieldKeySet,
86        flags: ArchiveFlags,
87        n_threads: i32,
88    ) -> Result<Self> {
89        let handle = unsafe {
90            ffi::aa_archive_stream::compression_rs_aa_convert_archive_output_stream_open(
91                stream.as_ptr(),
92                insert_key_set.as_ptr(),
93                remove_key_set.as_ptr(),
94                flags.bits(),
95                n_threads,
96            )
97        };
98        Ok(Self {
99            handle: util::nonnull_handle(handle, "AAConvertArchiveOutputStreamOpen")?,
100            _upstream: Some(ArchiveStreamUpstream::Archive(Box::new(stream))),
101            closed: false,
102            _message_handler: None,
103        })
104    }
105
106    pub(crate) fn as_ptr(&self) -> *mut c_void {
107        self.handle.as_ptr()
108    }
109
110    fn ensure_open(&self) -> Result<()> {
111        if self.closed {
112            Err(CompressionError::Closed {
113                resource: "archive stream",
114            })
115        } else {
116            Ok(())
117        }
118    }
119
120    /// Wraps `AAArchiveStreamWriteHeader`.
121    pub fn write_header(&mut self, header: &Header) -> Result<()> {
122        self.ensure_open()?;
123        let status = unsafe {
124            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_header(
125                self.as_ptr(),
126                header.as_ptr(),
127            )
128        };
129        util::status_result("AAArchiveStreamWriteHeader", status)
130    }
131
132    /// Wraps `AAArchiveStreamWriteBlob`.
133    pub fn write_blob(&mut self, key: FieldKey, buffer: &[u8]) -> Result<()> {
134        self.ensure_open()?;
135        let status = unsafe {
136            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_blob(
137                self.as_ptr(),
138                key.raw(),
139                buffer.as_ptr(),
140                buffer.len(),
141            )
142        };
143        util::status_result("AAArchiveStreamWriteBlob", status)
144    }
145
146    /// Wraps `AAArchiveStreamReadHeader`.
147    pub fn read_header(&mut self) -> Result<Option<Header>> {
148        self.ensure_open()?;
149        let mut status = 0_i32;
150        let handle = unsafe {
151            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_new(
152                self.as_ptr(),
153                &mut status,
154            )
155        };
156        match status {
157            1 => Ok(Some(Header::from_handle(
158                handle,
159                "AAArchiveStreamReadHeader",
160            )?)),
161            0 => Ok(None),
162            code => Err(CompressionError::OperationFailed {
163                operation: "AAArchiveStreamReadHeader",
164                code,
165            }),
166        }
167    }
168
169    /// Wraps `AAArchiveStreamReadHeader`.
170    pub fn read_header_into(&mut self, header: &mut Header) -> Result<bool> {
171        self.ensure_open()?;
172        match unsafe {
173            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_into(
174                self.as_ptr(),
175                header.as_ptr(),
176            )
177        } {
178            1 => Ok(true),
179            0 => Ok(false),
180            code => Err(CompressionError::OperationFailed {
181                operation: "AAArchiveStreamReadHeader",
182                code,
183            }),
184        }
185    }
186
187    /// Wraps `AAArchiveStreamReadBlob`.
188    pub fn read_blob(&mut self, key: FieldKey, buffer: &mut [u8]) -> Result<()> {
189        self.ensure_open()?;
190        let status = unsafe {
191            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_blob(
192                self.as_ptr(),
193                key.raw(),
194                buffer.as_mut_ptr(),
195                buffer.len(),
196            )
197        };
198        util::status_result("AAArchiveStreamReadBlob", status)
199    }
200
201    /// Wraps `AAArchiveStreamWritePathList`.
202    pub fn write_path_list(
203        &mut self,
204        path_list: &PathList,
205        key_set: &FieldKeySet,
206        dir: &str,
207        flags: ArchiveFlags,
208        n_threads: i32,
209    ) -> Result<()> {
210        self.ensure_open()?;
211        let dir = util::cstring("dir", dir)?;
212        let status = unsafe {
213            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_path_list(
214                self.as_ptr(),
215                path_list.as_ptr(),
216                key_set.as_ptr(),
217                dir.as_ptr(),
218                flags.bits(),
219                n_threads,
220            )
221        };
222        util::status_result("AAArchiveStreamWritePathList", status)
223    }
224
225    /// Wraps `AAArchiveStreamProcess`.
226    pub fn process_into(
227        &mut self,
228        output: &mut Self,
229        flags: ArchiveFlags,
230        n_threads: i32,
231    ) -> Result<u64> {
232        self.ensure_open()?;
233        output.ensure_open()?;
234        util::off_t_result("AAArchiveStreamProcess", unsafe {
235            ffi::aa_archive_stream::compression_rs_aa_archive_stream_process(
236                self.as_ptr(),
237                output.as_ptr(),
238                flags.bits(),
239                n_threads,
240            )
241        })
242    }
243
244    /// Wraps `AAArchiveStreamClose`.
245    pub fn cancel(&mut self) -> Result<()> {
246        self.ensure_open()?;
247        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_cancel(self.as_ptr()) };
248        Ok(())
249    }
250
251    #[deprecated(
252        since = "0.2.2",
253        note = "Use ArchiveStream::cancel; AAArchiveStreamAbort is a deprecated AppleArchive compatibility shim."
254    )]
255    /// Wraps `AAArchiveStreamClose`.
256    pub fn abort(&mut self) -> Result<()> {
257        self.ensure_open()?;
258        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_abort(self.as_ptr()) };
259        Ok(())
260    }
261
262    /// Wraps `AAArchiveStreamClose`.
263    pub fn close(&mut self) -> Result<()> {
264        if self.closed {
265            return Ok(());
266        }
267        let status = unsafe {
268            ffi::aa_archive_stream::compression_rs_aa_archive_stream_close(self.as_ptr())
269        };
270        self.closed = true;
271        util::status_result("AAArchiveStreamClose", status)
272    }
273}
274
275impl Drop for ArchiveStream {
276    fn drop(&mut self) {
277        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_release(self.as_ptr()) };
278    }
279}
280
281fn custom_archive_stream_error(operation: &'static str) -> CompressionError {
282    CompressionError::OperationFailed {
283        operation,
284        code: -1,
285    }
286}
287
288fn custom_archive_stream_code(error: &CompressionError) -> i32 {
289    match error {
290        CompressionError::OperationFailed { code, .. } if *code < 0 => *code,
291        _ => -1,
292    }
293}
294
295struct CustomArchiveStreamState {
296    callbacks: Box<dyn CustomArchiveStreamCallbacks>,
297}
298
299/// Wraps callbacks installed by `AACustomArchiveStreamSet*Proc`.
300pub trait CustomArchiveStreamCallbacks {
301    /// Wraps `AAArchiveStreamWriteHeader`.
302    fn write_header(&mut self, _header: &Header) -> Result<()> {
303        Err(custom_archive_stream_error("AAArchiveStreamWriteHeader"))
304    }
305
306    /// Wraps `AAArchiveStreamWriteBlob`.
307    fn write_blob(&mut self, _key: FieldKey, _buffer: &[u8]) -> Result<()> {
308        Err(custom_archive_stream_error("AAArchiveStreamWriteBlob"))
309    }
310
311    /// Wraps `AAArchiveStreamReadHeader`.
312    fn read_header(&mut self) -> Result<Option<Header>> {
313        Err(custom_archive_stream_error("AAArchiveStreamReadHeader"))
314    }
315
316    /// Wraps `AAArchiveStreamReadBlob`.
317    fn read_blob(&mut self, _key: FieldKey, _buffer: &mut [u8]) -> Result<()> {
318        Err(custom_archive_stream_error("AAArchiveStreamReadBlob"))
319    }
320
321    /// Wraps the `cancel` convenience for `CustomArchiveStreamCallbacks`.
322    fn cancel(&mut self) {}
323
324    /// Wraps the `close` convenience for `CustomArchiveStreamCallbacks`.
325    fn close(&mut self) -> Result<()> {
326        Ok(())
327    }
328}
329
330unsafe fn custom_archive_stream_state(
331    arg: *mut c_void,
332) -> Option<&'static mut CustomArchiveStreamState> {
333    if arg.is_null() {
334        None
335    } else {
336        Some(unsafe { &mut *arg.cast::<CustomArchiveStreamState>() })
337    }
338}
339
340unsafe fn custom_archive_stream_slice<'a>(
341    buffer: *const c_void,
342    length: usize,
343) -> Option<&'a [u8]> {
344    if length == 0 {
345        Some(&[])
346    } else if buffer.is_null() {
347        None
348    } else {
349        Some(unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), length) })
350    }
351}
352
353unsafe fn custom_archive_stream_slice_mut<'a>(
354    buffer: *mut c_void,
355    length: usize,
356) -> Option<&'a mut [u8]> {
357    if length == 0 {
358        Some(&mut [])
359    } else if buffer.is_null() {
360        None
361    } else {
362        Some(unsafe { std::slice::from_raw_parts_mut(buffer.cast::<u8>(), length) })
363    }
364}
365
366unsafe extern "C" fn custom_archive_stream_write_header(
367    arg: *mut c_void,
368    header: *mut c_void,
369) -> i32 {
370    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
371        return -1;
372    };
373    if header.is_null() {
374        return -1;
375    }
376    let header = match Header::from_raw_clone(header, "AAHeaderClone") {
377        Ok(header) => header,
378        Err(error) => return custom_archive_stream_code(&error),
379    };
380    match state.callbacks.write_header(&header) {
381        Ok(()) => 0,
382        Err(error) => custom_archive_stream_code(&error),
383    }
384}
385
386unsafe extern "C" fn custom_archive_stream_write_blob(
387    arg: *mut c_void,
388    key: u32,
389    buffer: *const c_void,
390    length: usize,
391) -> i32 {
392    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
393        return -1;
394    };
395    let Some(buffer) = (unsafe { custom_archive_stream_slice(buffer, length) }) else {
396        return -1;
397    };
398    match state.callbacks.write_blob(FieldKey::from_raw(key), buffer) {
399        Ok(()) => 0,
400        Err(error) => custom_archive_stream_code(&error),
401    }
402}
403
404unsafe extern "C" fn custom_archive_stream_read_header(
405    arg: *mut c_void,
406    header_out: *mut *mut c_void,
407) -> i32 {
408    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
409        return -1;
410    };
411    match state.callbacks.read_header() {
412        Ok(Some(header)) => {
413            if header_out.is_null() {
414                return -1;
415            }
416            let raw = match header.clone_raw() {
417                Ok(raw) => raw,
418                Err(error) => return custom_archive_stream_code(&error),
419            };
420            unsafe { *header_out = raw };
421            1
422        }
423        Ok(None) => 0,
424        Err(error) => custom_archive_stream_code(&error),
425    }
426}
427
428unsafe extern "C" fn custom_archive_stream_read_blob(
429    arg: *mut c_void,
430    key: u32,
431    buffer: *mut c_void,
432    length: usize,
433) -> i32 {
434    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
435        return -1;
436    };
437    let Some(buffer) = (unsafe { custom_archive_stream_slice_mut(buffer, length) }) else {
438        return -1;
439    };
440    match state.callbacks.read_blob(FieldKey::from_raw(key), buffer) {
441        Ok(()) => 0,
442        Err(error) => custom_archive_stream_code(&error),
443    }
444}
445
446unsafe extern "C" fn custom_archive_stream_cancel(arg: *mut c_void) {
447    if let Some(state) = unsafe { custom_archive_stream_state(arg) } {
448        state.callbacks.cancel();
449    }
450}
451
452unsafe extern "C" fn custom_archive_stream_close(arg: *mut c_void) -> i32 {
453    if arg.is_null() {
454        return 0;
455    }
456    let mut state = unsafe { Box::from_raw(arg.cast::<CustomArchiveStreamState>()) };
457    match state.callbacks.close() {
458        Ok(()) => 0,
459        Err(error) => custom_archive_stream_code(&error),
460    }
461}
462
463impl ArchiveStream {
464    /// Wraps `AACustomArchiveStreamOpen`.
465    pub fn custom<T: CustomArchiveStreamCallbacks + 'static>(callbacks: T) -> Result<Self> {
466        let handle =
467            unsafe { ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_open() };
468        let stream = Self {
469            handle: util::nonnull_handle(handle, "AACustomArchiveStreamOpen")?,
470            _upstream: None,
471            closed: false,
472            _message_handler: None,
473        };
474        let state = Box::new(CustomArchiveStreamState {
475            callbacks: Box::new(callbacks),
476        });
477        let data = Box::into_raw(state).cast::<c_void>();
478        unsafe {
479            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_data(
480                stream.as_ptr(),
481                data,
482            );
483            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_write_header_proc(
484                stream.as_ptr(),
485                Some(custom_archive_stream_write_header),
486            );
487            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_write_blob_proc(
488                stream.as_ptr(),
489                Some(custom_archive_stream_write_blob),
490            );
491            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_read_header_proc(
492                stream.as_ptr(),
493                Some(custom_archive_stream_read_header),
494            );
495            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_read_blob_proc(
496                stream.as_ptr(),
497                Some(custom_archive_stream_read_blob),
498            );
499            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_cancel_proc(
500                stream.as_ptr(),
501                Some(custom_archive_stream_cancel),
502            );
503            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_abort_proc(
504                stream.as_ptr(),
505                Some(custom_archive_stream_cancel),
506            );
507            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_close_proc(
508                stream.as_ptr(),
509                Some(custom_archive_stream_close),
510            );
511        }
512        Ok(stream)
513    }
514}
515
516/// Wraps payloads delivered through `AAEntryMessageProc`.
517pub enum EntryMessageData {
518    /// Wraps the `None` payload delivered by `AAEntryMessageProc`.
519    None,
520    /// Wraps the `Header` payload delivered by `AAEntryMessageProc`.
521    Header(Header),
522    /// Wraps the `EntryIds` payload delivered by `AAEntryMessageProc`.
523    EntryIds {
524        /// Wraps the `idx` field delivered by `AAEntryMessageProc`.
525        idx: u64,
526        /// Wraps the `idz` field delivered by `AAEntryMessageProc`.
527        idz: u64,
528    },
529    /// Wraps the `Progress` payload delivered by `AAEntryMessageProc`.
530    Progress {
531        /// Wraps the `total` field delivered by `AAEntryMessageProc`.
532        total: u64,
533        /// Wraps the `current` field delivered by `AAEntryMessageProc`.
534        current: u64,
535    },
536    /// Wraps the `Attributes` payload delivered by `AAEntryMessageProc`.
537    Attributes(EntryAttributes),
538    /// Wraps the `Xat` payload delivered by `AAEntryMessageProc`.
539    Xat(EntryXatBlob),
540    /// Wraps the `Acl` payload delivered by `AAEntryMessageProc`.
541    Acl(EntryAclBlob),
542}
543
544impl std::fmt::Debug for EntryMessageData {
545    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546        match self {
547            Self::None => f.write_str("None"),
548            Self::Header(_) => f.write_str("Header(..)"),
549            Self::EntryIds { idx, idz } => f
550                .debug_struct("EntryIds")
551                .field("idx", idx)
552                .field("idz", idz)
553                .finish(),
554            Self::Progress { total, current } => f
555                .debug_struct("Progress")
556                .field("total", total)
557                .field("current", current)
558                .finish(),
559            Self::Attributes(attributes) => f.debug_tuple("Attributes").field(attributes).finish(),
560            Self::Xat(_) => f.write_str("Xat(..)"),
561            Self::Acl(_) => f.write_str("Acl(..)"),
562        }
563    }
564}
565
566/// Wraps `AAEntryMessageProc` event data.
567#[derive(Debug)]
568pub struct EntryMessageEvent {
569    /// Wraps the `message` field of `EntryMessageEvent`.
570    pub message: EntryMessage,
571    /// Wraps the `path` field of `EntryMessageEvent`.
572    pub path: String,
573    /// Wraps the `data` field of `EntryMessageEvent`.
574    pub data: EntryMessageData,
575}
576
577/// Wraps handlers installed through `AAEntryMessageProc`.
578pub trait EntryMessageHandler {
579    /// Wraps the `handle` convenience for `EntryMessageHandler`.
580    fn handle(&mut self, event: &mut EntryMessageEvent) -> Result<i32>;
581}
582
583impl<F> EntryMessageHandler for F
584where
585    F: FnMut(&mut EntryMessageEvent) -> Result<i32>,
586{
587    fn handle(&mut self, event: &mut EntryMessageEvent) -> Result<i32> {
588        self(event)
589    }
590}
591
592struct ArchiveMessageState {
593    handler: Box<dyn EntryMessageHandler>,
594}
595
596impl std::fmt::Debug for ArchiveMessageState {
597    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598        f.write_str("ArchiveMessageState(..)")
599    }
600}
601
602fn archive_message_code(error: &CompressionError) -> i32 {
603    match error {
604        CompressionError::OperationFailed { code, .. } if *code < 0 => *code,
605        _ => -1,
606    }
607}
608
609unsafe fn archive_message_state(arg: *mut c_void) -> Option<&'static mut ArchiveMessageState> {
610    if arg.is_null() {
611        None
612    } else {
613        Some(unsafe { &mut *arg.cast::<ArchiveMessageState>() })
614    }
615}
616
617unsafe fn archive_message_pair(data: *mut c_void) -> Option<(u64, u64)> {
618    if data.is_null() {
619        None
620    } else {
621        let values = unsafe { std::slice::from_raw_parts(data.cast::<u64>(), 2) };
622        Some((values[0], values[1]))
623    }
624}
625
626fn archive_message_data(message: EntryMessage, data: *mut c_void) -> Result<EntryMessageData> {
627    match message {
628        EntryMessage::SearchPruneDir
629        | EntryMessage::SearchExclude
630        | EntryMessage::SearchFail
631        | EntryMessage::EncodeScanning => Ok(EntryMessageData::None),
632        EntryMessage::ExtractBegin
633        | EntryMessage::ConvertExclude
634        | EntryMessage::ProcessExclude => {
635            if data.is_null() {
636                Ok(EntryMessageData::None)
637            } else {
638                Ok(EntryMessageData::Header(Header::from_raw_clone(
639                    data,
640                    "AAHeaderClone",
641                )?))
642            }
643        }
644        EntryMessage::ExtractEnd | EntryMessage::ExtractFail => {
645            if let Some((idx, idz)) = unsafe { archive_message_pair(data) } {
646                Ok(EntryMessageData::EntryIds { idx, idz })
647            } else {
648                Ok(EntryMessageData::None)
649            }
650        }
651        EntryMessage::EncodeWriting | EntryMessage::DecodeReading => {
652            if let Some((total, current)) = unsafe { archive_message_pair(data) } {
653                Ok(EntryMessageData::Progress { total, current })
654            } else {
655                Ok(EntryMessageData::None)
656            }
657        }
658        EntryMessage::ExtractAttributes => {
659            if data.is_null() {
660                Ok(EntryMessageData::None)
661            } else {
662                Ok(EntryMessageData::Attributes(unsafe {
663                    *data.cast::<EntryAttributes>()
664                }))
665            }
666        }
667        EntryMessage::ExtractXat => {
668            if data.is_null() {
669                Ok(EntryMessageData::None)
670            } else {
671                Ok(EntryMessageData::Xat(EntryXatBlob::clone_from_raw(data)?))
672            }
673        }
674        EntryMessage::ExtractAcl => {
675            if data.is_null() {
676                Ok(EntryMessageData::None)
677            } else {
678                Ok(EntryMessageData::Acl(EntryAclBlob::clone_from_raw(data)?))
679            }
680        }
681    }
682}
683
684fn archive_message_sync(
685    message: EntryMessage,
686    data_ptr: *mut c_void,
687    data: &EntryMessageData,
688) -> Result<()> {
689    match (message, data) {
690        (EntryMessage::ExtractAttributes, EntryMessageData::Attributes(attributes)) => {
691            if !data_ptr.is_null() {
692                unsafe { *data_ptr.cast::<EntryAttributes>() = *attributes };
693            }
694            Ok(())
695        }
696        (EntryMessage::ExtractXat, EntryMessageData::Xat(xat)) => {
697            if data_ptr.is_null() {
698                Ok(())
699            } else {
700                EntryXatBlob::sync_into_raw(data_ptr, xat)
701            }
702        }
703        (EntryMessage::ExtractAcl, EntryMessageData::Acl(acl)) => {
704            if data_ptr.is_null() {
705                Ok(())
706            } else {
707                EntryAclBlob::sync_into_raw(data_ptr, acl)
708            }
709        }
710        _ => Ok(()),
711    }
712}
713
714unsafe extern "C" fn archive_entry_message_proc(
715    arg: *mut c_void,
716    message_raw: u32,
717    path: *const c_char,
718    data: *mut c_void,
719) -> i32 {
720    let Some(state) = (unsafe { archive_message_state(arg) }) else {
721        return -1;
722    };
723    let Some(message) = EntryMessage::from_raw(message_raw) else {
724        return -1;
725    };
726    if path.is_null() {
727        return -1;
728    }
729    let mut event = match archive_message_data(message, data) {
730        Ok(event_data) => EntryMessageEvent {
731            message,
732            path: unsafe { CStr::from_ptr(path) }
733                .to_string_lossy()
734                .into_owned(),
735            data: event_data,
736        },
737        Err(error) => return archive_message_code(&error),
738    };
739    match state.handler.handle(&mut event) {
740        Ok(code) => match archive_message_sync(message, data, &event.data) {
741            Ok(()) => code,
742            Err(error) => archive_message_code(&error),
743        },
744        Err(error) => archive_message_code(&error),
745    }
746}
747
748impl ArchiveStream {
749    /// Wraps `AAExtractArchiveOutputStreamOpen`.
750    pub fn extract_output_with_messages<T: EntryMessageHandler + 'static>(
751        dir: &str,
752        flags: ArchiveFlags,
753        n_threads: i32,
754        handler: T,
755    ) -> Result<Self> {
756        let dir = util::cstring("dir", dir)?;
757        let mut message_handler = Box::new(ArchiveMessageState {
758            handler: Box::new(handler),
759        });
760        let handle = unsafe {
761            ffi::aa_archive_stream::compression_rs_aa_extract_archive_output_stream_open_with_messages(
762                dir.as_ptr(),
763                flags.bits(),
764                n_threads,
765                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
766                Some(archive_entry_message_proc),
767            )
768        };
769        Ok(Self {
770            handle: util::nonnull_handle(handle, "AAExtractArchiveOutputStreamOpen")?,
771            _upstream: None,
772            closed: false,
773            _message_handler: Some(message_handler),
774        })
775    }
776
777    /// Wraps `AAEncodeArchiveOutputStreamOpen`.
778    pub fn encode_output_with_messages<T: EntryMessageHandler + 'static>(
779        stream: ByteStream,
780        flags: ArchiveFlags,
781        n_threads: i32,
782        handler: T,
783    ) -> Result<Self> {
784        let mut message_handler = Box::new(ArchiveMessageState {
785            handler: Box::new(handler),
786        });
787        let handle = unsafe {
788            ffi::aa_archive_stream::compression_rs_aa_encode_archive_output_stream_open_with_messages(
789                stream.as_ptr(),
790                flags.bits(),
791                n_threads,
792                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
793                Some(archive_entry_message_proc),
794            )
795        };
796        Ok(Self {
797            handle: util::nonnull_handle(handle, "AAEncodeArchiveOutputStreamOpen")?,
798            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
799            closed: false,
800            _message_handler: Some(message_handler),
801        })
802    }
803
804    /// Wraps `AADecodeArchiveInputStreamOpen`.
805    pub fn decode_input_with_messages<T: EntryMessageHandler + 'static>(
806        stream: ByteStream,
807        flags: ArchiveFlags,
808        n_threads: i32,
809        handler: T,
810    ) -> Result<Self> {
811        let mut message_handler = Box::new(ArchiveMessageState {
812            handler: Box::new(handler),
813        });
814        let handle = unsafe {
815            ffi::aa_archive_stream::compression_rs_aa_decode_archive_input_stream_open_with_messages(
816                stream.as_ptr(),
817                flags.bits(),
818                n_threads,
819                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
820                Some(archive_entry_message_proc),
821            )
822        };
823        Ok(Self {
824            handle: util::nonnull_handle(handle, "AADecodeArchiveInputStreamOpen")?,
825            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
826            closed: false,
827            _message_handler: Some(message_handler),
828        })
829    }
830
831    /// Wraps `AAConvertArchiveOutputStreamOpen`.
832    pub fn convert_output_with_messages<T: EntryMessageHandler + 'static>(
833        stream: ArchiveStream,
834        insert_key_set: &FieldKeySet,
835        remove_key_set: &FieldKeySet,
836        flags: ArchiveFlags,
837        n_threads: i32,
838        handler: T,
839    ) -> Result<Self> {
840        let mut message_handler = Box::new(ArchiveMessageState {
841            handler: Box::new(handler),
842        });
843        let handle = unsafe {
844            ffi::aa_archive_stream::compression_rs_aa_convert_archive_output_stream_open_with_messages(
845                stream.as_ptr(),
846                insert_key_set.as_ptr(),
847                remove_key_set.as_ptr(),
848                flags.bits(),
849                n_threads,
850                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
851                Some(archive_entry_message_proc),
852            )
853        };
854        Ok(Self {
855            handle: util::nonnull_handle(handle, "AAConvertArchiveOutputStreamOpen")?,
856            _upstream: Some(ArchiveStreamUpstream::Archive(Box::new(stream))),
857            closed: false,
858            _message_handler: Some(message_handler),
859        })
860    }
861
862    /// Wraps `AAArchiveStreamWritePathList`.
863    pub fn write_path_list_with_messages<T: EntryMessageHandler + 'static>(
864        &mut self,
865        path_list: &PathList,
866        key_set: &FieldKeySet,
867        dir: &str,
868        flags: ArchiveFlags,
869        n_threads: i32,
870        handler: T,
871    ) -> Result<()> {
872        self.ensure_open()?;
873        let dir = util::cstring("dir", dir)?;
874        let mut message_handler = Box::new(ArchiveMessageState {
875            handler: Box::new(handler),
876        });
877        let status = unsafe {
878            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_path_list_with_messages(
879                self.as_ptr(),
880                path_list.as_ptr(),
881                key_set.as_ptr(),
882                dir.as_ptr(),
883                flags.bits(),
884                n_threads,
885                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
886                Some(archive_entry_message_proc),
887            )
888        };
889        util::status_result("AAArchiveStreamWritePathList", status)
890    }
891
892    /// Wraps `AAArchiveStreamProcess`.
893    pub fn process_into_with_messages<T: EntryMessageHandler + 'static>(
894        &mut self,
895        output: &mut Self,
896        flags: ArchiveFlags,
897        n_threads: i32,
898        handler: T,
899    ) -> Result<u64> {
900        self.ensure_open()?;
901        output.ensure_open()?;
902        let mut message_handler = Box::new(ArchiveMessageState {
903            handler: Box::new(handler),
904        });
905        util::off_t_result("AAArchiveStreamProcess", unsafe {
906            ffi::aa_archive_stream::compression_rs_aa_archive_stream_process_with_messages(
907                self.as_ptr(),
908                output.as_ptr(),
909                flags.bits(),
910                n_threads,
911                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
912                Some(archive_entry_message_proc),
913            )
914        })
915    }
916}