facet_value/
value.rs

1//! Core `Value` type implementation using tagged pointers.
2//!
3//! # Memory Layout
4//!
5//! `Value` is a single pointer that encodes both the type tag and the data:
6//!
7//! ```text
8//! ┌─────────────────────────────────────────────────────────────┐
9//! │                        64-bit pointer                       │
10//! ├──────────────────────────────────────────────────────┬──────┤
11//! │                   payload (61 bits)                  │ tag  │
12//! │                                                      │(3bit)│
13//! └──────────────────────────────────────────────────────┴──────┘
14//! ```
15//!
16//! ## Inline vs Heap Values
17//!
18//! We distinguish inline values from heap pointers primarily by checking if `ptr < 8`.
19//! Additionally, some tag patterns (like inline short strings) are treated as inline even if
20//! the encoded pointer is ≥ 8 because their payload lives directly in the pointer bits.
21//!
22//! - **Inline values**: Either `ptr < 8` (null/booleans) or the tag explicitly denotes an inline
23//!   payload (e.g. short strings).
24//! - **Heap pointers** (ptr ≥ 8 without an inline tag): Value is `aligned_address | tag`
25//!
26//! Since heap addresses are 8-byte aligned (≥ 8) and tags are < 8, heap pointers
27//! are always ≥ 8 after OR-ing in the tag.
28//!
29//! ```text
30//! NULL:   ptr = 1                      → 1 < 8  → inline, tag=1 → Null
31//! String: ptr = 0x7f8a2000 | 1 = ...001  → ≥8  → heap,   tag=1 → String
32//!                                    └─ tag in low bits
33//! ```
34//!
35//! ## Tag Allocation
36//!
37//! | Tag | Inline (ptr < 8) | Heap (ptr ≥ 8) |
38//! |-----|------------------|----------------|
39//! | 0   | (invalid)        | Number         |
40//! | 1   | Null             | String         |
41//! | 2   | False            | Bytes          |
42//! | 3   | True             | Array          |
43//! | 4   | (invalid)        | Object         |
44//! | 5   | (invalid)        | DateTime       |
45//! | 6   | (inline short string payload) | (inline short string payload) |
46//! | 7   | reserved        | reserved       |
47
48use core::fmt::{self, Debug, Formatter};
49use core::hash::{Hash, Hasher};
50use core::mem;
51use core::ptr::{self, NonNull};
52
53use crate::array::VArray;
54use crate::bytes::VBytes;
55use crate::datetime::VDateTime;
56use crate::number::VNumber;
57use crate::object::VObject;
58use crate::other::{OtherKind, VQName, VUuid, get_other_kind};
59use crate::string::{VSafeString, VString};
60
61/// Alignment for heap-allocated values. Using 8-byte alignment gives us 3 tag bits.
62pub(crate) const ALIGNMENT: usize = 8;
63
64/// Type tags encoded in the low 3 bits of the pointer.
65#[repr(usize)]
66#[derive(Copy, Clone, Debug, PartialEq, Eq)]
67pub(crate) enum TypeTag {
68    /// Number type (always heap-allocated for now)
69    Number = 0,
70    /// String (pointer) or Null (inline when ptr < ALIGNMENT)
71    StringOrNull = 1,
72    /// Bytes (pointer) or False (inline when ptr < ALIGNMENT)
73    BytesOrFalse = 2,
74    /// Array (pointer) or True (inline when ptr < ALIGNMENT)
75    ArrayOrTrue = 3,
76    /// Object type
77    Object = 4,
78    /// DateTime type
79    DateTime = 5,
80    /// Inline short string payload (data encoded directly in the pointer bits)
81    InlineString = 6,
82    /// Extensible "Other" types with secondary discriminant on the heap
83    Other = 7,
84}
85
86impl From<usize> for TypeTag {
87    fn from(other: usize) -> Self {
88        // Safety: We mask to 3 bits, values 0-7 are all valid
89        match other & 0b111 {
90            0 => TypeTag::Number,
91            1 => TypeTag::StringOrNull,
92            2 => TypeTag::BytesOrFalse,
93            3 => TypeTag::ArrayOrTrue,
94            4 => TypeTag::Object,
95            5 => TypeTag::DateTime,
96            6 => TypeTag::InlineString,
97            7 => TypeTag::Other,
98            _ => unreachable!(),
99        }
100    }
101}
102
103/// Enum distinguishing the value types.
104#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
105pub enum ValueType {
106    /// Null value
107    Null,
108    /// Boolean value
109    Bool,
110    /// Number (integers and floats)
111    Number,
112    /// String (UTF-8)
113    String,
114    /// Binary data (useful for binary formats)
115    Bytes,
116    /// Array
117    Array,
118    /// Object (key-value map)
119    Object,
120    /// DateTime (offset, local datetime, local date, or local time)
121    DateTime,
122    /// Qualified name (namespace + local name, for XML namespaces)
123    QName,
124    /// UUID (128-bit universally unique identifier)
125    Uuid,
126}
127
128/// A dynamic value that can represent null, booleans, numbers, strings, bytes, arrays, or objects.
129///
130/// `Value` is exactly one pointer in size and uses tagged pointers for efficient type discrimination.
131/// Small values like null, booleans, and small integers are stored inline without heap allocation.
132#[repr(transparent)]
133pub struct Value {
134    ptr: NonNull<u8>,
135}
136
137// Safety: Value's internal pointer is either a tagged inline value or points to
138// Send+Sync heap data that we own.
139unsafe impl Send for Value {}
140unsafe impl Sync for Value {}
141
142impl Value {
143    // === Constants for inline values ===
144
145    /// JSON `null` value.
146    pub const NULL: Self = unsafe { Self::new_inline(TypeTag::StringOrNull) };
147
148    /// JSON `false` value.
149    pub const FALSE: Self = unsafe { Self::new_inline(TypeTag::BytesOrFalse) };
150
151    /// JSON `true` value.
152    pub const TRUE: Self = unsafe { Self::new_inline(TypeTag::ArrayOrTrue) };
153
154    // === Internal constructors ===
155
156    /// Create an inline value (for null, true, false).
157    /// Safety: Tag must not be Number or Object (those require pointers).
158    const unsafe fn new_inline(tag: TypeTag) -> Self {
159        unsafe {
160            Self {
161                // Use without_provenance since inline values are data packed into
162                // pointer bits, not actual pointers to memory.
163                ptr: NonNull::new_unchecked(ptr::without_provenance_mut(tag as usize)),
164            }
165        }
166    }
167
168    /// Create a value from a heap pointer.
169    /// Safety: Pointer must be non-null and aligned to at least ALIGNMENT.
170    pub(crate) unsafe fn new_ptr(p: *mut u8, tag: TypeTag) -> Self {
171        debug_assert!(!p.is_null());
172        debug_assert!((p as usize).is_multiple_of(ALIGNMENT));
173        unsafe {
174            Self {
175                ptr: NonNull::new_unchecked(p.wrapping_add(tag as usize)),
176            }
177        }
178    }
179
180    /// Create a value from a reference.
181    /// Safety: Reference must be aligned to at least ALIGNMENT.
182    #[allow(dead_code)]
183    pub(crate) unsafe fn new_ref<T>(r: &T, tag: TypeTag) -> Self {
184        unsafe { Self::new_ptr(r as *const T as *mut u8, tag) }
185    }
186
187    // === Internal accessors ===
188
189    /// Raw constructor from inline data bits (e.g., inline short strings).
190    /// Safety: `bits` must be non-zero and encode a valid inline representation.
191    /// This is only for inline values - heap pointers should use `new_ptr`.
192    pub(crate) unsafe fn from_bits(bits: usize) -> Self {
193        debug_assert!(bits != 0);
194        Self {
195            // Use without_provenance since this is inline data packed into
196            // pointer bits, not an actual pointer to memory.
197            ptr: unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(bits)) },
198        }
199    }
200
201    pub(crate) fn ptr_usize(&self) -> usize {
202        self.ptr.as_ptr().addr()
203    }
204
205    fn is_inline(&self) -> bool {
206        self.ptr_usize() < ALIGNMENT || self.is_inline_string()
207    }
208
209    fn type_tag(&self) -> TypeTag {
210        TypeTag::from(self.ptr_usize())
211    }
212
213    /// Returns `true` if the encoded value is an inline short string.
214    #[inline]
215    pub(crate) fn is_inline_string(&self) -> bool {
216        matches!(self.type_tag(), TypeTag::InlineString)
217    }
218
219    /// Get the actual heap pointer (strips the tag bits).
220    /// Safety: Must only be called on non-inline values.
221    pub(crate) fn heap_ptr(&self) -> *const u8 {
222        // Use map_addr to preserve provenance (strict provenance safe)
223        self.ptr.as_ptr().map_addr(|a| a & !(ALIGNMENT - 1)) as *const u8
224    }
225
226    /// Get the actual heap pointer with mutable provenance (strips the tag bits).
227    /// Safety: Must only be called on non-inline values.
228    pub(crate) unsafe fn heap_ptr_mut(&mut self) -> *mut u8 {
229        // Use map_addr to preserve provenance from the mutable reference
230        self.ptr.as_ptr().map_addr(|a| a & !(ALIGNMENT - 1))
231    }
232
233    /// Update the heap pointer while preserving the tag.
234    /// Safety: New pointer must be non-null and aligned to ALIGNMENT.
235    pub(crate) unsafe fn set_ptr(&mut self, ptr: *mut u8) {
236        let tag = self.type_tag();
237        unsafe {
238            self.ptr = NonNull::new_unchecked(ptr.wrapping_add(tag as usize));
239        }
240    }
241
242    /// Raw pointer equality (for comparing interned strings, etc.)
243    #[allow(dead_code)]
244    pub(crate) fn raw_eq(&self, other: &Self) -> bool {
245        self.ptr == other.ptr
246    }
247
248    /// Raw pointer hash
249    #[allow(dead_code)]
250    pub(crate) fn raw_hash<H: Hasher>(&self, state: &mut H) {
251        self.ptr.hash(state);
252    }
253
254    // === Public type checking ===
255
256    /// Returns the type of this value.
257    #[must_use]
258    pub fn value_type(&self) -> ValueType {
259        match (self.type_tag(), self.is_inline()) {
260            // Heap pointers
261            (TypeTag::Number, false) => ValueType::Number,
262            (TypeTag::StringOrNull, false) => ValueType::String,
263            (TypeTag::BytesOrFalse, false) => ValueType::Bytes,
264            (TypeTag::ArrayOrTrue, false) => ValueType::Array,
265            (TypeTag::Object, false) => ValueType::Object,
266            (TypeTag::DateTime, false) => ValueType::DateTime,
267            (TypeTag::InlineString, false) => ValueType::String,
268            (TypeTag::Other, false) => {
269                // Read secondary discriminant from heap
270                match unsafe { get_other_kind(self) } {
271                    OtherKind::QName => ValueType::QName,
272                    OtherKind::Uuid => ValueType::Uuid,
273                }
274            }
275
276            // Inline values
277            (TypeTag::StringOrNull, true) => ValueType::Null,
278            (TypeTag::BytesOrFalse, true) => ValueType::Bool, // false
279            (TypeTag::ArrayOrTrue, true) => ValueType::Bool,  // true
280            (TypeTag::InlineString, true) => ValueType::String,
281
282            // Invalid states (shouldn't happen)
283            (TypeTag::Number, true)
284            | (TypeTag::Object, true)
285            | (TypeTag::DateTime, true)
286            | (TypeTag::Other, true) => {
287                // These tags require heap pointers
288                unreachable!("invalid inline value with Number, Object, DateTime, or Other tag")
289            }
290        }
291    }
292
293    /// Returns `true` if this is the `null` value.
294    #[must_use]
295    pub fn is_null(&self) -> bool {
296        self.ptr == Self::NULL.ptr
297    }
298
299    /// Returns `true` if this is a boolean.
300    #[must_use]
301    pub fn is_bool(&self) -> bool {
302        self.ptr == Self::TRUE.ptr || self.ptr == Self::FALSE.ptr
303    }
304
305    /// Returns `true` if this is `true`.
306    #[must_use]
307    pub fn is_true(&self) -> bool {
308        self.ptr == Self::TRUE.ptr
309    }
310
311    /// Returns `true` if this is `false`.
312    #[must_use]
313    pub fn is_false(&self) -> bool {
314        self.ptr == Self::FALSE.ptr
315    }
316
317    /// Returns `true` if this is a number.
318    #[must_use]
319    pub fn is_number(&self) -> bool {
320        self.type_tag() == TypeTag::Number && !self.is_inline()
321    }
322
323    /// Returns `true` if this is a string.
324    #[must_use]
325    pub fn is_string(&self) -> bool {
326        match self.type_tag() {
327            TypeTag::StringOrNull => !self.is_inline(),
328            TypeTag::InlineString => true,
329            _ => false,
330        }
331    }
332
333    /// Returns `true` if this is bytes.
334    #[must_use]
335    pub fn is_bytes(&self) -> bool {
336        self.type_tag() == TypeTag::BytesOrFalse && !self.is_inline()
337    }
338
339    /// Returns `true` if this is an array.
340    #[must_use]
341    pub fn is_array(&self) -> bool {
342        self.type_tag() == TypeTag::ArrayOrTrue && !self.is_inline()
343    }
344
345    /// Returns `true` if this is an object.
346    #[must_use]
347    pub fn is_object(&self) -> bool {
348        self.type_tag() == TypeTag::Object && !self.is_inline()
349    }
350
351    /// Returns `true` if this is a datetime.
352    #[must_use]
353    pub fn is_datetime(&self) -> bool {
354        self.type_tag() == TypeTag::DateTime && !self.is_inline()
355    }
356
357    /// Returns `true` if this is a qualified name.
358    #[must_use]
359    pub fn is_qname(&self) -> bool {
360        self.value_type() == ValueType::QName
361    }
362
363    /// Returns `true` if this is a UUID.
364    #[must_use]
365    pub fn is_uuid(&self) -> bool {
366        self.value_type() == ValueType::Uuid
367    }
368
369    // === Conversions to concrete types ===
370
371    /// Converts this value to a `bool`. Returns `None` if not a boolean.
372    #[must_use]
373    pub fn as_bool(&self) -> Option<bool> {
374        if self.is_bool() {
375            Some(self.is_true())
376        } else {
377            None
378        }
379    }
380
381    /// Gets a reference to this value as a `VNumber`. Returns `None` if not a number.
382    #[must_use]
383    pub fn as_number(&self) -> Option<&VNumber> {
384        if self.is_number() {
385            // Safety: We checked the type, and VNumber is repr(transparent) over Value
386            Some(unsafe { &*(self as *const Value as *const VNumber) })
387        } else {
388            None
389        }
390    }
391
392    /// Gets a mutable reference to this value as a `VNumber`.
393    pub fn as_number_mut(&mut self) -> Option<&mut VNumber> {
394        if self.is_number() {
395            Some(unsafe { &mut *(self as *mut Value as *mut VNumber) })
396        } else {
397            None
398        }
399    }
400
401    /// Gets a reference to this value as a `VString`. Returns `None` if not a string.
402    #[must_use]
403    pub fn as_string(&self) -> Option<&VString> {
404        if self.is_string() {
405            Some(unsafe { &*(self as *const Value as *const VString) })
406        } else {
407            None
408        }
409    }
410
411    /// Gets a mutable reference to this value as a `VString`.
412    pub fn as_string_mut(&mut self) -> Option<&mut VString> {
413        if self.is_string() {
414            Some(unsafe { &mut *(self as *mut Value as *mut VString) })
415        } else {
416            None
417        }
418    }
419
420    /// Returns `true` if this is a safe string (marked as pre-escaped HTML, etc.).
421    ///
422    /// A safe string is a string with the safe flag set. Inline strings are never safe.
423    #[must_use]
424    pub fn is_safe_string(&self) -> bool {
425        self.as_string().is_some_and(|s| s.is_safe())
426    }
427
428    /// Gets a reference to this value as a `VSafeString`. Returns `None` if not a safe string.
429    #[must_use]
430    pub fn as_safe_string(&self) -> Option<&VSafeString> {
431        if self.is_safe_string() {
432            Some(unsafe { &*(self as *const Value as *const VSafeString) })
433        } else {
434            None
435        }
436    }
437
438    /// Gets a mutable reference to this value as a `VSafeString`.
439    pub fn as_safe_string_mut(&mut self) -> Option<&mut VSafeString> {
440        if self.is_safe_string() {
441            Some(unsafe { &mut *(self as *mut Value as *mut VSafeString) })
442        } else {
443            None
444        }
445    }
446
447    /// Gets a reference to this value as `VBytes`. Returns `None` if not bytes.
448    #[must_use]
449    pub fn as_bytes(&self) -> Option<&VBytes> {
450        if self.is_bytes() {
451            Some(unsafe { &*(self as *const Value as *const VBytes) })
452        } else {
453            None
454        }
455    }
456
457    /// Gets a mutable reference to this value as `VBytes`.
458    pub fn as_bytes_mut(&mut self) -> Option<&mut VBytes> {
459        if self.is_bytes() {
460            Some(unsafe { &mut *(self as *mut Value as *mut VBytes) })
461        } else {
462            None
463        }
464    }
465
466    /// Gets a reference to this value as a `VArray`. Returns `None` if not an array.
467    #[must_use]
468    pub fn as_array(&self) -> Option<&VArray> {
469        if self.is_array() {
470            Some(unsafe { &*(self as *const Value as *const VArray) })
471        } else {
472            None
473        }
474    }
475
476    /// Gets a mutable reference to this value as a `VArray`.
477    pub fn as_array_mut(&mut self) -> Option<&mut VArray> {
478        if self.is_array() {
479            Some(unsafe { &mut *(self as *mut Value as *mut VArray) })
480        } else {
481            None
482        }
483    }
484
485    /// Gets a reference to this value as a `VObject`. Returns `None` if not an object.
486    #[must_use]
487    pub fn as_object(&self) -> Option<&VObject> {
488        if self.is_object() {
489            Some(unsafe { &*(self as *const Value as *const VObject) })
490        } else {
491            None
492        }
493    }
494
495    /// Gets a mutable reference to this value as a `VObject`.
496    pub fn as_object_mut(&mut self) -> Option<&mut VObject> {
497        if self.is_object() {
498            Some(unsafe { &mut *(self as *mut Value as *mut VObject) })
499        } else {
500            None
501        }
502    }
503
504    /// Gets a reference to this value as a `VDateTime`. Returns `None` if not a datetime.
505    #[must_use]
506    pub fn as_datetime(&self) -> Option<&VDateTime> {
507        if self.is_datetime() {
508            Some(unsafe { &*(self as *const Value as *const VDateTime) })
509        } else {
510            None
511        }
512    }
513
514    /// Gets a mutable reference to this value as a `VDateTime`.
515    pub fn as_datetime_mut(&mut self) -> Option<&mut VDateTime> {
516        if self.is_datetime() {
517            Some(unsafe { &mut *(self as *mut Value as *mut VDateTime) })
518        } else {
519            None
520        }
521    }
522
523    /// Gets a reference to this value as a `VQName`. Returns `None` if not a qualified name.
524    #[must_use]
525    pub fn as_qname(&self) -> Option<&VQName> {
526        if self.is_qname() {
527            Some(unsafe { &*(self as *const Value as *const VQName) })
528        } else {
529            None
530        }
531    }
532
533    /// Gets a mutable reference to this value as a `VQName`.
534    pub fn as_qname_mut(&mut self) -> Option<&mut VQName> {
535        if self.is_qname() {
536            Some(unsafe { &mut *(self as *mut Value as *mut VQName) })
537        } else {
538            None
539        }
540    }
541
542    /// Gets a reference to this value as a `VUuid`. Returns `None` if not a UUID.
543    #[must_use]
544    pub fn as_uuid(&self) -> Option<&VUuid> {
545        if self.is_uuid() {
546            Some(unsafe { &*(self as *const Value as *const VUuid) })
547        } else {
548            None
549        }
550    }
551
552    /// Gets a mutable reference to this value as a `VUuid`.
553    pub fn as_uuid_mut(&mut self) -> Option<&mut VUuid> {
554        if self.is_uuid() {
555            Some(unsafe { &mut *(self as *mut Value as *mut VUuid) })
556        } else {
557            None
558        }
559    }
560
561    /// Takes this value, replacing it with `Value::NULL`.
562    pub fn take(&mut self) -> Value {
563        mem::replace(self, Value::NULL)
564    }
565}
566
567// === Clone ===
568
569impl Clone for Value {
570    fn clone(&self) -> Self {
571        match self.value_type() {
572            ValueType::Null | ValueType::Bool => {
573                // Inline values can be trivially copied
574                Self { ptr: self.ptr }
575            }
576            ValueType::Number => unsafe { self.as_number().unwrap_unchecked() }.clone_impl(),
577            ValueType::String => unsafe { self.as_string().unwrap_unchecked() }.clone_impl(),
578            ValueType::Bytes => unsafe { self.as_bytes().unwrap_unchecked() }.clone_impl(),
579            ValueType::Array => unsafe { self.as_array().unwrap_unchecked() }.clone_impl(),
580            ValueType::Object => unsafe { self.as_object().unwrap_unchecked() }.clone_impl(),
581            ValueType::DateTime => unsafe { self.as_datetime().unwrap_unchecked() }.clone_impl(),
582            ValueType::QName => unsafe { self.as_qname().unwrap_unchecked() }.clone_impl(),
583            ValueType::Uuid => unsafe { self.as_uuid().unwrap_unchecked() }.clone_impl(),
584        }
585    }
586}
587
588// === Drop ===
589
590impl Drop for Value {
591    fn drop(&mut self) {
592        match self.value_type() {
593            ValueType::Null | ValueType::Bool => {
594                // Inline values don't need dropping
595            }
596            ValueType::Number => unsafe { self.as_number_mut().unwrap_unchecked() }.drop_impl(),
597            ValueType::String => unsafe { self.as_string_mut().unwrap_unchecked() }.drop_impl(),
598            ValueType::Bytes => unsafe { self.as_bytes_mut().unwrap_unchecked() }.drop_impl(),
599            ValueType::Array => unsafe { self.as_array_mut().unwrap_unchecked() }.drop_impl(),
600            ValueType::Object => unsafe { self.as_object_mut().unwrap_unchecked() }.drop_impl(),
601            ValueType::DateTime => unsafe { self.as_datetime_mut().unwrap_unchecked() }.drop_impl(),
602            ValueType::QName => unsafe { self.as_qname_mut().unwrap_unchecked() }.drop_impl(),
603            ValueType::Uuid => unsafe { self.as_uuid_mut().unwrap_unchecked() }.drop_impl(),
604        }
605    }
606}
607
608// === PartialEq, Eq ===
609
610impl PartialEq for Value {
611    fn eq(&self, other: &Self) -> bool {
612        let (t1, t2) = (self.value_type(), other.value_type());
613        if t1 != t2 {
614            return false;
615        }
616
617        match t1 {
618            ValueType::Null | ValueType::Bool => self.ptr == other.ptr,
619            ValueType::Number => unsafe {
620                self.as_number().unwrap_unchecked() == other.as_number().unwrap_unchecked()
621            },
622            ValueType::String => unsafe {
623                self.as_string().unwrap_unchecked() == other.as_string().unwrap_unchecked()
624            },
625            ValueType::Bytes => unsafe {
626                self.as_bytes().unwrap_unchecked() == other.as_bytes().unwrap_unchecked()
627            },
628            ValueType::Array => unsafe {
629                self.as_array().unwrap_unchecked() == other.as_array().unwrap_unchecked()
630            },
631            ValueType::Object => unsafe {
632                self.as_object().unwrap_unchecked() == other.as_object().unwrap_unchecked()
633            },
634            ValueType::DateTime => unsafe {
635                self.as_datetime().unwrap_unchecked() == other.as_datetime().unwrap_unchecked()
636            },
637            ValueType::QName => unsafe {
638                self.as_qname().unwrap_unchecked() == other.as_qname().unwrap_unchecked()
639            },
640            ValueType::Uuid => unsafe {
641                self.as_uuid().unwrap_unchecked() == other.as_uuid().unwrap_unchecked()
642            },
643        }
644    }
645}
646
647impl Eq for Value {}
648
649// === PartialOrd ===
650
651impl PartialOrd for Value {
652    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
653        use core::cmp::Ordering;
654
655        let (t1, t2) = (self.value_type(), other.value_type());
656
657        // Different types: compare by type discriminant
658        if t1 != t2 {
659            return t1.partial_cmp(&t2);
660        }
661
662        // Same type: compare values
663        match t1 {
664            ValueType::Null => Some(Ordering::Equal),
665            ValueType::Bool => self.is_true().partial_cmp(&other.is_true()),
666            ValueType::Number => unsafe {
667                self.as_number()
668                    .unwrap_unchecked()
669                    .partial_cmp(other.as_number().unwrap_unchecked())
670            },
671            ValueType::String => unsafe {
672                self.as_string()
673                    .unwrap_unchecked()
674                    .partial_cmp(other.as_string().unwrap_unchecked())
675            },
676            ValueType::Bytes => unsafe {
677                self.as_bytes()
678                    .unwrap_unchecked()
679                    .partial_cmp(other.as_bytes().unwrap_unchecked())
680            },
681            ValueType::Array => unsafe {
682                self.as_array()
683                    .unwrap_unchecked()
684                    .partial_cmp(other.as_array().unwrap_unchecked())
685            },
686            // Objects don't have a natural ordering
687            ValueType::Object => None,
688            // DateTime comparison (returns None for different kinds)
689            ValueType::DateTime => unsafe {
690                self.as_datetime()
691                    .unwrap_unchecked()
692                    .partial_cmp(other.as_datetime().unwrap_unchecked())
693            },
694            // QNames don't have a natural ordering
695            ValueType::QName => None,
696            // UUIDs can be compared by their byte representation
697            ValueType::Uuid => unsafe {
698                self.as_uuid()
699                    .unwrap_unchecked()
700                    .as_bytes()
701                    .partial_cmp(other.as_uuid().unwrap_unchecked().as_bytes())
702            },
703        }
704    }
705}
706
707// === Hash ===
708
709impl Hash for Value {
710    fn hash<H: Hasher>(&self, state: &mut H) {
711        // Hash the type first
712        self.value_type().hash(state);
713
714        match self.value_type() {
715            ValueType::Null => {}
716            ValueType::Bool => self.is_true().hash(state),
717            ValueType::Number => unsafe { self.as_number().unwrap_unchecked() }.hash(state),
718            ValueType::String => unsafe { self.as_string().unwrap_unchecked() }.hash(state),
719            ValueType::Bytes => unsafe { self.as_bytes().unwrap_unchecked() }.hash(state),
720            ValueType::Array => unsafe { self.as_array().unwrap_unchecked() }.hash(state),
721            ValueType::Object => unsafe { self.as_object().unwrap_unchecked() }.hash(state),
722            ValueType::DateTime => unsafe { self.as_datetime().unwrap_unchecked() }.hash(state),
723            ValueType::QName => unsafe { self.as_qname().unwrap_unchecked() }.hash(state),
724            ValueType::Uuid => unsafe { self.as_uuid().unwrap_unchecked() }.hash(state),
725        }
726    }
727}
728
729// === Debug ===
730
731impl Debug for Value {
732    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
733        match self.value_type() {
734            ValueType::Null => f.write_str("null"),
735            ValueType::Bool => Debug::fmt(&self.is_true(), f),
736            ValueType::Number => Debug::fmt(unsafe { self.as_number().unwrap_unchecked() }, f),
737            ValueType::String => Debug::fmt(unsafe { self.as_string().unwrap_unchecked() }, f),
738            ValueType::Bytes => Debug::fmt(unsafe { self.as_bytes().unwrap_unchecked() }, f),
739            ValueType::Array => Debug::fmt(unsafe { self.as_array().unwrap_unchecked() }, f),
740            ValueType::Object => Debug::fmt(unsafe { self.as_object().unwrap_unchecked() }, f),
741            ValueType::DateTime => Debug::fmt(unsafe { self.as_datetime().unwrap_unchecked() }, f),
742            ValueType::QName => Debug::fmt(unsafe { self.as_qname().unwrap_unchecked() }, f),
743            ValueType::Uuid => Debug::fmt(unsafe { self.as_uuid().unwrap_unchecked() }, f),
744        }
745    }
746}
747
748// === Default ===
749
750impl Default for Value {
751    fn default() -> Self {
752        Self::NULL
753    }
754}
755
756// === From implementations ===
757
758impl From<bool> for Value {
759    fn from(b: bool) -> Self {
760        if b { Self::TRUE } else { Self::FALSE }
761    }
762}
763
764impl<T: Into<Value>> From<Option<T>> for Value {
765    fn from(opt: Option<T>) -> Self {
766        match opt {
767            Some(v) => v.into(),
768            None => Self::NULL,
769        }
770    }
771}
772
773// === FromIterator implementations ===
774
775#[cfg(feature = "alloc")]
776impl<T: Into<Value>> core::iter::FromIterator<T> for Value {
777    /// Collect into an array Value.
778    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
779        VArray::from_iter(iter).into()
780    }
781}
782
783#[cfg(feature = "alloc")]
784impl<K: Into<VString>, V: Into<Value>> core::iter::FromIterator<(K, V)> for Value {
785    /// Collect key-value pairs into an object Value.
786    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
787        VObject::from_iter(iter).into()
788    }
789}
790
791/// Enum for destructuring a `Value` by ownership.
792#[derive(Debug, Clone, PartialEq)]
793pub enum Destructured {
794    /// Null value
795    Null,
796    /// Boolean value
797    Bool(bool),
798    /// Number value
799    Number(VNumber),
800    /// String value
801    String(VString),
802    /// Bytes value
803    Bytes(VBytes),
804    /// Array value
805    Array(VArray),
806    /// Object value
807    Object(VObject),
808    /// DateTime value
809    DateTime(VDateTime),
810    /// Qualified name value
811    QName(VQName),
812    /// UUID value
813    Uuid(VUuid),
814}
815
816/// Enum for destructuring a `Value` by reference.
817#[derive(Debug, Copy, Clone, PartialEq)]
818pub enum DestructuredRef<'a> {
819    /// Null value
820    Null,
821    /// Boolean value
822    Bool(bool),
823    /// Number value
824    Number(&'a VNumber),
825    /// String value
826    String(&'a VString),
827    /// Bytes value
828    Bytes(&'a VBytes),
829    /// Array value
830    Array(&'a VArray),
831    /// Object value
832    Object(&'a VObject),
833    /// DateTime value
834    DateTime(&'a VDateTime),
835    /// Qualified name value
836    QName(&'a VQName),
837    /// UUID value
838    Uuid(&'a VUuid),
839}
840
841/// Enum for destructuring a `Value` by mutable reference.
842#[derive(Debug)]
843pub enum DestructuredMut<'a> {
844    /// Null value
845    Null,
846    /// Boolean value (use the mutable reference to the Value itself to change it)
847    Bool(bool),
848    /// Number value
849    Number(&'a mut VNumber),
850    /// String value
851    String(&'a mut VString),
852    /// Bytes value
853    Bytes(&'a mut VBytes),
854    /// Array value
855    Array(&'a mut VArray),
856    /// Object value
857    Object(&'a mut VObject),
858    /// DateTime value
859    DateTime(&'a mut VDateTime),
860    /// Qualified name value
861    QName(&'a mut VQName),
862    /// UUID value
863    Uuid(&'a mut VUuid),
864}
865
866impl Value {
867    /// Destructure this value into an enum for pattern matching (by ownership).
868    #[must_use]
869    pub fn destructure(self) -> Destructured {
870        match self.value_type() {
871            ValueType::Null => Destructured::Null,
872            ValueType::Bool => Destructured::Bool(self.is_true()),
873            ValueType::Number => Destructured::Number(VNumber(self)),
874            ValueType::String => Destructured::String(VString(self)),
875            ValueType::Bytes => Destructured::Bytes(VBytes(self)),
876            ValueType::Array => Destructured::Array(VArray(self)),
877            ValueType::Object => Destructured::Object(VObject(self)),
878            ValueType::DateTime => Destructured::DateTime(VDateTime(self)),
879            ValueType::QName => Destructured::QName(VQName(self)),
880            ValueType::Uuid => Destructured::Uuid(VUuid(self)),
881        }
882    }
883
884    /// Destructure this value into an enum for pattern matching (by reference).
885    #[must_use]
886    pub fn destructure_ref(&self) -> DestructuredRef<'_> {
887        match self.value_type() {
888            ValueType::Null => DestructuredRef::Null,
889            ValueType::Bool => DestructuredRef::Bool(self.is_true()),
890            ValueType::Number => {
891                DestructuredRef::Number(unsafe { self.as_number().unwrap_unchecked() })
892            }
893            ValueType::String => {
894                DestructuredRef::String(unsafe { self.as_string().unwrap_unchecked() })
895            }
896            ValueType::Bytes => {
897                DestructuredRef::Bytes(unsafe { self.as_bytes().unwrap_unchecked() })
898            }
899            ValueType::Array => {
900                DestructuredRef::Array(unsafe { self.as_array().unwrap_unchecked() })
901            }
902            ValueType::Object => {
903                DestructuredRef::Object(unsafe { self.as_object().unwrap_unchecked() })
904            }
905            ValueType::DateTime => {
906                DestructuredRef::DateTime(unsafe { self.as_datetime().unwrap_unchecked() })
907            }
908            ValueType::QName => {
909                DestructuredRef::QName(unsafe { self.as_qname().unwrap_unchecked() })
910            }
911            ValueType::Uuid => DestructuredRef::Uuid(unsafe { self.as_uuid().unwrap_unchecked() }),
912        }
913    }
914
915    /// Destructure this value into an enum for pattern matching (by mutable reference).
916    pub fn destructure_mut(&mut self) -> DestructuredMut<'_> {
917        match self.value_type() {
918            ValueType::Null => DestructuredMut::Null,
919            ValueType::Bool => DestructuredMut::Bool(self.is_true()),
920            ValueType::Number => {
921                DestructuredMut::Number(unsafe { self.as_number_mut().unwrap_unchecked() })
922            }
923            ValueType::String => {
924                DestructuredMut::String(unsafe { self.as_string_mut().unwrap_unchecked() })
925            }
926            ValueType::Bytes => {
927                DestructuredMut::Bytes(unsafe { self.as_bytes_mut().unwrap_unchecked() })
928            }
929            ValueType::Array => {
930                DestructuredMut::Array(unsafe { self.as_array_mut().unwrap_unchecked() })
931            }
932            ValueType::Object => {
933                DestructuredMut::Object(unsafe { self.as_object_mut().unwrap_unchecked() })
934            }
935            ValueType::DateTime => {
936                DestructuredMut::DateTime(unsafe { self.as_datetime_mut().unwrap_unchecked() })
937            }
938            ValueType::QName => {
939                DestructuredMut::QName(unsafe { self.as_qname_mut().unwrap_unchecked() })
940            }
941            ValueType::Uuid => {
942                DestructuredMut::Uuid(unsafe { self.as_uuid_mut().unwrap_unchecked() })
943            }
944        }
945    }
946}
947
948#[cfg(test)]
949mod tests {
950    use super::*;
951    use crate::string::VString;
952
953    #[test]
954    fn test_size() {
955        assert_eq!(
956            core::mem::size_of::<Value>(),
957            core::mem::size_of::<usize>(),
958            "Value should be pointer-sized"
959        );
960        assert_eq!(
961            core::mem::size_of::<Option<Value>>(),
962            core::mem::size_of::<usize>(),
963            "Option<Value> should be pointer-sized (niche optimization)"
964        );
965    }
966
967    #[test]
968    fn test_null() {
969        let v = Value::NULL;
970        assert!(v.is_null());
971        assert_eq!(v.value_type(), ValueType::Null);
972        assert!(!v.is_bool());
973        assert!(!v.is_number());
974    }
975
976    #[test]
977    fn test_bool() {
978        let t = Value::TRUE;
979        let f = Value::FALSE;
980
981        assert!(t.is_bool());
982        assert!(t.is_true());
983        assert!(!t.is_false());
984        assert_eq!(t.as_bool(), Some(true));
985
986        assert!(f.is_bool());
987        assert!(f.is_false());
988        assert!(!f.is_true());
989        assert_eq!(f.as_bool(), Some(false));
990
991        assert_eq!(Value::from(true), Value::TRUE);
992        assert_eq!(Value::from(false), Value::FALSE);
993    }
994
995    #[test]
996    fn test_clone_inline() {
997        let v = Value::TRUE;
998        let v2 = v.clone();
999        assert_eq!(v, v2);
1000    }
1001
1002    #[test]
1003    fn test_inline_short_string() {
1004        let v: Value = VString::new("inline").into();
1005        assert_eq!(v.value_type(), ValueType::String);
1006        assert!(v.is_string());
1007        assert!(v.is_inline());
1008    }
1009
1010    #[test]
1011    fn short_strings_are_stored_inline() {
1012        for len in 0..=VString::INLINE_LEN_MAX {
1013            let data = "s".repeat(len);
1014            let v = Value::from(data.as_str());
1015            assert!(
1016                v.is_inline_string(),
1017                "expected inline string for length {len}, ptr={:#x}",
1018                v.ptr_usize()
1019            );
1020            assert!(
1021                v.is_inline(),
1022                "inline flag should be true for strings of length {len}"
1023            );
1024            assert_eq!(
1025                v.as_string().unwrap().as_str(),
1026                data,
1027                "round-trip mismatch for inline string"
1028            );
1029        }
1030    }
1031
1032    #[test]
1033    fn long_strings_force_heap_storage() {
1034        let long = "l".repeat(VString::INLINE_LEN_MAX + 16);
1035        let v = Value::from(long.as_str());
1036        assert!(
1037            !v.is_inline_string(),
1038            "expected heap storage for long string ptr={:#x}",
1039            v.ptr_usize()
1040        );
1041        assert_eq!(
1042            v.as_string().unwrap().as_str(),
1043            long,
1044            "heap string should round-trip"
1045        );
1046    }
1047
1048    #[test]
1049    fn clone_preserves_inline_string_representation() {
1050        let original = Value::from("inline");
1051        assert!(original.is_inline_string());
1052        let clone = original.clone();
1053        assert!(
1054            clone.is_inline_string(),
1055            "clone lost inline tag for ptr={:#x}",
1056            clone.ptr_usize()
1057        );
1058        assert_eq!(
1059            clone.as_string().unwrap().as_str(),
1060            "inline",
1061            "clone should preserve payload"
1062        );
1063    }
1064
1065    #[test]
1066    fn string_mutations_transition_inline_and_heap() {
1067        let mut value = Value::from("seed");
1068        assert!(value.is_inline_string());
1069
1070        // Grow the string beyond inline capacity.
1071        {
1072            let slot = value.as_string_mut().expect("string value");
1073            let mut owned = slot.to_string();
1074            while owned.len() <= VString::INLINE_LEN_MAX {
1075                owned.push('g');
1076            }
1077            // Ensure we crossed the boundary by at least 4 bytes for good measure.
1078            owned.push_str("OVERFLOW");
1079            *slot = VString::new(&owned);
1080        }
1081        assert!(
1082            !value.is_inline_string(),
1083            "string expected to spill to heap after grow"
1084        );
1085
1086        // Shrink back to inline size.
1087        {
1088            let slot = value.as_string_mut().expect("string value");
1089            let mut owned = slot.to_string();
1090            owned.truncate(VString::INLINE_LEN_MAX);
1091            *slot = VString::new(&owned);
1092        }
1093        assert!(
1094            value.is_inline_string(),
1095            "string should return to inline storage after shrink"
1096        );
1097    }
1098}