1use alloc::vec::Vec;
9
10use crate::tchar::Tchar;
11
12const PARSE_BUFFER: usize = 128;
16
17pub(crate) fn parse_window(units: &[Tchar]) -> &[Tchar] {
19 &units[..units.len().min(PARSE_BUFFER - 1)]
20}
21
22#[inline]
24fn at(s: &[Tchar], i: usize) -> u32 {
25 s.get(i).map_or(0, |&c| u32::from(c))
26}
27
28fn scan(s: &[Tchar]) -> (isize, usize) {
33 const ZERO: u32 = b'0' as u32;
34 let mut v: isize = 0;
35
36 if at(s, 0) == ZERO && (at(s, 1) == b'x' as u32 || at(s, 1) == b'X' as u32) {
38 let mut i = 1;
39 loop {
40 i += 1;
41 let c = at(s, i);
42 let digit = match c {
43 0x30..=0x39 => c - 0x30,
44 0x61..=0x66 => c - 0x61 + 10,
45 0x41..=0x46 => c - 0x41 + 10,
46 _ => break,
47 };
48 v = (v << 4).wrapping_add(digit as isize);
49 }
50 return (v, i);
51 }
52
53 if at(s, 0) == ZERO && (ZERO..=b'7' as u32).contains(&at(s, 1)) {
55 let mut i = 0;
56 loop {
57 i += 1;
58 let c = at(s, i);
59 if !(ZERO..=b'7' as u32).contains(&c) {
60 break;
61 }
62 v = (v << 3).wrapping_add((c - ZERO) as isize);
63 }
64 return (v, i);
65 }
66
67 let negative = at(s, 0) == b'-' as u32;
70 let mut i = usize::from(negative);
71 loop {
72 let c = at(s, i);
73 if !(ZERO..=b'9' as u32).contains(&c) {
74 break;
75 }
76 v = v.wrapping_mul(10).wrapping_add((c - ZERO) as isize);
77 i += 1;
78 }
79 if negative {
80 v = v.wrapping_neg();
81 }
82 (v, i)
83}
84
85pub fn str_to_ptr(s: &[Tchar]) -> isize {
87 scan(s).0
88}
89
90pub fn atoi_or(s: &[Tchar]) -> isize {
95 let mut acc: isize = 0;
96 let mut rest = s;
97 loop {
98 let (v, end) = scan(rest);
99 acc |= v;
100 if at(rest, end) != b'|' as u32 {
101 return acc;
102 }
103 rest = &rest[end + 1..];
104 }
105}
106
107pub fn format(v: isize) -> Vec<Tchar> {
109 let negative = v < 0;
111 let mut magnitude = if negative {
112 (v as usize).wrapping_neg()
113 } else {
114 v as usize
115 };
116
117 let mut digits = [0u8; 24];
118 let mut n = 0;
119 loop {
120 digits[n] = b'0' + (magnitude % 10) as u8;
121 magnitude /= 10;
122 n += 1;
123 if magnitude == 0 {
124 break;
125 }
126 }
127
128 let mut out = Vec::with_capacity(n + 1);
129 if negative {
130 out.push(b'-' as Tchar);
131 }
132 for i in (0..n).rev() {
133 out.push(digits[i] as Tchar);
134 }
135 out
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::tchar::encode;
142
143 fn parse(s: &str) -> isize {
144 str_to_ptr(&encode(s))
145 }
146
147 fn parse_or(s: &str) -> isize {
148 atoi_or(&encode(s))
149 }
150
151 #[test]
152 fn decimal() {
153 assert_eq!(parse("0"), 0);
154 assert_eq!(parse("42"), 42);
155 assert_eq!(parse("-42"), -42);
156 assert_eq!(parse("9abc"), 9);
157 }
158
159 #[test]
160 fn hexadecimal() {
161 assert_eq!(parse("0x1f"), 31);
162 assert_eq!(parse("0X1F"), 31);
163 assert_eq!(parse("0xdeadg"), 0xdead);
164 assert_eq!(parse("0x"), 0);
166 }
167
168 #[test]
169 fn octal() {
170 assert_eq!(parse("0755"), 0o755);
171 assert_eq!(parse("017"), 15);
172 assert_eq!(parse("08"), 8);
175 }
176
177 #[test]
178 fn garbage_is_zero_not_an_error() {
179 assert_eq!(parse(""), 0);
180 assert_eq!(parse("hello"), 0);
181 assert_eq!(parse("-"), 0);
182 assert_eq!(parse(" 12"), 0);
183 }
184
185 #[test]
186 fn ored_forms() {
187 assert_eq!(parse_or("2|4|8"), 14);
188 assert_eq!(parse_or("0x1|0x2"), 3);
189 assert_eq!(parse_or("5"), 5);
190 assert_eq!(parse_or("1|"), 1);
192 }
193
194 #[test]
195 fn or_is_not_applied_by_the_plain_parser() {
196 assert_eq!(parse("2|4|8"), 2);
197 }
198
199 #[test]
200 fn round_trip() {
201 for v in [0isize, 1, -1, 12345, -12345, isize::MAX, isize::MIN] {
202 assert_eq!(str_to_ptr(&format(v)), v, "round-tripping {v}");
203 }
204 }
205}