Skip to content

Commit bb69c9d

Browse files
committed
Refactor underlying data of map_windows::Buffer into RawBuffer
Refactor the liveness and access invariants of Buffer into RawBuffer. This lets us initialize a Buffer without dropping it if it panics transiently. Fixes #156501.
1 parent 2aabf3c commit bb69c9d

1 file changed

Lines changed: 105 additions & 51 deletions

File tree

library/core/src/iter/adapters/map_windows.rs

Lines changed: 105 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,27 @@ struct MapWindowsInner<I: Iterator, const N: usize> {
3131
}
3232

3333
// `Buffer` uses two times of space to reduce moves among the iterations.
34+
struct Buffer<T, const N: usize> {
35+
// Invariant: N elements starting from `self.start` must be initialized at
36+
// with all other elements left uninitialized.
37+
buffer: RawBuffer<T, N>,
38+
// Invariant: `self.start <= N`
39+
start: usize,
40+
}
41+
42+
/// Internal storage for `Buffer<T, N>`.
43+
///
44+
/// Has no storage invariants, but has access invariants.
45+
/// See the unsafe method contracts for more information.
46+
///
47+
/// This type does not implement Drop, it will leak any data stored within.
48+
//
3449
// `Buffer<T, N>` is semantically `[MaybeUninit<T>; 2 * N]`. However, due
3550
// to limitations of const generics, we use this different type. Note that
3651
// it has the same underlying memory layout.
37-
struct Buffer<T, const N: usize> {
38-
// Invariant: `self.buffer[self.start..self.start + N]` is initialized,
39-
// with all other elements being uninitialized. This also
40-
// implies that `self.start <= N`.
41-
buffer: [[MaybeUninit<T>; N]; 2],
42-
start: usize,
52+
#[repr(transparent)]
53+
struct RawBuffer<T, const N: usize> {
54+
data: [[MaybeUninit<T>; N]; 2],
4355
}
4456

4557
impl<I: Iterator, F, const N: usize> MapWindows<I, F, N> {
@@ -79,7 +91,16 @@ impl<I: Iterator, const N: usize> MapWindowsInner<I, N> {
7991
Some(item) => buffer.push(item),
8092
},
8193
}
82-
self.buffer.as_ref().map(Buffer::as_array_ref)
94+
self.buffer.as_ref().map(|buf|
95+
// SAFETY:
96+
// - if this was the first time to advance, this is a new well-formed `Buffer`.
97+
//
98+
// - if we already had a buffer, and `iter.next()` was Some;
99+
// `Buffer::push` is responsible for upholding the invariant before we reach this.
100+
//
101+
// - if we already had a buffer, and `iter.next()` was None;
102+
// this closure is unreachable, as the buffer was taken beforehand.
103+
unsafe { buf.buffer.as_array_ref(buf.start) })
83104
}
84105

85106
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -98,45 +119,32 @@ impl<I: Iterator, const N: usize> MapWindowsInner<I, N> {
98119
}
99120

100121
impl<T, const N: usize> Buffer<T, N> {
101-
fn try_from_iter(iter: &mut impl Iterator<Item = T>) -> Option<Self> {
102-
let first_half = crate::array::iter_next_chunk(iter).ok()?;
103-
let buffer =
104-
[MaybeUninit::new(first_half).transpose(), [const { MaybeUninit::uninit() }; N]];
105-
Some(Self { buffer, start: 0 })
106-
}
107-
108-
#[inline]
109-
fn buffer_ptr(&self) -> *const MaybeUninit<T> {
110-
self.buffer.as_ptr().cast()
111-
}
112-
113-
#[inline]
114-
fn buffer_mut_ptr(&mut self) -> *mut MaybeUninit<T> {
115-
self.buffer.as_mut_ptr().cast()
116-
}
117-
122+
/// # Safety
123+
///
124+
/// This type implements `Drop`, and it has invariants that must be upheld:
125+
/// - `start` must be within the bounds `0..=N`.
126+
/// - `raw[start..start + N]` must be initialized.
118127
#[inline]
119-
fn as_array_ref(&self) -> &[T; N] {
120-
debug_assert!(self.start + N <= 2 * N);
121-
122-
// SAFETY: our invariant guarantees these elements are initialized.
123-
unsafe { &*self.buffer_ptr().add(self.start).cast() }
128+
unsafe fn new(raw: RawBuffer<T, N>, start: usize) -> Self {
129+
Self { buffer: raw, start }
124130
}
125131

126-
#[inline]
127-
fn as_uninit_array_mut(&mut self) -> &mut MaybeUninit<[T; N]> {
128-
debug_assert!(self.start + N <= 2 * N);
129-
130-
// SAFETY: our invariant guarantees these elements are in bounds.
131-
unsafe { &mut *self.buffer_mut_ptr().add(self.start).cast() }
132+
fn try_from_iter(iter: &mut impl Iterator<Item = T>) -> Option<Self> {
133+
let first_half: [T; N] = crate::array::iter_next_chunk(iter).ok()?;
134+
let raw = RawBuffer {
135+
data: [MaybeUninit::new(first_half).transpose(), [const { MaybeUninit::uninit() }; N]],
136+
};
137+
// SAFETY: buffer has the first `first_half.len()` items initialized, which upholds the
138+
// internal invariant for the start offset 0, which is also within bounds.
139+
Some(unsafe { Self::new(raw, 0) })
132140
}
133141

134142
/// Pushes a new item `next` to the back, and pops the front-most one.
135143
///
136144
/// All the elements will be shifted to the front end when pushing reaches
137145
/// the back end.
138146
fn push(&mut self, next: T) {
139-
let buffer_mut_ptr = self.buffer_mut_ptr();
147+
let buffer_mut_ptr = self.buffer.as_mut_ptr();
140148
debug_assert!(self.start + N <= 2 * N);
141149

142150
let to_drop = if self.start == N {
@@ -188,18 +196,67 @@ impl<T, const N: usize> Buffer<T, N> {
188196

189197
// SAFETY: the index is valid and this is element `a` in the
190198
// diagram above and has not been dropped yet.
191-
unsafe { ptr::drop_in_place(to_drop.cast_init()) };
199+
unsafe { to_drop.cast_init().drop_in_place() };
200+
}
201+
}
202+
203+
impl<T, const N: usize> RawBuffer<T, N> {
204+
fn as_ptr(&self) -> *const MaybeUninit<T> {
205+
self.data.as_ptr().cast()
206+
}
207+
208+
fn as_mut_ptr(&mut self) -> *mut MaybeUninit<T> {
209+
self.data.as_mut_ptr().cast()
210+
}
211+
212+
/// # Safety
213+
///
214+
/// `self.data` must uphold the internal invariants of `Buffer`:
215+
/// - `start` must be within the bounds `0..=N`
216+
/// - `self.data[start..start + N]` must be initialized.
217+
unsafe fn as_array_ref(&self, start: usize) -> &[T; N] {
218+
debug_assert!(start + N <= 2 * N);
219+
220+
// SAFETY: caller satisfies that `start` is within bounds.
221+
unsafe { &*self.as_ptr().add(start).cast() }
222+
}
223+
224+
/// # Safety
225+
///
226+
/// `self.data` must uphold the internal invariants of `Buffer`:
227+
/// - `start` must be within the bounds `0..=N`
228+
/// - `self.data[start..start + N]` must not overlap with the initialized part.
229+
#[inline]
230+
unsafe fn as_uninit_array_mut(&mut self, start: usize) -> &mut MaybeUninit<[T; N]> {
231+
debug_assert!(start + N <= 2 * N);
232+
233+
// SAFETY: caller satisfies that `start` is within bounds.
234+
unsafe { &mut *self.as_mut_ptr().add(start).cast() }
192235
}
193236
}
194237

195238
impl<T: Clone, const N: usize> Clone for Buffer<T, N> {
196239
fn clone(&self) -> Self {
197-
let mut buffer = Buffer {
198-
buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]],
199-
start: self.start,
240+
let mut new_raw = RawBuffer {
241+
data: [
242+
[const { MaybeUninit::<T>::uninit() }; N],
243+
[const { MaybeUninit::<T>::uninit() }; N],
244+
],
200245
};
201-
buffer.as_uninit_array_mut().write(self.as_array_ref().clone());
202-
buffer
246+
247+
// SAFETY: invariants of a well-formed `Buffer` guarantee N elements
248+
// starting at `start` are initialized.
249+
let init = unsafe { self.buffer.as_array_ref(self.start) };
250+
let cloned = init.clone();
251+
252+
// SAFETY: new_raw is currently fully uninitialized, which does not
253+
// overlap with any initialized part.
254+
unsafe { new_raw.as_uninit_array_mut(self.start).write(cloned) };
255+
256+
// SAFETY: new_raw has just been initialized above at the offset `self.start`,
257+
// and the invariants of a well-formed `Buffer` guarantee `self.start` is within
258+
// the bounds `0..=N`.
259+
unsafe { Self::new(new_raw, self.start) }
203260
}
204261
}
205262

@@ -215,15 +272,12 @@ where
215272

216273
impl<T, const N: usize> Drop for Buffer<T, N> {
217274
fn drop(&mut self) {
218-
// SAFETY: our invariant guarantees that N elements starting from
219-
// `self.start` are initialized. We drop them here.
220-
unsafe {
221-
let initialized_part: *mut [T] = crate::ptr::slice_from_raw_parts_mut(
222-
self.buffer_mut_ptr().add(self.start).cast(),
223-
N,
224-
);
225-
ptr::drop_in_place(initialized_part);
226-
}
275+
// SAFETY: our invariant guarantees that `self.start` is within bounds
276+
let init_ptr = unsafe { self.buffer.as_mut_ptr().add(self.start).cast_init() };
277+
278+
// SAFETY: our invariant guarnatees N elements starting from
279+
// `init_ptr` are initialized. We drop them here.
280+
unsafe { init_ptr.cast_slice(N).drop_in_place() };
227281
}
228282
}
229283

0 commit comments

Comments
 (0)