xsd_types/lexical/
double.rs

1use super::{lexical_form, Decimal, Float, FloatBuf, Integer, Overflow};
2use std::borrow::{Borrow, ToOwned};
3use std::fmt;
4use std::hash::Hash;
5
6lexical_form! {
7	/// Double number.
8	///
9	/// See: <https://www.w3.org/TR/xmlschema-2/#double>
10	ty: Double,
11
12	/// Owned double number.
13	///
14	/// See: <https://www.w3.org/TR/xmlschema-2/#double>
15	buffer: DoubleBuf,
16
17	/// Creates a new double from a string.
18	///
19	/// If the input string is ot a [valid XSD double](https://www.w3.org/TR/xmlschema-2/#double),
20	/// an [`InvalidDouble`] error is returned.
21	new,
22
23	/// Creates a new double from a string without checking it.
24	///
25	/// # Safety
26	///
27	/// The input string must be a [valid XSD double](https://www.w3.org/TR/xmlschema-2/#double).
28	new_unchecked,
29
30	value: crate::Double,
31	error: InvalidDouble,
32	as_ref: as_double,
33	parent_forms: {}
34}
35
36pub const NAN: &Double = unsafe { Double::new_unchecked_from_slice(b"NaN") };
37pub const POSITIVE_INFINITY: &Double = unsafe { Double::new_unchecked_from_slice(b"INF") };
38pub const NEGATIVE_INFINITY: &Double = unsafe { Double::new_unchecked_from_slice(b"-INF") };
39
40impl Double {
41	pub fn is_infinite(&self) -> bool {
42		matches!(&self.0, b"INF" | b"-INF")
43	}
44
45	pub fn is_finite(&self) -> bool {
46		!matches!(&self.0, b"INF" | b"-INF" | b"NaN")
47	}
48
49	pub fn is_nan(&self) -> bool {
50		&self.0 == b"NaN"
51	}
52
53	fn exponent_separator_index(&self) -> Option<usize> {
54		for (i, c) in self.0.iter().enumerate() {
55			if matches!(c, b'e' | b'E') {
56				return Some(i);
57			}
58		}
59
60		None
61	}
62
63	pub fn mantissa(&self) -> Option<&Decimal> {
64		if self.is_finite() {
65			Some(match self.exponent_separator_index() {
66				Some(e) => unsafe { Decimal::new_unchecked(&self[..e]) },
67				None => unsafe { Decimal::new_unchecked(self) },
68			})
69		} else {
70			None
71		}
72	}
73
74	pub fn exponent(&self) -> Option<&Integer> {
75		if self.is_finite() {
76			self.exponent_separator_index()
77				.map(|e| unsafe { Integer::new_unchecked(&self[(e + 1)..]) })
78		} else {
79			None
80		}
81	}
82
83	pub fn value(&self) -> crate::Double {
84		self.into()
85	}
86}
87
88impl PartialEq for Double {
89	fn eq(&self, other: &Self) -> bool {
90		self.as_bytes() == other.as_bytes()
91	}
92}
93
94impl Eq for Double {}
95
96impl Hash for Double {
97	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
98		self.as_bytes().hash(state)
99	}
100}
101
102macro_rules! integer_conversion {
103	{ $($ty:ty),* } => {
104		$(
105			impl From<$ty> for DoubleBuf {
106				fn from(i: $ty) -> Self {
107					unsafe { DoubleBuf::new_unchecked(i.to_string()) }
108				}
109			}
110
111			impl<'a> TryFrom<&'a Double> for $ty {
112				type Error = Overflow;
113
114				fn try_from(i: &'a Double) -> Result<Self, Overflow> {
115					i.as_str().parse().map_err(|_| Overflow)
116				}
117			}
118
119			impl TryFrom<DoubleBuf> for $ty {
120				type Error = Overflow;
121
122				fn try_from(i: DoubleBuf) -> Result<Self, Overflow> {
123					i.as_str().parse().map_err(|_| Overflow)
124				}
125			}
126		)*
127	};
128}
129
130integer_conversion! {
131	u8,
132	i8,
133	u16,
134	i16,
135	u32,
136	i32,
137	u64,
138	i64,
139	usize,
140	isize
141}
142
143const DTOA_CONFIG: pretty_dtoa::FmtFloatConfig = pretty_dtoa::FmtFloatConfig::default();
144
145impl From<f32> for DoubleBuf {
146	fn from(i: f32) -> Self {
147		if i.is_finite() {
148			unsafe { DoubleBuf::new_unchecked(pretty_dtoa::ftoa(i, DTOA_CONFIG)) }
149		} else if i.is_nan() {
150			DoubleBuf::nan()
151		} else if i.is_sign_positive() {
152			DoubleBuf::positive_infinity()
153		} else {
154			DoubleBuf::negative_infinity()
155		}
156	}
157}
158
159impl<'a> From<&'a Double> for f64 {
160	fn from(i: &'a Double) -> Self {
161		i.as_str().parse().unwrap()
162	}
163}
164
165impl From<DoubleBuf> for f64 {
166	fn from(i: DoubleBuf) -> Self {
167		i.as_str().parse().unwrap()
168	}
169}
170
171impl<'a> From<&'a Float> for f64 {
172	fn from(i: &'a Float) -> Self {
173		i.as_str().parse().unwrap()
174	}
175}
176
177impl From<FloatBuf> for f64 {
178	fn from(i: FloatBuf) -> Self {
179		i.as_str().parse().unwrap()
180	}
181}
182
183impl From<f64> for DoubleBuf {
184	fn from(i: f64) -> Self {
185		if i.is_finite() {
186			unsafe { DoubleBuf::new_unchecked(pretty_dtoa::dtoa(i, DTOA_CONFIG)) }
187		} else if i.is_nan() {
188			DoubleBuf::nan()
189		} else if i.is_sign_positive() {
190			DoubleBuf::positive_infinity()
191		} else {
192			DoubleBuf::negative_infinity()
193		}
194	}
195}
196
197impl<'a> From<&'a Decimal> for &'a Double {
198	#[inline(always)]
199	fn from(d: &'a Decimal) -> Self {
200		unsafe { Double::new_unchecked(d) }
201	}
202}
203
204impl DoubleBuf {
205	#[inline(always)]
206	pub fn nan() -> Self {
207		NAN.to_owned()
208	}
209
210	#[inline(always)]
211	pub fn positive_infinity() -> Self {
212		POSITIVE_INFINITY.to_owned()
213	}
214
215	#[inline(always)]
216	pub fn negative_infinity() -> Self {
217		NEGATIVE_INFINITY.to_owned()
218	}
219}
220
221fn check_bytes(s: &[u8]) -> bool {
222	s == b"INF" || s == b"-INF" || s == b"NaN" || check_normal(s.iter().cloned())
223}
224
225fn check_normal<C: Iterator<Item = u8>>(mut chars: C) -> bool {
226	enum State {
227		Initial,
228		NonEmptyInteger,
229		Integer,
230		NonEmptyDecimal,
231		Decimal,
232		ExponentSign,
233		NonEmptyExponent,
234		Exponent,
235	}
236
237	let mut state = State::Initial;
238
239	loop {
240		state = match state {
241			State::Initial => match chars.next() {
242				Some(b'+') => State::NonEmptyInteger,
243				Some(b'-') => State::NonEmptyInteger,
244				Some(b'.') => State::NonEmptyDecimal,
245				Some(b'0'..=b'9') => State::Integer,
246				_ => break false,
247			},
248			State::NonEmptyInteger => match chars.next() {
249				Some(b'0'..=b'9') => State::Integer,
250				Some(b'.') => State::Decimal,
251				_ => break false,
252			},
253			State::Integer => match chars.next() {
254				Some(b'0'..=b'9') => State::Integer,
255				Some(b'.') => State::Decimal,
256				Some(b'e' | b'E') => State::ExponentSign,
257				Some(_) => break false,
258				None => break true,
259			},
260			State::NonEmptyDecimal => match chars.next() {
261				Some(b'0'..=b'9') => State::Decimal,
262				_ => break false,
263			},
264			State::Decimal => match chars.next() {
265				Some(b'0'..=b'9') => State::Decimal,
266				Some(b'e' | b'E') => State::ExponentSign,
267				Some(_) => break false,
268				None => break true,
269			},
270			State::ExponentSign => match chars.next() {
271				Some(b'+' | b'-') => State::NonEmptyExponent,
272				Some(b'0'..=b'9') => State::Exponent,
273				_ => break false,
274			},
275			State::NonEmptyExponent => match chars.next() {
276				Some(b'0'..=b'9') => State::Exponent,
277				_ => break false,
278			},
279			State::Exponent => match chars.next() {
280				Some(b'0'..=b'9') => State::Exponent,
281				Some(_) => break false,
282				None => break true,
283			},
284		}
285	}
286}
287
288#[cfg(test)]
289mod tests {
290	use super::*;
291
292	#[test]
293	fn parse_01() {
294		Double::new("0").unwrap();
295	}
296
297	#[test]
298	#[should_panic]
299	fn parse_02() {
300		Double::new("+").unwrap();
301	}
302
303	#[test]
304	#[should_panic]
305	fn parse_03() {
306		Double::new("-").unwrap();
307	}
308
309	#[test]
310	#[should_panic]
311	fn parse_04() {
312		Double::new("012+").unwrap();
313	}
314
315	#[test]
316	fn parse_05() {
317		Double::new("+42").unwrap();
318	}
319
320	#[test]
321	fn parse_06() {
322		Double::new("-42").unwrap();
323	}
324
325	#[test]
326	#[should_panic]
327	fn parse_07() {
328		Double::new(".").unwrap();
329	}
330
331	#[test]
332	fn parse_08() {
333		Double::new(".0").unwrap();
334	}
335
336	#[test]
337	fn parse_09() {
338		Double::new("0.").unwrap();
339	}
340
341	#[test]
342	fn parse_10() {
343		Double::new("42.0").unwrap();
344	}
345
346	#[test]
347	fn parse_11() {
348		Double::new("INF").unwrap();
349	}
350
351	#[test]
352	fn parse_12() {
353		Double::new("-INF").unwrap();
354	}
355
356	#[test]
357	fn parse_13() {
358		Double::new("NaN").unwrap();
359	}
360
361	#[test]
362	fn parse_14() {
363		Double::new(".0e1").unwrap();
364	}
365
366	#[test]
367	fn parse_15() {
368		Double::new("0.e1").unwrap();
369	}
370
371	#[test]
372	fn parse_16() {
373		Double::new("42E10").unwrap();
374	}
375
376	#[test]
377	fn parse_17() {
378		Double::new("-42E+10").unwrap();
379	}
380
381	#[test]
382	fn parse_18() {
383		Double::new("-42E-10").unwrap();
384	}
385
386	#[test]
387	#[should_panic]
388	fn parse_19() {
389		Double::new("+42E-10e").unwrap();
390	}
391
392	#[test]
393	fn parse_20() {
394		Double::new("+42E-10").unwrap();
395	}
396
397	#[test]
398	fn parse_21() {
399		let d = Double::new("+01234E-56789").unwrap();
400		assert_eq!(d.mantissa(), Some(Decimal::new("+01234").unwrap()));
401		assert_eq!(d.exponent(), Some(Integer::new("-56789").unwrap()));
402	}
403
404	#[test]
405	fn parse_22() {
406		let a = DoubleBuf::new("+01234E-56789".to_string()).unwrap();
407		let b = Double::new("+01234E-56789").unwrap();
408		assert_eq!(a, b)
409	}
410
411	#[test]
412	fn format_01() {
413		assert_eq!(DoubleBuf::from(1.0e10f64).to_string(), "1.0e10")
414	}
415}