1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "paranoid", forbid(unsafe_code))]
3
4extern crate alloc;
5
6use alloc::vec::Vec;
7use ozlrip_core::{FrameInfo, Limits, Result};
8
9mod execute;
10mod parse;
11mod standard;
12
13pub const DEFAULT_PLAN_CACHE_MAX_FRAME_BYTES: usize = 4096;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub struct Options {
21 pub limits: Limits,
23 pub plan_cache_max_frame_bytes: usize,
27}
28
29impl Default for Options {
30 fn default() -> Self {
31 Self {
32 limits: Limits::default(),
33 plan_cache_max_frame_bytes: DEFAULT_PLAN_CACHE_MAX_FRAME_BYTES,
34 }
35 }
36}
37
38pub struct Decoder {
40 options: Options,
41 scratch: execute::DecodeScratch,
42 plan_cache: Option<CachedFramePlan>,
43 #[cfg(feature = "zstd")]
44 zstd: zrip::DecompressContext,
45}
46
47struct CachedFramePlan {
48 frame: Vec<u8>,
49 plan: parse::FramePlan,
50 direct_append_plans: Option<execute::DirectAppendChunkPlans>,
51}
52
53impl Decoder {
54 pub fn new() -> Self {
56 Self::with_options(Options::default())
57 }
58
59 pub fn with_options(options: Options) -> Self {
61 Self {
62 options,
63 scratch: execute::DecodeScratch::new(),
64 plan_cache: None,
65 #[cfg(feature = "zstd")]
66 zstd: zrip::DecompressContext::new(),
67 }
68 }
69
70 pub fn options(&self) -> Options {
71 self.options
72 }
73
74 pub fn limits(&self) -> Limits {
75 self.options.limits
76 }
77
78 pub fn decode_into(&mut self, input: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
83 #[cfg(feature = "zstd")]
84 if let Some(frame) = parse::parse_single_zstd_frame(input, self.options.limits)? {
85 return execute::decode_single_zstd_frame_with_context(
86 input,
87 frame,
88 dst,
89 self.options.limits,
90 &mut self.zstd,
91 );
92 }
93 if let Some(cached) = self.plan_cache.as_ref()
94 && cached.frame == input
95 {
96 if let Some(direct_append_plans) = cached.direct_append_plans.as_ref() {
97 return execute::decode_plan_with_cached_direct_append_plans(
98 input,
99 &cached.plan,
100 direct_append_plans,
101 dst,
102 self.options.limits,
103 &mut self.scratch,
104 #[cfg(feature = "zstd")]
105 &mut self.zstd,
106 );
107 }
108 return execute::decode_plan_with_context(
109 input,
110 &cached.plan,
111 dst,
112 self.options.limits,
113 &mut self.scratch,
114 #[cfg(feature = "zstd")]
115 &mut self.zstd,
116 );
117 }
118
119 let plan = parse::parse_frame_plan(input, self.options.limits)?;
120 self.remember_frame_plan(input, &plan);
121 execute::decode_plan_with_context(
122 input,
123 &plan,
124 dst,
125 self.options.limits,
126 &mut self.scratch,
127 #[cfg(feature = "zstd")]
128 &mut self.zstd,
129 )
130 }
131
132 fn remember_frame_plan(&mut self, input: &[u8], plan: &parse::FramePlan) {
133 if self.options.plan_cache_max_frame_bytes == 0
134 || input.len() > self.options.plan_cache_max_frame_bytes
135 {
136 self.plan_cache = None;
137 return;
138 }
139
140 let mut frame = Vec::new();
141 if frame.try_reserve_exact(input.len()).is_err() {
142 self.plan_cache = None;
143 return;
144 }
145 frame.extend_from_slice(input);
146 let direct_append_plans = execute::prepare_direct_append_chunk_plans(plan)
147 .ok()
148 .flatten();
149 self.plan_cache = Some(CachedFramePlan {
150 frame,
151 plan: plan.clone(),
152 direct_append_plans,
153 });
154 }
155
156 pub fn decode(&mut self, input: &[u8]) -> Result<Vec<u8>> {
158 let mut output = Vec::new();
159 self.decode_into(input, &mut output)?;
160 Ok(output)
161 }
162
163 pub fn inspect(&self, input: &[u8]) -> Result<FrameInfo> {
165 parse::inspect_frame(input, self.options.limits)
166 }
167}
168
169impl Default for Decoder {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175pub fn decode(input: &[u8]) -> Result<Vec<u8>> {
177 Decoder::new().decode(input)
178}
179
180pub fn decode_with_options(input: &[u8], options: Options) -> Result<Vec<u8>> {
182 Decoder::with_options(options).decode(input)
183}
184
185pub fn decode_into(input: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
190 Decoder::new().decode_into(input, dst)
191}
192
193pub fn decode_into_with_options(
195 input: &[u8],
196 dst: &mut Vec<u8>,
197 options: Options,
198) -> Result<usize> {
199 Decoder::with_options(options).decode_into(input, dst)
200}
201
202pub fn inspect(input: &[u8]) -> Result<FrameInfo> {
204 Decoder::default().inspect(input)
205}
206
207pub fn inspect_with_options(input: &[u8], options: Options) -> Result<FrameInfo> {
209 Decoder::with_options(options).inspect(input)
210}