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}
22
23#[repr(C, align(8))]
25struct NumberHeader {
26 type_: NumberType,
28 _pad: [u8; 7],
30 data: NumberData,
32}
33
34#[repr(C)]
35union NumberData {
36 i: i64,
37 u: u64,
38 f: f64,
39}
40
41#[repr(transparent)]
46#[derive(Clone)]
47pub struct VNumber(pub(crate) Value);
48
49impl VNumber {
50 fn layout() -> Layout {
51 Layout::new::<NumberHeader>()
52 }
53
54 #[cfg(feature = "alloc")]
55 fn alloc(type_: NumberType) -> *mut NumberHeader {
56 unsafe {
57 let ptr = alloc(Self::layout()).cast::<NumberHeader>();
58 (*ptr).type_ = type_;
59 ptr
60 }
61 }
62
63 #[cfg(feature = "alloc")]
64 fn dealloc(ptr: *mut NumberHeader) {
65 unsafe {
66 dealloc(ptr.cast::<u8>(), Self::layout());
67 }
68 }
69
70 fn header(&self) -> &NumberHeader {
71 unsafe { &*(self.0.heap_ptr() as *const NumberHeader) }
72 }
73
74 #[allow(dead_code)]
75 fn header_mut(&mut self) -> &mut NumberHeader {
76 unsafe { &mut *(self.0.heap_ptr_mut() as *mut NumberHeader) }
77 }
78
79 #[cfg(feature = "alloc")]
81 #[must_use]
82 pub fn from_i64(v: i64) -> Self {
83 unsafe {
84 let ptr = Self::alloc(NumberType::I64);
85 (*ptr).data.i = v;
86 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
87 }
88 }
89
90 #[cfg(feature = "alloc")]
92 #[must_use]
93 pub fn from_u64(v: u64) -> Self {
94 if let Ok(i) = i64::try_from(v) {
96 Self::from_i64(i)
97 } else {
98 unsafe {
99 let ptr = Self::alloc(NumberType::U64);
100 (*ptr).data.u = v;
101 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
102 }
103 }
104 }
105
106 #[cfg(feature = "alloc")]
110 #[must_use]
111 pub fn from_f64(v: f64) -> Option<Self> {
112 if !v.is_finite() {
113 return None;
114 }
115 unsafe {
116 let ptr = Self::alloc(NumberType::F64);
117 (*ptr).data.f = v;
118 Some(VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number)))
119 }
120 }
121
122 #[cfg(feature = "alloc")]
124 #[must_use]
125 pub fn zero() -> Self {
126 Self::from_i64(0)
127 }
128
129 #[cfg(feature = "alloc")]
131 #[must_use]
132 pub fn one() -> Self {
133 Self::from_i64(1)
134 }
135
136 #[must_use]
138 pub fn to_i64(&self) -> Option<i64> {
139 let hd = self.header();
140 unsafe {
141 match hd.type_ {
142 NumberType::I64 => Some(hd.data.i),
143 NumberType::U64 => i64::try_from(hd.data.u).ok(),
144 NumberType::F64 => {
145 let f = hd.data.f;
146 if f >= i64::MIN as f64 && f <= i64::MAX as f64 {
148 let i = f as i64;
149 if i as f64 == f {
150 return Some(i);
151 }
152 }
153 None
154 }
155 }
156 }
157 }
158
159 #[must_use]
161 pub fn to_u64(&self) -> Option<u64> {
162 let hd = self.header();
163 unsafe {
164 match hd.type_ {
165 NumberType::I64 => u64::try_from(hd.data.i).ok(),
166 NumberType::U64 => Some(hd.data.u),
167 NumberType::F64 => {
168 let f = hd.data.f;
169 if f >= 0.0 && f <= u64::MAX as f64 {
171 let u = f as u64;
172 if u as f64 == f {
173 return Some(u);
174 }
175 }
176 None
177 }
178 }
179 }
180 }
181
182 #[must_use]
184 pub fn to_f64(&self) -> Option<f64> {
185 let hd = self.header();
186 unsafe {
187 match hd.type_ {
188 NumberType::I64 => {
189 let i = hd.data.i;
190 let f = i as f64;
191 if f as i64 == i { Some(f) } else { None }
192 }
193 NumberType::U64 => {
194 let u = hd.data.u;
195 let f = u as f64;
196 if f as u64 == u { Some(f) } else { None }
197 }
198 NumberType::F64 => Some(hd.data.f),
199 }
200 }
201 }
202
203 #[must_use]
205 pub fn to_f64_lossy(&self) -> f64 {
206 let hd = self.header();
207 unsafe {
208 match hd.type_ {
209 NumberType::I64 => hd.data.i as f64,
210 NumberType::U64 => hd.data.u as f64,
211 NumberType::F64 => hd.data.f,
212 }
213 }
214 }
215
216 #[must_use]
218 pub fn to_i32(&self) -> Option<i32> {
219 self.to_i64().and_then(|v| i32::try_from(v).ok())
220 }
221
222 #[must_use]
224 pub fn to_u32(&self) -> Option<u32> {
225 self.to_u64().and_then(|v| u32::try_from(v).ok())
226 }
227
228 #[must_use]
230 pub fn to_f32(&self) -> Option<f32> {
231 self.to_f64().and_then(|f| {
232 let f32_val = f as f32;
233 if f32_val as f64 == f {
234 Some(f32_val)
235 } else {
236 None
237 }
238 })
239 }
240
241 #[must_use]
243 pub fn is_float(&self) -> bool {
244 self.header().type_ == NumberType::F64
245 }
246
247 #[must_use]
249 pub fn is_integer(&self) -> bool {
250 matches!(self.header().type_, NumberType::I64 | NumberType::U64)
251 }
252
253 pub(crate) fn clone_impl(&self) -> Value {
254 let hd = self.header();
255 unsafe {
256 match hd.type_ {
257 NumberType::I64 => Self::from_i64(hd.data.i).0,
258 NumberType::U64 => {
259 let ptr = Self::alloc(NumberType::U64);
260 (*ptr).data.u = hd.data.u;
261 Value::new_ptr(ptr.cast(), TypeTag::Number)
262 }
263 NumberType::F64 => Self::from_f64(hd.data.f).unwrap().0,
264 }
265 }
266 }
267
268 pub(crate) fn drop_impl(&mut self) {
269 unsafe {
270 Self::dealloc(self.0.heap_ptr_mut().cast());
271 }
272 }
273}
274
275impl PartialEq for VNumber {
276 fn eq(&self, other: &Self) -> bool {
277 self.cmp(other) == Ordering::Equal
278 }
279}
280
281impl Eq for VNumber {}
282
283impl PartialOrd for VNumber {
284 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
285 Some(self.cmp(other))
286 }
287}
288
289impl Ord for VNumber {
290 fn cmp(&self, other: &Self) -> Ordering {
291 let h1 = self.header();
292 let h2 = other.header();
293
294 unsafe {
295 if h1.type_ == h2.type_ {
297 match h1.type_ {
298 NumberType::I64 => h1.data.i.cmp(&h2.data.i),
299 NumberType::U64 => h1.data.u.cmp(&h2.data.u),
300 NumberType::F64 => h1.data.f.partial_cmp(&h2.data.f).unwrap_or(Ordering::Equal),
301 }
302 } else {
303 self.to_f64_lossy()
306 .partial_cmp(&other.to_f64_lossy())
307 .unwrap_or(Ordering::Equal)
308 }
309 }
310 }
311}
312
313impl Hash for VNumber {
314 fn hash<H: Hasher>(&self, state: &mut H) {
315 if let Some(i) = self.to_i64() {
317 0u8.hash(state); i.hash(state);
319 } else if let Some(u) = self.to_u64() {
320 1u8.hash(state); u.hash(state);
322 } else if let Some(f) = self.to_f64() {
323 2u8.hash(state); f.to_bits().hash(state);
325 }
326 }
327}
328
329impl Debug for VNumber {
330 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
331 if let Some(i) = self.to_i64() {
332 Debug::fmt(&i, f)
333 } else if let Some(u) = self.to_u64() {
334 Debug::fmt(&u, f)
335 } else if let Some(fl) = self.to_f64() {
336 Debug::fmt(&fl, f)
337 } else {
338 f.write_str("NaN")
339 }
340 }
341}
342
343impl Default for VNumber {
344 fn default() -> Self {
345 Self::zero()
346 }
347}
348
349macro_rules! impl_from_int {
352 ($($t:ty => $method:ident),* $(,)?) => {
353 $(
354 #[cfg(feature = "alloc")]
355 impl From<$t> for VNumber {
356 fn from(v: $t) -> Self {
357 Self::$method(v as _)
358 }
359 }
360
361 #[cfg(feature = "alloc")]
362 impl From<$t> for Value {
363 fn from(v: $t) -> Self {
364 VNumber::from(v).0
365 }
366 }
367 )*
368 };
369}
370
371impl_from_int! {
372 i8 => from_i64,
373 i16 => from_i64,
374 i32 => from_i64,
375 i64 => from_i64,
376 isize => from_i64,
377 u8 => from_i64,
378 u16 => from_i64,
379 u32 => from_i64,
380 u64 => from_u64,
381 usize => from_u64,
382}
383
384#[cfg(feature = "alloc")]
385impl TryFrom<f32> for VNumber {
386 type Error = ();
387
388 fn try_from(v: f32) -> Result<Self, Self::Error> {
389 Self::from_f64(f64::from(v)).ok_or(())
390 }
391}
392
393#[cfg(feature = "alloc")]
394impl TryFrom<f64> for VNumber {
395 type Error = ();
396
397 fn try_from(v: f64) -> Result<Self, Self::Error> {
398 Self::from_f64(v).ok_or(())
399 }
400}
401
402#[cfg(feature = "alloc")]
403impl From<f32> for Value {
404 fn from(v: f32) -> Self {
405 VNumber::from_f64(f64::from(v))
406 .map(|n| n.0)
407 .unwrap_or(Value::NULL)
408 }
409}
410
411#[cfg(feature = "alloc")]
412impl From<f64> for Value {
413 fn from(v: f64) -> Self {
414 VNumber::from_f64(v).map(|n| n.0).unwrap_or(Value::NULL)
415 }
416}
417
418impl AsRef<Value> for VNumber {
421 fn as_ref(&self) -> &Value {
422 &self.0
423 }
424}
425
426impl AsMut<Value> for VNumber {
427 fn as_mut(&mut self) -> &mut Value {
428 &mut self.0
429 }
430}
431
432impl From<VNumber> for Value {
433 fn from(n: VNumber) -> Self {
434 n.0
435 }
436}
437
438impl VNumber {
439 #[inline]
441 pub fn into_value(self) -> Value {
442 self.0
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449
450 #[test]
451 fn test_i64() {
452 let n = VNumber::from_i64(42);
453 assert_eq!(n.to_i64(), Some(42));
454 assert_eq!(n.to_u64(), Some(42));
455 assert_eq!(n.to_f64(), Some(42.0));
456 assert!(n.is_integer());
457 assert!(!n.is_float());
458 }
459
460 #[test]
461 fn test_negative() {
462 let n = VNumber::from_i64(-100);
463 assert_eq!(n.to_i64(), Some(-100));
464 assert_eq!(n.to_u64(), None);
465 assert_eq!(n.to_f64(), Some(-100.0));
466 }
467
468 #[test]
469 fn test_large_u64() {
470 let v = u64::MAX;
471 let n = VNumber::from_u64(v);
472 assert_eq!(n.to_u64(), Some(v));
473 assert_eq!(n.to_i64(), None);
474 }
475
476 #[test]
477 fn test_f64() {
478 let n = VNumber::from_f64(2.5).unwrap();
479 assert_eq!(n.to_f64(), Some(2.5));
480 assert_eq!(n.to_i64(), None); assert!(n.is_float());
482 assert!(!n.is_integer());
483 }
484
485 #[test]
486 fn test_f64_whole() {
487 let n = VNumber::from_f64(42.0).unwrap();
488 assert_eq!(n.to_f64(), Some(42.0));
489 assert_eq!(n.to_i64(), Some(42)); }
491
492 #[test]
493 fn test_nan_rejected() {
494 assert!(VNumber::from_f64(f64::NAN).is_none());
495 assert!(VNumber::from_f64(f64::INFINITY).is_none());
496 assert!(VNumber::from_f64(f64::NEG_INFINITY).is_none());
497 }
498
499 #[test]
500 fn test_equality() {
501 let a = VNumber::from_i64(42);
502 let b = VNumber::from_i64(42);
503 let c = VNumber::from_f64(42.0).unwrap();
504
505 assert_eq!(a, b);
506 assert_eq!(a, c); }
508
509 #[test]
510 fn test_ordering() {
511 let a = VNumber::from_i64(1);
512 let b = VNumber::from_i64(2);
513 let c = VNumber::from_f64(1.5).unwrap();
514
515 assert!(a < b);
516 assert!(a < c);
517 assert!(c < b);
518 }
519}