use crate::{
Error, MAGIC_SKIPPABLE_START, SEEKABLE_MAGIC_NUMBER, SEEK_TABLE_FOOTER_SIZE,
SKIPPABLE_HEADER_SIZE,
};
const SEEKABLE_MAX_FRAMES: usize = 0x8000000;
#[derive(Clone)]
struct FrameLogEntry {
c_size: u32,
d_size: u32,
checksum: u32,
}
pub struct FrameLog {
entries: Vec<FrameLogEntry>,
pub checksum_flag: u32,
seek_table_pos: u32,
seek_table_index: u32,
}
unsafe impl Send for FrameLog {}
impl FrameLog {
#[cfg(feature = "threadpool")]
pub fn new() -> Self {
FrameLog {
entries: Vec::new(),
checksum_flag: 1,
seek_table_pos: 0,
seek_table_index: 0,
}
}
pub fn with_capacity(x: usize) -> Self {
FrameLog {
entries: Vec::with_capacity(x),
checksum_flag: 1,
seek_table_pos: 0,
seek_table_index: 0,
}
}
pub fn log_frame(&mut self, c_size: u32, d_size: u32, checksum: u32) -> Result<(), Error> {
let size = self.entries.len();
if size == SEEKABLE_MAX_FRAMES {
return Err(Error::FIndexTooLarge(size, SEEKABLE_MAX_FRAMES as usize));
}
self.entries.push(FrameLogEntry {
c_size,
d_size,
checksum,
});
Ok(())
}
#[inline(always)]
pub fn seek_table_size(&self) -> usize {
let size_p_frame = 8 + if self.checksum_flag != 0 { 4 } else { 0 };
SKIPPABLE_HEADER_SIZE + size_p_frame * self.entries.len() + SEEK_TABLE_FOOTER_SIZE
}
pub fn stwrite32(
&mut self,
output: &mut [u8],
value: u32,
offset: u32,
pos: &mut usize,
) -> usize {
if self.seek_table_pos < offset + 4 {
let len_write = std::cmp::min(
output.len() - *pos,
(offset + 4 - self.seek_table_pos) as usize,
);
let bytes = value.to_le_bytes();
output[*pos..*pos + len_write].copy_from_slice(&bytes[..len_write]);
*pos += len_write;
self.seek_table_pos += len_write as u32;
return if len_write < 4 {
self.seek_table_size() - self.seek_table_pos as usize
} else {
0
};
}
0
}
pub fn write_seek_table(&mut self, output: &mut [u8], pos: &mut usize) -> Result<usize, Error> {
let size_p_frame = 8 + if self.checksum_flag != 0 { 4 } else { 0 };
let table_len = self.seek_table_size();
let ret = self.stwrite32(output, MAGIC_SKIPPABLE_START | 0xE, 0, pos);
if ret != 0 {
return Ok(ret);
};
let ret = self.stwrite32(output, (table_len - SKIPPABLE_HEADER_SIZE) as u32, 4, pos);
if ret != 0 {
return Ok(ret);
};
let mut i = self.seek_table_index as usize;
while i < self.entries.len() {
let start = (SKIPPABLE_HEADER_SIZE + size_p_frame * i) as u32;
let ret = self.stwrite32(output, self.entries[i].c_size, start, pos);
if ret != 0 {
return Ok(ret);
};
let ret = self.stwrite32(output, self.entries[i].d_size, start + 4, pos);
if ret != 0 {
return Ok(ret);
};
if self.checksum_flag != 0 {
let ret = self.stwrite32(output, self.entries[i].checksum, start + 8, pos);
if ret != 0 {
return Ok(ret);
};
}
i += 1;
}
self.seek_table_index = i as u32;
let ret = self.stwrite32(
output,
self.entries.len() as u32,
(table_len - SEEK_TABLE_FOOTER_SIZE) as u32,
pos,
);
if ret != 0 {
return Ok(ret);
};
if output.len() - *pos < 1 {
return Ok(table_len - self.seek_table_pos as usize);
}
if (self.seek_table_pos as usize) < (table_len - 4) {
let sfd = (self.checksum_flag << 7) as u8;
output[*pos] = sfd;
*pos += 1;
self.seek_table_pos += 1;
}
let ret = self.stwrite32(output, SEEKABLE_MAGIC_NUMBER, (table_len - 4) as u32, pos);
if ret != 0 {
return Ok(ret);
};
if (self.seek_table_pos as usize) != table_len {
Err(Error::Generic)
} else {
Ok(0)
}
}
#[cfg(feature = "threadpool")]
pub fn write_all<W: std::io::Write>(&mut self, mut w: W) -> Result<(), std::io::Error> {
use crate::ZSTD_outBuffer;
use libc::c_void;
let mut output = [0; 1024];
let mut output_ = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: 1024,
pos: 0,
};
while let Ok(ret) = self.write_seek_table(&mut output, &mut output_.pos) {
if ret == 0 {
break;
}
w.write_all(&output[..output_.pos as usize])?;
output_.pos = 0;
}
w.write_all(&output[..output_.pos as usize])?;
Ok(())
}
}