Skip to main content

base64_ng/v2/web/
one_shot.rs

1//! Transactional one-shot WHATWG forgiving decode operations.
2
3use super::{ForgivingBase64, ForgivingError};
4use crate::v2::{BackendFault, Progress, Status};
5
6impl ForgivingBase64 {
7    /// Validates one web string without returning decoded bytes.
8    pub fn validate(self, input: &str) -> Result<(), ForgivingError> {
9        self.decoded_len(input).map(|_| ())
10    }
11
12    /// Validates a web string and returns its exact decoded byte length.
13    pub fn decoded_len(self, input: &str) -> Result<usize, ForgivingError> {
14        measure(input)
15    }
16
17    /// Decodes a web string into a caller-owned destination transactionally.
18    ///
19    /// Full WHATWG validation and sizing happen before the first destination
20    /// write. Every returned error leaves the complete destination unchanged.
21    pub fn decode_into(self, input: &str, output: &mut [u8]) -> Result<usize, ForgivingError> {
22        let required = self.decoded_len(input)?;
23        if output.len() < required {
24            return Err(ForgivingError::OutputTooSmall {
25                required,
26                available: output.len(),
27            });
28        }
29        decode_validated(input, &mut output[..required])?;
30        Ok(required)
31    }
32}
33
34fn measure(input: &str) -> Result<usize, ForgivingError> {
35    let mut decoder = ForgivingBase64.decoder();
36    let mut offset = 0;
37    let mut total = 0usize;
38    let mut scratch = [0u8; 3];
39    while offset < input.len() {
40        let step = decoder.update(&input[offset..], &mut scratch)?;
41        let progress = step.progress();
42        require_progress(progress)?;
43        offset += progress.input_consumed();
44        total = total
45            .checked_add(progress.output_produced())
46            .ok_or(ForgivingError::PositionOverflow)?;
47    }
48    loop {
49        let step = decoder.finish(&mut scratch)?;
50        total = total
51            .checked_add(step.progress().output_produced())
52            .ok_or(ForgivingError::PositionOverflow)?;
53        match step.status() {
54            Status::Complete => return Ok(total),
55            Status::OutputFull(_) => require_output_progress(step.progress())?,
56            Status::NeedInput => return Err(impossible_state()),
57        }
58    }
59}
60
61fn decode_validated(input: &str, output: &mut [u8]) -> Result<(), ForgivingError> {
62    let mut decoder = ForgivingBase64.decoder();
63    let mut input_offset = 0;
64    let mut output_offset = 0;
65    while input_offset < input.len() {
66        let step = decoder.update(&input[input_offset..], &mut output[output_offset..])?;
67        let progress = step.progress();
68        require_progress(progress)?;
69        input_offset += progress.input_consumed();
70        output_offset += progress.output_produced();
71    }
72    loop {
73        let step = decoder.finish(&mut output[output_offset..])?;
74        output_offset += step.progress().output_produced();
75        match step.status() {
76            Status::Complete => return Ok(()),
77            Status::OutputFull(_) => require_output_progress(step.progress())?,
78            Status::NeedInput => return Err(impossible_state()),
79        }
80    }
81}
82
83fn require_progress(progress: Progress) -> Result<(), ForgivingError> {
84    if progress.input_consumed() == 0 && progress.output_produced() == 0 {
85        Err(impossible_state())
86    } else {
87        Ok(())
88    }
89}
90
91fn require_output_progress(progress: Progress) -> Result<(), ForgivingError> {
92    if progress.output_produced() == 0 {
93        Err(impossible_state())
94    } else {
95        Ok(())
96    }
97}
98
99const fn impossible_state() -> ForgivingError {
100    ForgivingError::Backend(BackendFault::ImpossibleState)
101}