Skip to main content

base64_ng/v2/
bounded.rs

1//! Ordinary bounded stack storage.
2
3use super::{
4    ordinary::OneShotError,
5    specifications::{Base64, Codec},
6};
7
8/// Error returned when a visible prefix exceeds its backing array.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct BufferLengthError {
11    length: usize,
12    capacity: usize,
13}
14
15impl BufferLengthError {
16    pub(super) const fn new(length: usize, capacity: usize) -> Self {
17        Self { length, capacity }
18    }
19
20    /// Returns the rejected visible length.
21    #[must_use]
22    pub const fn length(self) -> usize {
23        self.length
24    }
25
26    /// Returns the backing array capacity.
27    #[must_use]
28    pub const fn capacity(self) -> usize {
29        self.capacity
30    }
31}
32
33impl core::fmt::Display for BufferLengthError {
34    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        write!(
36            formatter,
37            "visible buffer length {} exceeds capacity {}",
38            self.length, self.capacity
39        )
40    }
41}
42
43#[cfg(feature = "std")]
44impl std::error::Error for BufferLengthError {}
45
46/// Ordinary bounded bytes intended to contain encoded text.
47///
48/// This ordinary value is `Copy`, has visible formatting, and performs no
49/// drop-time cleanup. Use `secret::SecretArray` for secret-bearing storage.
50#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
51pub struct EncodedArray<const CAP: usize> {
52    bytes: [u8; CAP],
53    len: usize,
54}
55
56/// Ordinary bounded decoded bytes.
57///
58/// This ordinary value is `Copy` and performs no drop-time cleanup. Its name
59/// describes transform direction, not secrecy.
60#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
61pub struct DecodedArray<const CAP: usize> {
62    bytes: [u8; CAP],
63    len: usize,
64}
65
66macro_rules! ordinary_array {
67    ($name:ident, $description:literal) => {
68        impl<const CAP: usize> $name<CAP> {
69            #[doc = $description]
70            pub const fn from_array(
71                bytes: [u8; CAP],
72                len: usize,
73            ) -> Result<Self, BufferLengthError> {
74                if len > CAP {
75                    Err(BufferLengthError::new(len, CAP))
76                } else {
77                    Ok(Self { bytes, len })
78                }
79            }
80
81            pub(crate) const fn from_initialized(
82                bytes: [u8; CAP],
83                len: usize,
84            ) -> Result<Self, BufferLengthError> {
85                Self::from_array(bytes, len)
86            }
87
88            /// Returns the initialized visible prefix.
89            #[must_use]
90            pub fn as_bytes(&self) -> &[u8] {
91                &self.bytes[..self.len]
92            }
93
94            /// Returns the initialized prefix length.
95            #[must_use]
96            pub const fn len(&self) -> usize {
97                self.len
98            }
99
100            /// Returns whether the initialized prefix is empty.
101            #[must_use]
102            pub const fn is_empty(&self) -> bool {
103                self.len == 0
104            }
105
106            /// Returns the fixed backing capacity.
107            #[must_use]
108            pub const fn capacity(&self) -> usize {
109                CAP
110            }
111
112            /// Returns the unused backing capacity.
113            #[must_use]
114            pub const fn remaining_capacity(&self) -> usize {
115                CAP - self.len
116            }
117
118            /// Consumes the wrapper and returns its backing array and length.
119            #[must_use]
120            pub const fn into_parts(self) -> ([u8; CAP], usize) {
121                (self.bytes, self.len)
122            }
123        }
124    };
125}
126
127ordinary_array!(
128    EncodedArray,
129    "Constructs ordinary encoded storage with a checked visible prefix."
130);
131ordinary_array!(
132    DecodedArray,
133    "Constructs ordinary decoded storage with a checked visible prefix."
134);
135
136impl<S: Codec> Base64<S> {
137    /// Encodes into a bounded ordinary stack array.
138    pub fn encode_bounded<const CAP: usize>(
139        &self,
140        input: &[u8],
141    ) -> Result<EncodedArray<CAP>, OneShotError> {
142        let mut bytes = [0u8; CAP];
143        let len = self.encode_into(input, &mut bytes)?;
144        EncodedArray::from_initialized(bytes, len)
145            .map_err(|_| OneShotError::Backend(super::contracts::BackendFault::ImpossibleState))
146    }
147
148    /// Decodes into a bounded ordinary stack array transactionally.
149    pub fn decode_bounded<const CAP: usize>(
150        &self,
151        input: &[u8],
152    ) -> Result<DecodedArray<CAP>, OneShotError> {
153        let mut bytes = [0u8; CAP];
154        let len = self.decode_into(input, &mut bytes)?;
155        DecodedArray::from_initialized(bytes, len)
156            .map_err(|_| OneShotError::Backend(super::contracts::BackendFault::ImpossibleState))
157    }
158}