reweb3_num/bint/
endian.rs

1use crate::digit;
2use crate::doc;
3// use core::mem::MaybeUninit;
4
5macro_rules! endian {
6    ($BUint: ident, $BInt: ident, $Digit: ident) => {
7        macro_rules! set_digit {
8            ($out_digits: ident, $i: expr, $digit: expr, $is_negative: expr, $sign_bits: expr) => {
9                if $i == Self::N_MINUS_1 {
10                    if ($digit as digit::$Digit::SignedDigit).is_negative() == $is_negative {
11                        $out_digits[$i] = $digit;
12                    } else {
13                        return None;
14                    }
15                } else if $i < N {
16                    $out_digits[$i] = $digit;
17                } else if $digit != $sign_bits {
18                    return None;
19                };
20            };
21        }
22
23        #[doc = doc::endian::impl_desc!($BInt)]
24        impl<const N: usize> $BInt<N> {
25            #[doc = doc::endian::from_be!(I 256)]
26            #[must_use = doc::must_use_op!()]
27            #[inline]
28            pub const fn from_be(x: Self) -> Self {
29                Self::from_bits($BUint::from_be(x.bits))
30            }
31
32            #[doc = doc::endian::from_le!(I 256)]
33            #[must_use = doc::must_use_op!()]
34            #[inline]
35            pub const fn from_le(x: Self) -> Self {
36                Self::from_bits($BUint::from_le(x.bits))
37            }
38
39            #[doc = doc::endian::to_be!(I 256)]
40            #[must_use = doc::must_use_op!()]
41            #[inline]
42            pub const fn to_be(self) -> Self {
43                Self::from_be(self)
44            }
45
46            #[doc = doc::endian::to_le!(I 256)]
47            #[must_use = doc::must_use_op!()]
48            #[inline]
49            pub const fn to_le(self) -> Self {
50                Self::from_le(self)
51            }
52
53            /// Create an integer value from a slice of bytes in big endian. The value is wrapped in an [`Option`](https://doc.rust-lang.org/core/option/enum.Option.html) as the integer represented by the slice of bytes may represent an integer too large to be represented by the type.
54            ///
55            /// If the length of the slice is shorter than `Self::BYTES`, the slice is padded with zeros or ones at the start so that it's length equals `Self::BYTES`. It is padded with ones if the bytes represent a negative integer, otherwise it is padded with zeros.
56            ///
57            /// If the length of the slice is longer than `Self::BYTES`, `None` will be returned, unless the bytes represent a non-negative integer and leading zeros from the slice can be removed until the length of the slice equals `Self::BYTES`, or if the bytes represent a negative integer and leading ones from the slice can be removed until the length of the slice equals `Self::BYTES`.
58            ///
59            /// For examples, see the
60            #[doc = concat!("[`from_be_slice`](crate::", stringify!($BUint), "::from_be_slice)")]
61            /// method documentation for
62            #[doc = concat!("[`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
63            #[must_use = doc::must_use_op!()]
64            pub const fn from_be_slice(slice: &[u8]) -> Option<Self> {
65                let len = slice.len();
66                if len == 0 {
67                    return Some(Self::ZERO);
68                }
69                let is_negative = (slice[0] as i8).is_negative();
70                let sign_bits = if is_negative {
71                    $Digit::MAX
72                } else {
73                    $Digit::MIN
74                };
75                let mut out_digits = if is_negative {
76                    [$Digit::MAX; N]
77                } else {
78                    [0; N]
79                };
80                let mut i = 0;
81                let exact = len >> digit::$Digit::BYTE_SHIFT;
82                while i < exact {
83                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
84                    let init_index = len - digit::$Digit::BYTES as usize;
85                    let mut j = init_index;
86                    while j < slice.len() {
87                        digit_bytes[j - init_index] = slice[j - (i << digit::$Digit::BYTE_SHIFT)];
88                        j += 1;
89                    }
90                    let digit = $Digit::from_be_bytes(digit_bytes);
91                    set_digit!(out_digits, i, digit, is_negative, sign_bits);
92                    i += 1;
93                }
94                let rem = len & (digit::$Digit::BYTES as usize - 1);
95                if rem == 0 {
96                    Some(Self::from_bits($BUint::from_digits(out_digits)))
97                } else {
98                    let pad_byte = if is_negative { u8::MAX } else { 0 };
99                    let mut last_digit_bytes = [pad_byte; digit::$Digit::BYTES as usize];
100                    let mut j = 0;
101                    while j < rem {
102                        last_digit_bytes[digit::$Digit::BYTES as usize - rem + j] = slice[j];
103                        j += 1;
104                    }
105                    let digit = $Digit::from_be_bytes(last_digit_bytes);
106                    set_digit!(out_digits, i, digit, is_negative, sign_bits);
107                    Some(Self::from_bits($BUint::from_digits(out_digits)))
108                }
109            }
110
111            /// Creates an integer value from a slice of bytes in little endian. The value is wrapped in an [`Option`](https://doc.rust-lang.org/core/option/enum.Option.html) as the bytes may represent an integer too large to be represented by the type.
112            ///
113            /// If the length of the slice is shorter than `Self::BYTES`, the slice is padded with zeros or ones at the end so that it's length equals `Self::BYTES`. It is padded with ones if the bytes represent a negative integer, otherwise it is padded with zeros.
114            ///
115            /// If the length of the slice is longer than `Self::BYTES`, `None` will be returned, unless the bytes represent a non-negative integer and trailing zeros from the slice can be removed until the length of the slice equals `Self::BYTES`, or if the bytes represent a negative integer and trailing ones from the slice can be removed until the length of the slice equals `Self::BYTES`.
116            ///
117            /// For examples, see the
118            #[doc = concat!("[`from_le_slice`](crate::", stringify!($BUint), "::from_le_slice)")]
119            /// method documentation for
120            #[doc = concat!("[`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
121            #[must_use = doc::must_use_op!()]
122            pub const fn from_le_slice(slice: &[u8]) -> Option<Self> {
123                let len = slice.len();
124                if len == 0 {
125                    return Some(Self::ZERO);
126                }
127                let is_negative = (slice[len - 1] as i8).is_negative();
128                let sign_bits = if is_negative {
129                    $Digit::MAX
130                } else {
131                    $Digit::MIN
132                };
133                let mut out_digits = [sign_bits; N];
134                // let slice_ptr = slice.as_ptr();
135                let mut i = 0;
136                let exact = len >> digit::$Digit::BYTE_SHIFT;
137                while i < exact {
138                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
139                    let init_index = i << digit::$Digit::BYTE_SHIFT;
140                    let mut j = init_index;
141                    while j < init_index + digit::$Digit::BYTES as usize {
142                        digit_bytes[j - init_index] = slice[j];
143                        j += 1;
144                    }
145
146                    let digit = $Digit::from_le_bytes(digit_bytes);
147                    set_digit!(out_digits, i, digit, is_negative, sign_bits);
148                    i += 1;
149                }
150                if len & (digit::$Digit::BYTES as usize - 1) == 0 {
151                    Some(Self::from_bits($BUint::from_digits(out_digits)))
152                } else {
153                    let pad_byte = if is_negative { u8::MAX } else { 0 };
154                    let mut last_digit_bytes = [pad_byte; digit::$Digit::BYTES as usize];
155                    let addition = exact << digit::$Digit::BYTE_SHIFT;
156                    let mut j = 0;
157                    while j + addition < len {
158                        last_digit_bytes[j] = slice[j + addition];
159                        j += 1;
160                    }
161                    let digit = $Digit::from_le_bytes(last_digit_bytes);
162                    set_digit!(out_digits, i, digit, is_negative, sign_bits);
163                    Some(Self::from_bits($BUint::from_digits(out_digits)))
164                }
165            }
166        }
167    };
168}
169
170crate::macro_impl!(endian);