Skip to main content

base64_ng/v2/
ordinary_alloc.rs

1//! Fallible allocating ordinary one-shot operations.
2
3use alloc::{string::String, vec::Vec};
4
5use super::{
6    ordinary::OneShotError,
7    specifications::{Base64, Codec},
8};
9
10impl<S: Codec> Base64<S> {
11    /// Encodes into a newly allocated string.
12    ///
13    /// Allocation uses `try_reserve_exact`; allocation failure is returned as
14    /// [`OneShotError::AllocationFailed`]. Process-aborting allocators remain
15    /// outside Rust's returned-error contract.
16    pub fn encode_to_string(&self, input: &[u8]) -> Result<String, OneShotError> {
17        self.encode_to_string_with_limit(input, usize::MAX)
18    }
19
20    /// Encodes into a string subject to an exact output-byte limit.
21    pub fn encode_to_string_with_limit(
22        &self,
23        input: &[u8],
24        max_output_len: usize,
25    ) -> Result<String, OneShotError> {
26        self.encode_to_string_with_reserver(input, max_output_len, |output, required| {
27            output
28                .try_reserve_exact(required)
29                .map_err(|_| OneShotError::AllocationFailed {
30                    requested: required,
31                })
32        })
33    }
34
35    fn encode_to_string_with_reserver<F>(
36        &self,
37        input: &[u8],
38        max_output_len: usize,
39        reserve: F,
40    ) -> Result<String, OneShotError>
41    where
42        F: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
43    {
44        let required = self.encoded_len(input.len())?;
45        require_allocation_limit(required, max_output_len)?;
46        let mut output = Vec::new();
47        reserve(&mut output, required)?;
48        output.resize(required, 0);
49        self.encode_into(input, &mut output)?;
50        String::from_utf8(output)
51            .map_err(|_| OneShotError::Backend(super::contracts::BackendFault::ImpossibleState))
52    }
53
54    /// Decodes into a newly allocated byte vector.
55    ///
56    /// Complete validation and exact sizing happen before allocation. No
57    /// plaintext is materialized before the full allocation is reserved.
58    pub fn decode_to_vec(&self, input: &[u8]) -> Result<Vec<u8>, OneShotError> {
59        self.decode_to_vec_with_limit(input, usize::MAX)
60    }
61
62    /// Decodes into a byte vector subject to an exact output-byte limit.
63    pub fn decode_to_vec_with_limit(
64        &self,
65        input: &[u8],
66        max_output_len: usize,
67    ) -> Result<Vec<u8>, OneShotError> {
68        self.decode_to_vec_with_reserver(input, max_output_len, |output, required| {
69            output
70                .try_reserve_exact(required)
71                .map_err(|_| OneShotError::AllocationFailed {
72                    requested: required,
73                })
74        })
75    }
76
77    fn decode_to_vec_with_reserver<F>(
78        &self,
79        input: &[u8],
80        max_output_len: usize,
81        reserve: F,
82    ) -> Result<Vec<u8>, OneShotError>
83    where
84        F: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
85    {
86        let required = self.decoded_len(input)?;
87        require_allocation_limit(required, max_output_len)?;
88        let mut output = Vec::new();
89        reserve(&mut output, required)?;
90        output.resize(required, 0);
91        self.decode_into(input, &mut output)?;
92        Ok(output)
93    }
94
95    #[cfg(test)]
96    pub(super) fn decode_to_vec_with_injected_reserver<F>(
97        &self,
98        input: &[u8],
99        max_output_len: usize,
100        reserve: F,
101    ) -> Result<Vec<u8>, OneShotError>
102    where
103        F: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
104    {
105        self.decode_to_vec_with_reserver(input, max_output_len, reserve)
106    }
107
108    #[cfg(test)]
109    pub(super) fn encode_to_string_with_injected_reserver<F>(
110        &self,
111        input: &[u8],
112        max_output_len: usize,
113        reserve: F,
114    ) -> Result<String, OneShotError>
115    where
116        F: FnOnce(&mut Vec<u8>, usize) -> Result<(), OneShotError>,
117    {
118        self.encode_to_string_with_reserver(input, max_output_len, reserve)
119    }
120}
121
122fn require_allocation_limit(required: usize, limit: usize) -> Result<(), OneShotError> {
123    if required > limit {
124        Err(OneShotError::AllocationLimitExceeded { required, limit })
125    } else {
126        Ok(())
127    }
128}