use alloc::sync::Arc;
use core::fmt;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicUsize, Ordering};
use allocator_api2::collections::TryReserveErrorKind;
use allocator_api2::vec::Vec;
mod allocator;
mod map;
mod shared_string;
mod sort;
mod string;
mod work;
pub use map::{HashWork, OwnedMap, RetainError};
pub use shared_string::SharedString;
pub use sort::SortError;
pub use string::OwnedString;
pub use work::{Execution, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryError {
LimitExceeded,
OutOfMemory,
CapacityOverflow,
WorkExceeded,
}
impl fmt::Display for MemoryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::LimitExceeded => "memory budget spent",
Self::OutOfMemory => "allocator could not supply storage",
Self::CapacityOverflow => "storage capacity overflow",
Self::WorkExceeded => "work budget spent",
})
}
}
#[cfg(feature = "std")]
impl std::error::Error for MemoryError {}
#[derive(Debug)]
pub struct MemoryAccount {
limit: usize,
used: AtomicUsize,
work_limit: usize,
work: AtomicUsize,
}
impl MemoryAccount {
#[must_use]
pub const fn new(limit: usize, work_limit: usize) -> Self {
Self {
limit,
used: AtomicUsize::new(0),
work_limit,
work: AtomicUsize::new(0),
}
}
fn reserve(&self, bytes: usize) -> core::result::Result<Reservation<'_>, MemoryError> {
let mut used = self.used.load(Ordering::Relaxed);
loop {
debug_assert!(used <= self.limit);
if bytes > self.limit - used {
return Err(MemoryError::LimitExceeded);
}
match self.used.compare_exchange_weak(
used,
used + bytes,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => {
return Ok(Reservation {
account: self,
bytes,
});
}
Err(current) => used = current,
}
}
}
fn release(&self, bytes: usize) {
let previous = self.used.fetch_sub(bytes, Ordering::Relaxed);
debug_assert!(previous >= bytes);
}
}
struct Reservation<'a> {
account: &'a MemoryAccount,
bytes: usize,
}
impl Reservation<'_> {
fn commit(mut self) {
self.bytes = 0;
}
}
impl Drop for Reservation<'_> {
fn drop(&mut self) {
self.account.release(self.bytes);
}
}
#[derive(Debug, Clone)]
pub struct Memory {
account: Arc<MemoryAccount>,
}
impl Memory {
#[must_use]
pub fn new(account: Arc<MemoryAccount>) -> Self {
Self { account }
}
#[must_use]
pub fn limit(&self) -> usize {
self.account.limit
}
#[must_use]
pub fn used(&self) -> usize {
self.account.used.load(Ordering::Relaxed)
}
#[must_use]
pub fn same_account(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.account, &other.account)
}
#[must_use]
pub fn work_used(&self) -> usize {
self.account.work.load(Ordering::Relaxed)
}
#[must_use]
pub fn work_remaining(&self) -> usize {
let spent = self.account.work.load(Ordering::Relaxed);
debug_assert!(spent <= self.account.work_limit);
self.account.work_limit - spent
}
pub fn charge_work(&self, amount: u64) -> core::result::Result<(), MemoryError> {
let amount = usize::try_from(amount).map_err(|_| MemoryError::WorkExceeded)?;
let mut spent = self.account.work.load(Ordering::Relaxed);
loop {
debug_assert!(spent <= self.account.work_limit);
if amount > self.account.work_limit - spent {
return Err(MemoryError::WorkExceeded);
}
match self.account.work.compare_exchange_weak(
spent,
spent + amount,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Ok(()),
Err(current) => spent = current,
}
}
}
pub fn reserve(&self, bytes: usize) -> core::result::Result<MemoryReservation, MemoryError> {
let reservation = self.account.reserve(bytes)?;
reservation.commit();
Ok(MemoryReservation {
memory: self.clone(),
bytes,
})
}
#[must_use]
pub fn vector<T>(&self) -> OwnedVec<T> {
Work::vector(self)
}
#[must_use]
pub fn map<K: HashWork, V>(&self) -> OwnedMap<K, V> {
OwnedMap::new(self.clone())
}
#[must_use]
pub fn string(&self) -> OwnedString {
OwnedString::new(self)
}
}
#[derive(Debug)]
#[must_use = "retain the reservation until its allocation has been deallocated"]
pub struct MemoryReservation {
memory: Memory,
bytes: usize,
}
impl Drop for MemoryReservation {
fn drop(&mut self) {
self.memory.account.release(self.bytes);
}
}
pub struct OwnedVec<T, W: Work = Memory> {
values: Vec<T, allocator::AccountAllocator<W>>,
}
impl<T> OwnedVec<T> {
pub fn with_execution<W: Work>(self, execution: &W) -> Result<OwnedVec<T, W>, Self> {
if !self.memory().same_account(execution.memory()) {
return Err(self);
}
Ok(OwnedVec {
values: allocator::scope(self.values, execution),
})
}
}
impl<T, W: Work> OwnedVec<T, W> {
#[must_use]
pub fn into_owned(self) -> OwnedVec<T> {
OwnedVec {
values: allocator::retain(self.values),
}
}
}
impl<T: fmt::Debug, W: Work> fmt::Debug for OwnedVec<T, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.values.fmt(f)
}
}
impl<T: PartialEq, W: Work> PartialEq for OwnedVec<T, W> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: Eq, W: Work> Eq for OwnedVec<T, W> {}
impl<T: PartialEq, W: Work, const N: usize> PartialEq<[T; N]> for OwnedVec<T, W> {
fn eq(&self, other: &[T; N]) -> bool {
&**self == other
}
}
impl<T, W: Work> OwnedVec<T, W> {
#[must_use]
pub fn memory(&self) -> &Memory {
self.values.allocator().memory()
}
#[must_use]
pub fn capacity(&self) -> usize {
self.values.capacity()
}
pub fn try_reserve_exact(
&mut self,
additional: usize,
) -> core::result::Result<(), MemoryError> {
self.values.allocator().clear_failure();
self.values
.try_reserve_exact(additional)
.map_err(|error| match error.kind() {
TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
})
}
pub fn try_reserve(&mut self, additional: usize) -> core::result::Result<(), MemoryError> {
self.values.allocator().clear_failure();
self.values
.try_reserve(additional)
.map_err(|error| match error.kind() {
TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
})
}
pub fn try_push(&mut self, value: T) -> core::result::Result<(), MemoryError> {
self.values.allocator().work().charge_work(1)?;
if self.values.len() == self.values.capacity() {
self.try_reserve(1)?;
}
self.values.push(value);
Ok(())
}
pub fn pop(&mut self) -> Option<T> {
self.values.pop()
}
pub fn clear(&mut self) {
self.values.clear();
}
pub fn try_sort_unstable_by<E>(
&mut self,
compare: impl FnMut(&T, &T) -> core::result::Result<core::cmp::Ordering, E>,
) -> core::result::Result<(), SortError<E>> {
let work = self.values.allocator().work().clone();
sort::sort(&mut self.values, &work, compare)
}
}
impl<T: Copy, W: Work> OwnedVec<T, W> {
pub fn try_extend_from_slice(&mut self, values: &[T]) -> core::result::Result<(), MemoryError> {
let work = values.len().max(core::mem::size_of_val(values));
self.values.allocator().work().charge_work(work as u64)?;
if self.values.capacity() == 0 {
self.try_reserve_exact(values.len())?;
} else {
self.try_reserve(values.len())?;
}
self.values.extend_from_slice(values);
Ok(())
}
pub fn try_copy(&self) -> core::result::Result<Self, MemoryError> {
let mut copy = self.values.allocator().work().vector();
let work = self.values.len().max(core::mem::size_of_val(&**self));
self.values.allocator().work().charge_work(work as u64)?;
copy.try_reserve_exact(self.values.len())?;
copy.values.extend_from_slice(&self.values);
Ok(copy)
}
}
impl<T, W: Work> Deref for OwnedVec<T, W> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.values
}
}
impl<T, W: Work> DerefMut for OwnedVec<T, W> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.values
}
}
impl<'a, T, W: Work> IntoIterator for &'a OwnedVec<T, W> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T, W: Work> IntoIterator for &'a mut OwnedVec<T, W> {
type Item = &'a mut T;
type IntoIter = core::slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct OwnedIter<T, W: Work = Memory> {
values: allocator_api2::vec::IntoIter<T, allocator::AccountAllocator<W>>,
}
impl<T, W: Work> Iterator for OwnedIter<T, W> {
type Item = T;
fn next(&mut self) -> Option<T> {
self.values.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.values.size_hint()
}
}
impl<T, W: Work> DoubleEndedIterator for OwnedIter<T, W> {
fn next_back(&mut self) -> Option<T> {
self.values.next_back()
}
}
impl<T, W: Work> ExactSizeIterator for OwnedIter<T, W> {}
impl<T, W: Work> core::iter::FusedIterator for OwnedIter<T, W> {}
impl<T, W: Work> IntoIterator for OwnedVec<T, W> {
type Item = T;
type IntoIter = OwnedIter<T, W>;
fn into_iter(self) -> Self::IntoIter {
OwnedIter {
values: self.values.into_iter(),
}
}
}
#[cfg(test)]
mod tests;