use crate::{DecodeError, STANDARD, constant_time_eq_public_len, wipe_bytes, wipe_tail};
pub struct DecodedBuffer<const CAP: usize> {
bytes: [u8; CAP],
len: usize,
}
pub struct ExposedDecodedArray<const CAP: usize> {
bytes: [u8; CAP],
len: usize,
}
impl<const CAP: usize> ExposedDecodedArray<CAP> {
#[must_use]
pub const fn from_array(bytes: [u8; CAP], len: usize) -> Self {
assert!(len <= CAP, "visible length exceeds array capacity");
Self { bytes, len }
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn capacity(&self) -> usize {
CAP
}
#[must_use = "caller must zeroize the returned array"]
pub fn into_exposed_unprotected_array_caller_must_zeroize(mut self) -> ([u8; CAP], usize) {
let len = self.len;
self.len = 0;
(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
}
}
impl<const CAP: usize> Drop for ExposedDecodedArray<CAP> {
fn drop(&mut self) {
wipe_bytes(&mut self.bytes);
self.len = 0;
}
}
impl<const CAP: usize> core::fmt::Debug for ExposedDecodedArray<CAP> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("ExposedDecodedArray")
.field("bytes", &"<redacted>")
.field("len", &self.len)
.field("capacity", &CAP)
.finish()
}
}
impl<const CAP: usize> DecodedBuffer<CAP> {
#[must_use]
pub const fn new() -> Self {
Self {
bytes: [0u8; CAP],
len: 0,
}
}
pub(crate) fn as_mut_capacity(&mut self) -> &mut [u8] {
&mut self.bytes
}
pub(crate) fn set_filled(&mut self, written: usize) -> Result<(), DecodeError> {
debug_assert!(
written <= CAP,
"decoder wrote past stack-backed buffer capacity"
);
if written > CAP {
self.clear();
return Err(DecodeError::OutputTooSmall {
required: written,
available: CAP,
});
}
self.len = written;
Ok(())
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn is_full(&self) -> bool {
self.len == CAP
}
#[must_use]
pub const fn capacity(&self) -> usize {
CAP
}
#[must_use]
pub const fn remaining_capacity(&self) -> usize {
CAP - self.len
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(self.as_bytes())
}
#[doc(alias = "constant_time_eq")]
#[must_use]
pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
constant_time_eq_public_len(self.as_bytes(), other)
}
#[must_use]
pub fn into_exposed_array(mut self) -> ExposedDecodedArray<CAP> {
let len = self.len;
self.len = 0;
ExposedDecodedArray::from_array(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
}
pub fn clear(&mut self) {
wipe_bytes(&mut self.bytes);
self.len = 0;
}
pub fn clear_tail(&mut self) {
wipe_tail(&mut self.bytes, self.len);
}
}
impl<const CAP: usize> AsRef<[u8]> for DecodedBuffer<CAP> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl<const CAP: usize> Clone for DecodedBuffer<CAP> {
fn clone(&self) -> Self {
let mut output = Self::new();
output.bytes[..self.len].copy_from_slice(self.as_bytes());
output.len = self.len;
output
}
}
impl<const CAP: usize> core::fmt::Debug for DecodedBuffer<CAP> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("DecodedBuffer")
.field("bytes", &"<redacted>")
.field("len", &self.len)
.field("capacity", &CAP)
.finish()
}
}
impl<const CAP: usize> Default for DecodedBuffer<CAP> {
fn default() -> Self {
Self::new()
}
}
impl<const CAP: usize> Drop for DecodedBuffer<CAP> {
fn drop(&mut self) {
self.clear();
}
}
impl<const CAP: usize> TryFrom<&[u8]> for DecodedBuffer<CAP> {
type Error = DecodeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
STANDARD.decode_buffer(input)
}
}
impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for DecodedBuffer<CAP> {
type Error = DecodeError;
fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
Self::try_from(&input[..])
}
}
impl<const CAP: usize> TryFrom<&str> for DecodedBuffer<CAP> {
type Error = DecodeError;
fn try_from(input: &str) -> Result<Self, Self::Error> {
Self::try_from(input.as_bytes())
}
}
impl<const CAP: usize> core::str::FromStr for DecodedBuffer<CAP> {
type Err = DecodeError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::try_from(input)
}
}