use std::{cmp, iter::FusedIterator, marker::PhantomData};
use crate::internal::arena::ArenaId;
const VALUES_PER_CHUNK: usize = 128;
#[derive(Clone)]
pub struct Mapping<TId, TValue> {
chunks: Vec<[Option<TValue>; VALUES_PER_CHUNK]>,
len: usize,
max: usize,
_phantom: PhantomData<TId>,
}
impl<TId: ArenaId, TValue> Default for Mapping<TId, TValue> {
fn default() -> Self {
Self::new()
}
}
impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
pub(crate) fn new() -> Self {
Self::with_capacity(1)
}
pub fn capacity(&self) -> usize {
self.chunks.len() * VALUES_PER_CHUNK
}
pub fn size_in_bytes(&self) -> usize {
self.capacity() * std::mem::size_of::<Option<TValue>>()
}
pub fn with_capacity(n: usize) -> Self {
let n = cmp::max(1, n);
let n_chunks = (n - 1) / VALUES_PER_CHUNK + 1;
let mut chunks = Vec::new();
chunks.resize_with(n_chunks, || std::array::from_fn(|_| None));
Self {
chunks,
len: 0,
max: 0,
_phantom: Default::default(),
}
}
#[inline]
pub const fn chunk_and_offset(id: usize) -> (usize, usize) {
let chunk = id / VALUES_PER_CHUNK;
let offset = id % VALUES_PER_CHUNK;
(chunk, offset)
}
pub fn insert(&mut self, id: TId, value: TValue) -> Option<TValue> {
let idx = id.to_usize();
let (chunk, offset) = Self::chunk_and_offset(idx);
if chunk >= self.chunks.len() {
self.chunks
.resize_with(chunk + 1, || std::array::from_fn(|_| None));
}
let previous_value = self.chunks[chunk][offset].replace(value);
if previous_value.is_none() {
self.len += 1;
}
self.max = self.max.max(idx);
previous_value
}
pub fn unset(&mut self, id: TId) -> Option<TValue> {
let idx = id.to_usize();
let (chunk, offset) = Self::chunk_and_offset(idx);
if chunk >= self.chunks.len() {
return None;
}
let previous_value = self.chunks[chunk][offset].take();
if previous_value.is_some() {
self.len -= 1;
}
previous_value
}
pub fn get(&self, id: TId) -> Option<&TValue> {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
if chunk >= self.chunks.len() {
return None;
}
unsafe {
self.chunks
.get_unchecked(chunk)
.get_unchecked(offset)
.as_ref()
}
}
pub fn get_mut(&mut self, id: TId) -> Option<&mut TValue> {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
if chunk >= self.chunks.len() {
return None;
}
unsafe {
self.chunks
.get_unchecked_mut(chunk)
.get_unchecked_mut(offset)
.as_mut()
}
}
pub unsafe fn get_unchecked(&self, id: TId) -> &TValue {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
unsafe { self.chunks.get_unchecked(chunk).get_unchecked(offset) }
.as_ref()
.unwrap()
}
pub unsafe fn get_unchecked_mut(&mut self, id: TId) -> &mut TValue {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
unsafe {
self.chunks
.get_unchecked_mut(chunk)
.get_unchecked_mut(offset)
}
.as_mut()
.unwrap()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn max(&self) -> usize {
self.max
}
pub fn slots(&self) -> usize {
self.chunks.len() * VALUES_PER_CHUNK
}
pub fn iter(&self) -> MappingIter<'_, TId, TValue> {
MappingIter {
mapping: self,
offset: 0,
}
}
}
pub struct MappingIter<'a, TId, TValue> {
mapping: &'a Mapping<TId, TValue>,
offset: usize,
}
impl<'a, TId: ArenaId, TValue> Iterator for MappingIter<'a, TId, TValue> {
type Item = (TId, &'a TValue);
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.offset >= self.mapping.len {
return None;
}
let (chunk, offset) = Mapping::<TId, TValue>::chunk_and_offset(self.offset);
let id = TId::from_usize(self.offset);
self.offset += 1;
unsafe {
if let Some(value) = &self
.mapping
.chunks
.get_unchecked(chunk)
.get_unchecked(offset)
{
break Some((id, value));
}
}
}
}
}
impl<TId: ArenaId, TValue> FusedIterator for MappingIter<'_, TId, TValue> {}
#[cfg(feature = "serde")]
impl<K: ArenaId, V: serde::Serialize> serde::Serialize for Mapping<K, V> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.chunks
.iter()
.flatten()
.take(self.max() + 1)
.collect::<Vec<_>>()
.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, K: ArenaId, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Mapping<K, V> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let values = Vec::<Option<V>>::deserialize(deserializer)?;
let mut mapping = Mapping::with_capacity(values.len());
for (i, value) in values.into_iter().enumerate() {
if let Some(value) = value {
mapping.insert(K::from_usize(i), value);
}
}
Ok(mapping)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Id {
id: usize,
}
impl ArenaId for Id {
fn from_usize(x: usize) -> Self {
Id { id: x }
}
fn to_usize(self) -> usize {
self.id
}
}
#[test]
pub fn test_mapping() {
let mut mapping = Mapping::<Id, usize>::new();
assert_eq!(mapping.len(), 0);
assert_eq!(mapping.slots(), VALUES_PER_CHUNK);
mapping.insert(Id::from_usize(0), 10usize);
assert_eq!(mapping.len(), 1);
assert_eq!(*mapping.get(Id::from_usize(0)).unwrap(), 10usize);
assert_eq!(mapping.slots(), VALUES_PER_CHUNK);
mapping.insert(Id::from_usize(VALUES_PER_CHUNK), 20usize);
assert_eq!(
*mapping.get(Id::from_usize(VALUES_PER_CHUNK)).unwrap(),
20usize
);
assert_eq!(mapping.len(), 2);
assert_eq!(mapping.slots(), VALUES_PER_CHUNK * 2);
}
#[cfg(feature = "serde")]
#[test]
pub fn test_serde() {
use serde_json::{from_value, to_value};
let values = [1, 3, 6, 9, 2, 4, 6, 1, 2, 3];
let json = to_value(values).unwrap();
let mapping =
values
.iter()
.copied()
.enumerate()
.fold(Mapping::new(), |mut mapping, (i, v)| {
mapping.insert(Id::from_usize(i), v);
mapping
});
assert_eq!(json, to_value(&mapping).unwrap());
itertools::assert_equal(
mapping.iter().map(|(_, &v)| v),
from_value::<Vec<i32>>(json).unwrap(),
);
}
}