Nothing to see here, move along
1pub fn parse_ip(s: &[u8]) -> Option<[u8; 4]> {
2 let mut octets = [0u8; 4];
3 let mut octet_idx = 0usize;
4 let mut current: u16 = 0;
5 let mut has_digit = false;
6
7 let result = (0..s.len()).try_fold((), |(), i| match s[i] {
8 b'0'..=b'9' => {
9 current = current * 10 + (s[i] - b'0') as u16;
10 has_digit = true;
11 match current > 255 {
12 true => None,
13 false => Some(()),
14 }
15 }
16 b'.' => match has_digit && octet_idx < 3 {
17 true => {
18 octets[octet_idx] = current as u8;
19 octet_idx += 1;
20 current = 0;
21 has_digit = false;
22 Some(())
23 }
24 false => None,
25 },
26 _ => None,
27 });
28
29 match result.is_some() && has_digit && octet_idx == 3 {
30 true => {
31 octets[3] = current as u8;
32 Some(octets)
33 }
34 false => None,
35 }
36}
37
38pub fn parse_decimal_u16(s: &[u8]) -> Option<u16> {
39 match s.is_empty() {
40 true => None,
41 false => {
42 let result = (0..s.len()).try_fold(0u32, |acc, i| match s[i] {
43 b'0'..=b'9' => {
44 let v = acc * 10 + (s[i] - b'0') as u32;
45 match v > 65535 {
46 true => None,
47 false => Some(v),
48 }
49 }
50 _ => None,
51 });
52 result.map(|v| v as u16)
53 }
54 }
55}
56
57pub fn skip_spaces(s: &[u8]) -> &[u8] {
58 match (0..s.len()).find(|&i| s[i] != b' ') {
59 Some(start) => &s[start..],
60 None => &[],
61 }
62}
63
64pub fn next_token(s: &[u8]) -> (&[u8], &[u8]) {
65 let trimmed = skip_spaces(s);
66 match (0..trimmed.len()).find(|&i| trimmed[i] == b' ') {
67 Some(pos) => (&trimmed[..pos], &trimmed[pos..]),
68 None => (trimmed, &[]),
69 }
70}
71
72pub fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
73 a.len() == b.len() && (0..a.len()).all(|i| a[i] == b[i])
74}
75
76pub fn write_u8_decimal(buf: &mut [u8], val: u8) -> usize {
77 match val {
78 0 => {
79 buf[0] = b'0';
80 1
81 }
82 _ => {
83 let mut digits = [0u8; 3];
84 let mut v = val;
85 let start = (0..3usize).rev().fold(3usize, |pos, _| match v {
86 0 => pos,
87 _ => {
88 let np = pos - 1;
89 digits[np] = b'0' + (v % 10);
90 v /= 10;
91 np
92 }
93 });
94 let len = 3 - start;
95 buf[..len].copy_from_slice(&digits[start..]);
96 len
97 }
98 }
99}
100
101pub fn write_u16_decimal(buf: &mut [u8], val: u16) -> usize {
102 match val {
103 0 => {
104 buf[0] = b'0';
105 1
106 }
107 _ => {
108 let mut digits = [0u8; 5];
109 let mut v = val;
110 let start = (0..5usize).rev().fold(5usize, |pos, _| match v {
111 0 => pos,
112 _ => {
113 let np = pos - 1;
114 digits[np] = b'0' + (v % 10) as u8;
115 v /= 10;
116 np
117 }
118 });
119 let len = 5 - start;
120 buf[..len].copy_from_slice(&digits[start..]);
121 len
122 }
123 }
124}
125
126pub fn write_ip_decimal(buf: &mut [u8], octets: &[u8]) -> usize {
127 let mut pos = 0usize;
128 pos += write_u8_decimal(&mut buf[pos..], octets[0]);
129 buf[pos] = b'.';
130 pos += 1;
131 pos += write_u8_decimal(&mut buf[pos..], octets[1]);
132 buf[pos] = b'.';
133 pos += 1;
134 pos += write_u8_decimal(&mut buf[pos..], octets[2]);
135 buf[pos] = b'.';
136 pos += 1;
137 pos += write_u8_decimal(&mut buf[pos..], octets[3]);
138 pos
139}
140
141pub fn write_ipv6(buf: &mut [u8], octets: &[u8]) -> usize {
142 let groups: [u16; 8] = [
143 (octets[0] as u16) << 8 | octets[1] as u16,
144 (octets[2] as u16) << 8 | octets[3] as u16,
145 (octets[4] as u16) << 8 | octets[5] as u16,
146 (octets[6] as u16) << 8 | octets[7] as u16,
147 (octets[8] as u16) << 8 | octets[9] as u16,
148 (octets[10] as u16) << 8 | octets[11] as u16,
149 (octets[12] as u16) << 8 | octets[13] as u16,
150 (octets[14] as u16) << 8 | octets[15] as u16,
151 ];
152
153 let (best_start, best_len) = (0u8..8).fold((255u8, 0u8), |(bs, bl), i| {
154 let run = (i..8).take_while(|&j| groups[j as usize] == 0).count() as u8;
155 match run > bl && run >= 2 {
156 true => (i, run),
157 false => (bs, bl),
158 }
159 });
160
161 let mut pos = 0usize;
162 let mut i = 0u8;
163 core::iter::from_fn(|| match i < 8 {
164 false => None,
165 true => {
166 match best_len > 0 && i == best_start {
167 true => {
168 if i == 0 {
169 buf[pos] = b':';
170 pos += 1;
171 }
172 buf[pos] = b':';
173 pos += 1;
174 i += best_len;
175 }
176 false => {
177 if i > 0 {
178 buf[pos] = b':';
179 pos += 1;
180 }
181 pos += write_hex_u16(&mut buf[pos..], groups[i as usize]);
182 i += 1;
183 }
184 }
185 Some(())
186 }
187 })
188 .count();
189
190 pos
191}
192
193pub fn parse_ipv6(s: &[u8]) -> Option<[u8; 16]> {
194 if s.is_empty() || s.len() > 39 {
195 return None;
196 }
197
198 let mut groups = [0u16; 8];
199 let mut group_count = 0u8;
200 let mut expand_pos: Option<u8> = None;
201 let mut current: u16 = 0;
202 let mut digits_in_group = 0u8;
203 let mut after_colon = false;
204
205 let result = (0..s.len()).try_fold((), |(), i| {
206 let ch = s[i];
207 match ch {
208 b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => {
209 let nibble = match ch {
210 b'0'..=b'9' => ch - b'0',
211 b'a'..=b'f' => ch - b'a' + 10,
212 _ => ch - b'A' + 10,
213 };
214 match digits_in_group >= 4 {
215 true => return None,
216 false => {
217 current = (current << 4) | nibble as u16;
218 digits_in_group += 1;
219 after_colon = false;
220 }
221 }
222 Some(())
223 }
224 b':' => match after_colon {
225 true => match expand_pos.is_some() {
226 true => None,
227 false => {
228 expand_pos = Some(group_count);
229 after_colon = false;
230 Some(())
231 }
232 },
233 false => {
234 match digits_in_group > 0 {
235 true => match group_count >= 8 {
236 true => return None,
237 false => {
238 groups[group_count as usize] = current;
239 group_count += 1;
240 current = 0;
241 digits_in_group = 0;
242 }
243 },
244 false => match i == 0 {
245 true => {}
246 false => return None,
247 },
248 }
249 after_colon = true;
250 Some(())
251 }
252 },
253 _ => None,
254 }
255 });
256
257 result?;
258
259 if digits_in_group > 0 {
260 match group_count >= 8 {
261 true => return None,
262 false => {
263 groups[group_count as usize] = current;
264 group_count += 1;
265 }
266 }
267 }
268
269 match expand_pos {
270 Some(pos) => {
271 if group_count > 8 {
272 return None;
273 }
274 let tail_len = group_count - pos;
275 let mut expanded = [0u16; 8];
276 (0..pos as usize).for_each(|i| expanded[i] = groups[i]);
277 (0..tail_len as usize).for_each(|i| {
278 expanded[8 - tail_len as usize + i] = groups[pos as usize + i];
279 });
280 groups = expanded;
281 }
282 None => {
283 if group_count != 8 {
284 return None;
285 }
286 }
287 }
288
289 let mut octets = [0u8; 16];
290 (0..8).for_each(|i| {
291 octets[i * 2] = (groups[i] >> 8) as u8;
292 octets[i * 2 + 1] = groups[i] as u8;
293 });
294 Some(octets)
295}
296
297pub fn write_hex_u16(buf: &mut [u8], val: u16) -> usize {
298 let hex = |n: u8| match n < 10 {
299 true => b'0' + n,
300 false => b'a' + (n - 10),
301 };
302 match val {
303 0 => {
304 buf[0] = b'0';
305 1
306 }
307 _ => {
308 let mut digits = [0u8; 4];
309 let mut v = val;
310 let start = (0..4usize).rev().fold(4usize, |p, _| match v {
311 0 => p,
312 _ => {
313 let np = p - 1;
314 digits[np] = hex((v & 0xf) as u8);
315 v >>= 4;
316 np
317 }
318 });
319 let len = 4 - start;
320 buf[..len].copy_from_slice(&digits[start..]);
321 len
322 }
323 }
324}