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 const 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")]
108 #[must_use]
109 pub fn from_f64(v: f64) -> Self {
110 unsafe {
111 let ptr = Self::alloc(NumberType::F64);
112 (*ptr).data.f = v;
113 VNumber(Value::new_ptr(ptr.cast(), TypeTag::Number))
114 }
115 }
116
117 #[cfg(feature = "alloc")]
119 #[must_use]
120 pub fn zero() -> Self {
121 Self::from_i64(0)
122 }
123
124 #[cfg(feature = "alloc")]
126 #[must_use]
127 pub fn one() -> Self {
128 Self::from_i64(1)
129 }
130
131 #[must_use]
133 pub fn to_i64(&self) -> Option<i64> {
134 let hd = self.header();
135 unsafe {
136 match hd.type_ {
137 NumberType::I64 => Some(hd.data.i),
138 NumberType::U64 => i64::try_from(hd.data.u).ok(),
139 NumberType::F64 => {
140 let f = hd.data.f;
141 if f >= i64::MIN as f64 && f <= i64::MAX as f64 {
143 let i = f as i64;
144 if i as f64 == f {
145 return Some(i);
146 }
147 }
148 None
149 }
150 }
151 }
152 }
153
154 #[must_use]
156 pub fn to_u64(&self) -> Option<u64> {
157 let hd = self.header();
158 unsafe {
159 match hd.type_ {
160 NumberType::I64 => u64::try_from(hd.data.i).ok(),
161 NumberType::U64 => Some(hd.data.u),
162 NumberType::F64 => {
163 let f = hd.data.f;
164 if f >= 0.0 && f <= u64::MAX as f64 {
166 let u = f as u64;
167 if u as f64 == f {
168 return Some(u);
169 }
170 }
171 None
172 }
173 }
174 }
175 }
176
177 #[must_use]
179 pub fn to_f64(&self) -> Option<f64> {
180 let hd = self.header();
181 unsafe {
182 match hd.type_ {
183 NumberType::I64 => {
184 let i = hd.data.i;
185 let f = i as f64;
186 if f as i64 == i { Some(f) } else { None }
187 }
188 NumberType::U64 => {
189 let u = hd.data.u;
190 let f = u as f64;
191 if f as u64 == u { Some(f) } else { None }
192 }
193 NumberType::F64 => Some(hd.data.f),
194 }
195 }
196 }
197
198 #[must_use]
200 pub fn to_f64_lossy(&self) -> f64 {
201 let hd = self.header();
202 unsafe {
203 match hd.type_ {
204 NumberType::I64 => hd.data.i as f64,
205 NumberType::U64 => hd.data.u as f64,
206 NumberType::F64 => hd.data.f,
207 }
208 }
209 }
210
211 #[must_use]
213 pub fn to_i32(&self) -> Option<i32> {
214 self.to_i64().and_then(|v| i32::try_from(v).ok())
215 }
216
217 #[must_use]
219 pub fn to_u32(&self) -> Option<u32> {
220 self.to_u64().and_then(|v| u32::try_from(v).ok())
221 }
222
223 #[must_use]
225 pub fn to_f32(&self) -> Option<f32> {
226 self.to_f64().and_then(|f| {
227 let f32_val = f as f32;
228 if f32_val as f64 == f {
229 Some(f32_val)
230 } else {
231 None
232 }
233 })
234 }
235
236 #[must_use]
238 pub fn is_float(&self) -> bool {
239 self.header().type_ == NumberType::F64
240 }
241
242 #[must_use]
244 pub fn is_integer(&self) -> bool {
245 matches!(self.header().type_, NumberType::I64 | NumberType::U64)
246 }
247
248 pub(crate) fn clone_impl(&self) -> Value {
249 let hd = self.header();
250 unsafe {
251 match hd.type_ {
252 NumberType::I64 => Self::from_i64(hd.data.i).0,
253 NumberType::U64 => {
254 let ptr = Self::alloc(NumberType::U64);
255 (*ptr).data.u = hd.data.u;
256 Value::new_ptr(ptr.cast(), TypeTag::Number)
257 }
258 NumberType::F64 => Self::from_f64(hd.data.f).0,
259 }
260 }
261 }
262
263 pub(crate) fn drop_impl(&mut self) {
264 unsafe {
265 Self::dealloc(self.0.heap_ptr_mut().cast());
266 }
267 }
268}
269
270impl PartialEq for VNumber {
271 fn eq(&self, other: &Self) -> bool {
272 self.partial_cmp(other) == Some(Ordering::Equal)
273 }
274}
275
276impl PartialOrd for VNumber {
277 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
278 let h1 = self.header();
279 let h2 = other.header();
280
281 unsafe {
282 if h1.type_ == h2.type_ {
284 match h1.type_ {
285 NumberType::I64 => Some(h1.data.i.cmp(&h2.data.i)),
286 NumberType::U64 => Some(h1.data.u.cmp(&h2.data.u)),
287 NumberType::F64 => h1.data.f.partial_cmp(&h2.data.f),
288 }
289 } else {
290 self.to_f64_lossy().partial_cmp(&other.to_f64_lossy())
293 }
294 }
295 }
296}
297
298impl Hash for VNumber {
299 fn hash<H: Hasher>(&self, state: &mut H) {
300 if let Some(i) = self.to_i64() {
302 0u8.hash(state); i.hash(state);
304 } else if let Some(u) = self.to_u64() {
305 1u8.hash(state); u.hash(state);
307 } else if let Some(f) = self.to_f64() {
308 2u8.hash(state); f.to_bits().hash(state);
310 }
311 }
312}
313
314impl Debug for VNumber {
315 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316 if let Some(i) = self.to_i64() {
317 Debug::fmt(&i, f)
318 } else if let Some(u) = self.to_u64() {
319 Debug::fmt(&u, f)
320 } else if let Some(fl) = self.to_f64() {
321 Debug::fmt(&fl, f)
322 } else {
323 f.write_str("NaN")
324 }
325 }
326}
327
328impl Default for VNumber {
329 fn default() -> Self {
330 Self::zero()
331 }
332}
333
334macro_rules! impl_from_int {
337 ($($t:ty => $method:ident),* $(,)?) => {
338 $(
339 #[cfg(feature = "alloc")]
340 impl From<$t> for VNumber {
341 fn from(v: $t) -> Self {
342 Self::$method(v as _)
343 }
344 }
345
346 #[cfg(feature = "alloc")]
347 impl From<$t> for Value {
348 fn from(v: $t) -> Self {
349 VNumber::from(v).0
350 }
351 }
352 )*
353 };
354}
355
356impl_from_int! {
357 i8 => from_i64,
358 i16 => from_i64,
359 i32 => from_i64,
360 i64 => from_i64,
361 isize => from_i64,
362 u8 => from_i64,
363 u16 => from_i64,
364 u32 => from_i64,
365 u64 => from_u64,
366 usize => from_u64,
367}
368
369#[cfg(feature = "alloc")]
370impl From<f32> for VNumber {
371 fn from(v: f32) -> Self {
372 Self::from_f64(f64::from(v))
373 }
374}
375
376#[cfg(feature = "alloc")]
377impl From<f64> for VNumber {
378 fn from(v: f64) -> Self {
379 Self::from_f64(v)
380 }
381}
382
383#[cfg(feature = "alloc")]
384impl From<f32> for Value {
385 fn from(v: f32) -> Self {
386 VNumber::from_f64(f64::from(v)).into_value()
387 }
388}
389
390#[cfg(feature = "alloc")]
391impl From<f64> for Value {
392 fn from(v: f64) -> Self {
393 VNumber::from_f64(v).into_value()
394 }
395}
396
397impl AsRef<Value> for VNumber {
400 fn as_ref(&self) -> &Value {
401 &self.0
402 }
403}
404
405impl AsMut<Value> for VNumber {
406 fn as_mut(&mut self) -> &mut Value {
407 &mut self.0
408 }
409}
410
411impl From<VNumber> for Value {
412 fn from(n: VNumber) -> Self {
413 n.0
414 }
415}
416
417impl VNumber {
418 #[inline]
420 pub fn into_value(self) -> Value {
421 self.0
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 #[test]
430 fn test_i64() {
431 let n = VNumber::from_i64(42);
432 assert_eq!(n.to_i64(), Some(42));
433 assert_eq!(n.to_u64(), Some(42));
434 assert_eq!(n.to_f64(), Some(42.0));
435 assert!(n.is_integer());
436 assert!(!n.is_float());
437 }
438
439 #[test]
440 fn test_negative() {
441 let n = VNumber::from_i64(-100);
442 assert_eq!(n.to_i64(), Some(-100));
443 assert_eq!(n.to_u64(), None);
444 assert_eq!(n.to_f64(), Some(-100.0));
445 }
446
447 #[test]
448 fn test_large_u64() {
449 let v = u64::MAX;
450 let n = VNumber::from_u64(v);
451 assert_eq!(n.to_u64(), Some(v));
452 assert_eq!(n.to_i64(), None);
453 }
454
455 #[test]
456 fn test_f64() {
457 let n = VNumber::from_f64(2.5);
458 assert_eq!(n.to_f64(), Some(2.5));
459 assert_eq!(n.to_i64(), None); assert!(n.is_float());
461 assert!(!n.is_integer());
462 }
463
464 #[test]
465 fn test_f64_whole() {
466 let n = VNumber::from_f64(42.0);
467 assert_eq!(n.to_f64(), Some(42.0));
468 assert_eq!(n.to_i64(), Some(42)); }
470
471 #[test]
472 fn test_nan_roundtrip() {
473 assert!(VNumber::from_f64(f64::NAN).to_f64().unwrap().is_nan());
474 assert_eq!(
475 VNumber::from_f64(f64::INFINITY).to_f64().unwrap(),
476 f64::INFINITY
477 );
478 assert_eq!(
479 VNumber::from_f64(f64::NEG_INFINITY).to_f64().unwrap(),
480 f64::NEG_INFINITY
481 );
482 }
483
484 #[test]
485 fn test_equality() {
486 let a = VNumber::from_i64(42);
487 let b = VNumber::from_i64(42);
488 let c = VNumber::from_f64(42.0);
489 let nan = VNumber::from_f64(f64::NAN);
490
491 assert_eq!(a, b);
492 assert_eq!(a, c); assert_ne!(c, nan);
496 assert_ne!(nan, nan);
497 }
498
499 #[test]
500 fn test_ordering() {
501 let a = VNumber::from_i64(1);
502 let b = VNumber::from_i64(2);
503 let c = VNumber::from_f64(1.5);
504 let nan = VNumber::from_f64(f64::NAN);
505 let inf = VNumber::from_f64(f64::INFINITY);
506
507 assert!(a < b);
508 assert!(a < c);
509 assert!(c < b);
510 assert!(b < inf);
511 assert!(!(c > nan || c < nan));
512 }
513}