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}