1use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7use crate::number::is_space;
8
9#[derive(Clone, Copy)]
11enum Part {
12 Hex(usize),
14 Char(u8),
16}
17
18use Part::{Char, Hex};
19
20const FORMATS: [[Part; 11]; 7] = [
22 [
23 Hex(0),
24 Char(b':'),
25 Hex(0),
26 Char(b':'),
27 Hex(0),
28 Char(b':'),
29 Hex(0),
30 Char(b':'),
31 Hex(0),
32 Char(b':'),
33 Hex(0),
34 ],
35 [
36 Hex(0),
37 Char(b'-'),
38 Hex(0),
39 Char(b'-'),
40 Hex(0),
41 Char(b'-'),
42 Hex(0),
43 Char(b'-'),
44 Hex(0),
45 Char(b'-'),
46 Hex(0),
47 ],
48 [
49 Hex(2),
50 Hex(2),
51 Hex(2),
52 Char(b':'),
53 Hex(2),
54 Hex(2),
55 Hex(2),
56 Char(0),
57 Char(0),
58 Char(0),
59 Char(0),
60 ],
61 [
62 Hex(2),
63 Hex(2),
64 Hex(2),
65 Char(b'-'),
66 Hex(2),
67 Hex(2),
68 Hex(2),
69 Char(0),
70 Char(0),
71 Char(0),
72 Char(0),
73 ],
74 [
75 Hex(2),
76 Hex(2),
77 Char(b'.'),
78 Hex(2),
79 Hex(2),
80 Char(b'.'),
81 Hex(2),
82 Hex(2),
83 Char(0),
84 Char(0),
85 Char(0),
86 ],
87 [
88 Hex(2),
89 Hex(2),
90 Char(b'-'),
91 Hex(2),
92 Hex(2),
93 Char(b'-'),
94 Hex(2),
95 Hex(2),
96 Char(0),
97 Char(0),
98 Char(0),
99 ],
100 [Hex(2), Hex(2), Hex(2), Hex(2), Hex(2), Hex(2), Char(0), Char(0), Char(0), Char(0), Char(0)],
101];
102
103fn scan_hex(b: &[u8], i: &mut usize, width: usize) -> Option<i32> {
105 let at = |i: usize| b.get(i).copied().unwrap_or(0);
106 while is_space(at(*i)) {
107 *i += 1;
108 }
109 let mut left = if width == 0 { usize::MAX } else { width };
110 let negative = at(*i) == b'-';
111 if negative || at(*i) == b'+' {
112 *i += 1;
113 left -= 1;
114 }
115 let mut digits = 0;
116 let mut value = 0u64;
117 let mut overflow = false;
118 if left != 0 && at(*i) == b'0' {
119 *i += 1;
120 left -= 1;
121 digits += 1;
122 if left != 0 && matches!(at(*i), b'x' | b'X') {
123 *i += 1;
124 left -= 1;
125 }
126 }
127 while left != 0 && at(*i).is_ascii_hexdigit() {
128 let digit = u64::from(char::from(at(*i)).to_digit(16).unwrap_or(0));
129 match value.checked_mul(16).and_then(|v| v.checked_add(digit)) {
130 Some(v) => value = v,
131 None => overflow = true,
132 }
133 *i += 1;
134 left -= 1;
135 digits += 1;
136 }
137 if digits == 0 {
138 return None;
139 }
140 let value = if overflow {
142 u64::MAX
143 } else if negative {
144 value.wrapping_neg()
145 } else {
146 value
147 };
148 u32::try_from(value & u64::from(u32::MAX)).ok().map(u32::cast_signed)
149}
150
151fn scan(s: &[u8], format: &[Part]) -> Option<[i32; 6]> {
153 let mut out = [0; 6];
154 let mut count = 0;
155 let mut i = 0;
156 for part in format {
157 match *part {
158 Hex(width) => {
159 out[count] = scan_hex(s, &mut i, width)?;
160 count += 1;
161 }
162 Char(0) => break,
163 Char(c) => {
164 if s.get(i) != Some(&c) {
165 return None;
166 }
167 i += 1;
168 }
169 }
170 }
171 while i < s.len() && is_space(s[i]) {
172 i += 1;
173 }
174 (i == s.len()).then_some(out)
175}
176
177pub fn macaddr_in(s: &str) -> Result<u64, TypeError> {
183 let numbers = FORMATS
184 .iter()
185 .find_map(|format| scan(s.as_bytes(), format))
186 .ok_or_else(|| TypeError::syntax("macaddr", s))?;
187 let mut value = 0u64;
188 for n in numbers {
189 let byte = u8::try_from(n).map_err(|_| {
190 TypeError::new(
191 SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
192 format!("invalid octet value in \"macaddr\" value: \"{s}\""),
193 )
194 })?;
195 value = value << 8 | u64::from(byte);
196 }
197 Ok(value)
198}
199
200fn bytes(value: u64, count: usize) -> impl Iterator<Item = u8> {
202 value.to_be_bytes().into_iter().skip(8 - count)
203}
204
205fn hex_out(value: u64, count: usize, out: &mut Vec<u8>) {
207 const HEX: &[u8; 16] = b"0123456789abcdef";
208 for (i, byte) in bytes(value, count).enumerate() {
209 if i > 0 {
210 out.push(b':');
211 }
212 out.push(HEX[usize::from(byte >> 4)]);
213 out.push(HEX[usize::from(byte & 15)]);
214 }
215}
216
217pub fn macaddr_out(value: u64, out: &mut Vec<u8>) {
219 hex_out(value, 6, out);
220}
221
222pub fn macaddr8_out(value: u64, out: &mut Vec<u8>) {
224 hex_out(value, 8, out);
225}
226
227pub fn macaddr8_in(s: &str) -> Result<u64, TypeError> {
233 let fail = || TypeError::syntax("macaddr8", s);
234 let b = s.as_bytes();
235 let at = |i: usize| b.get(i).copied().unwrap_or(0);
236 let mut i = 0;
237 while is_space(at(i)) {
238 i += 1;
239 }
240 let mut bytes = [0u8; 8];
241 let mut count = 0;
242 let mut spacer = 0;
243 while at(i) != 0 && at(i + 1) != 0 {
244 if count == 8 {
245 return Err(fail());
246 }
247 let digit = |c: u8| char::from(c).to_digit(16).and_then(|d| u8::try_from(d).ok());
248 let (Some(high), Some(low)) = (digit(at(i)), digit(at(i + 1))) else { return Err(fail()) };
249 bytes[count] = high << 4 | low;
250 count += 1;
251 i += 2;
252 if matches!(at(i), b':' | b'-' | b'.') {
253 if spacer == 0 {
254 spacer = at(i);
255 } else if spacer != at(i) {
256 return Err(fail());
257 }
258 i += 1;
259 }
260 if (count == 6 || count == 8) && is_space(at(i)) {
261 while is_space(at(i)) {
262 i += 1;
263 }
264 if at(i) != 0 {
265 return Err(fail());
266 }
267 }
268 }
269 let bytes = match count {
270 6 => [bytes[0], bytes[1], bytes[2], 0xff, 0xfe, bytes[3], bytes[4], bytes[5]],
271 8 => bytes,
272 _ => return Err(fail()),
273 };
274 Ok(u64::from_be_bytes(bytes))
275}
276
277pub fn macaddr_recv(recv: &mut Recv<'_>) -> Result<u64, TypeError> {
283 let mut value = 0;
284 for _ in 0..6 {
285 value = value << 8 | u64::from(recv.byte()?);
286 }
287 Ok(value)
288}
289
290pub fn macaddr8_recv(recv: &mut Recv<'_>) -> Result<u64, TypeError> {
296 if recv.remaining() == 6 {
297 return macaddr_recv(recv).map(macaddr_to_macaddr8);
298 }
299 let mut value = 0;
300 for _ in 0..8 {
301 value = value << 8 | u64::from(recv.byte()?);
302 }
303 Ok(value)
304}
305
306#[must_use]
308pub fn macaddr_to_macaddr8(value: u64) -> u64 {
309 (value >> 24) << 40 | 0xfffe << 24 | value & 0xff_ffff
310}
311
312#[must_use]
314pub fn macaddr8_to_macaddr(value: u64) -> Option<u64> {
315 (value >> 24 & 0xffff == 0xfffe).then_some((value >> 40) << 24 | value & 0xff_ffff)
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 fn text(f: fn(u64, &mut Vec<u8>), value: u64) -> String {
323 let mut out = Vec::new();
324 f(value, &mut out);
325 String::from_utf8(out).unwrap()
326 }
327
328 #[test]
330 fn text_forms() {
331 for input in [
332 "08:00:2b:01:02:03",
333 "08-00-2b-01-02-03",
334 "08002b:010203",
335 "08002b-010203",
336 "0800.2b01.0203",
337 "0800-2b01-0203",
338 "08002b010203",
339 " 08:00:2b:01:02:03 ",
340 "8:0:2b:1:2:3",
341 ] {
342 assert_eq!(
343 text(macaddr_out, macaddr_in(input).unwrap()),
344 "08:00:2b:01:02:03",
345 "{input}"
346 );
347 }
348 assert_eq!(
349 text(macaddr_out, macaddr_in("AB:CD:EF:01:02:03").unwrap()),
350 "ab:cd:ef:01:02:03"
351 );
352 for input in
353 ["08:00:2b:01:02", "08:00:2b:01:02:03:04", "08:00:2b:01:02:0g", "08:00-2b:01:02:03", ""]
354 {
355 let error = macaddr_in(input).unwrap_err();
356 assert_eq!(error.sqlstate.as_str(), "22P02", "{input}");
357 }
358 let error = macaddr_in("08:00:2b:01:02:100").unwrap_err();
359 assert_eq!(error.sqlstate.as_str(), "22003");
360 assert_eq!(
361 error.message,
362 "invalid octet value in \"macaddr\" value: \"08:00:2b:01:02:100\""
363 );
364
365 let mac8 = |s: &str| text(macaddr8_out, macaddr8_in(s).unwrap());
366 assert_eq!(mac8("08:00:2b:01:02:03"), "08:00:2b:ff:fe:01:02:03");
367 for input in [
368 "08:00:2b:01:02:03:04:05",
369 "08-00-2b-01-02-03-04-05",
370 "08002b0102030405",
371 "0800.2b01.0203.0405",
372 "08002b:0102030405",
373 "08:00:2b:01:02:03:04:05 ",
374 "0800:2b01:0203:0405",
375 ] {
376 assert_eq!(mac8(input), "08:00:2b:01:02:03:04:05", "{input}");
377 }
378 for input in ["08:00:2b:01:02:03:04", "08:00:2b:01:02:03:04:05:06", "x", "08:00:2b:01:02:3"]
379 {
380 assert_eq!(
381 macaddr8_in(input).unwrap_err().message,
382 format!("invalid input syntax for type macaddr8: \"{input}\"")
383 );
384 }
385 }
386
387 #[test]
388 fn binary_forms() {
389 let six = [8, 0, 0x2b, 1, 2, 3];
390 assert_eq!(macaddr_recv(&mut Recv::new(&six)).unwrap(), 0x0800_2b01_0203);
391 assert_eq!(macaddr8_recv(&mut Recv::new(&six)).unwrap(), 0x0800_2bff_fe01_0203);
392 let eight = [8, 0, 0x2b, 1, 2, 3, 4, 5];
393 assert_eq!(macaddr8_recv(&mut Recv::new(&eight)).unwrap(), 0x0800_2b01_0203_0405);
394 assert!(macaddr_recv(&mut Recv::new(&six[..5])).is_err());
395 }
396
397 #[test]
398 fn casts() {
399 let six = macaddr_in("08:00:2b:01:02:03").unwrap();
400 assert_eq!(text(macaddr8_out, macaddr_to_macaddr8(six)), "08:00:2b:ff:fe:01:02:03");
401 assert_eq!(macaddr8_to_macaddr(macaddr_to_macaddr8(six)), Some(six));
402 assert_eq!(macaddr8_to_macaddr(macaddr8_in("08:00:2b:01:02:03:04:05").unwrap()), None);
403 }
404}