1#[cfg(feature = "alloc")]
4use alloc::alloc::{Layout, alloc, dealloc};
5use core::cmp::Ordering;
6use core::fmt::{self, Debug, Formatter};
7use core::hash::{Hash, Hasher};
8
9use crate::value::{TypeTag, Value};
10
11#[repr(u8)]
13#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14enum NumberType {
15 I64 = 0,
17 U64 = 1,
19 F64 = 2,
21 I128 = 3,
23 U128 = 4,
25}
26
27#[repr(C, align(8))]
29struct NumberHeader {
30 type_: NumberType,
32 _pad: [u8; 7],
34 data: NumberData,
36}
37
38#[repr(C)]
39union NumberData {
40 i: i64,
41 u: u64,
42 f: f64,
43 i128: i128,
44 u128: u128,
45}
46
47#[repr(transparent)]
52#[derive(Clone)]
53pub struct VNumber(pub(crate) Value);
54
55impl VNumber {
56 const fn layout() -> Layout {
57 Layout::new::<NumberHeader>()
58 }
59
60 #[cfg(feature = "alloc")]
61 fn alloc(type_: NumberType) -> *mut NumberHeader {
62 unsafe {
63 let ptr = alloc(Self::layout()).cast::<NumberHeader>();
64 (*ptr).type_ = type_;
65 ptr
66 }
67 }
68
69 #[cfg(feature = "alloc")]
70 fn dealloc(ptr: *mut NumberHeader) {
71 unsafe {
72 dealloc(ptr.cast::<u8>(), Self::layout());
73 }
74 }
75
76 fn header(&self) -> &NumberHeader {
77 unsafe { &*(self.0.heap_ptr() as *const NumberHeader) }
78 }
79
80 #[allow(dead_code)]
81 fn header_mut(&mut self) -> &mut NumberHeader {
82 unsafe { &mut *(self.0.heap_ptr_mut() as *mut NumberHeader) }
83 }
84
85 #[cfg(feature = "alloc")]
87 #[must_use]
88 pub fn from_i64(v: i64) -> Self {
89 unsafe {
90 let ptr = Self::alloc(NumberType::I64);
91 (*ptr).data.i = v;
92 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
93 }
94 }
95
96 #[cfg(feature = "alloc")]
98 #[must_use]
99 pub fn from_u64(v: u64) -> Self {
100 if let Ok(i) = i64::try_from(v) {
102 Self::from_i64(i)
103 } else {
104 unsafe {
105 let ptr = Self::alloc(NumberType::U64);
106 (*ptr).data.u = v;
107 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
108 }
109 }
110 }
111
112 #[cfg(feature = "alloc")]
114 #[must_use]
115 pub fn from_f64(v: f64) -> Self {
116 unsafe {
117 let ptr = Self::alloc(NumberType::F64);
118 (*ptr).data.f = v;
119 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
120 }
121 }
122
123 #[cfg(feature = "alloc")]
130 #[must_use]
131 pub fn from_i128(v: i128) -> Self {
132 if let Ok(i) = i64::try_from(v) {
133 Self::from_i64(i)
134 } else if v >= 0 {
135 if let Ok(u) = u64::try_from(v) {
136 Self::from_u64(u)
137 } else {
138 unsafe {
140 let ptr = Self::alloc(NumberType::U128);
141 (*ptr).data.u128 = v as u128;
142 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
143 }
144 }
145 } else {
146 unsafe {
148 let ptr = Self::alloc(NumberType::I128);
149 (*ptr).data.i128 = v;
150 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
151 }
152 }
153 }
154
155 #[cfg(feature = "alloc")]
157 #[must_use]
158 pub fn from_u128(v: u128) -> Self {
159 if let Ok(u) = u64::try_from(v) {
160 Self::from_u64(u)
161 } else {
162 unsafe {
163 let ptr = Self::alloc(NumberType::U128);
164 (*ptr).data.u128 = v;
165 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
166 }
167 }
168 }
169
170 #[cfg(feature = "alloc")]
172 #[must_use]
173 pub fn zero() -> Self {
174 Self::from_i64(0)
175 }
176
177 #[cfg(feature = "alloc")]
179 #[must_use]
180 pub fn one() -> Self {
181 Self::from_i64(1)
182 }
183
184 #[must_use]
186 pub fn to_i64(&self) -> Option<i64> {
187 let hd = self.header();
188 unsafe {
189 match hd.type_ {
190 NumberType::I64 => Some(hd.data.i),
191 NumberType::U64 => i64::try_from(hd.data.u).ok(),
192 NumberType::I128 => i64::try_from(hd.data.i128).ok(),
193 NumberType::U128 => i64::try_from(hd.data.u128).ok(),
194 NumberType::F64 => {
195 let f = hd.data.f;
196 if f >= i64::MIN as f64 && f <= i64::MAX as f64 {
198 let i = f as i64;
199 if i as f64 == f {
200 return Some(i);
201 }
202 }
203 None
204 }
205 }
206 }
207 }
208
209 #[must_use]
211 pub fn to_u64(&self) -> Option<u64> {
212 let hd = self.header();
213 unsafe {
214 match hd.type_ {
215 NumberType::I64 => u64::try_from(hd.data.i).ok(),
216 NumberType::U64 => Some(hd.data.u),
217 NumberType::I128 => u64::try_from(hd.data.i128).ok(),
218 NumberType::U128 => u64::try_from(hd.data.u128).ok(),
219 NumberType::F64 => {
220 let f = hd.data.f;
221 if f >= 0.0 && f <= u64::MAX as f64 {
223 let u = f as u64;
224 if u as f64 == f {
225 return Some(u);
226 }
227 }
228 None
229 }
230 }
231 }
232 }
233
234 #[must_use]
236 pub fn to_i128(&self) -> Option<i128> {
237 let hd = self.header();
238 unsafe {
239 match hd.type_ {
240 NumberType::I64 => Some(i128::from(hd.data.i)),
241 NumberType::U64 => Some(i128::from(hd.data.u)),
242 NumberType::I128 => Some(hd.data.i128),
243 NumberType::U128 => i128::try_from(hd.data.u128).ok(),
244 NumberType::F64 => {
245 let f = hd.data.f;
246 if f >= i128::MIN as f64 && f <= i128::MAX as f64 {
248 let i = f as i128;
249 if i as f64 == f {
250 return Some(i);
251 }
252 }
253 None
254 }
255 }
256 }
257 }
258
259 #[must_use]
261 pub fn to_u128(&self) -> Option<u128> {
262 let hd = self.header();
263 unsafe {
264 match hd.type_ {
265 NumberType::I64 => u128::try_from(hd.data.i).ok(),
266 NumberType::U64 => Some(u128::from(hd.data.u)),
267 NumberType::I128 => u128::try_from(hd.data.i128).ok(),
268 NumberType::U128 => Some(hd.data.u128),
269 NumberType::F64 => {
270 let f = hd.data.f;
271 if f >= 0.0 && f <= u128::MAX as f64 {
273 let u = f as u128;
274 if u as f64 == f {
275 return Some(u);
276 }
277 }
278 None
279 }
280 }
281 }
282 }
283
284 #[must_use]
286 pub fn to_f64(&self) -> Option<f64> {
287 let hd = self.header();
288 unsafe {
289 match hd.type_ {
290 NumberType::I64 => {
291 let i = hd.data.i;
292 let f = i as f64;
293 if f as i64 == i { Some(f) } else { None }
294 }
295 NumberType::U64 => {
296 let u = hd.data.u;
297 let f = u as f64;
298 if f as u64 == u { Some(f) } else { None }
299 }
300 NumberType::I128 => {
301 let i = hd.data.i128;
302 let f = i as f64;
303 if f as i128 == i { Some(f) } else { None }
304 }
305 NumberType::U128 => {
306 let u = hd.data.u128;
307 let f = u as f64;
308 if f as u128 == u { Some(f) } else { None }
309 }
310 NumberType::F64 => Some(hd.data.f),
311 }
312 }
313 }
314
315 #[must_use]
317 pub fn to_f64_lossy(&self) -> f64 {
318 let hd = self.header();
319 unsafe {
320 match hd.type_ {
321 NumberType::I64 => hd.data.i as f64,
322 NumberType::U64 => hd.data.u as f64,
323 NumberType::I128 => hd.data.i128 as f64,
324 NumberType::U128 => hd.data.u128 as f64,
325 NumberType::F64 => hd.data.f,
326 }
327 }
328 }
329
330 #[must_use]
332 pub fn to_i32(&self) -> Option<i32> {
333 self.to_i64().and_then(|v| i32::try_from(v).ok())
334 }
335
336 #[must_use]
338 pub fn to_u32(&self) -> Option<u32> {
339 self.to_u64().and_then(|v| u32::try_from(v).ok())
340 }
341
342 #[must_use]
344 pub fn to_f32(&self) -> Option<f32> {
345 self.to_f64().and_then(|f| {
346 let f32_val = f as f32;
347 if f32_val as f64 == f {
348 Some(f32_val)
349 } else {
350 None
351 }
352 })
353 }
354
355 #[must_use]
357 pub fn is_float(&self) -> bool {
358 self.header().type_ == NumberType::F64
359 }
360
361 #[must_use]
363 pub fn is_integer(&self) -> bool {
364 matches!(
365 self.header().type_,
366 NumberType::I64 | NumberType::U64 | NumberType::I128 | NumberType::U128
367 )
368 }
369
370 pub(crate) fn clone_impl(&self) -> Value {
371 let hd = self.header();
372 unsafe {
373 match hd.type_ {
374 NumberType::I64 => Self::from_i64(hd.data.i).0,
375 NumberType::U64 => {
376 let ptr = Self::alloc(NumberType::U64);
377 (*ptr).data.u = hd.data.u;
378 Value::new_ptr(ptr.cast(), TypeTag::Number)
379 }
380 NumberType::I128 => {
381 let ptr = Self::alloc(NumberType::I128);
382 (*ptr).data.i128 = hd.data.i128;
383 Value::new_ptr(ptr.cast(), TypeTag::Number)
384 }
385 NumberType::U128 => {
386 let ptr = Self::alloc(NumberType::U128);
387 (*ptr).data.u128 = hd.data.u128;
388 Value::new_ptr(ptr.cast(), TypeTag::Number)
389 }
390 NumberType::F64 => Self::from_f64(hd.data.f).0,
391 }
392 }
393 }
394
395 pub(crate) fn drop_impl(&mut self) {
396 unsafe {
397 Self::dealloc(self.0.heap_ptr_mut().cast());
398 }
399 }
400}
401
402impl PartialEq for VNumber {
403 fn eq(&self, other: &Self) -> bool {
404 self.partial_cmp(other) == Some(Ordering::Equal)
405 }
406}
407
408impl PartialOrd for VNumber {
409 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
410 let h1 = self.header();
411 let h2 = other.header();
412
413 unsafe {
414 if h1.type_ == h2.type_ {
416 match h1.type_ {
417 NumberType::I64 => Some(h1.data.i.cmp(&h2.data.i)),
418 NumberType::U64 => Some(h1.data.u.cmp(&h2.data.u)),
419 NumberType::I128 => Some(h1.data.i128.cmp(&h2.data.i128)),
420 NumberType::U128 => Some(h1.data.u128.cmp(&h2.data.u128)),
421 NumberType::F64 => h1.data.f.partial_cmp(&h2.data.f),
422 }
423 } else if h1.type_ == NumberType::F64 || h2.type_ == NumberType::F64 {
424 self.to_f64_lossy().partial_cmp(&other.to_f64_lossy())
427 } else {
428 match (self.to_i128(), other.to_i128()) {
430 (Some(a), Some(b)) => Some(a.cmp(&b)),
431 (None, Some(_)) => Some(Ordering::Greater),
434 (Some(_), None) => Some(Ordering::Less),
435 (None, None) => Some(self.to_u128().unwrap().cmp(&other.to_u128().unwrap())),
437 }
438 }
439 }
440 }
441}
442
443impl Hash for VNumber {
444 fn hash<H: Hasher>(&self, state: &mut H) {
445 if let Some(i) = self.to_i64() {
449 0u8.hash(state); i.hash(state);
451 } else if let Some(u) = self.to_u64() {
452 1u8.hash(state); u.hash(state);
454 } else if let Some(i) = self.to_i128() {
455 3u8.hash(state); i.hash(state);
457 } else if let Some(u) = self.to_u128() {
458 4u8.hash(state); u.hash(state);
460 } else if let Some(f) = self.to_f64() {
461 2u8.hash(state); f.to_bits().hash(state);
463 }
464 }
465}
466
467impl Debug for VNumber {
468 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
469 if let Some(i) = self.to_i64() {
470 Debug::fmt(&i, f)
471 } else if let Some(u) = self.to_u64() {
472 Debug::fmt(&u, f)
473 } else if let Some(i) = self.to_i128() {
474 Debug::fmt(&i, f)
475 } else if let Some(u) = self.to_u128() {
476 Debug::fmt(&u, f)
477 } else if let Some(fl) = self.to_f64() {
478 Debug::fmt(&fl, f)
479 } else {
480 f.write_str("NaN")
481 }
482 }
483}
484
485impl Default for VNumber {
486 fn default() -> Self {
487 Self::zero()
488 }
489}
490
491macro_rules! impl_from_int {
494 ($($t:ty => $method:ident),* $(,)?) => {
495 $(
496 #[cfg(feature = "alloc")]
497 impl From<$t> for VNumber {
498 fn from(v: $t) -> Self {
499 Self::$method(v as _)
500 }
501 }
502
503 #[cfg(feature = "alloc")]
504 impl From<$t> for Value {
505 fn from(v: $t) -> Self {
506 VNumber::from(v).0
507 }
508 }
509 )*
510 };
511}
512
513impl_from_int! {
514 i8 => from_i64,
515 i16 => from_i64,
516 i32 => from_i64,
517 i64 => from_i64,
518 isize => from_i64,
519 u8 => from_i64,
520 u16 => from_i64,
521 u32 => from_i64,
522 u64 => from_u64,
523 usize => from_u64,
524}
525
526#[cfg(feature = "alloc")]
531impl From<i128> for VNumber {
532 fn from(v: i128) -> Self {
533 Self::from_i128(v)
534 }
535}
536
537#[cfg(feature = "alloc")]
538impl From<i128> for Value {
539 fn from(v: i128) -> Self {
540 VNumber::from_i128(v).0
541 }
542}
543
544#[cfg(feature = "alloc")]
545impl From<u128> for VNumber {
546 fn from(v: u128) -> Self {
547 Self::from_u128(v)
548 }
549}
550
551#[cfg(feature = "alloc")]
552impl From<u128> for Value {
553 fn from(v: u128) -> Self {
554 VNumber::from_u128(v).0
555 }
556}
557
558#[cfg(feature = "alloc")]
559impl From<f32> for VNumber {
560 fn from(v: f32) -> Self {
561 Self::from_f64(f64::from(v))
562 }
563}
564
565#[cfg(feature = "alloc")]
566impl From<f64> for VNumber {
567 fn from(v: f64) -> Self {
568 Self::from_f64(v)
569 }
570}
571
572#[cfg(feature = "alloc")]
573impl From<f32> for Value {
574 fn from(v: f32) -> Self {
575 VNumber::from_f64(f64::from(v)).into_value()
576 }
577}
578
579#[cfg(feature = "alloc")]
580impl From<f64> for Value {
581 fn from(v: f64) -> Self {
582 VNumber::from_f64(v).into_value()
583 }
584}
585
586impl AsRef<Value> for VNumber {
589 fn as_ref(&self) -> &Value {
590 &self.0
591 }
592}
593
594impl AsMut<Value> for VNumber {
595 fn as_mut(&mut self) -> &mut Value {
596 &mut self.0
597 }
598}
599
600impl From<VNumber> for Value {
601 fn from(n: VNumber) -> Self {
602 n.0
603 }
604}
605
606impl VNumber {
607 #[inline]
609 pub fn into_value(self) -> Value {
610 self.0
611 }
612}
613
614#[cfg(test)]
615mod tests {
616 use super::*;
617
618 #[test]
619 fn test_i64() {
620 let n = VNumber::from_i64(42);
621 assert_eq!(n.to_i64(), Some(42));
622 assert_eq!(n.to_u64(), Some(42));
623 assert_eq!(n.to_f64(), Some(42.0));
624 assert!(n.is_integer());
625 assert!(!n.is_float());
626 }
627
628 #[test]
629 fn test_negative() {
630 let n = VNumber::from_i64(-100);
631 assert_eq!(n.to_i64(), Some(-100));
632 assert_eq!(n.to_u64(), None);
633 assert_eq!(n.to_f64(), Some(-100.0));
634 }
635
636 #[test]
637 fn test_large_u64() {
638 let v = u64::MAX;
639 let n = VNumber::from_u64(v);
640 assert_eq!(n.to_u64(), Some(v));
641 assert_eq!(n.to_i64(), None);
642 }
643
644 #[test]
645 fn test_f64() {
646 let n = VNumber::from_f64(2.5);
647 assert_eq!(n.to_f64(), Some(2.5));
648 assert_eq!(n.to_i64(), None); assert!(n.is_float());
650 assert!(!n.is_integer());
651 }
652
653 #[test]
654 fn test_f64_whole() {
655 let n = VNumber::from_f64(42.0);
656 assert_eq!(n.to_f64(), Some(42.0));
657 assert_eq!(n.to_i64(), Some(42)); }
659
660 #[test]
661 fn test_nan_roundtrip() {
662 assert!(VNumber::from_f64(f64::NAN).to_f64().unwrap().is_nan());
663 assert_eq!(
664 VNumber::from_f64(f64::INFINITY).to_f64().unwrap(),
665 f64::INFINITY
666 );
667 assert_eq!(
668 VNumber::from_f64(f64::NEG_INFINITY).to_f64().unwrap(),
669 f64::NEG_INFINITY
670 );
671 }
672
673 #[test]
674 fn test_equality() {
675 let a = VNumber::from_i64(42);
676 let b = VNumber::from_i64(42);
677 let c = VNumber::from_f64(42.0);
678 let nan = VNumber::from_f64(f64::NAN);
679
680 assert_eq!(a, b);
681 assert_eq!(a, c); assert_ne!(c, nan);
685 assert_ne!(nan, nan);
686 }
687
688 #[test]
689 fn test_ordering() {
690 let a = VNumber::from_i64(1);
691 let b = VNumber::from_i64(2);
692 let c = VNumber::from_f64(1.5);
693 let nan = VNumber::from_f64(f64::NAN);
694 let inf = VNumber::from_f64(f64::INFINITY);
695
696 assert!(a < b);
697 assert!(a < c);
698 assert!(c < b);
699 assert!(b < inf);
700 assert!(c.partial_cmp(&nan).is_none());
701 }
702
703 #[test]
704 fn test_u128_max_roundtrip() {
705 let n = VNumber::from_u128(u128::MAX);
706 assert_eq!(n.to_u128(), Some(u128::MAX));
707 assert_eq!(n.to_i128(), None);
709 assert_eq!(n.to_u64(), None);
710 assert!(n.is_integer());
711 }
712
713 #[test]
714 fn test_i128_min_roundtrip() {
715 let n = VNumber::from_i128(i128::MIN);
716 assert_eq!(n.to_i128(), Some(i128::MIN));
717 assert_eq!(n.to_u128(), None);
718 assert_eq!(n.to_i64(), None);
719 assert!(n.is_integer());
720 }
721
722 #[test]
723 fn test_above_u64_max_roundtrip() {
724 let v = u128::from(u64::MAX) + 1;
725 let n = VNumber::from_u128(v);
726 assert_eq!(n.to_u128(), Some(v));
727 assert_eq!(n.to_i128(), Some(v as i128));
728 assert_eq!(n.to_u64(), None);
729 }
730
731 #[test]
732 fn test_128_canonicalization() {
733 assert_eq!(VNumber::from_i128(5), VNumber::from_i64(5));
735 assert_eq!(VNumber::from_u128(5), VNumber::from_i64(5));
736 assert_eq!(
738 VNumber::from_u128(u128::from(u64::MAX)),
739 VNumber::from_u64(u64::MAX)
740 );
741 assert_eq!(VNumber::from_i128(-42), VNumber::from_i64(-42));
743 }
744
745 #[test]
746 fn test_128_eq_hash_consistency() {
747 use std::collections::HashSet;
748
749 let a = VNumber::from_i128(5);
751 let b = VNumber::from_u128(5);
752 assert_eq!(a, b);
753
754 let mut set = HashSet::new();
755 set.insert(crate::Value::from(a.clone()));
756 assert!(!set.insert(crate::Value::from(b.clone())));
758
759 let big = u128::from(u64::MAX) + 12345;
761 let x = VNumber::from_u128(big);
762 let y = VNumber::from_i128(big as i128);
763 assert_eq!(x, y);
764 let mut set2 = HashSet::new();
765 set2.insert(crate::Value::from(x));
766 assert!(!set2.insert(crate::Value::from(y)));
767 }
768
769 #[test]
770 fn test_128_cross_type_ordering() {
771 let huge = VNumber::from_u128(u128::MAX);
773 let big_signed = VNumber::from_i128(i128::MAX);
774 assert!(big_signed < huge);
775
776 let small = VNumber::from_i128(i128::MIN);
778 assert!(small < big_signed);
779 assert!(small < VNumber::from_i64(0));
780 }
781
782 #[test]
783 fn test_from_128_impls() {
784 let v: crate::Value = (u128::MAX).into();
785 assert_eq!(v.as_number().unwrap().to_u128(), Some(u128::MAX));
786 let v: crate::Value = (i128::MIN).into();
787 assert_eq!(v.as_number().unwrap().to_i128(), Some(i128::MIN));
788 }
789}