use core::num::NonZeroUsize;
use qubit_codec::Codec;
use qubit_codec_binary::{
Leb128Codec,
Leb128DecodeErrorKind,
NonStrict,
Strict,
};
use super::assertions_tests::assert_decoded_eq;
fn nonzero(value: usize) -> NonZeroUsize {
NonZeroUsize::new(value).expect("test count must be non-zero")
}
fn assert_unsigned_u32_leb128_bytes(value: u32, expected: &[u8]) {
let mut output = [0u8; Leb128Codec::<u32, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<u32, NonStrict>::encode_unchecked(value, &mut output, 0)
};
assert_eq!(expected.len(), len);
assert_eq!(expected, &output[..len]);
let decoded =
unsafe { Leb128Codec::<u32, Strict>::decode_unchecked(&output, 0) }
.expect("canonical unsigned boundary value should decode");
assert_decoded_eq((value, len), decoded);
}
fn assert_signed_i32_leb128_bytes(value: i32, expected: &[u8]) {
let mut output = [0u8; Leb128Codec::<i32, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<i32, NonStrict>::encode_unchecked(value, &mut output, 0)
};
assert_eq!(expected.len(), len);
assert_eq!(expected, &output[..len]);
let decoded =
unsafe { Leb128Codec::<i32, Strict>::decode_unchecked(&output, 0) }
.expect("canonical signed boundary value should decode");
assert_decoded_eq((value, len), decoded);
}
#[test]
fn test_leb128_codec_exposes_unit_bounds() {
assert_eq!(1, Leb128Codec::<u8, NonStrict>::MIN_UNITS_PER_VALUE);
assert_eq!(2, Leb128Codec::<u8, NonStrict>::MAX_UNITS_PER_VALUE);
assert_eq!(3, Leb128Codec::<u16, NonStrict>::MAX_UNITS_PER_VALUE);
assert_eq!(5, Leb128Codec::<u32, NonStrict>::MAX_UNITS_PER_VALUE);
assert_eq!(10, Leb128Codec::<u64, NonStrict>::MAX_UNITS_PER_VALUE);
assert_eq!(19, Leb128Codec::<u128, NonStrict>::MAX_UNITS_PER_VALUE);
assert_eq!(
(usize::BITS as usize).div_ceil(7),
Leb128Codec::<usize, NonStrict>::MAX_UNITS_PER_VALUE
);
assert_eq!(1, Leb128Codec::<i8, Strict>::MIN_UNITS_PER_VALUE);
assert_eq!(2, Leb128Codec::<i8, Strict>::MAX_UNITS_PER_VALUE);
assert_eq!(3, Leb128Codec::<i16, Strict>::MAX_UNITS_PER_VALUE);
}
#[test]
fn test_leb128_codec_encodes_unsigned_7_bit_boundaries() {
let cases: &[(u32, &[u8])] = &[
(0, &[0x00]),
(1, &[0x01]),
(0x7f, &[0x7f]),
(0x80, &[0x80, 0x01]),
(0x3fff, &[0xff, 0x7f]),
(0x4000, &[0x80, 0x80, 0x01]),
(u32::MAX, &[0xff, 0xff, 0xff, 0xff, 0x0f]),
];
for &(value, expected) in cases {
assert_unsigned_u32_leb128_bytes(value, expected);
}
}
#[test]
fn test_leb128_codec_encodes_signed_7_bit_boundaries() {
let cases: &[(i32, &[u8])] = &[
(0, &[0x00]),
(-1, &[0x7f]),
(63, &[0x3f]),
(64, &[0xc0, 0x00]),
(-64, &[0x40]),
(-65, &[0xbf, 0x7f]),
(i32::MIN, &[0x80, 0x80, 0x80, 0x80, 0x78]),
(i32::MAX, &[0xff, 0xff, 0xff, 0xff, 0x07]),
];
for &(value, expected) in cases {
assert_signed_i32_leb128_bytes(value, expected);
}
}
#[test]
fn test_leb128_codec_reads_and_writes_unsigned_values_unchecked() {
let mut output =
[0u8; Leb128Codec::<u16, NonStrict>::MAX_UNITS_PER_VALUE + 2];
let len = unsafe {
Leb128Codec::<u16, NonStrict>::encode_unchecked(300, &mut output, 1)
};
assert_eq!(2, len);
assert_eq!([0x00, 0xac, 0x02, 0x00, 0x00], output);
let decoded =
unsafe { Leb128Codec::<u16, NonStrict>::decode_unchecked(&output, 1) }
.expect("valid u16 should decode");
assert_decoded_eq((300, 2), decoded);
let mut output = [0u8; Leb128Codec::<u16, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<u16, NonStrict>::encode_unchecked(
u16::MAX,
&mut output,
0,
)
};
let decoded =
unsafe { Leb128Codec::<u16, NonStrict>::decode_unchecked(&output, 0) }
.expect("u16::MAX should decode");
assert_decoded_eq((u16::MAX, len), decoded);
}
#[test]
fn test_leb128_codec_encodes_and_decodes_through_codec_trait() {
let codec = Leb128Codec::<u16, NonStrict>::default();
let mut output =
[0u8; Leb128Codec::<u16, NonStrict>::MAX_UNITS_PER_VALUE + 2];
assert_eq!(
Leb128Codec::<u16, NonStrict>::MIN_UNITS_PER_VALUE,
codec.min_units_per_value().get()
);
assert_eq!(
Leb128Codec::<u16, NonStrict>::MAX_UNITS_PER_VALUE,
codec.max_units_per_value().get()
);
let written =
unsafe { Codec::encode_unchecked(&codec, &300, &mut output, 1) }
.expect("LEB128 encoding should be infallible");
assert_eq!(2, written);
assert_eq!([0x00, 0xac, 0x02, 0x00, 0x00], output);
let decoded = unsafe { Codec::decode_unchecked(&codec, &output, 1) }
.expect("valid LEB128 value should decode");
assert_decoded_eq((300, 2), decoded);
}
#[test]
fn test_leb128_codec_trait_decodes_single_byte_unsigned_value() {
let codec = Leb128Codec::<u64, NonStrict>::default();
let input = [0x00u8];
let decoded = unsafe { Codec::decode_unchecked(&codec, &input, 0) }
.expect("single-byte unsigned LEB128 value should decode");
assert_decoded_eq((0, 1), decoded);
}
#[test]
fn test_signed_leb128_codec_encodes_and_decodes_through_codec_trait() {
let codec = Leb128Codec::<i16, NonStrict>::default();
let mut output =
[0u8; Leb128Codec::<i16, NonStrict>::MAX_UNITS_PER_VALUE + 2];
assert_eq!(
Leb128Codec::<i16, NonStrict>::MIN_UNITS_PER_VALUE,
codec.min_units_per_value().get()
);
assert_eq!(
Leb128Codec::<i16, NonStrict>::MAX_UNITS_PER_VALUE,
codec.max_units_per_value().get()
);
let written =
unsafe { Codec::encode_unchecked(&codec, &-300, &mut output, 1) }
.expect("signed LEB128 encoding should be infallible");
assert_eq!(2, written);
assert_eq!([0x00, 0xd4, 0x7d, 0x00, 0x00], output);
let decoded = unsafe { Codec::decode_unchecked(&codec, &output, 1) }
.expect("valid signed LEB128 value should decode");
assert_decoded_eq((-300, 2), decoded);
}
#[test]
fn test_leb128_codec_trait_decodes_single_byte_signed_value() {
let codec = Leb128Codec::<i64, NonStrict>::default();
let input = [0x7fu8];
let decoded = unsafe { Codec::decode_unchecked(&codec, &input, 0) }
.expect("single-byte signed LEB128 value should decode");
assert_decoded_eq((-1, 1), decoded);
}
#[test]
fn test_leb128_codec_reads_and_writes_signed_values_unchecked() {
let mut output =
[0u8; Leb128Codec::<i16, NonStrict>::MAX_UNITS_PER_VALUE + 2];
let len = unsafe {
Leb128Codec::<i16, NonStrict>::encode_unchecked(-300, &mut output, 1)
};
assert_eq!(2, len);
assert_eq!([0x00, 0xd4, 0x7d, 0x00, 0x00], output);
let decoded =
unsafe { Leb128Codec::<i16, NonStrict>::decode_unchecked(&output, 1) }
.expect("valid i16 should decode");
assert_decoded_eq((-300, 2), decoded);
let mut output = [0u8; Leb128Codec::<i16, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<i16, NonStrict>::encode_unchecked(300, &mut output, 0)
};
let decoded =
unsafe { Leb128Codec::<i16, NonStrict>::decode_unchecked(&output, 0) }
.expect("positive i16 should decode");
assert_decoded_eq((300, len), decoded);
let mut output = [0u8; Leb128Codec::<i128, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<i128, NonStrict>::encode_unchecked(
i128::MIN,
&mut output,
0,
)
};
let decoded =
unsafe { Leb128Codec::<i128, NonStrict>::decode_unchecked(&output, 0) }
.expect("i128::MIN should decode");
assert_decoded_eq((i128::MIN, len), decoded);
let values: [i16; 8] = [0, -1, 63, 64, -64, -65, i16::MIN, i16::MAX];
let mut output = [0u8; Leb128Codec::<i16, NonStrict>::MAX_UNITS_PER_VALUE];
for value in values {
output.fill(0);
let len = unsafe {
Leb128Codec::<i16, NonStrict>::encode_unchecked(
value,
&mut output,
0,
)
};
let decoded = unsafe {
Leb128Codec::<i16, NonStrict>::decode_unchecked(&output, 0)
}
.expect("signed boundary value should decode");
assert_decoded_eq((value, len), decoded);
}
}
#[test]
fn test_leb128_codec_roundtrips_all_strict_and_non_strict_instantiations() {
macro_rules! roundtrip_unsigned {
($ty:ty, $value:expr) => {{
let value = $value as $ty;
let mut output =
[0u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<$ty, NonStrict>::encode_unchecked(
value,
&mut output,
0,
)
};
let decoded = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(&output, 0)
}
.expect("non-strict unsigned value should decode");
assert_decoded_eq((value, len), decoded);
let mut output =
[0u8; Leb128Codec::<$ty, Strict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<$ty, Strict>::encode_unchecked(
value,
&mut output,
0,
)
};
let decoded = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&output, 0)
}
.expect("strict unsigned value should decode");
assert_decoded_eq((value, len), decoded);
}};
}
macro_rules! roundtrip_signed {
($ty:ty, $value:expr) => {{
let value = $value as $ty;
let mut output =
[0u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<$ty, NonStrict>::encode_unchecked(
value,
&mut output,
0,
)
};
let decoded = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(&output, 0)
}
.expect("non-strict signed value should decode");
assert_decoded_eq((value, len), decoded);
let mut output =
[0u8; Leb128Codec::<$ty, Strict>::MAX_UNITS_PER_VALUE];
let len = unsafe {
Leb128Codec::<$ty, Strict>::encode_unchecked(
value,
&mut output,
0,
)
};
let decoded = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&output, 0)
}
.expect("strict signed value should decode");
assert_decoded_eq((value, len), decoded);
}};
}
roundtrip_unsigned!(u8, u8::MAX);
roundtrip_unsigned!(u16, u16::MAX);
roundtrip_unsigned!(u32, u32::MAX);
roundtrip_unsigned!(u64, u64::MAX);
roundtrip_unsigned!(u128, u128::MAX);
roundtrip_unsigned!(usize, usize::MAX);
roundtrip_signed!(i8, i8::MIN);
roundtrip_signed!(i16, i16::MIN);
roundtrip_signed!(i32, i32::MIN);
roundtrip_signed!(i64, i64::MIN);
roundtrip_signed!(i128, i128::MIN);
roundtrip_signed!(isize, isize::MIN);
}
#[test]
fn test_leb128_codec_reports_incomplete_unsigned_values_unchecked() {
let input = [0x00, 0xac, 0x02, 0xff];
let pending = unsafe {
Leb128Codec::<u16, NonStrict>::decode_unchecked(&input[..2], 1)
}
.expect_err("partial unsigned LEB128 should report incomplete input");
assert_eq!(Leb128DecodeErrorKind::Incomplete, pending.kind());
assert_eq!(1, pending.start_index());
assert_eq!(2, pending.error_index());
assert_eq!(Some(nonzero(2)), pending.required());
assert_eq!(Some(1), pending.available());
assert_eq!(Some(nonzero(1)), pending.additional());
let decoded =
unsafe { Leb128Codec::<u16, NonStrict>::decode_unchecked(&input, 1) }
.expect("complete unsigned LEB128 should decode");
assert_decoded_eq((300, 2), decoded);
let error = unsafe {
Leb128Codec::<u16, Strict>::decode_unchecked(&[0x80, 0x00], 0)
}
.expect_err("non-canonical unsigned value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(1, error.error_index());
assert_eq!(Some(nonzero(2)), error.consumed());
}
#[test]
fn test_leb128_codec_reports_incomplete_signed_values_unchecked() {
let input = [0x00, 0xd4, 0x7d, 0xff];
let pending = unsafe {
Leb128Codec::<i16, NonStrict>::decode_unchecked(&input[..2], 1)
}
.expect_err("partial signed LEB128 should report incomplete input");
assert_eq!(Leb128DecodeErrorKind::Incomplete, pending.kind());
assert_eq!(1, pending.start_index());
assert_eq!(2, pending.error_index());
assert_eq!(Some(nonzero(2)), pending.required());
assert_eq!(Some(1), pending.available());
assert_eq!(Some(nonzero(1)), pending.additional());
let decoded =
unsafe { Leb128Codec::<i16, NonStrict>::decode_unchecked(&input, 1) }
.expect("complete signed LEB128 should decode");
assert_decoded_eq((-300, 2), decoded);
let error = unsafe {
Leb128Codec::<i16, Strict>::decode_unchecked(&[0xff, 0x7f], 0)
}
.expect_err("non-canonical signed value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(1, error.error_index());
assert_eq!(Some(nonzero(2)), error.consumed());
}
#[test]
fn test_leb128_codec_rejects_all_instantiated_error_paths() {
macro_rules! reject_unsigned {
($ty:ty) => {{
let unterminated =
[0x80u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
let error = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(
&unterminated,
0,
)
}
.expect_err("unterminated unsigned value should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let error = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&unterminated, 0)
}
.expect_err("unterminated strict unsigned value should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let max_bytes = Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE;
let bits = <$ty>::BITS as usize;
let used_bits = bits - (max_bytes - 1) * 7;
let mut malformed =
[0x80u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
malformed[max_bytes - 1] = 1u8 << used_bits;
let error = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(&malformed, 0)
}
.expect_err("too-wide unsigned payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let mut noncanonical =
[0u8; Leb128Codec::<$ty, Strict>::MAX_UNITS_PER_VALUE];
noncanonical[0] = 0x80;
let error = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&noncanonical, 0)
}
.expect_err("non-canonical unsigned value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
}};
}
macro_rules! reject_signed {
($ty:ty) => {{
let unterminated =
[0x80u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
let error = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(
&unterminated,
0,
)
}
.expect_err("unterminated signed value should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let error = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&unterminated, 0)
}
.expect_err("unterminated strict signed value should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let max_bytes = Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE;
let bits = <$ty>::BITS as usize;
let used_bits = bits - (max_bytes - 1) * 7;
let mut malformed =
[0x80u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
malformed[max_bytes - 1] = 1u8 << used_bits;
let error = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(&malformed, 0)
}
.expect_err("too-wide positive signed payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let mut malformed =
[0x80u8; Leb128Codec::<$ty, NonStrict>::MAX_UNITS_PER_VALUE];
malformed[max_bytes - 1] = 1u8 << (used_bits - 1);
let error = unsafe {
Leb128Codec::<$ty, NonStrict>::decode_unchecked(&malformed, 0)
}
.expect_err("too-narrow negative signed payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
let mut noncanonical =
[0u8; Leb128Codec::<$ty, Strict>::MAX_UNITS_PER_VALUE];
noncanonical[0] = 0xff;
noncanonical[1] = 0x7f;
let error = unsafe {
Leb128Codec::<$ty, Strict>::decode_unchecked(&noncanonical, 0)
}
.expect_err("non-canonical signed value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
}};
}
reject_unsigned!(u8);
reject_unsigned!(u16);
reject_unsigned!(u32);
reject_unsigned!(u64);
reject_unsigned!(u128);
reject_unsigned!(usize);
reject_signed!(i8);
reject_signed!(i16);
reject_signed!(i32);
reject_signed!(i64);
reject_signed!(i128);
reject_signed!(isize);
}
#[test]
fn test_leb128_codec_rejects_malformed_values() {
let error = unsafe {
Leb128Codec::<u16, NonStrict>::decode_unchecked(&[0x80, 0x80, 0x04], 0)
}
.expect_err("too-wide unsigned payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(2, error.error_index());
let error = unsafe {
Leb128Codec::<u16, NonStrict>::decode_unchecked(&[0x80, 0x80, 0x80], 0)
}
.expect_err("unterminated unsigned payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(2, error.error_index());
let error = unsafe {
Leb128Codec::<i16, NonStrict>::decode_unchecked(&[0x80, 0x80, 0x04], 0)
}
.expect_err("too-wide signed payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(2, error.error_index());
let error = unsafe {
Leb128Codec::<i16, NonStrict>::decode_unchecked(&[0x80, 0x80, 0x02], 0)
}
.expect_err("too-narrow negative signed payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(2, error.error_index());
let error = unsafe {
Leb128Codec::<i16, NonStrict>::decode_unchecked(&[0x80, 0x80, 0x80], 0)
}
.expect_err("unterminated signed payload should fail");
assert_eq!(Leb128DecodeErrorKind::Malformed, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(2, error.error_index());
}
#[test]
fn test_leb128_codec_rejects_noncanonical_strict_values() {
let decoded = unsafe {
Leb128Codec::<u16, Strict>::decode_unchecked(&[0xac, 0x02, 0x00], 0)
}
.expect("canonical unsigned value should decode");
assert_decoded_eq((300, 2), decoded);
let decoded = unsafe {
Leb128Codec::<i16, Strict>::decode_unchecked(&[0xd4, 0x7d, 0x00], 0)
}
.expect("canonical signed value should decode");
assert_decoded_eq((-300, 2), decoded);
let decoded = unsafe {
Leb128Codec::<i16, Strict>::decode_unchecked(&[0xac, 0x02, 0x00], 0)
}
.expect("canonical positive signed value should decode");
assert_decoded_eq((300, 2), decoded);
let error = unsafe {
Leb128Codec::<u16, Strict>::decode_unchecked(&[0x80, 0x00, 0x00], 0)
}
.expect_err("non-canonical unsigned value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(1, error.error_index());
let error = unsafe {
Leb128Codec::<i16, Strict>::decode_unchecked(&[0xff, 0x7f, 0x00], 0)
}
.expect_err("non-canonical signed value should fail");
assert_eq!(Leb128DecodeErrorKind::NonCanonical, error.kind());
assert_eq!(0, error.start_index());
assert_eq!(1, error.error_index());
}