use base64;
const VLQ_BASE_SHIFT: i32 = 5;
const VLQ_BASE: i32 = 1 << VLQ_BASE_SHIFT;
const VLQ_BASE_MASK: i32 = VLQ_BASE - 1;
const VLQ_CONTINUATION_BIT: i32 = VLQ_BASE;
#[test]
fn it_defines_sane_constants() {
assert!(VLQ_BASE == 0b100000);
assert!(VLQ_BASE_MASK == 0b011111);
assert!(VLQ_CONTINUATION_BIT == 0b100000);
}
#[allow(dead_code)]
pub fn to_vql(value: i32) -> i32 {
if value < 0 {
((-value) << 1) + 1
} else {
(value << 1) + 0
}
}
#[test]
fn it_converts_to_vql() {
assert!(to_vql(1) == 2);
assert!(to_vql(-1) == 3);
assert!(to_vql(2) == 4);
assert!(to_vql(-2) == 5);
}
pub fn from_vql(value: i32) -> i32 {
let is_neative = (value & 1) == 1;
let shifted = value >> 1;
if is_neative {
-shifted
} else {
shifted
}
}
#[test]
fn it_converts_from_vql() {
assert!(from_vql(2) == 1);
assert!(from_vql(3) == -1);
assert!(from_vql(4) == 2);
assert!(from_vql(5) == -2);
}
#[allow(dead_code)]
pub fn encode(value: i32) -> Option<Vec<u8>> {
let mut encoded: Vec<u8> = Vec::new();
let mut vlq = to_vql(value);
loop {
let mut digit = vlq & VLQ_BASE_MASK;
vlq = vlq >> VLQ_BASE_SHIFT;
if vlq > 0 {
digit |= VLQ_CONTINUATION_BIT;
}
encoded.push(match base64::encode(digit) {
Some(x) => x,
None => return None
});
if vlq <= 0 {
break;
}
};
Some(encoded)
}
macro_rules! assert_encodes_to(
($number:expr, $string:expr) => (
assert!(encode($number) == Some($string.into()))
);
);
#[test]
fn it_encodes_some_numbers() {
assert_encodes_to!(-1000000, "hkh9B");
assert_encodes_to!(-124133, "ruyH");
assert_encodes_to!(-12332, "5iY");
assert_encodes_to!(-2222, "9qE");
assert_encodes_to!(-1579, "3iD");
assert_encodes_to!(-65, "jE");
assert_encodes_to!(-25, "zB");
assert_encodes_to!(-20, "pB");
assert_encodes_to!(-11, "X");
assert_encodes_to!(-9, "T");
assert_encodes_to!(-2, "F");
assert_encodes_to!(-1, "D");
assert_encodes_to!(0, "A");
assert_encodes_to!(1, "C");
assert_encodes_to!(7, "O");
assert_encodes_to!(15, "e");
assert_encodes_to!(23, "uB");
assert_encodes_to!(88, "wF");
assert_encodes_to!(1254, "suC");
assert_encodes_to!(2493, "67E");
assert_encodes_to!(23903, "+1uB");
assert_encodes_to!(129383, "u28H");
assert_encodes_to!(298322, "k1mS");
assert_encodes_to!(1000000, "gkh9B");
}
pub fn decode(encoded: &[u8]) -> Option<(i32, usize)> {
let mut result: i32 = 0;
let mut shift: i32 = 0;
let mut characters_read = 0;
for &character in encoded {
characters_read += 1;
let mut digit = match base64::decode(character) {
Some(x) => x,
None => return None
};
let continuation = (digit & VLQ_CONTINUATION_BIT) != 0;
digit &= VLQ_BASE_MASK;
result = result + (digit << shift);
shift += VLQ_BASE_SHIFT;
if shift >= 32 {
return None;
}
if !continuation {
break;
}
}
Some((from_vql(result), characters_read))
}
macro_rules! assert_decodes_to(
($str_slice:expr, $number:expr) => (
assert!(decode($str_slice).unwrap().0 == $number)
);
);
#[test]
fn it_decodes_some_numbers() {
assert_decodes_to!(b"hkh9B", -1000000);
assert_decodes_to!(b"ruyH", -124133);
assert_decodes_to!(b"5iY", -12332);
assert_decodes_to!(b"9qE", -2222);
assert_decodes_to!(b"3iD", -1579);
assert_decodes_to!(b"jE", -65);
assert_decodes_to!(b"zB", -25);
assert_decodes_to!(b"pB", -20);
assert_decodes_to!(b"X", -11);
assert_decodes_to!(b"T", -9);
assert_decodes_to!(b"F", -2);
assert_decodes_to!(b"D", -1);
assert_decodes_to!(b"A", 0);
assert_decodes_to!(b"C", 1);
assert_decodes_to!(b"O", 7);
assert_decodes_to!(b"e", 15);
assert_decodes_to!(b"uB", 23);
assert_decodes_to!(b"wF", 88);
assert_decodes_to!(b"suC", 1254);
assert_decodes_to!(b"67E", 2493);
assert_decodes_to!(b"+1uB", 23903);
assert_decodes_to!(b"u28H", 129383);
assert_decodes_to!(b"k1mS", 298322);
assert_decodes_to!(b"gkh9B", 1000000);
}
#[test]
fn it_returns_sane_field_length() {
{
let result = decode(b"ABCDE").unwrap();
assert!(result.0 == 0);
assert!(result.1 == 1);
}
{
let result = decode(&b"ABCDE"[1..5]).unwrap();
assert!(result.0 == 0);
assert!(result.1 == 1);
}
{
let result = decode(&b"ABCDE"[2..5]).unwrap();
assert!(result.0 == 1);
assert!(result.1 == 1);
}
{
let result = decode(&b"ABCDE"[3..5]).unwrap();
assert!(result.0 == -1);
assert!(result.1 == 1);
}
{
let result = decode(&b"ABCDE"[4..5]).unwrap();
assert!(result.0 == 2);
assert!(result.1 == 1);
}
}
#[test]
fn it_does_not_panic_on_long_strings() {
decode(b"00000000");
}