facet_value/
object.rs

1//! Object (map) value type.
2
3#[cfg(feature = "alloc")]
4use alloc::alloc::{Layout, alloc, dealloc, realloc};
5#[cfg(feature = "alloc")]
6use alloc::borrow::ToOwned;
7#[cfg(feature = "std")]
8use alloc::boxed::Box;
9#[cfg(feature = "alloc")]
10use alloc::collections::BTreeMap;
11use core::fmt::{self, Debug, Formatter};
12use core::hash::{Hash, Hasher};
13use core::iter::FromIterator;
14use core::ops::{Index, IndexMut};
15use core::{cmp, mem, ptr};
16
17#[cfg(feature = "std")]
18use indexmap::IndexMap;
19#[cfg(feature = "std")]
20use std::collections::HashMap;
21
22use crate::string::VString;
23use crate::value::{TypeTag, Value};
24
25/// Threshold at which we switch from inline array to IndexMap storage.
26/// Below this size, linear search is competitive with hash lookups due to cache locality.
27#[cfg(feature = "std")]
28const LARGE_MODE_THRESHOLD: usize = 32;
29
30/// Sentinel value for capacity indicating large mode (IndexMap storage).
31#[cfg(feature = "std")]
32const LARGE_MODE_CAP_SENTINEL: usize = usize::MAX;
33
34/// A key-value pair.
35#[repr(C)]
36struct KeyValuePair {
37    key: VString,
38    value: Value,
39}
40
41/// Header for heap-allocated objects in small mode.
42#[repr(C, align(8))]
43struct ObjectHeader {
44    /// Number of key-value pairs
45    len: usize,
46    /// Capacity (usize::MAX indicates large mode with IndexMap storage)
47    cap: usize,
48    // Array of KeyValuePair follows immediately after (only in small mode)
49}
50
51/// Wrapper for IndexMap storage in large mode.
52/// Uses the same layout prefix as ObjectHeader so we can detect the mode.
53#[cfg(feature = "std")]
54#[repr(C, align(8))]
55struct LargeModeStorage {
56    /// Unused in large mode, but must be at same offset as ObjectHeader.len
57    _len_unused: usize,
58    /// Sentinel value (usize::MAX) to indicate large mode
59    cap_sentinel: usize,
60    /// The actual IndexMap
61    map: IndexMap<VString, Value>,
62}
63
64/// An object (map) value.
65///
66/// `VObject` is an ordered map of string keys to `Value`s.
67/// It preserves insertion order.
68///
69/// Storage modes:
70/// - Small mode (default): inline array of KeyValuePair with linear search
71/// - Large mode (std feature, >= 32 entries): IndexMap for O(1) lookups
72#[repr(transparent)]
73#[derive(Clone)]
74pub struct VObject(pub(crate) Value);
75
76impl VObject {
77    fn layout(cap: usize) -> Layout {
78        Layout::new::<ObjectHeader>()
79            .extend(Layout::array::<KeyValuePair>(cap).unwrap())
80            .unwrap()
81            .0
82            .pad_to_align()
83    }
84
85    #[cfg(feature = "alloc")]
86    fn alloc(cap: usize) -> *mut ObjectHeader {
87        unsafe {
88            let layout = Self::layout(cap);
89            let ptr = alloc(layout).cast::<ObjectHeader>();
90            (*ptr).len = 0;
91            (*ptr).cap = cap;
92            ptr
93        }
94    }
95
96    #[cfg(feature = "alloc")]
97    fn realloc_ptr(ptr: *mut ObjectHeader, new_cap: usize) -> *mut ObjectHeader {
98        unsafe {
99            let old_cap = (*ptr).cap;
100            let old_layout = Self::layout(old_cap);
101            let new_layout = Self::layout(new_cap);
102            let new_ptr =
103                realloc(ptr.cast::<u8>(), old_layout, new_layout.size()).cast::<ObjectHeader>();
104            (*new_ptr).cap = new_cap;
105            new_ptr
106        }
107    }
108
109    #[cfg(feature = "alloc")]
110    fn dealloc_ptr(ptr: *mut ObjectHeader) {
111        unsafe {
112            let cap = (*ptr).cap;
113            let layout = Self::layout(cap);
114            dealloc(ptr.cast::<u8>(), layout);
115        }
116    }
117
118    /// Returns true if this object is in large mode (IndexMap storage).
119    #[cfg(feature = "std")]
120    #[inline]
121    fn is_large_mode(&self) -> bool {
122        // In large mode, the cap_sentinel field (at same offset as ObjectHeader.cap)
123        // is set to LARGE_MODE_CAP_SENTINEL
124        unsafe {
125            let header = self.0.heap_ptr() as *const ObjectHeader;
126            (*header).cap == LARGE_MODE_CAP_SENTINEL
127        }
128    }
129
130    /// Returns true if this object is in large mode (IndexMap storage).
131    #[cfg(not(feature = "std"))]
132    #[inline]
133    fn is_large_mode(&self) -> bool {
134        // Without std, we never use large mode
135        false
136    }
137
138    /// Returns a reference to the IndexMap (large mode only).
139    #[cfg(feature = "std")]
140    #[inline]
141    fn as_indexmap(&self) -> &IndexMap<VString, Value> {
142        debug_assert!(self.is_large_mode());
143        unsafe {
144            let storage = self.0.heap_ptr() as *const LargeModeStorage;
145            &(*storage).map
146        }
147    }
148
149    /// Returns a mutable reference to the IndexMap (large mode only).
150    #[cfg(feature = "std")]
151    #[inline]
152    fn as_indexmap_mut(&mut self) -> &mut IndexMap<VString, Value> {
153        debug_assert!(self.is_large_mode());
154        unsafe {
155            let storage = self.0.heap_ptr_mut() as *mut LargeModeStorage;
156            &mut (*storage).map
157        }
158    }
159
160    fn header(&self) -> &ObjectHeader {
161        debug_assert!(!self.is_large_mode());
162        unsafe { &*(self.0.heap_ptr() as *const ObjectHeader) }
163    }
164
165    fn header_mut(&mut self) -> &mut ObjectHeader {
166        debug_assert!(!self.is_large_mode());
167        unsafe { &mut *(self.0.heap_ptr_mut() as *mut ObjectHeader) }
168    }
169
170    fn items_ptr(&self) -> *const KeyValuePair {
171        debug_assert!(!self.is_large_mode());
172        // Go through heap_ptr directly to avoid creating intermediate reference
173        // that would limit provenance to just the header
174        unsafe { (self.0.heap_ptr() as *const ObjectHeader).add(1).cast() }
175    }
176
177    fn items_ptr_mut(&mut self) -> *mut KeyValuePair {
178        debug_assert!(!self.is_large_mode());
179        // Use heap_ptr_mut directly to preserve mutable provenance
180        unsafe { (self.0.heap_ptr_mut() as *mut ObjectHeader).add(1).cast() }
181    }
182
183    fn items(&self) -> &[KeyValuePair] {
184        debug_assert!(!self.is_large_mode());
185        unsafe { core::slice::from_raw_parts(self.items_ptr(), self.small_len()) }
186    }
187
188    fn items_mut(&mut self) -> &mut [KeyValuePair] {
189        debug_assert!(!self.is_large_mode());
190        unsafe { core::slice::from_raw_parts_mut(self.items_ptr_mut(), self.small_len()) }
191    }
192
193    /// Returns the length when in small mode.
194    #[inline]
195    fn small_len(&self) -> usize {
196        debug_assert!(!self.is_large_mode());
197        self.header().len
198    }
199
200    /// Converts from small mode to large mode (IndexMap).
201    #[cfg(feature = "std")]
202    fn convert_to_large_mode(&mut self) {
203        debug_assert!(!self.is_large_mode());
204
205        // Build IndexMap from existing items
206        let mut map = IndexMap::with_capacity(self.small_len() + 1);
207        unsafe {
208            let len = self.small_len();
209            let items_ptr = self.items_ptr_mut();
210
211            // Move items into the IndexMap (taking ownership)
212            for i in 0..len {
213                let kv = items_ptr.add(i).read();
214                map.insert(kv.key, kv.value);
215            }
216
217            // Free the old small-mode allocation
218            Self::dealloc_ptr(self.0.heap_ptr_mut().cast());
219
220            // Allocate and store the LargeModeStorage
221            let storage = LargeModeStorage {
222                _len_unused: 0,
223                cap_sentinel: LARGE_MODE_CAP_SENTINEL,
224                map,
225            };
226            let boxed = Box::new(storage);
227            let ptr = Box::into_raw(boxed);
228            self.0.set_ptr(ptr.cast());
229        }
230    }
231
232    /// Creates a new empty object.
233    #[cfg(feature = "alloc")]
234    #[inline]
235    #[must_use]
236    pub fn new() -> Self {
237        Self::with_capacity(0)
238    }
239
240    /// Creates a new object with the specified capacity.
241    #[cfg(feature = "alloc")]
242    #[must_use]
243    pub fn with_capacity(cap: usize) -> Self {
244        // For large initial capacity with std feature, start directly in large mode
245        #[cfg(feature = "std")]
246        if cap >= LARGE_MODE_THRESHOLD {
247            let storage = LargeModeStorage {
248                _len_unused: 0,
249                cap_sentinel: LARGE_MODE_CAP_SENTINEL,
250                map: IndexMap::with_capacity(cap),
251            };
252            let boxed = Box::new(storage);
253            let ptr = Box::into_raw(boxed);
254            return VObject(unsafe { Value::new_ptr(ptr.cast(), TypeTag::Object) });
255        }
256
257        unsafe {
258            let ptr = Self::alloc(cap);
259            VObject(Value::new_ptr(ptr.cast(), TypeTag::Object))
260        }
261    }
262
263    /// Returns the number of entries.
264    #[inline]
265    #[must_use]
266    pub fn len(&self) -> usize {
267        #[cfg(feature = "std")]
268        if self.is_large_mode() {
269            return self.as_indexmap().len();
270        }
271        self.header().len
272    }
273
274    /// Returns `true` if the object is empty.
275    #[inline]
276    #[must_use]
277    pub fn is_empty(&self) -> bool {
278        self.len() == 0
279    }
280
281    /// Returns the capacity.
282    #[inline]
283    #[must_use]
284    pub fn capacity(&self) -> usize {
285        #[cfg(feature = "std")]
286        if self.is_large_mode() {
287            return self.as_indexmap().capacity();
288        }
289        self.header().cap
290    }
291
292    /// Reserves capacity for at least `additional` more entries.
293    #[cfg(feature = "alloc")]
294    pub fn reserve(&mut self, additional: usize) {
295        #[cfg(feature = "std")]
296        if self.is_large_mode() {
297            self.as_indexmap_mut().reserve(additional);
298            return;
299        }
300
301        let current_cap = self.capacity();
302        let desired_cap = self
303            .len()
304            .checked_add(additional)
305            .expect("capacity overflow");
306
307        if current_cap >= desired_cap {
308            return;
309        }
310
311        let new_cap = cmp::max(current_cap * 2, desired_cap.max(4));
312
313        unsafe {
314            let new_ptr = Self::realloc_ptr(self.0.heap_ptr_mut().cast(), new_cap);
315            self.0.set_ptr(new_ptr.cast());
316        }
317    }
318
319    /// Gets a value by key.
320    #[inline]
321    #[must_use]
322    pub fn get(&self, key: &str) -> Option<&Value> {
323        #[cfg(feature = "std")]
324        if self.is_large_mode() {
325            return self.as_indexmap().get(key);
326        }
327        self.items()
328            .iter()
329            .find(|kv| kv.key.as_str() == key)
330            .map(|kv| &kv.value)
331    }
332
333    /// Gets a mutable value by key.
334    #[inline]
335    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
336        #[cfg(feature = "std")]
337        if self.is_large_mode() {
338            return self.as_indexmap_mut().get_mut(key);
339        }
340        self.items_mut()
341            .iter_mut()
342            .find(|kv| kv.key.as_str() == key)
343            .map(|kv| &mut kv.value)
344    }
345
346    /// Gets a key-value pair by key.
347    #[inline]
348    #[must_use]
349    pub fn get_key_value(&self, key: &str) -> Option<(&VString, &Value)> {
350        #[cfg(feature = "std")]
351        if self.is_large_mode() {
352            return self.as_indexmap().get_key_value(key);
353        }
354        self.items()
355            .iter()
356            .find(|kv| kv.key.as_str() == key)
357            .map(|kv| (&kv.key, &kv.value))
358    }
359
360    /// Returns `true` if the object contains the key.
361    #[inline]
362    #[must_use]
363    pub fn contains_key(&self, key: &str) -> bool {
364        #[cfg(feature = "std")]
365        if self.is_large_mode() {
366            return self.as_indexmap().contains_key(key);
367        }
368        self.items().iter().any(|kv| kv.key.as_str() == key)
369    }
370
371    /// Inserts a key-value pair. Returns the old value if the key existed.
372    #[cfg(feature = "alloc")]
373    pub fn insert(&mut self, key: impl Into<VString>, value: impl Into<Value>) -> Option<Value> {
374        let key = key.into();
375        let value = value.into();
376
377        #[cfg(feature = "std")]
378        if self.is_large_mode() {
379            return self.as_indexmap_mut().insert(key, value);
380        }
381
382        // Check if key exists (linear search in small mode)
383        if let Some(idx) = self
384            .items()
385            .iter()
386            .position(|kv| kv.key.as_str() == key.as_str())
387        {
388            // Key exists, replace value
389            return Some(mem::replace(&mut self.items_mut()[idx].value, value));
390        }
391
392        // Check if we should convert to large mode
393        #[cfg(feature = "std")]
394        if self.small_len() >= LARGE_MODE_THRESHOLD {
395            self.convert_to_large_mode();
396            return self.as_indexmap_mut().insert(key, value);
397        }
398
399        // New key in small mode
400        self.reserve(1);
401        let new_idx = self.header().len;
402
403        unsafe {
404            let ptr = self.items_ptr_mut().add(new_idx);
405            ptr.write(KeyValuePair { key, value });
406            self.header_mut().len = new_idx + 1;
407        }
408
409        None
410    }
411
412    /// Removes a key-value pair. Returns the value if the key existed.
413    pub fn remove(&mut self, key: &str) -> Option<Value> {
414        self.remove_entry(key).map(|(_, v)| v)
415    }
416
417    /// Removes and returns a key-value pair.
418    pub fn remove_entry(&mut self, key: &str) -> Option<(VString, Value)> {
419        #[cfg(feature = "std")]
420        if self.is_large_mode() {
421            return self.as_indexmap_mut().shift_remove_entry(key);
422        }
423
424        let idx = self.items().iter().position(|kv| kv.key.as_str() == key)?;
425        let len = self.small_len();
426
427        unsafe {
428            let ptr = self.items_ptr_mut().add(idx);
429            let kv = ptr.read();
430
431            // Shift remaining elements
432            if idx < len - 1 {
433                ptr::copy(ptr.add(1), ptr, len - idx - 1);
434            }
435
436            self.header_mut().len = len - 1;
437
438            Some((kv.key, kv.value))
439        }
440    }
441
442    /// Clears the object.
443    pub fn clear(&mut self) {
444        #[cfg(feature = "std")]
445        if self.is_large_mode() {
446            self.as_indexmap_mut().clear();
447            return;
448        }
449
450        while !self.is_empty() {
451            unsafe {
452                let len = self.header().len;
453                self.header_mut().len = len - 1;
454                let ptr = self.items_ptr_mut().add(len - 1);
455                ptr::drop_in_place(ptr);
456            }
457        }
458    }
459
460    /// Returns an iterator over keys.
461    #[inline]
462    pub fn keys(&self) -> Keys<'_> {
463        #[cfg(feature = "std")]
464        if self.is_large_mode() {
465            return Keys(KeysInner::Large(self.as_indexmap().keys()));
466        }
467        Keys(KeysInner::Small(self.items().iter()))
468    }
469
470    /// Returns an iterator over values.
471    #[inline]
472    pub fn values(&self) -> Values<'_> {
473        #[cfg(feature = "std")]
474        if self.is_large_mode() {
475            return Values(ValuesInner::Large(self.as_indexmap().values()));
476        }
477        Values(ValuesInner::Small(self.items().iter()))
478    }
479
480    /// Returns an iterator over mutable values.
481    #[inline]
482    pub fn values_mut(&mut self) -> ValuesMut<'_> {
483        #[cfg(feature = "std")]
484        if self.is_large_mode() {
485            return ValuesMut(ValuesMutInner::Large(self.as_indexmap_mut().values_mut()));
486        }
487        ValuesMut(ValuesMutInner::Small(self.items_mut().iter_mut()))
488    }
489
490    /// Returns an iterator over key-value pairs.
491    #[inline]
492    pub fn iter(&self) -> Iter<'_> {
493        #[cfg(feature = "std")]
494        if self.is_large_mode() {
495            return Iter(IterInner::Large(self.as_indexmap().iter()));
496        }
497        Iter(IterInner::Small(self.items().iter()))
498    }
499
500    /// Returns an iterator over mutable key-value pairs.
501    #[inline]
502    pub fn iter_mut(&mut self) -> IterMut<'_> {
503        #[cfg(feature = "std")]
504        if self.is_large_mode() {
505            return IterMut(IterMutInner::Large(self.as_indexmap_mut().iter_mut()));
506        }
507        IterMut(IterMutInner::Small(self.items_mut().iter_mut()))
508    }
509
510    /// Shrinks the capacity to match the length.
511    #[cfg(feature = "alloc")]
512    pub fn shrink_to_fit(&mut self) {
513        #[cfg(feature = "std")]
514        if self.is_large_mode() {
515            self.as_indexmap_mut().shrink_to_fit();
516            return;
517        }
518
519        let len = self.len();
520        let cap = self.capacity();
521
522        if len < cap {
523            unsafe {
524                let new_ptr = Self::realloc_ptr(self.0.heap_ptr_mut().cast(), len);
525                self.0.set_ptr(new_ptr.cast());
526            }
527        }
528    }
529
530    pub(crate) fn clone_impl(&self) -> Value {
531        #[cfg(feature = "std")]
532        if self.is_large_mode() {
533            let storage = LargeModeStorage {
534                _len_unused: 0,
535                cap_sentinel: LARGE_MODE_CAP_SENTINEL,
536                map: self.as_indexmap().clone(),
537            };
538            let boxed = Box::new(storage);
539            let ptr = Box::into_raw(boxed);
540            return unsafe { Value::new_ptr(ptr.cast(), TypeTag::Object) };
541        }
542
543        let mut new = VObject::with_capacity(self.len());
544        for kv in self.items() {
545            new.insert(kv.key.clone(), kv.value.clone());
546        }
547        new.0
548    }
549
550    pub(crate) fn drop_impl(&mut self) {
551        #[cfg(feature = "std")]
552        if self.is_large_mode() {
553            unsafe {
554                drop(Box::from_raw(self.0.heap_ptr_mut() as *mut LargeModeStorage));
555            }
556            return;
557        }
558
559        self.clear();
560        unsafe {
561            Self::dealloc_ptr(self.0.heap_ptr_mut().cast());
562        }
563    }
564}
565
566// === Iterators ===
567
568enum KeysInner<'a> {
569    Small(core::slice::Iter<'a, KeyValuePair>),
570    #[cfg(feature = "std")]
571    Large(indexmap::map::Keys<'a, VString, Value>),
572}
573
574/// Iterator over keys.
575pub struct Keys<'a>(KeysInner<'a>);
576
577impl<'a> Iterator for Keys<'a> {
578    type Item = &'a VString;
579
580    fn next(&mut self) -> Option<Self::Item> {
581        match &mut self.0 {
582            KeysInner::Small(iter) => iter.next().map(|kv| &kv.key),
583            #[cfg(feature = "std")]
584            KeysInner::Large(iter) => iter.next(),
585        }
586    }
587
588    fn size_hint(&self) -> (usize, Option<usize>) {
589        match &self.0 {
590            KeysInner::Small(iter) => iter.size_hint(),
591            #[cfg(feature = "std")]
592            KeysInner::Large(iter) => iter.size_hint(),
593        }
594    }
595}
596
597impl ExactSizeIterator for Keys<'_> {}
598
599enum ValuesInner<'a> {
600    Small(core::slice::Iter<'a, KeyValuePair>),
601    #[cfg(feature = "std")]
602    Large(indexmap::map::Values<'a, VString, Value>),
603}
604
605/// Iterator over values.
606pub struct Values<'a>(ValuesInner<'a>);
607
608impl<'a> Iterator for Values<'a> {
609    type Item = &'a Value;
610
611    fn next(&mut self) -> Option<Self::Item> {
612        match &mut self.0 {
613            ValuesInner::Small(iter) => iter.next().map(|kv| &kv.value),
614            #[cfg(feature = "std")]
615            ValuesInner::Large(iter) => iter.next(),
616        }
617    }
618
619    fn size_hint(&self) -> (usize, Option<usize>) {
620        match &self.0 {
621            ValuesInner::Small(iter) => iter.size_hint(),
622            #[cfg(feature = "std")]
623            ValuesInner::Large(iter) => iter.size_hint(),
624        }
625    }
626}
627
628impl ExactSizeIterator for Values<'_> {}
629
630enum ValuesMutInner<'a> {
631    Small(core::slice::IterMut<'a, KeyValuePair>),
632    #[cfg(feature = "std")]
633    Large(indexmap::map::ValuesMut<'a, VString, Value>),
634}
635
636/// Iterator over mutable values.
637pub struct ValuesMut<'a>(ValuesMutInner<'a>);
638
639impl<'a> Iterator for ValuesMut<'a> {
640    type Item = &'a mut Value;
641
642    fn next(&mut self) -> Option<Self::Item> {
643        match &mut self.0 {
644            ValuesMutInner::Small(iter) => iter.next().map(|kv| &mut kv.value),
645            #[cfg(feature = "std")]
646            ValuesMutInner::Large(iter) => iter.next(),
647        }
648    }
649
650    fn size_hint(&self) -> (usize, Option<usize>) {
651        match &self.0 {
652            ValuesMutInner::Small(iter) => iter.size_hint(),
653            #[cfg(feature = "std")]
654            ValuesMutInner::Large(iter) => iter.size_hint(),
655        }
656    }
657}
658
659impl ExactSizeIterator for ValuesMut<'_> {}
660
661enum IterInner<'a> {
662    Small(core::slice::Iter<'a, KeyValuePair>),
663    #[cfg(feature = "std")]
664    Large(indexmap::map::Iter<'a, VString, Value>),
665}
666
667/// Iterator over `(&VString, &Value)` pairs.
668pub struct Iter<'a>(IterInner<'a>);
669
670impl<'a> Iterator for Iter<'a> {
671    type Item = (&'a VString, &'a Value);
672
673    fn next(&mut self) -> Option<Self::Item> {
674        match &mut self.0 {
675            IterInner::Small(iter) => iter.next().map(|kv| (&kv.key, &kv.value)),
676            #[cfg(feature = "std")]
677            IterInner::Large(iter) => iter.next(),
678        }
679    }
680
681    fn size_hint(&self) -> (usize, Option<usize>) {
682        match &self.0 {
683            IterInner::Small(iter) => iter.size_hint(),
684            #[cfg(feature = "std")]
685            IterInner::Large(iter) => iter.size_hint(),
686        }
687    }
688}
689
690impl ExactSizeIterator for Iter<'_> {}
691
692enum IterMutInner<'a> {
693    Small(core::slice::IterMut<'a, KeyValuePair>),
694    #[cfg(feature = "std")]
695    Large(indexmap::map::IterMut<'a, VString, Value>),
696}
697
698/// Iterator over `(&VString, &mut Value)` pairs.
699pub struct IterMut<'a>(IterMutInner<'a>);
700
701impl<'a> Iterator for IterMut<'a> {
702    type Item = (&'a VString, &'a mut Value);
703
704    fn next(&mut self) -> Option<Self::Item> {
705        match &mut self.0 {
706            IterMutInner::Small(iter) => iter.next().map(|kv| (&kv.key, &mut kv.value)),
707            #[cfg(feature = "std")]
708            IterMutInner::Large(iter) => iter.next(),
709        }
710    }
711
712    fn size_hint(&self) -> (usize, Option<usize>) {
713        match &self.0 {
714            IterMutInner::Small(iter) => iter.size_hint(),
715            #[cfg(feature = "std")]
716            IterMutInner::Large(iter) => iter.size_hint(),
717        }
718    }
719}
720
721impl ExactSizeIterator for IterMut<'_> {}
722
723/// Iterator over owned `(VString, Value)` pairs.
724pub struct ObjectIntoIter {
725    object: VObject,
726}
727
728impl Iterator for ObjectIntoIter {
729    type Item = (VString, Value);
730
731    fn next(&mut self) -> Option<Self::Item> {
732        if self.object.is_empty() {
733            None
734        } else {
735            // Remove from the front to preserve order
736            let key = self.object.items()[0].key.as_str().to_owned();
737            self.object.remove_entry(&key)
738        }
739    }
740
741    fn size_hint(&self) -> (usize, Option<usize>) {
742        let len = self.object.len();
743        (len, Some(len))
744    }
745}
746
747impl ExactSizeIterator for ObjectIntoIter {}
748
749impl IntoIterator for VObject {
750    type Item = (VString, Value);
751    type IntoIter = ObjectIntoIter;
752
753    fn into_iter(self) -> Self::IntoIter {
754        ObjectIntoIter { object: self }
755    }
756}
757
758impl<'a> IntoIterator for &'a VObject {
759    type Item = (&'a VString, &'a Value);
760    type IntoIter = Iter<'a>;
761
762    fn into_iter(self) -> Self::IntoIter {
763        self.iter()
764    }
765}
766
767impl<'a> IntoIterator for &'a mut VObject {
768    type Item = (&'a VString, &'a mut Value);
769    type IntoIter = IterMut<'a>;
770
771    fn into_iter(self) -> Self::IntoIter {
772        self.iter_mut()
773    }
774}
775
776// === Index ===
777
778impl Index<&str> for VObject {
779    type Output = Value;
780
781    fn index(&self, key: &str) -> &Value {
782        self.get(key).expect("key not found")
783    }
784}
785
786impl IndexMut<&str> for VObject {
787    fn index_mut(&mut self, key: &str) -> &mut Value {
788        self.get_mut(key).expect("key not found")
789    }
790}
791
792// === Comparison ===
793
794impl PartialEq for VObject {
795    fn eq(&self, other: &Self) -> bool {
796        if self.len() != other.len() {
797            return false;
798        }
799        for (k, v) in self.iter() {
800            if other.get(k.as_str()) != Some(v) {
801                return false;
802            }
803        }
804        true
805    }
806}
807
808impl Eq for VObject {}
809
810impl Hash for VObject {
811    fn hash<H: Hasher>(&self, state: &mut H) {
812        // Hash length and then each key-value pair
813        // Note: This doesn't depend on order, which is correct for map semantics
814        self.len().hash(state);
815
816        // Sum hashes to make order-independent (XOR is order-independent)
817        let mut total: u64 = 0;
818        for (k, _v) in self.iter() {
819            // Simple hash combining for each pair
820            let mut kh: u64 = 0;
821            for byte in k.as_bytes() {
822                kh = kh.wrapping_mul(31).wrapping_add(*byte as u64);
823            }
824            // Just XOR the key hash contribution
825            total ^= kh;
826        }
827        total.hash(state);
828    }
829}
830
831impl Debug for VObject {
832    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
833        f.debug_map().entries(self.iter()).finish()
834    }
835}
836
837impl Default for VObject {
838    fn default() -> Self {
839        Self::new()
840    }
841}
842
843// === FromIterator / Extend ===
844
845#[cfg(feature = "alloc")]
846impl<K: Into<VString>, V: Into<Value>> FromIterator<(K, V)> for VObject {
847    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
848        let iter = iter.into_iter();
849        let (lower, _) = iter.size_hint();
850        let mut obj = VObject::with_capacity(lower);
851        for (k, v) in iter {
852            obj.insert(k, v);
853        }
854        obj
855    }
856}
857
858#[cfg(feature = "alloc")]
859impl<K: Into<VString>, V: Into<Value>> Extend<(K, V)> for VObject {
860    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
861        let iter = iter.into_iter();
862        let (lower, _) = iter.size_hint();
863        self.reserve(lower);
864        for (k, v) in iter {
865            self.insert(k, v);
866        }
867    }
868}
869
870// === From implementations ===
871
872#[cfg(feature = "std")]
873impl<K: Into<VString>, V: Into<Value>> From<HashMap<K, V>> for VObject {
874    fn from(map: HashMap<K, V>) -> Self {
875        map.into_iter().collect()
876    }
877}
878
879#[cfg(feature = "alloc")]
880impl<K: Into<VString>, V: Into<Value>> From<BTreeMap<K, V>> for VObject {
881    fn from(map: BTreeMap<K, V>) -> Self {
882        map.into_iter().collect()
883    }
884}
885
886// === Value conversions ===
887
888impl AsRef<Value> for VObject {
889    fn as_ref(&self) -> &Value {
890        &self.0
891    }
892}
893
894impl AsMut<Value> for VObject {
895    fn as_mut(&mut self) -> &mut Value {
896        &mut self.0
897    }
898}
899
900impl From<VObject> for Value {
901    fn from(obj: VObject) -> Self {
902        obj.0
903    }
904}
905
906impl VObject {
907    /// Converts this VObject into a Value, consuming self.
908    #[inline]
909    pub fn into_value(self) -> Value {
910        self.0
911    }
912}
913
914#[cfg(test)]
915mod tests {
916    use super::*;
917    use crate::ValueType;
918
919    #[test]
920    fn test_new() {
921        let obj = VObject::new();
922        assert!(obj.is_empty());
923        assert_eq!(obj.len(), 0);
924    }
925
926    #[test]
927    fn test_insert_get() {
928        let mut obj = VObject::new();
929        obj.insert("name", Value::from("Alice"));
930        obj.insert("age", Value::from(30));
931
932        assert_eq!(obj.len(), 2);
933        assert!(obj.contains_key("name"));
934        assert!(obj.contains_key("age"));
935        assert!(!obj.contains_key("email"));
936
937        assert_eq!(
938            obj.get("name").unwrap().as_string().unwrap().as_str(),
939            "Alice"
940        );
941        assert_eq!(
942            obj.get("age").unwrap().as_number().unwrap().to_i64(),
943            Some(30)
944        );
945    }
946
947    #[test]
948    fn test_insert_replace() {
949        let mut obj = VObject::new();
950        assert!(obj.insert("key", Value::from(1)).is_none());
951        assert!(obj.insert("key", Value::from(2)).is_some());
952        assert_eq!(obj.len(), 1);
953        assert_eq!(
954            obj.get("key").unwrap().as_number().unwrap().to_i64(),
955            Some(2)
956        );
957    }
958
959    #[test]
960    fn test_remove() {
961        let mut obj = VObject::new();
962        obj.insert("a", Value::from(1));
963        obj.insert("b", Value::from(2));
964        obj.insert("c", Value::from(3));
965
966        let removed = obj.remove("b");
967        assert!(removed.is_some());
968        assert_eq!(obj.len(), 2);
969        assert!(!obj.contains_key("b"));
970    }
971
972    #[test]
973    fn test_clone() {
974        let mut obj = VObject::new();
975        obj.insert("key", Value::from("value"));
976
977        let obj2 = obj.clone();
978        assert_eq!(obj, obj2);
979    }
980
981    #[test]
982    fn test_iter() {
983        let mut obj = VObject::new();
984        obj.insert("a", Value::from(1));
985        obj.insert("b", Value::from(2));
986
987        let keys: Vec<_> = obj.keys().map(|k| k.as_str()).collect();
988        assert_eq!(keys, vec!["a", "b"]);
989    }
990
991    #[test]
992    fn test_collect() {
993        let obj: VObject = vec![("a", Value::from(1)), ("b", Value::from(2))]
994            .into_iter()
995            .collect();
996        assert_eq!(obj.len(), 2);
997    }
998
999    #[test]
1000    fn test_index() {
1001        let mut obj = VObject::new();
1002        obj.insert("key", Value::from(42));
1003
1004        assert_eq!(obj["key"].as_number().unwrap().to_i64(), Some(42));
1005    }
1006
1007    #[test]
1008    fn inline_strings_in_objects_remain_inline() {
1009        let mut obj = VObject::new();
1010        for idx in 0..=crate::string::VString::INLINE_LEN_MAX.min(5) {
1011            let key = format!("k{idx}");
1012            let val = "v".repeat(idx);
1013            obj.insert(key.as_str(), Value::from(val.as_str()));
1014        }
1015
1016        for (key, value) in obj.iter() {
1017            assert!(
1018                key.0.is_inline_string(),
1019                "object key {:?} expected inline storage",
1020                key.as_str()
1021            );
1022            if value.value_type() == ValueType::String {
1023                assert!(
1024                    value.is_inline_string(),
1025                    "object value {value:?} expected inline storage"
1026                );
1027            }
1028        }
1029
1030        let mut cloned = obj.clone();
1031        for (key, value) in cloned.iter() {
1032            assert!(key.0.is_inline_string(), "cloned key lost inline storage");
1033            if value.value_type() == ValueType::String {
1034                assert!(value.is_inline_string(), "cloned value lost inline storage");
1035            }
1036        }
1037
1038        let (removed_key, removed_value) = cloned.remove_entry("k1").expect("entry exists");
1039        assert!(
1040            removed_key.0.is_inline_string(),
1041            "removed key should stay inline"
1042        );
1043        if removed_value.value_type() == ValueType::String {
1044            assert!(
1045                removed_value.is_inline_string(),
1046                "removed value should stay inline"
1047            );
1048        }
1049    }
1050}