use crate::{
ExitCode,
preflate_error::{Result, err_exit_code},
};
pub struct PlainText {
data: Vec<u8>,
prefix_length: i32,
pos_offset: i32,
}
impl std::fmt::Debug for PlainText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"PlainText {{ prefix_length: {}, pos_offset:{} data: len={} }}",
self.prefix_length,
self.pos_offset,
self.data.len()
)
}
}
impl Clone for PlainText {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
prefix_length: self.prefix_length,
pos_offset: self.pos_offset,
}
}
}
impl PlainText {
pub fn new() -> Self {
Self {
data: Vec::new(),
prefix_length: 0,
pos_offset: 0,
}
}
pub fn shrink_to_dictionary(&mut self) {
self.pos_offset += self.data.len() as i32 - self.prefix_length;
let amount_to_keep = self.data.len().min(32768);
self.data.drain(..self.data.len() - amount_to_keep);
self.prefix_length = self.data.len() as i32;
}
pub fn new_with_data(data: Vec<u8>) -> Self {
Self {
data,
prefix_length: 0,
pos_offset: 0,
}
}
pub fn len(&self) -> usize {
self.data.len() - (self.prefix_length as usize)
}
pub fn total_length(&self) -> u32 {
self.pos_offset as u32 + self.len() as u32
}
pub fn text(&self) -> &[u8] {
&self.data[self.prefix_length as usize..]
}
pub fn prefix(&self) -> &[u8] {
&self.data[0..self.prefix_length as usize]
}
pub fn truncate(&mut self, len: usize) {
self.data.truncate(self.prefix_length as usize + len);
}
pub fn push(&mut self, c: u8) {
self.data.push(c);
}
pub fn append(&mut self, data: &[u8]) {
self.data.extend_from_slice(data);
}
pub fn append_iter(&mut self, data: impl Iterator<Item = u8>) {
self.data.extend(data);
}
#[inline(always)]
pub fn append_reference(&mut self, dist: u32, len: u32) -> Result<()> {
if dist as usize > self.data.len() {
return err_exit_code(ExitCode::InvalidDeflate, "Invalid distance in reference");
}
if dist == 1 {
let byte = self.data[self.data.len() - 1];
self.data.resize(self.data.len() + len as usize, byte);
} else if dist >= len {
self.data.extend_from_within(
self.data.len() - dist as usize..self.data.len() - dist as usize + len as usize,
);
} else {
let start = self.data.len() - dist as usize;
self.data.reserve(len as usize);
for i in 0..len {
let byte = self.data[start + i as usize];
self.data.push(byte);
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct PreflateInput<'a> {
data: &'a PlainText,
pos: i32,
}
impl<'a> PreflateInput<'a> {
pub fn new(v: &'a PlainText) -> Self {
PreflateInput {
data: v,
pos: v.prefix_length,
}
}
#[inline(always)]
pub fn pos(&self) -> u32 {
(self.pos + self.data.pos_offset - self.data.prefix_length) as u32
}
#[inline(always)]
pub fn total_length(&self) -> u32 {
self.data.total_length()
}
#[inline(always)]
pub fn cur_chars(&self, offset: i32) -> &[u8] {
&self.data.data[(self.pos + offset) as usize..]
}
#[inline(always)]
pub fn cur_char(&self, offset: i32) -> u8 {
self.data.data[(self.pos + offset) as usize]
}
#[inline(always)]
pub fn advance(&mut self, l: u32) {
self.pos += l as i32;
debug_assert!((self.pos) <= self.data.data.len() as i32);
}
#[inline(always)]
pub fn remaining(&self) -> u32 {
(self.data.data.len() as i32 - self.pos) as u32
}
}
#[test]
fn test_length_behavior() {
let mut data = PlainText::new_with_data(vec![0; 10000]);
let mut input = PreflateInput::new(&data);
assert_eq!(input.total_length(), 10000);
assert_eq!(input.pos(), 0);
assert_eq!(input.remaining(), 10000);
input.advance(1000);
assert_eq!(input.total_length(), 10000);
assert_eq!(input.pos(), 1000);
assert_eq!(input.remaining(), 9000);
input.advance(9000);
assert_eq!(input.total_length(), 10000);
assert_eq!(input.pos(), 10000);
assert_eq!(input.remaining(), 0);
data.shrink_to_dictionary();
data.append(&[1; 10000]);
let mut input = PreflateInput::new(&data);
assert_eq!(input.total_length(), 20000);
assert_eq!(input.pos(), 10000);
assert_eq!(input.remaining(), 10000);
assert_eq!(input.cur_char(0), 1);
assert_eq!(input.cur_char(-1), 0);
assert_eq!(input.cur_char(-1000), 0);
input.advance(1000);
assert_eq!(input.total_length(), 20000);
assert_eq!(input.pos(), 11000);
assert_eq!(input.remaining(), 9000);
assert_eq!(input.cur_char(-1), 1);
assert_eq!(input.cur_char(-1000), 1);
}