pub use crate::u32::{DecodeError, MAX_BYTES};
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct EncodedI32(crate::u32::EncodedU32);
impl EncodedI32 {
#[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 EncodedI32 {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl AsRef<[u8]> for EncodedI32 {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl core::borrow::Borrow<[u8]> for EncodedI32 {
#[inline]
fn borrow(&self) -> &[u8] {
self.0.as_slice()
}
}
impl IntoIterator for EncodedI32 {
type Item = u8;
type IntoIter = <crate::u32::EncodedU32 as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a EncodedI32 {
type Item = &'a u8;
type IntoIter = <&'a crate::u32::EncodedU32 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: i32) -> u32 {
((n << 1) ^ (n >> 31)) as u32
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_wrap)] pub const fn unzigzag(u: u32) -> i32 {
((u >> 1) as i32) ^ -((u & 1) as i32)
}
#[inline]
#[must_use]
pub const fn encoded_len(value: i32) -> usize {
crate::u32::encoded_len(zigzag(value))
}
#[inline]
pub fn encode(value: i32, buf: &mut Vec<u8>) {
crate::u32::encode(zigzag(value), buf);
}
#[inline]
#[must_use]
pub const fn encoded_bytes(value: i32) -> EncodedI32 {
EncodedI32(crate::u32::encoded_bytes(zigzag(value)))
}
#[inline]
pub const fn decode(buf: &[u8]) -> Result<(i32, usize), DecodeError> {
match crate::u32::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::u32::decode_iter(buf),
}
}
#[derive(Debug)]
pub struct DecodeIter<'a> {
inner: crate::u32::DecodeIter<'a>,
}
impl Iterator for DecodeIter<'_> {
type Item = Result<i32, 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<i32>, DecodeError> {
decode_iter(buf).collect()
}
#[cfg(test)]
mod tests {
use super::*;
type TestResult = core::result::Result<(), DecodeError>;
const VECTORS: &[(i32, &[u8])] = &[
(0, &[0x00]),
(-1, &[0x01]),
(1, &[0x02]),
(125, &[0xFA]), (-126, &[0xFB]), (126, &[0xFC, 0x00]), (-127, &[0xFC, 0x01]), (253, &[0xFC, 0xFE]),
(-254, &[0xFC, 0xFF]),
(254, &[0xFD, 0x00, 0x00]),
(i32::MAX, &[0xFF, 0xFE, 0xFE, 0xFE, 0x02]),
(i32::MIN, &[0xFF, 0xFE, 0xFE, 0xFE, 0x03]),
];
#[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 [
0i32,
-1,
1,
-126,
125,
-127,
126,
-33_022,
33_021,
i32::MIN,
i32::MAX,
i32::MIN + 1,
i32::MAX - 1,
] {
let z = zigzag(value);
assert_eq!(unzigzag(z), value);
assert_eq!(
encoded_bytes(value).as_slice(),
crate::u32::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(i32::MIN), u32::MAX);
assert_eq!(encoded_len(i32::MIN), MAX_BYTES);
assert_eq!(encoded_len(i32::MAX), MAX_BYTES);
Ok(())
}
#[test]
fn tier_edges_both_signs() -> TestResult {
const THRESHOLD: u32 = 252;
const TIERS: u32 = 4;
let mut offset: u32 = 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 += 1u32 << (8 * n);
}
}
assert_eq!(encoded_len(unzigzag(u32::MAX)), MAX_BYTES);
assert_eq!(encoded_len(unzigzag(u32::MAX - 1)), MAX_BYTES);
Ok(())
}
#[test]
fn errors_propagate() {
assert_eq!(decode(&[]), Err(DecodeError::BufferTooShort));
assert_eq!(decode(&[0xFC]), Err(DecodeError::BufferTooShort));
assert_eq!(
decode(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
Err(DecodeError::Overflow)
);
}
#[test]
fn decode_iter_and_all() -> TestResult {
let mut buf = Vec::new();
for v in [-2i32, -1, 0, 1, 2, i32::MIN, i32::MAX] {
encode(v, &mut buf);
}
assert_eq!(decode_all(&buf)?, [-2, -1, 0, 1, 2, i32::MIN, i32::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::<i32>().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");
}
});
}
}
}