#![allow(clippy::len_zero)]
#![allow(clippy::from_over_into)]
use super::Error;
use super::Packable;
use super::Unpackable;
use std::convert::TryInto;
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct v64 {
x: u64,
}
impl From<u8> for v64 {
fn from(x: u8) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<u8> for v64 {
type Error = Error;
fn try_into(self) -> Result<u8, Error> {
match self.x.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::UnsignedOverflow { value: self.x }),
}
}
}
impl From<u16> for v64 {
fn from(x: u16) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<u16> for v64 {
type Error = Error;
fn try_into(self) -> Result<u16, Error> {
match self.x.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::UnsignedOverflow { value: self.x }),
}
}
}
impl From<u32> for v64 {
fn from(x: u32) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<u32> for v64 {
type Error = Error;
fn try_into(self) -> Result<u32, Error> {
match self.x.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::UnsignedOverflow { value: self.x }),
}
}
}
impl From<u64> for v64 {
fn from(x: u64) -> v64 {
v64 { x }
}
}
impl Into<u64> for v64 {
fn into(self) -> u64 {
self.x
}
}
impl From<i8> for v64 {
fn from(x: i8) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<i8> for v64 {
type Error = Error;
fn try_into(self) -> Result<i8, Error> {
let value: i64 = self.x as i64;
match value.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::SignedOverflow { value }),
}
}
}
impl From<i16> for v64 {
fn from(x: i16) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<i16> for v64 {
type Error = Error;
fn try_into(self) -> Result<i16, Error> {
let value: i64 = self.x as i64;
match value.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::SignedOverflow { value }),
}
}
}
impl From<i32> for v64 {
fn from(x: i32) -> v64 {
v64 { x: x as u64 }
}
}
impl TryInto<i32> for v64 {
type Error = Error;
fn try_into(self) -> Result<i32, Error> {
let value: i64 = self.x as i64;
match value.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(Error::SignedOverflow { value }),
}
}
}
impl From<i64> for v64 {
fn from(x: i64) -> v64 {
v64 { x: x as u64 }
}
}
impl Into<i64> for v64 {
fn into(self) -> i64 {
self.x as i64
}
}
impl From<usize> for v64 {
fn from(x: usize) -> v64 {
let x: u64 = x.try_into().unwrap();
v64 { x }
}
}
impl Into<usize> for v64 {
fn into(self) -> usize {
let x: usize = self.x.try_into().unwrap();
x
}
}
impl Packable for v64 {
fn pack_sz(&self) -> usize {
let mut x: u64 = self.x;
let mut count: usize = 1;
x >>= 7;
while x > 0 {
x >>= 7;
count += 1;
}
count
}
fn pack(&self, out: &mut [u8]) {
let mut x: u64 = self.x;
out[0] = (x & 0x7f) as u8;
x >>= 7;
let mut idx: usize = 1;
while x > 0 {
out[idx - 1] |= 128;
out[idx] = (x & 0x7f) as u8;
idx += 1;
x >>= 7;
}
}
}
impl v64 {
fn unpack_slow(buf: &[u8]) -> Result<(Self, &[u8]), Error> {
let bytes: usize = if buf.len() < 10 { buf.len() } else { 10 };
let mut ret = 0u64;
let mut idx = 0;
let mut shl = 0;
while idx + 1 < bytes && buf[idx] & 128 != 0 {
ret |= (buf[idx] as u64 & 127) << shl;
idx += 1;
shl += 7;
}
if !buf.is_empty() && buf[idx] & 128 == 0 {
ret |= (buf[idx] as u64 & 127) << shl;
idx += 1;
let ret: v64 = ret.into();
Ok((ret, &buf[idx..]))
} else {
Err(Error::VarintOverflow { bytes })
}
}
fn unpack_size<const SZ: usize>(buf: &[u8]) -> Result<(Self, &[u8]), Error> {
let mut result = (buf[SZ - 1] as u64) << (7 * (SZ - 1));
let mut offset = 0;
for b in buf.iter().take(SZ - 1) {
result += (*b as u64 - 0x80) << offset;
offset += 7;
}
Ok((v64::from(result), &buf[SZ..]))
}
}
impl<'a> Unpackable<'a> for v64 {
type Error = Error;
#[inline(always)]
fn unpack<'b>(buf: &'b [u8]) -> Result<(Self, &'b [u8]), Error>
where
'b: 'a,
{
if buf.len() < 10 {
return Self::unpack_slow(buf);
}
if buf[0] < 128 {
Self::unpack_size::<1>(buf)
} else if buf[1] < 128 {
Self::unpack_size::<2>(buf)
} else if buf[2] < 128 {
Self::unpack_size::<3>(buf)
} else if buf[3] < 128 {
Self::unpack_size::<4>(buf)
} else if buf[4] < 128 {
Self::unpack_size::<5>(buf)
} else if buf[5] < 128 {
Self::unpack_size::<6>(buf)
} else if buf[6] < 128 {
Self::unpack_size::<7>(buf)
} else if buf[7] < 128 {
Self::unpack_size::<8>(buf)
} else if buf[8] < 128 {
Self::unpack_size::<9>(buf)
} else if buf[9] < 128 {
Self::unpack_size::<10>(buf)
} else {
Err(Error::VarintOverflow { bytes: buf.len() })
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn from_into_x<X, E>(x: X)
where
v64: std::convert::From<X> + std::convert::TryInto<X, Error = E>,
X: std::fmt::Debug + PartialEq + Copy,
E: std::fmt::Debug,
{
let v: v64 = v64::from(x);
let x2: X = v.try_into().unwrap();
assert_eq!(x, x2, "value did not survive a .into().into()");
}
#[test]
fn from_into_u8() {
from_into_x(u8::MIN);
from_into_x(u8::MAX);
from_into_x(1u8);
}
#[test]
fn try_into_u8() {
let x: u64 = (u8::MAX as u64) + 1;
let v: v64 = v64::from(x);
let x2: Result<u8, Error> = v.try_into();
assert_eq!(Err(Error::UnsignedOverflow { value: x }), x2);
}
#[test]
fn from_into_u16() {
from_into_x(u16::MIN);
from_into_x(u16::MAX);
from_into_x(1u16);
}
#[test]
fn try_into_u16() {
let x: u64 = (u16::MAX as u64) + 1;
let v: v64 = v64::from(x);
let x2: Result<u16, Error> = v.try_into();
assert_eq!(Err(Error::UnsignedOverflow { value: x }), x2);
}
#[test]
fn from_into_u32() {
from_into_x(u32::MIN);
from_into_x(u32::MAX);
from_into_x(1u32);
}
#[test]
fn try_into_u32() {
let x: u64 = (u32::MAX as u64) + 1;
let v: v64 = v64::from(x);
let x2: Result<u32, Error> = v.try_into();
assert_eq!(Err(Error::UnsignedOverflow { value: x }), x2);
}
#[test]
fn from_into_u64() {
from_into_x(u64::MIN);
from_into_x(u64::MAX);
from_into_x(1u64);
}
#[test]
fn from_into_i8() {
from_into_x(i8::MIN);
from_into_x(i8::MAX);
from_into_x(-1i8);
from_into_x(0i8);
from_into_x(1i8);
}
#[test]
fn try_into_i8() {
let x: i64 = (i8::MAX as i64) + 1;
let v: v64 = v64::from(x);
let x2: Result<i8, Error> = v.try_into();
assert_eq!(Err(Error::SignedOverflow { value: x }), x2);
}
#[test]
fn from_into_i16() {
from_into_x(i16::MIN);
from_into_x(i16::MAX);
from_into_x(-1i16);
from_into_x(0i16);
from_into_x(1i16);
}
#[test]
fn try_into_i16() {
let x: i64 = (i16::MAX as i64) + 1;
let v: v64 = v64::from(x);
let x2: Result<i16, Error> = v.try_into();
assert_eq!(Err(Error::SignedOverflow { value: x }), x2);
}
#[test]
fn from_into_i32() {
from_into_x(i32::MIN);
from_into_x(i32::MAX);
from_into_x(-1i32);
from_into_x(0i32);
from_into_x(1i32);
}
#[test]
fn try_into_i32() {
let x: i64 = (i32::MAX as i64) + 1;
let v: v64 = v64::from(x);
let x2: Result<i32, Error> = v.try_into();
assert_eq!(Err(Error::SignedOverflow { value: x }), x2);
}
#[test]
fn from_into_i64() {
from_into_x(i64::MIN);
from_into_x(i64::MAX);
from_into_x(-1i64);
from_into_x(0i64);
from_into_x(1i64);
}
#[test]
fn from_into_usize() {
from_into_x(usize::MIN);
from_into_x(usize::MAX);
from_into_x(1usize);
}
#[test]
fn assumption_u64_holds_usize() {
let min: u64 = usize::MIN.try_into().unwrap();
let min: usize = min.try_into().unwrap();
assert_eq!(usize::MIN, min, "u64 cannot hold usize::MIN");
let max: u64 = usize::MAX.try_into().unwrap();
let max: usize = max.try_into().unwrap();
assert_eq!(usize::MAX, max, "u64 cannot hold usize::MAX");
}
const TESTS: &[(u64, usize, &[u8])] = &[
(0, 1, &[0]),
(1, 1, &[1]),
((1 << 7) - 1, 1, &[127]),
((1 << 7), 2, &[128, 1]),
((1 << 14) - 1, 2, &[255, 127]),
((1 << 14), 3, &[128, 128, 1]),
((1 << 21) - 1, 3, &[255, 255, 127]),
((1 << 21), 4, &[128, 128, 128, 1]),
((1 << 28) - 1, 4, &[255, 255, 255, 127]),
((1 << 28), 5, &[128, 128, 128, 128, 1]),
((1 << 35) - 1, 5, &[255, 255, 255, 255, 127]),
((1 << 35), 6, &[128, 128, 128, 128, 128, 1]),
((1 << 42) - 1, 6, &[255, 255, 255, 255, 255, 127]),
((1 << 42), 7, &[128, 128, 128, 128, 128, 128, 1]),
((1 << 49) - 1, 7, &[255, 255, 255, 255, 255, 255, 127]),
((1 << 49), 8, &[128, 128, 128, 128, 128, 128, 128, 1]),
((1 << 56) - 1, 8, &[255, 255, 255, 255, 255, 255, 255, 127]),
((1 << 56), 9, &[128, 128, 128, 128, 128, 128, 128, 128, 1]),
(
(1 << 63) - 1,
9,
&[255, 255, 255, 255, 255, 255, 255, 255, 127],
),
(
(1 << 63),
10,
&[128, 128, 128, 128, 128, 128, 128, 128, 128, 1],
),
];
#[test]
fn pack_varint() {
for (idx, &(num, bytes, enc)) in TESTS.iter().enumerate() {
println!("test case={idx} x={num}, |x|={bytes}, s(x)={enc:?}");
let mut buf: [u8; 10] = [0; 10];
assert_eq!(bytes, enc.len(), "human got test case wrong?");
assert!(bytes <= buf.len(), "human made buffer too small?");
let num: v64 = num.into();
let req = num.pack_sz();
assert_eq!(bytes, req, "human got pack_sz wrong?");
num.pack(&mut buf[..bytes]);
assert_eq!(enc, &buf[..bytes], "human got encoder wrong?");
}
}
#[test]
fn unpack_varint() {
for (idx, &(num, bytes, enc)) in TESTS.iter().enumerate() {
println!("test case={idx} x={num}, |x|={bytes}, s(x)={enc:?}");
assert_eq!(bytes, enc.len(), "human got test case wrong?");
assert!(enc.len() <= 10, "human got test harness wrong?");
let mut buf: [u8; 10] = [0xff; 10];
buf[..enc.len()].copy_from_slice(enc);
let (x, rem): (v64, &[u8]) = Unpackable::unpack(&buf).unwrap();
let v: v64 = num.into();
assert_eq!(v, x, "human got decode wrong?");
assert_eq!(rem, &buf[bytes..], "human got remainder wrong?");
}
}
}