use crate::DecodeError;
const VLQ_BASE_SHIFT: u32 = 5;
const VLQ_BASE: u64 = 1 << VLQ_BASE_SHIFT; const VLQ_BASE_MASK: u64 = VLQ_BASE - 1; const VLQ_CONTINUATION_BIT: u64 = VLQ_BASE;
const VLQ_MAX_SHIFT: u32 = 60;
#[rustfmt::skip]
const BASE64_ENCODE: [u8; 64] = [
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H',
b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P',
b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f',
b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n',
b'o', b'p', b'q', b'r', b's', b't', b'u', b'v',
b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3',
b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/',
];
#[repr(align(64))]
struct AlignedBase64Table([u8; 64]);
#[rustfmt::skip]
static BASE64_TABLE: AlignedBase64Table = AlignedBase64Table([
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H',
b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P',
b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f',
b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n',
b'o', b'p', b'q', b'r', b's', b't', b'u', b'v',
b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3',
b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/',
]);
const BASE64_DECODE: [u8; 128] = {
let mut table = [255u8; 128];
let mut i = 0u8;
while i < 64 {
table[BASE64_ENCODE[i as usize] as usize] = i;
i += 1;
}
table
};
#[inline(always)]
pub unsafe fn vlq_encode_unchecked(out: &mut Vec<u8>, value: i64) {
let mut vlq: u64 = if value >= 0 {
(value as u64) << 1
} else {
(((!(value as u64)) + 1) << 1) | 1
};
let table = &BASE64_TABLE.0;
let ptr = unsafe { out.as_mut_ptr().add(out.len()) };
let mut i = 0;
loop {
let digit = vlq & VLQ_BASE_MASK;
vlq >>= VLQ_BASE_SHIFT;
if vlq == 0 {
unsafe {
*ptr.add(i) = *table.get_unchecked(digit as usize);
}
i += 1;
break;
}
unsafe {
*ptr.add(i) = *table.get_unchecked((digit | VLQ_CONTINUATION_BIT) as usize);
}
i += 1;
}
unsafe { out.set_len(out.len() + i) };
}
#[inline(always)]
pub fn vlq_encode(out: &mut Vec<u8>, value: i64) {
out.reserve(7);
unsafe { vlq_encode_unchecked(out, value) }
}
#[inline(always)]
pub fn vlq_decode(input: &[u8], pos: usize) -> Result<(i64, usize), DecodeError> {
if pos >= input.len() {
return Err(DecodeError::UnexpectedEof { offset: pos });
}
let b0 = input[pos];
if b0 >= 128 {
return Err(DecodeError::InvalidBase64 { byte: b0, offset: pos });
}
let d0 = BASE64_DECODE[b0 as usize];
if d0 == 255 {
return Err(DecodeError::InvalidBase64 { byte: b0, offset: pos });
}
if (d0 & 0x20) == 0 {
let val = (d0 >> 1) as i64;
return Ok((if (d0 & 1) != 0 { -val } else { val }, 1));
}
let mut result: u64 = (d0 & 0x1F) as u64;
let mut shift: u32 = 5;
let mut i = pos + 1;
loop {
if i >= input.len() {
return Err(DecodeError::UnexpectedEof { offset: i });
}
let byte = input[i];
if byte >= 128 {
return Err(DecodeError::InvalidBase64 { byte, offset: i });
}
let digit = BASE64_DECODE[byte as usize];
if digit == 255 {
return Err(DecodeError::InvalidBase64 { byte, offset: i });
}
i += 1;
if shift >= VLQ_MAX_SHIFT {
return Err(DecodeError::VlqOverflow { offset: pos });
}
result += ((digit & 0x1F) as u64) << shift;
shift += VLQ_BASE_SHIFT;
if (digit & 0x20) == 0 {
break;
}
}
let value = if (result & 1) == 1 { -((result >> 1) as i64) } else { (result >> 1) as i64 };
Ok((value, i - pos))
}
#[inline(always)]
pub unsafe fn vlq_encode_unsigned_unchecked(out: &mut Vec<u8>, value: u64) {
let table = &BASE64_TABLE.0;
let ptr = unsafe { out.as_mut_ptr().add(out.len()) };
let mut i = 0;
let mut vlq = value;
loop {
let digit = vlq & VLQ_BASE_MASK;
vlq >>= VLQ_BASE_SHIFT;
if vlq == 0 {
unsafe {
*ptr.add(i) = *table.get_unchecked(digit as usize);
}
i += 1;
break;
}
unsafe {
*ptr.add(i) = *table.get_unchecked((digit | VLQ_CONTINUATION_BIT) as usize);
}
i += 1;
}
unsafe { out.set_len(out.len() + i) };
}
#[inline(always)]
pub fn vlq_encode_unsigned(out: &mut Vec<u8>, value: u64) {
out.reserve(7);
unsafe { vlq_encode_unsigned_unchecked(out, value) }
}
#[inline]
pub fn vlq_decode_unsigned(input: &[u8], pos: usize) -> Result<(u64, usize), DecodeError> {
if pos >= input.len() {
return Err(DecodeError::UnexpectedEof { offset: pos });
}
let b0 = input[pos];
if b0 >= 128 {
return Err(DecodeError::InvalidBase64 { byte: b0, offset: pos });
}
let d0 = BASE64_DECODE[b0 as usize];
if d0 == 255 {
return Err(DecodeError::InvalidBase64 { byte: b0, offset: pos });
}
if (d0 & 0x20) == 0 {
return Ok((d0 as u64, 1));
}
let mut result: u64 = (d0 & 0x1F) as u64;
let mut shift: u32 = 5;
let mut i = pos + 1;
loop {
if i >= input.len() {
return Err(DecodeError::UnexpectedEof { offset: i });
}
let byte = input[i];
if byte >= 128 {
return Err(DecodeError::InvalidBase64 { byte, offset: i });
}
let digit = BASE64_DECODE[byte as usize];
if digit == 255 {
return Err(DecodeError::InvalidBase64 { byte, offset: i });
}
i += 1;
if shift >= VLQ_MAX_SHIFT {
return Err(DecodeError::VlqOverflow { offset: pos });
}
result += ((digit & 0x1F) as u64) << shift;
shift += VLQ_BASE_SHIFT;
if (digit & 0x20) == 0 {
break;
}
}
Ok((result, i - pos))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_zero() {
let mut buf = Vec::new();
vlq_encode(&mut buf, 0);
assert_eq!(&buf, b"A");
}
#[test]
fn encode_positive() {
let mut buf = Vec::new();
vlq_encode(&mut buf, 1);
assert_eq!(&buf, b"C");
}
#[test]
fn encode_negative() {
let mut buf = Vec::new();
vlq_encode(&mut buf, -1);
assert_eq!(&buf, b"D");
}
#[test]
fn encode_large_value() {
let mut buf = Vec::new();
vlq_encode(&mut buf, 1000);
let (decoded, _) = vlq_decode(&buf, 0).unwrap();
assert_eq!(decoded, 1000);
}
#[test]
fn roundtrip_values() {
let values = [
0,
1,
-1,
15,
-15,
16,
-16,
31,
32,
100,
-100,
1000,
-1000,
100_000,
1_000_000_000,
-1_000_000_000,
];
for &v in &values {
let mut buf = Vec::new();
vlq_encode(&mut buf, v);
let (decoded, consumed) = vlq_decode(&buf, 0).unwrap();
assert_eq!(decoded, v, "roundtrip failed for {v}");
assert_eq!(consumed, buf.len());
}
}
#[test]
fn decode_multi_char() {
let mut buf = Vec::new();
vlq_encode(&mut buf, 500);
assert!(buf.len() > 1, "500 should need multiple chars");
let (decoded, _) = vlq_decode(&buf, 0).unwrap();
assert_eq!(decoded, 500);
}
#[test]
fn decode_non_ascii_byte() {
let input = [0xC0u8];
let err = vlq_decode(&input, 0).unwrap_err();
assert_eq!(err, DecodeError::InvalidBase64 { byte: 0xC0, offset: 0 });
}
#[test]
fn decode_invalid_base64_char() {
let input = b"!";
let err = vlq_decode(input, 0).unwrap_err();
assert_eq!(err, DecodeError::InvalidBase64 { byte: b'!', offset: 0 });
}
#[test]
fn decode_unexpected_eof() {
let input = b"g";
let err = vlq_decode(input, 0).unwrap_err();
assert_eq!(err, DecodeError::UnexpectedEof { offset: 1 });
}
#[test]
fn decode_overflow() {
let input = b"ggggggggggggggA";
let err = vlq_decode(input, 0).unwrap_err();
assert!(matches!(err, DecodeError::VlqOverflow { .. }));
}
#[test]
fn decode_empty_input() {
let err = vlq_decode(b"", 0).unwrap_err();
assert_eq!(err, DecodeError::UnexpectedEof { offset: 0 });
}
#[test]
fn unsigned_encode_zero() {
let mut buf = Vec::new();
vlq_encode_unsigned(&mut buf, 0);
assert_eq!(&buf, b"A");
}
#[test]
fn unsigned_encode_small_values() {
let mut buf = Vec::new();
vlq_encode_unsigned(&mut buf, 1);
assert_eq!(&buf, b"B");
buf.clear();
vlq_encode_unsigned(&mut buf, 8);
assert_eq!(&buf, b"I");
}
#[test]
fn unsigned_roundtrip() {
let values: [u64; 10] = [0, 1, 8, 15, 16, 31, 32, 100, 1000, 100_000];
for &v in &values {
let mut buf = Vec::new();
vlq_encode_unsigned(&mut buf, v);
let (decoded, consumed) = vlq_decode_unsigned(&buf, 0).unwrap();
assert_eq!(decoded, v, "unsigned roundtrip failed for {v}");
assert_eq!(consumed, buf.len());
}
}
#[test]
fn unsigned_multi_char() {
let mut buf = Vec::new();
vlq_encode_unsigned(&mut buf, 500);
assert!(buf.len() > 1, "500 should need multiple chars");
let (decoded, _) = vlq_decode_unsigned(&buf, 0).unwrap();
assert_eq!(decoded, 500);
}
#[test]
fn unsigned_decode_empty() {
let err = vlq_decode_unsigned(b"", 0).unwrap_err();
assert_eq!(err, DecodeError::UnexpectedEof { offset: 0 });
}
#[test]
fn unsigned_decode_non_ascii() {
let err = vlq_decode_unsigned(&[0xC3, 0x80], 0).unwrap_err();
assert_eq!(err, DecodeError::InvalidBase64 { byte: 0xC3, offset: 0 });
}
#[test]
fn unsigned_decode_invalid_base64_char() {
let err = vlq_decode_unsigned(b"!", 0).unwrap_err();
assert_eq!(err, DecodeError::InvalidBase64 { byte: b'!', offset: 0 });
}
#[test]
fn unsigned_decode_overflow() {
let err = vlq_decode_unsigned(b"ggggggggggggggA", 0).unwrap_err();
assert!(matches!(err, DecodeError::VlqOverflow { .. }));
}
}