xsd_types/lexical/
float.rs

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