1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
21#![allow(unsafe_code)]
24
25mod simd;
26
27use std::fmt;
28
29pub const SYMBOL_COUNT: u8 = 94;
31const ESCAPE_RADIX: u8 = 93;
33pub const DECIMAL_MAX_LEN: usize = 10;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct DecodeError;
40
41impl fmt::Display for DecodeError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str("invalid base94 data")
44 }
45}
46
47impl std::error::Error for DecodeError {}
48
49pub fn encode_into(out: &mut Vec<u8>, src: &[u8], kf: u32) {
56 const UNROLL: usize = 8;
61
62 let kf8 = kf as u8;
63 let total = src.len() + simd::count_sub_ge(src, kf8, ESCAPE_RADIX);
64 let start = out.len();
65 out.reserve(total + 16);
70 let dst = out.as_mut_ptr();
71 let mut p = start;
72 let simd_consumed = simd::encode_simd(dst, &mut p, src, kf8);
74 let mut idx = simd_consumed;
75 while idx + UNROLL <= src.len() {
76 let chunk: [u8; UNROLL] = src[idx..idx + UNROLL].try_into().expect("fixed size");
77 let mut offs = [0usize; UNROLL];
78 let mut cursor = 0usize;
79 for (k, &b) in chunk.iter().enumerate() {
80 offs[k] = cursor;
81 cursor += 1 + usize::from(b.wrapping_sub(kf8) >= ESCAPE_RADIX);
82 }
83 for (k, &b) in chunk.iter().enumerate() {
84 let v = b.wrapping_sub(kf8);
85 let esc = u8::from(v >= ESCAPE_RADIX);
86 let q2 = u8::from(v >= 2 * ESCAPE_RADIX);
89 let c1 = if esc != 0 {
90 0x7d + q2
91 } else {
92 0x20 + v
93 };
94 let c2 = 0x20u8.wrapping_add(
95 v.wrapping_sub(ESCAPE_RADIX)
96 .wrapping_sub(ESCAPE_RADIX.wrapping_mul(q2)),
97 );
98 unsafe {
100 let q = dst.add(p + offs[k]);
101 *q = c1;
102 *q.add(1) = c2;
103 }
104 }
105 p += cursor;
106 idx += UNROLL;
107 }
108 for &b in &src[idx..] {
109 let v = b.wrapping_sub(kf8);
110 let esc = u8::from(v >= ESCAPE_RADIX);
111 let q2 = u8::from(v >= 2 * ESCAPE_RADIX);
112 let c1 = if esc != 0 {
113 0x7d + q2
114 } else {
115 0x20 + v
116 };
117 let c2 = 0x20u8.wrapping_add(
118 v.wrapping_sub(ESCAPE_RADIX)
119 .wrapping_sub(ESCAPE_RADIX.wrapping_mul(q2)),
120 );
121 unsafe {
123 *dst.add(p) = c1;
124 *dst.add(p + 1) = c2;
125 }
126 p += 1 + usize::from(esc);
127 }
128 unsafe { out.set_len(start + total) };
130}
131
132#[must_use]
134pub fn encoded_len(src: &[u8], kf: u32) -> usize {
135 src.len() + simd::count_sub_ge(src, kf as u8, ESCAPE_RADIX)
136}
137
138pub fn decode_into(out: &mut Vec<u8>, src: &[u8], kf: u32) -> Result<(), DecodeError> {
147 if !simd::all_ge(src, 0x20) {
148 return Err(DecodeError);
149 }
150 let kf8 = kf as u8;
151 let kf16 = u16::from(kf8);
152 let start = out.len();
153 out.reserve(src.len() + 16);
156 let dst = out.as_mut_ptr();
157 let mut i;
158 let mut p = start;
159 let n = src.len();
160 match simd::decode_simd(dst, &mut p, src, kf8) {
164 Ok((consumed, _)) | Err((consumed, _)) => i = consumed,
165 }
166 while i + 1 < n {
171 let b = u16::from(src[i]) - 0x20;
172 let b2 = u16::from(src[i + 1]) - 0x20;
173 let esc = u16::from(b >= u16::from(ESCAPE_RADIX));
174 let v_esc = (b.wrapping_sub(92))
177 .wrapping_mul(u16::from(ESCAPE_RADIX))
178 .wrapping_add(b2);
179 let bad = esc
182 & (u16::from(b > 94)
183 | u16::from(b2 > u16::from(ESCAPE_RADIX))
184 | u16::from(v_esc > 0xff));
185 if bad != 0 {
186 return Err(DecodeError);
187 }
188 let val = if esc != 0 {
189 v_esc
190 } else {
191 b
192 };
193 unsafe { *dst.add(p) = val.wrapping_add(kf16) as u8 };
196 p += 1;
197 i += 1 + esc as usize;
198 }
199 if i < n {
200 let b = src[i] - 0x20;
202 if b >= ESCAPE_RADIX {
203 return Err(DecodeError);
204 }
205 unsafe { *dst.add(p) = b.wrapping_add(kf8) };
206 p += 1;
207 }
208 unsafe { out.set_len(p) };
210 Ok(())
211}
212
213#[must_use]
215pub fn decimal_encode(v: u64, out: &mut [u8; DECIMAL_MAX_LEN]) -> usize {
216 let mut n = v;
217 let mut len = 0;
218 loop {
219 out[len] = (n % u64::from(SYMBOL_COUNT)) as u8 + 0x20;
220 len += 1;
221 n /= u64::from(SYMBOL_COUNT);
222 if n == 0 {
223 break;
224 }
225 }
226 out[..len].reverse();
227 len
228}
229
230pub fn decimal_decode(s: &[u8]) -> Result<u64, DecodeError> {
233 if s.is_empty() {
234 return Err(DecodeError);
235 }
236 let mut n: u64 = 0;
237 for &c in s {
238 if c < 0x20 {
239 return Err(DecodeError);
240 }
241 let d = c - 0x20;
242 if d >= SYMBOL_COUNT {
243 return Err(DecodeError);
244 }
245 n = n
246 .checked_mul(u64::from(SYMBOL_COUNT))
247 .and_then(|n| n.checked_add(u64::from(d)))
248 .ok_or(DecodeError)?;
249 }
250 Ok(n)
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn roundtrip_and_printable() {
259 for kf in [0u32, 1, 93, 94, 0xff, 0xdead_beef] {
260 let original: Vec<u8> = (0..=u8::MAX).collect();
261 let mut encoded = Vec::new();
262 encode_into(&mut encoded, &original, kf);
263 assert!(encoded.iter().all(|&c| (0x20..=0x7e).contains(&c)));
264 assert_eq!(encoded.len(), encoded_len(&original, kf));
265 let mut decoded = Vec::new();
266 decode_into(&mut decoded, &encoded, kf).unwrap();
267 assert_eq!(decoded, original);
268 }
269 }
270
271 #[test]
272 fn roundtrip_large_and_edge_shapes() {
273 let mut s = 0x0bad_c0deu64;
274 let mut step = || {
275 s ^= s << 13;
276 s ^= s >> 7;
277 s ^= s << 17;
278 (s >> 56) as u8
279 };
280 let all_escape: Vec<u8> = (0..2000)
283 .map(|i| 93u8.wrapping_add(i as u8 % 163))
284 .collect();
285 let no_escape: Vec<u8> = vec![7u8; 2001];
286 for dataset in [all_escape, no_escape] {
287 for kf in [0u32, 0x5a5a_5a5a] {
288 let mut encoded = Vec::new();
289 encode_into(&mut encoded, &dataset, kf);
290 assert_eq!(encoded.len(), encoded_len(&dataset, kf));
291 let mut decoded = Vec::new();
292 decode_into(&mut decoded, &encoded, kf).unwrap();
293 assert_eq!(decoded, dataset);
294 }
295 }
296 let mut random: Vec<u8> = (0..65_537).map(|_| step()).collect();
297 random[0] = 0x93; let mut encoded = Vec::new();
299 encode_into(&mut encoded, &random, 154_543_927);
300 let mut decoded = Vec::new();
301 decode_into(&mut decoded, &encoded, 154_543_927).unwrap();
302 assert_eq!(decoded, random);
303 }
304
305 #[test]
306 fn decode_uses_existing_out_prefix() {
307 let original = vec![0xde, 0xad, 0xbe, 0xef];
310 let mut encoded = Vec::new();
311 encode_into(&mut encoded, &original, 123);
312 let mut out = b"prefix".to_vec();
313 decode_into(&mut out, &encoded, 123).unwrap();
314 assert_eq!(out, [b"prefix".as_slice(), original.as_slice()].concat());
315 }
316
317 #[test]
318 fn escape_boundaries() {
319 let mut encoded = Vec::new();
321 encode_into(&mut encoded, &[0, 92, 93, 94, 255], 0);
322 assert_eq!(encoded, [0x20, 0x7c, 0x7d, 0x20, 0x7d, 0x21, 0x7e, 0x65]);
325 }
326
327 #[test]
328 fn decode_rejects_garbage() {
329 let mut out = Vec::new();
330 assert!(decode_into(&mut out, &[0x1f], 0).is_err());
331 assert!(decode_into(&mut out, &[0x7f], 0).is_err());
332 assert!(decode_into(&mut out, &[0x7d], 0).is_err()); assert!(decode_into(&mut out, &[0x7d, 0x7e], 0).is_err()); assert!(decode_into(&mut out, &[0x7e, 0x7e], 0).is_err()); assert!(decode_into(&mut out, &[0x7f, 0x20], 0).is_err());
337 assert!(out.is_empty(), "no partial output on failure");
338 }
339
340 fn decode_reference(out: &mut Vec<u8>, src: &[u8], kf: u32) -> Result<(), DecodeError> {
344 let kf8 = kf as u8;
345 let start = out.len();
346 let mut i = 0;
347 while i < src.len() {
348 let c = src[i];
349 if c < 0x20 {
350 out.truncate(start);
351 return Err(DecodeError);
352 }
353 let b = c - 0x20;
354 if b < ESCAPE_RADIX {
355 out.push(b.wrapping_add(kf8));
356 i += 1;
357 continue;
358 }
359 if b > 94 {
360 out.truncate(start);
361 return Err(DecodeError);
362 }
363 let Some(&c2) = src.get(i + 1) else {
364 out.truncate(start);
365 return Err(DecodeError);
366 };
367 if c2 < 0x20 {
368 out.truncate(start);
369 return Err(DecodeError);
370 }
371 let b2 = c2 - 0x20;
372 if b2 > ESCAPE_RADIX {
373 out.truncate(start);
374 return Err(DecodeError);
375 }
376 if b == 94 && b2 > 0xff - 2 * ESCAPE_RADIX {
377 out.truncate(start);
378 return Err(DecodeError);
379 }
380 let v = u32::from(b - ESCAPE_RADIX + 1) * u32::from(ESCAPE_RADIX) + u32::from(b2);
381 out.push((v as u8).wrapping_add(kf8));
382 i += 2;
383 }
384 Ok(())
385 }
386
387 #[test]
388 fn decode_fuzz_matches_reference() {
389 let mut s = 0x00c0_ffee_d00d_feedu64;
390 let mut next = || {
391 s ^= s << 13;
392 s ^= s >> 7;
393 s ^= s << 17;
394 s
395 };
396 for len in [
400 0usize, 1, 15, 16, 17, 31, 33, 64, 100, 199, 200, 201, 255, 256, 257, 513,
401 ] {
402 for _ in 0..40 {
403 let input: Vec<u8> = (0..len)
404 .map(|_| {
405 let r = (next() >> 32) as u8;
406 match r % 8 {
407 0..=4 => 0x20 + (r % 93),
408 5 => 0x7b,
409 6 => 0x7d + (r % 2),
410 _ => r, }
412 })
413 .collect();
414 for kf in [0u32, 77, 0x5a5a_5a5a] {
415 let mut got = Vec::new();
416 let mut want = Vec::new();
417 let a = decode_into(&mut got, &input, kf);
418 let b = decode_reference(&mut want, &input, kf);
419 assert_eq!(a.is_ok(), b.is_ok(), "len={len} kf={kf} ok-ness");
420 if let (Ok(()), Ok(())) = (a, b) {
421 assert_eq!(got, want, "len={len} kf={kf}");
422 }
423 }
424 }
425 }
426 }
427
428 #[test]
429 fn decode_rejects_7e_follower_in_simd_block() {
430 let input: Vec<u8> = b"ppppppppp#i}~C}(".to_vec();
434 assert_eq!(input.len(), 16);
435 let mut out = Vec::new();
436 assert!(decode_into(&mut out, &input, 125).is_err());
437 assert_eq!(out, [] as [u8; 0]);
438 for shift in 0..15usize {
440 let mut v = vec![0x41u8; 16];
441 v[shift] = 0x7d;
442 v[shift + 1] = 0x7e;
443 let mut out = Vec::new();
444 assert!(decode_into(&mut out, &v, 0).is_err(), "shift {shift}");
445 }
446 let mut v = vec![0x41u8; 16];
449 v[0] = 0x7e;
450 v[1] = 0x7d;
451 let mut out = Vec::new();
452 assert!(decode_into(&mut out, &v, 0).is_err());
453 assert_eq!(out, [] as [u8; 0]);
454 }
455
456 #[test]
457 fn decode_legal_adjacent_escape_pair() {
458 let mut big = vec![0x7du8; 64];
461 big.extend_from_slice(&[0x41; 8]);
462 for kf in [0u32, 200] {
463 let mut got = Vec::new();
464 decode_into(&mut got, &big, kf).unwrap();
465 let mut want = Vec::new();
466 decode_reference(&mut want, &big, kf).unwrap();
467 assert_eq!(got, want);
468 assert_eq!(got.len(), 32 + 8);
469 }
470 }
471
472 #[test]
473 fn decimal_roundtrip() {
474 let mut buf = [0u8; DECIMAL_MAX_LEN];
475 for v in [0u64, 1, 93, 94, 830_583, u64::from(u32::MAX), u64::MAX] {
476 let len = decimal_encode(v, &mut buf);
477 let digits = &buf[..len];
478 assert!(digits.iter().all(|&c| c >= 0x20));
479 if v > 0 {
480 assert_ne!(digits[0], 0x20, "no leading zero");
481 }
482 assert_eq!(decimal_decode(digits).unwrap(), v);
483 if len <= 3 {
486 let mut padded = [0x20u8; 3];
487 padded[3 - len..].copy_from_slice(digits);
488 assert_eq!(decimal_decode(&padded).unwrap(), v);
489 }
490 }
491 }
492
493 #[test]
494 fn decimal_known_value() {
495 let mut buf = [0u8; DECIMAL_MAX_LEN];
497 let len = decimal_encode(8836, &mut buf);
498 assert_eq!(len, 3);
499 assert_eq!(&buf[..3], &[0x21, 0x20, 0x20]);
500 }
501}