mandos/value_interpreter/
parse_num.rs

1use super::prefixes::*;
2use num_bigint::{BigInt, BigUint, Sign};
3use num_traits::identities::Zero;
4
5pub fn try_parse_fixed_width(s: &str) -> Option<Vec<u8>> {
6    if let Some(stripped) = s.strip_prefix(U64_PREFIX) {
7        return Some(parse_fixed_width_unsigned(stripped, 8));
8    }
9
10    if let Some(stripped) = s.strip_prefix(U32_PREFIX) {
11        return Some(parse_fixed_width_unsigned(stripped, 4));
12    }
13
14    if let Some(stripped) = s.strip_prefix(U16_PREFIX) {
15        return Some(parse_fixed_width_unsigned(stripped, 2));
16    }
17
18    if let Some(stripped) = s.strip_prefix(U8_PREFIX) {
19        return Some(parse_fixed_width_unsigned(stripped, 1));
20    }
21
22    if let Some(stripped) = s.strip_prefix(I64_PREFIX) {
23        return Some(parse_fixed_width_signed(stripped, 8));
24    }
25
26    if let Some(stripped) = s.strip_prefix(I32_PREFIX) {
27        return Some(parse_fixed_width_signed(stripped, 4));
28    }
29
30    if let Some(stripped) = s.strip_prefix(I16_PREFIX) {
31        return Some(parse_fixed_width_signed(stripped, 2));
32    }
33
34    if let Some(stripped) = s.strip_prefix(I8_PREFIX) {
35        return Some(parse_fixed_width_signed(stripped, 1));
36    }
37
38    if let Some(stripped) = s.strip_prefix(BIGUINT_PREFIX) {
39        return Some(parse_biguint(stripped));
40    }
41
42    None
43}
44
45pub fn parse_num(s: &str) -> Vec<u8> {
46    if let Some(stripped) = s.strip_prefix('+') {
47        let bi = BigInt::from_bytes_be(Sign::Plus, parse_unsigned(stripped).as_slice());
48        return big_int_to_bytes_be(&bi);
49    }
50
51    if let Some(stripped) = s.strip_prefix('-') {
52        let bi = BigInt::from_bytes_be(Sign::Minus, parse_unsigned(stripped).as_slice());
53        return big_int_to_bytes_be(&bi);
54    }
55
56    parse_unsigned(s)
57}
58
59fn parse_fixed_width_signed(s: &str, length: usize) -> Vec<u8> {
60    if let Some(stripped) = s.strip_prefix('-') {
61        let mut result = vec![0xffu8; length];
62        let bi = BigInt::from_bytes_be(Sign::Minus, parse_unsigned(stripped).as_slice());
63        let bytes = bi.to_signed_bytes_be();
64        assert!(
65            bytes.len() <= length,
66            "representation of {s} does not fit in {length} bytes"
67        );
68        let offset = length - bytes.len();
69        if !bytes.is_empty() {
70            result[offset..].clone_from_slice(&bytes[..]);
71        }
72        result
73    } else {
74        let s = if let Some(stripped) = s.strip_prefix('+') {
75            stripped
76        } else {
77            s
78        };
79        let result = parse_fixed_width_unsigned(s, length);
80        assert!(
81            result.is_empty() || result[0] >> 7 != 1,
82            "representation of {s} does not fit in {length} bytes"
83        );
84        result
85    }
86}
87
88fn parse_fixed_width_unsigned(s: &str, length: usize) -> Vec<u8> {
89    let parsed = parse_unsigned(s);
90    assert!(
91        parsed.len() <= length,
92        "representation of {s} does not fit in {length} bytes"
93    );
94
95    let mut result = vec![0u8; length];
96    let offset = length - parsed.len();
97    if !parsed.is_empty() {
98        result[offset..].clone_from_slice(&parsed[..]);
99    }
100    result
101}
102
103fn parse_biguint(s: &str) -> Vec<u8> {
104    let parsed = parse_unsigned(s);
105    let encoded_length = (parsed.len() as u32).to_be_bytes();
106    [&encoded_length[..], &parsed[..]].concat()
107}
108
109fn parse_unsigned(s: &str) -> Vec<u8> {
110    let clean = s.replace(&['_', ','][..], "");
111    if clean.starts_with("0x") || clean.starts_with("0X") {
112        let clean = &clean[2..];
113        return if clean.len() % 2 == 0 {
114            hex::decode(clean).unwrap()
115        } else {
116            let even_bytes = format!("0{clean}");
117            hex::decode(&even_bytes[..]).unwrap()
118        };
119    }
120
121    if clean.starts_with("0b") || clean.starts_with("0B") {
122        let clean = &clean[2..];
123        if clean.is_empty() {
124            return Vec::new();
125        }
126        let bu = BigUint::parse_bytes(clean.as_bytes(), 2).unwrap();
127        return big_uint_to_bytes_be(&bu);
128    }
129
130    if let Some(bu) = BigUint::parse_bytes(clean.as_bytes(), 10) {
131        big_uint_to_bytes_be(&bu)
132    } else {
133        panic!("Could not parse base 10 number: {clean}")
134    }
135}
136
137fn big_uint_to_bytes_be(bu: &BigUint) -> Vec<u8> {
138    if bu.is_zero() {
139        Vec::new()
140    } else {
141        bu.to_bytes_be()
142    }
143}
144
145fn big_int_to_bytes_be(bi: &BigInt) -> Vec<u8> {
146    if bi.is_zero() {
147        Vec::new()
148    } else {
149        bi.to_signed_bytes_be()
150    }
151}