1use std::borrow::{Borrow, ToOwned};
2use std::fmt;
3use std::hash::Hash;
4use std::ops::Deref;
5use std::str::FromStr;
6
7use smallvec::SmallVec;
8use str_newtype::Buffer;
9
10use crate::lexical::JsonBorrowLexical;
11
12pub const JSON_NUMBER_RAW_BUFFER_CAPACITY: usize = 8;
13
14pub type JsonNumberRawBuffer = SmallVec<[u8; JSON_NUMBER_RAW_BUFFER_CAPACITY]>;
15
16#[derive(Clone, Copy, Debug)]
20pub struct InvalidJsonNumber<T = String>(pub T);
21
22impl<T: fmt::Display> fmt::Display for InvalidJsonNumber<T> {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "invalid JSON number: {}", self.0)
25 }
26}
27
28impl<T: fmt::Display + fmt::Debug> std::error::Error for InvalidJsonNumber<T> {}
29
30#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
32pub enum Sign {
33 Negative,
34 Zero,
35 Positive,
36}
37
38impl Sign {
39 #[inline(always)]
41 pub fn is_zero(&self) -> bool {
42 matches!(self, Self::Zero)
43 }
44
45 #[inline(always)]
47 pub fn is_non_positive(&self) -> bool {
48 matches!(self, Self::Negative | Self::Zero)
49 }
50
51 #[inline(always)]
53 pub fn is_non_negative(&self) -> bool {
54 matches!(self, Self::Positive | Self::Zero)
55 }
56
57 #[inline(always)]
59 pub fn is_positive(&self) -> bool {
60 matches!(self, Self::Positive)
61 }
62
63 #[inline(always)]
65 pub fn is_negative(&self) -> bool {
66 matches!(self, Self::Negative)
67 }
68}
69
70#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct JsonNumber {
77 data: [u8],
78}
79
80impl JsonNumber {
81 pub fn new<B: AsRef<[u8]> + ?Sized>(data: &B) -> Result<&Self, InvalidJsonNumber<&B>> {
83 let s = data.as_ref();
84 if Self::validate_bytes(s) {
85 Ok(unsafe { Self::new_unchecked(s) })
86 } else {
87 Err(InvalidJsonNumber(data))
88 }
89 }
90
91 #[inline(always)]
97 pub unsafe fn new_unchecked<B: AsRef<[u8]> + ?Sized>(data: &B) -> &JsonNumber {
98 unsafe { std::mem::transmute(data.as_ref()) }
99 }
100
101 pub fn validate_bytes(s: &[u8]) -> bool {
102 enum State {
103 Init,
104 FirstDigit,
105 Zero,
106 NonZero,
107 FractionalFirst,
108 FractionalRest,
109 ExponentSign,
110 ExponentFirst,
111 ExponentRest,
112 }
113
114 let mut state = State::Init;
115
116 for b in s {
117 match state {
118 State::Init => match *b {
119 b'-' => state = State::FirstDigit,
120 b'0' => state = State::Zero,
121 b'1'..=b'9' => state = State::NonZero,
122 _ => return false,
123 },
124 State::FirstDigit => match *b {
125 b'0' => state = State::Zero,
126 b'1'..=b'9' => state = State::NonZero,
127 _ => return false,
128 },
129 State::Zero => match *b {
130 b'.' => state = State::FractionalFirst,
131 b'e' | b'E' => state = State::ExponentSign,
132 _ => return false,
133 },
134 State::NonZero => match *b {
135 b'0'..=b'9' => state = State::NonZero,
136 b'.' => state = State::FractionalFirst,
137 b'e' | b'E' => state = State::ExponentSign,
138 _ => return false,
139 },
140 State::FractionalFirst => match *b {
141 b'0'..=b'9' => state = State::FractionalRest,
142 _ => return false,
143 },
144 State::FractionalRest => match *b {
145 b'0'..=b'9' => state = State::FractionalRest,
146 b'e' | b'E' => state = State::ExponentSign,
147 _ => return false,
148 },
149 State::ExponentSign => match *b {
150 b'+' | b'-' => state = State::ExponentFirst,
151 b'0'..=b'9' => state = State::ExponentRest,
152 _ => return false,
153 },
154 State::ExponentFirst => match *b {
155 b'0'..=b'9' => state = State::ExponentRest,
156 _ => return false,
157 },
158 State::ExponentRest => match *b {
159 b'0'..=b'9' => state = State::ExponentRest,
160 _ => return false,
161 },
162 }
163 }
164
165 matches!(
166 state,
167 State::Zero | State::NonZero | State::FractionalRest | State::ExponentRest
168 )
169 }
170
171 #[inline(always)]
172 pub fn as_str(&self) -> &str {
173 unsafe {
174 std::str::from_utf8_unchecked(&self.data)
176 }
177 }
178
179 pub fn trimmed(&self) -> &Self {
184 let data = &self.data;
185
186 let exp_pos = data.iter().position(|&b| b == b'e' || b == b'E');
188 let mantissa_end = exp_pos.unwrap_or(data.len());
189
190 let mut trim_end = 1;
192 let mut in_frac = false;
193 for (i, b) in data.iter().copied().enumerate().take(mantissa_end).skip(1) {
194 match b {
195 b'0' if in_frac => (), b'.' => in_frac = true,
197 _ => trim_end = i + 1,
198 }
199 }
200
201 let zero_exp = exp_pos.is_none_or(|e| {
203 let after_e = &data[e + 1..];
204 let digits = if after_e.first().is_some_and(|&b| b == b'+' || b == b'-') {
205 &after_e[1..]
206 } else {
207 after_e
208 };
209 digits.iter().all(|&b| b == b'0')
210 });
211
212 let end = if zero_exp {
216 trim_end
217 } else {
218 data.len()
221 };
222
223 let start = if data[0] == b'-' && &data[1..trim_end] == b"0" {
225 1
226 } else {
227 0
228 };
229
230 unsafe { Self::new_unchecked(&data[start..end]) }
231 }
232
233 #[inline(always)]
239 pub fn is_zero(&self) -> bool {
240 for b in &self.data {
241 match b {
242 b'-' | b'0' | b'.' => (),
243 b'e' | b'E' => break,
244 _ => return false,
245 }
246 }
247
248 true
249 }
250
251 pub fn sign(&self) -> Sign {
253 let mut non_negative = true;
254
255 for b in &self.data {
256 match b {
257 b'-' => non_negative = false,
258 b'0' | b'.' => (),
259 b'e' | b'E' => break,
260 _ => {
261 return if non_negative {
262 Sign::Positive
263 } else {
264 Sign::Negative
265 };
266 }
267 }
268 }
269
270 Sign::Zero
271 }
272
273 #[inline(always)]
275 pub fn is_non_positive(&self) -> bool {
276 self.sign().is_non_positive()
277 }
278
279 #[inline(always)]
281 pub fn is_non_negative(&self) -> bool {
282 self.sign().is_non_negative()
283 }
284
285 #[inline(always)]
287 pub fn is_positive(&self) -> bool {
288 self.sign().is_positive()
289 }
290
291 #[inline(always)]
293 pub fn is_negative(&self) -> bool {
294 self.sign().is_negative()
295 }
296
297 #[inline(always)]
299 pub fn has_decimal_point(&self) -> bool {
300 self.data.contains(&b'.')
301 }
302
303 #[inline(always)]
307 pub fn has_fraction(&self) -> bool {
308 self.has_decimal_point()
309 }
310
311 #[inline(always)]
313 pub fn has_exponent(&self) -> bool {
314 for b in &self.data {
315 if matches!(b, b'e' | b'E') {
316 return true;
317 }
318 }
319
320 false
321 }
322
323 #[inline(always)]
324 pub fn is_i32(&self) -> bool {
325 self.as_i32().is_some()
326 }
327
328 #[inline(always)]
329 pub fn is_i64(&self) -> bool {
330 self.as_i64().is_some()
331 }
332
333 #[inline(always)]
334 pub fn is_u32(&self) -> bool {
335 self.as_u32().is_some()
336 }
337
338 #[inline(always)]
339 pub fn is_u64(&self) -> bool {
340 self.as_u64().is_some()
341 }
342
343 #[inline(always)]
344 pub fn as_i32(&self) -> Option<i32> {
345 self.as_str().parse().ok()
346 }
347
348 #[inline(always)]
349 pub fn as_i64(&self) -> Option<i64> {
350 self.as_str().parse().ok()
351 }
352
353 #[inline(always)]
354 pub fn as_u32(&self) -> Option<u32> {
355 self.as_str().parse().ok()
356 }
357
358 #[inline(always)]
359 pub fn as_u64(&self) -> Option<u64> {
360 self.as_str().parse().ok()
361 }
362
363 #[inline(always)]
364 pub fn as_f32_lossy(&self) -> f32 {
365 lexical::parse_with_options::<_, _, { lexical::format::JSON }>(
366 self.as_bytes(),
367 &LOSSY_PARSE_FLOAT,
368 )
369 .unwrap()
370 }
371
372 #[inline(always)]
378 pub fn as_f32_lossless(&self) -> Option<f32> {
379 let f = self.as_f32_lossy();
380 let n: JsonNumberBuf = f.try_into().unwrap();
381 if n.as_number() == self.trimmed() {
382 Some(f)
383 } else {
384 None
385 }
386 }
387
388 #[inline(always)]
389 pub fn as_f64_lossy(&self) -> f64 {
390 lexical::parse_with_options::<_, _, { lexical::format::JSON }>(
391 self.as_bytes(),
392 &LOSSY_PARSE_FLOAT,
393 )
394 .unwrap()
395 }
396
397 #[inline(always)]
403 pub fn as_f64_lossless(&self) -> Option<f64> {
404 let f = self.as_f64_lossy();
405 let n: JsonNumberBuf = f.try_into().unwrap();
406 if n.as_number() == self { Some(f) } else { None }
407 }
408
409 #[cfg(feature = "canonicalize")]
412 pub fn canonicalized_with<'b>(&'b self, buffer: &'b mut ryu_js::Buffer) -> &'b JsonNumber {
413 let f = self.as_f64_lossy();
414
415 if f.is_finite() {
416 unsafe { JsonNumber::new_unchecked(buffer.format_finite(self.as_f64_lossy())) }
417 } else {
418 self
419 }
420 }
421
422 #[cfg(feature = "canonicalize")]
425 pub fn canonicalized(&self) -> JsonNumberBuf {
426 let mut buffer = ryu_js::Buffer::new();
427 self.canonicalized_with(&mut buffer).to_owned()
428 }
429}
430
431impl JsonBorrowLexical for JsonNumber {}
432
433impl JsonBorrowLexical for JsonNumberBuf {}
434
435const LOSSY_PARSE_FLOAT: lexical::ParseFloatOptions = lexical::ParseFloatOptions::builder()
436 .lossy(true)
437 .build_unchecked();
438
439impl Deref for JsonNumber {
440 type Target = str;
441
442 #[inline(always)]
443 fn deref(&self) -> &str {
444 self.as_str()
445 }
446}
447
448impl AsRef<str> for JsonNumber {
449 #[inline(always)]
450 fn as_ref(&self) -> &str {
451 self.as_str()
452 }
453}
454
455impl Borrow<str> for JsonNumber {
456 #[inline(always)]
457 fn borrow(&self) -> &str {
458 self.as_str()
459 }
460}
461
462impl AsRef<[u8]> for JsonNumber {
463 #[inline(always)]
464 fn as_ref(&self) -> &[u8] {
465 self.as_bytes()
466 }
467}
468
469impl<'a> TryFrom<&'a str> for &'a JsonNumber {
470 type Error = InvalidJsonNumber<&'a str>;
471
472 #[inline(always)]
473 fn try_from(s: &'a str) -> Result<&'a JsonNumber, InvalidJsonNumber<&'a str>> {
474 JsonNumber::new(s)
475 }
476}
477
478impl ToOwned for JsonNumber {
479 type Owned = JsonNumberBuf;
480
481 fn to_owned(&self) -> Self::Owned {
482 unsafe { JsonNumberBuf::new_unchecked(self.as_bytes().into()) }
483 }
484}
485
486impl fmt::Display for JsonNumber {
487 #[inline(always)]
488 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
489 self.as_str().fmt(f)
490 }
491}
492
493impl fmt::Debug for JsonNumber {
494 #[inline(always)]
495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
496 self.as_str().fmt(f)
497 }
498}
499
500#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
502pub struct JsonNumberBuf {
503 data: JsonNumberRawBuffer,
504}
505
506impl JsonNumberBuf {
507 #[inline(always)]
509 pub fn new<T: Buffer>(data: T) -> Result<Self, InvalidJsonNumber<T>> {
510 match JsonNumber::new(data.as_bytes()) {
511 Ok(_) => Ok(JsonNumberBuf {
512 data: data.into_bytes().into(),
513 }),
514 Err(_) => Err(InvalidJsonNumber(data)),
515 }
516 }
517
518 #[inline(always)]
524 pub unsafe fn new_unchecked(data: JsonNumberRawBuffer) -> Self {
525 JsonNumberBuf { data }
526 }
527
528 #[inline(always)]
530 pub fn from_number(n: &JsonNumber) -> Self {
531 unsafe { JsonNumberBuf::new_unchecked(n.as_bytes().into()) }
532 }
533
534 #[inline(always)]
535 pub fn as_raw_buffer(&self) -> &JsonNumberRawBuffer {
536 &self.data
537 }
538
539 #[inline(always)]
540 pub fn into_raw_buffer(self) -> JsonNumberRawBuffer {
541 self.data
542 }
543}
544
545impl JsonNumberBuf {
546 #[cfg(feature = "canonicalize")]
549 pub fn canonicalize(&mut self) {
550 *self = self.canonicalized();
551 }
552
553 #[cfg(feature = "canonicalize")]
556 pub fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer) {
557 *self = self.canonicalized_with(buffer).to_owned()
558 }
559}
560
561impl JsonNumberBuf {
562 #[inline(always)]
563 pub fn into_string(self) -> String {
564 unsafe { String::from_utf8_unchecked(self.into_bytes()) }
565 }
566
567 #[inline(always)]
568 pub fn into_bytes(self) -> Vec<u8> {
569 self.data.into_vec()
570 }
571}
572
573impl JsonNumberBuf {
574 #[inline(always)]
575 pub fn as_number(&self) -> &JsonNumber {
576 unsafe { JsonNumber::new_unchecked(&self.data) }
577 }
578}
579
580impl FromStr for JsonNumberBuf {
581 type Err = InvalidJsonNumber;
582
583 #[inline(always)]
584 fn from_str(s: &str) -> Result<Self, Self::Err> {
585 Self::new(s.to_owned())
586 }
587}
588
589impl Deref for JsonNumberBuf {
590 type Target = JsonNumber;
591
592 #[inline(always)]
593 fn deref(&self) -> &JsonNumber {
594 self.as_number()
595 }
596}
597
598impl AsRef<JsonNumber> for JsonNumberBuf {
599 #[inline(always)]
600 fn as_ref(&self) -> &JsonNumber {
601 self.as_number()
602 }
603}
604
605impl Borrow<JsonNumber> for JsonNumberBuf {
606 #[inline(always)]
607 fn borrow(&self) -> &JsonNumber {
608 self.as_number()
609 }
610}
611
612impl AsRef<str> for JsonNumberBuf {
613 #[inline(always)]
614 fn as_ref(&self) -> &str {
615 self.as_str()
616 }
617}
618
619impl AsRef<[u8]> for JsonNumberBuf {
620 #[inline(always)]
621 fn as_ref(&self) -> &[u8] {
622 self.as_bytes()
623 }
624}
625
626impl fmt::Display for JsonNumberBuf {
627 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
628 self.as_str().fmt(f)
629 }
630}
631
632impl fmt::Debug for JsonNumberBuf {
633 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
634 self.as_str().fmt(f)
635 }
636}
637
638macro_rules! impl_from_int {
639 ($($ty:ty),*) => {
640 $(
641 impl From<$ty> for JsonNumberBuf {
642 #[inline(always)]
643 fn from(i: $ty) -> Self {
644 unsafe {
645 Self::new_unchecked(JsonNumberRawBuffer::from_vec(lexical::to_string(i).into_bytes()))
646 }
647 }
648 }
649 )*
650 };
651}
652
653#[derive(Clone, Copy, Debug)]
655pub enum TryFromFloatError {
656 Nan,
658
659 Infinite,
661}
662
663const WRITE_FLOAT: lexical::WriteFloatOptions = lexical::WriteFloatOptions::builder()
664 .trim_floats(true)
665 .exponent(b'e')
666 .build_unchecked();
667
668macro_rules! impl_try_from_float {
669 ($($ty:ty),*) => {
670 $(
671 impl TryFrom<$ty> for JsonNumberBuf {
672 type Error = TryFromFloatError;
673
674 #[inline(always)]
675 fn try_from(f: $ty) -> Result<Self, Self::Error> {
676 if f.is_finite() {
677 Ok(unsafe {
678 Self::new_unchecked(JsonNumberRawBuffer::from_vec(lexical::to_string_with_options::<_, {lexical::format::JSON}>(f, &WRITE_FLOAT).into_bytes()))
679 })
680 } else if f.is_nan() {
681 Err(TryFromFloatError::Nan)
682 } else {
683 Err(TryFromFloatError::Infinite)
684 }
685 }
686 }
687 )*
688 };
689}
690
691impl_from_int!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);
692impl_try_from_float!(f32, f64);
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697
698 fn trimming_test(a: &str, b: &str) {
699 let a = JsonNumber::new(a).unwrap();
700 let b = JsonNumber::new(b).unwrap();
701 assert_eq!(a.trimmed(), b)
702 }
703
704 #[test]
705 fn trimming() {
706 trimming_test("0", "0");
708 trimming_test("0.0", "0");
709 trimming_test("1.0", "1");
710 trimming_test("1.1", "1.1");
711 trimming_test("1.10000", "1.1");
712 trimming_test("100.0", "100");
713 trimming_test("100.1000", "100.1");
714
715 trimming_test("-0", "0");
717 trimming_test("-0.0", "0");
718 trimming_test("-0.000", "0");
719
720 trimming_test("0e0", "0");
722 trimming_test("1e0", "1");
723 trimming_test("1.0e0", "1");
724 trimming_test("1.5e0", "1.5");
725 trimming_test("100.0e0", "100");
726
727 trimming_test("-0e0", "0");
729 trimming_test("-0.0e0", "0");
730 trimming_test("-0.000e0", "0");
731
732 trimming_test("1e21", "1e21");
734 trimming_test("1.5e2", "1.5e2");
735 trimming_test("-1e5", "-1e5");
736
737 trimming_test("1.0e2", "1.0e2");
740 trimming_test("1.50e2", "1.50e2");
741 trimming_test("10.0e2", "10.0e2");
742 }
743
744 macro_rules! positive_tests {
745 { $($id:ident: $input:literal),* } => {
746 $(
747 #[test]
748 fn $id () {
749 assert!(JsonNumber::new($input).is_ok())
750 }
751 )*
752 };
753 }
754
755 macro_rules! negative_tests {
756 { $($id:ident: $input:literal),* } => {
757 $(
758 #[test]
759 fn $id () {
760 assert!(JsonNumber::new($input).is_err())
761 }
762 )*
763 };
764 }
765
766 macro_rules! sign_tests {
767 { $($id:ident: $input:literal => $sign:ident),* } => {
768 $(
769 #[test]
770 fn $id () {
771 assert_eq!(JsonNumber::new($input).unwrap().sign(), Sign::$sign)
772 }
773 )*
774 };
775 }
776
777 macro_rules! canonical_tests {
778 { $($id:ident: $input:literal => $output:literal),* } => {
779 $(
780 #[cfg(feature="canonicalize")]
781 #[test]
782 fn $id () {
783 assert_eq!(JsonNumber::new($input).unwrap().canonicalized().as_number(), JsonNumber::new($output).unwrap())
784 }
785 )*
786 };
787 }
788
789 positive_tests! {
790 pos_01: "0",
791 pos_02: "-0",
792 pos_03: "123",
793 pos_04: "1.23",
794 pos_05: "-12.34",
795 pos_06: "12.34e+56",
796 pos_07: "12.34E-56",
797 pos_08: "0.0000"
798 }
799
800 negative_tests! {
801 neg_01: "",
802 neg_02: "00",
803 neg_03: "01",
804 neg_04: "-00",
805 neg_05: "-01",
806 neg_06: "0.000e+-1",
807 neg_07: "12.34E-56abc",
808 neg_08: "1.",
809 neg_09: "12.34e",
810 neg_10: "12.34e+",
811 neg_11: "12.34E-"
812 }
813
814 sign_tests! {
815 sign_zero_01: "0" => Zero,
816 sign_zero_02: "-0" => Zero,
817 sign_zero_03: "0.0" => Zero,
818 sign_zero_04: "0.0e12" => Zero,
819 sign_zero_05: "-0.0E-12" => Zero,
820 sign_zero_06: "-0.00000" => Zero
821 }
822
823 sign_tests! {
824 sign_pos_01: "1" => Positive,
825 sign_pos_02: "0.1" => Positive,
826 sign_pos_03: "0.01e23" => Positive,
827 sign_pos_04: "1.0E-23" => Positive,
828 sign_pos_05: "0.00001" => Positive
829 }
830
831 sign_tests! {
832 sign_neg_01: "-1" => Negative,
833 sign_neg_02: "-0.1" => Negative,
834 sign_neg_03: "-0.01e23" => Negative,
835 sign_neg_04: "-1.0E-23" => Negative,
836 sign_neg_05: "-0.00001" => Negative
837 }
838
839 canonical_tests! {
840 canonical_01: "-0.0000" => "0",
841 canonical_02: "0.00000000028" => "2.8e-10"
842 }
843}