1use crate::support::encoding::{char_handling_error, parse_utf8};
18use crate::tokenizer::vocab_type::VocabType;
19
20pub fn decode_token(token: &str, vocab_type: VocabType) -> Vec<u8> {
22 match vocab_type {
23 VocabType::Raw => token.as_bytes().to_vec(),
24 VocabType::ByteFallback => space_replacer_decoder(&byte_fallback_decoder(token)),
25 VocabType::ByteLevel => byte_level_decoder(token),
26 }
27}
28
29fn byte_fallback_decoder(token: &str) -> Vec<u8> {
35 let bytes = token.as_bytes();
36 if bytes.len() == 6 && &bytes[0..3] == b"<0x" && bytes[5] == b'>' {
37 let mut byte: i32 = 0;
38 for &c in &bytes[3..5] {
39 let digit = if c.is_ascii_digit() {
41 (c - b'0') as i32
42 } else {
43 (c - b'A') as i32 + 10
44 };
45 byte = byte * 16 + digit;
46 }
47 return vec![byte as u8];
50 }
51 token.as_bytes().to_vec()
52}
53
54fn space_replacer_decoder(token: &[u8]) -> Vec<u8> {
59 let mut result = Vec::with_capacity(token.len());
60 let mut i = 0;
61 while i < token.len() {
62 if i + 2 < token.len() && token[i] == 0xE2 && token[i + 1] == 0x96 && token[i + 2] == 0x81 {
64 result.push(b' ');
65 i += 3;
66 } else {
67 result.push(token[i]);
68 i += 1;
69 }
70 }
71 result
72}
73
74pub fn char_to_byte_map() -> &'static [i16; 324] {
80 &CHAR_TO_BYTE_MAP
81}
82
83pub fn byte_to_char_map() -> &'static [u32; 256] {
89 &BYTE_TO_CHAR_MAP
90}
91
92#[rustfmt::skip]
100const CHAR_TO_BYTE_MAP: [i16; 324] = [
101 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
102 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
103 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
104 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
105 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
106 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, -1, -1, -1, -1,
107 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
108 -1, -1, -1, -1, -1, -1, -1, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, -1,
109 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
110 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
111 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
112 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
113 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
114 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 127, 128,
115 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
116 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 173,
117];
118
119const BYTE_TO_CHAR_MAP: [u32; 256] = build_byte_to_char_map();
123
124const fn build_byte_to_char_map() -> [u32; 256] {
125 let mut out = [0u32; 256];
126 let mut cp = 0usize;
127 while cp < CHAR_TO_BYTE_MAP.len() {
128 let b = CHAR_TO_BYTE_MAP[cp];
129 if b >= 0 {
130 out[b as usize] = cp as u32;
131 }
132 cp += 1;
133 }
134 out
135}
136
137fn byte_level_decoder(token: &str) -> Vec<u8> {
143 let codepoints = parse_utf8(token.as_bytes(), false);
144 if codepoints.len() == 1 && codepoints[0] == char_handling_error::INVALID_UTF8 {
145 return token.as_bytes().to_vec();
146 }
147
148 let map = char_to_byte_map();
149 let mut decoded = Vec::with_capacity(codepoints.len());
150 for cp in codepoints {
151 if cp < 0 || cp as usize >= map.len() || map[cp as usize] == -1 {
155 return token.as_bytes().to_vec();
156 }
157 decoded.push(map[cp as usize] as u8);
158 }
159 decoded
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn raw_is_literal() {
168 assert_eq!(decode_token("hello", VocabType::Raw), b"hello");
169 assert_eq!(decode_token("", VocabType::Raw), b"");
170 }
171
172 #[test]
173 fn byte_fallback_hex_token() {
174 assert_eq!(decode_token("<0x1B>", VocabType::ByteFallback), vec![0x1B]);
176 assert_eq!(decode_token("<0x00>", VocabType::ByteFallback), vec![0x00]);
178 assert_eq!(decode_token("<0xFF>", VocabType::ByteFallback), vec![0xFF]);
180 }
181
182 #[test]
183 fn byte_fallback_non_hex_passthrough() {
184 assert_eq!(decode_token("er", VocabType::ByteFallback), b"er");
186 assert_eq!(decode_token("<0x1>", VocabType::ByteFallback), b"<0x1>");
191 }
192
193 #[test]
194 fn byte_fallback_space_marker() {
195 let token = "\u{2581}hello";
197 assert_eq!(decode_token(token, VocabType::ByteFallback), b" hello");
198 }
199
200 #[test]
201 fn byte_fallback_double_space_marker() {
202 let token = "\u{2581}\u{2581}";
203 assert_eq!(decode_token(token, VocabType::ByteFallback), b" ");
204 }
205
206 #[test]
207 fn byte_level_round_trip_ascii() {
208 let map = byte_to_char_map();
211 assert_eq!(map[0x41], 0x41);
213 let mut enc = String::new();
214 for &b in b"automotive" {
215 enc.push(char::from_u32(map[b as usize]).unwrap());
216 }
217 assert_eq!(decode_token(&enc, VocabType::ByteLevel), b"automotive");
218 }
219
220 #[test]
221 fn byte_level_space_is_g_with_dot() {
222 let map = byte_to_char_map();
224 assert_eq!(map[0x20], 0x0120);
225 let mut enc = String::from("\u{0120}");
227 for &b in b"automotive" {
228 enc.push(char::from_u32(map[b as usize]).unwrap());
229 }
230 assert_eq!(decode_token(&enc, VocabType::ByteLevel), b" automotive");
231 }
232
233 #[test]
234 fn byte_level_high_bytes() {
235 let map = byte_to_char_map();
237 let mut enc = String::new();
238 for &b in "我".as_bytes() {
239 enc.push(char::from_u32(map[b as usize]).unwrap());
240 }
241 assert_eq!(decode_token(&enc, VocabType::ByteLevel), "我".as_bytes());
242 }
243
244 #[test]
245 fn byte_level_unmapped_codepoint_passthrough() {
246 assert_eq!(decode_token("中", VocabType::ByteLevel), "中".as_bytes());
248 }
249
250 #[test]
251 fn inverse_table_is_consistent() {
252 let inv = char_to_byte_map();
254 let fwd = byte_to_char_map();
255 for byte in 0u32..256 {
256 let cp = fwd[byte as usize];
257 assert_eq!(inv[cp as usize], byte as i16);
258 }
259 let mut seen = std::collections::HashSet::new();
262 for byte in 0u32..256 {
263 assert!(seen.insert(fwd[byte as usize]));
264 }
265 }
266
267 #[test]
268 fn byte_level_invalid_utf8_passthrough() {
269 assert_eq!(char_to_byte_map()[0], -1);
272 }
273}