pub use crate::u128::{DecodeError, MAX_BYTES};
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct EncodedI128(crate::u128::EncodedU128);
impl EncodedI128 {
#[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 EncodedI128 {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl AsRef<[u8]> for EncodedI128 {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl core::borrow::Borrow<[u8]> for EncodedI128 {
#[inline]
fn borrow(&self) -> &[u8] {
self.0.as_slice()
}
}
impl IntoIterator for EncodedI128 {
type Item = u8;
type IntoIter = <crate::u128::EncodedU128 as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a EncodedI128 {
type Item = &'a u8;
type IntoIter = <&'a crate::u128::EncodedU128 as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter()
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_sign_loss)] pub const fn zigzag(n: i128) -> u128 {
((n << 1) ^ (n >> 127)) as u128
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_wrap)] pub const fn unzigzag(u: u128) -> i128 {
((u >> 1) as i128) ^ -((u & 1) as i128)
}
#[inline]
#[must_use]
pub const fn encoded_len(value: i128) -> usize {
crate::u128::encoded_len(zigzag(value))
}
#[inline]
pub fn encode(value: i128, buf: &mut Vec<u8>) {
crate::u128::encode(zigzag(value), buf);
}
#[inline]
#[must_use]
pub const fn encoded_bytes(value: i128) -> EncodedI128 {
EncodedI128(crate::u128::encoded_bytes(zigzag(value)))
}
#[inline]
pub const fn decode(buf: &[u8]) -> Result<(i128, usize), DecodeError> {
match crate::u128::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::u128::decode_iter(buf),
}
}
#[derive(Debug)]
pub struct DecodeIter<'a> {
inner: crate::u128::DecodeIter<'a>,
}
impl Iterator for DecodeIter<'_> {
type Item = Result<i128, 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<i128>, DecodeError> {
decode_iter(buf).collect()
}
#[cfg(test)]
mod tests {
use super::*;
type TestResult = core::result::Result<(), DecodeError>;
const VECTORS: &[(i128, &[u8])] = &[
(0, &[0x00]),
(-1, &[0x01]),
(1, &[0x02]),
(119, &[0xEE]), (-120, &[0xEF]), (120, &[0xF0, 0x00]), (-121, &[0xF0, 0x01]), (247, &[0xF0, 0xFE]),
(-248, &[0xF0, 0xFF]),
(248, &[0xF1, 0x00, 0x00]),
(
i128::MAX,
&[
0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0x0E,
],
),
(
i128::MIN,
&[
0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0x0F,
],
),
];
#[test]
fn spec_vectors_round_trip() -> TestResult {
for &(value, expected) in VECTORS {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf, expected, "encode({value})");
assert_eq!(encoded_len(value), expected.len());
assert_eq!(decode(expected)?, (value, expected.len()));
}
Ok(())
}
#[test]
fn definitional_equivalence_and_extremes() -> TestResult {
for value in [
0i128,
-1,
1,
-120,
119,
-121,
120,
i128::from(i64::MIN),
i128::from(i64::MAX),
i128::MIN,
i128::MAX,
i128::MIN + 1,
i128::MAX - 1,
] {
let z = zigzag(value);
assert_eq!(unzigzag(z), value);
assert_eq!(
encoded_bytes(value).as_slice(),
crate::u128::encoded_bytes(z).as_slice()
);
let (decoded, consumed) = decode(encoded_bytes(value).as_slice())?;
assert_eq!(decoded, value);
assert_eq!(consumed, encoded_len(value));
}
assert_eq!(zigzag(i128::MIN), u128::MAX);
assert_eq!(encoded_len(i128::MIN), MAX_BYTES);
assert_eq!(encoded_len(i128::MAX), MAX_BYTES);
Ok(())
}
#[test]
fn tier_edges_both_signs() -> TestResult {
const THRESHOLD: u128 = 240;
const TIERS: u32 = 16;
let mut offset: u128 = 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 += 1u128 << (8 * n);
}
}
assert_eq!(encoded_len(unzigzag(u128::MAX)), MAX_BYTES);
assert_eq!(encoded_len(unzigzag(u128::MAX - 1)), MAX_BYTES);
Ok(())
}
#[test]
fn errors_propagate() {
assert_eq!(decode(&[]), Err(DecodeError::BufferTooShort));
assert_eq!(decode(&[0xF0]), Err(DecodeError::BufferTooShort));
assert_eq!(decode(&[0xFF; 17]), Err(DecodeError::Overflow));
}
#[test]
fn decode_iter_and_all() -> TestResult {
let mut buf = Vec::new();
for v in [-2i128, -1, 0, 1, 2, i128::MIN, i128::MAX] {
encode(v, &mut buf);
}
assert_eq!(decode_all(&buf)?, [-2, -1, 0, 1, 2, i128::MIN, i128::MAX]);
Ok(())
}
#[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::<i128>()
.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");
}
});
}
}
}