datetime_string/parse/
digits2.rs

1//! Parser functions for a string with 2 digits.
2
3/// Parses 2 digits effectively in little endian.
4///
5/// # Failures
6///
7/// Note that this returns meaningless value for strings with non-digit characters.
8/// This is safe in a sense that this never cause UB for any input, but callers
9/// should ensure that the bytes consists of only ASCII digits.
10#[cfg(any(not(target_endian = "big"), test))]
11#[must_use]
12fn parse_digits2_le(digits: [u8; 2]) -> u8 {
13    // Sample: digits == "12" (i.e. digits == [0x31, 0x32]).
14
15    // Sample: chunk == 0x32_31.
16    let chunk = u16::from_le_bytes(digits);
17
18    // Sample: chunk & 0x00_0f == 1.
19    // Sample: (chunk & 0x0f_00) == 0x02_00.
20    // Sample: (chunk & 0x0f_00) >> 8 == 2.
21    ((chunk & 0x00_0f) * 10 + ((chunk & 0x0f_00) >> 8)) as u8
22}
23
24/// Parses 2 digits effectively in big endian.
25///
26/// # Failures
27///
28/// Note that this returns meaningless value for strings with non-digit characters.
29/// This is safe in a sense that this never cause UB for any input, but callers
30/// should ensure that the bytes consists of only ASCII digits.
31#[cfg(any(target_endian = "big", test))]
32#[must_use]
33fn parse_digits2_be(digits: [u8; 2]) -> u8 {
34    // Sample: digits == "12" (i.e. digits == [0x31, 0x32]).
35
36    // Sample: chunk == 0x31_32.
37    let chunk = u16::from_be_bytes(digits);
38
39    // Sample: (chunk & 0x0f_00) == 0x01_00.
40    // Sample: (chunk & 0x0f_00) >> 8 == 1.
41    // Sample: chunk & 0x00_0f == 2.
42    (((chunk & 0x0f_00) >> 8) * 10 + (chunk & 0x00_0f)) as u8
43}
44
45/// Parses 2 digits effectively.
46///
47/// # Failures
48///
49/// Note that this returns meaningless value for strings with non-digit characters.
50/// This is safe in a sense that this never cause UB for any input, but callers
51/// should ensure that the bytes consists of only ASCII digits.
52#[inline]
53#[must_use]
54pub(crate) fn parse_digits2(digits: [u8; 2]) -> u8 {
55    #[cfg(not(target_endian = "big"))]
56    {
57        parse_digits2_le(digits)
58    }
59    #[cfg(target_endian = "big")]
60    {
61        parse_digits2_be(digits)
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    use core::convert::TryFrom;
70
71    #[cfg(feature = "alloc")]
72    use alloc::format;
73
74    fn as_digit2(s: &str) -> [u8; 2] {
75        TryFrom::try_from(s.as_bytes()).unwrap()
76    }
77
78    #[test]
79    fn digits2_le() {
80        #[cfg(not(feature = "alloc"))]
81        {
82            assert_eq!(parse_digits2_le(as_digit2("00")), 0);
83            assert_eq!(parse_digits2_le(as_digit2("01")), 1);
84            assert_eq!(parse_digits2_le(as_digit2("10")), 10);
85            assert_eq!(parse_digits2_le(as_digit2("12")), 12);
86            assert_eq!(parse_digits2_le(as_digit2("55")), 55);
87            assert_eq!(parse_digits2_le(as_digit2("99")), 99);
88        }
89        #[cfg(feature = "alloc")]
90        {
91            for i in 0_u8..=99_u8 {
92                let s = format!("{:02}", i);
93                assert_eq!(parse_digits2_le(as_digit2(&s)), i);
94            }
95        }
96    }
97
98    #[test]
99    fn digits2_be() {
100        #[cfg(not(feature = "alloc"))]
101        {
102            assert_eq!(parse_digits2_be(as_digit2("00")), 0);
103            assert_eq!(parse_digits2_be(as_digit2("01")), 1);
104            assert_eq!(parse_digits2_be(as_digit2("10")), 10);
105            assert_eq!(parse_digits2_be(as_digit2("12")), 12);
106            assert_eq!(parse_digits2_be(as_digit2("55")), 55);
107            assert_eq!(parse_digits2_be(as_digit2("99")), 99);
108        }
109        #[cfg(feature = "alloc")]
110        {
111            for i in 0_u8..=99_u8 {
112                let s = format!("{:02}", i);
113                assert_eq!(parse_digits2_be(as_digit2(&s)), i);
114            }
115        }
116    }
117
118    #[test]
119    fn digits2() {
120        #[cfg(not(feature = "alloc"))]
121        {
122            assert_eq!(parse_digits2(as_digit2("00")), 0);
123            assert_eq!(parse_digits2(as_digit2("01")), 1);
124            assert_eq!(parse_digits2(as_digit2("10")), 10);
125            assert_eq!(parse_digits2(as_digit2("12")), 12);
126            assert_eq!(parse_digits2(as_digit2("55")), 55);
127            assert_eq!(parse_digits2(as_digit2("99")), 99);
128        }
129        #[cfg(feature = "alloc")]
130        {
131            for i in 0_u8..=99_u8 {
132                let s = format!("{:02}", i);
133                assert_eq!(parse_digits2(as_digit2(&s)), i);
134            }
135        }
136    }
137}