fix: add safety comment to rle

This commit is contained in:
Zixuan Chen 2022-09-20 15:31:18 +08:00
parent 3ec2aed1a6
commit 9240ad12ee
9 changed files with 78 additions and 25 deletions

View file

@ -24,6 +24,7 @@ num = "0.4.0"
proptest = "1.0.0"
proptest-derive = "0.3.0"
rand = "0.8.5"
static_assertions = "1.1.0"
# See https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html
[lib]

View file

@ -17,3 +17,4 @@ ctor = "0.1.23"
proptest = "1.0.0"
smartstring = "1.0.1"
rand = "0.8.5"
static_assertions = "1.1.0"

View file

@ -20,6 +20,7 @@
//!
//!
#![allow(dead_code)]
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod range_map;
mod rle_trait;
pub mod rle_tree;

View file

@ -152,6 +152,8 @@ impl<Index: GlobalIndex, T: PartialEq + Eq> Mergable for WithStartEnd<Index, T>
#[cfg(test)]
mod test {
use std::ops::Range;
use super::*;
#[derive(Debug, PartialEq, Eq)]
struct V {
@ -205,4 +207,6 @@ mod test {
map.set(5, V::new(5, 20));
assert_eq!(map.get_range(9, 15), vec![&V::new(5, 20)]);
}
static_assertions::assert_not_impl_any!(RangeMap<usize, Range<usize>>: Sync, Send);
}

View file

@ -62,6 +62,9 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, 'bump,
}
}
/// # Safety
///
/// we need to make sure that the cursor is still valid
#[inline]
pub unsafe fn as_ref(&self) -> &'tree T {
self.leaf.as_ref().children[self.index]
@ -86,6 +89,9 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, 'bump,
}
}
/// # Safety
///
/// we need to make sure that the cursor is still valid
pub unsafe fn next(&self) -> Option<Self> {
let leaf = self.leaf.as_ref();
if leaf.children.len() > self.index + 1 {
@ -95,6 +101,9 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, 'bump,
leaf.next.map(|next| Self::new(next, 0, 0, Position::Start))
}
/// # Safety
///
/// we need to make sure that the cursor is still valid
pub unsafe fn prev(&self) -> Option<Self> {
let leaf = self.leaf.as_ref();
if self.index > 0 {
@ -109,6 +118,7 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, 'bump,
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsRef<T> for SafeCursor<'tree, 'bump, T, A> {
#[inline]
fn as_ref(&self) -> &'tree T {
// SAFETY: SafeCursor is a shared reference to the tree
unsafe { self.0.as_ref() }
}
}
@ -116,21 +126,25 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsRef<T> for SafeCursor<'t
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursor<'tree, 'bump, T, A> {
#[inline]
pub fn as_tree_ref(&self) -> &'tree T {
// SAFETY: SafeCursor is a shared reference to the tree
unsafe { self.0.as_ref() }
}
#[inline]
pub fn next(&self) -> Option<Self> {
// SAFETY: SafeCursor is a shared reference to the tree
unsafe { self.0.next().map(|x| Self(x)) }
}
#[inline]
pub fn prev(&self) -> Option<Self> {
// SAFETY: SafeCursor is a shared reference to the tree
unsafe { self.0.prev().map(|x| Self(x)) }
}
#[inline]
pub fn leaf(&self) -> &'tree LeafNode<'bump, T, A> {
// SAFETY: SafeCursor has shared reference lifetime to the tree
unsafe { self.0.leaf.as_ref() }
}
@ -170,6 +184,7 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsRef<T>
{
#[inline]
fn as_ref(&self) -> &T {
// SAFETY: SafeCursorMut is a exclusive reference to the tree
unsafe { self.0.as_ref() }
}
}
@ -177,11 +192,13 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsRef<T>
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump, T, A> {
#[inline]
pub fn as_ref_(&self) -> &'tree T {
// SAFETY: SafeCursorMut is a exclusive reference to the tree
unsafe { self.0.as_ref() }
}
#[inline]
pub fn leaf(&self) -> &'tree LeafNode<'bump, T, A> {
// SAFETY: SafeCursorMut is a exclusive reference to the tree
unsafe { self.0.leaf.as_ref() }
}
@ -204,19 +221,23 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
#[inline]
fn as_tree_mut(&mut self) -> &'tree mut T {
// SAFETY: SafeCursorMut is a exclusive reference to the tree
unsafe { self.0.as_mut() }
}
#[inline]
pub fn update_cache_recursively(&mut self) {
let leaf = unsafe { self.0.leaf.as_mut() };
A::update_cache_leaf(leaf);
let mut node = unsafe { leaf.parent.as_mut() };
loop {
A::update_cache_internal(node);
match node.parent {
Some(mut parent) => node = unsafe { parent.as_mut() },
None => return,
// SAFETY: SafeCursorMut is a exclusive reference to the tree
unsafe {
let leaf = self.0.leaf.as_mut();
A::update_cache_leaf(leaf);
let mut node = leaf.parent.as_mut();
loop {
A::update_cache_internal(node);
match node.parent {
Some(mut parent) => node = parent.as_mut(),
None => return,
}
}
}
}
@ -228,11 +249,15 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
#[inline]
pub fn next(&self) -> Option<Self> {
// SAFETY: SafeCursorMut is a exclusive reference to the tree so we are safe to
// get a reference to the element
unsafe { self.0.next().map(|x| Self(x)) }
}
#[inline]
pub fn prev(&self) -> Option<Self> {
// SAFETY: SafeCursorMut is a exclusive reference to the tree so we are safe to
// get a reference to the element
unsafe { self.0.prev().map(|x| Self(x)) }
}
@ -257,6 +282,8 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsMut<T>
{
#[inline]
fn as_mut(&mut self) -> &mut T {
// SAFETY: SafeCursorMut is a exclusive reference to the tree so we are safe to
// get a exclusive reference to the element
unsafe { self.0.as_mut() }
}
}

View file

@ -88,17 +88,23 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
#[inline]
pub(crate) fn parent_mut(&mut self) -> Option<&mut InternalNode<'a, T, A>> {
match self {
Node::Internal(node) => unsafe { node.parent.map(|mut x| x.as_mut()) },
Node::Leaf(node) => Some(unsafe { node.parent.as_mut() }),
// SAFETY: all tree data is pinned and can only be operating on single thread
unsafe {
match self {
Node::Internal(node) => node.parent.map(|mut x| x.as_mut()),
Node::Leaf(node) => Some(node.parent.as_mut()),
}
}
}
#[inline]
pub(crate) fn parent(&self) -> Option<&InternalNode<'a, T, A>> {
match self {
Node::Internal(node) => node.parent.map(|x| unsafe { x.as_ref() }),
Node::Leaf(node) => Some(unsafe { node.parent.as_ref() }),
// SAFETY: all tree data is pinned and can only be operating on single thread
unsafe {
match self {
Node::Internal(node) => node.parent.map(|x| x.as_ref()),
Node::Leaf(node) => Some(node.parent.as_ref()),
}
}
}

View file

@ -220,13 +220,16 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
let next = self.children[right_index]
.get_last_leaf()
.and_then(|x| x.next);
if let Some(mut prev) = prev {
let prev = unsafe { prev.as_mut() };
prev.next = next;
}
if let Some(mut next) = next {
let next = unsafe { next.as_mut() };
next.prev = prev;
// SAFETY: rle_tree is single threaded
unsafe {
if let Some(mut prev) = prev {
let prev = prev.as_mut();
prev.next = next;
}
if let Some(mut next) = next {
let next = next.as_mut();
next.prev = prev;
}
}
}
@ -372,12 +375,13 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
let mut any_delete: bool;
loop {
any_delete = false;
for (_, mut node_ptr) in zipper.iter() {
for (_, mut node_ptr) in zipper.iter_mut() {
if should_skip.contains(&node_ptr) {
continue;
}
let node = unsafe { node_ptr.as_mut() };
// SAFETY: node_ptr points to a valid descendant of self
let node: &mut Node<'a, T, A> = unsafe { node_ptr.as_mut() };
if let Some(node) = node.as_internal() {
let ptr = node as *const InternalNode<'a, T, A>;
if removed.contains(&ptr) {
@ -395,6 +399,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
if let Some((sibling, either)) = node.get_a_sibling() {
// if has sibling, borrow or merge to it
let sibling: &mut Node<'a, T, A> =
// SAFETY: node's sibling points to a valid descendant of self
unsafe { &mut *((sibling as *const _) as usize as *mut _) };
if node.children_num() + sibling.children_num() <= A::MAX_CHILDREN_NUM {
node.merge_to_sibling(sibling, either, notify);

View file

@ -42,6 +42,7 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
ans_inner.next = self.next;
ans_inner.prev = Some(NonNull::new(self).unwrap());
if let Some(mut next) = self.next {
// SAFETY: ans_inner is a valid pointer
unsafe { next.as_mut().prev = Some(NonNull::new_unchecked(ans_inner)) };
}
self.next = Some(NonNull::new(&mut *ans_inner).unwrap());
@ -98,10 +99,12 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
assert!(self.children.len() <= A::MAX_CHILDREN_NUM);
A::check_cache_leaf(self);
if let Some(next) = self.next {
// SAFETY: this is only for testing, and next must be a valid pointer
let self_ptr = unsafe { next.as_ref().prev.unwrap().as_ptr() };
assert!(std::ptr::eq(self, self_ptr));
}
if let Some(prev) = self.prev {
// SAFETY: this is only for testing, and prev must be a valid pointer
let self_ptr = unsafe { prev.as_ref().next.unwrap().as_ptr() };
assert!(std::ptr::eq(self, self_ptr));
}
@ -128,6 +131,7 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
}
pub fn is_deleted(&self) -> bool {
// SAFETY: we used bumpalo here, so even if current node is deleted we
unsafe {
let mut node = self.parent.as_ref();
if !node
@ -269,12 +273,14 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
#[inline]
pub fn next(&self) -> Option<&Self> {
self.next.map(|p| unsafe { p.as_ref() })
// SAFETY: internal variant ensure prev and next are valid reference
unsafe { self.next.map(|p| p.as_ref()) }
}
#[inline]
pub fn prev(&self) -> Option<&Self> {
self.prev.map(|p| unsafe { p.as_ref() })
// SAFETY: internal variant ensure prev and next are valid reference
unsafe { self.prev.map(|p| p.as_ref()) }
}
#[inline]

View file

@ -91,6 +91,7 @@ fn test(interactions: &[Interaction]) {
let mut range_map: RangeMap<u64, ValueIndex<'_>> = Default::default();
for interaction in interactions.iter() {
let mut func = |value: &Value, node: *mut LeafNode<'_, Value, ValueTreeTrait>| {
// SAFETY: this is safe because node must be valid
let ptr = unsafe { NonNull::new_unchecked(node as usize as *mut _) };
range_map.set(
value.value,
@ -126,6 +127,7 @@ fn test(interactions: &[Interaction]) {
origin_value,
range
);
// SAFETY: this is a test
assert!(!unsafe { origin_cursor.0.leaf.as_ref().is_deleted() });
let origin_leaf_ptr = origin_cursor.0.leaf.as_ptr() as usize;
let range_map_ptr = range_map_out.value.as_ptr() as usize;