1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2024 Google LLC. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 use super::opaque_pointee::opaque_pointee; 9 use core::cell::UnsafeCell; 10 use core::marker::PhantomData; 11 use core::mem::{align_of, align_of_val, size_of_val, MaybeUninit}; 12 use core::ptr::{self, NonNull}; 13 use core::slice; 14 15 opaque_pointee!(upb_Arena); 16 pub type RawArena = NonNull<upb_Arena>; 17 18 /// See `upb/port/def.inc`. 19 const UPB_MALLOC_ALIGN: usize = 8; 20 const _CHECK_UPB_MALLOC_ALIGN_AT_LEAST_POINTER_ALIGNED: () = 21 assert!(UPB_MALLOC_ALIGN >= align_of::<*const ()>()); 22 23 /// A wrapper over a `upb_Arena`. 24 /// 25 /// This is not a safe wrapper per se, because the allocation functions still 26 /// have sharp edges (see their safety docs for more info). 27 /// 28 /// This is an owning type and will automatically free the arena when 29 /// dropped. 30 /// 31 /// Note that this type is not `Sync` as it implements unsynchronized interior 32 /// mutability. The upb_Arena C object could be understood as being Sync (at 33 /// least vacuously under current API since there are not any const upb_Arena* 34 /// API functions), but the Rust Arena is necessarily expressed as interior 35 /// mutability (&self rather than &mut self receivers) See https://doc.rust-lang.org/nomicon/lifetime-mismatch.html and 36 /// https://blog.reverberate.org/2021/12/19/arenas-and-rust.html, and the 37 /// 'known problems' section of https://rust-lang.github.io/rust-clippy/master/index.html#/mut_from_ref. 38 #[derive(Debug)] 39 pub struct Arena { 40 // Safety invariant: this must always be a valid arena 41 raw: RawArena, 42 _not_sync: PhantomData<UnsafeCell<()>>, 43 } 44 45 // SAFETY: `Arena` uniquely holds the underlying RawArena and has no 46 // thread-local data. 47 unsafe impl Send for Arena {} 48 49 impl Arena { 50 /// Allocates a fresh arena. 51 #[inline] new() -> Self52 pub fn new() -> Self { 53 #[inline(never)] 54 #[cold] 55 fn arena_new_failed() -> ! { 56 panic!("Could not create a new UPB arena"); 57 } 58 59 // SAFETY: 60 // - `upb_Arena_New` is assumed to be implemented correctly and always sound to 61 // call; if it returned a non-null pointer, it is a valid arena. 62 unsafe { 63 let Some(raw) = upb_Arena_New() else { arena_new_failed() }; 64 Self { raw, _not_sync: PhantomData } 65 } 66 } 67 68 /// # Safety 69 /// - The `raw_arena` must point to a valid arena. 70 /// - The caller must ensure that the Arena's destructor does not run. from_raw(raw_arena: RawArena) -> Self71 pub unsafe fn from_raw(raw_arena: RawArena) -> Self { 72 Arena { raw: raw_arena, _not_sync: PhantomData } 73 } 74 75 /// Returns the raw, UPB-managed pointer to the arena. 76 #[inline] raw(&self) -> RawArena77 pub fn raw(&self) -> RawArena { 78 self.raw 79 } 80 81 /// Allocates some memory on the arena. Returns None if the allocation 82 /// failed. 83 /// 84 /// # Safety 85 /// 86 /// - `align` must be less than `UPB_MALLOC_ALIGN`. 87 #[allow(clippy::mut_from_ref)] 88 #[inline] alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]>89 pub unsafe fn alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]> { 90 debug_assert!(align <= UPB_MALLOC_ALIGN); 91 // SAFETY: `self.raw` is a valid UPB arena 92 let ptr = unsafe { upb_Arena_Malloc(self.raw, size) }; 93 94 if ptr.is_null() { 95 None 96 } else { 97 // SAFETY: 98 // - `upb_Arena_Malloc` promises that if the return pointer is non-null, it is 99 // dereferencable for `size` bytes and has an alignment of `UPB_MALLOC_ALIGN` 100 // until the arena is destroyed. 101 // - `[MaybeUninit<u8>]` has no alignment requirement, and `ptr` is aligned to a 102 // `UPB_MALLOC_ALIGN` boundary. 103 Some(unsafe { slice::from_raw_parts_mut(ptr.cast(), size) }) 104 } 105 } 106 107 /// Same as alloc() but panics if `align > UPB_MALLOC_ALIGN`. 108 #[allow(clippy::mut_from_ref)] 109 #[inline] checked_alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]>110 pub fn checked_alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]> { 111 assert!(align <= UPB_MALLOC_ALIGN); 112 // SAFETY: align <= UPB_MALLOC_ALIGN asserted. 113 unsafe { self.alloc(size, align) } 114 } 115 116 /// Copies the T into this arena and returns a pointer to the T data inside 117 /// the arena. Returns None if the allocation failed. copy_in<'a, T: Copy>(&'a self, data: &T) -> Option<&'a T>118 pub fn copy_in<'a, T: Copy>(&'a self, data: &T) -> Option<&'a T> { 119 let size = size_of_val(data); 120 let align = align_of_val(data); 121 122 self.checked_alloc(size, align).map(|alloc| { 123 // SAFETY: 124 // - alloc is valid for `size` bytes and is the uninit bytes are written to not 125 // read from until written. 126 // - T is copy so copying the bytes of the value is sound. 127 unsafe { 128 let alloc = alloc.as_mut_ptr().cast::<MaybeUninit<T>>(); 129 &*(*alloc).write(*data) 130 } 131 }) 132 } 133 134 /// Copies the str into this arena and returns a pointer to the T data 135 /// inside the arena. Returns None if the allocation failed. copy_str_in<'a>(&'a self, s: &str) -> Option<&'a str>136 pub fn copy_str_in<'a>(&'a self, s: &str) -> Option<&'a str> { 137 self.copy_slice_in(s.as_bytes()).map(|copied_bytes| { 138 // SAFETY: `copied_bytes` has same contents as `s` and so must meet &str 139 // criteria. 140 unsafe { core::str::from_utf8_unchecked(copied_bytes) } 141 }) 142 } 143 144 /// Copies the slice into this arena and returns a pointer to the T data 145 /// inside the arena. Returns None if the allocation failed. copy_slice_in<'a, T: Copy>(&'a self, data: &[T]) -> Option<&'a [T]>146 pub fn copy_slice_in<'a, T: Copy>(&'a self, data: &[T]) -> Option<&'a [T]> { 147 let size = size_of_val(data); 148 let align = align_of_val(data); 149 self.checked_alloc(size, align).map(|alloc| { 150 let alloc: *mut T = alloc.as_mut_ptr().cast(); 151 // SAFETY: 152 // - uninit_alloc is valid for `layout.len()` bytes and is the uninit bytes are 153 // written to not read from until written. 154 // - T is copy so copying the bytes of the values is sound. 155 unsafe { 156 ptr::copy_nonoverlapping(data.as_ptr(), alloc, data.len()); 157 slice::from_raw_parts(alloc, data.len()) 158 } 159 }) 160 } 161 162 /// Fuse two arenas so they share the same lifetime. 163 /// 164 /// `fuse` will make it so that the memory allocated by `self` or `other` is 165 /// guaranteed to last until both `self` and `other` have been dropped. 166 /// The pointers returned by `Arena::alloc` will continue to be valid so 167 /// long as either `self` or `other` has not been dropped. fuse(&self, other: &Arena)168 pub fn fuse(&self, other: &Arena) { 169 // SAFETY: `self.raw()` and `other.raw()` are both valid UPB arenas. 170 let success = unsafe { upb_Arena_Fuse(self.raw(), other.raw()) }; 171 if !success { 172 // Fusing can fail if any of the arenas has an initial block i.e. the arena is 173 // backed by a preallocated chunk of memory that it doesn't own and thus cannot 174 // lifetime extend. This function panics because this is typically not a 175 // recoverable error but a logic bug in a program. 176 panic!("Could not fuse two UPB arenas."); 177 } 178 } 179 } 180 181 impl Default for Arena { default() -> Self182 fn default() -> Self { 183 Self::new() 184 } 185 } 186 187 impl Drop for Arena { 188 #[inline] drop(&mut self)189 fn drop(&mut self) { 190 unsafe { 191 upb_Arena_Free(self.raw); 192 } 193 } 194 } 195 196 extern "C" { 197 // `Option<NonNull<T: Sized>>` is ABI-compatible with `*mut T` upb_Arena_New() -> Option<RawArena>198 fn upb_Arena_New() -> Option<RawArena>; upb_Arena_Free(arena: RawArena)199 fn upb_Arena_Free(arena: RawArena); upb_Arena_Malloc(arena: RawArena, size: usize) -> *mut u8200 fn upb_Arena_Malloc(arena: RawArena, size: usize) -> *mut u8; upb_Arena_Fuse(arena1: RawArena, arena2: RawArena) -> bool201 fn upb_Arena_Fuse(arena1: RawArena, arena2: RawArena) -> bool; 202 } 203 204 #[cfg(test)] 205 mod tests { 206 use super::*; 207 use googletest::gtest; 208 209 #[gtest] assert_arena_linked()210 fn assert_arena_linked() { 211 use crate::assert_linked; 212 assert_linked!(upb_Arena_New); 213 assert_linked!(upb_Arena_Free); 214 assert_linked!(upb_Arena_Malloc); 215 assert_linked!(upb_Arena_Fuse); 216 } 217 218 #[gtest] raw_ffi_test()219 fn raw_ffi_test() { 220 // SAFETY: FFI unit test uses C API under expected patterns. 221 unsafe { 222 let arena = upb_Arena_New().unwrap(); 223 let bytes = upb_Arena_Malloc(arena, 3); 224 *bytes.add(2) = 7; 225 upb_Arena_Free(arena); 226 } 227 } 228 229 #[gtest] test_arena_new_and_free()230 fn test_arena_new_and_free() { 231 let arena = Arena::new(); 232 drop(arena); 233 } 234 } 235