datetime_string/parse/digits4.rs
1//! Parser functions for a string with 4 digits.
2
3/// Parses 4 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_digits4_le(digits: [u8; 4]) -> u16 {
13 // Sample: digits == "1234" (i.e. digits == [0x31, 0x32, 0x33, 0x34]).
14
15 // Sample: chunk1 == 0x34_33_32_31.
16 let chunk1 = u32::from_le_bytes(digits);
17
18 /// Mask for 2nd and 4th significant digits.
19 const LOWER_MASK_1: u32 = 0x0f_00_0f_00;
20 /// Mask for 1st and 3rd significant digits.
21 const UPPER_MASK_1: u32 = 0x00_0f_00_0f;
22
23 // Sample: (chunk1 & LOWER_MASK_1) == 0x04_00_02_00.
24 // Sample: (chunk1 & LOWER_MASK_1) >> 8 == 0x00_04_00_02 (i.e. u32::from_be([0, 4, 0, 2]).
25 let chunk1_lower = (chunk1 & LOWER_MASK_1) >> 8;
26 // Sample: (chunk1 & UPPER_MASK_1) == 0x00_03_00_01 (i.e. u32::from_be([0x00, 0x03, 0x00, 0x01])).
27 // Sample: (chunk1 & UPPER_MASK_1) * 10 == u32::from_be([0, 30, 0, 10]).
28 let chunk1_upper = (chunk1 & UPPER_MASK_1) * 10;
29
30 // Sample: chunk1_lower + chunk1_upper == (4 + 30) << 16 + (2 + 10).
31 let chunk2: u32 = chunk1_lower + chunk1_upper;
32
33 // Sample: (chunk2 >> 16) as u16 == 4 + 30.
34 // Sample: chunk2 * 100 == (2 + 10) * 100 + (((4 + 30) << 16) * 100).
35 // Sample: (chunk2 * 100) as u16 == (2 + 10) * 100.
36 // Here `(chunk2 * 100) as u16` can be `chunk2 * 100`, because `99 * 100` is representable in
37 // 16-bits.
38 ((chunk2 >> 16) + (chunk2 * 100)) as u16
39}
40
41/// Parses 4 digits effectively in big endian.
42///
43/// # Failures
44///
45/// Note that this returns meaningless value for strings with non-digit characters.
46/// This is safe in a sense that this never cause UB for any input, but callers
47/// should ensure that the bytes consists of only ASCII digits.
48#[cfg(any(target_endian = "big", test))]
49#[must_use]
50fn parse_digits4_be(digits: [u8; 4]) -> u16 {
51 // Sample: digits == "1234" (i.e. digits == [0x31, 0x32, 0x33, 0x34]).
52
53 // Sample: chunk1 == 0x31_32_33_34.
54 let chunk1 = u32::from_be_bytes(digits);
55
56 /// Mask for 2nd and 4th significant digits.
57 const LOWER_MASK_1: u32 = 0x00_0f_00_0f;
58 /// Mask for 1st and 3rd significant digits.
59 const UPPER_MASK_1: u32 = 0x0f_00_0f_00;
60
61 // Sample: (chunk1 & LOWER_MASK_1) == 0x00_02_00_04 (i.e. u32::from_be([0, 2, 0, 4]).
62 let chunk1_lower = chunk1 & LOWER_MASK_1;
63 // Sample: (chunk1 & UPPER_MASK_1) == 0x01_00_03_00.
64 // Sample: (chunk1 & UPPER_MASK_1) >> 8 == 0x00_01_00_03.
65 // Sample: ((chunk1 & UPPER_MASK_1) >> 8) * 10 == u32::from_be([0, 10, 0, 30]).
66 let chunk1_upper = ((chunk1 & UPPER_MASK_1) >> 8) * 10;
67
68 // Sample: chunk1_lower + chunk1_upper == (2 + 10) << 16 + (4 + 30).
69 let chunk2: u32 = chunk1_lower + chunk1_upper;
70
71 // Sample: chunk2 = (2 + 10) << 16 + (4 + 30).
72 // Sample: (chunk2 >> 16) as u16 == 2 + 10.
73 // Sample: (chunk2 >> 16) * 100 as u16 == (2 + 10) * 100.
74 // Sample: chunk2 as u16 = 4 + 30.
75 ((chunk2 >> 16) * 100 + chunk2) as u16
76}
77
78/// Parses 4 digits effectively.
79///
80/// # Failures
81///
82/// Note that this returns meaningless value for strings with non-digit characters.
83/// This is safe in a sense that this never cause UB for any input, but callers
84/// should ensure that the bytes consists of only ASCII digits.
85#[inline]
86#[must_use]
87pub(crate) fn parse_digits4(digits: [u8; 4]) -> u16 {
88 #[cfg(not(target_endian = "big"))]
89 {
90 parse_digits4_le(digits)
91 }
92 #[cfg(target_endian = "big")]
93 {
94 parse_digits4_be(digits)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 use core::convert::TryFrom;
103
104 fn as_digit4(s: &str) -> [u8; 4] {
105 TryFrom::try_from(s.as_bytes()).unwrap()
106 }
107
108 #[test]
109 fn digits4_le() {
110 assert_eq!(parse_digits4_le(as_digit4("0000")), 0000);
111 assert_eq!(parse_digits4_le(as_digit4("1234")), 1234);
112 assert_eq!(parse_digits4_le(as_digit4("8765")), 8765);
113 assert_eq!(parse_digits4_le(as_digit4("9999")), 9999);
114 }
115
116 #[test]
117 fn digits4_be() {
118 assert_eq!(parse_digits4_be(as_digit4("0000")), 0000);
119 assert_eq!(parse_digits4_be(as_digit4("1234")), 1234);
120 assert_eq!(parse_digits4_be(as_digit4("8765")), 8765);
121 assert_eq!(parse_digits4_be(as_digit4("9999")), 9999);
122 }
123
124 #[test]
125 fn digits4() {
126 assert_eq!(parse_digits4(as_digit4("0000")), 0000);
127 assert_eq!(parse_digits4(as_digit4("1234")), 1234);
128 assert_eq!(parse_digits4(as_digit4("8765")), 8765);
129 assert_eq!(parse_digits4(as_digit4("9999")), 9999);
130 }
131}