1use crate::{Error, ASCII_OFFSET, MASK_SIX_BITS};
8
9#[inline(always)]
29pub fn decode(bytes: &[u8], len: usize) -> Result<String, Error> {
30 if bytes.len() != (len * 6 + 7) / 8 {
31 return Err(Error::InvalidBytesLength);
32 }
33 Ok(decode_core(bytes, len))
34}
35
36#[inline(always)]
62pub fn decode_unchecked(bytes: &[u8], len: usize) -> String {
63 decode_core(bytes, len)
64}
65
66#[inline(always)]
67fn decode_core(bytes: &[u8], len: usize) -> String {
68 if len == 0 {
69 return String::new();
70 }
71
72 let mut result = vec![0u8; len];
73 let full_chunks = len / 4;
74 let remaining_chars = len % 4;
75
76 let bytes_ptr = bytes.as_ptr();
77 let result_ptr: *mut u8 = result.as_mut_ptr();
78
79 unsafe {
80 for chunk_idx in 0..full_chunks {
82 let byte_idx = chunk_idx * 3;
83 let str_idx = chunk_idx * 4;
84
85 let bytes = ((*bytes_ptr.add(byte_idx) as u32) << 16)
87 | ((*bytes_ptr.add(byte_idx + 1) as u32) << 8)
88 | (*bytes_ptr.add(byte_idx + 2) as u32);
89
90 let char1 = ((bytes >> 18) as u8 & MASK_SIX_BITS) + ASCII_OFFSET;
92 let char2 = ((bytes >> 12) as u8 & MASK_SIX_BITS) + ASCII_OFFSET;
93 let char3 = ((bytes >> 6) as u8 & MASK_SIX_BITS) + ASCII_OFFSET;
94 let char4 = (bytes as u8 & MASK_SIX_BITS) + ASCII_OFFSET;
95
96 *result_ptr.add(str_idx) = char1;
98 *result_ptr.add(str_idx + 1) = char2;
99 *result_ptr.add(str_idx + 2) = char3;
100 *result_ptr.add(str_idx + 3) = char4;
101 }
102
103 match remaining_chars {
105 0 => {},
106 1 => {
107 let byte0 = *bytes_ptr.add(full_chunks * 3);
108 let char1 = (byte0 >> 2) + ASCII_OFFSET;
109 *result_ptr.add(full_chunks * 4) = char1;
110 },
111 2 => {
112 let byte0 = *bytes_ptr.add(full_chunks * 3);
113 let byte1 = *bytes_ptr.add(full_chunks * 3 + 1);
114 let char1 = (byte0 >> 2) + ASCII_OFFSET;
115 let char2 = (((byte0 & 0b00000011) << 4) | (byte1 >> 4)) + ASCII_OFFSET;
116 *result_ptr.add(full_chunks * 4) = char1;
117 *result_ptr.add(full_chunks * 4 + 1) = char2;
118 },
119 3 => {
120 let byte0 = *bytes_ptr.add(full_chunks * 3);
121 let byte1 = *bytes_ptr.add(full_chunks * 3 + 1);
122 let byte2 = *bytes_ptr.add(full_chunks * 3 + 2);
123 let char1 = (byte0 >> 2) + ASCII_OFFSET;
124 let char2 = (((byte0 & 0b00000011) << 4) | (byte1 >> 4)) + ASCII_OFFSET;
125 let char3 = (((byte1 & 0b00001111) << 2) | (byte2 >> 6)) + ASCII_OFFSET;
126 *result_ptr.add(full_chunks * 4) = char1;
127 *result_ptr.add(full_chunks * 4 + 1) = char2;
128 *result_ptr.add(full_chunks * 4 + 2) = char3;
129 },
130 _ => unreachable!(),
131 }
132 }
133
134 unsafe { String::from_utf8_unchecked(result) }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_decode_empty() {
144 let bytes = [];
145 let decoded = decode(&bytes, 0).unwrap();
146 assert_eq!(decoded, "");
147 }
148
149 #[test]
150 fn test_decode_basic() {
151 let input = "HELLO";
152 let (encoded_bytes, length) = crate::encode(input).unwrap();
153 let decoded = decode(&encoded_bytes, length).unwrap();
154 assert_eq!(decoded, input);
155 }
156
157 #[test]
158 fn test_decode_unchecked() {
159 let input = "WORLD";
160 let (encoded_bytes, length) = crate::encode(input).unwrap();
161 let decoded = decode_unchecked(&encoded_bytes, length);
162 assert_eq!(decoded, input);
163 }
164
165 #[test]
166 fn test_invalid_length() {
167 let bytes = [0u8; 2];
168 assert!(decode(&bytes, 3).is_err());
169 }
170
171 #[test]
172 fn test_not_zero_len_but_empty() {
173 let bytes = [0u8; 0];
174 let decoded = decode(&bytes, 1);
175 assert!(decoded.is_err());
176 }
177}