Skip to content

Commit

Permalink
Abstract away the source behind an unsafe trait
Browse files Browse the repository at this point in the history
  • Loading branch information
aumetra committed Mar 20, 2024
1 parent f3ae0fd commit df9683e
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
2 changes: 1 addition & 1 deletion packages/std/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ impl Api for ExternalApi {
}

fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
let send = build_region(&canonical);
let send = build_region(canonical.as_slice());
let send_ptr = &*send as *const Region as u32;
let human = alloc(HUMAN_ADDRESS_BUFFER_LENGTH);

Expand Down
58 changes: 48 additions & 10 deletions packages/std/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,7 @@ pub fn alloc(size: usize) -> *mut Region {
/// a pointer to the Region (preventing the memory from being freed until explicitly called later).
///
/// The resulting Region has capacity = length, the buffer capacity is shrunk down to its length.
pub fn release_buffer(mut buffer: Vec<u8>) -> *mut Region {
// Shrinking the buffer down to the length is important to uphold a safety invariant by the `dealloc` method.
// Passing in a differing size into the `dealloc` layout is considered undefined behaviour.
//
// See: <https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html#safety-2>
buffer.shrink_to_fit();
pub fn release_buffer(buffer: Vec<u8>) -> *mut Region {
let region = build_region(&buffer);
mem::forget(buffer);
Box::into_raw(region)
Expand Down Expand Up @@ -74,16 +69,59 @@ pub unsafe fn consume_region(ptr: *mut Region) -> Vec<u8> {
)
}

/// Element that can be used to construct a new `Box<Region>`
///
/// # Safety
///
/// The following invariant must be upheld:
///
/// - full allocated capacity == value returned by capacity
///
/// This is important to uphold the safety invariant of the `dealloc` method, which requires us to pass the same Layout
/// into it as was used to allocate a memory region.
///
/// And since `size` is one of the parameters, it is important to pass in the exact same capacity.
///
/// See: <https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html#safety-2>
pub unsafe trait RegionSource: AsRef<[u8]> {
fn capacity(&self) -> usize;
}

unsafe impl RegionSource for &[u8] {
fn capacity(&self) -> usize {
self.len()
}
}

unsafe impl RegionSource for Vec<u8> {
fn capacity(&self) -> usize {
self.capacity()
}
}

unsafe impl<T: ?Sized> RegionSource for &T
where
T: RegionSource,
{
fn capacity(&self) -> usize {
(**self).capacity()
}
}

/// Returns a box of a Region, which can be sent over a call to extern
/// note that this DOES NOT take ownership of the data, and we MUST NOT consume_region
/// the resulting data.
/// The Box must be dropped (with scope), but not the data
pub fn build_region(data: &[u8]) -> Box<Region> {
let data_ptr = data.as_ptr() as usize;
pub fn build_region<S>(data: S) -> Box<Region>
where
S: RegionSource,
{
let data_slice = data.as_ref();
let data_ptr = data_slice.as_ptr() as usize;
build_region_from_components(
u32::try_from(data_ptr).expect("pointer doesn't fit in u32"),
u32::try_from(data.len()).expect("length doesn't fit in u32"),
u32::try_from(data.len()).expect("length doesn't fit in u32"),
u32::try_from(data.capacity()).expect("capacity doesn't fit in u32"),
u32::try_from(data_slice.len()).expect("length doesn't fit in u32"),
)
}

Expand Down

0 comments on commit df9683e

Please sign in to comment.