1use crate::memory::align_index;
2use crate::raw_vec::RawVec;
3use crate::value_type::ValueType;
4use std::mem::MaybeUninit;
5use std::mem::size_of;
6
7#[derive(Debug)]
11pub struct RawStack {
12 buffer: RawVec,
13 owners: Vec<fn(&mut RawStack)>,
14 raw_owners: Vec<RawOwner>,
15}
16
17#[derive(Debug)]
19struct RawOwner {
20 depth: usize,
21 before: usize,
22 align: usize,
23 size: usize,
24 ty: Option<ValueType>,
25}
26
27struct UnwindGuard<'a> {
29 stack: &'a mut RawStack,
30 byte_origin: usize,
31 depth: usize,
32 armed: bool,
33}
34
35impl Drop for UnwindGuard<'_> {
36 fn drop(&mut self) {
38 if self.armed {
39 self.stack.unwind_to(self.byte_origin, self.depth);
40 }
41 }
42}
43
44impl Drop for RawStack {
45 fn drop(&mut self) {
49 self.unwind_to(0, 0);
50 }
51}
52
53impl RawStack {
54 #[must_use]
63 pub fn with_base_alignment(base_alignment: usize) -> Self {
64 RawStack {
65 buffer: RawVec::with_base_alignment(base_alignment),
66 owners: Vec::new(),
67 raw_owners: Vec::new(),
68 }
69 }
70
71 pub(crate) fn depth(&self) -> usize {
73 self.owners.len()
74 }
75
76 pub(crate) fn drop_top(&mut self) {
83 debug_assert!(self.depth() > 0);
84 let destroy = *self.owners.last().expect("top owner exists");
85 destroy(self);
86 }
87
88 pub(crate) fn release_top(&mut self, count: usize) {
93 debug_assert!(count <= self.depth());
94 let depth = self.depth() - count;
95 while self
96 .raw_owners
97 .last()
98 .is_some_and(|owner| owner.depth > depth)
99 {
100 self.raw_owners.pop();
101 }
102 self.owners.truncate(depth);
103 }
104
105 pub(crate) fn own_top(&mut self, ty: ValueType) {
110 let depth = self.depth();
111 let owner = self.raw_owners.last_mut().expect("top slot must be raw");
112 debug_assert_eq!(owner.depth, depth);
113 debug_assert!(owner.ty.is_none());
114 debug_assert_eq!(owner.size, ty.size());
115 debug_assert_eq!(owner.align, ty.align());
116 owner.ty = Some(ty);
117 }
118
119 pub(crate) fn unwind_to(&mut self, byte_origin: usize, depth: usize) {
126 debug_assert!(depth <= self.depth());
127 debug_assert!(byte_origin <= self.len());
128 let mut guard = UnwindGuard {
129 stack: self,
130 byte_origin,
131 depth,
132 armed: true,
133 };
134 while guard.stack.depth() > depth {
135 guard.stack.drop_top();
136 }
137 guard.stack.buffer.truncate(byte_origin);
138 guard.armed = false;
139 }
140
141 pub(crate) fn with_unwind<R>(
152 &mut self,
153 byte_origin: usize,
154 depth: usize,
155 callback: impl FnOnce(&mut RawStack) -> anyhow::Result<R>,
156 ) -> anyhow::Result<R> {
157 debug_assert!(depth <= self.depth());
158 debug_assert!(byte_origin <= self.len());
159 let mut guard = UnwindGuard {
160 stack: self,
161 byte_origin,
162 depth,
163 armed: true,
164 };
165 let result = callback(guard.stack);
166 if result.is_ok() {
167 guard.armed = false;
168 }
169 result
170 }
171
172 fn destroy_typed<T, const PADDING: bool>(&mut self) {
174 drop(unsafe { self.pop::<T>(PADDING) });
175 }
176
177 fn destroy_raw(&mut self) {
179 let owner = self.raw_owners.pop().expect("raw owner exists");
180 debug_assert_eq!(owner.depth, self.depth());
181 self.owners.pop();
182 let offset = align_index(owner.align, owner.before);
183 let pointer = unsafe { self.buffer.as_mut_ptr().add(offset).cast::<u8>() };
184 self.buffer.truncate(owner.before);
185 if let Some(ty) = owner.ty {
186 unsafe { crate::owned_value::drop_raw(&ty, pointer) };
187 }
188 }
189
190 #[must_use]
192 #[allow(clippy::len_without_is_empty)]
193 pub fn len(&self) -> usize {
194 self.buffer.len()
195 }
196
197 pub fn push<T: 'static>(&mut self, value: T) -> bool {
241 let len = self.buffer.len();
242 let aligned_index = align_index(align_of::<T>(), len);
243 let new_len = aligned_index + size_of::<T>();
244
245 self.owners.reserve(1);
246 self.buffer.reserve((new_len - len).max(1));
247 unsafe {
248 self.buffer.set_len(new_len);
249 if aligned_index - len > 0 {
250 self.buffer[len].write(1);
252 self.buffer[len + 1..aligned_index].fill(MaybeUninit::new(0));
253 }
254
255 std::ptr::write(
256 self.buffer.as_mut_ptr().add(aligned_index).cast::<T>(),
257 value,
258 );
259 }
260 let padding = aligned_index - len > 0;
261 self.owners.push(if padding {
262 Self::destroy_typed::<T, true>
263 } else {
264 Self::destroy_typed::<T, false>
265 });
266 padding
267 }
268
269 pub unsafe fn push_raw(
287 &mut self,
288 align: usize,
289 size: usize,
290 src: *const MaybeUninit<u8>,
291 ) -> bool {
292 debug_assert!(align.is_power_of_two());
293 let len = self.buffer.len();
294 let aligned_index = align_index(align, len);
295 let new_len = aligned_index + size;
296
297 self.owners.reserve(1);
298 self.raw_owners.reserve(1);
299 self.buffer.reserve((new_len - len).max(1));
300 unsafe {
301 self.buffer.set_len(new_len);
302 if aligned_index - len > 0 {
303 self.buffer[len].write(1);
304 self.buffer[len + 1..aligned_index].fill(MaybeUninit::new(0));
305 }
306 std::ptr::copy_nonoverlapping(src, self.buffer.as_mut_ptr().add(aligned_index), size);
307 }
308 self.owners.push(Self::destroy_raw);
309 self.raw_owners.push(RawOwner {
310 depth: self.depth(),
311 before: len,
312 align,
313 size,
314 ty: None,
315 });
316 aligned_index - len > 0
317 }
318
319 pub unsafe fn reserve_and_write(
335 &mut self,
336 align: usize,
337 size: usize,
338 write: impl FnOnce(*mut u8),
339 ) -> bool {
340 debug_assert!(align.is_power_of_two());
341 let len = self.buffer.len();
342 let aligned_index = align_index(align, len);
343 let new_len = aligned_index + size;
344
345 self.owners.reserve(1);
346 self.raw_owners.reserve(1);
347 self.buffer.reserve((new_len - len).max(1));
348 unsafe {
349 self.buffer.set_len(new_len);
350 if aligned_index - len > 0 {
351 self.buffer[len].write(1);
352 self.buffer[len + 1..aligned_index].fill(MaybeUninit::new(0));
353 }
354 write(self.buffer.as_mut_ptr().add(aligned_index).cast::<u8>());
355 }
356 self.owners.push(Self::destroy_raw);
357 self.raw_owners.push(RawOwner {
358 depth: self.depth(),
359 before: len,
360 align,
361 size,
362 ty: None,
363 });
364 aligned_index - len > 0
365 }
366
367 pub unsafe fn copy_from(&self, offset: usize, size: usize, dst: *mut MaybeUninit<u8>) {
380 debug_assert!(offset + size <= self.buffer.len());
381 unsafe {
382 std::ptr::copy_nonoverlapping(self.buffer.as_ptr().add(offset), dst, size);
383 }
384 }
385
386 pub unsafe fn drop_at(&mut self, offset: usize, run_drop: impl FnOnce(*mut u8)) {
394 unsafe { run_drop(self.buffer.as_mut_ptr().add(offset).cast::<u8>()) };
395 }
396
397 pub unsafe fn read_at<R>(&self, offset: usize, read: impl FnOnce(*const u8) -> R) -> R {
407 unsafe { read(self.buffer.as_ptr().add(offset).cast::<u8>()) }
408 }
409
410 pub unsafe fn truncate_to(&mut self, new_len: usize, padding: bool) {
419 debug_assert!(new_len <= self.buffer.len());
420 let padding_count = if padding {
421 self.buffer[..new_len]
422 .iter()
423 .rev()
424 .take_while(|&x| unsafe { x.assume_init() == 0 })
425 .count()
426 + 1
427 } else {
428 0
429 };
430 self.buffer.truncate(new_len - padding_count);
431 }
432
433 pub unsafe fn drop_sized(
440 &mut self,
441 size: usize,
442 padding: bool,
443 run_drop: impl FnOnce(*mut u8),
444 ) {
445 debug_assert!(size <= self.buffer.len());
446 let p = self.buffer.len() - size;
447 let pointer = unsafe { self.buffer.as_mut_ptr().add(p).cast::<u8>() };
448 self.release_top(1);
449 unsafe {
450 self.truncate_to(p, padding);
451 run_drop(pointer);
452 }
453 }
454
455 pub unsafe fn repack(
482 &mut self,
483 ambient_start: usize,
484 dest_base: usize,
485 total_size: usize,
486 src_offsets: &[usize],
487 dest_offsets: &[usize],
488 sizes: &[usize],
489 ) -> bool {
490 debug_assert!(dest_base >= ambient_start);
491 debug_assert_eq!(src_offsets.len(), sizes.len());
492 debug_assert_eq!(dest_offsets.len(), sizes.len());
493
494 let target_len = dest_base + total_size;
495 let current_len = self.buffer.len();
496 let grown_len = current_len.max(target_len);
497 unsafe {
498 if grown_len > current_len {
499 self.buffer.reserve(grown_len - current_len);
500 self.buffer.set_len(grown_len);
501 }
502 let base_ptr = self.buffer.as_mut_ptr();
503 for i in (0..sizes.len()).rev() {
508 std::ptr::copy(
509 base_ptr.add(src_offsets[i]),
510 base_ptr.add(dest_base + dest_offsets[i]),
511 sizes[i],
512 );
513 }
514 if dest_base > ambient_start {
515 self.buffer[ambient_start].write(1);
516 self.buffer[ambient_start + 1..dest_base].fill(MaybeUninit::new(0));
517 }
518 self.buffer.set_len(target_len);
519 }
520 dest_base > ambient_start
521 }
522
523 pub unsafe fn pop<T>(&mut self, padding: bool) -> T {
544 let p: usize = self.buffer.len() - size_of::<T>();
545 let result = unsafe { std::ptr::read(self.buffer.as_ptr().add(p).cast::<T>()) };
546 let padding_count = if padding {
548 self.buffer[..p]
549 .iter()
550 .rev()
551 .take_while(|&x| unsafe { x.assume_init() == 0 })
552 .count()
553 + 1
554 } else {
555 0
556 };
557 self.buffer.truncate(p - padding_count);
558 self.release_top(1);
559 result
560 }
561
562 pub unsafe fn drop<T>(&mut self, padding: bool) {
571 unsafe { self.pop::<T>(padding) };
572 }
573}
574
575#[cfg(test)]
577mod tests {
578 use super::*;
579 use std::cell::RefCell;
580 use std::cmp::max;
581 use std::rc::Rc;
582
583 struct Tracked {
585 id: usize,
586 drops: Rc<RefCell<Vec<usize>>>,
587 panic: bool,
588 }
589
590 impl Drop for Tracked {
591 fn drop(&mut self) {
593 self.drops.borrow_mut().push(self.id);
594 assert!(!self.panic, "destructor panic");
595 }
596 }
597
598 fn track(
600 stack: &mut RawStack,
601 drops: &Rc<RefCell<Vec<usize>>>,
602 id: usize,
603 panic: bool,
604 ) -> bool {
605 stack.push(Tracked {
606 id,
607 drops: drops.clone(),
608 panic,
609 })
610 }
611
612 #[test]
613 fn live_owners_drop_in_reverse_order() {
615 let drops = Rc::new(RefCell::new(Vec::new()));
616 {
617 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
618 track(&mut stack, &drops, 1, false);
619 stack.push(1u8);
620 track(&mut stack, &drops, 2, false);
621 }
622 assert_eq!(*drops.borrow(), [2, 1]);
623 }
624
625 #[test]
626 fn popped_owner_is_not_dropped_twice() {
628 let drops = Rc::new(RefCell::new(Vec::new()));
629 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
630 let padding = track(&mut stack, &drops, 1, false);
631 let value: Tracked = unsafe { stack.pop(padding) };
632 drop(stack);
633 assert!(drops.borrow().is_empty());
634 drop(value);
635 assert_eq!(*drops.borrow(), [1]);
636 }
637
638 #[test]
639 fn drop_top_preserves_prefix_and_padding() {
641 ZST_DROPS.with(|count| count.set(0));
642 let drops = Rc::new(RefCell::new(Vec::new()));
643 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
644 stack.push(42u8);
645 track(&mut stack, &drops, 1, false);
646 stack.push(ZeroSized);
647 let before_zero = stack.len();
648 stack.drop_top();
649 assert_eq!(stack.len(), before_zero);
650 assert_eq!(stack.depth(), 2);
651 ZST_DROPS.with(|count| assert_eq!(count.get(), 1));
652 stack.drop_top();
653 assert_eq!(stack.len(), 1);
654 assert_eq!(stack.depth(), 1);
655 assert_eq!(*drops.borrow(), [1]);
656 let prefix: u8 = unsafe { stack.pop(false) };
657 assert_eq!(prefix, 42);
658 drop(stack);
659 assert_eq!(*drops.borrow(), [1]);
660 }
661
662 #[test]
663 fn drop_top_panic_does_not_repeat_destruction() {
665 let drops = Rc::new(RefCell::new(Vec::new()));
666 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
667 track(&mut stack, &drops, 1, false);
668 let origin = stack.len();
669 track(&mut stack, &drops, 2, true);
670 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
671 stack.drop_top();
672 }));
673 assert!(result.is_err());
674 assert_eq!(stack.len(), origin);
675 assert_eq!(stack.depth(), 1);
676 assert_eq!(*drops.borrow(), [2]);
677 drop(stack);
678 assert_eq!(*drops.borrow(), [2, 1]);
679 }
680
681 thread_local! {
682 static ZST_DROPS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
683 }
684
685 struct ZeroSized;
687
688 impl Drop for ZeroSized {
689 fn drop(&mut self) {
691 ZST_DROPS.with(|count| count.set(count.get() + 1));
692 }
693 }
694
695 #[test]
696 fn scoped_unwind_preserves_zero_sized_prefix() {
698 ZST_DROPS.with(|count| count.set(0));
699 let mut stack = RawStack::with_base_alignment(align_of::<usize>());
700 stack.push(42usize);
701 stack.push(ZeroSized);
702 let origin = stack.len();
703 let depth = stack.depth();
704 let result: anyhow::Result<()> = stack.with_unwind(origin, depth, |stack| {
705 stack.push(ZeroSized);
706 stack.push(ZeroSized);
707 anyhow::bail!("callback failed")
708 });
709 assert!(result.is_err());
710 assert_eq!(stack.len(), origin);
711 assert_eq!(stack.depth(), depth);
712 ZST_DROPS.with(|count| assert_eq!(count.get(), 2));
713 drop(stack);
714 ZST_DROPS.with(|count| assert_eq!(count.get(), 3));
715 }
716
717 #[test]
718 fn callback_panic_cleans_suffix() {
720 let drops = Rc::new(RefCell::new(Vec::new()));
721 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
722 track(&mut stack, &drops, 1, false);
723 let origin = stack.len();
724 let depth = stack.depth();
725 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
726 let _: anyhow::Result<()> = stack.with_unwind(origin, depth, |stack| {
727 track(stack, &drops, 2, false);
728 panic!("callback panic")
729 });
730 }));
731 assert!(result.is_err());
732 assert_eq!(*drops.borrow(), [2]);
733 assert_eq!(stack.depth(), depth);
734 assert_eq!(stack.len(), origin);
735 drop(stack);
736 assert_eq!(*drops.borrow(), [2, 1]);
737 }
738
739 #[test]
740 fn destructor_panic_resumes_cleanup() {
742 let drops = Rc::new(RefCell::new(Vec::new()));
743 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
744 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
745 track(&mut stack, &drops, 1, false);
746 track(&mut stack, &drops, 2, true);
747 track(&mut stack, &drops, 3, false);
748 }));
749 assert!(result.is_err());
750 assert_eq!(*drops.borrow(), [3, 2, 1]);
751 }
752
753 #[test]
754 fn raw_bytes_become_erased_owner() {
756 let drops = Rc::new(RefCell::new(Vec::new()));
757 let value = std::mem::ManuallyDrop::new(Tracked {
758 id: 1,
759 drops: drops.clone(),
760 panic: false,
761 });
762 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
763 unsafe {
764 stack.push_raw(
765 align_of::<Tracked>(),
766 size_of::<Tracked>(),
767 (&*value as *const Tracked).cast(),
768 );
769 }
770 stack.own_top(crate::value_type::ValueType::leaf::<Tracked>());
771 drop(stack);
772 assert_eq!(*drops.borrow(), [1]);
773 }
774
775 #[test]
776 fn drop_top_destroys_erased_owner_and_padding() {
778 let drops = Rc::new(RefCell::new(Vec::new()));
779 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
780 stack.push(42u8);
781 unsafe {
782 stack.reserve_and_write(align_of::<Tracked>(), size_of::<Tracked>(), |pointer| {
783 pointer.cast::<Tracked>().write(Tracked {
784 id: 1,
785 drops: drops.clone(),
786 panic: false,
787 });
788 });
789 }
790 stack.own_top(ValueType::leaf::<Tracked>());
791 stack.drop_top();
792 assert_eq!(stack.len(), 1);
793 assert_eq!(stack.depth(), 1);
794 assert_eq!(*drops.borrow(), [1]);
795 drop(stack);
796 assert_eq!(*drops.borrow(), [1]);
797 }
798
799 #[test]
800 fn successful_scope_keeps_owners() {
802 let drops = Rc::new(RefCell::new(Vec::new()));
803 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
804 let value = stack
805 .with_unwind(0, 0, |stack| {
806 track(stack, &drops, 1, false);
807 Ok(42)
808 })
809 .unwrap();
810 assert_eq!(value, 42);
811 assert_eq!(stack.depth(), 1);
812 assert!(drops.borrow().is_empty());
813 drop(stack);
814 assert_eq!(*drops.borrow(), [1]);
815 }
816
817 #[test]
818 fn typed_pop_transfers_erased_owner() {
820 let drops = Rc::new(RefCell::new(Vec::new()));
821 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
822 let padding = unsafe {
823 stack.reserve_and_write(align_of::<Tracked>(), size_of::<Tracked>(), |pointer| {
824 pointer.cast::<Tracked>().write(Tracked {
825 id: 1,
826 drops: drops.clone(),
827 panic: false,
828 });
829 })
830 };
831 stack.own_top(ValueType::leaf::<Tracked>());
832 let value: Tracked = unsafe { stack.pop(padding) };
833 assert_eq!(stack.depth(), 0);
834 track(&mut stack, &drops, 2, false);
835 drop(stack);
836 assert_eq!(*drops.borrow(), [2]);
837 drop(value);
838 assert_eq!(*drops.borrow(), [2, 1]);
839 }
840
841 #[test]
842 fn initializer_panic_registers_no_owner() {
844 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
845 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
846 stack.reserve_and_write(align_of::<Tracked>(), size_of::<Tracked>(), |_| {
847 panic!("uninitialized")
848 });
849 }));
850 assert!(result.is_err());
851 assert_eq!(stack.depth(), 0);
852 stack.unwind_to(0, 0);
853 assert_eq!(stack.len(), 0);
854 }
855
856 #[test]
857 fn release_top_preserves_bytes_and_releases_zero_sized_owner() {
859 ZST_DROPS.with(|count| count.set(0));
860 let mut stack = RawStack::with_base_alignment(align_of::<usize>());
861 stack.push(42usize);
862 stack.push(ZeroSized);
863 let origin = stack.len();
864 stack.release_top(1);
865 assert_eq!(stack.len(), origin);
866 assert_eq!(stack.depth(), 1);
867 let value: usize = unsafe { stack.pop(false) };
868 assert_eq!(value, 42);
869 drop(stack);
870 ZST_DROPS.with(|count| assert_eq!(count.get(), 0));
871 }
872
873 #[test]
874 fn scoped_destructor_panic_restores_prefix() {
876 let drops = Rc::new(RefCell::new(Vec::new()));
877 let mut stack = RawStack::with_base_alignment(align_of::<Tracked>());
878 track(&mut stack, &drops, 1, false);
879 let origin = stack.len();
880 let depth = stack.depth();
881 track(&mut stack, &drops, 2, false);
882 track(&mut stack, &drops, 3, true);
883 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
884 stack.unwind_to(origin, depth);
885 }));
886 assert!(result.is_err());
887 assert_eq!(*drops.borrow(), [3, 2]);
888 assert_eq!(stack.len(), origin);
889 assert_eq!(stack.depth(), depth);
890 drop(stack);
891 assert_eq!(*drops.borrow(), [3, 2, 1]);
892 }
893
894 #[test]
895 fn push_pop_u32() {
896 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
897 let padding = stack.push(10u32);
898 let result: u32 = unsafe { stack.pop(padding) };
899 assert_eq!(result, 10);
900 }
901
902 #[test]
903 fn multiple_push_pop() {
904 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
905 let padding1 = stack.push(1u32);
906 let padding2 = stack.push(2u32);
907 let padding3 = stack.push(3u32);
908 let v3: u32 = unsafe { stack.pop(padding3) };
909 let v2: u32 = unsafe { stack.pop(padding2) };
910 let v1: u32 = unsafe { stack.pop(padding1) };
911 assert_eq!(v1, 1);
912 assert_eq!(v2, 2);
913 assert_eq!(v3, 3);
914 }
915
916 #[test]
917 fn push_pop_different_types() {
918 let mut stack = RawStack::with_base_alignment(max(align_of::<u32>(), align_of::<f64>()));
919 let padding1 = stack.push(42u32);
920 let padding2 = stack.push(42.14f64);
921 let value_f: f64 = unsafe { stack.pop(padding2) };
922 let value_u: u32 = unsafe { stack.pop(padding1) };
923 assert_eq!(value_f, 42.14);
924 assert_eq!(value_u, 42);
925 }
926
927 #[test]
928 fn len_reflects_pushed_bytes() {
929 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
930 assert_eq!(stack.len(), 0);
931 let _ = stack.push(7u32);
932 assert_eq!(stack.len(), size_of::<u32>());
933 }
934
935 #[test]
936 fn push_raw_round_trips_like_push() {
937 let mut stack = RawStack::with_base_alignment(align_of::<f64>());
938 let padding1 = stack.push(1u8);
939 let value = 42.14f64;
940 let padding2 = unsafe {
941 stack.push_raw(
942 align_of::<f64>(),
943 size_of::<f64>(),
944 (&value as *const f64).cast::<MaybeUninit<u8>>(),
945 )
946 };
947 let popped: f64 = unsafe { stack.pop(padding2) };
948 assert_eq!(popped, 42.14);
949 let popped_u8: u8 = unsafe { stack.pop(padding1) };
950 assert_eq!(popped_u8, 1);
951 }
952
953 #[test]
954 fn push_raw_padding_matches_typed_push() {
955 let mut stack_a = RawStack::with_base_alignment(align_of::<f64>());
956 let _ = stack_a.push(1u8);
957 let padding_typed = stack_a.push(2.5f64);
958
959 let mut stack_b = RawStack::with_base_alignment(align_of::<f64>());
960 let _ = stack_b.push(1u8);
961 let value = 2.5f64;
962 let padding_raw = unsafe {
963 stack_b.push_raw(
964 align_of::<f64>(),
965 size_of::<f64>(),
966 (&value as *const f64).cast::<MaybeUninit<u8>>(),
967 )
968 };
969 assert_eq!(padding_typed, padding_raw);
970 }
971
972 #[test]
973 fn reserve_and_write_matches_push_raw_padding_and_bytes() {
974 let mut stack_a = RawStack::with_base_alignment(align_of::<f64>());
975 let _ = stack_a.push(1u8);
976 let value = 2.5f64;
977 let padding_a = unsafe {
978 stack_a.push_raw(
979 align_of::<f64>(),
980 size_of::<f64>(),
981 (&value as *const f64).cast::<MaybeUninit<u8>>(),
982 )
983 };
984
985 let mut stack_b = RawStack::with_base_alignment(align_of::<f64>());
986 let _ = stack_b.push(1u8);
987 let padding_b = unsafe {
988 stack_b.reserve_and_write(align_of::<f64>(), size_of::<f64>(), |dst| {
989 std::ptr::write(dst.cast::<f64>(), 2.5f64);
990 })
991 };
992
993 assert_eq!(padding_a, padding_b);
994 let popped_a: f64 = unsafe { stack_a.pop(padding_a) };
995 let popped_b: f64 = unsafe { stack_b.pop(padding_b) };
996 assert_eq!(popped_a, popped_b);
997 }
998
999 #[test]
1000 fn reserve_and_write_runs_write_exactly_once_with_correct_size() {
1001 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
1002 let mut call_count = 0;
1003 let padding = unsafe {
1004 stack.reserve_and_write(align_of::<u32>(), size_of::<u32>(), |dst| {
1005 call_count += 1;
1006 std::ptr::write(dst.cast::<u32>(), 42);
1007 })
1008 };
1009 assert_eq!(call_count, 1);
1010 let result: u32 = unsafe { stack.pop(padding) };
1011 assert_eq!(result, 42);
1012 }
1013
1014 #[test]
1015 fn copy_from_reads_bytes_at_offset() {
1016 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
1017 let _ = stack.push(10u32);
1018 let _ = stack.push(20u32);
1019 let mut buf = [0u8; 4];
1020 unsafe { stack.copy_from(0, 4, buf.as_mut_ptr().cast::<MaybeUninit<u8>>()) };
1021 assert_eq!(u32::from_ne_bytes(buf), 10);
1022 }
1023
1024 #[test]
1025 fn read_at_gives_a_pointer_to_the_value_without_copying() {
1026 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
1027 let _ = stack.push(10u32);
1028 let _ = stack.push(20u32);
1029 let first: u32 = unsafe { stack.read_at(0, |ptr| *ptr.cast::<u32>()) };
1030 let second: u32 = unsafe { stack.read_at(4, |ptr| *ptr.cast::<u32>()) };
1031 assert_eq!(first, 10);
1032 assert_eq!(second, 20);
1033 }
1034
1035 #[test]
1036 fn drop_at_runs_destructor_without_changing_length() {
1037 use std::sync::Arc;
1038 use std::sync::atomic::{AtomicUsize, Ordering};
1039
1040 struct DropCounter(Arc<AtomicUsize>);
1041 impl Drop for DropCounter {
1042 fn drop(&mut self) {
1043 self.0.fetch_add(1, Ordering::SeqCst);
1044 }
1045 }
1046
1047 let count = Arc::new(AtomicUsize::new(0));
1048 let mut stack = RawStack::with_base_alignment(align_of::<DropCounter>());
1049 let _ = stack.push(DropCounter(count.clone()));
1050 let len_before = stack.len();
1051 stack.release_top(1);
1052 unsafe {
1053 stack.drop_at(0, |ptr| std::ptr::drop_in_place(ptr.cast::<DropCounter>()));
1054 }
1055 assert_eq!(count.load(Ordering::SeqCst), 1);
1056 assert_eq!(stack.len(), len_before);
1057 }
1058
1059 #[test]
1060 fn truncate_to_strips_recorded_padding() {
1061 let mut stack = RawStack::with_base_alignment(align_of::<f64>());
1062 let _ = stack.push(1u8);
1063 let padding = stack.push(2.5f64); let len_with_value = stack.len();
1065 stack.release_top(1);
1066 unsafe { stack.truncate_to(len_with_value - size_of::<f64>(), padding) };
1067 assert_eq!(stack.len(), 1); }
1069
1070 #[test]
1071 fn drop_sized_combines_drop_at_and_truncate_to() {
1072 use std::sync::Arc;
1073 use std::sync::atomic::{AtomicUsize, Ordering};
1074
1075 struct DropCounter(Arc<AtomicUsize>);
1076 impl Drop for DropCounter {
1077 fn drop(&mut self) {
1078 self.0.fetch_add(1, Ordering::SeqCst);
1079 }
1080 }
1081
1082 let count = Arc::new(AtomicUsize::new(0));
1083 let mut stack = RawStack::with_base_alignment(align_of::<DropCounter>());
1084 let _ = stack.push(1u8);
1085 let padding = stack.push(DropCounter(count.clone()));
1086 unsafe {
1087 stack.drop_sized(size_of::<DropCounter>(), padding, |ptr| {
1088 std::ptr::drop_in_place(ptr.cast::<DropCounter>())
1089 });
1090 }
1091 assert_eq!(count.load(Ordering::SeqCst), 1);
1092 assert_eq!(stack.len(), 1);
1093 }
1094
1095 #[test]
1096 fn repack_moves_elements_to_ideal_offsets_and_reports_padding() {
1097 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
1100 let ambient_start = stack.len(); let _ = stack.push(0xAAu8); let _p1 = stack.push(0xBBBB_BBBBu32); let _p2 = stack.push(0xCCu8); let src_offsets = [0usize, 4, 8];
1109 let dest_offsets = [0usize, 4, 8];
1110 let sizes = [1usize, 4, 1];
1111 let total_size = 12usize;
1112 let dest_base = 0usize; stack.release_top(3);
1115 let padding = unsafe {
1116 stack.repack(
1117 ambient_start,
1118 dest_base,
1119 total_size,
1120 &src_offsets,
1121 &dest_offsets,
1122 &sizes,
1123 )
1124 };
1125 assert!(
1126 !padding,
1127 "ambient_start was already aligned; no leading pad expected"
1128 );
1129 assert_eq!(stack.len(), dest_base + total_size);
1130
1131 let mut a = [0u8; 1];
1132 let mut b = [0u8; 4];
1133 let mut c = [0u8; 1];
1134 unsafe {
1135 stack.copy_from(dest_base, 1, a.as_mut_ptr().cast::<MaybeUninit<u8>>());
1136 stack.copy_from(dest_base + 4, 4, b.as_mut_ptr().cast::<MaybeUninit<u8>>());
1137 stack.copy_from(dest_base + 8, 1, c.as_mut_ptr().cast::<MaybeUninit<u8>>());
1138 }
1139 assert_eq!(a[0], 0xAA);
1140 assert_eq!(u32::from_ne_bytes(b), 0xBBBB_BBBB);
1141 assert_eq!(c[0], 0xCC);
1142 }
1143
1144 #[test]
1145 fn repack_shifts_right_without_corrupting_unread_source_bytes() {
1146 let mut stack = RawStack::with_base_alignment(align_of::<u32>());
1153 let _ = stack.push(0xFFu8); let ambient_start = stack.len(); let _ = stack.push(0xDDu8); let _ = stack.push(0x1122_3344u32); let src_offsets = [1usize, 4];
1160 let dest_offsets = [0usize, 4];
1161 let sizes = [1usize, 4];
1162 let total_size = 8usize;
1163 let dest_base = 4usize; stack.release_top(2);
1166 let padding = unsafe {
1167 stack.repack(
1168 ambient_start,
1169 dest_base,
1170 total_size,
1171 &src_offsets,
1172 &dest_offsets,
1173 &sizes,
1174 )
1175 };
1176 assert!(
1177 padding,
1178 "ambient_start (1) is not 4-aligned; a leading pad is expected"
1179 );
1180 assert_eq!(stack.len(), dest_base + total_size);
1181
1182 let mut sentinel = [0u8; 1];
1183 let mut a = [0u8; 1];
1184 let mut b = [0u8; 4];
1185 unsafe {
1186 stack.copy_from(0, 1, sentinel.as_mut_ptr().cast::<MaybeUninit<u8>>());
1187 stack.copy_from(dest_base, 1, a.as_mut_ptr().cast::<MaybeUninit<u8>>());
1188 stack.copy_from(dest_base + 4, 4, b.as_mut_ptr().cast::<MaybeUninit<u8>>());
1189 }
1190 assert_eq!(
1191 sentinel[0], 0xFF,
1192 "bytes before the tuple must be untouched"
1193 );
1194 assert_eq!(a[0], 0xDD);
1195 assert_eq!(
1196 u32::from_ne_bytes(b),
1197 0x1122_3344,
1198 "the u32's bytes must survive the repack uncorrupted"
1199 );
1200 }
1201}