diff options
-rw-r--r-- | serde_v8/magic/v8slice.rs | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/serde_v8/magic/v8slice.rs b/serde_v8/magic/v8slice.rs index d4101847e..6950e29d9 100644 --- a/serde_v8/magic/v8slice.rs +++ b/serde_v8/magic/v8slice.rs @@ -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]) + } } } |