1use std::fmt;
18
19const TAG_SHIFT: u32 = 48;
20const TAG_INT32: u64 = 0xFFF9;
21const TAG_SPECIAL: u64 = 0xFFFA;
22const TAG_OBJECT: u64 = 0xFFFB;
23const TAG_STRING: u64 = 0xFFFC;
24const TAG_SYMBOL: u64 = 0xFFFD;
25const TAG_BIGINT: u64 = 0xFFFE;
26
27const BOXED_MIN: u64 = TAG_INT32 << TAG_SHIFT;
29pub const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
31
32const SPECIAL_BOOL: u64 = 0;
33const SPECIAL_UNDEFINED: u64 = 1;
34const SPECIAL_NULL: u64 = 2;
35const SPECIAL_EMPTY: u64 = 3;
36const SPECIAL_TRUE_BIT: u64 = 1 << 2;
37
38const fn boxed(tag: u64, payload: u32) -> u64 {
39 (tag << TAG_SHIFT) | payload as u64
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44#[repr(u8)]
45pub enum Kind {
46 Number,
47 Int32,
48 Bool,
49 Undefined,
50 Null,
51 Empty,
52 Object,
53 String,
54 Symbol,
55 BigInt,
56}
57
58const KIND_TABLE: [Kind; 16] = [
62 Kind::Number,
63 Kind::Int32,
64 Kind::Bool, Kind::Object,
66 Kind::String,
67 Kind::Symbol,
68 Kind::BigInt,
69 Kind::Number, Kind::Bool,
71 Kind::Undefined,
72 Kind::Null,
73 Kind::Empty,
74 Kind::Number, Kind::Number,
76 Kind::Number,
77 Kind::Number,
78];
79
80#[repr(transparent)]
86#[derive(Clone, Copy, PartialEq, Eq, Hash)]
87pub struct Value(u64);
88
89const _: () = assert!(size_of::<Value>() == 8);
90const _: () = assert!(size_of::<[Value; 4]>() == 32);
91
92impl Value {
93 pub const UNDEFINED: Value = Value(boxed(TAG_SPECIAL, SPECIAL_UNDEFINED as u32));
94 pub const NULL: Value = Value(boxed(TAG_SPECIAL, SPECIAL_NULL as u32));
95 pub const TRUE: Value = Value(boxed(TAG_SPECIAL, (SPECIAL_BOOL | SPECIAL_TRUE_BIT) as u32));
96 pub const FALSE: Value = Value(boxed(TAG_SPECIAL, SPECIAL_BOOL as u32));
97 pub const EMPTY: Value = Value(boxed(TAG_SPECIAL, SPECIAL_EMPTY as u32));
100
101 #[inline]
106 pub fn number(value: f64) -> Value {
107 if value.is_nan() {
108 return Value::nan(value);
109 }
110 Value(value.to_bits())
111 }
112
113 #[cold]
116 #[inline(never)]
117 fn nan(value: f64) -> Value {
118 let bits = value.to_bits();
119 if bits >= BOXED_MIN {
120 Value(CANONICAL_NAN)
121 } else {
122 Value(bits)
123 }
124 }
125
126 #[inline]
127 pub const fn int32(value: i32) -> Value {
128 Value(boxed(TAG_INT32, value as u32))
129 }
130
131 #[inline]
132 pub const fn bool(value: bool) -> Value {
133 if value { Value::TRUE } else { Value::FALSE }
134 }
135
136 #[inline]
137 pub const fn object(id: ObjectId) -> Value {
138 Value(boxed(TAG_OBJECT, id.0))
139 }
140
141 #[inline]
142 pub const fn string(id: StringId) -> Value {
143 Value(boxed(TAG_STRING, id.0))
144 }
145
146 #[inline]
147 pub const fn symbol(id: SymbolId) -> Value {
148 Value(boxed(TAG_SYMBOL, id.0))
149 }
150
151 #[inline]
152 pub const fn bigint(id: BigIntId) -> Value {
153 Value(boxed(TAG_BIGINT, id.0))
154 }
155
156 #[inline]
158 pub const fn bits(self) -> u64 {
159 self.0
160 }
161
162 #[inline]
163 pub fn kind(self) -> Kind {
164 let tag = (self.0 >> TAG_SHIFT) as u32;
165 let index = tag.saturating_sub(TAG_INT32 as u32 - 1) as usize;
167 let special = usize::from(tag == TAG_SPECIAL as u32) * (6 + (self.0 & 3) as usize);
169 KIND_TABLE[(index + special) & 15]
170 }
171
172 #[inline]
174 pub const fn is_number(self) -> bool {
175 self.0 < (TAG_SPECIAL << TAG_SHIFT)
176 }
177
178 #[inline]
179 pub const fn is_double(self) -> bool {
180 self.0 < BOXED_MIN
181 }
182
183 #[inline]
184 pub const fn is_int32(self) -> bool {
185 self.0 >> TAG_SHIFT == TAG_INT32
186 }
187
188 #[inline]
189 pub const fn is_empty(self) -> bool {
190 self.0 == Value::EMPTY.0
191 }
192
193 #[inline]
194 pub const fn is_undefined(self) -> bool {
195 self.0 == Value::UNDEFINED.0
196 }
197
198 #[inline]
199 pub const fn is_null(self) -> bool {
200 self.0 == Value::NULL.0
201 }
202
203 #[inline]
205 pub fn as_f64(self) -> Option<f64> {
206 if self.is_double() {
207 Some(f64::from_bits(self.0))
208 } else if self.is_int32() {
209 Some(f64::from(self.0 as u32 as i32))
210 } else {
211 None
212 }
213 }
214
215 #[inline]
216 pub const fn as_int32(self) -> Option<i32> {
217 if self.is_int32() {
218 Some(self.0 as u32 as i32)
219 } else {
220 None
221 }
222 }
223
224 #[inline]
225 pub const fn as_bool(self) -> Option<bool> {
226 if self.0 >> 3 == Value::FALSE.0 >> 3 && self.0 & 3 == SPECIAL_BOOL {
227 Some(self.0 & SPECIAL_TRUE_BIT != 0)
228 } else {
229 None
230 }
231 }
232
233 #[inline]
234 const fn payload_if(self, tag: u64) -> Option<u32> {
235 if self.0 >> TAG_SHIFT == tag {
236 Some(self.0 as u32)
237 } else {
238 None
239 }
240 }
241
242 #[inline]
243 pub const fn as_object(self) -> Option<ObjectId> {
244 match self.payload_if(TAG_OBJECT) {
245 Some(index) => Some(ObjectId(index)),
246 None => None,
247 }
248 }
249
250 #[inline]
251 pub const fn as_string(self) -> Option<StringId> {
252 match self.payload_if(TAG_STRING) {
253 Some(index) => Some(StringId(index)),
254 None => None,
255 }
256 }
257
258 #[inline]
259 pub const fn as_symbol(self) -> Option<SymbolId> {
260 match self.payload_if(TAG_SYMBOL) {
261 Some(index) => Some(SymbolId(index)),
262 None => None,
263 }
264 }
265
266 #[inline]
267 pub const fn as_bigint(self) -> Option<BigIntId> {
268 match self.payload_if(TAG_BIGINT) {
269 Some(index) => Some(BigIntId(index)),
270 None => None,
271 }
272 }
273
274 #[inline]
276 pub const fn is_heap(self) -> bool {
277 let tag = self.0 >> TAG_SHIFT;
278 tag >= TAG_OBJECT && tag <= TAG_BIGINT
279 }
280}
281
282impl fmt::Debug for Value {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 match self.kind() {
285 Kind::Number => write!(f, "Number({:?})", f64::from_bits(self.0)),
286 Kind::Int32 => write!(f, "Int32({})", self.0 as u32 as i32),
287 Kind::Bool => write!(f, "Bool({})", self.0 & SPECIAL_TRUE_BIT != 0),
288 Kind::Undefined => f.write_str("Undefined"),
289 Kind::Null => f.write_str("Null"),
290 Kind::Empty => f.write_str("Empty"),
291 Kind::Object => write!(f, "Object({})", self.0 as u32),
292 Kind::String => write!(f, "String({})", self.0 as u32),
293 Kind::Symbol => write!(f, "Symbol({})", self.0 as u32),
294 Kind::BigInt => write!(f, "BigInt({})", self.0 as u32),
295 }
296 }
297}
298
299macro_rules! identifier {
300 ($(#[$doc:meta])* $name:ident) => {
301 $(#[$doc])*
302 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
303 pub struct $name(pub(crate) u32);
304
305 impl $name {
306 #[inline]
308 pub const fn index(self) -> u32 {
309 self.0
310 }
311 }
312
313 impl fmt::Debug for $name {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 write!(f, concat!(stringify!($name), "({})"), self.0)
316 }
317 }
318 };
319}
320
321identifier!(
322 ObjectId
324);
325identifier!(
326 StringId
328);
329identifier!(
330 ShapeId
332);
333identifier!(
334 SymbolId
336);
337identifier!(
338 BigIntId
340);
341identifier!(
342 ScopeId
344);
345identifier!(
346 ValidityCellId
348);
349identifier!(
350 BytecodeId
353);
354
355impl ScopeId {
356 #[inline]
357 pub const fn object(self) -> ObjectId {
358 ObjectId(self.0)
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365
366 #[test]
367 fn sizes() {
368 assert_eq!(size_of::<Value>(), 8);
369 assert_eq!(size_of::<[Value; 4]>(), 32);
370 }
371
372 #[test]
373 fn nan_keeps_its_bits_below_the_boxed_range() {
374 assert_eq!(Value::number(f64::NAN).0, 0x7FF8_0000_0000_0000);
375 for bits in [
376 0x7FF8_0000_0000_0001u64,
377 0x7FF0_0000_0000_0001, 0xFFF8_0000_0000_0000, ] {
380 let value = Value::number(f64::from_bits(bits));
381 assert_eq!(value.0, bits, "{bits:#x}");
382 assert_eq!(value.kind(), Kind::Number);
383 assert!(value.as_f64().unwrap().is_nan());
384 }
385 for bits in [
386 0xFFF9_0000_0000_0000u64, 0xFFFB_0000_0000_0007, 0xFFFF_FFFF_FFFF_FFFF,
389 ] {
390 let value = Value::number(f64::from_bits(bits));
391 assert_eq!(value.0, CANONICAL_NAN, "{bits:#x}");
392 assert_eq!(value.kind(), Kind::Number);
393 assert!(value.as_f64().unwrap().is_nan());
394 }
395 }
396
397 #[test]
398 fn doubles_round_trip() {
399 let cases = [
400 0.0,
401 -0.0,
402 1.0,
403 -1.5,
404 f64::INFINITY,
405 f64::NEG_INFINITY,
406 f64::MAX,
407 f64::MIN,
408 f64::MIN_POSITIVE,
409 f64::from_bits(1), -f64::from_bits(0x000F_FFFF_FFFF_FFFF), f64::EPSILON,
412 2f64.powi(53),
413 ];
414 for case in cases {
415 let value = Value::number(case);
416 assert_eq!(value.kind(), Kind::Number);
417 assert!(value.is_number() && value.is_double() && !value.is_int32());
418 assert_eq!(value.as_f64().unwrap().to_bits(), case.to_bits());
419 assert_eq!(value.as_int32(), None);
420 assert_eq!(value.as_object(), None);
421 }
422 assert_ne!(Value::number(0.0), Value::number(-0.0));
423 }
424
425 #[test]
426 fn int32_round_trip() {
427 for case in [0, 1, -1, i32::MIN, i32::MAX, 42, -123_456] {
428 let value = Value::int32(case);
429 assert_eq!(value.kind(), Kind::Int32);
430 assert!(value.is_number() && value.is_int32() && !value.is_double());
431 assert_eq!(value.as_int32(), Some(case));
432 assert_eq!(value.as_f64(), Some(f64::from(case)));
433 assert_eq!(value.as_bool(), None);
434 }
435 }
436
437 #[test]
438 fn specials_round_trip() {
439 let cases = [
440 (Value::TRUE, Kind::Bool),
441 (Value::FALSE, Kind::Bool),
442 (Value::UNDEFINED, Kind::Undefined),
443 (Value::NULL, Kind::Null),
444 (Value::EMPTY, Kind::Empty),
445 ];
446 for (index, (value, kind)) in cases.into_iter().enumerate() {
447 assert_eq!(value.kind(), kind);
448 assert!(!value.is_number() && !value.is_heap());
449 assert_eq!(value.as_f64(), None);
450 for (other, (other_value, _)) in cases.into_iter().enumerate() {
451 assert_eq!(index == other, value == other_value);
452 }
453 }
454 assert_eq!(Value::TRUE.as_bool(), Some(true));
455 assert_eq!(Value::FALSE.as_bool(), Some(false));
456 assert_eq!(Value::bool(true), Value::TRUE);
457 assert_eq!(Value::UNDEFINED.as_bool(), None);
458 assert_eq!(Value::EMPTY.as_bool(), None);
459 assert!(Value::EMPTY.is_empty() && !Value::UNDEFINED.is_empty());
460 assert!(Value::UNDEFINED.is_undefined() && Value::NULL.is_null());
461 }
462
463 #[test]
464 fn heap_kinds_round_trip() {
465 for index in [0, 1, 0x7FFF_FFFF, u32::MAX] {
466 let object = Value::object(ObjectId(index));
467 let string = Value::string(StringId(index));
468 let symbol = Value::symbol(SymbolId(index));
469 let bigint = Value::bigint(BigIntId(index));
470 assert_eq!(object.kind(), Kind::Object);
471 assert_eq!(string.kind(), Kind::String);
472 assert_eq!(symbol.kind(), Kind::Symbol);
473 assert_eq!(bigint.kind(), Kind::BigInt);
474 assert_eq!(object.as_object(), Some(ObjectId(index)));
475 assert_eq!(string.as_string(), Some(StringId(index)));
476 assert_eq!(symbol.as_symbol(), Some(SymbolId(index)));
477 assert_eq!(bigint.as_bigint(), Some(BigIntId(index)));
478 for value in [object, string, symbol, bigint] {
479 assert!(value.is_heap() && !value.is_number());
480 assert_eq!(value.as_f64(), None);
481 }
482 assert_eq!(object.as_string(), None);
483 assert_eq!(string.as_object(), None);
484 assert_eq!(symbol.as_bigint(), None);
485 assert_eq!(bigint.as_symbol(), None);
486 }
487 }
488}