use super::{Arg, Splitter};
use crate::string::Bytes;
use base64::DecodeError;
#[derive(Clone)]
pub struct ChunkEncoder {
splitter: Result<Splitter<Arg<'static>>, bool>,
max: usize,
}
impl ChunkEncoder {
pub fn new<B: AsRef<[u8]>>(bytes: B, max: usize, secret: bool) -> Self {
use base64::engine::{general_purpose::STANDARD as ENGINE, Engine};
if max == 0 {
return ChunkEncoder::empty();
}
let encoded: Bytes<'static> = ENGINE.encode(bytes).into();
let splitter = if !encoded.is_empty() {
let mut encoded = unsafe { Arg::from_unchecked(encoded) };
if secret {
encoded = encoded.secret();
}
Ok(Splitter::new(encoded))
} else {
Err(secret)
};
ChunkEncoder { splitter, max }
}
pub const fn empty() -> Self {
ChunkEncoder { splitter: Err(false), max: 0 }
}
pub fn is_empty(&self) -> bool {
self.max == 0
}
}
impl Default for ChunkEncoder {
fn default() -> Self {
ChunkEncoder::empty()
}
}
impl Iterator for ChunkEncoder {
type Item = Arg<'static>;
fn next(&mut self) -> Option<Self::Item> {
if self.max == 0 {
return None;
}
match &mut self.splitter {
Ok(splitter) => {
let chunk = splitter.save_end().until_count(self.max).rest::<Arg>().unwrap();
if chunk.len() < self.max {
*self = Self::empty();
} else if splitter.is_empty() {
self.splitter = Err(splitter.is_secret());
}
Some(chunk)
}
Err(secret) => {
let retval = if *secret { crate::names::PLUS.secret() } else { crate::names::PLUS };
*self = Self::empty();
Some(retval)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let chunks = std::iter::ExactSizeIterator::len(self);
(chunks, Some(chunks))
}
}
impl std::iter::FusedIterator for ChunkEncoder {}
impl std::iter::ExactSizeIterator for ChunkEncoder {
fn len(&self) -> usize {
if self.max == 0 {
0
} else if let Ok(splitter) = &self.splitter {
splitter.len() / self.max + 1
} else {
1
}
}
}
#[derive(Clone)]
pub struct ChunkDecoder(Vec<u8>, usize);
impl ChunkDecoder {
pub const fn new(chunk_len: usize) -> Self {
Self(Vec::new(), chunk_len)
}
pub fn add<B: AsRef<[u8]>>(&mut self, chunk: B) -> Option<Result<Bytes<'static>, DecodeError>> {
let chunk = chunk.as_ref();
if chunk.len() < self.1 {
if chunk != b"+" {
self.0.extend_from_slice(chunk);
}
Some(self.decode())
} else {
self.0.extend_from_slice(chunk);
None
}
}
pub fn decode(&mut self) -> Result<Bytes<'static>, DecodeError> {
use base64::engine::{general_purpose::STANDARD as ENGINE, Engine};
ENGINE.decode(std::mem::take(&mut self.0)).map(Bytes::from)
}
}