blvm_primitives/serialization/
varint.rs1use crate::error::{ConsensusError, Result};
15use std::borrow::Cow;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum VarIntError {
20 InsufficientBytes,
22 InvalidEncoding,
24 ValueTooLarge,
26}
27
28impl std::fmt::Display for VarIntError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 VarIntError::InsufficientBytes => write!(f, "Insufficient bytes to decode VarInt"),
32 VarIntError::InvalidEncoding => write!(f, "Invalid VarInt encoding"),
33 VarIntError::ValueTooLarge => write!(f, "VarInt value too large"),
34 }
35 }
36}
37
38impl std::error::Error for VarIntError {}
39
40pub fn encode_varint(value: u64) -> Vec<u8> {
42 if value < 0xfd {
43 vec![value as u8]
44 } else if value <= 0xffff {
45 debug_assert!(value >= 0xfd);
46 let mut result = vec![0xfd];
47 result.extend_from_slice(&(value as u16).to_le_bytes());
48 debug_assert!(result.len() == 3);
49 result
50 } else if value <= 0xffffffff {
51 debug_assert!(value > 0xffff);
52 let mut result = vec![0xfe];
53 result.extend_from_slice(&(value as u32).to_le_bytes());
54 debug_assert!(result.len() == 5);
55 result
56 } else {
57 debug_assert!(value > 0xffffffff);
58 let mut result = vec![0xff];
59 result.extend_from_slice(&value.to_le_bytes());
60 debug_assert!(result.len() == 9);
61 result
62 }
63}
64
65pub fn decode_varint(data: &[u8]) -> Result<(u64, usize)> {
69 if data.is_empty() {
70 return Err(ConsensusError::Serialization(Cow::Owned(
71 VarIntError::InsufficientBytes.to_string(),
72 )));
73 }
74
75 let first_byte = data[0];
76
77 match first_byte {
78 b if b < 0xfd => Ok((b as u64, 1)),
79
80 0xfd => {
81 if data.len() < 3 {
82 return Err(ConsensusError::Serialization(Cow::Owned(
83 VarIntError::InsufficientBytes.to_string(),
84 )));
85 }
86 let value = u16::from_le_bytes([data[1], data[2]]) as u64;
87 if value < 0xfd {
88 return Err(ConsensusError::Serialization(Cow::Owned(
89 VarIntError::InvalidEncoding.to_string(),
90 )));
91 }
92 Ok((value, 3))
93 }
94
95 0xfe => {
96 if data.len() < 5 {
97 return Err(ConsensusError::Serialization(Cow::Owned(
98 VarIntError::InsufficientBytes.to_string(),
99 )));
100 }
101 let value = u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as u64;
102 if value <= 0xffff {
103 return Err(ConsensusError::Serialization(Cow::Owned(
104 VarIntError::InvalidEncoding.to_string(),
105 )));
106 }
107 Ok((value, 5))
108 }
109
110 0xff => {
111 if data.len() < 9 {
112 return Err(ConsensusError::Serialization(Cow::Owned(
113 VarIntError::InsufficientBytes.to_string(),
114 )));
115 }
116 let value = u64::from_le_bytes([
117 data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
118 ]);
119 if value <= 0xffffffff {
120 return Err(ConsensusError::Serialization(Cow::Owned(
121 VarIntError::InvalidEncoding.to_string(),
122 )));
123 }
124 Ok((value, 9))
125 }
126
127 _ => Err(ConsensusError::Serialization(Cow::Owned(
128 VarIntError::InvalidEncoding.to_string(),
129 ))),
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_encode_decode_round_trip() {
139 let values = [
140 0u64,
141 252,
142 253,
143 65535,
144 65536,
145 0xffffffff,
146 0x100000000,
147 u64::MAX,
148 ];
149 for value in values {
150 let encoded = encode_varint(value);
151 let (decoded, len) = decode_varint(&encoded).unwrap();
152 assert_eq!(decoded, value);
153 assert_eq!(len, encoded.len());
154 }
155 }
156}