clvm_traits/
int_encoding.rs

1pub fn encode_number(slice: &[u8], negative: bool) -> Vec<u8> {
2    let mut start = 0;
3    let pad_byte = if negative { 0xFF } else { 0x00 };
4
5    // Skip leading pad bytes
6    while start < slice.len() && slice[start] == pad_byte {
7        start += 1;
8    }
9
10    let needs_padding = if negative {
11        start == slice.len() || (slice[start] & 0x80) == 0
12    } else {
13        start < slice.len() && (slice[start] & 0x80) != 0
14    };
15
16    let mut result = Vec::with_capacity(if needs_padding {
17        slice.len() - start + 1
18    } else {
19        slice.len() - start
20    });
21
22    if needs_padding {
23        result.push(pad_byte);
24    }
25
26    result.extend_from_slice(&slice[start..]);
27    result
28}
29
30pub fn decode_number<const LEN: usize>(mut slice: &[u8], signed: bool) -> Option<[u8; LEN]> {
31    let negative = signed && !slice.is_empty() && slice[0] & 0x80 != 0;
32    let padding_byte = if negative { 0xFF } else { 0x00 };
33
34    if slice.len() > LEN && slice[0] == padding_byte {
35        slice = &slice[slice.len() - LEN..];
36    }
37
38    if slice.len() > LEN {
39        return None;
40    }
41
42    assert!(slice.len() <= LEN);
43
44    let mut result = [padding_byte; LEN];
45    let start = LEN - slice.len();
46
47    result[start..].copy_from_slice(slice);
48
49    Some(result)
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    use clvmr::Allocator;
57
58    macro_rules! test_roundtrip {
59        ( $num:expr, $signed:expr ) => {
60            let mut allocator = Allocator::new();
61            let ptr = allocator.new_number($num.into()).unwrap();
62            let atom = allocator.atom(ptr);
63            let expected = atom.as_ref();
64
65            #[allow(unused_comparisons)]
66            let encoded = encode_number(&$num.to_be_bytes(), $num < 0);
67            assert_eq!(expected, encoded);
68
69            let expected = $num.to_be_bytes();
70            let decoded = decode_number(&encoded, $signed).unwrap();
71            assert_eq!(expected, decoded);
72        };
73    }
74
75    #[test]
76    fn test_signed_encoding() {
77        test_roundtrip!(0i32, true);
78        test_roundtrip!(1i32, true);
79        test_roundtrip!(2i32, true);
80        test_roundtrip!(3i32, true);
81        test_roundtrip!(255i32, true);
82        test_roundtrip!(4716i32, true);
83        test_roundtrip!(-255i32, true);
84        test_roundtrip!(-10i32, true);
85        test_roundtrip!(i32::MIN, true);
86        test_roundtrip!(i32::MAX, true);
87    }
88
89    #[test]
90    fn test_unsigned_encoding() {
91        test_roundtrip!(0u32, false);
92        test_roundtrip!(1u32, false);
93        test_roundtrip!(2u32, false);
94        test_roundtrip!(3u32, false);
95        test_roundtrip!(255u32, false);
96        test_roundtrip!(u32::MAX, false);
97    }
98}