pub use crate::u64::{DecodeError, MAX_BYTES};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct EncodedI64(crate::u64::EncodedU64);
impl EncodedI64 {
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
}
impl core::ops::Deref for EncodedI64 {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl AsRef<[u8]> for EncodedI64 {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl core::borrow::Borrow<[u8]> for EncodedI64 {
#[inline]
fn borrow(&self) -> &[u8] {
self.0.as_slice()
}
}
impl IntoIterator for EncodedI64 {
type Item = u8;
type IntoIter = <crate::u64::EncodedU64 as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a EncodedI64 {
type Item = &'a u8;
type IntoIter = <&'a crate::u64::EncodedU64 as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter()
}
}
use alloc::vec::Vec;
#[inline]
#[must_use]
#[allow(clippy::cast_sign_loss)] pub const fn zigzag(n: i64) -> u64 {
((n << 1) ^ (n >> 63)) as u64
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_wrap)] pub const fn unzigzag(u: u64) -> i64 {
((u >> 1) as i64) ^ -((u & 1) as i64)
}
#[inline]
#[must_use]
pub const fn encoded_len(value: i64) -> usize {
crate::u64::encoded_len(zigzag(value))
}
#[inline]
pub fn encode(value: i64, buf: &mut Vec<u8>) {
crate::u64::encode(zigzag(value), buf);
}
#[inline]
#[must_use]
pub const fn encoded_bytes(value: i64) -> EncodedI64 {
EncodedI64(crate::u64::encoded_bytes(zigzag(value)))
}
#[inline]
pub const fn decode(buf: &[u8]) -> Result<(i64, usize), DecodeError> {
match crate::u64::decode(buf) {
Ok((value, consumed)) => Ok((unzigzag(value), consumed)),
Err(err) => Err(err),
}
}
#[must_use]
pub const fn decode_iter(buf: &[u8]) -> DecodeIter<'_> {
DecodeIter {
inner: crate::u64::decode_iter(buf),
}
}
#[derive(Debug)]
pub struct DecodeIter<'a> {
inner: crate::u64::DecodeIter<'a>,
}
impl Iterator for DecodeIter<'_> {
type Item = Result<i64, DecodeError>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some(Ok(value)) => Some(Ok(unzigzag(value))),
Some(Err(err)) => Some(Err(err)),
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl core::iter::FusedIterator for DecodeIter<'_> {}
pub fn decode_all(buf: &[u8]) -> Result<Vec<i64>, DecodeError> {
decode_iter(buf).collect()
}
#[cfg(test)]
mod tests {
use super::*;
type TestResult = core::result::Result<(), DecodeError>;
const VECTORS: &[(i64, &[u8])] = &[
(0, &[0x00]),
(-1, &[0x01]),
(1, &[0x02]),
(-2, &[0x03]),
(2, &[0x04]),
(-124, &[0xF7]), (123, &[0xF6]), (124, &[0xF8, 0x00]), (-125, &[0xF8, 0x01]), (251, &[0xF8, 0xFE]), (-252, &[0xF8, 0xFF]), (252, &[0xF9, 0x00, 0x00]), (
i64::MAX,
&[0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x06],
), (
i64::MIN,
&[0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x07],
), ];
#[test]
fn spec_vectors_encode() {
for &(value, expected) in VECTORS {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf, expected, "encode({value})");
assert_eq!(encoded_bytes(value).as_slice(), expected);
assert_eq!(encoded_len(value), expected.len());
}
}
#[test]
fn spec_vectors_decode() -> TestResult {
for &(expected, bytes) in VECTORS {
let (value, consumed) = decode(bytes)?;
assert_eq!(value, expected, "decode({bytes:02X?})");
assert_eq!(consumed, bytes.len());
}
Ok(())
}
#[test]
fn zigzag_bijection_at_extremes() {
for value in [0i64, -1, 1, i64::MIN, i64::MAX, i64::MIN + 1, i64::MAX - 1] {
assert_eq!(unzigzag(zigzag(value)), value);
}
assert_eq!(zigzag(i64::MIN), u64::MAX);
assert_eq!(zigzag(i64::MAX), u64::MAX - 1);
}
#[test]
fn tier_boundaries_round_trip() -> TestResult {
let unsigned_boundaries: &[u64] = &[
0,
247,
248,
503,
504,
66_039,
66_040,
16_843_255,
16_843_256,
4_311_810_551,
4_311_810_552,
1_103_823_438_327,
1_103_823_438_328,
282_578_800_148_983,
282_578_800_148_984,
72_340_172_838_076_919,
72_340_172_838_076_920,
u64::MAX,
];
for &u in unsigned_boundaries {
let value = unzigzag(u);
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), crate::u64::encoded_len(u));
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value);
assert_eq!(consumed, buf.len());
}
Ok(())
}
#[test]
fn tier_edges_both_signs() -> TestResult {
const THRESHOLD: u64 = 248;
const TIERS: u32 = 8;
let mut offset: u64 = THRESHOLD;
for n in 1..=TIERS {
let below = n as usize; let at = n as usize + 1; for (z, expected_len) in [
(offset - 2, below),
(offset - 1, below),
(offset, at),
(offset + 1, at),
] {
let value = unzigzag(z);
assert_eq!(encoded_len(value), expected_len, "z={z} value={value}");
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), expected_len);
assert_eq!(decode(&buf)?, (value, expected_len));
}
if n < TIERS {
offset += 1u64 << (8 * n);
}
}
assert_eq!(encoded_len(unzigzag(u64::MAX)), MAX_BYTES);
assert_eq!(encoded_len(unzigzag(u64::MAX - 1)), MAX_BYTES);
Ok(())
}
#[test]
fn errors_propagate() {
assert_eq!(decode(&[]), Err(DecodeError::BufferTooShort));
assert_eq!(decode(&[0xF8]), Err(DecodeError::BufferTooShort));
assert_eq!(
decode(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
Err(DecodeError::Overflow)
);
}
#[test]
fn decode_iter_fuses_and_decode_all_short_circuits() {
let mut buf = Vec::new();
encode(-42, &mut buf);
buf.push(0xF8);
let mut iter = decode_iter(&buf);
assert_eq!(iter.next(), Some(Ok(-42)));
assert_eq!(iter.next(), Some(Err(DecodeError::BufferTooShort)));
assert_eq!(iter.next(), None);
assert_eq!(decode_all(&buf), Err(DecodeError::BufferTooShort));
}
#[test]
fn byte_order_is_zigzag_order() {
let order = [0i64, -1, 1, -2, 2, -124, 124];
let encoded: Vec<EncodedI64> = order.iter().map(|&v| encoded_bytes(v)).collect();
for (a, b) in encoded.iter().zip(encoded.iter().skip(1)) {
assert!(a < b, "zigzag order violated");
}
}
#[cfg(feature = "bolero")]
#[allow(clippy::indexing_slicing, clippy::unwrap_used, clippy::panic)]
mod property {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn round_trip() {
bolero::check!().with_arbitrary::<i64>().for_each(|&value| {
let mut buf = Vec::new();
encode(value, &mut buf);
let (decoded, consumed) = decode(&buf).unwrap_or_else(|e| {
panic!("round-trip decode failed for {value}: {e}");
});
assert_eq!(decoded, value);
assert_eq!(consumed, buf.len());
assert_eq!(encoded_len(value), buf.len());
assert_eq!(encoded_bytes(value).as_slice(), &buf[..]);
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn canonical_all_decodable_buffers_reencode_to_themselves() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|bytes| {
if let Ok((value, consumed)) = decode(bytes) {
let mut re = Vec::new();
encode(value, &mut re);
assert_eq!(&bytes[..consumed], &re[..], "non-canonical decode");
}
});
}
}
}