1 //! Enumerations for the sign-bit of a number.
2 
3 use super::format::NumberFormat;
4 use super::num::Number;
5 
6 // ENUMERATION
7 
8 /// Enumeration for the sign of a a number.
9 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
10 pub enum Sign {
11     /// Negative value.
12     Negative,
13     /// Positive value.
14     Positive,
15 }
16 
17 // HELPERS
18 
19 // Get if an option contains a digit separator.
20 #[inline(always)]
21 #[cfg(feature = "format")]
is_digit_separator(option: Option<&u8>, digit_separator: u8) -> bool22 fn is_digit_separator(option: Option<&u8>, digit_separator: u8) -> bool {
23     option == Some(&digit_separator)
24 }
25 
26 // Convert option of byte to option of sign.
27 #[inline(always)]
28 #[cfg(feature = "format")]
to_sign<T>(option: Option<&u8>) -> Option<Sign> where T: Number29 fn to_sign<T>(option: Option<&u8>)
30     -> Option<Sign>
31     where T: Number
32 {
33     match option {
34         Some(&b'+')                 => Some(Sign::Positive),
35         Some(&b'-') if T::IS_SIGNED => Some(Sign::Negative),
36         _                           => None
37     }
38 }
39 
40 // PARSE
41 
42 /// Find and parse sign without any possible digit separators.
43 #[inline(always)]
parse_sign_no_separator<'a, T>(bytes: &'a [u8], _: u8) -> (Sign, &'a [u8]) where T: Number44 pub(crate) fn parse_sign_no_separator<'a, T>(bytes: &'a [u8], _: u8)
45     -> (Sign, &'a [u8])
46     where T: Number
47 {
48     match bytes.get(0) {
49         Some(&b'+')                 => (Sign::Positive, &index!(bytes[1..])),
50         Some(&b'-') if T::IS_SIGNED => (Sign::Negative, &index!(bytes[1..])),
51         _                           => (Sign::Positive, bytes)
52     }
53 }
54 
55 /// Find and parse sign with leading and consecutive digit separators.
56 ///
57 /// We need to consider the following possibilities:
58 ///     1). _*[+-]\d+
59 #[inline(always)]
60 #[cfg(feature = "format")]
parse_sign_lc_separator<'a, T>(bytes: &'a [u8], digit_separator: u8) -> (Sign, &'a [u8]) where T: Number61 pub(crate) fn parse_sign_lc_separator<'a, T>(bytes: &'a [u8], digit_separator: u8)
62     -> (Sign, &'a [u8])
63     where T: Number
64 {
65     let mut index = 0;
66     while is_digit_separator(bytes.get(index), digit_separator) {
67         index += 1;
68     }
69     if let Some(sign) = to_sign::<T>(bytes.get(index)) {
70         (sign, &index!(bytes[index+1..]))
71     } else {
72         (Sign::Positive, bytes)
73     }
74 }
75 
76 /// Find and parse sign with leading digit separators.
77 ///
78 /// We need to consider the following possibilities:
79 ///     1). [+-]\d+
80 ///     2). _[+-]\d+
81 #[inline(always)]
82 #[cfg(feature = "format")]
parse_sign_l_separator<'a, T>(bytes: &'a [u8], digit_separator: u8) -> (Sign, &'a [u8]) where T: Number83 pub(crate) fn parse_sign_l_separator<'a, T>(bytes: &'a [u8], digit_separator: u8)
84     -> (Sign, &'a [u8])
85     where T: Number
86 {
87     let b0 = bytes.get(0);
88     if let Some(sign) = to_sign::<T>(b0) {
89         (sign, &index!(bytes[1..]))
90     } else if is_digit_separator(b0, digit_separator) {
91         if let Some(sign) = to_sign::<T>(bytes.get(1)) {
92             (sign, &index!(bytes[2..]))
93         } else {
94             (Sign::Positive, bytes)
95         }
96     } else {
97         (Sign::Positive, bytes)
98     }
99 }
100 
101 /// Find and parse sign with digit separators.
102 #[inline(always)]
103 #[cfg(feature = "format")]
parse_sign_separator<'a, T>(bytes: &'a [u8], format: NumberFormat) -> (Sign, &'a [u8]) where T: Number104 pub(crate) fn parse_sign_separator<'a, T>(bytes: &'a [u8], format: NumberFormat)
105     -> (Sign, &'a [u8])
106     where T: Number
107 {
108     // If the integer cannot have leading digit separators, we know the sign
109     // byte must by the first byte. Otherwise, we must consider digit separators
110     // before the sign byte.
111     let leading = format.integer_leading_digit_separator();
112     let consecutive = format.integer_consecutive_digit_separator();
113     match (leading, consecutive) {
114         (true, true)    => parse_sign_lc_separator::<T>(bytes, format.digit_separator()),
115         (true, false)   => parse_sign_l_separator::<T>(bytes, format.digit_separator()),
116         (false, _)      => parse_sign_no_separator::<T>(bytes, format.digit_separator())
117     }
118 }
119 
120 /// Find and parse sign.
121 #[inline]
parse_sign<'a, T>(bytes: &'a [u8], format: NumberFormat) -> (Sign, &'a [u8]) where T: Number122 pub(crate) fn parse_sign<'a, T>(bytes: &'a [u8], format: NumberFormat)
123     -> (Sign, &'a [u8])
124     where T: Number
125 {
126     #[cfg(not(feature = "format"))]
127     return parse_sign_no_separator::<T>(bytes, format.digit_separator());
128 
129     #[cfg(feature = "format")]
130     return parse_sign_separator::<T>(bytes, format);
131 }
132 
133 // TESTS
134 // -----
135 
136 #[cfg(test)]
137 mod tests {
138     use super::*;
139     use crate::util::test::*;
140 
141     #[test]
parse_sign_no_separator_test()142     fn parse_sign_no_separator_test() {
143         // Signed
144         assert_eq!(parse_sign_no_separator::<i32>(b"", b'_'), (Sign::Positive, b!("")));
145         assert_eq!(parse_sign_no_separator::<i32>(b"+", b'_'), (Sign::Positive, b!("")));
146         assert_eq!(parse_sign_no_separator::<i32>(b"-", b'_'), (Sign::Negative, b!("")));
147         assert_eq!(parse_sign_no_separator::<i32>(b"+5", b'_'), (Sign::Positive, b!("5")));
148         assert_eq!(parse_sign_no_separator::<i32>(b"-5", b'_'), (Sign::Negative, b!("5")));
149         assert_eq!(parse_sign_no_separator::<i32>(b"_-5", b'_'), (Sign::Positive, b!("_-5")));
150         assert_eq!(parse_sign_no_separator::<i32>(b"___-5", b'_'), (Sign::Positive, b!("___-5")));
151 
152         // Unsigned
153         assert_eq!(parse_sign_no_separator::<u32>(b"+5", b'_'), (Sign::Positive, b!("5")));
154         assert_eq!(parse_sign_no_separator::<u32>(b"-5", b'_'), (Sign::Positive, b!("-5")));
155     }
156 
157     #[test]
158     #[cfg(feature = "format")]
parse_sign_lc_separator_test()159     fn parse_sign_lc_separator_test() {
160         assert_eq!(parse_sign_lc_separator::<i32>(b"", b'_'), (Sign::Positive, b!("")));
161         assert_eq!(parse_sign_lc_separator::<i32>(b"+", b'_'), (Sign::Positive, b!("")));
162         assert_eq!(parse_sign_lc_separator::<i32>(b"-", b'_'), (Sign::Negative, b!("")));
163         assert_eq!(parse_sign_lc_separator::<i32>(b"+5", b'_'), (Sign::Positive, b!("5")));
164         assert_eq!(parse_sign_lc_separator::<i32>(b"-5", b'_'), (Sign::Negative, b!("5")));
165         assert_eq!(parse_sign_lc_separator::<i32>(b"_-5", b'_'), (Sign::Negative, b!("5")));
166         assert_eq!(parse_sign_lc_separator::<i32>(b"___-5", b'_'), (Sign::Negative, b!("5")));
167 
168         // Unsigned
169         assert_eq!(parse_sign_lc_separator::<u32>(b"___+5", b'_'), (Sign::Positive, b!("5")));
170         assert_eq!(parse_sign_lc_separator::<u32>(b"___-5", b'_'), (Sign::Positive, b!("___-5")));
171     }
172 
173     #[test]
174     #[cfg(feature = "format")]
parse_sign_l_separator_test()175     fn parse_sign_l_separator_test() {
176         assert_eq!(parse_sign_l_separator::<i32>(b"", b'_'), (Sign::Positive, b!("")));
177         assert_eq!(parse_sign_l_separator::<i32>(b"+", b'_'), (Sign::Positive, b!("")));
178         assert_eq!(parse_sign_l_separator::<i32>(b"-", b'_'), (Sign::Negative, b!("")));
179         assert_eq!(parse_sign_l_separator::<i32>(b"+5", b'_'), (Sign::Positive, b!("5")));
180         assert_eq!(parse_sign_l_separator::<i32>(b"-5", b'_'), (Sign::Negative, b!("5")));
181         assert_eq!(parse_sign_l_separator::<i32>(b"_-5", b'_'), (Sign::Negative, b!("5")));
182         assert_eq!(parse_sign_l_separator::<i32>(b"___-5", b'_'), (Sign::Positive, b!("___-5")));
183 
184         // Unsigned
185         assert_eq!(parse_sign_l_separator::<u32>(b"_+5", b'_'), (Sign::Positive, b!("5")));
186         assert_eq!(parse_sign_l_separator::<u32>(b"_-5", b'_'), (Sign::Positive, b!("_-5")));
187     }
188 
189     #[test]
190     #[cfg(feature = "format")]
parse_sign_separator_test()191     fn parse_sign_separator_test() {
192         let format = NumberFormat::ignore(b'_').unwrap();
193         assert_eq!(parse_sign_separator::<i32>(b"", format), (Sign::Positive, b!("")));
194         assert_eq!(parse_sign_separator::<i32>(b"+", format), (Sign::Positive, b!("")));
195         assert_eq!(parse_sign_separator::<i32>(b"-", format), (Sign::Negative, b!("")));
196         assert_eq!(parse_sign_separator::<i32>(b"+5", format), (Sign::Positive, b!("5")));
197         assert_eq!(parse_sign_separator::<i32>(b"-5", format), (Sign::Negative, b!("5")));
198         assert_eq!(parse_sign_separator::<i32>(b"_-5", format), (Sign::Negative, b!("5")));
199         assert_eq!(parse_sign_separator::<i32>(b"___-5", format), (Sign::Negative, b!("5")));
200 
201         // Unsigned
202         assert_eq!(parse_sign_separator::<u32>(b"__+5", format), (Sign::Positive, b!("5")));
203         assert_eq!(parse_sign_separator::<u32>(b"__-5", format), (Sign::Positive, b!("__-5")));
204     }
205 
206     #[test]
parse_sign_test()207     fn parse_sign_test() {
208         let format = NumberFormat::standard().unwrap();
209         assert_eq!(parse_sign::<i32>(b"", format), (Sign::Positive, b!("")));
210         assert_eq!(parse_sign::<i32>(b"+", format), (Sign::Positive, b!("")));
211         assert_eq!(parse_sign::<i32>(b"-", format), (Sign::Negative, b!("")));
212         assert_eq!(parse_sign::<i32>(b"+5", format), (Sign::Positive, b!("5")));
213         assert_eq!(parse_sign::<i32>(b"-5", format), (Sign::Negative, b!("5")));
214         assert_eq!(parse_sign::<i32>(b"_-5", format), (Sign::Positive, b!("_-5")));
215         assert_eq!(parse_sign::<i32>(b"___-5", format), (Sign::Positive, b!("___-5")));
216 
217         // Unsigned
218         assert_eq!(parse_sign::<u32>(b"+5", format), (Sign::Positive, b!("5")));
219         assert_eq!(parse_sign::<u32>(b"-5", format), (Sign::Positive, b!("-5")));
220     }
221 }
222