use bitcoincash::consensus::encode::ReadExt;
use bitcoincash::consensus::Decodable;
use bitcoincash::consensus::Encodable;
use bitcoincash::consensus::WriteExt;
use std::io::{self};
#[derive(Copy, PartialEq, Eq, Clone, Debug, Default)]
pub struct BitcoindVarInt(pub u64);
impl Decodable for BitcoindVarInt {
#[inline]
fn consensus_decode<R: io::Read + ?Sized>(
r: &mut R,
) -> Result<Self, bitcoincash::consensus::encode::Error> {
let mut v: u64 = 0;
loop {
let x = ReadExt::read_u8(r)?;
v = (v << 7) | (x & 0x7f) as u64;
if x >= 0x80 {
v += 1;
continue;
}
return Ok(BitcoindVarInt(v));
}
}
}
impl Encodable for BitcoindVarInt {
#[inline]
fn consensus_encode<W: std::io::Write + ?Sized>(
&self,
w: &mut W,
) -> Result<usize, std::io::Error> {
const TMP_SIZE: usize = (u64::BITS * 8).div_ceil(7) as usize;
let mut tmp: [u8; TMP_SIZE] = [0; TMP_SIZE];
let mut n = self.0;
let mut len = 0;
loop {
tmp[len] = (n & 0x7F) as u8 | (if len != 0 { 0x80 } else { 0x00 });
if n <= 0x7F {
break;
}
n = (n >> 7) - 1;
len += 1;
}
let encoded_size = len + 1;
loop {
w.emit_u8(tmp[len])?;
if len == 0 {
break;
}
len -= 1;
}
Ok(encoded_size)
}
}
#[cfg(test)]
mod test {
use super::*;
use bitcoincash::consensus::deserialize;
use bitcoincash::consensus::serialize;
#[test]
fn encode_decode() {
let test = |number, expected_encoding_length| {
let n = BitcoindVarInt(number);
let s = serialize(&n);
let u: BitcoindVarInt = deserialize(&s).unwrap();
assert_eq!(s.len(), expected_encoding_length, "Encoded length mismatch");
assert_eq!(u.0, number, "Encoded/decoded does not match original");
};
test(0, 1);
test(127, 1);
test(128, 2);
}
}