1 use crate::{ 2 alphabet::Alphabet, 3 engine::{ 4 general_purpose::{self, decode_table, encode_table}, 5 Config, DecodeEstimate, DecodePaddingMode, Engine, 6 }, 7 DecodeError, PAD_BYTE, 8 }; 9 use alloc::ops::BitOr; 10 use std::ops::{BitAnd, Shl, Shr}; 11 12 /// Comparatively simple implementation that can be used as something to compare against in tests 13 pub struct Naive { 14 encode_table: [u8; 64], 15 decode_table: [u8; 256], 16 config: NaiveConfig, 17 } 18 19 impl Naive { 20 const ENCODE_INPUT_CHUNK_SIZE: usize = 3; 21 const DECODE_INPUT_CHUNK_SIZE: usize = 4; 22 new(alphabet: &Alphabet, config: NaiveConfig) -> Self23 pub const fn new(alphabet: &Alphabet, config: NaiveConfig) -> Self { 24 Self { 25 encode_table: encode_table(alphabet), 26 decode_table: decode_table(alphabet), 27 config, 28 } 29 } 30 decode_byte_into_u32(&self, offset: usize, byte: u8) -> Result<u32, DecodeError>31 fn decode_byte_into_u32(&self, offset: usize, byte: u8) -> Result<u32, DecodeError> { 32 let decoded = self.decode_table[byte as usize]; 33 34 if decoded == general_purpose::INVALID_VALUE { 35 return Err(DecodeError::InvalidByte(offset, byte)); 36 } 37 38 Ok(decoded as u32) 39 } 40 } 41 42 impl Engine for Naive { 43 type Config = NaiveConfig; 44 type DecodeEstimate = NaiveEstimate; 45 internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize46 fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize { 47 // complete chunks first 48 49 const LOW_SIX_BITS: u32 = 0x3F; 50 51 let rem = input.len() % Self::ENCODE_INPUT_CHUNK_SIZE; 52 // will never underflow 53 let complete_chunk_len = input.len() - rem; 54 55 let mut input_index = 0_usize; 56 let mut output_index = 0_usize; 57 if let Some(last_complete_chunk_index) = 58 complete_chunk_len.checked_sub(Self::ENCODE_INPUT_CHUNK_SIZE) 59 { 60 while input_index <= last_complete_chunk_index { 61 let chunk = &input[input_index..input_index + Self::ENCODE_INPUT_CHUNK_SIZE]; 62 63 // populate low 24 bits from 3 bytes 64 let chunk_int: u32 = 65 (chunk[0] as u32).shl(16) | (chunk[1] as u32).shl(8) | (chunk[2] as u32); 66 // encode 4x 6-bit output bytes 67 output[output_index] = self.encode_table[chunk_int.shr(18) as usize]; 68 output[output_index + 1] = 69 self.encode_table[chunk_int.shr(12_u8).bitand(LOW_SIX_BITS) as usize]; 70 output[output_index + 2] = 71 self.encode_table[chunk_int.shr(6_u8).bitand(LOW_SIX_BITS) as usize]; 72 output[output_index + 3] = 73 self.encode_table[chunk_int.bitand(LOW_SIX_BITS) as usize]; 74 75 input_index += Self::ENCODE_INPUT_CHUNK_SIZE; 76 output_index += 4; 77 } 78 } 79 80 // then leftovers 81 if rem == 2 { 82 let chunk = &input[input_index..input_index + 2]; 83 84 // high six bits of chunk[0] 85 output[output_index] = self.encode_table[chunk[0].shr(2) as usize]; 86 // bottom 2 bits of [0], high 4 bits of [1] 87 output[output_index + 1] = 88 self.encode_table[(chunk[0].shl(4_u8).bitor(chunk[1].shr(4_u8)) as u32) 89 .bitand(LOW_SIX_BITS) as usize]; 90 // bottom 4 bits of [1], with the 2 bottom bits as zero 91 output[output_index + 2] = 92 self.encode_table[(chunk[1].shl(2_u8) as u32).bitand(LOW_SIX_BITS) as usize]; 93 94 output_index += 3; 95 } else if rem == 1 { 96 let byte = input[input_index]; 97 output[output_index] = self.encode_table[byte.shr(2) as usize]; 98 output[output_index + 1] = 99 self.encode_table[(byte.shl(4_u8) as u32).bitand(LOW_SIX_BITS) as usize]; 100 output_index += 2; 101 } 102 103 output_index 104 } 105 internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate106 fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate { 107 NaiveEstimate::new(input_len) 108 } 109 internal_decode( &self, input: &[u8], output: &mut [u8], estimate: Self::DecodeEstimate, ) -> Result<usize, DecodeError>110 fn internal_decode( 111 &self, 112 input: &[u8], 113 output: &mut [u8], 114 estimate: Self::DecodeEstimate, 115 ) -> Result<usize, DecodeError> { 116 if estimate.rem == 1 { 117 // trailing whitespace is so common that it's worth it to check the last byte to 118 // possibly return a better error message 119 if let Some(b) = input.last() { 120 if *b != PAD_BYTE 121 && self.decode_table[*b as usize] == general_purpose::INVALID_VALUE 122 { 123 return Err(DecodeError::InvalidByte(input.len() - 1, *b)); 124 } 125 } 126 127 return Err(DecodeError::InvalidLength); 128 } 129 130 let mut input_index = 0_usize; 131 let mut output_index = 0_usize; 132 const BOTTOM_BYTE: u32 = 0xFF; 133 134 // can only use the main loop on non-trailing chunks 135 if input.len() > Self::DECODE_INPUT_CHUNK_SIZE { 136 // skip the last chunk, whether it's partial or full, since it might 137 // have padding, and start at the beginning of the chunk before that 138 let last_complete_chunk_start_index = estimate.complete_chunk_len 139 - if estimate.rem == 0 { 140 // Trailing chunk is also full chunk, so there must be at least 2 chunks, and 141 // this won't underflow 142 Self::DECODE_INPUT_CHUNK_SIZE * 2 143 } else { 144 // Trailing chunk is partial, so it's already excluded in 145 // complete_chunk_len 146 Self::DECODE_INPUT_CHUNK_SIZE 147 }; 148 149 while input_index <= last_complete_chunk_start_index { 150 let chunk = &input[input_index..input_index + Self::DECODE_INPUT_CHUNK_SIZE]; 151 let decoded_int: u32 = self.decode_byte_into_u32(input_index, chunk[0])?.shl(18) 152 | self 153 .decode_byte_into_u32(input_index + 1, chunk[1])? 154 .shl(12) 155 | self.decode_byte_into_u32(input_index + 2, chunk[2])?.shl(6) 156 | self.decode_byte_into_u32(input_index + 3, chunk[3])?; 157 158 output[output_index] = decoded_int.shr(16_u8).bitand(BOTTOM_BYTE) as u8; 159 output[output_index + 1] = decoded_int.shr(8_u8).bitand(BOTTOM_BYTE) as u8; 160 output[output_index + 2] = decoded_int.bitand(BOTTOM_BYTE) as u8; 161 162 input_index += Self::DECODE_INPUT_CHUNK_SIZE; 163 output_index += 3; 164 } 165 } 166 167 general_purpose::decode_suffix::decode_suffix( 168 input, 169 input_index, 170 output, 171 output_index, 172 &self.decode_table, 173 self.config.decode_allow_trailing_bits, 174 self.config.decode_padding_mode, 175 ) 176 } 177 config(&self) -> &Self::Config178 fn config(&self) -> &Self::Config { 179 &self.config 180 } 181 } 182 183 pub struct NaiveEstimate { 184 /// remainder from dividing input by `Naive::DECODE_CHUNK_SIZE` 185 rem: usize, 186 /// Length of input that is in complete `Naive::DECODE_CHUNK_SIZE`-length chunks 187 complete_chunk_len: usize, 188 } 189 190 impl NaiveEstimate { new(input_len: usize) -> Self191 fn new(input_len: usize) -> Self { 192 let rem = input_len % Naive::DECODE_INPUT_CHUNK_SIZE; 193 let complete_chunk_len = input_len - rem; 194 195 Self { 196 rem, 197 complete_chunk_len, 198 } 199 } 200 } 201 202 impl DecodeEstimate for NaiveEstimate { decoded_len_estimate(&self) -> usize203 fn decoded_len_estimate(&self) -> usize { 204 ((self.complete_chunk_len / 4) + ((self.rem > 0) as usize)) * 3 205 } 206 } 207 208 #[derive(Clone, Copy, Debug)] 209 pub struct NaiveConfig { 210 pub encode_padding: bool, 211 pub decode_allow_trailing_bits: bool, 212 pub decode_padding_mode: DecodePaddingMode, 213 } 214 215 impl Config for NaiveConfig { encode_padding(&self) -> bool216 fn encode_padding(&self) -> bool { 217 self.encode_padding 218 } 219 } 220