1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#![cfg(feature = "fastrlp")]
#![cfg_attr(has_doc_cfg, doc(cfg(feature = "fastrlp")))]
use crate::Uint;
use core::mem::size_of;
use fastrlp::{BufMut, Decodable, DecodeError, Encodable, Header};
impl<const BITS: usize, const LIMBS: usize> Encodable for Uint<BITS, LIMBS> {
fn length(&self) -> usize {
let bits = self.bit_len();
match bits {
n if n <= 7 => 1,
n if n <= 55 * 8 => 1 + (n + 7) / 8,
n => {
let bytes = (n + 7) / 8;
let len_bytes = size_of::<usize>() - bytes.leading_zeros() as usize / 8;
1 + len_bytes + bytes
}
}
}
fn encode(&self, out: &mut dyn BufMut) {
match self.bit_len() {
0 => out.put_u8(0x80),
n if n <= 7 => {
#[allow(clippy::cast_possible_truncation)]
out.put_u8(self.as_limbs()[0] as u8);
}
n if n <= 55 * 8 => {
let bytes = self.to_be_bytes_vec();
let bytes = trim_leading_zeros(&bytes);
#[allow(clippy::cast_possible_truncation)]
out.put_u8(0x80 + bytes.len() as u8);
out.put_slice(bytes);
}
_ => {
let bytes = self.to_be_bytes_vec();
let bytes = trim_leading_zeros(&bytes);
let length_bytes = bytes.len().to_be_bytes();
let length_bytes = trim_leading_zeros(&length_bytes);
#[allow(clippy::cast_possible_truncation)]
out.put_u8(0xb7 + length_bytes.len() as u8);
out.put_slice(length_bytes);
out.put_slice(bytes);
}
}
}
}
impl<const BITS: usize, const LIMBS: usize> Decodable for Uint<BITS, LIMBS> {
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
let header = Header::decode(buf)?;
if header.list {
return Err(DecodeError::UnexpectedList);
}
let bytes = &buf[..header.payload_length];
*buf = &buf[header.payload_length..];
Self::try_from_be_slice(bytes).ok_or(DecodeError::Overflow)
}
}
fn trim_leading_zeros(bytes: &[u8]) -> &[u8] {
let zeros = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len());
&bytes[zeros..]
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
aliases::{U0, U256},
const_for, nlimbs,
};
use hex_literal::hex;
use proptest::proptest;
fn encode<T: Encodable>(value: T) -> Vec<u8> {
let mut buf = vec![];
value.encode(&mut buf);
buf
}
#[test]
fn test_rlp() {
assert_eq!(encode(U0::from(0))[..], hex!("80"));
assert_eq!(encode(U256::from(0))[..], hex!("80"));
assert_eq!(encode(U256::from(15))[..], hex!("0f"));
assert_eq!(encode(U256::from(1024))[..], hex!("820400"));
assert_eq!(encode(U256::from(0x1234_5678))[..], hex!("8412345678"));
}
#[test]
fn test_roundtrip() {
const_for!(BITS in SIZES {
const LIMBS: usize = nlimbs(BITS);
proptest!(|(value: Uint<BITS, LIMBS>)| {
let serialized = encode(value);
assert_eq!(serialized.len(), value.length());
let mut reader = &serialized[..];
let deserialized = Uint::decode(&mut reader).unwrap();
assert_eq!(reader.len(), 0);
assert_eq!(value, deserialized);
});
});
}
#[test]
#[cfg(feature = "rlp")]
fn test_rlp_fastrlp_compat() {
use rlp::Encodable;
const_for!(BITS in SIZES {
const LIMBS: usize = nlimbs(BITS);
proptest!(|(value: Uint<BITS, LIMBS>)| {
let serialized = encode(value);
let serialized_rlp = value.rlp_bytes();
assert_eq!(serialized, serialized_rlp);
});
});
}
}