use core::{fmt::Debug, ptr, mem, num::NonZeroUsize, hint};
use super::{ListStorage, MoveFix};
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "alloc")))]
pub type Vec<T> = SparseStorage<T, alloc::vec::Vec<Slot<T>>>;
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "alloc")))]
pub type VecDeque<T> = SparseStorage<T, alloc::collections::VecDeque<Slot<T>>>;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct SparseStorage<E, S>
where
S: ListStorage<Element = Slot<E>>,
{
storage: S,
hole_list: Option<(NonZeroUsize, usize, usize)>,
}
impl<E, S> SparseStorage<E, S>
where
S: ListStorage<Element = Slot<E>>,
{
pub fn defragment(&mut self) {
self.defragment_impl(|_, _, _| {});
}
pub fn defragment_and_fix(&mut self)
where
E: MoveFix,
{
self.defragment_impl(|s, i, j| {
unsafe {
E::fix_move(s, i, j);
}
});
}
fn defragment_impl(&mut self, mut f: impl FnMut(&mut Self, usize, usize)) {
let hole_info = if let Some(val) = self.hole_list {
val
} else {
return;
};
for i in 0..self.len() {
let element = unsafe {
self.storage.get_unchecked_mut(i)
};
let element_is_hole = element.is_hole();
let element = element as *mut _;
if element_is_hole {
'j: for j in (0..self.len()).rev() {
if i == j {
break 'j;
}
let other_element = unsafe {
self.storage.get_unchecked_mut(j)
};
if other_element.is_element() {
unsafe {
ptr::swap_nonoverlapping(element, other_element as *mut _, 1);
}
f(self, j, i);
}
}
}
}
for (_, _) in (0..self.len()).rev().zip(0..hole_info.0.get()) {
self.storage.pop();
}
self.hole_list = None;
}
pub fn into_inner(self) -> S {
self.storage
}
pub fn num_holes(&self) -> usize {
self.hole_list.map_or(0, |x| x.0.get())
}
pub fn is_dense(&self) -> bool {
self.num_holes() == 0
}
unsafe fn punch_hole(&mut self, index: usize) -> Option<E> {
let element = {
self.storage.get_unchecked_mut(index)
};
element.punch_hole(None).map(move |val| {
if let Some(hole_info) = &mut self.hole_list {
hole_info.0 = {
let mut raw = hole_info.0.get();
raw += 1;
NonZeroUsize::new_unchecked(raw)
};
let old_end = {
self.storage.get_unchecked_mut(hole_info.2)
};
{
old_end.set_hole_link(Some(index));
}
hole_info.2 = index;
} else {
self.hole_list = Some((
{
NonZeroUsize::new_unchecked(1)
}, index, index, ));
}
val
})
}
}
static HOLE_PANIC_MSG: &str = "\
the element at the specified index was a hole in the sparse storage";
unsafe impl<E, S> ListStorage for SparseStorage<E, S>
where
S: ListStorage<Element = Slot<E>>,
{
type Element = E;
fn with_capacity(capacity: usize) -> Self {
Self {
storage: S::with_capacity(capacity),
hole_list: None,
}
}
fn insert(&mut self, index: usize, element: Self::Element) {
self.storage.insert(index, Slot::new_element(element))
}
fn remove(&mut self, index: usize) -> Self::Element {
if self.is_dense() {
self.storage.remove(index).unwrap()
} else {
unimplemented!(
"\
cannot perform raw removal from sparse storage without defragmenting, use remove_and_shiftfix or \
defragment before doing this"
)
}
}
fn len(&self) -> usize {
self.storage.len()
}
unsafe fn get_unchecked(&self, index: usize) -> &Self::Element {
self.storage
.get_unchecked(index)
.element_checked()
.expect(HOLE_PANIC_MSG)
}
unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut Self::Element {
self.storage
.get_unchecked_mut(index)
.element_checked_mut()
.expect(HOLE_PANIC_MSG)
}
#[track_caller]
fn get(&self, index: usize) -> Option<&Self::Element> {
match self.storage.get(index) {
Some(x) if x.is_element() => unsafe {
Some(x.element())
},
Some(..) => panic!("{}", HOLE_PANIC_MSG),
None => None,
}
}
#[track_caller]
fn get_mut(&mut self, index: usize) -> Option<&mut Self::Element> {
match self.storage.get_mut(index) {
Some(x) if x.is_element() => unsafe {
Some(x.element_mut())
},
Some(..) => panic!("{}", HOLE_PANIC_MSG),
None => None,
}
}
fn new() -> Self {
Self {
storage: S::new(),
hole_list: None,
}
}
fn push(&mut self, element: Self::Element) {
self.storage.push(Slot::new_element(element))
}
fn pop(&mut self) -> Option<Self::Element> {
if self.is_dense() {
self.storage.pop().map(Slot::unwrap)
} else {
unimplemented!(
"\
cannot perform raw removal from sparse storage without defragmenting, use remove_and_shiftfix or \
defragment before doing this"
)
}
}
fn capacity(&self) -> usize {
self.storage.capacity()
}
fn reserve(&mut self, additional: usize) {
self.storage.reserve(additional)
}
fn shrink_to_fit(&mut self) {
self.storage.shrink_to_fit()
}
fn truncate(&mut self, len: usize) {
self.storage.truncate(len)
}
fn insert_and_shiftfix(&mut self, index: usize, element: Self::Element)
where
Self::Element: MoveFix,
{
self.insert(index, element);
unsafe {
Self::Element::fix_right_shift(self, index, super::U_ONE);
}
}
#[track_caller]
fn remove_and_shiftfix(&mut self, index: usize) -> Self::Element
where
Self::Element: MoveFix,
{
assert!(self.len() > index, "index out of bounds");
unsafe {
self.punch_hole(index)
}
.expect(HOLE_PANIC_MSG)
}
#[allow(clippy::option_if_let_else)] fn add(&mut self, element: Self::Element) -> usize {
if let Some(hole_info) = &mut self.hole_list {
let new_hole_count = NonZeroUsize::new(hole_info.0.get() - 1);
let used_hole_index = hole_info.1;
let hole = unsafe {
self.storage.get_unchecked_mut(used_hole_index)
};
let next_hole = unsafe {
hole.hole_link()
};
*hole = Slot::new_element(element);
if let Some(new_hole_count) = new_hole_count {
hole_info.0 = new_hole_count;
hole_info.1 = next_hole.unwrap_or_else(|| unsafe {
hint::unreachable_unchecked()
});
} else {
self.hole_list = None;
}
used_hole_index
} else {
self.push(element);
self.len() - 1
}
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct Slot<T>(SlotInner<T>);
impl<T> Slot<T> {
const fn new_element(val: T) -> Self {
Self(SlotInner::new_element(val))
}
const fn is_element(&self) -> bool {
self.0.is_element()
}
const fn is_hole(&self) -> bool {
self.0.is_hole()
}
unsafe fn element(&self) -> &T {
self.0.element()
}
fn element_checked(&self) -> Option<&T> {
if self.is_element() {
unsafe {
Some(self.element())
}
} else {
None
}
}
unsafe fn element_mut(&mut self) -> &mut T {
self.0.element_mut()
}
fn element_checked_mut(&mut self) -> Option<&mut T> {
if self.is_element() {
unsafe {
Some(self.element_mut())
}
} else {
None
}
}
unsafe fn hole_link(&self) -> Option<usize> {
self.0.hole_link()
}
unsafe fn set_hole_link(&mut self, val: Option<usize>) {
self.0.set_hole_link(val)
}
#[track_caller]
fn unwrap(self) -> T {
if self.is_element() {
let element_owned = unsafe {
ptr::read(self.element())
};
mem::forget(self);
element_owned
} else {
panic!("{}", HOLE_PANIC_MSG)
}
}
fn punch_hole(&mut self, next: Option<usize>) -> Option<T> {
self.0.punch_hole(next)
}
}
#[cfg(feature = "union_optimizations")]
type SlotInner<T> = SlotUnionBased<T>;
#[cfg(not(feature = "union_optimizations"))]
type SlotInner<T> = SlotEnumBased<T>;
#[cfg(feature = "union_optimizations")]
struct SlotUnionBased<T> {
discrim: u8,
data: SlotUnion<T>,
}
#[cfg(feature = "union_optimizations")]
impl<T> SlotUnionBased<T> {
const HOLE_DISCRIM_BIT: u8 = 0b0000_0000;
const ELEMENT_DISCRIM_BIT: u8 = 0b0000_0001;
const UNION_DISCRIM_MASK: u8 = 0b0000_0001;
const LINK_DISCRIM_MASK: u8 = 0b0000_0010;
const fn new_element(val: T) -> Self {
Self {
discrim: Self::ELEMENT_DISCRIM_BIT,
data: SlotUnion {
element: mem::ManuallyDrop::new(val),
},
}
}
#[allow(clippy::manual_unwrap_or)] const fn new_hole(val: Option<usize>) -> Self {
Self {
discrim: Self::HOLE_DISCRIM_BIT | ((matches!(val, Some(..)) as u8) << 1),
data: SlotUnion {
hole_link: match val {
Some(val) => val,
None => 0,
},
},
}
}
const fn is_element(&self) -> bool {
self.discrim & Self::UNION_DISCRIM_MASK == Self::ELEMENT_DISCRIM_BIT
}
const fn is_hole(&self) -> bool {
self.discrim & Self::UNION_DISCRIM_MASK == Self::HOLE_DISCRIM_BIT
}
unsafe fn element(&self) -> &T {
&self.data.element
}
unsafe fn element_mut(&mut self) -> &mut T {
&mut self.data.element
}
unsafe fn hole_link(&self) -> Option<usize> {
#[allow(clippy::if_not_else)] if self.discrim & Self::LINK_DISCRIM_MASK != 0 {
Some(self.data.hole_link)
} else {
None
}
}
unsafe fn set_hole_link(&mut self, val: Option<usize>) {
let link_bit = (val.is_some() as u8) << 1;
self.discrim = (self.discrim & Self::UNION_DISCRIM_MASK) | link_bit;
self.data.hole_link = val.unwrap_or_default(); }
fn punch_hole(&mut self, next: Option<usize>) -> Option<T> {
match self.discrim & Self::UNION_DISCRIM_MASK {
Self::ELEMENT_DISCRIM_BIT => {
let val_owned = unsafe {
ptr::read(&self.data.element)
};
unsafe {
ptr::write(self, Self::new_hole(next));
}
Some(mem::ManuallyDrop::into_inner(val_owned))
}
Self::HOLE_DISCRIM_BIT => None,
_ => unsafe {
hint::unreachable_unchecked()
},
}
}
}
#[cfg(feature = "union_optimizations")]
impl<T: Debug> Debug for SlotUnionBased<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.is_element() {
let element_ref = unsafe {
self.element()
};
f.debug_tuple("Element").field(element_ref).finish()
} else {
let hole_link = unsafe {
self.hole_link()
};
f.debug_tuple("Hole").field(&hole_link).finish()
}
}
}
#[cfg(feature = "union_optimizations")]
impl<T> Drop for SlotUnionBased<T> {
fn drop(&mut self) {
if self.is_element() {
unsafe {
ptr::drop_in_place(self.element_mut());
}
}
}
}
#[cfg(feature = "union_optimizations")]
union SlotUnion<T> {
hole_link: usize,
element: mem::ManuallyDrop<T>,
}
#[cfg(not(feature = "union_optimizations"))]
#[derive(Debug, Hash)]
enum SlotEnumBased<T> {
Element(T),
Hole(Option<usize>),
}
#[cfg(not(feature = "union_optimizations"))]
impl<T> SlotEnumBased<T> {
const fn new_element(val: T) -> Self {
Self::Element(val)
}
const fn is_element(&self) -> bool {
matches!(self, Self::Element(..))
}
const fn is_hole(&self) -> bool {
matches!(self, Self::Hole(..))
}
#[allow(clippy::missing_const_for_fn)] unsafe fn element(&self) -> &T {
match self {
Self::Element(x) => x,
Self::Hole(..) => hint::unreachable_unchecked(),
}
}
unsafe fn element_mut(&mut self) -> &mut T {
match self {
Self::Element(x) => x,
Self::Hole(..) => hint::unreachable_unchecked(),
}
}
#[allow(clippy::missing_const_for_fn)] unsafe fn hole_link(&self) -> Option<usize> {
match self {
Self::Hole(x) => *x,
Self::Element(..) => hint::unreachable_unchecked(),
}
}
unsafe fn set_hole_link(&mut self, val: Option<usize>) {
match self {
Self::Hole(x) => {
*x = val;
}
Self::Element(..) => hint::unreachable_unchecked(),
}
}
fn punch_hole(&mut self, next: Option<usize>) -> Option<T> {
match self {
Self::Element(val) => {
let val_owned = unsafe {
ptr::read(val)
};
unsafe {
ptr::write(self, Self::Hole(next));
}
Some(val_owned)
}
Self::Hole(..) => None,
}
}
}