base64_ng/engine/
encode_in_place.rs1use crate::{
2 Alphabet, EncodeError, Engine, checked_encoded_len, encode_base64_value_runtime, wipe_bytes,
3 wipe_tail,
4};
5
6impl<A, const PAD: bool> Engine<A, PAD>
7where
8 A: Alphabet,
9{
10 pub fn encode_in_place<'a>(
33 &self,
34 buffer: &'a mut [u8],
35 input_len: usize,
36 ) -> Result<&'a mut [u8], EncodeError> {
37 if input_len > buffer.len() {
38 return Err(EncodeError::InputTooLarge {
39 input_len,
40 buffer_len: buffer.len(),
41 });
42 }
43
44 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
45 if buffer.len() < required {
46 return Err(EncodeError::OutputTooSmall {
47 required,
48 available: buffer.len(),
49 });
50 }
51
52 let mut read = input_len;
53 let mut write = required;
54
55 match input_len % 3 {
56 0 => {}
57 1 => {
58 read -= 1;
59 let b0 = buffer[read];
60 if PAD {
61 write -= 4;
62 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
63 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
64 buffer[write + 2] = b'=';
65 buffer[write + 3] = b'=';
66 } else {
67 write -= 2;
68 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
69 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
70 }
71 }
72 2 => {
73 read -= 2;
74 let b0 = buffer[read];
75 let b1 = buffer[read + 1];
76 if PAD {
77 write -= 4;
78 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
79 buffer[write + 1] =
80 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
81 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
82 buffer[write + 3] = b'=';
83 } else {
84 write -= 3;
85 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
86 buffer[write + 1] =
87 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
88 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
89 }
90 }
91 _ => unreachable!(),
92 }
93
94 while read > 0 {
95 read -= 3;
96 write -= 4;
97 let b0 = buffer[read];
98 let b1 = buffer[read + 1];
99 let b2 = buffer[read + 2];
100
101 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
102 buffer[write + 1] =
103 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
104 buffer[write + 2] =
105 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
106 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
107 }
108
109 assert_eq!(
113 write, 0,
114 "encode_in_place invariant violated: right-to-left loop did not complete"
115 );
116 Ok(&mut buffer[..required])
117 }
118
119 pub fn encode_in_place_clear_tail<'a>(
137 &self,
138 buffer: &'a mut [u8],
139 input_len: usize,
140 ) -> Result<&'a mut [u8], EncodeError> {
141 let len = match self.encode_in_place(buffer, input_len) {
142 Ok(encoded) => encoded.len(),
143 Err(err) => {
144 wipe_bytes(buffer);
145 return Err(err);
146 }
147 };
148 wipe_tail(buffer, len);
149 Ok(&mut buffer[..len])
150 }
151}