Skip to main content

praxis_runtime/
repr_c_vec.rs

1//! [`ReprCVec<T>`] — a growable vector whose field layout is a promise
2//! (ADR-118).
3//!
4//! `std::Vec<T>` is `#[repr(Rust)]`. Its length and its element pointer live
5//! inside a private `RawVec`, so `offset_of!` cannot name them and the field
6//! order is not stable across compiler versions. That is fine for Rust code and
7//! fatal for generated code: a backend that wants to read a `Vec[T]`'s length
8//! inline has nothing it is allowed to read.
9//!
10//! `ReprCVec` is a `#[repr(C)]` triple — pointer, length, capacity — that
11//! *never holds anything a `Vec` did not hand it*. Every construction goes
12//! through [`ReprCVec::from_vec`] and every mutation goes through
13//! [`ReprCVec::vec_mut`], which reconstitutes a real `Vec`, lets `Vec` do the
14//! growth, and takes the parts back. No allocator logic is reimplemented here;
15//! `RawVec`'s amortized growth, its capacity-overflow checks and its allocation
16//! failure handling are all still the ones doing the work. What is new is only
17//! that the three words are somewhere the compiler is allowed to look.
18//!
19//! # The measurement toggle
20//!
21//! The `std-vec-payload` cargo feature replaces the `#[repr(C)]` triple with a
22//! `#[repr(transparent)]` newtype over `std::Vec<T>`, leaving every caller in
23//! the tree byte-for-byte unchanged. That is the arm A / arm B pair ADR-118 is
24//! measured with: the only thing that differs between the two binaries is the
25//! representation of this one struct. The change is expected to be
26//! **performance neutral** — it changes a container's layout, not an algorithm
27//! — so the comparison is a regression check, not a win.
28
29use std::fmt;
30use std::ops::{Deref, DerefMut};
31
32// The three the `#[repr(C)]` arm needs and the `std-vec-payload` arm does not.
33#[cfg(not(feature = "std-vec-payload"))]
34use std::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
35
36// ===========================================================================
37// The representation, and the one place the two arms differ.
38// ===========================================================================
39
40/// A growable vector with a pinned `#[repr(C)]` layout.
41///
42/// **The field order is an ABI decision** (ADR-118). `ptr` is at offset 0 and
43/// `len` at offset 8 because those are the two words generated code reads —
44/// `praxis_vec_get` wants both, `praxis_vec_len` wants the second — and putting
45/// them adjacent and first means one base register and two small displacements,
46/// on the same cache line as the payload's element descriptor at offset 0 of
47/// [`VecPayload`](crate::collections::VecPayload). `cap` is last because no
48/// generated code has a reason to read it: capacity is the allocator's
49/// business, reached only from Rust through
50/// [`capacity`](ReprCVec::capacity) for GC pacing.
51///
52/// The order is pinned by `const _` assertions below rather than by a sentence,
53/// per the house rule for `#[repr(C)]` layout claims.
54#[cfg(not(feature = "std-vec-payload"))]
55#[repr(C)]
56pub struct ReprCVec<T> {
57    /// The element buffer. Non-null even when `cap == 0`, exactly as
58    /// `Vec::as_mut_ptr` is — an empty `Vec` answers a dangling aligned
59    /// pointer, and this field holds whatever it answered.
60    ptr: NonNull<T>,
61    /// The number of initialized elements. This is the word `praxis_vec_len`
62    /// exists to read.
63    len: usize,
64    /// The allocated capacity, in elements. This is the number
65    /// `Vec::from_raw_parts` demands back — not `len` — and getting that wrong
66    /// is a heap corruption rather than a wrong answer, which is why
67    /// [`a_vec_survives_the_round_trip_with_its_capacity_intact`] exists.
68    cap: usize,
69    /// `ReprCVec<T>` owns its `T`s, so it is invariant-free but drop-relevant:
70    /// this is what tells dropck and the auto traits that dropping the
71    /// container drops `T`s.
72    _owns: PhantomData<T>,
73}
74
75/// The `std-vec-payload` arm: the same API over an ordinary `std::Vec`.
76///
77/// This exists only so ADR-118's A/B has a toggle point that is exactly the
78/// representation and nothing else. It is never built by `just ci`.
79#[cfg(feature = "std-vec-payload")]
80#[repr(transparent)]
81pub struct ReprCVec<T> {
82    inner: Vec<T>,
83}
84
85// The layout claims, in the tree rather than in a comment. The size equality is
86// checked in both arms, and it is what keeps `VecPayload` in its block size
87// class: 8 bytes of element descriptor plus 24 bytes of vector is 32.
88const _: () = assert!(
89    std::mem::size_of::<ReprCVec<crate::GcRef>>() == std::mem::size_of::<Vec<crate::GcRef>>()
90);
91const _: () = assert!(
92    std::mem::align_of::<ReprCVec<crate::GcRef>>() == std::mem::align_of::<Vec<crate::GcRef>>()
93);
94const _: () = assert!(std::mem::size_of::<ReprCVec<crate::GcRef>>() == 24);
95
96#[cfg(not(feature = "std-vec-payload"))]
97mod layout {
98    use super::ReprCVec;
99    use crate::GcRef;
100    use std::mem::offset_of;
101
102    // The field order generated code bakes in. Changing any of these three
103    // numbers is an ABI change even though no ABI constant names them,
104    // because the moment the backend emits a load at a displacement, the
105    // displacement is the contract (ABI v20, ADR-118 part 2).
106    const _: () = assert!(offset_of!(ReprCVec<GcRef>, ptr) == 0);
107    const _: () = assert!(offset_of!(ReprCVec<GcRef>, len) == 8);
108    const _: () = assert!(offset_of!(ReprCVec<GcRef>, cap) == 16);
109
110    // …and the same three for `u64`, which is `BitSetPayload.words`. The two
111    // instantiations are asserted separately rather than argued to be equal:
112    // the fields are all pointer-width whatever `T` is, so the equality is
113    // obvious and therefore exactly the kind of thing that goes unchecked until
114    // a `PhantomData` moves. These two are the only instantiations generated
115    // code reads, and a third one wants its own pair of lines here.
116    const _: () = assert!(offset_of!(ReprCVec<u64>, ptr) == 0);
117    const _: () = assert!(offset_of!(ReprCVec<u64>, len) == 8);
118    const _: () = assert!(offset_of!(ReprCVec<u64>, cap) == 16);
119}
120
121/// The displacement of the element pointer within a [`ReprCVec`], for the two
122/// payloads generated code reads (`VecPayload.items`, `BitSetPayload.words`).
123///
124/// **This constant does not exist under `std-vec-payload`, and that is the
125/// point.** ADR-118 part 1's measurement arm replaces the pinned triple with a
126/// `std::Vec`, whose word order is precisely what nothing may assume — so a
127/// backend that emitted a load at this displacement against that arm would read
128/// a capacity where it wanted a length. Naming these two constants
129/// unconditionally in `praxis-codegen-cranelift` makes the combination a
130/// **build failure** rather than a miscompile.
131#[cfg(not(feature = "std-vec-payload"))]
132pub const REPR_C_VEC_ELEMENTS_OFFSET: usize = std::mem::offset_of!(ReprCVec<crate::GcRef>, ptr);
133
134/// The displacement of the length word within a [`ReprCVec`]. See
135/// [`REPR_C_VEC_ELEMENTS_OFFSET`].
136#[cfg(not(feature = "std-vec-payload"))]
137pub const REPR_C_VEC_LEN_OFFSET: usize = std::mem::offset_of!(ReprCVec<crate::GcRef>, len);
138
139/// Everything generated code needs to walk one collection payload's pinned
140/// [`ReprCVec`] inline, as **one value with private fields** (ADR-118 part 2).
141///
142/// This is [`InlineInternSite`](crate::InlineInternSite)'s shape and it is here
143/// for the same reason. The sequence needs a descriptor to prove and three
144/// displacements, and handed over as loose constants they are four independent
145/// chances to pair one payload's offsets with another's descriptor — which is
146/// not a hypothetical: `VecPayload` and `GridPayload` are both a `Vec<GcRef>`
147/// behind a leading word, at *different* displacements, and a `Grid` proved as
148/// a `Vec` would read its width as an element pointer. So they are one value,
149/// its fields are private, its constructor is `pub(crate)`, and each instance is
150/// minted in the module that owns the payload whose layout it describes.
151///
152/// The backend cannot assemble one; it can only name one this crate wrote.
153///
154/// **The displacements are from the object's base**, not from its payload:
155/// generated code holds a `GcRef` and the header size is `GcHeader`'s business
156/// (ADR-039 decision 1), so the addition happens once, here, rather than at
157/// three emit sites.
158#[cfg(not(feature = "std-vec-payload"))]
159#[derive(Clone, Copy, Debug)]
160pub struct InlineSliceSite {
161    type_id: crate::descriptor::BuiltinTypeId,
162    elements_offset: usize,
163    len_offset: usize,
164    element_shift: u8,
165}
166
167#[cfg(not(feature = "std-vec-payload"))]
168impl InlineSliceSite {
169    /// The site for a payload of alignment `payload_align` whose `ReprCVec`
170    /// field begins at `field_offset` within it and holds elements of
171    /// `element_size` bytes, in objects whose descriptor is `type_id`'s.
172    ///
173    /// # Panics
174    /// At compile time (every call is a `const` initializer) if `element_size`
175    /// is not a power of two — the emitted index arithmetic is a shift.
176    pub(crate) const fn new(
177        type_id: crate::descriptor::BuiltinTypeId,
178        payload_align: usize,
179        field_offset: usize,
180        element_size: usize,
181    ) -> InlineSliceSite {
182        assert!(
183            element_size.is_power_of_two(),
184            "the element scale must be a shift"
185        );
186        let base = crate::GcHeader::payload_offset_for(payload_align) + field_offset;
187        InlineSliceSite {
188            type_id,
189            elements_offset: base + REPR_C_VEC_ELEMENTS_OFFSET,
190            len_offset: base + REPR_C_VEC_LEN_OFFSET,
191            element_shift: element_size.trailing_zeros() as u8,
192        }
193    }
194
195    /// The built-in whose descriptor an object must carry before any of the
196    /// displacements below may be read. The proof is ADR-102's, and it is what
197    /// makes the folded offsets the offsets the allocator actually used.
198    #[must_use]
199    pub const fn type_id(self) -> crate::descriptor::BuiltinTypeId {
200        self.type_id
201    }
202
203    /// Object base → the element buffer pointer.
204    #[must_use]
205    pub const fn elements_offset(self) -> usize {
206        self.elements_offset
207    }
208
209    /// Object base → the element count.
210    #[must_use]
211    pub const fn len_offset(self) -> usize {
212        self.len_offset
213    }
214
215    /// `log2(element_size)`: the shift that turns an index into a byte offset.
216    #[must_use]
217    pub const fn element_shift(self) -> u8 {
218        self.element_shift
219    }
220}
221
222// ===========================================================================
223// The primitives. Everything else in this file is written in terms of these
224// four, so the two arms share all of the API surface and none of the unsafe.
225// ===========================================================================
226
227#[cfg(not(feature = "std-vec-payload"))]
228impl<T> ReprCVec<T> {
229    /// Decompose a `Vec` into the three words. No allocation, no copy.
230    #[inline]
231    #[must_use]
232    pub fn from_vec(vec: Vec<T>) -> Self {
233        let mut vec = ManuallyDrop::new(vec);
234        let (ptr, len, cap) = (vec.as_mut_ptr(), vec.len(), vec.capacity());
235        Self {
236            // SAFETY: `Vec::as_mut_ptr` never returns null — for a zero-capacity
237            // `Vec` it answers `NonNull::dangling()`, which is aligned and
238            // non-null by construction. This is the only route into `ptr`, so
239            // "the pointer came out of a live `Vec`" is an invariant of the
240            // type rather than an obligation on callers.
241            ptr: unsafe { NonNull::new_unchecked(ptr) },
242            len,
243            cap,
244            _owns: PhantomData,
245        }
246    }
247
248    /// Reassemble the `Vec` these three words came from.
249    #[inline]
250    #[must_use]
251    pub fn into_vec(self) -> Vec<T> {
252        let me = ManuallyDrop::new(self);
253        // SAFETY: the three words were produced by `from_vec` from a live `Vec`
254        // (the only constructor) and have not been observed by anything else
255        // since — `ptr` and `cap` are private and no method writes them except
256        // through a `from_vec`. `cap` is therefore the *allocated* capacity and
257        // not the length, which is `from_raw_parts`'s one easy contract to
258        // break. `ManuallyDrop` is what stops the buffer being freed twice:
259        // this function consumes `self`, and the returned `Vec` is now its
260        // owner.
261        unsafe { Vec::from_raw_parts(me.ptr.as_ptr(), me.len, me.cap) }
262    }
263
264    /// The elements, in order.
265    #[inline]
266    #[must_use]
267    pub fn as_slice(&self) -> &[T] {
268        // SAFETY: `ptr` is the buffer of the `Vec` `from_vec` decomposed and
269        // `len` is that `Vec`'s length, so `len` elements from `ptr` are
270        // initialized and contiguous. The borrow of `self` is what bounds the
271        // slice's lifetime to the container's.
272        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
273    }
274
275    /// The elements, mutably. The *length* cannot be changed through this —
276    /// that is [`vec_mut`](Self::vec_mut)'s job.
277    #[inline]
278    #[must_use]
279    pub fn as_mut_slice(&mut self) -> &mut [T] {
280        // SAFETY: as `as_slice`, and the `&mut self` borrow is what makes the
281        // aliasing exclusive.
282        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
283    }
284
285    /// The allocated capacity, in elements. Read by `vec_owned_bytes` for GC
286    /// pacing: the buffer's real footprint, not its occupancy.
287    #[inline]
288    #[must_use]
289    pub fn capacity(&self) -> usize {
290        self.cap
291    }
292
293    /// Borrow the contents as a real `Vec` for the duration of a mutation.
294    ///
295    /// This is the **only** way to change the length. The returned guard hands
296    /// the parts back on drop, including on unwind.
297    #[inline]
298    pub fn vec_mut(&mut self) -> VecMut<'_, T> {
299        // Leave `self` genuinely empty rather than stale. A guard that is
300        // `mem::forget`ed then leaks the buffer — which is safe — instead of
301        // leaving `self` holding a pointer the `Vec` may since have
302        // reallocated away from, which is a use-after-free. Three stores, on a
303        // path that is about to call `RawVec::grow`.
304        let taken = std::mem::take(self).into_vec();
305        VecMut {
306            vec: ManuallyDrop::new(taken),
307            owner: self,
308        }
309    }
310}
311
312#[cfg(feature = "std-vec-payload")]
313impl<T> ReprCVec<T> {
314    /// Wrap the `Vec`. The `std-vec-payload` arm keeps it whole.
315    #[inline]
316    #[must_use]
317    pub fn from_vec(vec: Vec<T>) -> Self {
318        Self { inner: vec }
319    }
320
321    /// Unwrap the `Vec`.
322    #[inline]
323    #[must_use]
324    pub fn into_vec(self) -> Vec<T> {
325        self.inner
326    }
327
328    /// The elements, in order.
329    #[inline]
330    #[must_use]
331    pub fn as_slice(&self) -> &[T] {
332        self.inner.as_slice()
333    }
334
335    /// The elements, mutably.
336    #[inline]
337    #[must_use]
338    pub fn as_mut_slice(&mut self) -> &mut [T] {
339        self.inner.as_mut_slice()
340    }
341
342    /// The allocated capacity, in elements.
343    #[inline]
344    #[must_use]
345    pub fn capacity(&self) -> usize {
346        self.inner.capacity()
347    }
348
349    /// Borrow the contents as a real `Vec` for the duration of a mutation.
350    #[inline]
351    pub fn vec_mut(&mut self) -> VecMut<'_, T> {
352        VecMut { owner: self }
353    }
354}
355
356/// A `Vec` borrowed out of a [`ReprCVec`] for the duration of a mutation.
357///
358/// Dropping it writes the (possibly reallocated) parts back. While it is alive
359/// the `ReprCVec` it came from is empty, not stale — see
360/// [`ReprCVec::vec_mut`].
361#[cfg(not(feature = "std-vec-payload"))]
362pub struct VecMut<'a, T> {
363    /// `ManuallyDrop` because [`Drop`] hands the parts back to `owner` instead
364    /// of freeing them.
365    vec: ManuallyDrop<Vec<T>>,
366    owner: &'a mut ReprCVec<T>,
367}
368
369#[cfg(not(feature = "std-vec-payload"))]
370impl<T> Drop for VecMut<'_, T> {
371    #[inline]
372    fn drop(&mut self) {
373        // SAFETY: `ManuallyDrop::take` needs the value never to be used again.
374        // This is `Drop::drop`, `self.vec` is private, and `self` is gone the
375        // instant this returns — so there is no "again". Running on the unwind
376        // path is deliberate: a wrapper whose mutation panicked still gets its
377        // elements back, which is what makes the two ADR-118 arms agree even
378        // under a fault.
379        let vec = unsafe { ManuallyDrop::take(&mut self.vec) };
380        *self.owner = ReprCVec::from_vec(vec);
381    }
382}
383
384#[cfg(not(feature = "std-vec-payload"))]
385impl<T> Deref for VecMut<'_, T> {
386    type Target = Vec<T>;
387
388    #[inline]
389    fn deref(&self) -> &Vec<T> {
390        &self.vec
391    }
392}
393
394#[cfg(not(feature = "std-vec-payload"))]
395impl<T> DerefMut for VecMut<'_, T> {
396    #[inline]
397    fn deref_mut(&mut self) -> &mut Vec<T> {
398        &mut self.vec
399    }
400}
401
402/// A `Vec` borrowed out of a [`ReprCVec`] for the duration of a mutation.
403#[cfg(feature = "std-vec-payload")]
404pub struct VecMut<'a, T> {
405    owner: &'a mut ReprCVec<T>,
406}
407
408#[cfg(feature = "std-vec-payload")]
409impl<T> Deref for VecMut<'_, T> {
410    type Target = Vec<T>;
411
412    #[inline]
413    fn deref(&self) -> &Vec<T> {
414        &self.owner.inner
415    }
416}
417
418#[cfg(feature = "std-vec-payload")]
419impl<T> DerefMut for VecMut<'_, T> {
420    #[inline]
421    fn deref_mut(&mut self) -> &mut Vec<T> {
422        &mut self.owner.inner
423    }
424}
425
426// ===========================================================================
427// The shared API. Written entirely in terms of the five primitives above, so
428// neither arm can drift from the other.
429// ===========================================================================
430
431impl<T> ReprCVec<T> {
432    /// An empty vector. Allocates nothing, exactly as `Vec::new` does.
433    #[inline]
434    #[must_use]
435    pub fn new() -> Self {
436        Self::from_vec(Vec::new())
437    }
438
439    /// An empty vector with room for `capacity` elements.
440    #[inline]
441    #[must_use]
442    pub fn with_capacity(capacity: usize) -> Self {
443        Self::from_vec(Vec::with_capacity(capacity))
444    }
445
446    /// The number of elements.
447    #[inline]
448    #[must_use]
449    pub fn len(&self) -> usize {
450        self.as_slice().len()
451    }
452
453    /// Whether there are no elements.
454    #[inline]
455    #[must_use]
456    pub fn is_empty(&self) -> bool {
457        self.len() == 0
458    }
459
460    /// Append one element, growing if needed.
461    #[inline]
462    pub fn push(&mut self, value: T) {
463        self.vec_mut().push(value);
464    }
465
466    /// Remove and answer the last element, or `None` when empty.
467    #[inline]
468    pub fn pop(&mut self) -> Option<T> {
469        self.vec_mut().pop()
470    }
471
472    /// Insert `value` at `index`, shifting everything after it right.
473    #[inline]
474    pub fn insert(&mut self, index: usize, value: T) {
475        self.vec_mut().insert(index, value);
476    }
477
478    /// Remove and answer the element at `index`, shifting everything after it
479    /// left.
480    #[inline]
481    pub fn remove(&mut self, index: usize) -> T {
482        self.vec_mut().remove(index)
483    }
484
485    /// Remove the element at `index` by swapping the last one into its place.
486    #[inline]
487    pub fn swap_remove(&mut self, index: usize) -> T {
488        self.vec_mut().swap_remove(index)
489    }
490
491    /// Drop every element, keeping the capacity.
492    #[inline]
493    pub fn clear(&mut self) {
494        self.vec_mut().clear();
495    }
496
497    /// Shorten to `len` elements, dropping the rest. A no-op if already shorter.
498    #[inline]
499    pub fn truncate(&mut self, len: usize) {
500        self.vec_mut().truncate(len);
501    }
502
503    /// Keep only the elements `f` answers `true` for, in order.
504    #[inline]
505    pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
506        self.vec_mut().retain(f);
507    }
508
509    /// Reserve room for at least `additional` more elements.
510    #[inline]
511    pub fn reserve(&mut self, additional: usize) {
512        self.vec_mut().reserve(additional);
513    }
514
515    /// Move every element of `other` onto the end of this vector.
516    #[inline]
517    pub fn append(&mut self, other: &mut Vec<T>) {
518        self.vec_mut().append(other);
519    }
520}
521
522impl<T: Clone> ReprCVec<T> {
523    /// Append a copy of every element of `other`.
524    #[inline]
525    pub fn extend_from_slice(&mut self, other: &[T]) {
526        self.vec_mut().extend_from_slice(other);
527    }
528
529    /// Grow or shrink to `len` elements, filling new slots with `value`.
530    #[inline]
531    pub fn resize(&mut self, len: usize, value: T) {
532        self.vec_mut().resize(len, value);
533    }
534}
535
536impl<T> Default for ReprCVec<T> {
537    #[inline]
538    fn default() -> Self {
539        Self::new()
540    }
541}
542
543impl<T> Deref for ReprCVec<T> {
544    type Target = [T];
545
546    #[inline]
547    fn deref(&self) -> &[T] {
548        self.as_slice()
549    }
550}
551
552impl<T> DerefMut for ReprCVec<T> {
553    #[inline]
554    fn deref_mut(&mut self) -> &mut [T] {
555        self.as_mut_slice()
556    }
557}
558
559// `for x in &items` and `for x in &mut items` do not go through `Deref`:
560// trait selection for `IntoIterator` does not autoderef. These two impls are
561// what lets the runtime's reading sites iterate a `ReprCVec` exactly as they
562// would a `Vec`.
563impl<'a, T> IntoIterator for &'a ReprCVec<T> {
564    type Item = &'a T;
565    type IntoIter = std::slice::Iter<'a, T>;
566
567    #[inline]
568    fn into_iter(self) -> Self::IntoIter {
569        self.as_slice().iter()
570    }
571}
572
573impl<'a, T> IntoIterator for &'a mut ReprCVec<T> {
574    type Item = &'a mut T;
575    type IntoIter = std::slice::IterMut<'a, T>;
576
577    #[inline]
578    fn into_iter(self) -> Self::IntoIter {
579        self.as_mut_slice().iter_mut()
580    }
581}
582
583impl<T> IntoIterator for ReprCVec<T> {
584    type Item = T;
585    type IntoIter = std::vec::IntoIter<T>;
586
587    #[inline]
588    fn into_iter(self) -> Self::IntoIter {
589        self.into_vec().into_iter()
590    }
591}
592
593impl<T> Extend<T> for ReprCVec<T> {
594    #[inline]
595    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
596        self.vec_mut().extend(iter);
597    }
598}
599
600impl<T> FromIterator<T> for ReprCVec<T> {
601    #[inline]
602    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
603        Self::from_vec(iter.into_iter().collect())
604    }
605}
606
607impl<T> From<Vec<T>> for ReprCVec<T> {
608    #[inline]
609    fn from(vec: Vec<T>) -> Self {
610        Self::from_vec(vec)
611    }
612}
613
614impl<T> From<ReprCVec<T>> for Vec<T> {
615    #[inline]
616    fn from(vec: ReprCVec<T>) -> Self {
617        vec.into_vec()
618    }
619}
620
621impl<T: Clone> Clone for ReprCVec<T> {
622    #[inline]
623    fn clone(&self) -> Self {
624        Self::from_vec(self.as_slice().to_vec())
625    }
626}
627
628impl<T: fmt::Debug> fmt::Debug for ReprCVec<T> {
629    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630        fmt::Debug::fmt(self.as_slice(), f)
631    }
632}
633
634impl<T: PartialEq> PartialEq for ReprCVec<T> {
635    #[inline]
636    fn eq(&self, other: &Self) -> bool {
637        self.as_slice() == other.as_slice()
638    }
639}
640
641impl<T: Eq> Eq for ReprCVec<T> {}
642
643/// Freeing the buffer is `Vec`'s job; this hands it back to `Vec` to do.
644///
645/// Only the `#[repr(C)]` arm needs one — the `std-vec-payload` arm's field
646/// drops itself.
647#[cfg(not(feature = "std-vec-payload"))]
648impl<T> Drop for ReprCVec<T> {
649    #[inline]
650    fn drop(&mut self) {
651        // `into_vec` consumes a `ReprCVec`, so take one out and leave an empty
652        // one behind rather than reading the fields in place: what stays behind
653        // is a valid container over no allocation, so there is nothing for the
654        // drop glue that runs after this body to free a second time. The empty
655        // container's own `Drop::drop` is not re-entered — drop glue never
656        // re-calls `Drop::drop` on the value a `Drop::drop` body wrote back —
657        // and `Default::default` allocates nothing, so this is not recursive.
658        drop(std::mem::take(self).into_vec());
659    }
660}
661
662// `Vec<T>` is `Send`/`Sync` when `T` is, on the grounds that the container
663// uniquely owns its elements. The `#[repr(C)]` arm holds a `NonNull<T>`, which
664// is unconditionally neither, so without these two impls the two ADR-118 arms
665// would differ in something other than layout. The reasoning is `Vec`'s,
666// unchanged: `ReprCVec` is the sole owner of its buffer and hands out
667// references only through `&self`/`&mut self`.
668//
669// SAFETY: sole ownership of the `T`s, so moving the container between threads
670// moves the `T`s and nothing else observes them.
671#[cfg(not(feature = "std-vec-payload"))]
672unsafe impl<T: Send> Send for ReprCVec<T> {}
673// SAFETY: `&ReprCVec<T>` hands out only `&T`, so sharing the container across
674// threads shares the `T`s and nothing more.
675#[cfg(not(feature = "std-vec-payload"))]
676unsafe impl<T: Sync> Sync for ReprCVec<T> {}
677
678#[cfg(test)]
679mod tests {
680    use super::*;
681    use std::sync::Arc;
682    use std::sync::atomic::{AtomicUsize, Ordering};
683
684    #[test]
685    fn a_repr_c_vec_is_the_same_three_words_a_std_vec_is() {
686        assert_eq!(
687            std::mem::size_of::<ReprCVec<u64>>(),
688            std::mem::size_of::<Vec<u64>>()
689        );
690        assert_eq!(
691            std::mem::align_of::<ReprCVec<u64>>(),
692            std::mem::align_of::<Vec<u64>>()
693        );
694    }
695
696    #[cfg(not(feature = "std-vec-payload"))]
697    #[test]
698    fn the_pointer_is_first_the_length_is_second_and_the_capacity_is_last() {
699        use std::mem::offset_of;
700        assert_eq!(offset_of!(ReprCVec<u64>, ptr), 0);
701        assert_eq!(offset_of!(ReprCVec<u64>, len), 8);
702        assert_eq!(offset_of!(ReprCVec<u64>, cap), 16);
703    }
704
705    #[cfg(not(feature = "std-vec-payload"))]
706    #[test]
707    fn the_length_word_is_readable_at_its_declared_offset() {
708        // The whole point of the type: a reader that knows only the offsets can
709        // find the length. This is the Rust-side rehearsal of the `load`
710        // generated code emits at displacement 8.
711        let v = ReprCVec::from_vec(vec![1_u64, 2, 3, 4, 5]);
712        let base = std::ptr::addr_of!(v).cast::<u8>();
713        // SAFETY: `base` points at a live `ReprCVec<u64>`, whose layout the
714        // `const _` assertions above pin: a pointer at 0 and a `usize` at 8.
715        let len = unsafe { base.add(8).cast::<usize>().read() };
716        assert_eq!(len, 5);
717        // SAFETY: as above, the element pointer is the word at offset 0.
718        let ptr = unsafe { base.cast::<*const u64>().read() };
719        // SAFETY: `ptr` is the live buffer and index 3 is in bounds.
720        assert_eq!(unsafe { *ptr.add(3) }, 4);
721    }
722
723    #[test]
724    fn a_vec_survives_the_round_trip_with_its_capacity_intact() {
725        // `Vec::from_raw_parts` wants the *allocated* capacity, not the length.
726        // Handing it the length frees the wrong number of bytes, which is a
727        // heap corruption rather than a wrong answer — so the capacity is
728        // asserted, not just the contents.
729        let mut original: Vec<u64> = Vec::with_capacity(17);
730        original.extend([10, 20, 30]);
731        let capacity = original.capacity();
732        assert!(
733            capacity >= 17,
734            "with_capacity should over-reserve, not exact"
735        );
736
737        let wrapped = ReprCVec::from_vec(original);
738        assert_eq!(wrapped.len(), 3);
739        assert_eq!(wrapped.capacity(), capacity);
740        assert_eq!(wrapped.as_slice(), &[10, 20, 30]);
741
742        let back = wrapped.into_vec();
743        assert_eq!(back, vec![10, 20, 30]);
744        assert_eq!(
745            back.capacity(),
746            capacity,
747            "the capacity is the allocation's, and it must survive the trip"
748        );
749    }
750
751    #[test]
752    fn an_empty_vec_round_trips_without_touching_the_allocator() {
753        let wrapped = ReprCVec::from_vec(Vec::<u64>::new());
754        assert_eq!(wrapped.capacity(), 0);
755        assert!(wrapped.is_empty());
756        let back = wrapped.into_vec();
757        assert_eq!(back.capacity(), 0);
758        assert!(back.is_empty());
759    }
760
761    #[test]
762    fn a_push_that_reallocates_leaves_the_container_pointing_at_the_new_buffer() {
763        let mut v = ReprCVec::<u64>::new();
764        for i in 0..1000_u64 {
765            v.push(i);
766        }
767        assert_eq!(v.len(), 1000);
768        assert_eq!(v[0], 0);
769        assert_eq!(v[999], 999);
770        assert!(v.capacity() >= 1000);
771        // Read every element: a stale pointer left behind by a realloc shows up
772        // here and nowhere else.
773        assert_eq!(v.iter().sum::<u64>(), (0..1000).sum::<u64>());
774    }
775
776    /// Counts its own drops, so a leak and a double free are both visible.
777    struct DropProbe(Arc<AtomicUsize>);
778
779    impl Drop for DropProbe {
780        fn drop(&mut self) {
781            self.0.fetch_add(1, Ordering::SeqCst);
782        }
783    }
784
785    #[test]
786    fn dropping_the_container_drops_every_element_exactly_once() {
787        let count = Arc::new(AtomicUsize::new(0));
788        {
789            let mut v = ReprCVec::new();
790            for _ in 0..64 {
791                v.push(DropProbe(Arc::clone(&count)));
792            }
793            assert_eq!(count.load(Ordering::SeqCst), 0, "no drops while it lives");
794        }
795        assert_eq!(count.load(Ordering::SeqCst), 64);
796    }
797
798    #[test]
799    fn a_round_trip_through_a_vec_does_not_drop_anything() {
800        let count = Arc::new(AtomicUsize::new(0));
801        let v = ReprCVec::from_vec(vec![
802            DropProbe(Arc::clone(&count)),
803            DropProbe(Arc::clone(&count)),
804        ]);
805        let back = v.into_vec();
806        assert_eq!(count.load(Ordering::SeqCst), 0);
807        drop(back);
808        assert_eq!(count.load(Ordering::SeqCst), 2);
809    }
810
811    #[test]
812    fn a_removed_element_is_dropped_by_its_new_owner_and_not_by_the_container() {
813        let count = Arc::new(AtomicUsize::new(0));
814        let mut v = ReprCVec::new();
815        v.push(DropProbe(Arc::clone(&count)));
816        v.push(DropProbe(Arc::clone(&count)));
817        let taken = v.pop().expect("two were pushed");
818        assert_eq!(count.load(Ordering::SeqCst), 0);
819        drop(taken);
820        assert_eq!(count.load(Ordering::SeqCst), 1);
821        drop(v);
822        assert_eq!(count.load(Ordering::SeqCst), 2);
823    }
824
825    #[test]
826    fn clear_and_truncate_drop_what_they_remove() {
827        let count = Arc::new(AtomicUsize::new(0));
828        let mut v = ReprCVec::new();
829        for _ in 0..10 {
830            v.push(DropProbe(Arc::clone(&count)));
831        }
832        v.truncate(4);
833        assert_eq!(count.load(Ordering::SeqCst), 6);
834        assert_eq!(v.len(), 4);
835        v.clear();
836        assert_eq!(count.load(Ordering::SeqCst), 10);
837        assert!(v.is_empty());
838        // Capacity survives a clear, which is what `vec_owned_bytes` reports.
839        assert!(v.capacity() >= 10);
840    }
841
842    // Arm-B only, and not because the `std-vec-payload` arm is weaker: that arm
843    // has no raw pointer that could go stale, so its guard is a plain borrow and
844    // forgetting it changes nothing. The property being asserted here only
845    // exists where the hazard does.
846    #[cfg(not(feature = "std-vec-payload"))]
847    #[test]
848    fn a_forgotten_mutation_guard_leaves_the_container_empty_rather_than_stale() {
849        // `mem::forget` on the guard is the one way the write-back can be
850        // skipped. It must leak the buffer, not leave a dangling pointer in a
851        // container the collector will read: `vec_trace` walks `items` on every
852        // mark.
853        let count = Arc::new(AtomicUsize::new(0));
854        let mut v = ReprCVec::new();
855        v.push(DropProbe(Arc::clone(&count)));
856
857        let mut guard = v.vec_mut();
858        guard.push(DropProbe(Arc::clone(&count)));
859        std::mem::forget(guard);
860
861        assert_eq!(v.len(), 0, "a forgotten guard leaves an empty container");
862        assert_eq!(v.capacity(), 0);
863        assert!(v.iter().next().is_none());
864        drop(v);
865        // Both probes are leaked with the buffer. A leak is safe; a stale
866        // pointer read by `vec_trace` would not be.
867        assert_eq!(count.load(Ordering::SeqCst), 0);
868    }
869
870    #[test]
871    fn a_mutation_that_panics_still_hands_the_elements_back() {
872        let mut v = ReprCVec::from_vec(vec![1_u64, 2, 3]);
873        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
874            let mut guard = v.vec_mut();
875            guard.push(4);
876            panic!("the wrapper faulted mid-mutation");
877        }));
878        assert!(result.is_err());
879        assert_eq!(
880            v.as_slice(),
881            &[1, 2, 3, 4],
882            "the guard's Drop runs on the unwind path too"
883        );
884    }
885
886    #[test]
887    fn the_reading_api_is_the_slice_api() {
888        let v = ReprCVec::from_vec(vec![3_u64, 1, 2]);
889        assert_eq!(v.len(), 3);
890        assert_eq!(v[1], 1);
891        assert_eq!(v.first().copied(), Some(3));
892        assert_eq!(v.last().copied(), Some(2));
893        assert!(v.contains(&2));
894        assert_eq!(v.iter().copied().max(), Some(3));
895        let doubled: Vec<u64> = (&v).into_iter().map(|x| x * 2).collect();
896        assert_eq!(doubled, vec![6, 2, 4]);
897    }
898
899    #[test]
900    fn the_mutable_slice_api_reaches_through_deref_mut() {
901        let mut v = ReprCVec::from_vec(vec![3_u64, 1, 2]);
902        v.sort_unstable();
903        assert_eq!(v.as_slice(), &[1, 2, 3]);
904        v.swap(0, 2);
905        assert_eq!(v.as_slice(), &[3, 2, 1]);
906        for x in &mut v {
907            *x += 1;
908        }
909        assert_eq!(v.as_slice(), &[4, 3, 2]);
910    }
911
912    #[test]
913    fn extend_insert_remove_and_retain_agree_with_a_std_vec() {
914        let mut ours = ReprCVec::<u64>::new();
915        let mut theirs = Vec::<u64>::new();
916
917        ours.extend(0..20);
918        theirs.extend(0..20);
919
920        ours.insert(5, 99);
921        theirs.insert(5, 99);
922
923        assert_eq!(ours.remove(0), theirs.remove(0));
924        assert_eq!(ours.swap_remove(3), theirs.swap_remove(3));
925
926        ours.retain(|x| x % 2 == 0);
927        theirs.retain(|x| x % 2 == 0);
928
929        ours.extend_from_slice(&[7, 7, 7]);
930        theirs.extend_from_slice(&[7, 7, 7]);
931
932        assert_eq!(ours.as_slice(), theirs.as_slice());
933        assert_eq!(ours.into_vec(), theirs);
934    }
935
936    #[test]
937    fn collect_clone_and_equality_behave() {
938        let v: ReprCVec<u64> = (0..5).collect();
939        let w = v.clone();
940        assert_eq!(v, w);
941        assert_eq!(format!("{v:?}"), "[0, 1, 2, 3, 4]");
942        let owned: Vec<u64> = v.into_iter().collect();
943        assert_eq!(owned, vec![0, 1, 2, 3, 4]);
944    }
945
946    #[test]
947    fn a_zero_sized_element_type_round_trips() {
948        // `Vec<()>` has a dangling pointer and `usize::MAX` capacity; the
949        // decomposition must not care.
950        let v = ReprCVec::from_vec(vec![(), (), ()]);
951        assert_eq!(v.len(), 3);
952        let back = v.into_vec();
953        assert_eq!(back.len(), 3);
954    }
955}