use std::any::Any;
pub use vortex_buffer::BufferAllocator;
pub use vortex_buffer::BufferAllocatorRef;
pub use vortex_buffer::StaticBufferAllocator;
use vortex_session::SessionExt;
use vortex_session::SessionGuard;
use vortex_session::SessionVar;
use vortex_session::VortexSession;
#[derive(Clone, Debug)]
pub struct MemorySession {
allocator: BufferAllocatorRef,
}
impl MemorySession {
pub fn new(allocator: BufferAllocatorRef) -> Self {
Self { allocator }
}
pub fn allocator(&self) -> BufferAllocatorRef {
self.allocator.clone()
}
pub fn set_allocator(&mut self, allocator: BufferAllocatorRef) {
self.allocator = allocator;
}
}
impl Default for MemorySession {
fn default() -> Self {
Self::new(BufferAllocatorRef::statically_allocated())
}
}
impl SessionVar for MemorySession {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub trait MemorySessionExt: SessionExt {
fn memory(&self) -> SessionGuard<'_, MemorySession> {
self.get::<MemorySession>()
}
fn allocator(&self) -> BufferAllocatorRef {
self.memory().allocator()
}
fn with_allocator(self, allocator: BufferAllocatorRef) -> VortexSession {
let session = self.session();
session.get_mut::<MemorySession>().set_allocator(allocator);
session
}
}
impl<S: SessionExt> MemorySessionExt for S {}
#[cfg(test)]
pub(crate) mod test_allocator {
use std::alloc::Layout;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use allocator_api2::alloc::AllocError;
use allocator_api2::alloc::Allocator;
use allocator_api2::alloc::Global;
use parking_lot::Mutex;
use super::BufferAllocatorRef;
#[derive(Debug)]
struct CountingAllocator(Arc<AtomicUsize>);
unsafe impl Allocator for CountingAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.0.fetch_add(1, Ordering::Relaxed);
Global.allocate(layout)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { Global.deallocate(ptr, layout) }
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct AllocationTracker {
allocations: Arc<Mutex<Vec<(usize, usize)>>>,
}
impl AllocationTracker {
#[track_caller]
pub(crate) fn assert_owns<T>(&self, values: &[T]) {
assert!(
!values.is_empty(),
"allocation ownership needs a nonempty payload",
);
let start = values.as_ptr() as usize;
let end = start + size_of_val(values);
assert!(
self.allocations
.lock()
.iter()
.any(|&(base, size)| base <= start && end <= base + size),
"the returned payload must be backed by the configured allocator",
);
}
pub(crate) fn live_allocations(&self) -> usize {
self.allocations.lock().len()
}
}
#[derive(Debug)]
struct TrackingAllocator(AllocationTracker);
unsafe impl Allocator for TrackingAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let allocation = Global.allocate(layout)?;
self.0
.allocations
.lock()
.push((allocation.cast::<u8>().as_ptr() as usize, allocation.len()));
Ok(allocation)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
self.0
.allocations
.lock()
.retain(|&(base, _)| base != ptr.as_ptr() as usize);
unsafe { Global.deallocate(ptr, layout) }
}
}
pub(crate) fn tracking_allocator() -> (BufferAllocatorRef, AllocationTracker) {
let tracker = AllocationTracker::default();
(
BufferAllocatorRef::new(TrackingAllocator(tracker.clone())),
tracker,
)
}
pub(crate) fn counting_allocator() -> (BufferAllocatorRef, Arc<AtomicUsize>) {
let allocations = Arc::new(AtomicUsize::new(0));
(
BufferAllocatorRef::new(CountingAllocator(Arc::clone(&allocations))),
allocations,
)
}
}
#[cfg(test)]
mod tests {
use vortex_buffer::BufferAllocatorRef;
use super::MemorySession;
#[test]
fn memory_session_replaces_allocator() {
let allocator = BufferAllocatorRef::statically_allocated();
let mut session = MemorySession::default();
session.set_allocator(allocator);
let buffer = session.allocator().copy_from([1u32, 2, 3]);
assert_eq!(buffer.as_slice(), [1, 2, 3]);
}
}