Skip to main content

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