use core::ops::ControlFlow;
use core::ops::RangeFull;
use core::sync::atomic::Ordering;
#[cfg_attr(not(doc), expect(unused))]
use crate::ConcurrentMap;
use crate::Key;
#[cfg_attr(not(doc), expect(unused))]
use crate::SequentialMap;
use crate::concurrent::Shard;
use crate::concurrent::Smr;
use crate::concurrent::Value;
use crate::concurrent::iter;
use crate::concurrent::smr;
use crate::concurrent::smr::Guard as _;
use crate::concurrent::value;
use crate::raw::Cursor;
use crate::raw::Edge;
use crate::raw::cursor;
use crate::raw::cursor::Path;
use crate::raw::cursor::path;
use crate::raw::edge::Meta as _;
use crate::raw::key::Len as _;
use crate::sequential;
use crate::stat;
pub type Guard<'g, K, V, S> = <S as Smr<K, V>>::Guard<'g>;
pub type Owned<'g, K, V, S> = value::Owned<Guard<'g, K, V, S>, V>;
pub type Shared<'g, K, V, S> = value::Shared<Guard<'g, K, V, S>, V>;
pub type Updated<'g, K, V, S> = value::Updated<Guard<'g, K, V, S>, V>;
pub type Upserted<'g, K, V, S> = value::Upserted<Guard<'g, K, V, S>, V>;
pub struct Map<K: Key, V: Value, S = smr::Default> {
smr: S,
seq: sequential::Map<K, V>,
}
impl<K: Key, V: Value, S: Default> Default for Map<K, V, S> {
fn default() -> Self {
Self::new()
}
}
impl<K: Key, V: Value, S: Default> Map<K, V, S> {
pub fn new() -> Self {
Self::with_smr(S::default())
}
}
impl<K: Key, V: Value, S> Map<K, V, S> {
pub const fn with_smr(smr: S) -> Self {
Self {
smr,
seq: sequential::Map::<K, V>::new(),
}
}
}
impl<K: Key, V: Value, S: Smr<K, V>> Map<K, V, S> {
#[inline]
pub fn as_sequential(&mut self) -> &mut sequential::Map<K, V> {
&mut self.seq
}
#[inline]
pub fn smr(&self) -> &S {
&self.smr
}
#[inline]
pub fn smr_mut(&mut self) -> &mut S {
&mut self.smr
}
}
impl<K: Key, V: Value, S: Smr<K, V>> Map<K, V, S> {
pub fn contains_key(&self, key: &K::Borrowed) -> bool {
let reader = K::Read::from(key);
let mut guard = self.smr.guard(reader);
unsafe { self.get_raw(&mut guard, reader) }.is_some()
}
pub fn get<'g>(&'g self, key: &K::Borrowed) -> Option<Shared<'g, K, V, S>> {
let reader = K::Read::from(key);
let mut guard = self.smr.guard(reader);
let value = unsafe { self.get_raw(&mut guard, reader)? };
Some(unsafe { Shared::<'_, K, V, S>::wrap(guard, value) })
}
#[expect(clippy::type_complexity)]
pub fn insert<'g, 'k>(
&'g self,
key: K::Insert<'k>,
value: V,
) -> Result<Shared<'g, K, V, S>, (Shared<'g, K, V, S>, V)> {
let mut value = Some(value);
self.insert_with(key, || value.take().expect("Call thunk once"))
.map_err(|(shared, initial)| {
(
shared,
value
.xor(initial)
.expect("Value must be in thunk or initial"),
)
})
}
pub fn upsert<'k>(&self, key: K::Insert<'k>, value: V) -> Upserted<'_, K, V, S> {
match self.upsert_with(key, Some(value), |_, new| {
ControlFlow::<(), _>::Continue(new.take().expect("Value is always initialized"))
}) {
Upsert::Success(upserted) => upserted,
Upsert::Break { .. } => unreachable!(),
}
}
pub fn update<'g>(&'g self, key: &K::Borrowed, value: V) -> Result<Updated<'g, K, V, S>, V> {
match self.update_with(key, Some(value), |_, initial| {
ControlFlow::<(), _>::Continue(initial.take().expect("Value is always initialized"))
}) {
Update::Absent { new: Some(initial) } => Err(initial),
Update::Success(updated) => Ok(updated),
Update::Absent { new: None } | Update::Break { .. } => unreachable!(),
}
}
pub fn remove<'g>(&'g self, key: &K::Borrowed) -> Option<Owned<'g, K, V, S>> {
match self.remove_with(key, |_| ControlFlow::Continue(())) {
Remove::Absent => None,
Remove::Success { old } => Some(old),
Remove::Break { old: _ } => unreachable!(),
}
}
pub fn remove_non_recursive(&self, key: &K::Borrowed) -> Option<Owned<'_, K, V, S>> {
match self.remove_non_recursive_with(key, |_| ControlFlow::Continue(())) {
Remove::Absent => None,
Remove::Success { old } => Some(old),
Remove::Break { old: _ } => unreachable!(),
}
}
}
impl<K, V, S> Map<K, V, S>
where
K: Key,
V: Value,
S: Smr<K, V>,
{
pub fn all(&self) -> iter::Shard<'_, 'static, K, V, RangeFull, Guard<'_, K, V, S>> {
let guard = self.smr.guard(K::Read::default());
unsafe { Shard::new(guard, self.seq.raw.all()) }
}
pub fn prefix<'g, 'k>(
&'g self,
prefix: impl Into<K::Read<'k>>,
) -> iter::Shard<'g, 'k, K, V, RangeFull, Guard<'g, K, V, S>> {
let prefix = prefix.into();
let guard = self.smr.guard(prefix);
unsafe { Shard::new(guard, self.seq.raw.prefix(prefix)) }
}
pub fn range<'g, 'k, R>(&'g self, range: R) -> iter::Shard<'g, 'k, K, V, R, Guard<'g, K, V, S>>
where
R: crate::raw::iter::Range<K::Read<'k>>,
{
let prefix = range.common_prefix();
let guard = self.smr.guard(prefix);
unsafe { Shard::new(guard, self.seq.raw.range(range, prefix)) }
}
}
impl<K, V, S> Map<K, V, S>
where
K: Key,
V: Value,
S: Smr<K, V>,
{
#[expect(clippy::type_complexity)]
pub fn insert_with<'g, 'k, F>(
&'g self,
key: K::Insert<'k>,
insert: F,
) -> Result<Shared<'g, K, V, S>, (Shared<'g, K, V, S>, Option<V>)>
where
F: FnOnce() -> V,
{
let mut thunk = Some(insert);
match self.upsert_with(key, None, |old, new| match old {
None => ControlFlow::Continue(match new.take() {
None => (thunk.take().expect("Call thunk once"))(),
Some(new) => new,
}),
Some(_) => ControlFlow::Break(()),
}) {
Upsert::Success(upserted) => Ok(upserted
.try_into_inserted()
.unwrap_or_else(|_| unreachable!("Continue on `None`"))),
Upsert::Break { old, new } => Err((old.expect("Break on `Some`"), new)),
}
}
pub fn upsert_with<'g, 'k, F>(
&'g self,
key: K::Insert<'k>,
mut initial: Option<V>,
mut upsert: F,
) -> Upsert<'g, K, V, S>
where
F: FnMut(Option<&V::Borrowed>, &mut Option<V>) -> ControlFlow<(), V>,
{
let reader = K::insert_as_read(key);
let mut guard = self.smr.guard(reader);
macro_rules! upsert {
() => {
|old: Option<u64>, new: Option<u64>| {
initial = new.map(|new| V::from_raw_unchecked(new));
match upsert(
old.as_ref().map(|old| V::borrow_from_raw_unchecked(old)),
&mut initial,
) {
ControlFlow::Continue(new) => ControlFlow::Continue(new.into_raw()),
ControlFlow::Break(()) => ControlFlow::Break(()),
}
}
};
}
let upsert = match if cfg!(feature = "opt-no-path") {
Err(initial.take().map(V::into_raw))
} else {
unsafe {
self.upsert_with_optimistic(
&mut guard,
reader,
initial.take().map(V::into_raw),
upsert!(),
)
}
} {
Ok(upsert) => upsert,
Err(initial) => unsafe {
self.upsert_with_pessimistic(&mut guard, reader, initial, upsert!())
},
};
match upsert {
UpsertRaw::Success { old, new } => {
Upsert::Success(unsafe { Upserted::<K, V, S>::wrap(guard, old, new) })
}
UpsertRaw::Break { old } => Upsert::Break {
old: old.map(|old| unsafe { Shared::<K, V, S>::wrap(guard, old) }),
new: initial,
},
}
}
pub fn update_with<'g, F>(
&'g self,
key: &K::Borrowed,
mut initial: Option<V>,
mut update: F,
) -> Update<'g, K, V, S>
where
F: FnMut(&V::Borrowed, &mut Option<V>) -> ControlFlow<(), V>,
{
let reader = K::Read::from(key);
let mut guard = self.smr.guard(reader);
macro_rules! update {
() => {
|old: u64, new: Option<u64>| {
initial = new.map(|new| V::from_raw_unchecked(new));
match update(V::borrow_from_raw_unchecked(&old), &mut initial) {
ControlFlow::Continue(new) => ControlFlow::Continue(new.into_raw()),
ControlFlow::Break(()) => ControlFlow::Break(()),
}
}
};
}
let update = match if cfg!(feature = "opt-no-path") {
Err(initial.take().map(V::into_raw))
} else {
unsafe {
self.update_with_optimistic(
&mut guard,
reader,
initial.take().map(V::into_raw),
update!(),
)
}
} {
Ok(update) => update,
Err(initial) => unsafe {
self.update_with_pessimistic(&mut guard, reader, initial, update!())
},
};
match update {
UpdateRaw::Absent { new } => Update::Absent {
new: new.map(|new| unsafe { V::from_raw_unchecked(new) }),
},
UpdateRaw::Success { old, new } => {
Update::Success(unsafe { Updated::<K, V, S>::wrap(guard, old, new) })
}
UpdateRaw::Break { old } => Update::Break {
old: unsafe { Shared::<K, V, S>::wrap(guard, old) },
new: initial,
},
}
}
pub fn remove_with<'g, F>(&'g self, key: &K::Borrowed, mut remove: F) -> Remove<'g, K, V, S>
where
F: FnMut(&V::Borrowed) -> ControlFlow<(), ()>,
{
let reader = K::Read::from(key);
let mut guard = self.smr.guard(reader);
let Ok(remove) = unsafe {
self.remove_with_raw::<true, path::Full<_>, _>(&mut guard, reader, |value| {
remove(V::borrow_from_raw_unchecked(&value))
})
};
match remove {
RemoveRaw::Absent => Remove::Absent,
RemoveRaw::Success { old } => Remove::Success {
old: unsafe { Owned::<K, V, S>::wrap(guard, old) },
},
RemoveRaw::Break { old } => Remove::Break {
old: unsafe { Shared::<K, V, S>::wrap(guard, old) },
},
}
}
pub fn remove_non_recursive_with<F>(
&self,
key: &K::Borrowed,
mut remove: F,
) -> Remove<'_, K, V, S>
where
F: FnMut(&V::Borrowed) -> ControlFlow<(), ()>,
{
let reader = K::Read::from(key);
let mut guard = self.smr.guard(reader);
let mut remove = |value: u64| remove(unsafe { V::borrow_from_raw_unchecked(&value) });
let remove = match if cfg!(feature = "opt-no-path") {
Err(())
} else {
unsafe { self.remove_non_recursive_with_optimistic(&mut guard, reader, &mut remove) }
} {
Ok(remove) => remove,
Err(()) => unsafe {
self.remove_non_recursive_with_pessimistic(&mut guard, reader, &mut remove)
},
};
match remove {
RemoveRaw::Absent => Remove::Absent,
RemoveRaw::Success { old } => Remove::Success {
old: unsafe { Owned::<K, V, S>::wrap(guard, old) },
},
RemoveRaw::Break { old } => Remove::Break {
old: unsafe { Shared::<K, V, S>::wrap(guard, old) },
},
}
}
}
pub enum Upsert<'g, K, V, S>
where
K: Key,
V: Value + 'g,
S: Smr<K, V> + 'g,
{
Success(Upserted<'g, K, V, S>),
Break {
old: Option<Shared<'g, K, V, S>>,
new: Option<V>,
},
}
enum UpsertRaw {
Success { old: Option<u64>, new: u64 },
Break { old: Option<u64> },
}
pub enum Update<'g, K, V, S>
where
K: Key,
V: Value + 'g,
S: Smr<K, V> + 'g,
{
Absent {
new: Option<V>,
},
Success(Updated<'g, K, V, S>),
Break {
old: Shared<'g, K, V, S>,
new: Option<V>,
},
}
enum UpdateRaw {
Absent { new: Option<u64> },
Success { old: u64, new: u64 },
Break { old: u64 },
}
pub enum Remove<'g, K, V, S>
where
K: Key,
V: Value + 'g,
S: Smr<K, V> + 'g,
{
Absent,
Success {
old: Owned<'g, K, V, S>,
},
Break {
old: Shared<'g, K, V, S>,
},
}
enum RemoveRaw {
Absent,
Success { old: u64 },
Break { old: u64 },
}
impl<K, V, S> Map<K, V, S>
where
K: Key,
V: Value,
S: Smr<K, V>,
{
#[inline]
unsafe fn get_raw<'g>(&'g self, _guard: &mut S::Guard<'g>, reader: K::Read<'_>) -> Option<u64> {
unsafe {
let mut cursor = self.seq.raw.cursor::<path::Discard<_>>(reader);
let walk = cursor.edge().load_packed(Ordering::Relaxed);
cursor
.traverse_value(walk)
.map(|cursor::Value { value, edge: _ }| {
if V::INDIRECT {
crate::sync::atomic::fence(Ordering::Acquire);
}
value
})
}
}
#[inline]
unsafe fn upsert_with_optimistic<'g, 'k, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'k>,
initial: Option<u64>,
upsert: F,
) -> Result<UpsertRaw, Option<u64>>
where
F: FnMut(Option<u64>, Option<u64>) -> ControlFlow<(), u64>,
{
unsafe { self.upsert_with_raw::<path::Point<_>, _>(guard, reader, initial, upsert) }
}
#[cold]
unsafe fn upsert_with_pessimistic<'g, 'k, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'k>,
initial: Option<u64>,
upsert: F,
) -> UpsertRaw
where
F: FnMut(Option<u64>, Option<u64>) -> ControlFlow<(), u64>,
{
stat::increment(stat::Counter::InsertPessimistic);
unsafe { self.upsert_with_raw::<path::Full<_>, _>(guard, reader, initial, upsert) }
.expect("path::Retain::PopError is Infallible")
}
#[inline]
unsafe fn upsert_with_raw<'g, 'k, P, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'k>,
mut initial: Option<u64>,
mut upsert: F,
) -> Result<UpsertRaw, Option<u64>>
where
P: Path<K::Read<'k>>,
F: FnMut(Option<u64>, Option<u64>) -> ControlFlow<(), u64>,
{
let mut cursor = unsafe { self.seq.raw.cursor::<P>(reader) };
let mut walk = cursor.edge().load_packed(Ordering::Relaxed);
loop {
match unsafe { cursor.traverse_insert(walk) } {
cursor::Insert::Value {
value: old_value,
edge: old_edge,
} => {
if V::INDIRECT {
crate::sync::atomic::fence(Ordering::Acquire);
}
let new_value = match upsert(old_value, initial) {
ControlFlow::Continue(new_value) => new_value,
ControlFlow::Break(()) => {
return Ok(UpsertRaw::Break { old: old_value });
}
};
if old_edge.meta().is_frozen() {
initial = Some(new_value);
} else {
let (new_edge, _) = cursor.create_path(old_edge, new_value);
match cursor.edge().compare_exchange_packed(
old_edge,
new_edge,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => {
return Ok(UpsertRaw::Success {
old: old_value,
new: new_value,
});
}
Err(conflict) => {
if let Some(node) = new_edge.as_node() {
unsafe {
stat::increment(stat::Counter::FreeConflict);
node.deallocate_recursive::<K::Edge>();
}
}
initial = Some(new_value);
walk = conflict;
continue;
}
}
}
}
cursor::Insert::Replace {
node: old_node,
edge: old_edge,
} if !old_edge.meta().is_frozen() => {
let (smo, new_edge) = unsafe {
old_node.freeze::<K::Edge>();
old_node.replace(old_edge.meta())
};
match cursor.edge().compare_exchange_packed(
old_edge,
new_edge,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => {
unsafe { guard.retire_node(cursor.len().bits(), old_node.into_raw()) };
walk = new_edge;
}
Err(conflict) => {
if smo.is_allocate() {
let node = new_edge.as_node().expect("Allocating SMO creates node");
unsafe {
stat::increment(stat::Counter::FreeConflict);
node.deallocate();
}
}
walk = conflict;
}
}
continue;
}
cursor::Insert::Replace { .. } => (),
}
walk = self.freeze(guard, &mut cursor).map_err(|_| initial)?;
}
}
#[inline]
unsafe fn update_with_optimistic<'g, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'_>,
initial: Option<u64>,
update: F,
) -> Result<UpdateRaw, Option<u64>>
where
F: FnMut(u64, Option<u64>) -> ControlFlow<(), u64>,
{
unsafe { self.update_with_raw::<path::Point<_>, _>(guard, reader, initial, update) }
}
#[cold]
unsafe fn update_with_pessimistic<'g, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'_>,
initial: Option<u64>,
update: F,
) -> UpdateRaw
where
F: FnMut(u64, Option<u64>) -> ControlFlow<(), u64>,
{
stat::increment(stat::Counter::UpdatePessimistic);
unsafe { self.update_with_raw::<path::Full<_>, _>(guard, reader, initial, update) }
.expect("path::Retain::PopError is Infallible")
}
#[inline]
unsafe fn update_with_raw<'g, 'k, P, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'k>,
mut initial: Option<u64>,
mut update: F,
) -> Result<UpdateRaw, Option<u64>>
where
P: Path<K::Read<'k>>,
F: FnMut(u64, Option<u64>) -> ControlFlow<(), u64>,
{
let mut cursor = unsafe { self.seq.raw.cursor::<P>(reader) };
let mut walk = cursor.edge().load_packed(Ordering::Relaxed);
loop {
let cursor::Value {
value: old_value,
edge: old_edge,
} = match unsafe { cursor.traverse_value(walk) } {
None => return Ok(UpdateRaw::Absent { new: initial }),
Some(update) if !update.edge.meta().is_frozen() => update,
Some(_) => {
walk = self.freeze(guard, &mut cursor).map_err(|_| initial)?;
continue;
}
};
if V::INDIRECT {
crate::sync::atomic::fence(Ordering::Acquire);
}
let new_value = match update(old_value, initial) {
ControlFlow::Continue(new_value) => new_value,
ControlFlow::Break(()) => {
return Ok(UpdateRaw::Break { old: old_value });
}
};
match cursor.edge().compare_exchange_packed(
old_edge,
Edge::new_value(old_edge.meta(), new_value),
if V::INDIRECT {
Ordering::Release
} else {
Ordering::Relaxed
},
Ordering::Relaxed,
) {
Ok(_) => {
return Ok(UpdateRaw::Success {
old: old_value,
new: new_value,
});
}
Err(conflict) => {
initial = Some(new_value);
walk = conflict;
}
}
}
}
#[inline]
unsafe fn remove_non_recursive_with_optimistic<'g, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'_>,
remove: F,
) -> Result<RemoveRaw, ()>
where
F: FnMut(u64) -> ControlFlow<(), ()>,
{
unsafe { self.remove_with_raw::<false, path::Point<_>, _>(guard, reader, remove) }
}
#[cold]
unsafe fn remove_non_recursive_with_pessimistic<'g, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'_>,
remove: F,
) -> RemoveRaw
where
F: FnMut(u64) -> ControlFlow<(), ()>,
{
let Ok(remove) =
unsafe { self.remove_with_raw::<false, path::Full<_>, _>(guard, reader, remove) };
remove
}
#[inline]
unsafe fn remove_with_raw<'g, 'k, const RECURSIVE: bool, P, F>(
&'g self,
guard: &mut S::Guard<'g>,
reader: K::Read<'k>,
mut remove: F,
) -> Result<RemoveRaw, P::PopError>
where
P: Path<K::Read<'k>>,
F: FnMut(u64) -> ControlFlow<(), ()>,
{
let mut cursor = unsafe { self.seq.raw.cursor::<P>(reader) };
let mut walk = cursor.edge().load_packed(Ordering::Relaxed);
let (value, edge) = loop {
let cursor::Value { value, edge } = match unsafe { cursor.traverse_value(walk) } {
None => return Ok(RemoveRaw::Absent),
Some(update) if !update.edge.meta().is_frozen() => update,
Some(_) => {
walk = self.freeze(guard, &mut cursor)?;
continue;
}
};
if V::INDIRECT {
crate::sync::atomic::fence(Ordering::Acquire);
}
match remove(value) {
ControlFlow::Continue(()) => (),
ControlFlow::Break(()) => {
return Ok(RemoveRaw::Break { old: value });
}
}
match cursor.edge().compare_exchange_packed(
edge,
Edge::NULL,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break (value, edge),
Err(conflict) => walk = conflict,
}
};
if RECURSIVE {
let mut trim = edge.meta().len().into();
let mut pop = 0;
'pop: while let Some((mut old_len, old_node)) =
cursor.pop().expect("Recursive remove requires path")
{
if unsafe { old_node.len::<K::Edge>() } > 1 {
break 'pop;
}
cursor.trim(K::Len::BYTE + trim);
pop += 1;
let mut old_edge = cursor.edge().load_packed(Ordering::Relaxed);
'freeze: loop {
let addr = cursor.edge();
match unsafe { cursor.freeze(old_len, old_node, old_edge) }
.expect("Recursive remove requires path")
{
cursor::Freeze::Traverse { edge } => {
old_edge = edge;
}
cursor::Freeze::Success {
old_node: node,
new_edge,
} => {
if let Some(node) = node {
unsafe { guard.retire_node(cursor.len().bits(), node.into_raw()) };
}
if core::ptr::eq(cursor.edge(), addr) {
trim = old_len.into();
continue 'pop;
}
old_edge = new_edge;
}
}
match cursor.traverse_node(old_edge) {
Ok(edge) => {
old_len = edge.meta().len();
old_edge = edge;
continue 'freeze;
}
Err(len) => {
trim = len;
continue 'pop;
}
}
}
}
stat::record(stat::Record::RemovePop, pop);
}
Ok(RemoveRaw::Success { old: value })
}
fn freeze<'g, 'k, P>(
&'g self,
guard: &mut S::Guard<'g>,
cursor: &mut Cursor<K::Read<'k>, P>,
) -> Result<ribbit::Packed<Edge<K::Edge>>, P::PopError>
where
P: Path<K::Read<'k>>,
{
let (old_len, old_node) = cursor.pop()?.expect("Root edge cannot be frozen");
match unsafe {
cursor.freeze(
old_len,
old_node,
cursor.edge().load_packed(Ordering::Relaxed),
)
}? {
cursor::Freeze::Traverse { edge }
| cursor::Freeze::Success {
old_node: None,
new_edge: edge,
} => Ok(edge),
cursor::Freeze::Success {
old_node: Some(node),
new_edge,
} => {
unsafe { guard.retire_node(cursor.len().bits(), node.into_raw()) };
Ok(new_edge)
}
}
}
}
impl<K, V, S> From<sequential::Map<K, V>> for Map<K, V, S>
where
K: Key,
V: Value,
S: Default,
{
#[inline]
fn from(seq: sequential::Map<K, V>) -> Self {
Self {
smr: S::default(),
seq,
}
}
}
impl<K, V, S> From<Map<K, V, S>> for sequential::Map<K, V>
where
K: Key,
V: Value,
{
#[inline]
fn from(map: Map<K, V, S>) -> sequential::Map<K, V> {
map.seq
}
}
#[cfg(test)]
mod tests {
use core::convert::Infallible;
use core::ops::ControlFlow;
use crate::Order;
use crate::concurrent::Map;
use crate::key::BoxedSlice;
use crate::key::BoxedStr;
use crate::key::NonNull;
use crate::key::Slice;
use crate::key::Str;
use crate::key::Terminated;
use crate::raw::key::Read as _;
#[test]
fn smoke() {
let map = Map::<BoxedStr<NonNull>, _>::default();
map.upsert(unsafe { Slice::new_unchecked("abcd") }, 1u64);
assert_eq!(
map.get(unsafe { Slice::new_unchecked("abcd") })
.as_deref()
.copied(),
Some(1)
);
}
#[test]
fn smoke_u64_key() {
let map = Map::<[u8; 8], _>::default();
let key = 0xdeadbeefu64.to_be_bytes();
map.upsert(&key, 1u64);
assert_eq!(map.get(&key).as_deref().copied(), Some(1));
}
#[test]
fn smoke_value_ref() {
let values = [0, 1, 2, 3, 4, 5];
let map = Map::<u64, &u64>::default();
for (key, value) in values.iter().enumerate() {
map.upsert(key as u64, value);
}
#[expect(clippy::needless_range_loop)]
for key in 0..values.len() {
let value = map.get(&(key as u64)).as_deref().copied().unwrap();
assert!(core::ptr::eq(value, &values[key]));
}
}
#[test]
fn smoke_value_box() {
let values = [0, 1, 2, 3, 4, 5];
let map = Map::<u64, Box<u64>>::default();
for (key, value) in values.iter().enumerate() {
map.upsert(key as u64, Box::new(*value));
}
std::thread::scope(|scope| {
for _ in 0..8 {
scope.spawn(|| {
for key in (0..values.len()).cycle().take(100_000) {
let value = map.get(&(key as u64)).as_deref().copied().unwrap();
assert_eq!(key, value as usize);
}
});
}
});
for key in 0..values.len() {
let value = map.get(&(key as u64)).as_deref().copied().unwrap();
assert_eq!(key, value as usize);
}
}
#[test]
fn scan_value() {
let map = Map::<u64, _>::default();
let key = 1u64;
map.upsert(key, 2u64);
assert_eq!(
map.range(1u64..=1u64)
.entries(Order::Ascend)
.collect::<Vec<_>>(),
vec![(1, 2)]
);
}
#[test]
fn scan_node3() {
insert_all(0u64..3);
}
#[test]
fn scan_node256() {
insert_all(0u64..256);
}
#[test]
fn scan_gap() {
let map = insert_all((0u64..512).step_by(2));
assert_eq!(
map.range(256u64..=511u64)
.entries(Order::Ascend)
.collect::<Vec<_>>(),
(256..512)
.step_by(2)
.map(|key| (key, key / 2))
.collect::<Vec<_>>()
);
}
#[test]
fn node3_overwrite() {
let mut map = Map::<u64, _>::default();
for value in [1u64, 2, 3] {
map.upsert(1, value);
assert_eq!(map.get(&1).as_deref().copied(), Some(value));
}
assert_eq!(map.as_sequential().all().entries(Order::Ascend).count(), 1);
map.as_sequential()
.all()
.entries(Order::Ascend)
.try_fold((), |(), (key, value)| {
assert_eq!(key, 1);
assert_eq!(*value, 3);
ControlFlow::<Infallible>::Continue(())
});
}
#[test]
fn node3_reverse() {
insert_all((0u16..3).rev());
}
#[test]
fn node3_full() {
insert_all(0u16..3);
}
#[test]
fn node3_expand() {
insert_all(0u16..4);
}
#[test]
fn node15_full() {
insert_all(0u16..15);
}
#[test]
fn node15_expand() {
insert_all(0u16..16);
}
#[test]
fn node47_full() {
insert_all(0u16..47);
}
#[test]
fn node47_expand() {
insert_all(0u16..61);
}
#[test]
fn node256_full() {
insert_all(0u16..=255);
}
#[test]
fn range_reverse() {
let map = Map::<u64, _>::default();
for key in [5, 1, 4, 3, 2] {
map.upsert(key, key);
assert_eq!(map.get(&key).as_deref().copied(), Some(key));
}
assert_eq!(
map.range(2..=4).entries(Order::Descend).collect::<Vec<_>>(),
vec![(4, 4), (3, 3), (2, 2)]
);
}
#[test]
fn split_edges() {
let mut key = (1..100).collect::<Vec<_>>();
insert_all(core::iter::from_fn(|| {
if key.is_empty() {
None
} else {
let mut next = key.clone();
next.push(0);
key.pop();
let next = next.into_boxed_slice();
Some(BoxedSlice::<Terminated<0>>::new(next).unwrap())
}
}));
}
#[test]
fn one_long_key() {
insert_all([BoxedStr::<NonNull>::new("a".repeat(1000)).unwrap()]);
}
#[test]
fn short_key() {
insert_all([BoxedStr::<NonNull>::new("\n".to_string()).unwrap()]);
}
#[test]
fn two_long_keys() {
insert_all([
BoxedStr::<NonNull>::new("a".repeat(1000)).unwrap(),
BoxedStr::<NonNull>::new("b".repeat(1000)).unwrap(),
]);
}
#[test]
fn smoke_key_slice() {
let keys = ["ad", "abc"];
let map = crate::concurrent::Map::<&Str<NonNull>, u64>::new();
map.insert(Str::new(keys[0]).unwrap(), 0)
.unwrap_or_else(|(_, _)| panic!());
map.insert(Str::new(keys[1]).unwrap(), 1)
.unwrap_or_else(|(_, _)| panic!());
let temp = "adabc";
assert_eq!(
map.get(Str::new(&temp[..2]).unwrap()).as_deref().copied(),
Some(0)
);
assert_eq!(
map.get(Str::new(&temp[2..]).unwrap()).as_deref().copied(),
Some(1)
);
}
#[test]
fn key_slice_long_prefix() {
let keys = (0..10)
.map(|i| "a".repeat(100) + &i.to_string())
.collect::<Vec<_>>();
let map = crate::concurrent::Map::<&Slice<NonNull>, u64>::new();
for (i, key) in keys.iter().enumerate() {
map.insert(Slice::new(key.as_bytes()).unwrap(), i as u64)
.unwrap();
}
for (i, key) in keys.iter().enumerate() {
assert_eq!(
map.get(Slice::new(key.as_bytes()).unwrap())
.as_deref()
.copied(),
Some(i as u64)
);
}
}
fn insert_all<I, K>(iter: I) -> Map<K, u64>
where
I: IntoIterator<Item = K>,
K: crate::Key + Clone + Ord + core::fmt::Debug,
{
let mut keys = iter
.into_iter()
.enumerate()
.map(|(index, key)| (key, index as u64))
.collect::<Vec<_>>();
let mut map = Map::default();
for (key, value) in &keys {
map.upsert(key.as_insert(), *value);
assert_eq!(map.get(key.borrow()).as_deref().copied(), Some(*value));
}
for (key, value) in &keys {
assert_eq!(map.get(key.borrow()).as_deref().copied(), Some(*value));
}
let mut iter = map.as_sequential().all().entries(Order::Ascend);
let mut count = 0;
while iter.lend().is_some() {
count += 1;
}
drop(iter);
assert_eq!(count, keys.len());
keys.sort_by(|(l, _), (r, _)| l.cmp(r));
map.as_sequential()
.all()
.entries(Order::Ascend)
.zip(&keys)
.for_each(|((lk, lv), (rk, rv))| {
assert_eq!(lk, *rk);
assert_eq!(*lv, *rv);
});
let Some(((first, _), (last, _))) = keys.first().zip(keys.last()) else {
return map;
};
map.prefix(K::Read::from(first.borrow()).common_prefix(K::Read::from(last.borrow())))
.entries(Order::Descend)
.zip(keys.iter().rev())
.for_each(|((lk, lv), (rk, rv))| {
assert_eq!(lk, *rk);
assert_eq!(lv, *rv);
});
let mut i = 0;
map.range(first.borrow()..=last.borrow())
.entries(Order::Descend)
.zip(keys.iter().rev())
.for_each(|((lk, lv), (rk, rv))| {
i += 1;
assert_eq!(lk, *rk);
assert_eq!(lv, *rv);
});
assert_eq!(i, keys.len());
map
}
}