use crate::unwrap_return;
pub trait ExtensibleInteger {
fn is_byte() -> bool;
fn is_signed() -> bool;
fn to_u8(&self) -> u8;
fn from_u8(value: u8) -> Self;
fn to_u128(&self) -> u128;
fn from_u128(value: u128) -> Self;
fn to_i128(&self) -> i128;
fn from_i128(value: i128) -> Self;
}
macro_rules! impl_int_traits {
( $( ($type:ty, $is_byte:expr, $is_signed:expr), )* ) => {
$(
impl ExtensibleInteger for $type {
fn is_byte() -> bool { $is_byte }
fn is_signed() -> bool { $is_signed }
fn to_u8(&self) -> u8 { *self as u8 }
fn from_u8(value: u8) -> Self { value as Self }
fn to_u128(&self) -> u128 { *self as u128 }
fn from_u128(value: u128) -> Self { value as Self }
fn to_i128(&self) -> i128 { *self as i128 }
fn from_i128(value: i128) -> Self { value as Self }
}
)*
};
}
impl_int_traits!(
(u8, true, false),
(u16, false, false),
(u32, false, false),
(u64, false, false),
(u128, false, false),
(usize, false, false),
(i8, true, true),
(i16, false, true),
(i32, false, true),
(i64, false, true),
(i128, false, true),
(isize, false, true),
);
fn serialize_i128(mut value: i128, serialized: &mut Vec<u8>) {
loop {
let byte = value as u8 & 0x7f;
value >>= 7;
if (value == 0 && (byte & 0x40) == 0) || (value == -1 && (byte & 0x40) != 0) {
serialized.push(byte);
break;
}
serialized.push(byte | 0x80);
}
}
fn serialize_u128(mut value: u128, serialized: &mut Vec<u8>) {
loop {
let mut byte = value & 0x7f;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
serialized.push(byte as u8);
if value == 0 {
break;
}
}
}
pub fn serialize<T: ExtensibleInteger>(source: T) -> Vec<u8> {
let mut serialized = vec![];
if T::is_byte() {
serialized.push(source.to_u8());
} else if T::is_signed() {
serialize_i128(source.to_i128(), &mut serialized);
} else {
serialize_u128(source.to_u128(), &mut serialized);
}
serialized
}
macro_rules! get_byte {
($bytevec:tt) => {
unwrap_return!($bytevec.drain(0..1).next())
};
($bytevec:tt, $type:ty) => {
get_byte!($bytevec) as $type
};
}
fn parse_i128(data: &mut Vec<u8>) -> Option<i128> {
let mut result = 0;
let mut shift = 0;
loop {
let byte = get_byte!(data, i128);
result |= (byte & 0x7f) << shift;
shift += 7;
if 0x80 & byte == 0 {
if shift < 128 && (byte & 0x40) != 0 {
result |= !0 << shift;
}
break;
}
}
Some(result)
}
fn parse_u128(data: &mut Vec<u8>) -> Option<u128> {
let mut result = 0;
let mut shift = 0;
loop {
let byte = get_byte!(data, u128);
result |= (byte & 0x7f) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
}
Some(result)
}
pub fn parse<T: ExtensibleInteger>(data: &mut Vec<u8>) -> Option<T> {
if T::is_byte() {
Some(T::from_u8(get_byte!(data)))
} else if T::is_signed() {
parse_i128(data).map(T::from_i128)
} else {
parse_u128(data).map(T::from_u128)
}
}