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}