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
10pub const OPEN_READ_ONLY: i32 = 0x0000;
11pub const OPEN_WRITE_ONLY: i32 = 0x0001;
12pub const OPEN_READ_WRITE: i32 = 0x0002;
13pub const OPEN_CREATE: i32 = 0x0200;
14pub const OPEN_TRUNCATE: i32 = 0x0400;
15pub const DEFAULT_FILE_MODE: u32 = 0o644;
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
18pub enum ArchiveCompressionAlgorithm {
19    None,
20    Lz4,
21    Zlib,
22    Lzma,
23    Lzfse,
24    Lzbitmap,
25}
26
27impl ArchiveCompressionAlgorithm {
28    pub const fn as_raw(self) -> u32 {
29        match self {
30            Self::None => 0x000,
31            Self::Lz4 => 0x100,
32            Self::Zlib => 0x505,
33            Self::Lzma => 0x306,
34            Self::Lzfse => 0x801,
35            Self::Lzbitmap => 0x702,
36        }
37    }
38}
39
40#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
41pub struct ArchiveFlags(u64);
42
43impl ArchiveFlags {
44    pub const IGNORE_EPERM: Self = Self(1_u64 << 0);
45    pub const ARCHIVE_DEDUPLICATE_DAT: Self = Self(1_u64 << 1);
46    pub const ARCHIVE_NO_RESOLVE_ACL_QUALIFIERS: Self = Self(1_u64 << 2);
47    pub const REPLACE_ATTRIBUTES: Self = Self(1_u64 << 3);
48    pub const EXTRACT_NO_AUTO_DEDUP: Self = Self(1_u64 << 4);
49    pub const EXTRACT_NO_AUTO_SPARSE: Self = Self(1_u64 << 5);
50    pub const CROSS_VOLUME_BOUNDARIES: Self = Self(1_u64 << 6);
51    pub const EXTRACT_AUTO_DEDUP_AS_HARD_LINKS: Self = Self(1_u64 << 7);
52    pub const DECODE_INSERT_IDX: Self = Self(1_u64 << 8);
53    pub const EXCLUDE_METADATA_ENTRIES: Self = Self(1_u64 << 9);
54    pub const PROCESS_RANDOM_ACCESS_OUTPUT: Self = Self(1_u64 << 10);
55    pub const VERBOSITY_0: Self = Self(0_u64 << 62);
56    pub const VERBOSITY_1: Self = Self(1_u64 << 62);
57    pub const VERBOSITY_2: Self = Self(2_u64 << 62);
58    pub const VERBOSITY_3: Self = Self(3_u64 << 62);
59
60    pub const fn empty() -> Self {
61        Self(0)
62    }
63
64    pub const fn bits(self) -> u64 {
65        self.0
66    }
67
68    pub const fn from_bits(bits: u64) -> Self {
69        Self(bits)
70    }
71
72    pub const fn contains(self, other: Self) -> bool {
73        (self.0 & other.0) == other.0
74    }
75
76    pub const fn verbosity(level: u64) -> Self {
77        let clamped = if level > 3 { 3 } else { level };
78        Self(clamped << 62)
79    }
80}
81
82impl BitOr for ArchiveFlags {
83    type Output = Self;
84
85    fn bitor(self, rhs: Self) -> Self::Output {
86        Self(self.0 | rhs.0)
87    }
88}
89
90impl BitOrAssign for ArchiveFlags {
91    fn bitor_assign(&mut self, rhs: Self) {
92        self.0 |= rhs.0;
93    }
94}
95
96impl BitAnd for ArchiveFlags {
97    type Output = Self;
98
99    fn bitand(self, rhs: Self) -> Self::Output {
100        Self(self.0 & rhs.0)
101    }
102}
103
104impl BitAndAssign for ArchiveFlags {
105    fn bitand_assign(&mut self, rhs: Self) {
106        self.0 &= rhs.0;
107    }
108}
109
110#[allow(dead_code)]
111#[derive(Debug)]
112enum ByteStreamUpstream {
113    Stream(Box<ByteStream>),
114}
115
116#[derive(Debug)]
117pub struct ByteStream {
118    handle: NonNull<c_void>,
119    _upstream: Option<ByteStreamUpstream>,
120    closed: bool,
121}
122
123impl ByteStream {
124    pub fn from_fd(fd: RawFd, automatic_close: bool) -> Result<Self> {
125        let handle = unsafe {
126            ffi::aa_byte_stream::compression_rs_aa_byte_stream_open_with_fd(fd, automatic_close)
127        };
128        Ok(Self {
129            handle: util::nonnull_handle(handle, "AAFileStreamOpenWithFD")?,
130            _upstream: None,
131            closed: false,
132        })
133    }
134
135    pub fn from_file(file: File) -> Result<Self> {
136        Self::from_fd(file.into_raw_fd(), true)
137    }
138
139    pub fn open_with_path(path: &str, open_flags: i32, open_mode: u32) -> Result<Self> {
140        let path = util::cstring("path", path)?;
141        let handle = unsafe {
142            ffi::aa_byte_stream::compression_rs_aa_byte_stream_open_with_path(
143                path.as_ptr(),
144                open_flags,
145                open_mode,
146            )
147        };
148        Ok(Self {
149            handle: util::nonnull_handle(handle, "AAFileStreamOpenWithPath")?,
150            _upstream: None,
151            closed: false,
152        })
153    }
154
155    pub fn temp_file() -> Result<Self> {
156        let handle = unsafe { ffi::aa_byte_stream::compression_rs_aa_temp_file_stream_open() };
157        Ok(Self {
158            handle: util::nonnull_handle(handle, "AATempFileStreamOpen")?,
159            _upstream: None,
160            closed: false,
161        })
162    }
163
164    pub fn shared_buffer_pipe(buffer_capacity: usize) -> Result<(Self, Self)> {
165        let mut ostream = std::ptr::null_mut();
166        let mut istream = std::ptr::null_mut();
167        let status = unsafe {
168            ffi::aa_byte_stream::compression_rs_aa_shared_buffer_pipe_open(
169                &mut ostream,
170                &mut istream,
171                buffer_capacity,
172            )
173        };
174        util::status_result("AASharedBufferPipeOpen", status)?;
175        Ok((
176            Self {
177                handle: util::nonnull_handle(ostream, "AASharedBufferPipeOpen(ostream)")?,
178                _upstream: None,
179                closed: false,
180            },
181            Self {
182                handle: util::nonnull_handle(istream, "AASharedBufferPipeOpen(istream)")?,
183                _upstream: None,
184                closed: false,
185            },
186        ))
187    }
188
189    pub(crate) fn as_ptr(&self) -> *mut c_void {
190        self.handle.as_ptr()
191    }
192
193    fn ensure_open(&self) -> Result<()> {
194        if self.closed {
195            Err(CompressionError::Closed {
196                resource: "byte stream",
197            })
198        } else {
199            Ok(())
200        }
201    }
202
203    pub fn into_compression_output(
204        self,
205        compression_algorithm: ArchiveCompressionAlgorithm,
206        block_size: usize,
207        flags: ArchiveFlags,
208        n_threads: i32,
209    ) -> Result<Self> {
210        let handle = unsafe {
211            ffi::aa_byte_stream::compression_rs_aa_compression_output_stream_open(
212                self.as_ptr(),
213                compression_algorithm.as_raw(),
214                block_size,
215                flags.bits(),
216                n_threads,
217            )
218        };
219        Ok(Self {
220            handle: util::nonnull_handle(handle, "AACompressionOutputStreamOpen")?,
221            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
222            closed: false,
223        })
224    }
225
226    pub fn into_existing_compression_output(
227        self,
228        flags: ArchiveFlags,
229        n_threads: i32,
230    ) -> Result<Self> {
231        let handle = unsafe {
232            ffi::aa_byte_stream::compression_rs_aa_compression_output_stream_open_existing(
233                self.as_ptr(),
234                flags.bits(),
235                n_threads,
236            )
237        };
238        Ok(Self {
239            handle: util::nonnull_handle(handle, "AACompressionOutputStreamOpenExisting")?,
240            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
241            closed: false,
242        })
243    }
244
245    pub fn into_decompression_input(self, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
246        let handle = unsafe {
247            ffi::aa_byte_stream::compression_rs_aa_decompression_input_stream_open(
248                self.as_ptr(),
249                flags.bits(),
250                n_threads,
251            )
252        };
253        Ok(Self {
254            handle: util::nonnull_handle(handle, "AADecompressionInputStreamOpen")?,
255            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
256            closed: false,
257        })
258    }
259
260    pub fn into_random_access_decompression_input(
261        self,
262        alloc_limit: usize,
263        flags: ArchiveFlags,
264        n_threads: i32,
265    ) -> Result<Self> {
266        let handle = unsafe {
267            ffi::aa_byte_stream::compression_rs_aa_decompression_random_access_input_stream_open(
268                self.as_ptr(),
269                alloc_limit,
270                flags.bits(),
271                n_threads,
272            )
273        };
274        Ok(Self {
275            handle: util::nonnull_handle(handle, "AADecompressionRandomAccessInputStreamOpen")?,
276            _upstream: Some(ByteStreamUpstream::Stream(Box::new(self))),
277            closed: false,
278        })
279    }
280
281    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
282        self.ensure_open()?;
283        util::ssize_result("AAByteStreamWrite", unsafe {
284            ffi::aa_byte_stream::compression_rs_aa_byte_stream_write(
285                self.as_ptr(),
286                buffer.as_ptr(),
287                buffer.len(),
288            )
289        })
290    }
291
292    pub fn write_all(&mut self, mut buffer: &[u8]) -> Result<()> {
293        while !buffer.is_empty() {
294            let written = self.write(buffer)?;
295            if written == 0 {
296                return Err(CompressionError::OperationFailed {
297                    operation: "AAByteStreamWrite",
298                    code: -1,
299                });
300            }
301            buffer = &buffer[written..];
302        }
303        Ok(())
304    }
305
306    pub fn pwrite(&mut self, buffer: &[u8], offset: i64) -> Result<usize> {
307        self.ensure_open()?;
308        util::ssize_result("AAByteStreamPWrite", unsafe {
309            ffi::aa_byte_stream::compression_rs_aa_byte_stream_pwrite(
310                self.as_ptr(),
311                buffer.as_ptr(),
312                buffer.len(),
313                offset,
314            )
315        })
316    }
317
318    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
319        self.ensure_open()?;
320        util::ssize_result("AAByteStreamRead", unsafe {
321            ffi::aa_byte_stream::compression_rs_aa_byte_stream_read(
322                self.as_ptr(),
323                buffer.as_mut_ptr(),
324                buffer.len(),
325            )
326        })
327    }
328
329    pub fn pread(&mut self, buffer: &mut [u8], offset: i64) -> Result<usize> {
330        self.ensure_open()?;
331        util::ssize_result("AAByteStreamPRead", unsafe {
332            ffi::aa_byte_stream::compression_rs_aa_byte_stream_pread(
333                self.as_ptr(),
334                buffer.as_mut_ptr(),
335                buffer.len(),
336                offset,
337            )
338        })
339    }
340
341    pub fn seek(&mut self, offset: i64, whence: i32) -> Result<u64> {
342        self.ensure_open()?;
343        util::off_t_result("AAByteStreamSeek", unsafe {
344            ffi::aa_byte_stream::compression_rs_aa_byte_stream_seek(self.as_ptr(), offset, whence)
345        })
346    }
347
348    pub fn read_to_end(&mut self) -> Result<Vec<u8>> {
349        let mut output = Vec::new();
350        loop {
351            let mut buffer = vec![0_u8; READ_CHUNK_LEN];
352            let read = self.read(&mut buffer)?;
353            if read == 0 {
354                return Ok(output);
355            }
356            output.extend_from_slice(&buffer[..read]);
357        }
358    }
359
360    pub fn cancel(&mut self) -> Result<()> {
361        self.ensure_open()?;
362        unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_cancel(self.as_ptr()) };
363        Ok(())
364    }
365
366    pub fn close(&mut self) -> Result<()> {
367        if self.closed {
368            return Ok(());
369        }
370        let status =
371            unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_close(self.as_ptr()) };
372        self.closed = true;
373        util::status_result("AAByteStreamClose", status)
374    }
375
376    pub fn process_into(&mut self, output: &mut Self) -> Result<u64> {
377        self.ensure_open()?;
378        output.ensure_open()?;
379        util::off_t_result("AAByteStreamProcess", unsafe {
380            ffi::aa_byte_stream::compression_rs_aa_byte_stream_process(
381                self.as_ptr(),
382                output.as_ptr(),
383            )
384        })
385    }
386
387    pub fn process_random_access_into(
388        &mut self,
389        output: &mut Self,
390        max_offset: i64,
391        block_size: usize,
392        flags: ArchiveFlags,
393        n_threads: i32,
394    ) -> Result<u64> {
395        self.ensure_open()?;
396        output.ensure_open()?;
397        util::off_t_result("AARandomAccessByteStreamProcess", unsafe {
398            ffi::aa_byte_stream::compression_rs_aa_random_access_byte_stream_process(
399                self.as_ptr(),
400                output.as_ptr(),
401                max_offset,
402                block_size,
403                flags.bits(),
404                n_threads,
405            )
406        })
407    }
408}
409
410impl Drop for ByteStream {
411    fn drop(&mut self) {
412        unsafe { ffi::aa_byte_stream::compression_rs_aa_byte_stream_release(self.as_ptr()) };
413    }
414}