use crate::error::{MqttError, Result};
use crate::prelude::{format, ToString, Vec};
use bebytes::BeBytes;
use bytes::{Buf, BufMut};
use core::fmt;
pub const VARIABLE_INT_MAX: u32 = 268_435_455;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VariableInt {
value: u32,
}
impl VariableInt {
pub fn new(value: u32) -> Result<Self> {
if value > VARIABLE_INT_MAX {
return Err(MqttError::ProtocolError(format!(
"Variable integer value {value} exceeds maximum {VARIABLE_INT_MAX}"
)));
}
Ok(Self { value })
}
#[must_use]
pub fn new_unchecked(value: u32) -> Self {
debug_assert!(value <= VARIABLE_INT_MAX);
Self { value }
}
#[must_use]
pub fn value(&self) -> u32 {
self.value
}
#[must_use]
pub fn encoded_size(&self) -> u32 {
match self.value {
0..=127 => 1,
128..=16_383 => 2,
16_384..=2_097_151 => 3,
2_097_152..=VARIABLE_INT_MAX => 4,
_ => unreachable!("Invalid variable int value"),
}
}
pub fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
let mut val = self.value;
loop {
let mut byte = (val % 128) as u8;
val /= 128;
if val > 0 {
byte |= 0x80; }
buf.put_u8(byte);
if val == 0 {
break;
}
}
Ok(())
}
pub fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
let mut value = 0u32;
let mut multiplier = 1u32;
let mut byte_count = 0;
loop {
if !buf.has_remaining() {
return Err(MqttError::MalformedPacket(
"Insufficient bytes for variable integer".to_string(),
));
}
byte_count += 1;
if byte_count > 4 {
return Err(MqttError::MalformedPacket(
"Variable integer exceeds 4 bytes".to_string(),
));
}
let byte = buf.get_u8();
value += u32::from(byte & 0x7F) * multiplier;
if (byte & 0x80) == 0 {
break;
}
multiplier *= 128;
if multiplier > 128 * 128 * 128 {
return Err(MqttError::MalformedPacket(
"Variable integer overflow".to_string(),
));
}
}
if value > VARIABLE_INT_MAX {
return Err(MqttError::MalformedPacket(format!(
"Variable integer value {value} exceeds maximum"
)));
}
Ok(Self { value })
}
}
impl fmt::Display for VariableInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl From<VariableInt> for u32 {
fn from(v: VariableInt) -> Self {
v.value
}
}
impl TryFrom<u32> for VariableInt {
type Error = MqttError;
fn try_from(value: u32) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<usize> for VariableInt {
type Error = MqttError;
fn try_from(value: usize) -> Result<Self> {
let value = u32::try_from(value).map_err(|_| {
MqttError::ProtocolError("Value too large for variable integer".to_string())
})?;
Self::new(value)
}
}
impl BeBytes for VariableInt {
fn field_size() -> usize {
0
}
fn to_be_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
let _ = self.encode(&mut buf); buf
}
fn try_from_be_bytes(
bytes: &[u8],
) -> core::result::Result<(Self, usize), bebytes::BeBytesError> {
use bytes::Bytes;
let mut buf = Bytes::copy_from_slice(bytes);
let start_len = buf.len();
match Self::decode(&mut buf) {
Ok(var_int) => {
let consumed = start_len - buf.len();
Ok((var_int, consumed))
}
Err(_) => Err(bebytes::BeBytesError::InsufficientData {
expected: 1, actual: bytes.len(),
}),
}
}
fn to_le_bytes(&self) -> Vec<u8> {
self.to_be_bytes()
}
fn try_from_le_bytes(
bytes: &[u8],
) -> core::result::Result<(Self, usize), bebytes::BeBytesError> {
Self::try_from_be_bytes(bytes)
}
}
pub fn encode_variable_int<B: BufMut>(buf: &mut B, value: u32) -> Result<()> {
VariableInt::new(value)?.encode(buf)
}
pub fn decode_variable_int<B: Buf>(buf: &mut B) -> Result<u32> {
Ok(VariableInt::decode(buf)?.value())
}
#[must_use]
pub fn variable_int_len(value: u32) -> usize {
VariableInt::new_unchecked(value.min(VARIABLE_INT_MAX)).encoded_size() as usize
}
#[must_use]
pub fn encoded_variable_int_len(value: u32) -> usize {
variable_int_len(value)
}
pub const VARIABLE_BYTE_INT_MAX: u32 = VARIABLE_INT_MAX;
#[cfg(test)]
mod tests {
use super::*;
use bytes::BytesMut;
#[test]
fn test_new_valid_values() {
assert!(VariableInt::new(0).is_ok());
assert!(VariableInt::new(127).is_ok());
assert!(VariableInt::new(128).is_ok());
assert!(VariableInt::new(16_383).is_ok());
assert!(VariableInt::new(16_384).is_ok());
assert!(VariableInt::new(2_097_151).is_ok());
assert!(VariableInt::new(2_097_152).is_ok());
assert!(VariableInt::new(VARIABLE_INT_MAX).is_ok());
}
#[test]
fn test_new_invalid_values() {
assert!(VariableInt::new(VARIABLE_INT_MAX + 1).is_err());
assert!(VariableInt::new(u32::MAX).is_err());
}
#[test]
fn test_encoded_size() {
assert_eq!(VariableInt::new_unchecked(0).encoded_size(), 1);
assert_eq!(VariableInt::new_unchecked(127).encoded_size(), 1);
assert_eq!(VariableInt::new_unchecked(128).encoded_size(), 2);
assert_eq!(VariableInt::new_unchecked(16_383).encoded_size(), 2);
assert_eq!(VariableInt::new_unchecked(16_384).encoded_size(), 3);
assert_eq!(VariableInt::new_unchecked(2_097_151).encoded_size(), 3);
assert_eq!(VariableInt::new_unchecked(2_097_152).encoded_size(), 4);
assert_eq!(
VariableInt::new_unchecked(VARIABLE_INT_MAX).encoded_size(),
4
);
}
#[test]
fn test_encode_decode_single_byte() {
let mut buf = BytesMut::new();
for value in [0, 1, 64, 127] {
buf.clear();
let var_int = VariableInt::new(value).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf.len(), 1);
let decoded = VariableInt::decode(&mut buf).unwrap();
assert_eq!(decoded.value(), value);
}
}
#[test]
fn test_encode_decode_two_bytes() {
let mut buf = BytesMut::new();
for value in [128, 129, 321, 16_383] {
buf.clear();
let var_int = VariableInt::new(value).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf.len(), 2);
let decoded = VariableInt::decode(&mut buf).unwrap();
assert_eq!(decoded.value(), value);
}
}
#[test]
fn test_encode_decode_three_bytes() {
let mut buf = BytesMut::new();
for value in [16_384, 65_535, 2_097_151] {
buf.clear();
let var_int = VariableInt::new(value).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf.len(), 3);
let decoded = VariableInt::decode(&mut buf).unwrap();
assert_eq!(decoded.value(), value);
}
}
#[test]
fn test_encode_decode_four_bytes() {
let mut buf = BytesMut::new();
for value in [2_097_152, 10_000_000, VARIABLE_INT_MAX] {
buf.clear();
let var_int = VariableInt::new(value).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf.len(), 4);
let decoded = VariableInt::decode(&mut buf).unwrap();
assert_eq!(decoded.value(), value);
}
}
#[test]
fn test_mqtt_spec_examples() {
let mut buf = BytesMut::new();
let var_int = VariableInt::new(64).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf[0], 0x40);
buf.clear();
let var_int = VariableInt::new(321).unwrap();
var_int.encode(&mut buf).unwrap();
assert_eq!(buf[0], 0xC1);
assert_eq!(buf[1], 0x02);
}
#[test]
fn test_bebytes_integration() {
use bebytes::BeBytes;
let var_int = VariableInt::new(321).unwrap();
let bytes = var_int.to_be_bytes();
assert_eq!(bytes.len(), 2);
assert_eq!(bytes[0], 0xC1);
assert_eq!(bytes[1], 0x02);
let (decoded, consumed) = VariableInt::try_from_be_bytes(&bytes).unwrap();
assert_eq!(decoded.value(), 321);
assert_eq!(consumed, 2);
assert_eq!(VariableInt::field_size(), 0); }
#[test]
fn test_decode_insufficient_bytes() {
let mut buf = BytesMut::new();
buf.put_u8(0x80);
let result = VariableInt::decode(&mut buf);
assert!(result.is_err());
}
#[test]
fn test_decode_too_many_bytes() {
let mut buf = BytesMut::new();
buf.put_u8(0x80);
buf.put_u8(0x80);
buf.put_u8(0x80);
buf.put_u8(0x80);
buf.put_u8(0x01);
let result = VariableInt::decode(&mut buf);
assert!(result.is_err());
}
#[test]
fn test_conversions() {
let var_int = VariableInt::new(123).unwrap();
let value: u32 = var_int.into();
assert_eq!(value, 123);
let var_int2 = VariableInt::try_from(456u32).unwrap();
assert_eq!(var_int2.value(), 456);
let var_int3 = VariableInt::try_from(789usize).unwrap();
assert_eq!(var_int3.value(), 789);
assert!(VariableInt::try_from(VARIABLE_INT_MAX + 1).is_err());
}
#[test]
fn test_display() {
let var_int = VariableInt::new(12345).unwrap();
assert_eq!(format!("{var_int}"), "12345");
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_round_trip(value in 0u32..=VARIABLE_INT_MAX) {
let mut buf = BytesMut::new();
let var_int = VariableInt::new(value).unwrap();
var_int.encode(&mut buf).unwrap();
let decoded = VariableInt::decode(&mut buf).unwrap();
prop_assert_eq!(decoded.value(), value);
}
#[test]
fn prop_encoded_size_matches_actual(value in 0u32..=VARIABLE_INT_MAX) {
let mut buf = BytesMut::new();
let var_int = VariableInt::new(value).unwrap();
let predicted_size = var_int.encoded_size();
var_int.encode(&mut buf).unwrap();
let actual_size = u32::try_from(buf.len()).expect("buffer size should fit in u32");
prop_assert_eq!(predicted_size, actual_size);
}
#[test]
fn prop_bebytes_round_trip(value in 0u32..=VARIABLE_INT_MAX) {
use bebytes::BeBytes;
let var_int = VariableInt::new(value).unwrap();
let bytes = var_int.to_be_bytes();
prop_assert_eq!(bytes.len(), var_int.encoded_size() as usize);
let (decoded, consumed) = VariableInt::try_from_be_bytes(&bytes).unwrap();
prop_assert_eq!(decoded.value(), value);
prop_assert_eq!(consumed, bytes.len());
}
#[test]
fn prop_invalid_values_rejected(value in (VARIABLE_INT_MAX + 1)..=u32::MAX) {
let result = VariableInt::new(value);
prop_assert!(result.is_err());
}
#[test]
fn prop_size_boundaries(value in 0u32..=VARIABLE_INT_MAX) {
let var_int = VariableInt::new(value).unwrap();
let size = var_int.encoded_size();
match value {
0..=127 => prop_assert_eq!(size, 1),
128..=16_383 => prop_assert_eq!(size, 2),
16_384..=2_097_151 => prop_assert_eq!(size, 3),
2_097_152..=VARIABLE_INT_MAX => prop_assert_eq!(size, 4),
_ => unreachable!(),
}
}
}
}
}