Skip to main content

sbpf_common/
inst_param.rs

1use {
2    core::fmt,
3    serde::{Deserialize, Serialize},
4};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct Register {
8    pub n: u8,
9}
10
11impl fmt::Display for Register {
12    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13        write!(f, "r{}", self.n)
14    }
15}
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum Number {
19    Int(i64),
20    Addr(i64),
21}
22
23impl Number {
24    pub fn to_i16(&self) -> i16 {
25        match self {
26            Number::Int(v) => *v as i16,
27            Number::Addr(a) => *a as i16,
28        }
29    }
30
31    pub fn to_i64(&self) -> i64 {
32        match self {
33            Number::Int(v) => *v,
34            Number::Addr(a) => *a,
35        }
36    }
37
38    fn retag(&self, other: &Number, value: i64) -> Number {
39        match (self, other) {
40            (Number::Int(_), Number::Int(_)) => Number::Int(value),
41            _ => Number::Addr(value),
42        }
43    }
44
45    pub fn checked_add(&self, other: &Number) -> Option<Number> {
46        Some(self.retag(other, self.to_i64().checked_add(other.to_i64())?))
47    }
48
49    pub fn checked_sub(&self, other: &Number) -> Option<Number> {
50        Some(self.retag(other, self.to_i64().checked_sub(other.to_i64())?))
51    }
52
53    pub fn checked_mul(&self, other: &Number) -> Option<Number> {
54        Some(self.retag(other, self.to_i64().checked_mul(other.to_i64())?))
55    }
56
57    pub fn checked_div(&self, other: &Number) -> Option<Number> {
58        Some(self.retag(other, self.to_i64().checked_div(other.to_i64())?))
59    }
60}
61
62impl fmt::Display for Number {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        match self {
65            Number::Int(i) => write!(f, "{}", i),
66            Number::Addr(a) => write!(f, "{}", a),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_register_display() {
77        let reg = Register { n: 5 };
78        assert_eq!(reg.to_string(), "r5");
79
80        let reg0 = Register { n: 0 };
81        assert_eq!(reg0.to_string(), "r0");
82
83        let reg10 = Register { n: 10 };
84        assert_eq!(reg10.to_string(), "r10");
85    }
86
87    #[test]
88    fn test_number_to_i16() {
89        assert_eq!(Number::Int(42).to_i16(), 42i16);
90        assert_eq!(Number::Addr(100).to_i16(), 100i16);
91        assert_eq!(Number::Int(-5).to_i16(), -5i16);
92    }
93
94    #[test]
95    fn test_number_to_i64() {
96        assert_eq!(Number::Int(42).to_i64(), 42i64);
97        assert_eq!(Number::Addr(100).to_i64(), 100i64);
98        assert_eq!(Number::Int(-5).to_i64(), -5i64);
99    }
100
101    #[test]
102    fn test_number_display() {
103        assert_eq!(Number::Int(42).to_string(), "42");
104        assert_eq!(Number::Addr(100).to_string(), "100");
105        assert_eq!(Number::Int(-5).to_string(), "-5");
106    }
107
108    #[test]
109    fn test_number_checked_add() {
110        // Int + Int
111        assert_eq!(
112            Number::Int(10).checked_add(&Number::Int(20)),
113            Some(Number::Int(30))
114        );
115        // Addr + Addr
116        assert_eq!(
117            Number::Addr(10).checked_add(&Number::Addr(20)),
118            Some(Number::Addr(30))
119        );
120        // Int + Addr / Addr + Int both tag the result as Addr
121        assert_eq!(
122            Number::Int(10).checked_add(&Number::Addr(20)),
123            Some(Number::Addr(30))
124        );
125        assert_eq!(
126            Number::Addr(10).checked_add(&Number::Int(20)),
127            Some(Number::Addr(30))
128        );
129    }
130
131    #[test]
132    fn test_number_checked_sub() {
133        assert_eq!(
134            Number::Int(30).checked_sub(&Number::Int(10)),
135            Some(Number::Int(20))
136        );
137        assert_eq!(
138            Number::Addr(30).checked_sub(&Number::Int(10)),
139            Some(Number::Addr(20))
140        );
141    }
142
143    #[test]
144    fn test_number_checked_mul() {
145        assert_eq!(
146            Number::Int(5).checked_mul(&Number::Int(4)),
147            Some(Number::Int(20))
148        );
149        assert_eq!(
150            Number::Addr(5).checked_mul(&Number::Int(4)),
151            Some(Number::Addr(20))
152        );
153    }
154
155    #[test]
156    fn test_number_checked_div() {
157        assert_eq!(
158            Number::Int(20).checked_div(&Number::Int(4)),
159            Some(Number::Int(5))
160        );
161        assert_eq!(
162            Number::Addr(20).checked_div(&Number::Int(4)),
163            Some(Number::Addr(5))
164        );
165    }
166
167    #[test]
168    fn test_number_checked_arithmetic_reports_overflow() {
169        // Overflow returns None instead of panicking / wrapping.
170        assert_eq!(Number::Int(i64::MAX).checked_add(&Number::Int(1)), None);
171        assert_eq!(Number::Int(i64::MIN).checked_sub(&Number::Int(1)), None);
172        assert_eq!(Number::Int(i64::MAX).checked_mul(&Number::Int(2)), None);
173        // i64::MIN / -1 overflows two's-complement division.
174        assert_eq!(Number::Int(i64::MIN).checked_div(&Number::Int(-1)), None);
175    }
176
177    #[test]
178    fn test_number_checked_div_by_zero() {
179        assert_eq!(Number::Int(8).checked_div(&Number::Int(0)), None);
180        assert_eq!(Number::Addr(8).checked_div(&Number::Int(0)), None);
181    }
182}