chore(serde_v8): V8Slice SAFETY
@@ -41,12 +41,26 @@ impl V8Slice {
}
fn as_slice(&self) -> &[u8] {
+ // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
+ // it points to a fixed continuous slice of bytes on the heap.
+ // We assume it's initialized and thus safe to read (though may not contain meaningful data)
unsafe { &*(&self.store[self.range.clone()] as *const _ as *const [u8]) }
}
- #[allow(clippy::cast_ref_to_mut)]
fn as_slice_mut(&mut self) -> &mut [u8] {
- unsafe { &mut *(&self.store[self.range.clone()] as *const _ as *mut [u8]) }
+ // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
+ // it points to a fixed continuous slice of bytes on the heap.
+ // It's safe-ish to mutate concurrently because it can not be
+ // shrunk/grown/moved/reallocated, thus avoiding dangling refs (unlike a Vec).
+ // Concurrent writes can't lead to meaningful structural invalidation
+ // since we treat them as opaque buffers / "bags of bytes",
+ // concurrent mutation is simply an accepted fact of life.
+ // And in practice V8Slices also do not have overallping read/write phases.
+ // TLDR: permissive interior mutability on slices of bytes is "fine"
+ #[allow(clippy::cast_ref_to_mut)]
+ unsafe {
+ &mut *(&self.store[self.range.clone()] as *const _ as *mut [u8])
+ }
}
}
