Skip to main content

compression/
aa_byte_stream.rs

1use crate::{ffi, util, CompressionError, Result};
2use std::ffi::c_void;
3use std::fs::File;
4use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
5use std::os::fd::{IntoRawFd, RawFd};
6use std::ptr::NonNull;
7
8const READ_CHUNK_LEN: usize = 32 * 1024;
9
10/// Wraps `OPEN_READ_ONLY`.
11pub const OPEN_READ_ONLY: i32 = 0x0000;
12/// Wraps `OPEN_WRITE_ONLY`.
13pub const OPEN_WRITE_ONLY: i32 = 0x0001;
14/// Wraps `OPEN_READ_WRITE`.
15pub const OPEN_READ_WRITE: i32 = 0x0002;
16/// Wraps `OPEN_CREATE`.
17pub const OPEN_CREATE: i32 = 0x0200;
18/// Wraps `OPEN_TRUNCATE`.
19pub const OPEN_TRUNCATE: i32 = 0x0400;
20/// Wraps `DEFAULT_FILE_MODE`.
21pub const DEFAULT_FILE_MODE: u32 = 0o644;
22
23/// Wraps AppleArchive compression algorithm identifiers.
24#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
25pub enum ArchiveCompressionAlgorithm {
26    /// Wraps the `None` variant of `ArchiveCompressionAlgorithm`.
27    None,
28    /// Wraps the `Lz4` variant of `ArchiveCompressionAlgorithm`.
29    Lz4,
30    /// Wraps the `Zlib` variant of `ArchiveCompressionAlgorithm`.
31    Zlib,
32    /// Wraps the `Lzma` variant of `ArchiveCompressionAlgorithm`.
33    Lzma,
34    /// Wraps the `Lzfse` variant of `ArchiveCompressionAlgorithm`.
35    Lzfse,
36    /// Wraps the `Lzbitmap` variant of `ArchiveCompressionAlgorithm`.
37    Lzbitmap,
38}
39
40impl ArchiveCompressionAlgorithm {
41    /// Wraps raw AppleArchive compression algorithm values.
42    pub const fn from_raw(raw: u32) -> Option<Self> {
43        match raw {
44            0x000 => Some(Self::None),
45            0x100 => Some(Self::Lz4),
46            0x505 => Some(Self::Zlib),
47            0x306 => Some(Self::Lzma),
48            0x801 => Some(Self::Lzfse),
49            0x702 => Some(Self::Lzbitmap),
50            _ => None,
51        }
52    }
53
54    /// Wraps raw AppleArchive compression algorithm values.
55    pub const fn as_raw(self) -> u32 {
56        match self {
57            Self::None => 0x000,
58            Self::Lz4 => 0x100,
59            Self::Zlib => 0x505,
60            Self::Lzma => 0x306,
61            Self::Lzfse => 0x801,
62            Self::Lzbitmap => 0x702,
63        }
64    }
65}
66
67/// Wraps AppleArchive archive-processing flags.
68#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
69pub struct ArchiveFlags(u64);
70
71impl ArchiveFlags {
72    /// Wraps the `IGNORE_EPERM` AppleArchive archive flag bit.
73    pub const IGNORE_EPERM: Self = Self(1_u64 << 0);
74    /// Wraps the `ARCHIVE_DEDUPLICATE_DAT` AppleArchive archive flag bit.
75    pub const ARCHIVE_DEDUPLICATE_DAT: Self = Self(1_u64 << 1);
76    /// Wraps the `ARCHIVE_NO_RESOLVE_ACL_QUALIFIERS` AppleArchive archive flag bit.
77    pub const ARCHIVE_NO_RESOLVE_ACL_QUALIFIERS: Self = Self(1_u64 << 2);
78    /// Wraps the `REPLACE_ATTRIBUTES` AppleArchive archive flag bit.
79    pub const REPLACE_ATTRIBUTES: Self = Self(1_u64 << 3);
80    /// Wraps the `EXTRACT_NO_AUTO_DEDUP` AppleArchive archive flag bit.
81    pub const EXTRACT_NO_AUTO_DEDUP: Self = Self(1_u64 << 4);
82    /// Wraps the `EXTRACT_NO_AUTO_SPARSE` AppleArchive archive flag bit.
83    pub const EXTRACT_NO_AUTO_SPARSE: Self = Self(1_u64 << 5);
84    /// Wraps the `CROSS_VOLUME_BOUNDARIES` AppleArchive archive flag bit.
85    pub const CROSS_VOLUME_BOUNDARIES: Self = Self(1_u64 << 6);
86    /// Wraps the `EXTRACT_AUTO_DEDUP_AS_HARD_LINKS` AppleArchive archive flag bit.
87    pub const EXTRACT_AUTO_DEDUP_AS_HARD_LINKS: Self = Self(1_u64 << 7);
88    /// Wraps the `DECODE_INSERT_IDX` AppleArchive archive flag bit.
89    pub const DECODE_INSERT_IDX: Self = Self(1_u64 << 8);
90    /// Wraps the `EXCLUDE_METADATA_ENTRIES` AppleArchive archive flag bit.
91    pub const EXCLUDE_METADATA_ENTRIES: Self = Self(1_u64 << 9);
92    /// Wraps the `PROCESS_RANDOM_ACCESS_OUTPUT` AppleArchive archive flag bit.
93    pub const PROCESS_RANDOM_ACCESS_OUTPUT: Self = Self(1_u64 << 10);
94    /// Wraps the `VERBOSITY_0` AppleArchive archive flag bit.
95    pub const VERBOSITY_0: Self = Self(0_u64 << 62);
96    /// Wraps the `VERBOSITY_1` AppleArchive archive flag bit.
97    pub const VERBOSITY_1: Self = Self(1_u64 << 62);
98    /// Wraps the `VERBOSITY_2` AppleArchive archive flag bit.
99    pub const VERBOSITY_2: Self = Self(2_u64 << 62);
100    /// Wraps the `VERBOSITY_3` AppleArchive archive flag bit.
101    pub const VERBOSITY_3: Self = Self(3_u64 << 62);
102
103    /// Wraps an empty AppleArchive archive flag set.
104    pub const fn empty() -> Self {
105        Self(0)
106    }
107
108    /// Wraps the raw AppleArchive archive flag bits.
109    pub const fn bits(self) -> u64 {
110        self.0
111    }
112
113    /// Wraps raw AppleArchive archive flag bits.
114    pub const fn from_bits(bits: u64) -> Self {
115        Self(bits)
116    }
117
118    /// Wraps AppleArchive archive flag containment checks.
119    pub const fn contains(self, other: Self) -> bool {
120        (self.0 & other.0) == other.0
121    }
122
123    /// Wraps AppleArchive archive verbosity flag construction.
124    pub const fn verbosity(level: u64) -> Self {
125        let clamped = if level > 3 { 3 } else { level };
126        Self(clamped << 62)
127    }
128}
129
130impl BitOr for ArchiveFlags {
131    type Output = Self;
132
133    fn bitor(self, rhs: Self) -> Self::Output {
134        Self(self.0 | rhs.0)
135    }
136}
137
138impl BitOrAssign for ArchiveFlags {
139    fn bitor_assign(&mut self, rhs: Self) {
140        self.0 |= rhs.0;
141    }
142}
143
144impl BitAnd for ArchiveFlags {
145    type Output = Self;
146
147    fn bitand(self, rhs: Self) -> Self::Output {
148        Self(self.0 & rhs.0)
149    }
150}
151
152impl BitAndAssign for ArchiveFlags {
153    fn bitand_assign(&mut self, rhs: Self) {
154        self.0 &= rhs.0;
155    }
156}
157
158#[allow(dead_code)]
159#[derive(Debug)]
160pub enum ByteStreamUpstream {
161    Stream(Box<ByteStream>),
162}
163
164/// Wraps an `AAByteStream` handle.
165#[derive(Debug)]
166pub struct ByteStream {
167    handle: NonNull<c_void>,
168    _upstream: Option<ByteStreamUpstream>,
169    closed: bool,
170}
171
172impl ByteStream {
173    pub(crate) fn from_handle_with_upstream(
174        handle: *mut c_void,
175        operation: &'static str,
176        upstream: Option<ByteStreamUpstream>,
177    ) -> Result<Self> {
178        Ok(Self {
179            handle: util::nonnull_handle(handle, operation)?,
180            _upstream: upstream,
181            closed: false,
182        })
183    }
184
185    /// Wraps `AAFileStreamOpenWithFD`.
186    pub fn from_fd(fd: RawFd, automatic_close: bool) -> Result<Self> {
187        let handle = unsafe {
188            ffi::aa_byte_stream::compression_rs_aa_byte_stream_open_with_fd(fd, automatic_close)
189        };
190        Ok(Self {
191            handle: util::nonnull_handle(handle, "AAFileStreamOpenWithFD")?,
192            _upstream: None,
193            closed: false,
194        })
195    }
196
197    /// Wraps `AAFileStreamOpenWithFD` through `File` ownership.
198    pub fn from_file(file: File) -> Result<Self> {
199        Self::from_fd(file.into_raw_fd(), true)
200    }
201
202    /// Wraps `AAFileStreamOpenWithPath`.
203    pub fn open_with_path(path: &str, open_flags: i32, open_mode: u32) -> Result<Self> {
204        let path = util::cstring("path", path)?;
205        let handle = unsafe {
206            ffi::aa_byte_stream::compression_rs_aa_byte_stream_open_with_path(
207                path.as_ptr(),
208                open_flags,
209                open_mode,
210            )
211        };
212        Ok(Self {
213            handle: util::nonnull_handle(handle, "AAFileStreamOpenWithPath")?,
214            _upstream: None,
215            closed: false,
216        })
217    }
218
219    /// Wraps `AATempFileStreamOpen`.
220    pub fn temp_file() -> Result<Self> {
221        let handle = unsafe { ffi::aa_byte_stream::compression_rs_aa_temp_file_stream_open() };
222        Ok(Self {
223            handle: util::nonnull_handle(handle, "AATempFileStreamOpen")?,
224            _upstream: None,
225            closed: false,
226        })
227    }
228
229    /// Wraps `AASharedBufferPipeOpen`.
230    pub fn shared_buffer_pipe(buffer_capacity: usize) -> Result<(Self, Self)> {
231        let mut ostream = std::ptr::null_mut();
232        let mut istream = std::ptr::null_mut();
233        let status = unsafe {
234            ffi::aa_byte_stream::compression_rs_aa_shared_buffer_pipe_open(
235                &mut ostream,
236                &mut istream,
237                buffer_capacity,
238            )
239        };
240        util::status_result("AASharedBufferPipeOpen", status)?;
241        Ok((
242            Self {
243                handle: util::nonnull_handle(ostream, "AASharedBufferPipeOpen(ostream)")?,
244                _upstream: None,
245                closed: false,
246            },
247            Self {
248                handle: util::nonnull_handle(istream, "AASharedBufferPipeOpen(istream)")?,
249                _upstream: None,
250                closed: false,
251            },
252        ))
253    }
254
255    pub(crate) fn as_ptr(&self) -> *mut c_void {
256        self.handle.as_ptr()
257    }
258
259    pub(crate) fn mark_closed(&mut self) {
260        self.closed = true;
261    }
262
263    fn ensure_open(&self) -> Result<()> {
264        if self.closed {
265            Err(CompressionError::Closed {
266                resource: "byte stream",
267            })
268        } else {
269            Ok(())
270        }
271    }
272
273    /// Wraps `AACompressionOutputStreamOpen`.
274    pub fn into_compression_output(
275        self,
276        compression_algorithm: ArchiveCompressionAlgorithm,
277        block_size: usize,
278        flags: ArchiveFlags,
279        n_threads: i32,
280    ) -> Result<Self> {
281        let handle = unsafe {
282            ffi::aa_byte_stream::compression_rs_aa_compression_output_stream_open(
283                self.as_ptr(),
284                compression_algorithm.as_raw(),
285                block_size,
286                flags.bits(),
287                n_threads,
288            )
289        };
290        Ok(Self {
291            handle: util::nonnull_handle(handle, "AACompressionOutputStreamOpen")?,
292            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
293            closed: false,
294        })
295    }
296
297    /// Wraps `AACompressionOutputStreamOpenExisting`.
298    pub fn into_existing_compression_output(
299        self,
300        flags: ArchiveFlags,
301        n_threads: i32,
302    ) -> Result<Self> {
303        let handle = unsafe {
304            ffi::aa_byte_stream::compression_rs_aa_compression_output_stream_open_existing(
305                self.as_ptr(),
306                flags.bits(),
307                n_threads,
308            )
309        };
310        Ok(Self {
311            handle: util::nonnull_handle(handle, "AACompressionOutputStreamOpenExisting")?,
312            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
313            closed: false,
314        })
315    }
316
317    /// Wraps `AADecompressionInputStreamOpen`.
318    pub fn into_decompression_input(self, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
319        let handle = unsafe {
320            ffi::aa_byte_stream::compression_rs_aa_decompression_input_stream_open(
321                self.as_ptr(),
322                flags.bits(),
323                n_threads,
324            )
325        };
326        Ok(Self {
327            handle: util::nonnull_handle(handle, "AADecompressionInputStreamOpen")?,
328            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
329            closed: false,
330        })
331    }
332
333    /// Wraps `AADecompressionRandomAccessInputStreamOpen`.
334    pub fn into_random_access_decompression_input(
335        self,
336        alloc_limit: usize,
337        flags: ArchiveFlags,
338        n_threads: i32,
339    ) -> Result<Self> {
340        let handle = unsafe {
341            ffi::aa_byte_stream::compression_rs_aa_decompression_random_access_input_stream_open(
342                self.as_ptr(),
343                alloc_limit,
344                flags.bits(),
345                n_threads,
346            )
347        };
348        Ok(Self {
349            handle: util::nonnull_handle(handle, "AADecompressionRandomAccessInputStreamOpen")?,
350            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
351            closed: false,
352        })
353    }
354
355    /// Wraps `AAByteStreamWrite`.
356    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
357        self.ensure_open()?;
358        util::ssize_result("AAByteStreamWrite", unsafe {
359            ffi::aa_byte_stream::compression_rs_aa_byte_stream_write(
360                self.as_ptr(),
361                buffer.as_ptr(),
362                buffer.len(),
363            )
364        })
365    }
366
367    /// Wraps `AAByteStreamWrite`.
368    pub fn write_all(&mut self, mut buffer: &[u8]) -> Result<()> {
369        while !buffer.is_empty() {
370            let written = self.write(buffer)?;
371            if written == 0 {
372                return Err(CompressionError::OperationFailed {
373                    operation: "AAByteStreamWrite",
374                    code: -1,
375                });
376            }
377            buffer = &buffer[written..];
378        }
379        Ok(())
380    }
381
382    /// Wraps `AAByteStreamPWrite`.
383    pub fn pwrite(&mut self, buffer: &[u8], offset: i64) -> Result<usize> {
384        self.ensure_open()?;
385        util::ssize_result("AAByteStreamPWrite", unsafe {
386            ffi::aa_byte_stream::compression_rs_aa_byte_stream_pwrite(
387                self.as_ptr(),
388                buffer.as_ptr(),
389                buffer.len(),
390                offset,
391            )
392        })
393    }
394
395    /// Wraps `AAByteStreamRead`.
396    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
397        self.ensure_open()?;
398        util::ssize_result("AAByteStreamRead", unsafe {
399            ffi::aa_byte_stream::compression_rs_aa_byte_stream_read(
400                self.as_ptr(),
401                buffer.as_mut_ptr(),
402                buffer.len(),
403            )
404        })
405    }
406
407    /// Wraps `AAByteStreamPRead`.
408    pub fn pread(&mut self, buffer: &mut [u8], offset: i64) -> Result<usize> {
409        self.ensure_open()?;
410        util::ssize_result("AAByteStreamPRead", unsafe {
411            ffi::aa_byte_stream::compression_rs_aa_byte_stream_pread(
412                self.as_ptr(),
413                buffer.as_mut_ptr(),
414                buffer.len(),
415                offset,
416            )
417        })
418    }
419
420    /// Wraps `AAByteStreamSeek`.
421    pub fn seek(&mut self, offset: i64, whence: i32) -> Result<u64> {
422        self.ensure_open()?;
423        util::off_t_result("AAByteStreamSeek", unsafe {
424            ffi::aa_byte_stream::compression_rs_aa_byte_stream_seek(self.as_ptr(), offset, whence)
425        })
426    }
427
428    /// Wraps repeated `AAByteStreamRead` calls.
429    pub fn read_to_end(&mut self) -> Result<Vec<u8>> {
430        let mut output = Vec::new();
431        loop {
432            let mut buffer = vec![0_u8; READ_CHUNK_LEN];
433            let read = self.read(&mut buffer)?;
434            if read == 0 {
435                return Ok(output);
436            }
437            output.extend_from_slice(&buffer[..read]);
438        }
439    }
440
441    /// Wraps `AAByteStreamClose`.
442    pub fn cancel(&mut self) -> Result<()> {
443        self.ensure_open()?;
444        unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_cancel(self.as_ptr()) };
445        Ok(())
446    }
447
448    #[deprecated(
449        since = "0.2.2",
450        note = "Use ByteStream::cancel; AAByteStreamAbort is a deprecated AppleArchive compatibility shim."
451    )]
452    /// Wraps `AAByteStreamClose`.
453    pub fn abort(&mut self) -> Result<()> {
454        self.ensure_open()?;
455        unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_abort(self.as_ptr()) };
456        Ok(())
457    }
458
459    /// Wraps `AAByteStreamClose`.
460    pub fn close(&mut self) -> Result<()> {
461        if self.closed {
462            return Ok(());
463        }
464        let status =
465            unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_close(self.as_ptr()) };
466        self.closed = true;
467        util::status_result("AAByteStreamClose", status)
468    }
469
470    /// Wraps `AAByteStreamProcess`.
471    pub fn process_into(&mut self, output: &mut Self) -> Result<u64> {
472        self.ensure_open()?;
473        output.ensure_open()?;
474        util::off_t_result("AAByteStreamProcess", unsafe {
475            ffi::aa_byte_stream::compression_rs_aa_byte_stream_process(
476                self.as_ptr(),
477                output.as_ptr(),
478            )
479        })
480    }
481
482    /// Wraps `AARandomAccessByteStreamProcess`.
483    pub fn process_random_access_into(
484        &mut self,
485        output: &mut Self,
486        max_offset: i64,
487        block_size: usize,
488        flags: ArchiveFlags,
489        n_threads: i32,
490    ) -> Result<u64> {
491        self.ensure_open()?;
492        output.ensure_open()?;
493        util::off_t_result("AARandomAccessByteStreamProcess", unsafe {
494            ffi::aa_byte_stream::compression_rs_aa_random_access_byte_stream_process(
495                self.as_ptr(),
496                output.as_ptr(),
497                max_offset,
498                block_size,
499                flags.bits(),
500                n_threads,
501            )
502        })
503    }
504}
505
506impl Drop for ByteStream {
507    fn drop(&mut self) {
508        unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_release(self.as_ptr()) };
509    }
510}
511
512fn custom_byte_stream_error(operation: &'static str) -> CompressionError {
513    CompressionError::OperationFailed {
514        operation,
515        code: -1,
516    }
517}
518
519fn custom_byte_stream_code(error: &CompressionError) -> i32 {
520    match error {
521        CompressionError::OperationFailed { code, .. } if *code < 0 => *code,
522        _ => -1,
523    }
524}
525
526struct CustomByteStreamState {
527    callbacks: Box<dyn CustomByteStreamCallbacks>,
528}
529
530/// Wraps callbacks installed by `AACustomByteStreamSet*Proc`.
531pub trait CustomByteStreamCallbacks {
532    /// Wraps `AAByteStreamWrite`.
533    fn write(&mut self, _buffer: &[u8]) -> Result<usize> {
534        Err(custom_byte_stream_error("AAByteStreamWrite"))
535    }
536
537    /// Wraps `AAByteStreamPWrite`.
538    fn pwrite(&mut self, _buffer: &[u8], _offset: i64) -> Result<usize> {
539        Err(custom_byte_stream_error("AAByteStreamPWrite"))
540    }
541
542    /// Wraps `AAByteStreamRead`.
543    fn read(&mut self, _buffer: &mut [u8]) -> Result<usize> {
544        Err(custom_byte_stream_error("AAByteStreamRead"))
545    }
546
547    /// Wraps `AAByteStreamPRead`.
548    fn pread(&mut self, _buffer: &mut [u8], _offset: i64) -> Result<usize> {
549        Err(custom_byte_stream_error("AAByteStreamPRead"))
550    }
551
552    /// Wraps `AAByteStreamSeek`.
553    fn seek(&mut self, _offset: i64, _whence: i32) -> Result<i64> {
554        Err(custom_byte_stream_error("AAByteStreamSeek"))
555    }
556
557    /// Wraps the `cancel` convenience for `CustomByteStreamCallbacks`.
558    fn cancel(&mut self) {}
559
560    /// Wraps the `close` convenience for `CustomByteStreamCallbacks`.
561    fn close(&mut self) -> Result<()> {
562        Ok(())
563    }
564}
565
566unsafe fn custom_byte_stream_slice<'a>(buffer: *const c_void, length: usize) -> Option<&'a [u8]> {
567    if length == 0 {
568        Some(&[])
569    } else if buffer.is_null() {
570        None
571    } else {
572        Some(unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), length) })
573    }
574}
575
576unsafe fn custom_byte_stream_slice_mut<'a>(
577    buffer: *mut c_void,
578    length: usize,
579) -> Option<&'a mut [u8]> {
580    if length == 0 {
581        Some(&mut [])
582    } else if buffer.is_null() {
583        None
584    } else {
585        Some(unsafe { std::slice::from_raw_parts_mut(buffer.cast::<u8>(), length) })
586    }
587}
588
589unsafe fn custom_byte_stream_state(arg: *mut c_void) -> Option<&'static mut CustomByteStreamState> {
590    if arg.is_null() {
591        None
592    } else {
593        Some(unsafe { &mut *arg.cast::<CustomByteStreamState>() })
594    }
595}
596
597unsafe extern "C" fn custom_byte_stream_write(
598    arg: *mut c_void,
599    buffer: *const c_void,
600    length: usize,
601) -> i64 {
602    let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
603        return -1;
604    };
605    let Some(buffer) = (unsafe { custom_byte_stream_slice(buffer, length) }) else {
606        return -1;
607    };
608    match state.callbacks.write(buffer) {
609        Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
610        Err(error) => i64::from(custom_byte_stream_code(&error)),
611    }
612}
613
614unsafe extern "C" fn custom_byte_stream_pwrite(
615    arg: *mut c_void,
616    buffer: *const c_void,
617    length: usize,
618    offset: i64,
619) -> i64 {
620    let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
621        return -1;
622    };
623    let Some(buffer) = (unsafe { custom_byte_stream_slice(buffer, length) }) else {
624        return -1;
625    };
626    match state.callbacks.pwrite(buffer, offset) {
627        Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
628        Err(error) => i64::from(custom_byte_stream_code(&error)),
629    }
630}
631
632unsafe extern "C" fn custom_byte_stream_read(
633    arg: *mut c_void,
634    buffer: *mut c_void,
635    length: usize,
636) -> i64 {
637    let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
638        return -1;
639    };
640    let Some(buffer) = (unsafe { custom_byte_stream_slice_mut(buffer, length) }) else {
641        return -1;
642    };
643    match state.callbacks.read(buffer) {
644        Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
645        Err(error) => i64::from(custom_byte_stream_code(&error)),
646    }
647}
648
649unsafe extern "C" fn custom_byte_stream_pread(
650    arg: *mut c_void,
651    buffer: *mut c_void,
652    length: usize,
653    offset: i64,
654) -> i64 {
655    let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
656        return -1;
657    };
658    let Some(buffer) = (unsafe { custom_byte_stream_slice_mut(buffer, length) }) else {
659        return -1;
660    };
661    match state.callbacks.pread(buffer, offset) {
662        Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
663        Err(error) => i64::from(custom_byte_stream_code(&error)),
664    }
665}
666
667unsafe extern "C" fn custom_byte_stream_seek(arg: *mut c_void, offset: i64, whence: i32) -> i64 {
668    let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
669        return -1;
670    };
671    match state.callbacks.seek(offset, whence) {
672        Ok(position) => position,
673        Err(error) => i64::from(custom_byte_stream_code(&error)),
674    }
675}
676
677unsafe extern "C" fn custom_byte_stream_cancel(arg: *mut c_void) {
678    if let Some(state) = unsafe { custom_byte_stream_state(arg) } {
679        state.callbacks.cancel();
680    }
681}
682
683unsafe extern "C" fn custom_byte_stream_close(arg: *mut c_void) -> i32 {
684    if arg.is_null() {
685        return 0;
686    }
687    let mut state = unsafe { Box::from_raw(arg.cast::<CustomByteStreamState>()) };
688    match state.callbacks.close() {
689        Ok(()) => 0,
690        Err(error) => custom_byte_stream_code(&error),
691    }
692}
693
694impl ByteStream {
695    /// Wraps `AACustomByteStreamOpen`.
696    pub fn custom<T: CustomByteStreamCallbacks + 'static>(callbacks: T) -> Result<Self> {
697        let handle = unsafe { ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_open() };
698        let stream = Self::from_handle_with_upstream(handle, "AACustomByteStreamOpen", None)?;
699        let state = Box::new(CustomByteStreamState {
700            callbacks: Box::new(callbacks),
701        });
702        let data = Box::into_raw(state).cast::<c_void>();
703        unsafe {
704            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_data(
705                stream.as_ptr(),
706                data,
707            );
708            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_write_proc(
709                stream.as_ptr(),
710                Some(custom_byte_stream_write),
711            );
712            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_pwrite_proc(
713                stream.as_ptr(),
714                Some(custom_byte_stream_pwrite),
715            );
716            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_read_proc(
717                stream.as_ptr(),
718                Some(custom_byte_stream_read),
719            );
720            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_pread_proc(
721                stream.as_ptr(),
722                Some(custom_byte_stream_pread),
723            );
724            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_seek_proc(
725                stream.as_ptr(),
726                Some(custom_byte_stream_seek),
727            );
728            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_cancel_proc(
729                stream.as_ptr(),
730                Some(custom_byte_stream_cancel),
731            );
732            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_abort_proc(
733                stream.as_ptr(),
734                Some(custom_byte_stream_cancel),
735            );
736            ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_close_proc(
737                stream.as_ptr(),
738                Some(custom_byte_stream_close),
739            );
740        }
741        Ok(stream)
742    }
743}