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    /// Copies this array into `target`, returning a copy with `'static` lifetime.
242    ///
243    /// # Safety
244    ///
245    /// `target` must have sufficient space as computed by
246    /// [`compute_size`](crate::item::owned).
247    pub(crate) unsafe fn emplace_in(
248        &self,
249        target: &mut crate::item::owned::ItemCopyTarget,
250    ) -> InternalArray<'static> {
251        let len = self.len as usize;
252        if len == 0 {
253            return InternalArray::new();
254        }
255        let byte_size = len * size_of::<Item<'static>>();
256        // SAFETY: Caller guarantees sufficient aligned space for len items.
257        let dst_ptr = unsafe { target.alloc_aligned(byte_size) }
258            .as_ptr()
259            .cast::<Item<'static>>();
260        for (i, item) in self.as_slice().iter().enumerate() {
261            let new_item = unsafe { item.emplace_in(target) };
262            // SAFETY: i < len, within the allocated region.
263            unsafe { dst_ptr.add(i).write(new_item) };
264        }
265        InternalArray {
266            len: self.len,
267            cap: self.len,
268            // SAFETY: dst_ptr is non-null (from alloc_aligned).
269            ptr: unsafe { NonNull::new_unchecked(dst_ptr) },
270        }
271    }
272}
273
274impl std::fmt::Debug for InternalArray<'_> {
275    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276        f.debug_list().entries(self.as_slice()).finish()
277    }
278}
279
280impl<'a, 'de> IntoIterator for &'a InternalArray<'de> {
281    type Item = &'a Item<'de>;
282    type IntoIter = std::slice::Iter<'a, Item<'de>>;
283
284    fn into_iter(self) -> Self::IntoIter {
285        self.as_slice().iter()
286    }
287}
288
289impl<'a, 'de> IntoIterator for &'a mut InternalArray<'de> {
290    type Item = &'a mut Item<'de>;
291    type IntoIter = std::slice::IterMut<'a, Item<'de>>;
292
293    fn into_iter(self) -> Self::IntoIter {
294        self.as_mut_slice().iter_mut()
295    }
296}
297
298/// Consuming iterator over an [`InternalArray`], yielding [`Item`]s.
299pub(crate) struct InternalArrayIntoIter<'de> {
300    arr: InternalArray<'de>,
301    index: u32,
302}
303
304impl<'de> Iterator for InternalArrayIntoIter<'de> {
305    type Item = Item<'de>;
306
307    fn next(&mut self) -> Option<Self::Item> {
308        if self.index < self.arr.len {
309            // SAFETY: index < len is checked above, so the read is within
310            // initialized elements.
311            let val = unsafe { self.arr.ptr.as_ptr().add(self.index as usize).read() };
312            self.index += 1;
313            Some(val)
314        } else {
315            None
316        }
317    }
318
319    fn size_hint(&self) -> (usize, Option<usize>) {
320        let remaining = (self.arr.len - self.index) as usize;
321        (remaining, Some(remaining))
322    }
323}
324
325impl<'de> ExactSizeIterator for InternalArrayIntoIter<'de> {}
326
327impl<'de> IntoIterator for InternalArray<'de> {
328    type Item = Item<'de>;
329    type IntoIter = InternalArrayIntoIter<'de>;
330
331    fn into_iter(self) -> Self::IntoIter {
332        InternalArrayIntoIter {
333            arr: self,
334            index: 0,
335        }
336    }
337}
338
339impl<'de> std::ops::Index<usize> for InternalArray<'de> {
340    type Output = MaybeItem<'de>;
341
342    #[inline]
343    fn index(&self, index: usize) -> &Self::Output {
344        if let Some(item) = self.get(index) {
345            return MaybeItem::from_ref(item);
346        }
347        &crate::item::NONE
348    }
349}
350
351// Public Array wrapper (same layout as Item when tag == TAG_ARRAY)
352
353/// A TOML array with span information.
354///
355/// An `Array` stores [`Item`] elements in insertion order with arena-allocated
356/// backing storage. It carries the byte-offset [`Span`] from the source
357/// document.
358///
359/// # Accessing elements
360///
361/// - **Index operator**: `array[i]` returns a [`MaybeItem`] that never
362///   panics on out-of-bounds access.
363/// - **`get` / `get_mut`**: return `Option<&Item>` / `Option<&mut Item>`.
364/// - **Iteration**: `for item in &array { ... }`.
365///
366/// # Mutation
367///
368/// [`push`](Self::push) appends an element. An [`Arena`] is required because
369/// array storage is arena-allocated.
370#[repr(C)]
371pub struct Array<'de> {
372    pub(crate) value: InternalArray<'de>,
373    pub(crate) meta: ItemMetadata,
374}
375
376const _: () = assert!(std::mem::size_of::<Array<'_>>() == std::mem::size_of::<Item<'_>>());
377const _: () = assert!(std::mem::align_of::<Array<'_>>() == std::mem::align_of::<Item<'_>>());
378
379// SAFETY: `Array` is layout-compatible with `Item` when the item's tag is
380// `TAG_ARRAY` (see `as_item`/`into_item`). Its `InternalArray` holds a
381// `NonNull` into arena memory plus a length/capacity pair, with no interior
382// mutability: `push`, `remove`, `pop`, and `as_mut_slice` all require
383// `&mut Array`, so shared readers cannot race with a writer. Element
384// `Item`s transitively satisfy the same `Send`/`Sync` invariants.
385unsafe impl Send for Array<'_> {}
386unsafe impl Sync for Array<'_> {}
387
388// SAFETY: `IntoIter` owns the `InternalArray` it drains. The same reasoning
389// as for `Array` applies: no interior mutability and the `NonNull` points
390// into arena storage that the iterator exclusively reads through `&mut self`.
391unsafe impl Send for IntoIter<'_> {}
392unsafe impl Sync for IntoIter<'_> {}
393
394impl<'de> Array<'de> {
395    /// Creates an empty array in format-hints mode (no source span).
396    ///
397    /// The array starts with automatic style: normalization will choose
398    /// between inline and header (array-of-tables) based on content. Call
399    /// [`set_style`](Self::set_style) to override.
400    pub fn new() -> Self {
401        let mut meta = ItemMetadata::hints(TAG_ARRAY, FLAG_ARRAY);
402        meta.set_auto_style();
403        Self {
404            meta,
405            value: InternalArray::new(),
406        }
407    }
408
409    /// Creates an empty array with pre-allocated capacity.
410    ///
411    /// Returns `None` if `cap` exceeds `u32::MAX`.
412    pub fn try_with_capacity(cap: usize, arena: &'de Arena) -> Option<Self> {
413        let cap: u32 = cap.try_into().ok()?;
414        let mut meta = ItemMetadata::hints(TAG_ARRAY, FLAG_ARRAY);
415        meta.set_auto_style();
416        Some(Self {
417            meta,
418            value: InternalArray::with_capacity(cap, arena),
419        })
420    }
421
422    /// Creates an empty array in span mode (parser-produced).
423    #[cfg(test)]
424    pub(crate) fn new_spanned(span: Span) -> Self {
425        Self {
426            meta: ItemMetadata::spanned(TAG_ARRAY, FLAG_ARRAY, span.start, span.end),
427            value: InternalArray::new(),
428        }
429    }
430
431    /// Returns the byte-offset span of this array in the source document.
432    /// Only valid on parser-produced arrays (span mode).
433    #[cfg_attr(not(test), allow(dead_code))]
434    pub(crate) fn span_unchecked(&self) -> Span {
435        self.meta.span_unchecked()
436    }
437
438    /// Returns the source span, or `0..0` if this array was constructed
439    /// programmatically (format-hints mode).
440    pub fn span(&self) -> Span {
441        self.meta.span()
442    }
443
444    /// Appends a value to the end of the array.
445    #[inline]
446    pub fn push(&mut self, value: Item<'de>, arena: &'de Arena) {
447        self.value.push(value, arena);
448    }
449
450    /// Returns the number of elements.
451    #[inline]
452    pub fn len(&self) -> usize {
453        self.value.len()
454    }
455
456    /// Returns `true` if the array contains no elements.
457    #[inline]
458    pub fn is_empty(&self) -> bool {
459        self.value.is_empty()
460    }
461
462    /// Returns a reference to the element at the given index.
463    #[inline]
464    pub fn get(&self, index: usize) -> Option<&Item<'de>> {
465        self.value.get(index)
466    }
467
468    /// Returns a mutable reference to the element at the given index.
469    #[inline]
470    pub fn get_mut(&mut self, index: usize) -> Option<&mut Item<'de>> {
471        self.value.get_mut(index)
472    }
473
474    /// Removes and returns the element at `index`, shifting subsequent
475    /// elements to the left.
476    ///
477    /// # Panics
478    ///
479    /// Panics if `index` is out of bounds.
480    #[inline]
481    pub fn remove(&mut self, index: usize) -> Item<'de> {
482        self.value.remove(index)
483    }
484
485    /// Removes and returns the last element, or `None` if empty.
486    #[inline]
487    pub fn pop(&mut self) -> Option<Item<'de>> {
488        self.value.pop()
489    }
490
491    /// Returns a mutable reference to the last element.
492    #[inline]
493    pub fn last_mut(&mut self) -> Option<&mut Item<'de>> {
494        self.value.last_mut()
495    }
496
497    /// Returns an iterator over references to the elements.
498    #[inline]
499    pub fn iter(&self) -> std::slice::Iter<'_, Item<'de>> {
500        self.value.iter()
501    }
502
503    /// Returns the contents as a slice.
504    #[inline]
505    pub fn as_slice(&self) -> &[Item<'de>] {
506        self.value.as_slice()
507    }
508
509    /// Returns the contents as a mutable slice.
510    #[inline]
511    pub fn as_mut_slice(&mut self) -> &mut [Item<'de>] {
512        self.value.as_mut_slice()
513    }
514
515    /// Converts this `Array` into an [`Item`] with the same span and payload.
516    pub fn as_item(&self) -> &Item<'de> {
517        // SAFETY: Array is #[repr(C)] { InternalArray, ItemMetadata }.
518        // Item  is #[repr(C)] { Payload,       ItemMetadata }.
519        // Payload is a union whose `array` field is ManuallyDrop<InternalArray>
520        // (#[repr(transparent)]). Both types are 24 bytes, align 8 (verified
521        // by const assertions). The field offsets match (data at 0..16,
522        // metadata at 16..24). Only a shared reference is returned.
523        unsafe { &*(self as *const Array<'de>).cast::<Item<'de>>() }
524    }
525
526    /// Converts this `Array` into an [`Item`] with the same span and payload.
527    pub fn into_item(self) -> Item<'de> {
528        // SAFETY: Same layout argument as as_item(). Size and alignment
529        // equality verified by const assertions. The tag in ItemMetadata is
530        // preserved unchanged through the transmute.
531        unsafe { std::mem::transmute(self) }
532    }
533
534    /// Returns the [`ArrayStyle`] recorded on this array.
535    #[inline]
536    pub fn style(&self) -> ArrayStyle {
537        match self.meta.flag() {
538            FLAG_AOT => ArrayStyle::Header,
539            _ => ArrayStyle::Inline,
540        }
541    }
542
543    /// Pins the [`ArrayStyle`] used when this array is emitted.
544    ///
545    /// Arrays built programmatically start out with an unresolved style and
546    /// emit picks a rendering from their contents. Calling this method
547    /// locks in `kind` so the choice survives emission unchanged.
548    #[inline]
549    pub fn set_style(&mut self, kind: ArrayStyle) {
550        let flag = match kind {
551            ArrayStyle::Inline => FLAG_ARRAY,
552            ArrayStyle::Header => FLAG_AOT,
553        };
554        self.meta.set_flag(flag);
555        self.meta.clear_auto_style();
556    }
557
558    /// Deep-clones this array into `arena`. Keys and strings are shared
559    /// with the source.
560    pub fn clone_in(&self, arena: &'de Arena) -> Array<'de> {
561        Array {
562            value: self.value.clone_in(arena),
563            meta: self.meta,
564        }
565    }
566}
567
568impl<'de> Default for Array<'de> {
569    fn default() -> Self {
570        Self::new()
571    }
572}
573
574impl std::fmt::Debug for Array<'_> {
575    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576        self.value.fmt(f)
577    }
578}
579
580impl<'de> std::ops::Index<usize> for Array<'de> {
581    type Output = MaybeItem<'de>;
582
583    #[inline]
584    fn index(&self, index: usize) -> &Self::Output {
585        if let Some(item) = self.value.get(index) {
586            return MaybeItem::from_ref(item);
587        }
588        &crate::item::NONE
589    }
590}
591
592impl<'a, 'de> IntoIterator for &'a Array<'de> {
593    type Item = &'a Item<'de>;
594    type IntoIter = std::slice::Iter<'a, Item<'de>>;
595
596    fn into_iter(self) -> Self::IntoIter {
597        self.value.as_slice().iter()
598    }
599}
600
601impl<'a, 'de> IntoIterator for &'a mut Array<'de> {
602    type Item = &'a mut Item<'de>;
603    type IntoIter = std::slice::IterMut<'a, Item<'de>>;
604
605    fn into_iter(self) -> Self::IntoIter {
606        self.value.as_mut_slice().iter_mut()
607    }
608}
609
610/// Consuming iterator over an [`Array`], yielding [`Item`]s.
611pub struct IntoIter<'de> {
612    arr: InternalArray<'de>,
613    index: u32,
614}
615
616impl<'de> Iterator for IntoIter<'de> {
617    type Item = Item<'de>;
618
619    fn next(&mut self) -> Option<Self::Item> {
620        if self.index < self.arr.len {
621            // SAFETY: index < len is checked above, so the read is within
622            // initialized elements.
623            let val = unsafe { self.arr.ptr.as_ptr().add(self.index as usize).read() };
624            self.index += 1;
625            Some(val)
626        } else {
627            None
628        }
629    }
630
631    fn size_hint(&self) -> (usize, Option<usize>) {
632        let remaining = (self.arr.len - self.index) as usize;
633        (remaining, Some(remaining))
634    }
635}
636
637impl<'de> ExactSizeIterator for IntoIter<'de> {}
638
639impl<'de> IntoIterator for Array<'de> {
640    type Item = Item<'de>;
641    type IntoIter = IntoIter<'de>;
642
643    fn into_iter(self) -> Self::IntoIter {
644        IntoIter {
645            arr: self.value,
646            index: 0,
647        }
648    }
649}