1use core::ptr;
7use lexical::format::JSON;
8use smallvec::SmallVec;
9use std::borrow::{Borrow, ToOwned};
10use std::error::Error as StdError;
11use std::fmt;
12use std::ops::Deref;
13use std::str;
14use std::str::FromStr;
15
16#[cfg(feature = "serde")]
18pub mod serde;
19
20#[cfg(feature = "serde_json")]
22pub mod serde_json;
23
24#[cfg(feature = "sonic-rs")]
26pub mod sonic_rs;
27
28pub type SmallNumberBuf<const LEN: usize = 8> = NumberBuf<SmallVec<[u8; LEN]>>;
30
31unsafe impl<A: smallvec::Array<Item = u8>> Buffer for SmallVec<A> {
32 fn from_vec(bytes: Vec<u8>) -> Self {
33 bytes.into()
34 }
35
36 fn from_bytes(bytes: &[u8]) -> Self {
37 bytes.into()
38 }
39}
40
41#[derive(Clone, Copy, Debug)]
45pub struct InvalidNumber<T>(pub T);
46
47impl<T: fmt::Display> fmt::Display for InvalidNumber<T> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "invalid JSON number: {}", self.0)
50 }
51}
52
53impl<T: fmt::Display + fmt::Debug> StdError for InvalidNumber<T> {}
54
55#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
57pub enum Sign {
58 Negative,
60
61 Zero,
63
64 Positive,
66}
67
68impl Sign {
69 #[inline(always)]
71 #[must_use]
72 pub const fn is_zero(&self) -> bool {
73 matches!(self, Self::Zero)
74 }
75
76 #[inline(always)]
78 #[must_use]
79 pub const fn is_non_positive(&self) -> bool {
80 matches!(self, Self::Negative | Self::Zero)
81 }
82
83 #[inline(always)]
85 #[must_use]
86 pub const fn is_non_negative(&self) -> bool {
87 matches!(self, Self::Positive | Self::Zero)
88 }
89
90 #[inline(always)]
92 #[must_use]
93 pub const fn is_positive(&self) -> bool {
94 matches!(self, Self::Positive)
95 }
96
97 #[inline(always)]
99 #[must_use]
100 pub const fn is_negative(&self) -> bool {
101 matches!(self, Self::Negative)
102 }
103}
104
105#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
111#[repr(transparent)]
112pub struct Number {
113 data: [u8],
114}
115
116impl Number {
117 pub fn new<B: AsRef<[u8]> + ?Sized>(data: &B) -> Result<&Number, InvalidNumber<&B>> {
122 enum State {
123 Init,
124 FirstDigit,
125 Zero,
126 NonZero,
127 FractionalFirst,
128 FractionalRest,
129 ExponentSign,
130 ExponentFirst,
131 ExponentRest,
132 }
133
134 let s = data.as_ref();
135 let mut state = State::Init;
136
137 for b in s {
138 match state {
139 State::Init => match *b {
140 b'-' => state = State::FirstDigit,
141 b'0' => state = State::Zero,
142 b'1'..=b'9' => state = State::NonZero,
143 _ => return Err(InvalidNumber(data)),
144 },
145 State::FirstDigit => match *b {
146 b'0' => state = State::Zero,
147 b'1'..=b'9' => state = State::NonZero,
148 _ => return Err(InvalidNumber(data)),
149 },
150 State::Zero => match *b {
151 b'.' => state = State::FractionalFirst,
152 b'e' | b'E' => state = State::ExponentSign,
153 _ => return Err(InvalidNumber(data)),
154 },
155 State::NonZero => match *b {
156 b'0'..=b'9' => state = State::NonZero,
157 b'.' => state = State::FractionalFirst,
158 b'e' | b'E' => state = State::ExponentSign,
159 _ => return Err(InvalidNumber(data)),
160 },
161 State::FractionalFirst => match *b {
162 b'0'..=b'9' => state = State::FractionalRest,
163 _ => return Err(InvalidNumber(data)),
164 },
165 State::FractionalRest => match *b {
166 b'0'..=b'9' => state = State::FractionalRest,
167 b'e' | b'E' => state = State::ExponentSign,
168 _ => return Err(InvalidNumber(data)),
169 },
170 State::ExponentSign => match *b {
171 b'+' | b'-' => state = State::ExponentFirst,
172 b'0'..=b'9' => state = State::ExponentRest,
173 _ => return Err(InvalidNumber(data)),
174 },
175 State::ExponentFirst | State::ExponentRest => match *b {
176 b'0'..=b'9' => state = State::ExponentRest,
177 _ => return Err(InvalidNumber(data)),
178 },
179 }
180 }
181
182 if matches!(
183 state,
184 State::Zero | State::NonZero | State::FractionalRest | State::ExponentRest
185 ) {
186 Ok(unsafe { Self::new_unchecked(s) })
187 } else {
188 Err(InvalidNumber(data))
189 }
190 }
191
192 #[inline(always)]
198 pub unsafe fn new_unchecked<B: AsRef<[u8]> + ?Sized>(data: &B) -> &Number {
199 unsafe { &*(ptr::from_ref(data.as_ref()) as *const Number) }
200 }
201
202 #[inline(always)]
204 #[must_use]
205 pub const fn as_str(&self) -> &str {
206 unsafe {
207 str::from_utf8_unchecked(&self.data)
209 }
210 }
211
212 #[must_use]
217 pub fn trimmed(&self) -> &Self {
218 let mut end = 1;
219 let mut i = 1;
220 let mut fractional_part = false;
221 while i < self.data.len() {
222 match self.data[i] {
223 b'0' if fractional_part => (),
224 b'.' => fractional_part = true,
225 _ => end = i + 1,
226 }
227
228 i += 1;
229 }
230
231 unsafe { Self::new_unchecked(&self.data[0..end]) }
232 }
233
234 #[inline(always)]
240 #[must_use]
241 pub fn is_zero(&self) -> bool {
242 for b in &self.data {
243 match b {
244 b'-' | b'0' | b'.' => (),
245 b'e' | b'E' => break,
246 _ => return false,
247 }
248 }
249
250 true
251 }
252
253 #[must_use]
255 pub fn sign(&self) -> Sign {
256 let mut non_negative = true;
257
258 for b in &self.data {
259 match b {
260 b'-' => non_negative = false,
261 b'0' | b'.' => (),
262 b'e' | b'E' => break,
263 _ => {
264 return if non_negative {
265 Sign::Positive
266 } else {
267 Sign::Negative
268 };
269 }
270 }
271 }
272
273 Sign::Zero
274 }
275
276 #[inline(always)]
278 #[must_use]
279 pub fn is_non_positive(&self) -> bool {
280 self.sign().is_non_positive()
281 }
282
283 #[inline(always)]
285 #[must_use]
286 pub fn is_non_negative(&self) -> bool {
287 self.sign().is_non_negative()
288 }
289
290 #[inline(always)]
292 #[must_use]
293 pub fn is_positive(&self) -> bool {
294 self.sign().is_positive()
295 }
296
297 #[inline(always)]
299 #[must_use]
300 pub fn is_negative(&self) -> bool {
301 self.sign().is_negative()
302 }
303
304 #[inline(always)]
306 #[must_use]
307 pub fn has_decimal_point(&self) -> bool {
308 self.data.contains(&b'.')
309 }
310
311 #[inline(always)]
315 #[must_use]
316 pub fn has_fraction(&self) -> bool {
317 self.has_decimal_point()
318 }
319
320 #[inline(always)]
322 #[must_use]
323 pub fn has_exponent(&self) -> bool {
324 for b in &self.data {
325 if matches!(b, b'e' | b'E') {
326 return true;
327 }
328 }
329
330 false
331 }
332
333 #[inline(always)]
335 #[must_use]
336 pub fn is_i32(&self) -> bool {
337 self.as_i32().is_some()
338 }
339
340 #[inline(always)]
342 #[must_use]
343 pub fn is_i64(&self) -> bool {
344 self.as_i64().is_some()
345 }
346
347 #[inline(always)]
349 #[must_use]
350 pub fn is_u32(&self) -> bool {
351 self.as_u32().is_some()
352 }
353
354 #[inline(always)]
356 #[must_use]
357 pub fn is_u64(&self) -> bool {
358 self.as_u64().is_some()
359 }
360
361 #[inline(always)]
364 #[must_use]
365 pub fn as_i32(&self) -> Option<i32> {
366 self.as_str().parse().ok()
367 }
368
369 #[inline(always)]
372 #[must_use]
373 pub fn as_i64(&self) -> Option<i64> {
374 self.as_str().parse().ok()
375 }
376
377 #[inline(always)]
380 #[must_use]
381 pub fn as_u32(&self) -> Option<u32> {
382 self.as_str().parse().ok()
383 }
384
385 #[inline(always)]
388 #[must_use]
389 pub fn as_u64(&self) -> Option<u64> {
390 self.as_str().parse().ok()
391 }
392
393 #[inline(always)]
395 #[must_use]
396 pub fn as_f32_lossy(&self) -> f32 {
397 lexical::parse_with_options::<_, _, { JSON }>(self.as_bytes(), &LOSSY_PARSE_FLOAT)
398 .unwrap_or(f32::NAN)
399 }
400
401 #[inline(always)]
407 #[must_use]
408 pub fn as_f32_lossless(&self) -> Option<f32> {
409 let f = self.as_f32_lossy();
410 let Ok(n) = <NumberBuf as TryFrom<f32>>::try_from(f) else {
411 return None;
412 };
413 if n.as_number() == self.trimmed() {
414 Some(f)
415 } else {
416 None
417 }
418 }
419
420 #[inline(always)]
422 #[must_use]
423 pub fn as_f64_lossy(&self) -> f64 {
424 lexical::parse_with_options::<_, _, { JSON }>(self.as_bytes(), &LOSSY_PARSE_FLOAT)
425 .unwrap_or(f64::NAN)
426 }
427
428 #[inline(always)]
434 #[must_use]
435 pub fn as_f64_lossless(&self) -> Option<f64> {
436 let f = self.as_f64_lossy();
437 let Ok(n) = <NumberBuf as TryFrom<f64>>::try_from(f) else {
438 return None;
439 };
440 if n.as_number() == self { Some(f) } else { None }
441 }
442
443 #[cfg(feature = "canonicalize")]
446 pub fn canonical_with<'b>(&self, buffer: &'b mut ryu_js::Buffer) -> &'b Number {
447 unsafe { Number::new_unchecked(buffer.format_finite(self.as_f64_lossy())) }
448 }
449
450 #[cfg(feature = "canonicalize")]
453 #[must_use]
454 pub fn canonical(&self) -> NumberBuf {
455 let mut buffer = ryu_js::Buffer::new();
456 self.canonical_with(&mut buffer).to_owned()
457 }
458}
459
460const LOSSY_PARSE_FLOAT: lexical::ParseFloatOptions = lexical::ParseFloatOptions::builder()
461 .lossy(true)
462 .build_unchecked();
463
464impl Deref for Number {
465 type Target = str;
466
467 #[inline(always)]
468 fn deref(&self) -> &str {
469 self.as_str()
470 }
471}
472
473impl AsRef<str> for Number {
474 #[inline(always)]
475 fn as_ref(&self) -> &str {
476 self.as_str()
477 }
478}
479
480impl Borrow<str> for Number {
481 #[inline(always)]
482 fn borrow(&self) -> &str {
483 self.as_str()
484 }
485}
486
487impl AsRef<[u8]> for Number {
488 #[inline(always)]
489 fn as_ref(&self) -> &[u8] {
490 self.as_bytes()
491 }
492}
493
494impl<'a> TryFrom<&'a str> for &'a Number {
495 type Error = InvalidNumber<&'a str>;
496
497 #[inline(always)]
498 fn try_from(s: &'a str) -> Result<&'a Number, InvalidNumber<&'a str>> {
499 Number::new(s)
500 }
501}
502
503impl ToOwned for Number {
504 type Owned = NumberBuf;
505
506 fn to_owned(&self) -> Self::Owned {
507 unsafe { NumberBuf::new_unchecked(self.as_bytes().to_owned()) }
508 }
509}
510
511impl fmt::Display for Number {
512 #[inline(always)]
513 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514 self.as_str().fmt(f)
515 }
516}
517
518impl fmt::Debug for Number {
519 #[inline(always)]
520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 self.as_str().fmt(f)
522 }
523}
524
525pub unsafe trait Buffer: AsRef<[u8]> {
532 fn from_bytes(bytes: &[u8]) -> Self;
534
535 fn from_vec(bytes: Vec<u8>) -> Self;
538}
539
540unsafe impl Buffer for Vec<u8> {
541 fn from_bytes(bytes: &[u8]) -> Self {
542 bytes.into()
543 }
544
545 fn from_vec(bytes: Vec<u8>) -> Self {
546 bytes
547 }
548}
549
550#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
552pub struct NumberBuf<B = Vec<u8>> {
553 data: B,
554}
555
556impl<B> NumberBuf<B> {
557 #[inline(always)]
559 pub fn new(data: B) -> Result<Self, InvalidNumber<B>>
563 where
564 B: AsRef<[u8]>,
565 {
566 match Number::new(&data) {
567 Ok(_) => Ok(NumberBuf { data }),
568 Err(_) => Err(InvalidNumber(data)),
569 }
570 }
571
572 #[inline(always)]
578 pub const unsafe fn new_unchecked(data: B) -> Self {
579 NumberBuf { data }
580 }
581
582 #[inline(always)]
584 #[must_use]
585 pub fn from_number(n: &Number) -> Self
586 where
587 B: FromIterator<u8>,
588 {
589 unsafe { NumberBuf::new_unchecked(n.bytes().collect()) }
590 }
591
592 #[inline(always)]
594 pub const fn buffer(&self) -> &B {
595 &self.data
596 }
597
598 #[inline(always)]
600 pub fn into_buffer(self) -> B {
601 self.data
602 }
603}
604
605impl NumberBuf<String> {
606 #[inline(always)]
608 #[must_use]
609 pub fn into_string(self) -> String {
610 self.data
611 }
612
613 #[inline(always)]
616 #[must_use]
617 pub fn into_bytes(self) -> Vec<u8> {
618 self.data.into_bytes()
619 }
620}
621
622impl<B: Buffer> NumberBuf<B> {
623 #[inline(always)]
625 pub fn as_number(&self) -> &Number {
626 unsafe { Number::new_unchecked(&self.data) }
627 }
628}
629
630impl<B: Buffer> FromStr for NumberBuf<B> {
631 type Err = InvalidNumber<B>;
632
633 #[inline(always)]
634 fn from_str(s: &str) -> Result<Self, Self::Err> {
635 Self::new(B::from_bytes(s.as_bytes()))
636 }
637}
638
639impl<B: Buffer> Deref for NumberBuf<B> {
640 type Target = Number;
641
642 #[inline(always)]
643 fn deref(&self) -> &Number {
644 self.as_number()
645 }
646}
647
648impl<B: Buffer> AsRef<Number> for NumberBuf<B> {
649 #[inline(always)]
650 fn as_ref(&self) -> &Number {
651 self.as_number()
652 }
653}
654
655impl<B: Buffer> Borrow<Number> for NumberBuf<B> {
656 #[inline(always)]
657 fn borrow(&self) -> &Number {
658 self.as_number()
659 }
660}
661
662impl<B: Buffer> AsRef<str> for NumberBuf<B> {
663 #[inline(always)]
664 fn as_ref(&self) -> &str {
665 self.as_str()
666 }
667}
668
669impl<B: Buffer> Borrow<str> for NumberBuf<B> {
670 #[inline(always)]
671 fn borrow(&self) -> &str {
672 self.as_str()
673 }
674}
675
676impl<B: Buffer> AsRef<[u8]> for NumberBuf<B> {
677 #[inline(always)]
678 fn as_ref(&self) -> &[u8] {
679 self.as_bytes()
680 }
681}
682
683impl<B: Buffer> Borrow<[u8]> for NumberBuf<B> {
684 #[inline(always)]
685 fn borrow(&self) -> &[u8] {
686 self.as_bytes()
687 }
688}
689
690impl<B: Buffer> fmt::Display for NumberBuf<B> {
691 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
692 self.as_str().fmt(f)
693 }
694}
695
696impl<B: Buffer> fmt::Debug for NumberBuf<B> {
697 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
698 self.as_str().fmt(f)
699 }
700}
701
702macro_rules! impl_from_int {
703 ($($ty:ty),*) => {
704 $(
705 impl<B: Buffer> From<$ty> for NumberBuf<B> {
706 #[inline(always)]
707 fn from(i: $ty) -> Self {
708 unsafe {
709 Self::new_unchecked(B::from_vec(lexical::to_string(i).into_bytes()))
710 }
711 }
712 }
713 )*
714 };
715}
716
717#[derive(Clone, Copy, Debug)]
719pub enum TryFromFloatError {
720 Nan,
722
723 Infinite,
725}
726
727const WRITE_FLOAT: lexical::WriteFloatOptions = lexical::WriteFloatOptions::builder()
728 .trim_floats(true)
729 .exponent(b'e')
730 .build_unchecked();
731
732macro_rules! impl_try_from_float {
733 ($($ty:ty),*) => {
734 $(
735 impl<B: Buffer> TryFrom<$ty> for NumberBuf<B> {
736 type Error = TryFromFloatError;
737
738 #[inline(always)]
739 fn try_from(f: $ty) -> Result<Self, Self::Error> {
740 if f.is_finite() {
741 Ok(unsafe {
742 Self::new_unchecked(B::from_vec(lexical::to_string_with_options::<_, {lexical::format::JSON}>(f, &WRITE_FLOAT).into_bytes()))
743 })
744 } else if f.is_nan() {
745 Err(TryFromFloatError::Nan)
746 } else {
747 Err(TryFromFloatError::Infinite)
748 }
749 }
750 }
751 )*
752 };
753}
754
755impl_from_int!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);
756impl_try_from_float!(f32, f64);
757
758#[cfg(test)]
759mod tests {
760 use super::*;
761
762 fn trimming_test(a: &str, b: &str) {
763 let a = Number::new(a).unwrap();
764 let b = Number::new(b).unwrap();
765 assert_eq!(a.trimmed(), b);
766 }
767
768 #[test]
769 fn trimming() {
770 trimming_test("0", "0");
771 trimming_test("0.0", "0");
772 trimming_test("1.0", "1");
773 trimming_test("1.0", "1");
774 trimming_test("1.1", "1.1");
775 trimming_test("1.10000", "1.1");
776 trimming_test("100.0", "100");
777 trimming_test("100.1000", "100.1");
778 }
779
780 macro_rules! positive_tests {
781 { $($id:ident: $input:literal),* } => {
782 $(
783 #[test]
784 fn $id () {
785 assert!(Number::new($input).is_ok())
786 }
787 )*
788 };
789 }
790
791 macro_rules! negative_tests {
792 { $($id:ident: $input:literal),* } => {
793 $(
794 #[test]
795 fn $id () {
796 assert!(Number::new($input).is_err())
797 }
798 )*
799 };
800 }
801
802 macro_rules! sign_tests {
803 { $($id:ident: $input:literal => $sign:ident),* } => {
804 $(
805 #[test]
806 fn $id () {
807 assert_eq!(Number::new($input).unwrap().sign(), Sign::$sign)
808 }
809 )*
810 };
811 }
812
813 macro_rules! canonical_tests {
814 { $($id:ident: $input:literal => $output:literal),* } => {
815 $(
816 #[cfg(feature="canonicalize")]
817 #[test]
818 fn $id () {
819 assert_eq!(Number::new($input).unwrap().canonical().as_number(), Number::new($output).unwrap())
820 }
821 )*
822 };
823 }
824
825 positive_tests! {
826 pos_01: "0",
827 pos_02: "-0",
828 pos_03: "123",
829 pos_04: "1.23",
830 pos_05: "-12.34",
831 pos_06: "12.34e+56",
832 pos_07: "12.34E-56",
833 pos_08: "0.0000"
834 }
835
836 negative_tests! {
837 neg_01: "",
838 neg_02: "00",
839 neg_03: "01",
840 neg_04: "-00",
841 neg_05: "-01",
842 neg_06: "0.000e+-1",
843 neg_07: "12.34E-56abc",
844 neg_08: "1.",
845 neg_09: "12.34e",
846 neg_10: "12.34e+",
847 neg_11: "12.34E-"
848 }
849
850 sign_tests! {
851 sign_zero_01: "0" => Zero,
852 sign_zero_02: "-0" => Zero,
853 sign_zero_03: "0.0" => Zero,
854 sign_zero_04: "0.0e12" => Zero,
855 sign_zero_05: "-0.0E-12" => Zero,
856 sign_zero_06: "-0.00000" => Zero
857 }
858
859 sign_tests! {
860 sign_pos_01: "1" => Positive,
861 sign_pos_02: "0.1" => Positive,
862 sign_pos_03: "0.01e23" => Positive,
863 sign_pos_04: "1.0E-23" => Positive,
864 sign_pos_05: "0.00001" => Positive
865 }
866
867 sign_tests! {
868 sign_neg_01: "-1" => Negative,
869 sign_neg_02: "-0.1" => Negative,
870 sign_neg_03: "-0.01e23" => Negative,
871 sign_neg_04: "-1.0E-23" => Negative,
872 sign_neg_05: "-0.00001" => Negative
873 }
874
875 canonical_tests! {
876 canonical_01: "-0.0000" => "0",
877 canonical_02: "0.00000000028" => "2.8e-10"
878 }
879}