Skip to main content

multiversx_sc_codec/impl_for_types/
impl_num_signed.rs

1use crate::{
2    DecodeError, DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput,
3    NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput,
4    dep_encode_num_mimic,
5};
6
7macro_rules! top_encode_num_signed {
8    ($num_type:ty, $size_in_bits:expr) => {
9        impl TopEncode for $num_type {
10            #[inline]
11            fn top_encode_or_handle_err<O, H>(&self, output: O, _h: H) -> Result<(), H::HandledErr>
12            where
13                O: TopEncodeOutput,
14                H: EncodeErrorHandler,
15            {
16                output.set_i64(*self as i64);
17                Ok(())
18            }
19        }
20    };
21}
22
23top_encode_num_signed! {i64, 64}
24top_encode_num_signed! {i32, 32}
25top_encode_num_signed! {isize, 32}
26top_encode_num_signed! {i16, 16}
27top_encode_num_signed! {i8, 8}
28
29dep_encode_num_mimic! {i64, u64}
30dep_encode_num_mimic! {i32, u32}
31dep_encode_num_mimic! {isize, u32}
32dep_encode_num_mimic! {i16, u16}
33dep_encode_num_mimic! {i8, u8}
34
35macro_rules! dep_decode_num_signed {
36    ($ty:ty, $num_bytes:expr) => {
37        impl NestedDecode for $ty {
38            fn dep_decode_or_handle_err<I, H>(input: &mut I, h: H) -> Result<Self, H::HandledErr>
39            where
40                I: NestedDecodeInput,
41                H: DecodeErrorHandler,
42            {
43                let mut bytes = [0u8; $num_bytes];
44                input.read_into(&mut bytes[..], h)?;
45                let num = <$ty>::from_be_bytes(bytes);
46                Ok(num)
47            }
48        }
49    };
50}
51
52dep_decode_num_signed!(i8, 1);
53dep_decode_num_signed!(i16, 2);
54dep_decode_num_signed!(i32, 4);
55dep_decode_num_signed!(i64, 8);
56
57impl NestedDecode for isize {
58    #[inline]
59    fn dep_decode_or_handle_err<I, H>(input: &mut I, h: H) -> Result<Self, H::HandledErr>
60    where
61        I: NestedDecodeInput,
62        H: DecodeErrorHandler,
63    {
64        i32::dep_decode_or_handle_err(input, h).map(|num| num as isize)
65    }
66}
67
68macro_rules! top_decode_num_signed {
69    ($ty:ty, $bounds_ty:ty) => {
70        impl TopDecode for $ty {
71            #[inline]
72            fn top_decode_or_handle_err<I, H>(input: I, h: H) -> Result<Self, H::HandledErr>
73            where
74                I: TopDecodeInput,
75                H: DecodeErrorHandler,
76            {
77                let arg_i64 = input.into_i64(h)?;
78                let min = <$bounds_ty>::MIN as i64;
79                let max = <$bounds_ty>::MAX as i64;
80                if arg_i64 < min || arg_i64 > max {
81                    Err(h.handle_error(DecodeError::INPUT_OUT_OF_RANGE))
82                } else {
83                    Ok(arg_i64 as $ty)
84                }
85            }
86        }
87    };
88}
89
90top_decode_num_signed!(i8, i8);
91top_decode_num_signed!(i16, i16);
92top_decode_num_signed!(i32, i32);
93top_decode_num_signed!(isize, i32); // even if isize can be 64 bits on some platforms, we always deserialize as max 32 bits
94top_decode_num_signed!(i64, i64);
95
96#[cfg(test)]
97pub mod tests {
98    use crate::{
99        DecodeError, TopDecode,
100        test_util::{check_dep_encode_decode, check_top_encode_decode},
101    };
102
103    #[test]
104    fn test_top() {
105        // signed zero
106        check_top_encode_decode(0i8, &[]);
107        check_top_encode_decode(0i16, &[]);
108        check_top_encode_decode(0i32, &[]);
109        check_top_encode_decode(0i64, &[]);
110        check_top_encode_decode(0isize, &[]);
111        // signed positive
112        check_top_encode_decode(5i8, &[5]);
113        check_top_encode_decode(5i16, &[5]);
114        check_top_encode_decode(5i32, &[5]);
115        check_top_encode_decode(5i64, &[5]);
116        check_top_encode_decode(5isize, &[5]);
117        // signed negative
118        check_top_encode_decode(-5i8, &[251]);
119        check_top_encode_decode(-5i16, &[251]);
120        check_top_encode_decode(-5i32, &[251]);
121        check_top_encode_decode(-5i64, &[251]);
122        check_top_encode_decode(-5isize, &[251]);
123    }
124
125    #[test]
126    fn test_dep() {
127        // signed zero
128        check_dep_encode_decode(0i8, &[0]);
129        check_dep_encode_decode(0i16, &[0, 0]);
130        check_dep_encode_decode(0i32, &[0, 0, 0, 0]);
131        check_dep_encode_decode(0isize, &[0, 0, 0, 0]);
132        check_dep_encode_decode(0i64, &[0, 0, 0, 0, 0, 0, 0, 0]);
133        // signed positive
134        check_dep_encode_decode(5i8, &[5]);
135        check_dep_encode_decode(5i16, &[0, 5]);
136        check_dep_encode_decode(5i32, &[0, 0, 0, 5]);
137        check_dep_encode_decode(5isize, &[0, 0, 0, 5]);
138        check_dep_encode_decode(5i64, &[0, 0, 0, 0, 0, 0, 0, 5]);
139        // signed negative
140        check_dep_encode_decode(-5i8, &[251]);
141        check_dep_encode_decode(-5i16, &[255, 251]);
142        check_dep_encode_decode(-5i32, &[255, 255, 255, 251]);
143        check_dep_encode_decode(-5isize, &[255, 255, 255, 251]);
144        check_dep_encode_decode(-5i64, &[255, 255, 255, 255, 255, 255, 255, 251]);
145    }
146
147    #[test]
148    fn test_top_min_max() {
149        // i8
150        check_top_encode_decode(i8::MAX, &[127]);
151        check_top_encode_decode(i8::MIN, &[128]); // -128 top-encoded as minimal signed bytes
152        // i16
153        check_top_encode_decode(i16::MAX, &[0x7F, 0xFF]);
154        check_top_encode_decode(i16::MIN, &[0x80, 0x00]);
155        // i32
156        check_top_encode_decode(i32::MAX, &[0x7F, 0xFF, 0xFF, 0xFF]);
157        check_top_encode_decode(i32::MIN, &[0x80, 0x00, 0x00, 0x00]);
158        // i64
159        check_top_encode_decode(i64::MAX, &[0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
160        check_top_encode_decode(i64::MIN, &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
161    }
162
163    #[test]
164    fn test_dep_min_max() {
165        check_dep_encode_decode(i8::MAX, &[0x7F]);
166        check_dep_encode_decode(i8::MIN, &[0x80]);
167        check_dep_encode_decode(i16::MAX, &[0x7F, 0xFF]);
168        check_dep_encode_decode(i16::MIN, &[0x80, 0x00]);
169        check_dep_encode_decode(i32::MAX, &[0x7F, 0xFF, 0xFF, 0xFF]);
170        check_dep_encode_decode(i32::MIN, &[0x80, 0x00, 0x00, 0x00]);
171        check_dep_encode_decode(i64::MAX, &[0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
172        check_dep_encode_decode(i64::MIN, &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
173    }
174
175    #[test]
176    fn test_top_decode_out_of_range() {
177        // 128 doesn't fit in i8 (max 127)
178        assert_eq!(
179            i8::top_decode(&[0u8, 128u8][..]),
180            Err(DecodeError::INPUT_OUT_OF_RANGE),
181        );
182        // -129 doesn't fit in i8 (min -128)
183        assert_eq!(
184            i8::top_decode(&[0xFF, 0x7F][..]),
185            Err(DecodeError::INPUT_OUT_OF_RANGE),
186        );
187    }
188}