diff --git a/src/sealed.rs b/src/sealed.rs deleted file mode 100644 index a5f07c9..0000000 --- a/src/sealed.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! Collections that never leak references to their content, and therefore can be safely accessed via shared references. - -use std::borrow::Borrow; -use std::cell::UnsafeCell; -use std::collections::{HashSet, VecDeque, hash_set, vec_deque}; -use std::fmt; -use std::hash::Hash; - -/// FIFO queue that never leaks references to its content -pub struct Queue(UnsafeCell>); - -impl Queue { - pub fn new() -> Self { - Self(UnsafeCell::new(VecDeque::new())) - } - - pub fn with_capacity(capacity: usize) -> Self { - Self(UnsafeCell::new(VecDeque::with_capacity(capacity))) - } - - pub fn push(&self, item: T) { - let inner = unsafe { &mut *self.0.get() }; - inner.push_back(item); - } - - pub fn pop(&self) -> Option { - let inner = unsafe { &mut *self.0.get() }; - inner.pop_front() - } - - pub fn contains(&self, item: &T) -> bool - where - T: PartialEq, - { - let inner = unsafe { &*self.0.get() }; - inner.contains(item) - } - - pub fn remove_all(&self, item: &T) -> bool - where - T: PartialEq, - { - let inner = unsafe { &mut *self.0.get() }; - let initial_len = inner.len(); - inner.retain(|e| e != item); - inner.len() != initial_len - } - - pub fn remove_if(&mut self, mut pred: F) -> bool - where - F: FnMut(&T) -> bool, - { - let inner = self.0.get_mut(); - let initial_len = inner.len(); - inner.retain(|e| !pred(e)); - inner.len() != initial_len - } - - pub fn len(&self) -> usize { - let inner = unsafe { &*self.0.get() }; - inner.len() - } - - pub fn capacity(&self) -> usize { - let inner = unsafe { &*self.0.get() }; - inner.capacity() - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl fmt::Debug for Queue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = unsafe { &*self.0.get() }; - inner.fmt(f) - } -} - -impl Default for Queue { - fn default() -> Self { - Self::new() - } -} - -impl Clone for Queue { - fn clone(&self) -> Self { - let inner = unsafe { &*self.0.get() }; - Self(UnsafeCell::new(inner.clone())) - } -} - -impl IntoIterator for Queue { - type Item = T; - type IntoIter = vec_deque::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_inner().into_iter() - } -} - -/// Unordered set that never leaks references to its content -pub struct Set(UnsafeCell>); - -impl Set { - pub fn new() -> Self { - Self::default() - } - - pub fn with_capacity(capacity: usize) -> Self { - Self(UnsafeCell::new(HashSet::with_capacity(capacity))) - } - - pub fn contains(&self, value: &Q) -> bool - where - T: Borrow, - Q: ?Sized + Hash + Eq, - { - let inner = unsafe { &*self.0.get() }; - inner.contains(value) - } - - pub fn insert(&self, value: T) -> bool { - let inner = unsafe { &mut *self.0.get() }; - inner.insert(value) - } - - pub fn remove(&self, value: &Q) -> bool - where - T: Borrow, - Q: ?Sized + Hash + Eq, - { - let inner = unsafe { &mut *self.0.get() }; - inner.remove(value) - } - - pub fn clear(&self) { - let inner = unsafe { &mut *self.0.get() }; - inner.clear(); - } - - pub fn len(&self) -> usize { - let inner = unsafe { &*self.0.get() }; - inner.len() - } - - pub fn is_empty(&self) -> bool { - let inner = unsafe { &*self.0.get() }; - inner.is_empty() - } -} - -impl fmt::Debug for Set { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = unsafe { &*self.0.get() }; - inner.fmt(f) - } -} - -impl Default for Set { - fn default() -> Self { - Self(Default::default()) - } -} - -impl Clone for Set { - fn clone(&self) -> Self { - let inner = unsafe { &*self.0.get() }; - Self(UnsafeCell::new(inner.clone())) - } -} - -impl IntoIterator for Set { - type Item = T; - type IntoIter = hash_set::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_inner().into_iter() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use static_assertions::{assert_impl_all, assert_not_impl_any}; - use std::{rc::Rc, sync::Arc}; - - #[test] - fn test_queue_is_send_but_not_sync() { - assert_impl_all!(Queue: std::marker::Send); - assert_not_impl_any!(Queue>: std::marker::Send); - assert_not_impl_any!(Queue>: Sync); - assert_not_impl_any!(Arc>: std::marker::Send, Sync); - } - - #[test] - fn test_set_is_send_but_not_sync() { - assert_impl_all!(Set: std::marker::Send); - assert_not_impl_any!(Set>: std::marker::Send); - assert_not_impl_any!(Set>: Sync); - assert_not_impl_any!(Arc>: std::marker::Send, Sync); - } -} diff --git a/src/sealed/mod.rs b/src/sealed/mod.rs new file mode 100644 index 0000000..5a27967 --- /dev/null +++ b/src/sealed/mod.rs @@ -0,0 +1,8 @@ +//! Collections that never leak references to their content, and therefore can be safely accessed via shared references. + +mod queue; +mod set; +mod utils; + +pub use queue::Queue; +pub use set::Set; diff --git a/src/sealed/queue.rs b/src/sealed/queue.rs new file mode 100644 index 0000000..3f6139a --- /dev/null +++ b/src/sealed/queue.rs @@ -0,0 +1,121 @@ +use super::utils::UnsafeWrapper; +use std::collections::{VecDeque, vec_deque}; +use std::fmt; + +/// FIFO queue that never leaks references to its content +pub struct Queue(UnsafeWrapper>); + +impl Queue { + pub fn new() -> Self { + Self(UnsafeWrapper::new(VecDeque::new())) + } + + pub fn with_capacity(capacity: usize) -> Self { + Self(UnsafeWrapper::new(VecDeque::with_capacity(capacity))) + } + + pub fn push(&self, item: T) { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.push_back(item)) } + } + + pub fn pop(&self) -> Option { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.pop_front()) } + } + + pub fn contains(&self, item: &T) -> bool + where + T: PartialEq, + { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.contains(item)) } + } + + pub fn remove_all(&self, item: &T) -> bool + where + T: PartialEq, + { + // SAFETY: `with()` is never invoked recursively + unsafe { + self.0.with(|inner| { + let initial_len = inner.len(); + inner.retain(|e| e != item); + inner.len() != initial_len + }) + } + } + + pub fn clear(&self) { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.clear()) } + } + + pub fn len(&self) -> usize { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.len()) } + } + + pub fn capacity(&self) -> usize { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.capacity()) } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn into_inner(self) -> VecDeque { + self.0.into_inner() + } +} + +impl From> for Queue { + fn from(vec_deque: VecDeque) -> Self { + Self(UnsafeWrapper::new(vec_deque)) + } +} + +impl fmt::Debug for Queue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.fmt(f)) } + } +} + +impl Default for Queue { + fn default() -> Self { + Self::new() + } +} + +impl Clone for Queue { + fn clone(&self) -> Self { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| Self(UnsafeWrapper::new(inner.clone()))) } + } +} + +impl IntoIterator for Queue { + type Item = T; + type IntoIter = vec_deque::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_inner().into_iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + use std::{rc::Rc, sync::Arc}; + + #[test] + fn test_queue_is_send_but_not_sync() { + assert_impl_all!(Queue: std::marker::Send); + assert_not_impl_any!(Queue>: std::marker::Send); + assert_not_impl_any!(Queue>: Sync); + assert_not_impl_any!(Arc>: std::marker::Send, Sync); + } +} diff --git a/src/sealed/set.rs b/src/sealed/set.rs new file mode 100644 index 0000000..5598a44 --- /dev/null +++ b/src/sealed/set.rs @@ -0,0 +1,115 @@ +use super::utils::UnsafeWrapper; +use std::borrow::Borrow; +use std::collections::{HashSet, hash_set}; +use std::fmt; +use std::hash::Hash; + +/// Unordered set that never leaks references to its content +pub struct Set(UnsafeWrapper>); + +impl Set { + pub fn new() -> Self { + Self::default() + } + + pub fn with_capacity(capacity: usize) -> Self { + Self(UnsafeWrapper::new(HashSet::with_capacity(capacity))) + } + + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Hash + Eq, + { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.contains(value)) } + } + + pub fn insert(&self, value: T) -> bool { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.insert(value)) } + } + + pub fn remove(&self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Hash + Eq, + { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.remove(value)) } + } + + pub fn clear(&self) { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.clear()) } + } + + pub fn len(&self) -> usize { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.len()) } + } + + pub fn capacity(&self) -> usize { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.capacity()) } + } + + pub fn is_empty(&self) -> bool { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.is_empty()) } + } + + pub fn into_inner(self) -> HashSet { + self.0.into_inner() + } +} + +impl From> for Set { + fn from(hash_set: HashSet) -> Self { + Self(UnsafeWrapper::new(hash_set)) + } +} + +impl fmt::Debug for Set { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| inner.fmt(f)) } + } +} + +impl Default for Set { + fn default() -> Self { + Self(UnsafeWrapper::new(HashSet::default())) + } +} + +impl Clone for Set { + fn clone(&self) -> Self { + // SAFETY: `with()` is never invoked recursively + unsafe { self.0.with(|inner| Self(UnsafeWrapper::new(inner.clone()))) } + } +} + +impl IntoIterator for Set { + type Item = T; + type IntoIter = hash_set::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_inner().into_iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + use std::{rc::Rc, sync::Arc}; + + #[test] + fn test_set_is_send_but_not_sync() { + assert_impl_all!(Set: std::marker::Send); + assert_not_impl_any!(Set>: std::marker::Send); + assert_not_impl_any!(Set>: Sync); + assert_not_impl_any!(Arc>: std::marker::Send, Sync); + } +} diff --git a/src/sealed/utils.rs b/src/sealed/utils.rs new file mode 100644 index 0000000..3dbdedb --- /dev/null +++ b/src/sealed/utils.rs @@ -0,0 +1,24 @@ +use std::cell::UnsafeCell; + +/// A (hopefully) zero-cost wrapper that simplifies working with unsafe code. +pub struct UnsafeWrapper(UnsafeCell); + +impl UnsafeWrapper { + pub fn new(inner: T) -> Self { + Self(UnsafeCell::new(inner)) + } + + /// # Safety + /// Calls to `with()` can't be nested. + #[inline(always)] + pub unsafe fn with(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + f(unsafe { &mut *self.0.get() }) + } + + pub fn into_inner(self) -> T { + self.0.into_inner() + } +}