#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "paranoid", forbid(unsafe_code))]
extern crate alloc;
use alloc::vec::Vec;
use ozlrip_core::{FrameInfo, Limits, Result};
mod execute;
mod parse;
mod standard;
pub const DEFAULT_PLAN_CACHE_MAX_FRAME_BYTES: usize = 4096;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Options {
pub limits: Limits,
pub plan_cache_max_frame_bytes: usize,
}
impl Default for Options {
fn default() -> Self {
Self {
limits: Limits::default(),
plan_cache_max_frame_bytes: DEFAULT_PLAN_CACHE_MAX_FRAME_BYTES,
}
}
}
pub struct Decoder {
options: Options,
scratch: execute::DecodeScratch,
plan_cache: Option<CachedFramePlan>,
#[cfg(feature = "zstd")]
zstd: zrip::DecompressContext,
}
struct CachedFramePlan {
frame: Vec<u8>,
plan: parse::FramePlan,
direct_append_plans: Option<execute::DirectAppendChunkPlans>,
}
impl Decoder {
pub fn new() -> Self {
Self::with_options(Options::default())
}
pub fn with_options(options: Options) -> Self {
Self {
options,
scratch: execute::DecodeScratch::new(),
plan_cache: None,
#[cfg(feature = "zstd")]
zstd: zrip::DecompressContext::new(),
}
}
pub fn options(&self) -> Options {
self.options
}
pub fn limits(&self) -> Limits {
self.options.limits
}
pub fn decode_into(&mut self, input: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
#[cfg(feature = "zstd")]
if let Some(frame) = parse::parse_single_zstd_frame(input, self.options.limits)? {
return execute::decode_single_zstd_frame_with_context(
input,
frame,
dst,
self.options.limits,
&mut self.zstd,
);
}
if let Some(cached) = self.plan_cache.as_ref()
&& cached.frame == input
{
if let Some(direct_append_plans) = cached.direct_append_plans.as_ref() {
return execute::decode_plan_with_cached_direct_append_plans(
input,
&cached.plan,
direct_append_plans,
dst,
self.options.limits,
&mut self.scratch,
#[cfg(feature = "zstd")]
&mut self.zstd,
);
}
return execute::decode_plan_with_context(
input,
&cached.plan,
dst,
self.options.limits,
&mut self.scratch,
#[cfg(feature = "zstd")]
&mut self.zstd,
);
}
let plan = parse::parse_frame_plan(input, self.options.limits)?;
self.remember_frame_plan(input, &plan);
execute::decode_plan_with_context(
input,
&plan,
dst,
self.options.limits,
&mut self.scratch,
#[cfg(feature = "zstd")]
&mut self.zstd,
)
}
fn remember_frame_plan(&mut self, input: &[u8], plan: &parse::FramePlan) {
if self.options.plan_cache_max_frame_bytes == 0
|| input.len() > self.options.plan_cache_max_frame_bytes
{
self.plan_cache = None;
return;
}
let mut frame = Vec::new();
if frame.try_reserve_exact(input.len()).is_err() {
self.plan_cache = None;
return;
}
frame.extend_from_slice(input);
let direct_append_plans = execute::prepare_direct_append_chunk_plans(plan)
.ok()
.flatten();
self.plan_cache = Some(CachedFramePlan {
frame,
plan: plan.clone(),
direct_append_plans,
});
}
pub fn decode(&mut self, input: &[u8]) -> Result<Vec<u8>> {
let mut output = Vec::new();
self.decode_into(input, &mut output)?;
Ok(output)
}
pub fn inspect(&self, input: &[u8]) -> Result<FrameInfo> {
parse::inspect_frame(input, self.options.limits)
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
pub fn decode(input: &[u8]) -> Result<Vec<u8>> {
Decoder::new().decode(input)
}
pub fn decode_with_options(input: &[u8], options: Options) -> Result<Vec<u8>> {
Decoder::with_options(options).decode(input)
}
pub fn decode_into(input: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
Decoder::new().decode_into(input, dst)
}
pub fn decode_into_with_options(
input: &[u8],
dst: &mut Vec<u8>,
options: Options,
) -> Result<usize> {
Decoder::with_options(options).decode_into(input, dst)
}
pub fn inspect(input: &[u8]) -> Result<FrameInfo> {
Decoder::default().inspect(input)
}
pub fn inspect_with_options(input: &[u8], options: Options) -> Result<FrameInfo> {
Decoder::with_options(options).inspect(input)
}