From 14341435cbd398b4742fc80decb4549584f5661a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:45:27 +0000 Subject: [PATCH 1/5] Initial plan From 2d1b0a4173887c40da2d53d275ae478662953b6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:54:23 +0000 Subject: [PATCH 2/5] Fix allocation layout padding Co-authored-by: SOF3 <19623715+SOF3@users.noreply.github.com> --- src/internal.rs | 17 ++++++++--------- src/tests.rs | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index d65bbdf..1834f10 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -74,14 +74,11 @@ pub(crate) struct Large(pub(crate) NonNull>); impl Large { pub(crate) fn new_layout(cap: usize) -> Layout { - let additional_size = size_of::().checked_mul(cap).expect("new capacity is too large"); - let size = size_of::>() - .checked_add(additional_size) - .expect("new capacity is too large"); - let align = align_of::>(); - // SAFETY: size of Allocated must be a multiple of align of Allocated, - // which must be a multiple of align of T due to the `data` field. - unsafe { Layout::from_size_align_unchecked(size, align) } + let header = Layout::new::>(); + let data = Layout::array::(cap).expect("new capacity is too large"); + let (layout, offset) = header.extend(data).expect("new capacity is too large"); + debug_assert_eq!(offset, size_of::>()); + layout.pad_to_align() } pub(crate) fn as_allocated(&self) -> (&Allocated, *const T) { @@ -262,6 +259,8 @@ impl Allocated { /// /// The data behind the header are allowed to be uninitialized. pub(crate) unsafe fn data_start(this: NonNull) -> *mut T { - unsafe { (&raw mut (*this.as_ptr()).data_start).cast() } + let base = this.as_ptr().cast::(); + // SAFETY: base points to a valid allocation whose data starts immediately after the header. + unsafe { base.add(size_of::()).cast::() } } } diff --git a/src/tests.rs b/src/tests.rs index fc5ba16..0d58c6c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,6 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; +use alloc::alloc::dealloc; use core::cell::Cell; use core::panic::AssertUnwindSafe; use core::{mem, ops}; @@ -14,6 +15,26 @@ fn assert_size() { assert_eq!(size_of::>(), 8); } +#[test] +fn test_large_layout_padding_and_data_start() { + use crate::internal::{Allocated, Large}; + + let layout = Large::::new_layout(1); + assert_eq!(layout.size() % layout.align(), 0); + + let large = Large::::new_empty(1); + let (allocated, data_start) = large.as_allocated(); + let base = allocated as *const Allocated as *const u8; + let offset = unsafe { data_start.offset_from(base) as usize }; + assert_eq!(offset, size_of::>()); + + let layout = large.current_layout(); + let ptr = large.0; + unsafe { + dealloc(ptr.as_ptr().cast(), layout); + } +} + struct AssertDrop<'a> { string: String, counter: &'a Cell, From 9cd64cbc52442427fde52ec0f8d2a899cb88cce3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:56:00 +0000 Subject: [PATCH 3/5] Expand layout test coverage Co-authored-by: SOF3 <19623715+SOF3@users.noreply.github.com> --- src/internal.rs | 1 + src/tests.rs | 37 ++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 1834f10..f2e1571 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -77,6 +77,7 @@ impl Large { let header = Layout::new::>(); let data = Layout::array::(cap).expect("new capacity is too large"); let (layout, offset) = header.extend(data).expect("new capacity is too large"); + // `Allocated` already aligns the trailing data via its zero-length `data_start` field. debug_assert_eq!(offset, size_of::>()); layout.pad_to_align() } diff --git a/src/tests.rs b/src/tests.rs index 0d58c6c..84311d2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -19,20 +19,31 @@ fn assert_size() { fn test_large_layout_padding_and_data_start() { use crate::internal::{Allocated, Large}; - let layout = Large::::new_layout(1); - assert_eq!(layout.size() % layout.align(), 0); - - let large = Large::::new_empty(1); - let (allocated, data_start) = large.as_allocated(); - let base = allocated as *const Allocated as *const u8; - let offset = unsafe { data_start.offset_from(base) as usize }; - assert_eq!(offset, size_of::>()); - - let layout = large.current_layout(); - let ptr = large.0; - unsafe { - dealloc(ptr.as_ptr().cast(), layout); + #[allow(dead_code)] + #[repr(align(32))] + struct Align32(u8); + + fn assert_layout() { + let layout = Large::::new_layout(1); + assert_eq!(layout.size() % layout.align(), 0); + + let large = Large::::new_empty(1); + let (allocated, data_start) = large.as_allocated(); + let base = allocated as *const Allocated as *const u8; + let offset = unsafe { data_start.cast::().offset_from(base) as usize }; + assert_eq!(offset, size_of::>()); + + let layout = large.current_layout(); + let ptr = large.0; + unsafe { + dealloc(ptr.as_ptr().cast(), layout); + } } + + assert_layout::(); + assert_layout::(); + assert_layout::(); + assert_layout::(); } struct AssertDrop<'a> { From cfe685115365a20ae0d3f50a53af02b24ccc517a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:56:40 +0000 Subject: [PATCH 4/5] Clarify layout invariants Co-authored-by: SOF3 <19623715+SOF3@users.noreply.github.com> --- src/internal.rs | 1 + src/tests.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/internal.rs b/src/internal.rs index f2e1571..283f50f 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -262,6 +262,7 @@ impl Allocated { pub(crate) unsafe fn data_start(this: NonNull) -> *mut T { let base = this.as_ptr().cast::(); // SAFETY: base points to a valid allocation whose data starts immediately after the header. + // `new_layout` uses the same header size (see the debug assertion there). unsafe { base.add(size_of::()).cast::() } } } diff --git a/src/tests.rs b/src/tests.rs index 84311d2..af765d8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -34,6 +34,7 @@ fn test_large_layout_padding_and_data_start() { assert_eq!(offset, size_of::>()); let layout = large.current_layout(); + assert_eq!(layout, Large::::new_layout(1)); let ptr = large.0; unsafe { dealloc(ptr.as_ptr().cast(), layout); From 3ff61b53a2e8c345d6bc20bd808e2ece50d3d03d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:58:04 +0000 Subject: [PATCH 5/5] Guard layout test deallocation Co-authored-by: SOF3 <19623715+SOF3@users.noreply.github.com> --- src/internal.rs | 3 ++- src/tests.rs | 25 ++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 283f50f..2fad5e2 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -77,7 +77,8 @@ impl Large { let header = Layout::new::>(); let data = Layout::array::(cap).expect("new capacity is too large"); let (layout, offset) = header.extend(data).expect("new capacity is too large"); - // `Allocated` already aligns the trailing data via its zero-length `data_start` field. + // `Layout::new::>()` already includes padding for `T`, + // so the trailing data starts immediately after the header. debug_assert_eq!(offset, size_of::>()); layout.pad_to_align() } diff --git a/src/tests.rs b/src/tests.rs index af765d8..a0a64d9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -24,21 +24,32 @@ fn test_large_layout_padding_and_data_start() { struct Align32(u8); fn assert_layout() { + use core::alloc::Layout; + use core::ptr::NonNull; + let layout = Large::::new_layout(1); assert_eq!(layout.size() % layout.align(), 0); let large = Large::::new_empty(1); + let layout = large.current_layout(); + assert_eq!(layout, Large::::new_layout(1)); + struct DeallocGuard { + ptr: NonNull>, + layout: Layout, + } + impl Drop for DeallocGuard { + fn drop(&mut self) { + unsafe { + dealloc(self.ptr.as_ptr().cast(), self.layout); + } + } + } + let _guard = DeallocGuard { ptr: large.0, layout }; + let (allocated, data_start) = large.as_allocated(); let base = allocated as *const Allocated as *const u8; let offset = unsafe { data_start.cast::().offset_from(base) as usize }; assert_eq!(offset, size_of::>()); - - let layout = large.current_layout(); - assert_eq!(layout, Large::::new_layout(1)); - let ptr = large.0; - unsafe { - dealloc(ptr.as_ptr().cast(), layout); - } } assert_layout::();