havok_types/
parse_int.rs
1use core::num::NonZeroU8;
4use lexical::NumberFormatBuilder;
5
6const BASE_FORMAT: NumberFormatBuilder = NumberFormatBuilder::new()
7 .integer_internal_digit_separator(true)
8 .digit_separator(NonZeroU8::new(b'_'));
9
10const BIN_FORMAT: u128 = BASE_FORMAT
11 .radix(2)
12 .base_prefix(NonZeroU8::new(b'b'))
13 .base_prefix(NonZeroU8::new(b'B'))
14 .build();
15const OCTAL_FORMAT: u128 = BASE_FORMAT
16 .radix(8)
17 .base_prefix(NonZeroU8::new(b'o'))
18 .base_prefix(NonZeroU8::new(b'O'))
19 .build();
20const HEX_FORMAT: u128 = BASE_FORMAT
21 .radix(16)
22 .base_prefix(NonZeroU8::new(b'x'))
23 .base_prefix(NonZeroU8::new(b'X'))
24 .build();
25
26#[inline]
28fn parse_base<T>(input: &str, options: &T::Options) -> Result<T, lexical::Error>
29where
30 T: lexical::FromLexicalWithOptions + lexical::FromLexical,
31{
32 let prefix = if input.len() >= 2 { &input[0..2] } else { "" };
33 let input = input.as_bytes();
34 match prefix {
35 "0b" | "0B" => T::from_lexical_with_options::<BIN_FORMAT>(input, options),
36 "0o" | "0O" => T::from_lexical_with_options::<OCTAL_FORMAT>(input, options),
37 "0x" | "0X" => T::from_lexical_with_options::<HEX_FORMAT>(input, options),
38 _ => lexical::parse(input),
39 }
40}
41
42use lexical::parse_integer_options::Options;
44
45pub trait ParseNumber: Sized {
49 fn parse(input: &str) -> Result<Self, lexical::Error>;
61
62 fn parse_wrapping(input: &str) -> Result<Self, lexical::Error>;
95}
96
97macro_rules! impl_parse_number {
99 ($($t:ty),*) => {
100 $(
101 impl ParseNumber for $t {
102 fn parse(input: &str) -> Result<Self, lexical::Error> {
103 parse_base(input, &Options::new())
104 }
105
106 fn parse_wrapping(input: &str) -> Result<Self, lexical::Error> {
107 if input.starts_with('-') {
108 let value = <i64 as ParseNumber>::parse(input)?;
110 Ok(value as Self)
111 } else {
112 let value = <u64 as ParseNumber>::parse(input)?;
114 Ok(value as Self)
115 }
116 }
117 }
118 )*
119 };
120}
121
122impl_parse_number!(
123 i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize
124);
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn parse_numbers() {
132 let inputs = [
134 ("0xFFFFF300", 4294963968_i64), ("0b11000", 24), ("0B1111_1111_1111_1111", 65535), ("0o11000", 4608), ("0O71432", 29466), ("01432", 1432), ("3432", 3432), ("0", 0), ];
143
144 for (input, expected) in inputs {
145 match <i64 as ParseNumber>::parse(input) {
146 Ok(result) => assert_eq!(result, expected, "Failed to parse input: {input}"),
147 Err(e) => panic!("Error parsing input {input}: {e}"),
148 }
149 }
150 }
151
152 #[test]
153 fn test_parse_wrapping_i16_u16() {
154 {
158 const OVERFLOW_U32_STR: &str = "0xFFFFF300";
161 assert_eq!(
162 <i16 as ParseNumber>::parse_wrapping(OVERFLOW_U32_STR).unwrap(),
163 -3328
164 );
165 assert_eq!(format!("{:#X}", (-3328_i16) as u32), OVERFLOW_U32_STR);
166 }
167
168 assert_eq!(
169 <u16 as ParseNumber>::parse_wrapping("0xFFFFF300").unwrap(),
170 0xF300
171 );
172
173 assert_eq!(
174 <i16 as ParseNumber>::parse_wrapping("0x7FFF").unwrap(),
175 32767
176 );
177 assert_eq!(
178 <u16 as ParseNumber>::parse_wrapping("0x7FFF").unwrap(),
179 32767
180 );
181
182 assert_eq!(
183 <i16 as ParseNumber>::parse_wrapping("0x8000").unwrap(),
184 -32768
185 );
186 assert_eq!(
187 <u16 as ParseNumber>::parse_wrapping("0x8000").unwrap(),
188 32768
189 );
190
191 assert_eq!(<i16 as ParseNumber>::parse_wrapping("0xFFFF").unwrap(), -1);
192 assert_eq!(
193 <u16 as ParseNumber>::parse_wrapping("0xFFFF").unwrap(),
194 65535
195 );
196
197 assert_eq!(<i16 as ParseNumber>::parse_wrapping("0x10000").unwrap(), 0);
198 assert_eq!(<u16 as ParseNumber>::parse_wrapping("0x10000").unwrap(), 0);
199 }
200
201 #[test]
202 fn test_parse_wrapping_i8_u8() {
203 assert_eq!(
205 <i8 as ParseNumber>::parse_wrapping("0b11111111").unwrap(),
206 -1
207 );
208 assert_eq!(
209 <u8 as ParseNumber>::parse_wrapping("0b11111111").unwrap(),
210 255
211 );
212
213 assert_eq!(
214 <i8 as ParseNumber>::parse_wrapping("0b10000000").unwrap(),
215 -128
216 );
217 assert_eq!(
218 <u8 as ParseNumber>::parse_wrapping("0b10000000").unwrap(),
219 128
220 );
221
222 assert_eq!(<i8 as ParseNumber>::parse_wrapping("0xFF").unwrap(), -1);
223 assert_eq!(<u8 as ParseNumber>::parse_wrapping("0xFF").unwrap(), 255);
224
225 assert_eq!(<i8 as ParseNumber>::parse_wrapping("0x100").unwrap(), 0);
226 assert_eq!(<u8 as ParseNumber>::parse_wrapping("0x100").unwrap(), 0);
227 }
228
229 #[test]
230 fn test_parse_wrapping_overflow() {
231 assert_eq!(<u8 as ParseNumber>::parse_wrapping("256").unwrap(), 0); assert_eq!(<i8 as ParseNumber>::parse_wrapping("-129").unwrap(), 127); }
235}