From bcd8a0e2eb0cf785d2136df41ad67e3588ddea19 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 10 Jul 2026 15:51:25 -0400 Subject: [PATCH 01/11] introduce fallible alternatives for MutableBuffer --- arrow-buffer/src/buffer/mutable.rs | 200 +++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index b6e6a70c6cba..40a3f372427b 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -33,6 +33,29 @@ use std::sync::Mutex; use super::Buffer; +/// Error returned by fallible [`MutableBuffer`] operations. +/// +/// The infallible counterparts panic on these conditions; the `try_*` methods +/// return this error instead. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MutableBufferError { + /// Arithmetic overflow when computing the required buffer length or capacity. + LengthOverflow, + /// The requested capacity cannot be represented as a valid allocation layout. + LayoutError, +} + +impl std::fmt::Display for MutableBufferError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::LengthOverflow => write!(f, "buffer length overflow"), + Self::LayoutError => write!(f, "invalid allocation layout for requested capacity"), + } + } +} + +impl std::error::Error for MutableBufferError {} + /// A [`MutableBuffer`] is a wrapper over memory regions, used to build /// [`Buffer`]s out of items or slices of items. /// @@ -259,6 +282,27 @@ impl MutableBuffer { } } + /// Fallible version of [`MutableBuffer::reserve`]. + /// + /// Returns [`MutableBufferError::LengthOverflow`] if `self.len + additional` + /// overflows `usize`, or [`MutableBufferError::LayoutError`] if the required + /// capacity cannot be rounded to a 64-byte boundary and form a valid layout. + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), MutableBufferError> { + let required_cap = self + .len + .checked_add(additional) + .ok_or(MutableBufferError::LengthOverflow)?; + if required_cap > self.layout.size() { + let new_capacity = required_cap + .checked_next_multiple_of(64) + .ok_or(MutableBufferError::LayoutError)?; + // Mirror the doubling heuristic of the infallible `reserve`. + let new_capacity = std::cmp::max(new_capacity, self.layout.size().saturating_mul(2)); + self.try_reallocate(new_capacity)?; + } + Ok(()) + } /// Ensures that this buffer has at least `self.len + additional` bytes. This re-allocates iff /// `self.len + additional > capacity`. /// # Example @@ -290,6 +334,48 @@ impl MutableBuffer { } } + /// Fallible version of [`MutableBuffer::repeat_slice_n_times`]. + /// + /// Returns [`MutableBufferError::LengthOverflow`] if computing the total byte + /// count overflows `usize`, or a layout error for the same reasons as + /// [`MutableBuffer::try_reserve`]. + pub fn try_repeat_slice_n_times( + &mut self, + slice_to_repeat: &[T], + repeat_count: usize, + ) -> Result<(), MutableBufferError> { + if repeat_count == 0 || slice_to_repeat.is_empty() { + return Ok(()); + } + + let bytes_per_copy = size_of_val(slice_to_repeat); + let total_bytes = repeat_count + .checked_mul(bytes_per_copy) + .ok_or(MutableBufferError::LengthOverflow)?; + // Verify the final length won't overflow before touching any state. + self.len + .checked_add(total_bytes) + .ok_or(MutableBufferError::LengthOverflow)?; + + self.try_reserve(total_bytes)?; + + let length_before = self.len; + self.try_extend_from_slice(slice_to_repeat)?; + + let mut already_repeated = 1usize; + while already_repeated < repeat_count { + let to_copy = already_repeated.min(repeat_count - already_repeated); + let byte_count = to_copy * bytes_per_copy; + unsafe { + let src = self.data.as_ptr().add(length_before) as *const u8; + let dst = self.data.as_ptr().add(self.len); + std::ptr::copy_nonoverlapping(src, dst, byte_count); + } + self.len += byte_count; + already_repeated += to_copy; + } + Ok(()) + } /// Adding to this mutable buffer `slice_to_repeat` repeated `repeat_count` times. /// /// # Example @@ -371,6 +457,39 @@ impl MutableBuffer { } } + /// mirrors [`MutableBuffer::reallocate`] but returns + /// [`MutableBufferError::LayoutError`] instead of panicking on an invalid layout. + #[cold] + #[allow(dead_code)] + fn try_reallocate(&mut self, capacity: usize) -> Result<(), MutableBufferError> { + let new_layout = Layout::from_size_align(capacity, self.layout.align()) + .map_err(|_| MutableBufferError::LayoutError)?; + + if new_layout.size() == 0 { + if self.layout.size() != 0 { + // Safety: data was allocated with the current layout. + unsafe { std::alloc::dealloc(self.as_mut_ptr(), self.layout) }; + self.layout = new_layout; + } + return Ok(()); + } + + let data = match self.layout.size() { + // Safety: new_layout is non-zero. + 0 => unsafe { std::alloc::alloc(new_layout) }, + // Safety: new_layout is valid and non-zero. + _ => unsafe { std::alloc::realloc(self.as_mut_ptr(), self.layout, capacity) }, + }; + self.data = NonNull::new(data).unwrap_or_else(|| handle_alloc_error(new_layout)); + self.layout = new_layout; + #[cfg(feature = "pool")] + { + if let Some(reservation) = self.reservation.lock().unwrap().as_mut() { + reservation.resize(self.layout.size()); + } + } + Ok(()) + } #[cold] fn reallocate(&mut self, capacity: usize) { let new_layout = Layout::from_size_align(capacity, self.layout.align()).unwrap(); @@ -416,6 +535,26 @@ impl MutableBuffer { } } + /// Fallible version of [`MutableBuffer::resize`]. + /// + /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. + #[inline] + pub fn try_resize(&mut self, new_len: usize, value: u8) -> Result<(), MutableBufferError> { + if new_len > self.len { + let diff = new_len - self.len; + self.try_reserve(diff)?; + // Safety: try_reserve ensured capacity >= new_len. + unsafe { self.data.as_ptr().add(self.len).write_bytes(value, diff) }; + } + self.len = new_len; + #[cfg(feature = "pool")] + { + if let Some(reservation) = self.reservation.lock().unwrap().as_mut() { + reservation.resize(self.len); + } + } + Ok(()) + } /// Resizes the buffer, either truncating its contents (with no change in capacity), or /// growing it (potentially reallocating it) and writing `value` in the newly available bytes. /// # Example @@ -450,6 +589,21 @@ impl MutableBuffer { } } + /// Fallible version of [`MutableBuffer::shrink_to_fit`]. + /// + /// Returns [`MutableBufferError::LayoutError`] if rounding `self.len` up to the + /// nearest 64-byte boundary overflows (practically impossible, but checked for + /// completeness). + pub fn try_shrink_to_fit(&mut self) -> Result<(), MutableBufferError> { + let new_capacity = self + .len + .checked_next_multiple_of(64) + .ok_or(MutableBufferError::LayoutError)?; + if new_capacity < self.layout.size() { + self.try_reallocate(new_capacity)?; + } + Ok(()) + } /// Shrinks the capacity of the buffer as much as possible. /// The new capacity will aligned to the nearest 64 bit alignment. /// @@ -575,6 +729,24 @@ impl MutableBuffer { offsets } + /// Fallible version of [`MutableBuffer::extend_from_slice`]. + /// + /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. + #[inline] + pub fn try_extend_from_slice( + &mut self, + items: &[T], + ) -> Result<(), MutableBufferError> { + let additional = mem::size_of_val(items); + self.try_reserve(additional)?; + unsafe { + let src = items.as_ptr() as *const u8; + let dst = self.data.as_ptr().add(self.len); + std::ptr::copy_nonoverlapping(src, dst, additional); + } + self.len += additional; + Ok(()) + } /// Extends this buffer from a slice of items that can be represented in bytes, increasing its capacity if needed. /// # Example /// ``` @@ -603,6 +775,21 @@ impl MutableBuffer { self.len += additional; } + /// Fallible version of [`MutableBuffer::push`]. + /// + /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. + #[inline] + pub fn try_push(&mut self, item: T) -> Result<(), MutableBufferError> { + let additional = std::mem::size_of::(); + self.try_reserve(additional)?; + unsafe { + let src = item.to_byte_slice().as_ptr(); + let dst = self.data.as_ptr().add(self.len); + std::ptr::copy_nonoverlapping(src, dst, additional); + } + self.len += additional; + Ok(()) + } /// Extends the buffer with a new item, increasing its capacity if needed. /// # Example /// ``` @@ -640,6 +827,19 @@ impl MutableBuffer { self.len += additional; } + /// Fallible version of [`MutableBuffer::extend_zeros`] + /// + /// Returns [`MutableBufferError::LengthOverflow`] if `self.len + additional` + /// overflows `usize`, or a layout error for the same reasons as + /// [`MutableBuffer::try_reserve`]. + #[inline] + pub fn try_extend_zeros(&mut self, additional: usize) -> Result<(), MutableBufferError> { + let new_len = self + .len + .checked_add(additional) + .ok_or(MutableBufferError::LengthOverflow)?; + self.try_resize(new_len, 0) + } /// Extends the buffer by `additional` bytes equal to `0u8`, incrementing its capacity if needed. /// /// # Panics From 7d4308295be893f2037dc0cafa42c56749aff85b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 10 Jul 2026 16:23:09 -0400 Subject: [PATCH 02/11] add test --- arrow-buffer/src/buffer/mutable.rs | 103 ++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 40a3f372427b..64a038ccb5c1 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -34,9 +34,6 @@ use std::sync::Mutex; use super::Buffer; /// Error returned by fallible [`MutableBuffer`] operations. -/// -/// The infallible counterparts panic on these conditions; the `try_*` methods -/// return this error instead. #[derive(Debug, Clone, PartialEq, Eq)] pub enum MutableBufferError { /// Arithmetic overflow when computing the required buffer length or capacity. @@ -1885,4 +1882,104 @@ mod tests { buf.push(1u8); buf.reserve(usize::MAX); } + + #[test] + fn test_try_reserve_ok_and_overflow() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_reserve(64).is_ok()); + assert!(buf.capacity() >= 64); + + buf.push(1u8); + assert_eq!( + buf.try_reserve(usize::MAX), + Err(MutableBufferError::LengthOverflow) + ); + } + + #[test] + fn test_try_resize_ok_and_overflow() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_resize(128, 0xAB).is_ok()); + assert_eq!(buf.len(), 128); + assert!(buf.as_slice().iter().all(|&b| b == 0xAB)); + + // usize::MAX itself doesn't cause a length overflow, but it can't be rounded + // up to the next 64-byte boundary without wrapping, so we get LayoutError. + assert_eq!( + buf.try_resize(usize::MAX, 0), + Err(MutableBufferError::LayoutError) + ); + } + + #[test] + fn test_try_shrink_to_fit_ok() { + let mut buf = MutableBuffer::new(256); + assert_eq!(buf.capacity(), 256); + buf.push(1u8); + buf.push(2u8); + + assert!(buf.try_shrink_to_fit().is_ok()); + assert!(buf.capacity() >= 64 && buf.capacity() < 256); + } + + #[test] + fn test_try_extend_zeros_ok_and_overflow() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_extend_zeros(32).is_ok()); + assert_eq!(buf.len(), 32); + assert!(buf.as_slice().iter().all(|&b| b == 0)); + + assert_eq!( + buf.try_extend_zeros(usize::MAX), + Err(MutableBufferError::LengthOverflow) + ); + } + + #[test] + fn test_try_push_ok() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_push(42u32).is_ok()); + assert_eq!(buf.len(), 4); + assert_eq!(&buf.as_slice()[..4], &42u32.to_le_bytes()); + + // Chaining multiple pushes works fine. + assert!(buf.try_push(0xDEADBEEFu32).is_ok()); + assert_eq!(buf.len(), 8); + } + + #[test] + fn test_try_extend_from_slice_ok() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_extend_from_slice(&[1u32, 2, 3]).is_ok()); + assert_eq!(buf.len(), 12); + + // Round-trips correctly: the bytes match a native-endian u32 slice. + let expected: Vec = [1u32, 2, 3] + .iter() + .flat_map(|v| v.to_ne_bytes()) + .collect(); + assert_eq!(buf.as_slice(), expected.as_slice()); + } + + #[test] + fn test_try_repeat_slice_n_times_ok_and_overflow() { + let mut buf = MutableBuffer::new(0); + assert!(buf.try_repeat_slice_n_times(&[0xFFu8], 8).is_ok()); + assert_eq!(buf.as_slice(), &[0xFF; 8]); + + // A 2-byte slice repeated usize::MAX/2 + 1 times overflows checked_mul. + let mut buf2 = MutableBuffer::new(0); + assert_eq!( + buf2.try_repeat_slice_n_times(&[0u8, 0u8], usize::MAX / 2 + 1), + Err(MutableBufferError::LengthOverflow) + ); + } + + #[test] + fn test_try_reallocate_layout_error() { + let mut buf = MutableBuffer::new(0); + // Requesting a capacity that cannot form a valid Layout should return LayoutError. + let result = buf.try_reallocate(usize::MAX); + assert_eq!(result, Err(MutableBufferError::LayoutError)); + } } From bbcb99e7cdb3799dd5c693bc1547545f906bff2e Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 10:45:41 -0400 Subject: [PATCH 03/11] revise PR after review --- arrow-buffer/src/buffer/mutable.rs | 226 ++++++----------------------- 1 file changed, 41 insertions(+), 185 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 64a038ccb5c1..1fea7c0b7ce5 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -141,6 +141,12 @@ impl MutableBuffer { Self::with_capacity(capacity) } + /// Fallible version of [`MutableBuffer::new`]. + #[inline] + pub fn try_new(capacity: usize) -> Result { + Self::try_with_capacity(capacity) + } + /// Allocate a new [MutableBuffer] with initial capacity to be at least `capacity`. /// /// # Panics @@ -149,9 +155,17 @@ impl MutableBuffer { /// then `isize::MAX`, then this function will panic. #[inline] pub fn with_capacity(capacity: usize) -> Self { - let capacity = bit_util::round_upto_multiple_of_64(capacity); + Self::try_with_capacity(capacity).unwrap_or_else(|e| panic!("{e}")) + } + + /// Fallible version of [`MutableBuffer::with_capacity`]. + #[inline] + pub fn try_with_capacity(capacity: usize) -> Result { + let capacity = capacity + .checked_next_multiple_of(64) + .ok_or(MutableBufferError::LayoutError)?; let layout = Layout::from_size_align(capacity, ALIGNMENT) - .expect("failed to create layout for MutableBuffer"); + .map_err(|_| MutableBufferError::LayoutError)?; let data = match layout.size() { 0 => dangling_ptr(), _ => { @@ -160,13 +174,13 @@ impl MutableBuffer { NonNull::new(raw_ptr).unwrap_or_else(|| handle_alloc_error(layout)) } }; - Self { + Ok(Self { data, len: 0, layout, #[cfg(feature = "pool")] reservation: std::sync::Mutex::new(None), - } + }) } /// Allocates a new [MutableBuffer] with `len` and capacity to be at least `len` where @@ -280,10 +294,6 @@ impl MutableBuffer { } /// Fallible version of [`MutableBuffer::reserve`]. - /// - /// Returns [`MutableBufferError::LengthOverflow`] if `self.len + additional` - /// overflows `usize`, or [`MutableBufferError::LayoutError`] if the required - /// capacity cannot be rounded to a 64-byte boundary and form a valid layout. #[inline] pub fn try_reserve(&mut self, additional: usize) -> Result<(), MutableBufferError> { let required_cap = self @@ -294,7 +304,6 @@ impl MutableBuffer { let new_capacity = required_cap .checked_next_multiple_of(64) .ok_or(MutableBufferError::LayoutError)?; - // Mirror the doubling heuristic of the infallible `reserve`. let new_capacity = std::cmp::max(new_capacity, self.layout.size().saturating_mul(2)); self.try_reallocate(new_capacity)?; } @@ -320,22 +329,11 @@ impl MutableBuffer { // exits. #[inline(always)] pub fn reserve(&mut self, additional: usize) { - let required_cap = self - .len - .checked_add(additional) - .expect("buffer length overflow"); - if required_cap > self.layout.size() { - let new_capacity = bit_util::round_upto_multiple_of_64(required_cap); - let new_capacity = std::cmp::max(new_capacity, self.layout.size() * 2); - self.reallocate(new_capacity) - } + self.try_reserve(additional) + .unwrap_or_else(|e| panic!("{e}")) } /// Fallible version of [`MutableBuffer::repeat_slice_n_times`]. - /// - /// Returns [`MutableBufferError::LengthOverflow`] if computing the total byte - /// count overflows `usize`, or a layout error for the same reasons as - /// [`MutableBuffer::try_reserve`]. pub fn try_repeat_slice_n_times( &mut self, slice_to_repeat: &[T], @@ -344,21 +342,16 @@ impl MutableBuffer { if repeat_count == 0 || slice_to_repeat.is_empty() { return Ok(()); } - - let bytes_per_copy = size_of_val(slice_to_repeat); let total_bytes = repeat_count - .checked_mul(bytes_per_copy) + .checked_mul(size_of_val(slice_to_repeat)) .ok_or(MutableBufferError::LengthOverflow)?; - // Verify the final length won't overflow before touching any state. self.len .checked_add(total_bytes) .ok_or(MutableBufferError::LengthOverflow)?; - self.try_reserve(total_bytes)?; - + let bytes_per_copy = size_of_val(slice_to_repeat); let length_before = self.len; self.try_extend_from_slice(slice_to_repeat)?; - let mut already_repeated = 1usize; while already_repeated < repeat_count { let to_copy = already_repeated.min(repeat_count - already_repeated); @@ -396,68 +389,11 @@ impl MutableBuffer { slice_to_repeat: &[T], repeat_count: usize, ) { - if repeat_count == 0 || slice_to_repeat.is_empty() { - return; - } - - let bytes_to_repeat = size_of_val(slice_to_repeat); - let repeated_bytes = repeat_count - .checked_mul(bytes_to_repeat) - .expect("repeated slice byte length overflow"); - self.len - .checked_add(repeated_bytes) - .expect("mutable buffer length overflow"); - - // Ensure capacity - self.reserve(repeated_bytes); - - // Save the length before we do all the copies to know where to start from - let length_before = self.len; - - // Copy the initial slice once so we can use doubling strategy on it - self.extend_from_slice(slice_to_repeat); - - // This tracks how much bytes we have added by repeating so far - let added_repeats_length = bytes_to_repeat; - assert_eq!( - self.len - length_before, - added_repeats_length, - "should copy exactly the same number of bytes" - ); - - // Number of times the slice was repeated - let mut already_repeated_times = 1; - - // We will use doubling strategy to fill the buffer in log(repeat_count) steps - while already_repeated_times < repeat_count { - // How many slices can we copy in this iteration - // (either double what we have, or just the remaining ones) - let number_of_slices_to_copy = - already_repeated_times.min(repeat_count - already_repeated_times); - let number_of_bytes_to_copy = number_of_slices_to_copy * bytes_to_repeat; - - unsafe { - // Get to the start of the data before we started copying anything - let src = self.data.as_ptr().add(length_before) as *const u8; - - // Go to the current location to copy to (end of current data) - let dst = self.data.as_ptr().add(self.len); - - // SAFETY: the pointers are not overlapping as there is `number_of_bytes_to_copy` or less between them - std::ptr::copy_nonoverlapping(src, dst, number_of_bytes_to_copy) - } - - // Advance the length by the amount of data we just copied (doubled) - self.len += number_of_bytes_to_copy; - - already_repeated_times += number_of_slices_to_copy; - } + self.try_repeat_slice_n_times(slice_to_repeat, repeat_count) + .unwrap_or_else(|e| panic!("{e}")) } - /// mirrors [`MutableBuffer::reallocate`] but returns - /// [`MutableBufferError::LayoutError`] instead of panicking on an invalid layout. #[cold] - #[allow(dead_code)] fn try_reallocate(&mut self, capacity: usize) -> Result<(), MutableBufferError> { let new_layout = Layout::from_size_align(capacity, self.layout.align()) .map_err(|_| MutableBufferError::LayoutError)?; @@ -488,31 +424,10 @@ impl MutableBuffer { Ok(()) } #[cold] + #[expect(unused)] fn reallocate(&mut self, capacity: usize) { - let new_layout = Layout::from_size_align(capacity, self.layout.align()).unwrap(); - if new_layout.size() == 0 { - if self.layout.size() != 0 { - // Safety: data was allocated with layout - unsafe { std::alloc::dealloc(self.as_mut_ptr(), self.layout) }; - self.layout = new_layout - } - return; - } - - let data = match self.layout.size() { - // Safety: new_layout is not empty - 0 => unsafe { std::alloc::alloc(new_layout) }, - // Safety: verified new layout is valid and not empty - _ => unsafe { std::alloc::realloc(self.as_mut_ptr(), self.layout, capacity) }, - }; - self.data = NonNull::new(data).unwrap_or_else(|| handle_alloc_error(new_layout)); - self.layout = new_layout; - #[cfg(feature = "pool")] - { - if let Some(reservation) = self.reservation.lock().unwrap().as_mut() { - reservation.resize(self.layout.size()); - } - } + self.try_reallocate(capacity) + .unwrap_or_else(|e| panic!("{e}")) } /// Truncates this buffer to `len` bytes @@ -533,8 +448,6 @@ impl MutableBuffer { } /// Fallible version of [`MutableBuffer::resize`]. - /// - /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. #[inline] pub fn try_resize(&mut self, new_len: usize, value: u8) -> Result<(), MutableBufferError> { if new_len > self.len { @@ -570,27 +483,11 @@ impl MutableBuffer { // exits. #[inline(always)] pub fn resize(&mut self, new_len: usize, value: u8) { - if new_len > self.len { - let diff = new_len - self.len; - self.reserve(diff); - // write the value - unsafe { self.data.as_ptr().add(self.len).write_bytes(value, diff) }; - } - // this truncates the buffer when new_len < self.len - self.len = new_len; - #[cfg(feature = "pool")] - { - if let Some(reservation) = self.reservation.lock().unwrap().as_mut() { - reservation.resize(self.len); - } - } + self.try_resize(new_len, value) + .unwrap_or_else(|e| panic!("{e}")) } /// Fallible version of [`MutableBuffer::shrink_to_fit`]. - /// - /// Returns [`MutableBufferError::LayoutError`] if rounding `self.len` up to the - /// nearest 64-byte boundary overflows (practically impossible, but checked for - /// completeness). pub fn try_shrink_to_fit(&mut self) -> Result<(), MutableBufferError> { let new_capacity = self .len @@ -622,10 +519,7 @@ impl MutableBuffer { /// Panics if the current length is too large to round up to the next 64-byte boundary and /// construct a valid allocation layout. pub fn shrink_to_fit(&mut self) { - let new_capacity = bit_util::round_upto_multiple_of_64(self.len); - if new_capacity < self.layout.size() { - self.reallocate(new_capacity) - } + self.try_shrink_to_fit().unwrap_or_else(|e| panic!("{e}")) } /// Returns whether this buffer is empty or not. @@ -727,8 +621,6 @@ impl MutableBuffer { } /// Fallible version of [`MutableBuffer::extend_from_slice`]. - /// - /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. #[inline] pub fn try_extend_from_slice( &mut self, @@ -759,22 +651,11 @@ impl MutableBuffer { /// reasons as [`MutableBuffer::reserve`]. #[inline] pub fn extend_from_slice(&mut self, items: &[T]) { - let additional = mem::size_of_val(items); - self.reserve(additional); - unsafe { - // this assumes that `[ToByteSlice]` can be copied directly - // without calling `to_byte_slice` for each element, - // which is correct for all ArrowNativeType implementations. - let src = items.as_ptr() as *const u8; - let dst = self.data.as_ptr().add(self.len); - std::ptr::copy_nonoverlapping(src, dst, additional) - } - self.len += additional; + self.try_extend_from_slice(items) + .unwrap_or_else(|e| panic!("{e}")) } /// Fallible version of [`MutableBuffer::push`]. - /// - /// Returns an error for the same reasons as [`MutableBuffer::try_reserve`]. #[inline] pub fn try_push(&mut self, item: T) -> Result<(), MutableBufferError> { let additional = std::mem::size_of::(); @@ -802,14 +683,7 @@ impl MutableBuffer { /// reasons as [`MutableBuffer::reserve`]. #[inline] pub fn push(&mut self, item: T) { - let additional = std::mem::size_of::(); - self.reserve(additional); - unsafe { - let src = item.to_byte_slice().as_ptr(); - let dst = self.data.as_ptr().add(self.len); - std::ptr::copy_nonoverlapping(src, dst, additional); - } - self.len += additional; + self.try_push(item).unwrap_or_else(|e| panic!("{e}")) } /// Extends the buffer with a new item, without checking for sufficient capacity @@ -824,11 +698,7 @@ impl MutableBuffer { self.len += additional; } - /// Fallible version of [`MutableBuffer::extend_zeros`] - /// - /// Returns [`MutableBufferError::LengthOverflow`] if `self.len + additional` - /// overflows `usize`, or a layout error for the same reasons as - /// [`MutableBuffer::try_reserve`]. + /// Fallible version of [`MutableBuffer::extend_zeros`]. #[inline] pub fn try_extend_zeros(&mut self, additional: usize) -> Result<(), MutableBufferError> { let new_len = self @@ -845,11 +715,8 @@ impl MutableBuffer { /// reserving a capacity that fails for the same reasons as [`MutableBuffer::reserve`]. #[inline] pub fn extend_zeros(&mut self, additional: usize) { - let new_len = self - .len - .checked_add(additional) - .expect("buffer length overflow"); - self.resize(new_len, 0); + self.try_extend_zeros(additional) + .unwrap_or_else(|e| panic!("{e}")) } /// # Safety @@ -1621,7 +1488,7 @@ mod tests { } #[test] - #[should_panic(expected = "failed to create layout for MutableBuffer: LayoutError")] + #[should_panic(expected = "invalid allocation layout for requested capacity")] fn test_with_capacity_panics_above_max_capacity() { let max_capacity = isize::MAX as usize - (isize::MAX as usize % ALIGNMENT); let _ = MutableBuffer::with_capacity(max_capacity + 1); @@ -1773,14 +1640,14 @@ mod tests { } #[test] - #[should_panic(expected = "repeated slice byte length overflow")] + #[should_panic(expected = "buffer length overflow")] fn test_repeat_slice_count_multiply_overflow() { let mut buffer = MutableBuffer::new(0); buffer.repeat_slice_n_times(&[0_u64], usize::MAX / mem::size_of::() + 1); } #[test] - #[should_panic(expected = "mutable buffer length overflow")] + #[should_panic(expected = "buffer length overflow")] fn test_repeat_slice_count_len_overflow() { let mut buffer = MutableBuffer::new(0); buffer.push(0_u8); @@ -1868,7 +1735,7 @@ mod tests { } #[test] - #[should_panic(expected = "failed to round upto multiple of 64")] + #[should_panic(expected = "invalid allocation layout for requested capacity")] fn test_mutable_new_capacity_overflow() { // Tests overflow during initial allocation let _ = MutableBuffer::new(usize::MAX - 10); @@ -1954,10 +1821,7 @@ mod tests { assert_eq!(buf.len(), 12); // Round-trips correctly: the bytes match a native-endian u32 slice. - let expected: Vec = [1u32, 2, 3] - .iter() - .flat_map(|v| v.to_ne_bytes()) - .collect(); + let expected: Vec = [1u32, 2, 3].iter().flat_map(|v| v.to_ne_bytes()).collect(); assert_eq!(buf.as_slice(), expected.as_slice()); } @@ -1974,12 +1838,4 @@ mod tests { Err(MutableBufferError::LengthOverflow) ); } - - #[test] - fn test_try_reallocate_layout_error() { - let mut buf = MutableBuffer::new(0); - // Requesting a capacity that cannot form a valid Layout should return LayoutError. - let result = buf.try_reallocate(usize::MAX); - assert_eq!(result, Err(MutableBufferError::LayoutError)); - } } From 631cb699421e8e4a82e8222f4292b4923ee9e2d6 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 10:59:42 -0400 Subject: [PATCH 04/11] revision #2 --- arrow-buffer/src/buffer/mutable.rs | 102 +++++++++++------------------ 1 file changed, 38 insertions(+), 64 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 1fea7c0b7ce5..635637c446b2 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -400,7 +400,7 @@ impl MutableBuffer { if new_layout.size() == 0 { if self.layout.size() != 0 { - // Safety: data was allocated with the current layout. + // Safety: data was allocated with layout unsafe { std::alloc::dealloc(self.as_mut_ptr(), self.layout) }; self.layout = new_layout; } @@ -408,9 +408,9 @@ impl MutableBuffer { } let data = match self.layout.size() { - // Safety: new_layout is non-zero. + // Safety: new_layout is not empty 0 => unsafe { std::alloc::alloc(new_layout) }, - // Safety: new_layout is valid and non-zero. + // Safety: verified new layout is valid and not empty _ => unsafe { std::alloc::realloc(self.as_mut_ptr(), self.layout, capacity) }, }; self.data = NonNull::new(data).unwrap_or_else(|| handle_alloc_error(new_layout)); @@ -683,7 +683,14 @@ impl MutableBuffer { /// reasons as [`MutableBuffer::reserve`]. #[inline] pub fn push(&mut self, item: T) { - self.try_push(item).unwrap_or_else(|e| panic!("{e}")) + let additional = std::mem::size_of::(); + self.reserve(additional); + unsafe { + let src = item.to_byte_slice().as_ptr(); + let dst = self.data.as_ptr().add(self.len); + std::ptr::copy_nonoverlapping(src, dst, additional); + } + self.len += additional; } /// Extends the buffer with a new item, without checking for sufficient capacity @@ -1751,90 +1758,57 @@ mod tests { } #[test] - fn test_try_reserve_ok_and_overflow() { + fn test_try_methods_ok() { let mut buf = MutableBuffer::new(0); assert!(buf.try_reserve(64).is_ok()); assert!(buf.capacity() >= 64); - buf.push(1u8); - assert_eq!( - buf.try_reserve(usize::MAX), - Err(MutableBufferError::LengthOverflow) - ); - } - - #[test] - fn test_try_resize_ok_and_overflow() { - let mut buf = MutableBuffer::new(0); assert!(buf.try_resize(128, 0xAB).is_ok()); assert_eq!(buf.len(), 128); assert!(buf.as_slice().iter().all(|&b| b == 0xAB)); - // usize::MAX itself doesn't cause a length overflow, but it can't be rounded - // up to the next 64-byte boundary without wrapping, so we get LayoutError. - assert_eq!( - buf.try_resize(usize::MAX, 0), - Err(MutableBufferError::LayoutError) - ); - } - - #[test] - fn test_try_shrink_to_fit_ok() { - let mut buf = MutableBuffer::new(256); - assert_eq!(buf.capacity(), 256); - buf.push(1u8); - buf.push(2u8); - assert!(buf.try_shrink_to_fit().is_ok()); assert!(buf.capacity() >= 64 && buf.capacity() < 256); - } - #[test] - fn test_try_extend_zeros_ok_and_overflow() { - let mut buf = MutableBuffer::new(0); assert!(buf.try_extend_zeros(32).is_ok()); - assert_eq!(buf.len(), 32); - assert!(buf.as_slice().iter().all(|&b| b == 0)); - - assert_eq!( - buf.try_extend_zeros(usize::MAX), - Err(MutableBufferError::LengthOverflow) - ); - } - - #[test] - fn test_try_push_ok() { - let mut buf = MutableBuffer::new(0); assert!(buf.try_push(42u32).is_ok()); - assert_eq!(buf.len(), 4); - assert_eq!(&buf.as_slice()[..4], &42u32.to_le_bytes()); - // Chaining multiple pushes works fine. - assert!(buf.try_push(0xDEADBEEFu32).is_ok()); - assert_eq!(buf.len(), 8); - } - - #[test] - fn test_try_extend_from_slice_ok() { let mut buf = MutableBuffer::new(0); assert!(buf.try_extend_from_slice(&[1u32, 2, 3]).is_ok()); - assert_eq!(buf.len(), 12); - - // Round-trips correctly: the bytes match a native-endian u32 slice. let expected: Vec = [1u32, 2, 3].iter().flat_map(|v| v.to_ne_bytes()).collect(); assert_eq!(buf.as_slice(), expected.as_slice()); - } - #[test] - fn test_try_repeat_slice_n_times_ok_and_overflow() { let mut buf = MutableBuffer::new(0); assert!(buf.try_repeat_slice_n_times(&[0xFFu8], 8).is_ok()); assert_eq!(buf.as_slice(), &[0xFF; 8]); + } - // A 2-byte slice repeated usize::MAX/2 + 1 times overflows checked_mul. - let mut buf2 = MutableBuffer::new(0); + #[test] + fn test_try_methods_errors() { + let mut buf = MutableBuffer::new(0); + buf.push(1u8); + assert_eq!( + buf.try_reserve(usize::MAX), + Err(MutableBufferError::LengthOverflow) + ); + + let mut buf = MutableBuffer::new(0); + buf.try_resize(128, 0).unwrap(); + assert_eq!( + buf.try_resize(usize::MAX, 0), + Err(MutableBufferError::LayoutError) + ); + + let mut buf = MutableBuffer::new(0); + buf.push(1u8); + assert_eq!( + buf.try_extend_zeros(usize::MAX), + Err(MutableBufferError::LengthOverflow) + ); + + let mut buf = MutableBuffer::new(0); assert_eq!( - buf2.try_repeat_slice_n_times(&[0u8, 0u8], usize::MAX / 2 + 1), + buf.try_repeat_slice_n_times(&[0u8, 0u8], usize::MAX / 2 + 1), Err(MutableBufferError::LengthOverflow) ); } From 922eab2f1746a42f4e269685c1b43deb68cc67c1 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 11:03:35 -0400 Subject: [PATCH 05/11] Revision ++ --- arrow-buffer/src/buffer/mutable.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 635637c446b2..d89f00b8a0d9 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -655,19 +655,6 @@ impl MutableBuffer { .unwrap_or_else(|e| panic!("{e}")) } - /// Fallible version of [`MutableBuffer::push`]. - #[inline] - pub fn try_push(&mut self, item: T) -> Result<(), MutableBufferError> { - let additional = std::mem::size_of::(); - self.try_reserve(additional)?; - unsafe { - let src = item.to_byte_slice().as_ptr(); - let dst = self.data.as_ptr().add(self.len); - std::ptr::copy_nonoverlapping(src, dst, additional); - } - self.len += additional; - Ok(()) - } /// Extends the buffer with a new item, increasing its capacity if needed. /// # Example /// ``` @@ -1771,7 +1758,6 @@ mod tests { assert!(buf.capacity() >= 64 && buf.capacity() < 256); assert!(buf.try_extend_zeros(32).is_ok()); - assert!(buf.try_push(42u32).is_ok()); let mut buf = MutableBuffer::new(0); assert!(buf.try_extend_from_slice(&[1u32, 2, 3]).is_ok()); From ad300531aee552fb9462ba28259646b7cda73e47 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 11:14:38 -0400 Subject: [PATCH 06/11] fix lint --- arrow-buffer/src/buffer/mutable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index d89f00b8a0d9..d28c18f832ce 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -424,7 +424,7 @@ impl MutableBuffer { Ok(()) } #[cold] - #[expect(unused)] + #[allow(dead_code)] fn reallocate(&mut self, capacity: usize) { self.try_reallocate(capacity) .unwrap_or_else(|e| panic!("{e}")) From 702d73e19a14f9c496913c68b54f79fb9d118deb Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 22:36:22 -0400 Subject: [PATCH 07/11] trim PR; remove un-needed test --- arrow-buffer/src/buffer/mutable.rs | 66 +----------------------------- 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index d28c18f832ce..6a2138e1888e 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -423,13 +423,6 @@ impl MutableBuffer { } Ok(()) } - #[cold] - #[allow(dead_code)] - fn reallocate(&mut self, capacity: usize) { - self.try_reallocate(capacity) - .unwrap_or_else(|e| panic!("{e}")) - } - /// Truncates this buffer to `len` bytes /// /// If `len` is greater than the buffer's current length, this has no effect @@ -1504,14 +1497,14 @@ mod tests { assert_eq!(pool.used(), 128); // Reallocate to a larger size - buffer.reallocate(200); + buffer.try_reallocate(200).unwrap(); // The capacity is exactly the requested size, not rounded up assert_eq!(buffer.capacity(), 200); assert_eq!(pool.used(), 200); // Reallocate to a smaller size - buffer.reallocate(50); + buffer.try_reallocate(50).unwrap(); // The capacity is exactly the requested size, not rounded up assert_eq!(buffer.capacity(), 50); @@ -1743,59 +1736,4 @@ mod tests { buf.push(1u8); buf.reserve(usize::MAX); } - - #[test] - fn test_try_methods_ok() { - let mut buf = MutableBuffer::new(0); - assert!(buf.try_reserve(64).is_ok()); - assert!(buf.capacity() >= 64); - - assert!(buf.try_resize(128, 0xAB).is_ok()); - assert_eq!(buf.len(), 128); - assert!(buf.as_slice().iter().all(|&b| b == 0xAB)); - - assert!(buf.try_shrink_to_fit().is_ok()); - assert!(buf.capacity() >= 64 && buf.capacity() < 256); - - assert!(buf.try_extend_zeros(32).is_ok()); - - let mut buf = MutableBuffer::new(0); - assert!(buf.try_extend_from_slice(&[1u32, 2, 3]).is_ok()); - let expected: Vec = [1u32, 2, 3].iter().flat_map(|v| v.to_ne_bytes()).collect(); - assert_eq!(buf.as_slice(), expected.as_slice()); - - let mut buf = MutableBuffer::new(0); - assert!(buf.try_repeat_slice_n_times(&[0xFFu8], 8).is_ok()); - assert_eq!(buf.as_slice(), &[0xFF; 8]); - } - - #[test] - fn test_try_methods_errors() { - let mut buf = MutableBuffer::new(0); - buf.push(1u8); - assert_eq!( - buf.try_reserve(usize::MAX), - Err(MutableBufferError::LengthOverflow) - ); - - let mut buf = MutableBuffer::new(0); - buf.try_resize(128, 0).unwrap(); - assert_eq!( - buf.try_resize(usize::MAX, 0), - Err(MutableBufferError::LayoutError) - ); - - let mut buf = MutableBuffer::new(0); - buf.push(1u8); - assert_eq!( - buf.try_extend_zeros(usize::MAX), - Err(MutableBufferError::LengthOverflow) - ); - - let mut buf = MutableBuffer::new(0); - assert_eq!( - buf.try_repeat_slice_n_times(&[0u8, 0u8], usize::MAX / 2 + 1), - Err(MutableBufferError::LengthOverflow) - ); - } } From 0b1a641f076be061c9020ad6872c8da3b261c667 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 14 Jul 2026 22:57:51 -0400 Subject: [PATCH 08/11] avoid panics on OOM errors --- arrow-buffer/src/buffer/mutable.rs | 34 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 6a2138e1888e..615cee90c4b8 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use std::alloc::{Layout, handle_alloc_error}; +use std::alloc::Layout; use std::mem; use std::ptr::NonNull; @@ -40,6 +40,8 @@ pub enum MutableBufferError { LengthOverflow, /// The requested capacity cannot be represented as a valid allocation layout. LayoutError, + /// An allocation failed due to insufficient memory. + AllocationError(Layout), } impl std::fmt::Display for MutableBufferError { @@ -47,6 +49,9 @@ impl std::fmt::Display for MutableBufferError { match self { Self::LengthOverflow => write!(f, "buffer length overflow"), Self::LayoutError => write!(f, "invalid allocation layout for requested capacity"), + Self::AllocationError(layout) => { + write!(f, "failed to allocate memory for layout {layout:?}") + } } } } @@ -171,7 +176,10 @@ impl MutableBuffer { _ => { // Safety: Verified size != 0 let raw_ptr = unsafe { std::alloc::alloc(layout) }; - NonNull::new(raw_ptr).unwrap_or_else(|| handle_alloc_error(layout)) + match NonNull::new(raw_ptr) { + Some(ptr) => ptr, + None => return Err(MutableBufferError::AllocationError(layout)), + } } }; Ok(Self { @@ -199,22 +207,31 @@ impl MutableBuffer { /// /// Panics if `len` is too large to construct a valid allocation [`Layout`] pub fn from_len_zeroed(len: usize) -> Self { - let layout = Layout::from_size_align(len, ALIGNMENT).unwrap(); + Self::try_from_len_zeroed(len).unwrap_or_else(|e| panic!("{e}")) + } + + /// Fallible version of [`MutableBuffer::from_len_zeroed`]. + pub fn try_from_len_zeroed(len: usize) -> Result { + let layout = + Layout::from_size_align(len, ALIGNMENT).map_err(|_| MutableBufferError::LayoutError)?; let data = match layout.size() { 0 => dangling_ptr(), _ => { // Safety: Verified size != 0 let raw_ptr = unsafe { std::alloc::alloc_zeroed(layout) }; - NonNull::new(raw_ptr).unwrap_or_else(|| handle_alloc_error(layout)) + match NonNull::new(raw_ptr) { + Some(ptr) => ptr, + None => return Err(MutableBufferError::AllocationError(layout)), + } } }; - Self { + Ok(Self { data, len, layout, #[cfg(feature = "pool")] reservation: std::sync::Mutex::new(None), - } + }) } /// Allocates a new [MutableBuffer] from given `Bytes`. @@ -413,7 +430,10 @@ impl MutableBuffer { // Safety: verified new layout is valid and not empty _ => unsafe { std::alloc::realloc(self.as_mut_ptr(), self.layout, capacity) }, }; - self.data = NonNull::new(data).unwrap_or_else(|| handle_alloc_error(new_layout)); + self.data = match NonNull::new(data) { + Some(ptr) => ptr, + None => return Err(MutableBufferError::AllocationError(new_layout)), + }; self.layout = new_layout; #[cfg(feature = "pool")] { From 6f03becc7066aa8a7edfc5d24a472db537bd571f Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 15 Jul 2026 13:57:20 -0400 Subject: [PATCH 09/11] re-introduce comments --- arrow-buffer/src/buffer/mutable.rs | 33 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 615cee90c4b8..b30a932b35a7 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -143,13 +143,7 @@ impl MutableBuffer { /// See [`MutableBuffer::with_capacity`]. #[inline] pub fn new(capacity: usize) -> Self { - Self::with_capacity(capacity) - } - - /// Fallible version of [`MutableBuffer::new`]. - #[inline] - pub fn try_new(capacity: usize) -> Result { - Self::try_with_capacity(capacity) + Self::try_with_capacity(capacity).unwrap_or_else(|e| panic!("{e}")) } /// Allocate a new [MutableBuffer] with initial capacity to be at least `capacity`. @@ -359,25 +353,41 @@ impl MutableBuffer { if repeat_count == 0 || slice_to_repeat.is_empty() { return Ok(()); } + let bytes_per_copy = size_of_val(slice_to_repeat); let total_bytes = repeat_count - .checked_mul(size_of_val(slice_to_repeat)) + .checked_mul(bytes_per_copy) .ok_or(MutableBufferError::LengthOverflow)?; self.len .checked_add(total_bytes) .ok_or(MutableBufferError::LengthOverflow)?; + + // Ensure capacity self.try_reserve(total_bytes)?; - let bytes_per_copy = size_of_val(slice_to_repeat); + + // Save the length before we do all the copies to know where to start from let length_before = self.len; + + // Copy the initial slice once so we can use doubling strategy on it self.try_extend_from_slice(slice_to_repeat)?; + + // Number of times the slice was repeated let mut already_repeated = 1usize; + + // We will use doubling strategy to fill the buffer in log(repeat_count) steps while already_repeated < repeat_count { + // How many slices can we copy in this iteration + // (either double what we have, or just the remaining ones) let to_copy = already_repeated.min(repeat_count - already_repeated); let byte_count = to_copy * bytes_per_copy; unsafe { + // Get to the start of the data before we started copying anything let src = self.data.as_ptr().add(length_before) as *const u8; + // Go to the current location to copy to (end of current data) let dst = self.data.as_ptr().add(self.len); + // SAFETY: the pointers are not overlapping as there is `byte_count` or less between them std::ptr::copy_nonoverlapping(src, dst, byte_count); } + // Advance the length by the amount of data we just copied (doubled) self.len += byte_count; already_repeated += to_copy; } @@ -467,8 +477,10 @@ impl MutableBuffer { let diff = new_len - self.len; self.try_reserve(diff)?; // Safety: try_reserve ensured capacity >= new_len. + // write the value unsafe { self.data.as_ptr().add(self.len).write_bytes(value, diff) }; } + // this truncates the buffer when new_len < self.len self.len = new_len; #[cfg(feature = "pool")] { @@ -642,6 +654,9 @@ impl MutableBuffer { let additional = mem::size_of_val(items); self.try_reserve(additional)?; unsafe { + // this assumes that `[ToByteSlice]` can be copied directly + // without calling `to_byte_slice` for each element, + // which is correct for all ArrowNativeType implementations. let src = items.as_ptr() as *const u8; let dst = self.data.as_ptr().add(self.len); std::ptr::copy_nonoverlapping(src, dst, additional); From 5c48380bad1697ee95736956117393c715eca39e Mon Sep 17 00:00:00 2001 From: rich-T-kid Date: Wed, 22 Jul 2026 23:15:53 -0400 Subject: [PATCH 10/11] mark MutBuffErr enum as non_exhaustive --- arrow-buffer/src/buffer/mutable.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index b30a932b35a7..9439cb408210 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -35,6 +35,7 @@ use super::Buffer; /// Error returned by fallible [`MutableBuffer`] operations. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum MutableBufferError { /// Arithmetic overflow when computing the required buffer length or capacity. LengthOverflow, From b9ae54b1739ed6ae46b6ed014ad7ddce72d1df09 Mon Sep 17 00:00:00 2001 From: rich-T-kid Date: Thu, 23 Jul 2026 09:15:50 -0400 Subject: [PATCH 11/11] revert tag --- arrow-buffer/src/buffer/mutable.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 9439cb408210..b30a932b35a7 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -35,7 +35,6 @@ use super::Buffer; /// Error returned by fallible [`MutableBuffer`] operations. #[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] pub enum MutableBufferError { /// Arithmetic overflow when computing the required buffer length or capacity. LengthOverflow,