#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
mod cv_stack;
mod decoder;
mod lzma;
pub mod reed_solomon;
mod encoder;
mod circular_buffer;
mod header;
mod metadata;
#[cfg(not(feature = "std"))]
mod no_std;
mod trailer;
#[cfg(feature = "std")]
mod work_queue;
#[cfg(feature = "std")]
pub(crate) use std::io::Error;
#[cfg(feature = "std")]
pub(crate) use std::io::Read;
#[cfg(feature = "std")]
pub(crate) use std::io::Write;
pub use cv_stack::CVStack;
#[cfg(feature = "std")]
pub use decoder::TOAFileDecoder;
pub use decoder::{ECCDecoder, TOAStreamingDecoder};
#[cfg(feature = "std")]
pub use encoder::TOAFileEncoder;
pub use encoder::{ECCEncoder, TOABlockWriter, TOAOptions, TOAStreamingEncoder};
pub use header::{TOABlockHeader, TOAHeader};
pub use lzma::filter;
pub use metadata::TOAMetadata;
#[cfg(not(feature = "std"))]
pub use no_std::Error;
#[cfg(not(feature = "std"))]
pub use no_std::Read;
#[cfg(not(feature = "std"))]
pub use no_std::Write;
pub use trailer::TOAFileTrailer;
#[cfg(feature = "std")]
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(not(feature = "std"))]
pub type Result<T> = core::result::Result<T, Error>;
const TOA_MAGIC: [u8; 4] = [0xFE, 0xDC, 0xBA, 0x98];
const TOA_VERSION: u8 = 0x01;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCorrection {
None,
Standard,
Paranoid,
Extreme,
}
impl ErrorCorrection {
pub(crate) fn capability_bits(self) -> u8 {
match self {
ErrorCorrection::None => 0b00,
ErrorCorrection::Standard => 0b01,
ErrorCorrection::Paranoid => 0b10,
ErrorCorrection::Extreme => 0b11,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Prefilter {
None,
BcjX86,
BcjArm,
BcjArmThumb,
BcjArm64,
BcjSparc,
BcjPowerPc,
BcjIa64,
BcjRiscV,
}
impl From<Prefilter> for u8 {
fn from(value: Prefilter) -> Self {
match value {
Prefilter::None => 0x00,
Prefilter::BcjX86 => 0x01,
Prefilter::BcjArm => 0x02,
Prefilter::BcjArmThumb => 0x03,
Prefilter::BcjArm64 => 0x04,
Prefilter::BcjSparc => 0x05,
Prefilter::BcjPowerPc => 0x06,
Prefilter::BcjIa64 => 0x07,
Prefilter::BcjRiscV => 0x08,
}
}
}
impl TryFrom<u8> for Prefilter {
type Error = ();
fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
match value {
0x00 => Ok(Prefilter::None),
0x01 => Ok(Prefilter::BcjX86),
0x02 => Ok(Prefilter::BcjArm),
0x03 => Ok(Prefilter::BcjArmThumb),
0x04 => Ok(Prefilter::BcjArm64),
0x05 => Ok(Prefilter::BcjSparc),
0x06 => Ok(Prefilter::BcjPowerPc),
0x07 => Ok(Prefilter::BcjIa64),
0x08 => Ok(Prefilter::BcjRiscV),
_ => Err(()),
}
}
}
#[cfg(feature = "std")]
#[inline(always)]
fn error_other(msg: &'static str) -> Error {
Error::other(msg)
}
#[cfg(feature = "std")]
#[inline(always)]
fn error_invalid_input(msg: &'static str) -> Error {
Error::new(std::io::ErrorKind::InvalidInput, msg)
}
#[cfg(feature = "std")]
#[inline(always)]
fn error_invalid_data(msg: &'static str) -> Error {
Error::new(std::io::ErrorKind::InvalidData, msg)
}
#[cfg(feature = "std")]
#[inline(always)]
fn error_unsupported(msg: &'static str) -> Error {
Error::new(std::io::ErrorKind::Unsupported, msg)
}
#[cfg(feature = "std")]
#[inline(always)]
fn copy_error(error: &Error) -> Error {
Error::new(error.kind(), error.to_string())
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_eof() -> Error {
Error::EOF
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_other(msg: &'static str) -> Error {
Error::Other(msg)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_invalid_input(msg: &'static str) -> Error {
Error::InvalidInput(msg)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_invalid_data(msg: &'static str) -> Error {
Error::InvalidData(msg)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_out_of_memory(msg: &'static str) -> Error {
Error::OutOfMemory(msg)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn error_unsupported(msg: &'static str) -> Error {
Error::Unsupported(msg)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn copy_error(error: &Error) -> Error {
*error
}
#[cfg(feature = "std")]
pub fn copy_wide<R: Read, W: Write>(reader: &mut R, writer: &mut W) -> Result<u64> {
const BUFFER_SIZE: usize = 64 * 1024; let mut buf = [0u8; BUFFER_SIZE];
let mut written = 0u64;
loop {
match reader.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
writer.write_all(&buf[..n])?;
written += n as u64;
}
Err(e) => return Err(e),
}
}
Ok(written)
}
#[cfg(feature = "std")]
pub(crate) struct LimitedReader<R> {
inner: std::io::BufReader<R>,
remaining: u64,
}
#[cfg(feature = "std")]
impl<R: Read> LimitedReader<R> {
pub(crate) fn new(reader: R, limit: u64) -> Self {
Self {
inner: std::io::BufReader::with_capacity(64 << 10, reader),
remaining: limit,
}
}
}
#[cfg(feature = "std")]
impl<R: Read> Read for LimitedReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if self.remaining == 0 {
return Ok(0);
}
let max_read = (buf.len() as u64).min(self.remaining) as usize;
let bytes_read = self.inner.read(&mut buf[..max_read])?;
self.remaining -= bytes_read as u64;
Ok(bytes_read)
}
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn transpose_for_simd<const BATCH: usize, const LEN: usize>(
batched_data: &[[u8; LEN]; BATCH],
) -> [[u8; BATCH]; LEN] {
let mut transposed = [[0u8; BATCH]; LEN];
for (codeword_idx, codeword) in batched_data.iter().enumerate() {
for (byte_idx, &byte) in codeword.iter().enumerate() {
transposed[byte_idx][codeword_idx] = byte;
}
}
transposed
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn transpose_from_simd<const BATCH: usize, const DATA_LEN: usize, const PARITY_LEN: usize>(
transposed_data: &[[u8; BATCH]; DATA_LEN],
transposed_parity: &[[u8; BATCH]; PARITY_LEN],
) -> ([[u8; DATA_LEN]; BATCH], [[u8; PARITY_LEN]; BATCH]) {
let mut data_codewords = [[0u8; DATA_LEN]; BATCH];
let mut parity_codewords = [[0u8; PARITY_LEN]; BATCH];
for (byte_idx, byte_batch) in transposed_data.iter().enumerate() {
for (codeword_idx, &byte) in byte_batch.iter().enumerate() {
data_codewords[codeword_idx][byte_idx] = byte;
}
}
for (byte_idx, byte_batch) in transposed_parity.iter().enumerate() {
for (codeword_idx, &byte) in byte_batch.iter().enumerate() {
parity_codewords[codeword_idx][byte_idx] = byte;
}
}
(data_codewords, parity_codewords)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SimdOverride {
Auto,
ForceScalar,
#[cfg(target_arch = "x86_64")]
ForceSse2Gfni,
#[cfg(target_arch = "x86_64")]
ForceSsse3,
#[cfg(target_arch = "x86_64")]
ForceAvx2,
#[cfg(target_arch = "x86_64")]
ForceAvx2Gfni,
#[cfg(target_arch = "aarch64")]
ForceNeon,
}
#[cfg(test)]
mod tests {
pub(crate) struct Lcg(u64);
impl Lcg {
pub(crate) fn new(seed: u64) -> Self {
Lcg(seed)
}
pub(crate) fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_mul(0xDA942042E4DD58B5);
self.0.wrapping_shr(64)
}
pub(crate) fn next_u8(&mut self) -> u8 {
let next = self.next_u64();
(next >> 16) as u8
}
pub(crate) fn next_usize(&mut self, max: usize) -> usize {
let next = self.next_u64();
next as usize % max
}
pub(crate) fn fill_buffer(&mut self, buf: &mut [u8]) {
for chunk in buf.chunks_mut(8) {
let next = self.next_u64();
let bytes = next.to_le_bytes();
chunk.copy_from_slice(&bytes[..chunk.len()]);
}
}
}
}