Skip to content

Commit ebe1bf7

Browse files
authored
Add wasmi_collections::Stable{Arena,Vec}::get_mut_ptr method (#1992)
* add Stable{Arena,Vec}::get_mut_ptr method * add unit tests for new method
1 parent bd3732c commit ebe1bf7

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

crates/collections/src/arena/stable_arena.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use core::{
33
iter::{Enumerate, FusedIterator, repeat_with},
44
marker::PhantomData,
55
ops::{Index, IndexMut, Range},
6+
ptr::NonNull,
67
};
78

89
/// An append-only [`Arena`] whose entities have stable addresses.
@@ -155,6 +156,21 @@ where
155156
.ok_or(ArenaError::KeyOutOfBounds)
156157
}
157158

159+
/// Returns a raw pointer to the entity at the given key.
160+
///
161+
/// Unlike [`get_mut`](StableArena::get_mut), this never forms an intermediate `&mut T`, so the
162+
/// pointer carries the entity's underlying allocation provenance.
163+
///
164+
/// # Errors
165+
///
166+
/// If the `key` is out of bounds.
167+
#[inline]
168+
pub fn get_mut_ptr(&mut self, key: Key) -> Result<NonNull<T>, ArenaError> {
169+
self.items
170+
.get_mut_ptr(key.into_usize())
171+
.ok_or(ArenaError::KeyOutOfBounds)
172+
}
173+
158174
/// Returns exclusive references to the pair of entities at the given keys if any.
159175
///
160176
/// # Errors
@@ -406,6 +422,19 @@ mod tests {
406422
));
407423
}
408424

425+
#[test]
426+
fn get_mut_ptr_writes_through() {
427+
let mut arena: Arena = (0..10).collect();
428+
let ptr = arena.get_mut_ptr(3).unwrap();
429+
// Safety: key `3` is in bounds and uniquely owned here.
430+
unsafe { *ptr.as_ptr() = 30 };
431+
assert_eq!(arena.get(3).unwrap(), &30);
432+
assert!(matches!(
433+
arena.get_mut_ptr(10),
434+
Err(ArenaError::KeyOutOfBounds)
435+
));
436+
}
437+
409438
#[test]
410439
fn iter_yields_keys_and_is_double_ended() {
411440
let arena: Arena = (0..5).collect();

crates/collections/src/arena/stable_vec.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ impl<T> StableVec<T> {
121121
Some(unsafe { &mut *ptr.as_ptr().add(slot) })
122122
}
123123

124+
/// Returns a raw pointer to the item at `index`, or `None` if out of bounds.
125+
///
126+
/// Unlike [`get_mut`](StableVec::get_mut), this never forms an intermediate `&mut T`, so the
127+
/// pointer carries the bucket allocation's own provenance.
128+
#[inline]
129+
pub fn get_mut_ptr(&mut self, index: usize) -> Option<NonNull<T>> {
130+
if index >= self.len {
131+
return None;
132+
}
133+
let (bucket_index, slot) = Self::locate(index);
134+
// Safety: `index < len` implies the bucket is allocated and `slot` is initialized.
135+
let ptr = unsafe { self.buckets[bucket_index].unwrap_unchecked() };
136+
Some(unsafe { NonNull::new_unchecked(ptr.as_ptr().add(slot)) })
137+
}
138+
124139
/// Returns exclusive references to the items at `a` and `b`.
125140
///
126141
/// # Errors
@@ -482,6 +497,23 @@ mod tests {
482497
assert_eq!(vector.get_mut(100), None);
483498
}
484499

500+
#[test]
501+
fn get_mut_ptr_writes_through() {
502+
let mut vector = StableVec::new();
503+
for i in 0..100 {
504+
vector.push(i);
505+
}
506+
for i in 0..100 {
507+
let ptr = vector.get_mut_ptr(i).unwrap();
508+
// Safety: `i < len`, so the slot is initialized and uniquely owned here.
509+
unsafe { *ptr.as_ptr() *= 2 };
510+
}
511+
let collected: Vec<usize> = vector.iter().copied().collect();
512+
let expected: Vec<usize> = (0..100).map(|i| i * 2).collect();
513+
assert_eq!(collected, expected);
514+
assert!(vector.get_mut_ptr(100).is_none());
515+
}
516+
485517
#[test]
486518
fn iter_double_ended_and_exact_size() {
487519
let mut vector = StableVec::new();

0 commit comments

Comments
 (0)