bitcoin_varint/
lib.rs

1pub mod test;
2
3use std::io::Error;
4
5/// CompactSize Unsigned Integers  
6///
7/// The raw transaction format and several peer-to-peer network messages use a type of variable-length integer to indicate the number of bytes in a following piece of data.
8///
9/// Bitcoin Core code and this document refers to these variable length integers as compactSize.
10/// Many other documents refer to them as var_int or varInt, but this risks conflation with other variable-length integer encodings—such as the CVarInt class used in Bitcoin Core for serializing data to disk.
11/// Because it’s used in the transaction format, the format of compactSize unsigned integers is part of the consensus rules.
12///
13/// https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers
14///
15/// https://learnmeabitcoin.com/technical/varint
16pub struct VarInt;
17
18impl VarInt {
19    /// For numbers from 0 to 252, compactSize unsigned integers look like regular unsigned integers.
20    /// For other numbers up to 0xffffffffffffffff, a byte is prefixed to the number to indicate its length—but otherwise the numbers look like regular unsigned integers in little-endian order.
21    pub fn encode(size: u64) -> Result<Vec<u8>, Error> {
22        let size_bytes = size.to_le_bytes();
23        let result = match size {
24            x if x <= 252 => vec![size_bytes[0]],
25            x if (253..0xffff).contains(&x) => {
26                vec![0xfd, size_bytes[0], size_bytes[1]]
27            }
28            x if (0x10000..0xffffffff).contains(&x) => vec![
29                0xfe,
30                size_bytes[0],
31                size_bytes[1],
32                size_bytes[2],
33                size_bytes[3],
34            ],
35            x if (0x100000000..u64::MAX).contains(&x) => {
36                let mut x = size_bytes.to_vec();
37                x.insert(0, 0xff);
38                x
39            }
40            _ => panic!("VarInt: unexpected input"),
41        };
42        Ok(result)
43    }
44
45    /// For numbers from 0 to 252, compactSize unsigned integers look like regular unsigned integers.
46    /// For other numbers up to 0xffffffffffffffff, a byte is prefixed to the number to indicate its length—but otherwise the numbers look like regular unsigned integers in little-endian order.
47    pub fn decode(bytes: &[u8]) -> Result<u64, Error> {
48        let result = match bytes[0] {
49            x if x < 0xfd => u64::from_le_bytes([bytes[0], 0, 0, 0, 0, 0, 0, 0]),
50            x if x == 0xfd => u64::from_le_bytes([bytes[1], bytes[2], 0, 0, 0, 0, 0, 0]),
51            x if x == 0xfe => {
52                u64::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0, 0])
53            }
54            x if x == 0xff => u64::from_le_bytes([
55                bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
56            ]),
57            _ => panic!("VarInt: unexpected input"),
58        };
59        Ok(result)
60    }
61
62    /// Returns the bytes needed to encode this varint
63    pub fn get_size(varint: u64) -> Result<u8, Error> {
64        match varint {
65            x if x <= 252 => Ok(1),
66            x if (253..0xffff).contains(&x) => Ok(3),
67            x if (0x10000..0xffffffff).contains(&x) => Ok(5),
68            x if (0x10000000..u64::MAX).contains(&x) => Ok(9),
69            _ => panic!("VarInt: unexpected input"),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_varint_encode() {
80        assert_eq!(VarInt::encode(515).unwrap(), vec![0xfd, 3, 2]);
81    }
82
83    #[test]
84    fn test_varint_decode() {
85        assert_eq!(VarInt::decode(&vec![0xfd, 3, 2]).unwrap(), 515);
86    }
87
88    #[test]
89    fn test_varint_get_size() {
90        assert_eq!(VarInt::get_size(515).unwrap(), 3);
91    }
92}