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
10pub const OPEN_READ_ONLY: i32 = 0x0000;
12pub const OPEN_WRITE_ONLY: i32 = 0x0001;
14pub const OPEN_READ_WRITE: i32 = 0x0002;
16pub const OPEN_CREATE: i32 = 0x0200;
18pub const OPEN_TRUNCATE: i32 = 0x0400;
20pub const DEFAULT_FILE_MODE: u32 = 0o644;
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
25pub enum ArchiveCompressionAlgorithm {
26 None,
28 Lz4,
30 Zlib,
32 Lzma,
34 Lzfse,
36 Lzbitmap,
38}
39
40impl ArchiveCompressionAlgorithm {
41 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 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#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
69pub struct ArchiveFlags(u64);
70
71impl ArchiveFlags {
72 pub const IGNORE_EPERM: Self = Self(1_u64 << 0);
74 pub const ARCHIVE_DEDUPLICATE_DAT: Self = Self(1_u64 << 1);
76 pub const ARCHIVE_NO_RESOLVE_ACL_QUALIFIERS: Self = Self(1_u64 << 2);
78 pub const REPLACE_ATTRIBUTES: Self = Self(1_u64 << 3);
80 pub const EXTRACT_NO_AUTO_DEDUP: Self = Self(1_u64 << 4);
82 pub const EXTRACT_NO_AUTO_SPARSE: Self = Self(1_u64 << 5);
84 pub const CROSS_VOLUME_BOUNDARIES: Self = Self(1_u64 << 6);
86 pub const EXTRACT_AUTO_DEDUP_AS_HARD_LINKS: Self = Self(1_u64 << 7);
88 pub const DECODE_INSERT_IDX: Self = Self(1_u64 << 8);
90 pub const EXCLUDE_METADATA_ENTRIES: Self = Self(1_u64 << 9);
92 pub const PROCESS_RANDOM_ACCESS_OUTPUT: Self = Self(1_u64 << 10);
94 pub const VERBOSITY_0: Self = Self(0_u64 << 62);
96 pub const VERBOSITY_1: Self = Self(1_u64 << 62);
98 pub const VERBOSITY_2: Self = Self(2_u64 << 62);
100 pub const VERBOSITY_3: Self = Self(3_u64 << 62);
102
103 pub const fn empty() -> Self {
105 Self(0)
106 }
107
108 pub const fn bits(self) -> u64 {
110 self.0
111 }
112
113 pub const fn from_bits(bits: u64) -> Self {
115 Self(bits)
116 }
117
118 pub const fn contains(self, other: Self) -> bool {
120 (self.0 & other.0) == other.0
121 }
122
123 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#[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 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 pub fn from_file(file: File) -> Result<Self> {
199 Self::from_fd(file.into_raw_fd(), true)
200 }
201
202 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
530pub trait CustomByteStreamCallbacks {
532 fn write(&mut self, _buffer: &[u8]) -> Result<usize> {
534 Err(custom_byte_stream_error("AAByteStreamWrite"))
535 }
536
537 fn pwrite(&mut self, _buffer: &[u8], _offset: i64) -> Result<usize> {
539 Err(custom_byte_stream_error("AAByteStreamPWrite"))
540 }
541
542 fn read(&mut self, _buffer: &mut [u8]) -> Result<usize> {
544 Err(custom_byte_stream_error("AAByteStreamRead"))
545 }
546
547 fn pread(&mut self, _buffer: &mut [u8], _offset: i64) -> Result<usize> {
549 Err(custom_byte_stream_error("AAByteStreamPRead"))
550 }
551
552 fn seek(&mut self, _offset: i64, _whence: i32) -> Result<i64> {
554 Err(custom_byte_stream_error("AAByteStreamSeek"))
555 }
556
557 fn cancel(&mut self) {}
559
560 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
597fn guard_byte_i64(f: impl FnOnce() -> i64) -> i64 {
600 std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or(-1)
601}
602
603unsafe extern "C" fn custom_byte_stream_write(
604 arg: *mut c_void,
605 buffer: *const c_void,
606 length: usize,
607) -> i64 {
608 let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
609 return -1;
610 };
611 let Some(buffer) = (unsafe { custom_byte_stream_slice(buffer, length) }) else {
612 return -1;
613 };
614 guard_byte_i64(|| match state.callbacks.write(buffer) {
615 Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
616 Err(error) => i64::from(custom_byte_stream_code(&error)),
617 })
618}
619
620unsafe extern "C" fn custom_byte_stream_pwrite(
621 arg: *mut c_void,
622 buffer: *const c_void,
623 length: usize,
624 offset: i64,
625) -> i64 {
626 let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
627 return -1;
628 };
629 let Some(buffer) = (unsafe { custom_byte_stream_slice(buffer, length) }) else {
630 return -1;
631 };
632 guard_byte_i64(|| match state.callbacks.pwrite(buffer, offset) {
633 Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
634 Err(error) => i64::from(custom_byte_stream_code(&error)),
635 })
636}
637
638unsafe extern "C" fn custom_byte_stream_read(
639 arg: *mut c_void,
640 buffer: *mut c_void,
641 length: usize,
642) -> i64 {
643 let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
644 return -1;
645 };
646 let Some(buffer) = (unsafe { custom_byte_stream_slice_mut(buffer, length) }) else {
647 return -1;
648 };
649 guard_byte_i64(|| match state.callbacks.read(buffer) {
650 Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
651 Err(error) => i64::from(custom_byte_stream_code(&error)),
652 })
653}
654
655unsafe extern "C" fn custom_byte_stream_pread(
656 arg: *mut c_void,
657 buffer: *mut c_void,
658 length: usize,
659 offset: i64,
660) -> i64 {
661 let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
662 return -1;
663 };
664 let Some(buffer) = (unsafe { custom_byte_stream_slice_mut(buffer, length) }) else {
665 return -1;
666 };
667 guard_byte_i64(|| match state.callbacks.pread(buffer, offset) {
668 Ok(count) => i64::try_from(count).unwrap_or(i64::MAX),
669 Err(error) => i64::from(custom_byte_stream_code(&error)),
670 })
671}
672
673unsafe extern "C" fn custom_byte_stream_seek(arg: *mut c_void, offset: i64, whence: i32) -> i64 {
674 let Some(state) = (unsafe { custom_byte_stream_state(arg) }) else {
675 return -1;
676 };
677 guard_byte_i64(|| match state.callbacks.seek(offset, whence) {
678 Ok(position) => position,
679 Err(error) => i64::from(custom_byte_stream_code(&error)),
680 })
681}
682
683unsafe extern "C" fn custom_byte_stream_cancel(arg: *mut c_void) {
684 if let Some(state) = unsafe { custom_byte_stream_state(arg) } {
685 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| state.callbacks.cancel()));
686 }
687}
688
689unsafe extern "C" fn custom_byte_stream_close(arg: *mut c_void) -> i32 {
690 if arg.is_null() {
691 return 0;
692 }
693 let mut state = unsafe { Box::from_raw(arg.cast::<CustomByteStreamState>()) };
694 std::panic::catch_unwind(std::panic::AssertUnwindSafe(
695 || match state.callbacks.close() {
696 Ok(()) => 0,
697 Err(error) => custom_byte_stream_code(&error),
698 },
699 ))
700 .unwrap_or(-1)
701}
702
703impl ByteStream {
704 pub fn custom<T: CustomByteStreamCallbacks + 'static>(callbacks: T) -> Result<Self> {
706 let handle = unsafe { ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_open() };
707 let stream = Self::from_handle_with_upstream(handle, "AACustomByteStreamOpen", None)?;
708 let state = Box::new(CustomByteStreamState {
709 callbacks: Box::new(callbacks),
710 });
711 let data = Box::into_raw(state).cast::<c_void>();
712 unsafe {
713 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_data(
714 stream.as_ptr(),
715 data,
716 );
717 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_write_proc(
718 stream.as_ptr(),
719 Some(custom_byte_stream_write),
720 );
721 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_pwrite_proc(
722 stream.as_ptr(),
723 Some(custom_byte_stream_pwrite),
724 );
725 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_read_proc(
726 stream.as_ptr(),
727 Some(custom_byte_stream_read),
728 );
729 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_pread_proc(
730 stream.as_ptr(),
731 Some(custom_byte_stream_pread),
732 );
733 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_seek_proc(
734 stream.as_ptr(),
735 Some(custom_byte_stream_seek),
736 );
737 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_cancel_proc(
738 stream.as_ptr(),
739 Some(custom_byte_stream_cancel),
740 );
741 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_abort_proc(
742 stream.as_ptr(),
743 Some(custom_byte_stream_cancel),
744 );
745 ffi::aa_byte_stream::compression_rs_aa_custom_byte_stream_set_close_proc(
746 stream.as_ptr(),
747 Some(custom_byte_stream_close),
748 );
749 }
750 Ok(stream)
751 }
752}