190 lines
4.6 KiB
Rust
190 lines
4.6 KiB
Rust
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
// Copyright by contributors to this project.
|
|
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
|
|
|
use crate::{Error, MlsDecode, MlsEncode, MlsSize};
|
|
use alloc::vec::Vec;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub struct VarInt(pub u32);
|
|
|
|
impl VarInt {
|
|
pub const MAX: VarInt = VarInt((1 << 30) - 1);
|
|
}
|
|
|
|
impl From<VarInt> for u32 {
|
|
#[inline]
|
|
fn from(n: VarInt) -> u32 {
|
|
n.0
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u32> for VarInt {
|
|
type Error = Error;
|
|
|
|
fn try_from(n: u32) -> Result<Self, Error> {
|
|
(n <= u32::from(VarInt::MAX))
|
|
.then_some(VarInt(n))
|
|
.ok_or(Error::VarIntOutOfRange)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<usize> for VarInt {
|
|
type Error = Error;
|
|
|
|
fn try_from(n: usize) -> Result<Self, Error> {
|
|
u32::try_from(n)
|
|
.map_err(|_| Error::VarIntOutOfRange)?
|
|
.try_into()
|
|
}
|
|
}
|
|
|
|
impl MlsSize for VarInt {
|
|
#[inline]
|
|
fn mls_encoded_len(&self) -> usize {
|
|
count_bytes_to_encode_int(*self) as usize
|
|
}
|
|
}
|
|
|
|
impl MlsEncode for VarInt {
|
|
fn mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
|
|
let mut bytes = self.0.to_be_bytes();
|
|
|
|
let bytes = match count_bytes_to_encode_int(*self) {
|
|
LengthEncoding::One => &bytes[3..],
|
|
LengthEncoding::Two => {
|
|
bytes[2] |= 0x40;
|
|
&bytes[2..]
|
|
}
|
|
LengthEncoding::Four => {
|
|
bytes[0] |= 0x80;
|
|
&bytes
|
|
}
|
|
};
|
|
|
|
writer.extend_from_slice(bytes);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl MlsDecode for VarInt {
|
|
fn mls_decode(reader: &mut &[u8]) -> Result<Self, Error> {
|
|
let first = u8::mls_decode(reader)?;
|
|
|
|
let prefix = first >> 6;
|
|
|
|
let count = (prefix < 3)
|
|
.then_some(1 << prefix)
|
|
.ok_or(Error::InvalidVarIntPrefix(prefix))?;
|
|
|
|
let n = (1..count).try_fold(u32::from(first & 0x3f), |n, _| {
|
|
u8::mls_decode(reader).map(|b| n << 8 | u32::from(b))
|
|
})?;
|
|
|
|
let n = VarInt(n);
|
|
|
|
if count_bytes_to_encode_int(n) as usize == count {
|
|
Ok(n)
|
|
} else {
|
|
Err(Error::VarIntMinimumLengthEncoding)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Number of bytes to encode a variable-size integer.
|
|
#[derive(Debug)]
|
|
enum LengthEncoding {
|
|
One = 1,
|
|
Two = 2,
|
|
Four = 4,
|
|
}
|
|
|
|
fn count_bytes_to_encode_int(n: VarInt) -> LengthEncoding {
|
|
let used_bits = 32 - n.0.leading_zeros();
|
|
match used_bits {
|
|
0..=6 => LengthEncoding::One,
|
|
7..=14 => LengthEncoding::Two,
|
|
15..=30 => LengthEncoding::Four,
|
|
_ => panic!("Such a large VarInt cannot be instantiated"),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::VarInt;
|
|
use crate::{Error, MlsDecode, MlsEncode};
|
|
use assert_matches::assert_matches;
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
use wasm_bindgen_test::wasm_bindgen_test as test;
|
|
|
|
#[test]
|
|
fn zero_is_convertible_to_varint() {
|
|
assert_matches!(VarInt::try_from(0u32).map(u32::from), Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn successor_of_max_varint_is_not_convertible_to_varint() {
|
|
let n = u32::from(VarInt::MAX) + 1;
|
|
assert_matches!(VarInt::try_from(n), Err(Error::VarIntOutOfRange));
|
|
}
|
|
|
|
#[test]
|
|
fn zero_serializes_as_single_null_byte() {
|
|
assert_eq!(
|
|
VarInt::try_from(0u32).unwrap().mls_encode_to_vec().unwrap(),
|
|
[0]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn zero_roundtrips() {
|
|
let n = VarInt::try_from(0u32).unwrap();
|
|
|
|
let serialized = n.mls_encode_to_vec().unwrap();
|
|
let restored = VarInt::mls_decode(&mut &*serialized).unwrap();
|
|
|
|
assert_eq!(restored, n);
|
|
}
|
|
|
|
#[test]
|
|
fn varint_max_roundtrips() {
|
|
let n = VarInt::MAX;
|
|
|
|
let serialized = n.mls_encode_to_vec().unwrap();
|
|
let restored = VarInt::mls_decode(&mut &*serialized).unwrap();
|
|
|
|
assert_eq!(restored, n);
|
|
}
|
|
|
|
fn decoding_matches_rfc(encoded: u32, decoded: u32) {
|
|
let bytes = encoded.to_be_bytes();
|
|
|
|
let start = bytes
|
|
.iter()
|
|
.position(|&b| b != 0)
|
|
.unwrap_or(bytes.len() - 1);
|
|
|
|
let bytes = &bytes[start..];
|
|
|
|
assert_eq!(
|
|
VarInt::mls_decode(&mut &*bytes).unwrap(),
|
|
VarInt::try_from(decoded).unwrap()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn decoding_0x25_matches_rfc_result() {
|
|
decoding_matches_rfc(0x25, 37);
|
|
}
|
|
|
|
#[test]
|
|
fn decoding_0x7bbd_matches_rfc_result() {
|
|
decoding_matches_rfc(0x7bbd, 15293);
|
|
}
|
|
|
|
#[test]
|
|
fn decoding_0x9d7f3e7d_matches_rfc_result() {
|
|
decoding_matches_rfc(0x9d7f3e7d, 494878333);
|
|
}
|
|
}
|