From b843ae217de4157419538317e859cbcc88d42b28 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 26 Nov 2024 13:24:00 +0100 Subject: [PATCH] Document why `try_realloc` is safe, and actually make it safe :) --- arrow-buffer/src/bytes.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arrow-buffer/src/bytes.rs b/arrow-buffer/src/bytes.rs index 133294b32a66..924b7a720270 100644 --- a/arrow-buffer/src/bytes.rs +++ b/arrow-buffer/src/bytes.rs @@ -104,10 +104,19 @@ impl Bytes { /// In case of `Err`, the [`Bytes`] will remain as it was (i.e. have the old size). pub fn try_realloc(&mut self, new_len: usize) -> Result<(), ()> { if let Deallocation::Standard(old_layout) = self.deallocation { + let new_len = new_len.max(1); // realloc requires a non-zero size + if old_layout.size() == new_len { + return Ok(()); // Nothing to do + } if let Ok(new_layout) = std::alloc::Layout::from_size_align(new_len, old_layout.align()) { - let new_ptr = - unsafe { std::alloc::realloc(self.ptr.as_mut(), old_layout, new_len) }; + let old_ptr = self.ptr.as_ptr(); + // SAFETY: the call to `realloc` is safe if all of the following holds (from https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#method.realloc): + // * `old_ptr` must be currently allocated via this allocator (guaranteed by the invariant/contract of `Bytes`) + // * `old_layout` must be the same layout that was used to allocate that block of memory (same) + // * `new_len` must be greater than zero (ensured by the `max` call earlier) + // * `new_len`, when rounded up to the nearest multiple of `layout.align()`, must not overflow `isize` (guaranteed by the success of `Layout::from_size_align`) + let new_ptr = unsafe { std::alloc::realloc(old_ptr, old_layout, new_len) }; if let Some(ptr) = NonNull::new(new_ptr) { self.ptr = ptr; self.len = new_len;