1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
////////////////////////////////////////////////////////////////////////////////
// This Source Code Form is subject to the terms of the Mozilla Public /
// License, v. 2.0. If a copy of the MPL was not distributed with this /
// file, You can obtain one at https://mozilla.org/MPL/2.0/. /
// /
////////////////////////////////////////////////////////////////////////////////
//! Decompression parsing, algorithms, and functionality
use std::io::{Cursor, Read, Seek, Write};
use crate::data::control::Command;
use crate::data::{copy_from_reader, rle_decode_fixed};
use crate::format::Format;
use crate::header::Header;
use crate::RefPackError;
// Returning the internal buffer is the fastest way to return the data
// since that way the buffer doesn't have to be copied,
// this function is used to reach optimal performance
fn decompress_internal<F: Format>(
reader: &mut (impl Read + Seek),
) -> Result<Vec<u8>, RefPackError> {
let Header {
decompressed_length,
..
} = Header::read::<F::HeaderMode>(reader)?;
let mut decompression_buffer = vec![0; decompressed_length as usize];
let mut position = 0usize;
loop {
let command = Command::read::<F::ControlMode>(reader)?;
match command {
Command::Short {
offset,
length,
literal,
}
| Command::Medium {
offset,
length,
literal,
} => {
if literal > 0 {
position = copy_from_reader(
&mut decompression_buffer,
reader,
position,
literal as usize,
)?;
}
position = rle_decode_fixed(
&mut decompression_buffer,
position,
offset as usize,
length as usize,
)?;
}
Command::Long {
offset,
length,
literal,
} => {
if literal > 0 {
position = copy_from_reader(
&mut decompression_buffer,
reader,
position,
literal as usize,
)?;
}
position = rle_decode_fixed(
&mut decompression_buffer,
position,
offset as usize,
length as usize,
)?;
}
Command::Literal(literal) => {
position = copy_from_reader(
&mut decompression_buffer,
reader,
position,
literal as usize,
)?;
}
Command::Stop(literal) => {
copy_from_reader(
&mut decompression_buffer,
reader,
position,
literal as usize,
)?;
break;
}
}
}
Ok(decompression_buffer)
}
/// Decompress `refpack` data. Accepts arbitrary `Read`s and `Write`s.
///
/// # Example
///
/// ```Rust
/// use std::io::Cursor;
///
/// let mut input = Cursor::new(/* some refpack data */);
/// let mut output = Cursor::new(Vec::new());
///
/// // decompress the input into the output
/// refpack::compress(&mut input, &mut output);
/// // output now contains the decompressed version of the input
///
/// ```
/// # Errors
///
/// - Will return `Error::InvalidMagic` if the header is malformed, indicating uncompressed data or
/// attempting to decompress data in the incorrect format
/// - Will return `Error::Io` if there is an IO error
pub fn decompress<F: Format>(
reader: &mut (impl Read + Seek),
writer: &mut impl Write,
) -> Result<(), RefPackError> {
let data = decompress_internal::<F>(reader)?;
writer.write_all(data.as_slice())?;
writer.flush()?;
Ok(())
}
/// Wrapped decompress function with a bit easier and cleaner of an API.
/// Takes a slice of bytes and returns a Vec of byes
/// In implementation this just creates `Cursor`s for the reader and writer and calls `decompress`
///
/// # Returns
///
/// A Result containing either `Vec<u8>` of the decompressed data or a `RefPackError`.
///
/// # Errors
///
/// Will return `Error::InvalidMagic` if the header is malformed, indicating uncompressed data
/// Will return `Error::Io` if there is an IO error
#[inline]
pub fn easy_decompress<F: Format>(input: &[u8]) -> Result<Vec<u8>, RefPackError> {
let mut reader = Cursor::new(input);
decompress_internal::<F>(&mut reader)
}