Skip to main content

toml_spanner/item/
array.rs

1#[cfg(test)]
2#[path = "./array_tests.rs"]
3mod tests;
4
5use crate::MaybeItem;
6use crate::Span;
7use crate::arena::Arena;
8use crate::item::{ArrayStyle, FLAG_AOT, FLAG_ARRAY, Item, ItemMetadata, TAG_ARRAY};
9use std::mem::size_of;
10use std::ptr::NonNull;
11
12const MIN_CAP: u32 = 4;
13
14#[repr(C, align(8))]
15pub(crate) struct InternalArray<'de> {
16    pub(super) len: u32,
17    pub(super) cap: u32,
18    pub(super) ptr: NonNull<Item<'de>>,
19}
20
21impl<'de> Default for InternalArray<'de> {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl<'de> InternalArray<'de> {
28    #[inline]
29    pub(crate) fn new() -> Self {
30        Self {
31            len: 0,
32            cap: 0,
33            ptr: NonNull::dangling(),
34        }
35    }
36
37    pub(crate) fn with_capacity(cap: u32, arena: &'de Arena) -> Self {
38        let mut arr = Self::new();
39        if cap > 0 {
40            arr.grow_to(cap, arena);
41        }
42        arr
43    }
44
45    pub(crate) fn with_single(value: Item<'de>, arena: &'de Arena) -> Self {
46        let mut arr = Self::with_capacity(MIN_CAP, arena);
47        // SAFETY: with_capacity allocated space for at least MIN_CAP items,
48        // so writing at index 0 is within bounds.
49        unsafe {
50            arr.ptr.as_ptr().write(value);
51        }
52        arr.len = 1;
53        arr
54    }
55
56    #[inline]
57    pub(crate) fn push(&mut self, value: Item<'de>, arena: &'de Arena) {
58        let len = self.len;
59        if len == self.cap {
60            self.grow(arena);
61        }
62        // SAFETY: grow() ensures len < cap, so ptr.add(len) is within the allocation.
63        unsafe {
64            self.ptr.as_ptr().add(len as usize).write(value);
65        }
66        self.len = len + 1;
67    }
68
69    #[inline]
70    pub(crate) fn len(&self) -> usize {
71        self.len as usize
72    }
73
74    #[inline]
75    pub(crate) fn is_empty(&self) -> bool {
76        self.len == 0
77    }
78
79    #[inline]
80    pub(crate) fn get(&self, index: usize) -> Option<&Item<'de>> {
81        if index < self.len as usize {
82            // SAFETY: index < len is checked above, so the pointer is within
83            // initialized elements.
84            Some(unsafe { &*self.ptr.as_ptr().add(index) })
85        } else {
86            None
87        }
88    }
89
90    #[inline]
91    pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Item<'de>> {
92        if index < self.len as usize {
93            // SAFETY: index < len is checked above.
94            Some(unsafe { &mut *self.ptr.as_ptr().add(index) })
95        } else {
96            None
97        }
98    }
99
100    pub(crate) fn remove(&mut self, index: usize) -> Item<'de> {
101        assert!(index < self.len as usize, "index out of bounds");
102        let len = self.len as usize;
103        // SAFETY: index < len is asserted, so ptr.add(index) is in bounds.
104        // copy within the same allocation shifts elements left by one.
105        unsafe {
106            let ptr = self.ptr.as_ptr().add(index);
107            let removed = ptr.read();
108            std::ptr::copy(ptr.add(1), ptr, len - index - 1);
109            self.len -= 1;
110            removed
111        }
112    }
113
114    #[inline]
115    pub(crate) fn pop(&mut self) -> Option<Item<'de>> {
116        if self.len == 0 {
117            None
118        } else {
119            self.len -= 1;
120            // SAFETY: len was > 0 and was just decremented, so ptr.add(len)
121            // points to the last initialized element.
122            Some(unsafe { self.ptr.as_ptr().add(self.len as usize).read() })
123        }
124    }
125
126    #[inline]
127    pub(crate) fn last_mut(&mut self) -> Option<&mut Item<'de>> {
128        if self.len == 0 {
129            None
130        } else {
131            // SAFETY: len > 0 is checked above, so ptr.add(len - 1) is within bounds.
132            Some(unsafe { &mut *self.ptr.as_ptr().add(self.len as usize - 1) })
133        }
134    }
135
136    #[inline]
137    pub(crate) fn iter(&self) -> std::slice::Iter<'_, Item<'de>> {
138        self.as_slice().iter()
139    }
140
141    #[inline]
142    pub(crate) fn as_slice(&self) -> &[Item<'de>] {
143        if self.len == 0 {
144            &[]
145        } else {
146            // SAFETY: len > 0 is checked above. ptr points to arena-allocated
147            // memory with at least len initialized items.
148            unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len as usize) }
149        }
150    }
151
152    #[inline]
153    pub(crate) fn as_mut_slice(&mut self) -> &mut [Item<'de>] {
154        if self.len == 0 {
155            &mut []
156        } else {
157            // SAFETY: same as as_slice() — ptr is valid for len initialized items.
158            unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len as usize) }
159        }
160    }
161
162    #[cold]
163    fn grow(&mut self, arena: &'de Arena) {
164        let new_cap = if self.cap == 0 {
165            MIN_CAP
166        } else {
167            self.cap.checked_mul(2).expect("capacity overflow")
168        };
169        self.grow_to(new_cap, arena);
170    }
171
172    fn grow_to(&mut self, new_cap: u32, arena: &'de Arena) {
173        // On 64-bit, u32 * size_of::<Item>() cannot overflow usize.
174        #[cfg(target_pointer_width = "32")]
175        let new_size = (new_cap as usize)
176            .checked_mul(size_of::<Item<'_>>())
177            .expect("capacity overflow");
178        #[cfg(not(target_pointer_width = "32"))]
179        let new_size = new_cap as usize * size_of::<Item<'_>>();
180        if self.cap > 0 {
181            let old_size = self.cap as usize * size_of::<Item<'_>>();
182            // Safety: ptr was returned by a prior arena alloc of old_size bytes.
183            self.ptr = unsafe { arena.realloc(self.ptr.cast(), old_size, new_size).cast() };
184        } else {
185            self.ptr = arena.alloc(new_size).cast();
186        }
187        self.cap = new_cap;
188    }
189
190    /// Deep-clones this array into `arena`. Keys and strings are shared
191    /// with the source.
192    pub(crate) fn clone_in(&self, arena: &'de Arena) -> Self {
193        let len = self.len as usize;
194        if len == 0 {
195            return Self::new();
196        }
197        let size = len * size_of::<Item<'de>>();
198        let dst: NonNull<Item<'de>> = arena.alloc(size).cast();
199        let src = self.ptr.as_ptr();
200        let dst_ptr = dst.as_ptr();
201
202        let mut run_start = 0;
203        for i in 0..len {
204            // SAFETY: i < len, so src.add(i) is within initialized elements.
205            if unsafe { !(*src.add(i)).is_scalar() } {
206                if run_start < i {
207                    // SAFETY: src[run_start..i] are scalars — bitwise copy is
208                    // correct. Source and destination are disjoint arena regions.
209                    unsafe {
210                        std::ptr::copy_nonoverlapping(
211                            src.add(run_start),
212                            dst_ptr.add(run_start),
213                            i - run_start,
214                        );
215                    }
216                }
217                // SAFETY: the item is an aggregate; deep-clone it.
218                unsafe {
219                    dst_ptr.add(i).write((*src.add(i)).clone_in(arena));
220                }
221                run_start = i + 1;
222            }
223        }
224        if run_start < len {
225            unsafe {
226                std::ptr::copy_nonoverlapping(
227                    src.add(run_start),
228                    dst_ptr.add(run_start),
229                    len - run_start,
230                );
231            }
232        }
233
234        Self {
235            len: self.len,
236            cap: self.len,
237            ptr: dst,
238        }
239    }
240}
241
242impl std::fmt::Debug for InternalArray<'_> {
243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244        f.debug_list().entries(self.as_slice()).finish()
245    }
246}
247
248impl<'a, 'de> IntoIterator for &'a InternalArray<'de> {
249    type Item = &'a Item<'de>;
250    type IntoIter = std::slice::Iter<'a, Item<'de>>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        self.as_slice().iter()
254    }
255}
256
257impl<'a, 'de> IntoIterator for &'a mut InternalArray<'de> {
258    type Item = &'a mut Item<'de>;
259    type IntoIter = std::slice::IterMut<'a, Item<'de>>;
260
261    fn into_iter(self) -> Self::IntoIter {
262        self.as_mut_slice().iter_mut()
263    }
264}
265
266/// Consuming iterator over an [`InternalArray`], yielding [`Item`]s.
267pub(crate) struct InternalArrayIntoIter<'de> {
268    arr: InternalArray<'de>,
269    index: u32,
270}
271
272impl<'de> Iterator for InternalArrayIntoIter<'de> {
273    type Item = Item<'de>;
274
275    fn next(&mut self) -> Option<Self::Item> {
276        if self.index < self.arr.len {
277            // SAFETY: index < len is checked above, so the read is within
278            // initialized elements.
279            let val = unsafe { self.arr.ptr.as_ptr().add(self.index as usize).read() };
280            self.index += 1;
281            Some(val)
282        } else {
283            None
284        }
285    }
286
287    fn size_hint(&self) -> (usize, Option<usize>) {
288        let remaining = (self.arr.len - self.index) as usize;
289        (remaining, Some(remaining))
290    }
291}
292
293impl<'de> ExactSizeIterator for InternalArrayIntoIter<'de> {}
294
295impl<'de> IntoIterator for InternalArray<'de> {
296    type Item = Item<'de>;
297    type IntoIter = InternalArrayIntoIter<'de>;
298
299    fn into_iter(self) -> Self::IntoIter {
300        InternalArrayIntoIter {
301            arr: self,
302            index: 0,
303        }
304    }
305}
306
307impl<'de> std::ops::Index<usize> for InternalArray<'de> {
308    type Output = MaybeItem<'de>;
309
310    #[inline]
311    fn index(&self, index: usize) -> &Self::Output {
312        if let Some(item) = self.get(index) {
313            return MaybeItem::from_ref(item);
314        }
315        &crate::item::NONE
316    }
317}
318
319// Public Array wrapper (same layout as Item when tag == TAG_ARRAY)
320
321/// A TOML array with span information.
322///
323/// An `Array` stores [`Item`] elements in insertion order with arena-allocated
324/// backing storage. It carries the byte-offset [`Span`] from the source
325/// document.
326///
327/// # Accessing elements
328///
329/// - **Index operator**: `array[i]` returns a [`MaybeItem`] that never
330///   panics on out-of-bounds access.
331/// - **`get` / `get_mut`**: return `Option<&Item>` / `Option<&mut Item>`.
332/// - **Iteration**: `for item in &array { ... }`.
333///
334/// # Mutation
335///
336/// [`push`](Self::push) appends an element. An [`Arena`] is required because
337/// array storage is arena-allocated.
338#[repr(C)]
339pub struct Array<'de> {
340    pub(crate) value: InternalArray<'de>,
341    pub(crate) meta: ItemMetadata,
342}
343
344const _: () = assert!(std::mem::size_of::<Array<'_>>() == std::mem::size_of::<Item<'_>>());
345const _: () = assert!(std::mem::align_of::<Array<'_>>() == std::mem::align_of::<Item<'_>>());
346
347impl<'de> Array<'de> {
348    /// Creates an empty array in format-hints mode (no source span).
349    ///
350    /// The array starts with automatic style: normalization will choose
351    /// between inline and header (array-of-tables) based on content. Call
352    /// [`set_style`](Self::set_style) to override.
353    pub fn new() -> Self {
354        let mut meta = ItemMetadata::hints(TAG_ARRAY, FLAG_ARRAY);
355        meta.set_auto_style();
356        Self {
357            meta,
358            value: InternalArray::new(),
359        }
360    }
361
362    /// Creates an empty array with pre-allocated capacity.
363    ///
364    /// Returns `None` if `cap` exceeds `u32::MAX`.
365    pub fn try_with_capacity(cap: usize, arena: &'de Arena) -> Option<Self> {
366        let cap: u32 = cap.try_into().ok()?;
367        let mut meta = ItemMetadata::hints(TAG_ARRAY, FLAG_ARRAY);
368        meta.set_auto_style();
369        Some(Self {
370            meta,
371            value: InternalArray::with_capacity(cap, arena),
372        })
373    }
374
375    /// Creates an empty array in span mode (parser-produced).
376    #[cfg(test)]
377    pub(crate) fn new_spanned(span: Span) -> Self {
378        Self {
379            meta: ItemMetadata::spanned(TAG_ARRAY, FLAG_ARRAY, span.start, span.end),
380            value: InternalArray::new(),
381        }
382    }
383
384    /// Returns the byte-offset span of this array in the source document.
385    /// Only valid on parser-produced arrays (span mode).
386    #[cfg_attr(not(test), allow(dead_code))]
387    pub(crate) fn span_unchecked(&self) -> Span {
388        self.meta.span_unchecked()
389    }
390
391    /// Returns the source span, or `0..0` if this array was constructed
392    /// programmatically (format-hints mode).
393    pub fn span(&self) -> Span {
394        self.meta.span()
395    }
396
397    /// Appends a value to the end of the array.
398    #[inline]
399    pub fn push(&mut self, value: Item<'de>, arena: &'de Arena) {
400        self.value.push(value, arena);
401    }
402
403    /// Returns the number of elements.
404    #[inline]
405    pub fn len(&self) -> usize {
406        self.value.len()
407    }
408
409    /// Returns `true` if the array contains no elements.
410    #[inline]
411    pub fn is_empty(&self) -> bool {
412        self.value.is_empty()
413    }
414
415    /// Returns a reference to the element at the given index.
416    #[inline]
417    pub fn get(&self, index: usize) -> Option<&Item<'de>> {
418        self.value.get(index)
419    }
420
421    /// Returns a mutable reference to the element at the given index.
422    #[inline]
423    pub fn get_mut(&mut self, index: usize) -> Option<&mut Item<'de>> {
424        self.value.get_mut(index)
425    }
426
427    /// Removes and returns the element at `index`, shifting subsequent
428    /// elements to the left.
429    ///
430    /// # Panics
431    ///
432    /// Panics if `index` is out of bounds.
433    #[inline]
434    pub fn remove(&mut self, index: usize) -> Item<'de> {
435        self.value.remove(index)
436    }
437
438    /// Removes and returns the last element, or `None` if empty.
439    #[inline]
440    pub fn pop(&mut self) -> Option<Item<'de>> {
441        self.value.pop()
442    }
443
444    /// Returns a mutable reference to the last element.
445    #[inline]
446    pub fn last_mut(&mut self) -> Option<&mut Item<'de>> {
447        self.value.last_mut()
448    }
449
450    /// Returns an iterator over references to the elements.
451    #[inline]
452    pub fn iter(&self) -> std::slice::Iter<'_, Item<'de>> {
453        self.value.iter()
454    }
455
456    /// Returns the contents as a slice.
457    #[inline]
458    pub fn as_slice(&self) -> &[Item<'de>] {
459        self.value.as_slice()
460    }
461
462    /// Returns the contents as a mutable slice.
463    #[inline]
464    pub fn as_mut_slice(&mut self) -> &mut [Item<'de>] {
465        self.value.as_mut_slice()
466    }
467
468    /// Converts this `Array` into an [`Item`] with the same span and payload.
469    pub fn as_item(&self) -> &Item<'de> {
470        // SAFETY: Array is #[repr(C)] { InternalArray, ItemMetadata }.
471        // Item  is #[repr(C)] { Payload,       ItemMetadata }.
472        // Payload is a union whose `array` field is ManuallyDrop<InternalArray>
473        // (#[repr(transparent)]). Both types are 24 bytes, align 8 (verified
474        // by const assertions). The field offsets match (data at 0..16,
475        // metadata at 16..24). Only a shared reference is returned.
476        unsafe { &*(self as *const Array<'de>).cast::<Item<'de>>() }
477    }
478
479    /// Converts this `Array` into an [`Item`] with the same span and payload.
480    pub fn into_item(self) -> Item<'de> {
481        // SAFETY: Same layout argument as as_item(). Size and alignment
482        // equality verified by const assertions. The tag in ItemMetadata is
483        // preserved unchanged through the transmute.
484        unsafe { std::mem::transmute(self) }
485    }
486
487    /// Returns the kind of this array (inline or header/array-of-tables).
488    #[inline]
489    pub fn style(&self) -> ArrayStyle {
490        match self.meta.flag() {
491            FLAG_AOT => ArrayStyle::Header,
492            _ => ArrayStyle::Inline,
493        }
494    }
495
496    /// Sets the kind of this array, clearing automatic style resolution.
497    #[inline]
498    pub fn set_style(&mut self, kind: ArrayStyle) {
499        let flag = match kind {
500            ArrayStyle::Inline => FLAG_ARRAY,
501            ArrayStyle::Header => FLAG_AOT,
502        };
503        self.meta.set_flag(flag);
504        self.meta.clear_auto_style();
505    }
506
507    /// Deep-clones this array into `arena`. Keys and strings are shared
508    /// with the source.
509    pub fn clone_in(&self, arena: &'de Arena) -> Array<'de> {
510        Array {
511            value: self.value.clone_in(arena),
512            meta: self.meta,
513        }
514    }
515}
516
517impl<'de> Default for Array<'de> {
518    fn default() -> Self {
519        Self::new()
520    }
521}
522
523impl std::fmt::Debug for Array<'_> {
524    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525        self.value.fmt(f)
526    }
527}
528
529impl<'de> std::ops::Index<usize> for Array<'de> {
530    type Output = MaybeItem<'de>;
531
532    #[inline]
533    fn index(&self, index: usize) -> &Self::Output {
534        if let Some(item) = self.value.get(index) {
535            return MaybeItem::from_ref(item);
536        }
537        &crate::item::NONE
538    }
539}
540
541impl<'a, 'de> IntoIterator for &'a Array<'de> {
542    type Item = &'a Item<'de>;
543    type IntoIter = std::slice::Iter<'a, Item<'de>>;
544
545    fn into_iter(self) -> Self::IntoIter {
546        self.value.as_slice().iter()
547    }
548}
549
550impl<'a, 'de> IntoIterator for &'a mut Array<'de> {
551    type Item = &'a mut Item<'de>;
552    type IntoIter = std::slice::IterMut<'a, Item<'de>>;
553
554    fn into_iter(self) -> Self::IntoIter {
555        self.value.as_mut_slice().iter_mut()
556    }
557}
558
559/// Consuming iterator over an [`Array`], yielding [`Item`]s.
560pub struct IntoIter<'de> {
561    arr: InternalArray<'de>,
562    index: u32,
563}
564
565impl<'de> Iterator for IntoIter<'de> {
566    type Item = Item<'de>;
567
568    fn next(&mut self) -> Option<Self::Item> {
569        if self.index < self.arr.len {
570            // SAFETY: index < len is checked above, so the read is within
571            // initialized elements.
572            let val = unsafe { self.arr.ptr.as_ptr().add(self.index as usize).read() };
573            self.index += 1;
574            Some(val)
575        } else {
576            None
577        }
578    }
579
580    fn size_hint(&self) -> (usize, Option<usize>) {
581        let remaining = (self.arr.len - self.index) as usize;
582        (remaining, Some(remaining))
583    }
584}
585
586impl<'de> ExactSizeIterator for IntoIter<'de> {}
587
588impl<'de> IntoIterator for Array<'de> {
589    type Item = Item<'de>;
590    type IntoIter = IntoIter<'de>;
591
592    fn into_iter(self) -> Self::IntoIter {
593        IntoIter {
594            arr: self.value,
595            index: 0,
596        }
597    }
598}