#![deny(missing_docs)]
#[doc(inline)]
pub use crate::configs::CustomConfig;
pub use crate::decode::DecodeError;
pub use crate::display::Display;
use crate::configs::{Crypt, Fast, Std, StdNoPad, UrlSafe, UrlSafeNoPad};
pub const STD: Std = Std;
pub const STD_NO_PAD: StdNoPad = StdNoPad;
pub const URL_SAFE: UrlSafe = UrlSafe;
pub const URL_SAFE_NO_PAD: UrlSafeNoPad = UrlSafeNoPad;
pub const CRYPT: Crypt = Crypt;
pub const FAST: Fast = Fast;
mod private {
use crate::decode::block::IntoBlockDecoder;
use crate::encode::block::IntoBlockEncoder;
use crate::u6::U6;
pub trait SealedConfig: IntoBlockEncoder + IntoBlockDecoder {
fn encode_u6(self, input: U6) -> u8;
fn decode_u8(self, input: u8) -> u8;
fn padding_byte(self) -> Option<u8>;
}
}
pub trait Config: Copy + private::SealedConfig {
#[inline]
fn encode<I>(self, input: &I) -> String
where
I: AsRef<[u8]> + ?Sized,
{
let input = input.as_ref();
let mut output = vec![0; input.len() * 4 / 3 + 3];
let bytes_written = crate::encode::encode_slice(self, input, output.as_mut_slice());
output.truncate(bytes_written);
debug_assert!(output.iter().all(u8::is_ascii));
unsafe { String::from_utf8_unchecked(output) }
}
#[inline]
fn encode_with_buffer<'i, 'b, I>(self, input: &'i I, buffer: &'b mut Vec<u8>) -> &'b str
where
I: AsRef<[u8]> + ?Sized,
{
let input = input.as_ref();
let output_size = input.len() * 4 / 3 + 3;
if output_size > buffer.len() {
buffer.resize(output_size, 0);
}
let num_encoded_bytes = crate::encode::encode_slice(self, input, buffer.as_mut_slice());
let encoded = &buffer[..num_encoded_bytes];
debug_assert!(encoded.iter().all(u8::is_ascii));
unsafe { std::str::from_utf8_unchecked(encoded) }
}
#[inline]
fn encode_slice<I>(self, input: &I, output: &mut [u8]) -> usize
where
I: AsRef<[u8]> + ?Sized,
{
crate::encode::encode_slice(self, input.as_ref(), output)
}
#[inline]
fn decode<I>(self, input: &I) -> Result<Vec<u8>, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
let mut output = Vec::new();
let decoded_len = self.decode_with_buffer(input, &mut output)?.len();
output.truncate(decoded_len);
Ok(output)
}
#[inline]
fn decode_with_buffer<'i, 'b, I>(
self,
input: &'i I,
buffer: &'b mut Vec<u8>,
) -> Result<&'b [u8], DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
let input = input.as_ref();
let output_size = input.len() * 3 / 4 + 1;
if output_size > buffer.len() {
buffer.resize(output_size, 0);
}
let num_decoded_bytes = crate::decode::decode_slice(self, input, buffer.as_mut_slice())?;
Ok(&buffer[..num_decoded_bytes])
}
#[inline]
fn decode_slice<I>(self, input: &I, output: &mut [u8]) -> Result<usize, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
crate::decode::decode_slice(self, input.as_ref(), output)
}
}
macro_rules! define_block_iter {
(
name = $name:ident,
input_chunk_size = $input_chunk_size:expr,
input_stride = $input_stride:expr,
output_chunk_size = $output_chunk_size:expr,
output_stride = $output_stride:expr
) => {
struct $name<'a, 'b> {
input: &'a [u8],
output: &'b mut [u8],
input_index: usize,
output_index: usize,
}
impl<'a, 'b> $name<'a, 'b> {
#[inline]
fn new(input: &'a [u8], output: &'b mut [u8]) -> Self {
$name {
input,
output,
input_index: 0,
output_index: 0,
}
}
#[inline]
fn next_chunk(
&mut self,
) -> Option<(&[u8; $input_chunk_size], &mut [u8; $output_chunk_size])> {
if self.input_index + $input_chunk_size <= self.input.len()
&& self.output_index + $output_chunk_size <= self.output.len()
{
use arrayref::{array_mut_ref, array_ref};
let input = array_ref!(self.input, self.input_index, $input_chunk_size);
let output = array_mut_ref!(self.output, self.output_index, $output_chunk_size);
self.input_index += $input_stride;
self.output_index += $output_stride;
Some((input, output))
} else {
None
}
}
#[allow(dead_code)]
fn step_back(&mut self) {
if self.input_index > 0 {
self.input_index -= $input_stride;
self.output_index -= $output_stride;
}
}
#[inline]
fn remaining(self) -> (usize, usize) {
(self.input_index, self.output_index)
}
}
};
}
pub mod configs;
pub(crate) mod decode;
pub(crate) mod display;
pub(crate) mod encode;
pub mod io;
pub(crate) mod tables;
pub(crate) mod u6;
use std::ops::Bound;
use std::ops::RangeBounds;
pub(crate) fn copy_in_place<T: Copy, R: RangeBounds<usize>>(slice: &mut [T], src: R, dest: usize) {
let src_start = match src.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("range bound overflows usize"),
Bound::Unbounded => 0,
};
let src_end = match src.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("range bound overflows usize"),
Bound::Excluded(&n) => n,
Bound::Unbounded => slice.len(),
};
assert!(src_start <= src_end, "src end is before src start");
assert!(src_end <= slice.len(), "src is out of bounds");
let count = src_end - src_start;
assert!(dest <= slice.len() - count, "dest is out of bounds");
unsafe {
core::ptr::copy(
slice.get_unchecked(src_start),
slice.get_unchecked_mut(dest),
count,
);
}
}