Skip to content

Commit df9683e

Browse files
committed
Abstract away the source behind an unsafe trait
1 parent f3ae0fd commit df9683e

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

packages/std/src/imports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ impl Api for ExternalApi {
348348
}
349349

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

packages/std/src/memory.rs

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ pub fn alloc(size: usize) -> *mut Region {
3434
/// a pointer to the Region (preventing the memory from being freed until explicitly called later).
3535
///
3636
/// The resulting Region has capacity = length, the buffer capacity is shrunk down to its length.
37-
pub fn release_buffer(mut buffer: Vec<u8>) -> *mut Region {
38-
// Shrinking the buffer down to the length is important to uphold a safety invariant by the `dealloc` method.
39-
// Passing in a differing size into the `dealloc` layout is considered undefined behaviour.
40-
//
41-
// See: <https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html#safety-2>
42-
buffer.shrink_to_fit();
37+
pub fn release_buffer(buffer: Vec<u8>) -> *mut Region {
4338
let region = build_region(&buffer);
4439
mem::forget(buffer);
4540
Box::into_raw(region)
@@ -74,16 +69,59 @@ pub unsafe fn consume_region(ptr: *mut Region) -> Vec<u8> {
7469
)
7570
}
7671

72+
/// Element that can be used to construct a new `Box<Region>`
73+
///
74+
/// # Safety
75+
///
76+
/// The following invariant must be upheld:
77+
///
78+
/// - full allocated capacity == value returned by capacity
79+
///
80+
/// This is important to uphold the safety invariant of the `dealloc` method, which requires us to pass the same Layout
81+
/// into it as was used to allocate a memory region.
82+
///
83+
/// And since `size` is one of the parameters, it is important to pass in the exact same capacity.
84+
///
85+
/// See: <https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html#safety-2>
86+
pub unsafe trait RegionSource: AsRef<[u8]> {
87+
fn capacity(&self) -> usize;
88+
}
89+
90+
unsafe impl RegionSource for &[u8] {
91+
fn capacity(&self) -> usize {
92+
self.len()
93+
}
94+
}
95+
96+
unsafe impl RegionSource for Vec<u8> {
97+
fn capacity(&self) -> usize {
98+
self.capacity()
99+
}
100+
}
101+
102+
unsafe impl<T: ?Sized> RegionSource for &T
103+
where
104+
T: RegionSource,
105+
{
106+
fn capacity(&self) -> usize {
107+
(**self).capacity()
108+
}
109+
}
110+
77111
/// Returns a box of a Region, which can be sent over a call to extern
78112
/// note that this DOES NOT take ownership of the data, and we MUST NOT consume_region
79113
/// the resulting data.
80114
/// The Box must be dropped (with scope), but not the data
81-
pub fn build_region(data: &[u8]) -> Box<Region> {
82-
let data_ptr = data.as_ptr() as usize;
115+
pub fn build_region<S>(data: S) -> Box<Region>
116+
where
117+
S: RegionSource,
118+
{
119+
let data_slice = data.as_ref();
120+
let data_ptr = data_slice.as_ptr() as usize;
83121
build_region_from_components(
84122
u32::try_from(data_ptr).expect("pointer doesn't fit in u32"),
85-
u32::try_from(data.len()).expect("length doesn't fit in u32"),
86-
u32::try_from(data.len()).expect("length doesn't fit in u32"),
123+
u32::try_from(data.capacity()).expect("capacity doesn't fit in u32"),
124+
u32::try_from(data_slice.len()).expect("length doesn't fit in u32"),
87125
)
88126
}
89127

0 commit comments

Comments
 (0)