1use std::any::Any;
7
8pub use vortex_buffer::BufferAllocator;
9pub use vortex_buffer::BufferAllocatorRef;
10pub use vortex_buffer::StaticBufferAllocator;
11use vortex_session::SessionExt;
12use vortex_session::SessionGuard;
13use vortex_session::SessionVar;
14use vortex_session::VortexSession;
15
16#[derive(Clone, Debug)]
18pub struct MemorySession {
19 allocator: BufferAllocatorRef,
20}
21
22impl MemorySession {
23 pub fn new(allocator: BufferAllocatorRef) -> Self {
25 Self { allocator }
26 }
27
28 pub fn allocator(&self) -> BufferAllocatorRef {
30 self.allocator.clone()
31 }
32
33 pub fn set_allocator(&mut self, allocator: BufferAllocatorRef) {
35 self.allocator = allocator;
36 }
37}
38
39impl Default for MemorySession {
40 fn default() -> Self {
41 Self::new(BufferAllocatorRef::statically_allocated())
42 }
43}
44
45impl SessionVar for MemorySession {
46 fn as_any(&self) -> &dyn Any {
47 self
48 }
49
50 fn as_any_mut(&mut self) -> &mut dyn Any {
51 self
52 }
53}
54
55pub trait MemorySessionExt: SessionExt {
57 fn memory(&self) -> SessionGuard<'_, MemorySession> {
59 self.get::<MemorySession>()
60 }
61
62 fn allocator(&self) -> BufferAllocatorRef {
64 self.memory().allocator()
65 }
66
67 fn with_allocator(self, allocator: BufferAllocatorRef) -> VortexSession {
69 let session = self.session();
70 session.get_mut::<MemorySession>().set_allocator(allocator);
71 session
72 }
73}
74
75impl<S: SessionExt> MemorySessionExt for S {}
76
77#[cfg(test)]
78pub(crate) mod test_allocator {
79 use std::alloc::Layout;
80 use std::ptr::NonNull;
81 use std::sync::Arc;
82 use std::sync::atomic::AtomicUsize;
83 use std::sync::atomic::Ordering;
84
85 use allocator_api2::alloc::AllocError;
86 use allocator_api2::alloc::Allocator;
87 use allocator_api2::alloc::Global;
88 use parking_lot::Mutex;
89
90 use super::BufferAllocatorRef;
91
92 #[derive(Debug)]
93 struct CountingAllocator(Arc<AtomicUsize>);
94
95 unsafe impl Allocator for CountingAllocator {
96 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
97 self.0.fetch_add(1, Ordering::Relaxed);
98 Global.allocate(layout)
99 }
100
101 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
102 unsafe { Global.deallocate(ptr, layout) }
104 }
105 }
106
107 #[derive(Clone, Debug, Default)]
109 pub(crate) struct AllocationTracker {
110 allocations: Arc<Mutex<Vec<(usize, usize)>>>,
111 }
112
113 impl AllocationTracker {
114 #[track_caller]
115 pub(crate) fn assert_owns<T>(&self, values: &[T]) {
116 assert!(
117 !values.is_empty(),
118 "allocation ownership needs a nonempty payload",
119 );
120 let start = values.as_ptr() as usize;
121 let end = start + size_of_val(values);
122 assert!(
123 self.allocations
124 .lock()
125 .iter()
126 .any(|&(base, size)| base <= start && end <= base + size),
127 "the returned payload must be backed by the configured allocator",
128 );
129 }
130
131 pub(crate) fn live_allocations(&self) -> usize {
132 self.allocations.lock().len()
133 }
134 }
135
136 #[derive(Debug)]
137 struct TrackingAllocator(AllocationTracker);
138
139 unsafe impl Allocator for TrackingAllocator {
142 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
143 let allocation = Global.allocate(layout)?;
144 self.0
145 .allocations
146 .lock()
147 .push((allocation.cast::<u8>().as_ptr() as usize, allocation.len()));
148 Ok(allocation)
149 }
150
151 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
152 self.0
153 .allocations
154 .lock()
155 .retain(|&(base, _)| base != ptr.as_ptr() as usize);
156 unsafe { Global.deallocate(ptr, layout) }
158 }
159 }
160
161 pub(crate) fn tracking_allocator() -> (BufferAllocatorRef, AllocationTracker) {
162 let tracker = AllocationTracker::default();
163 (
164 BufferAllocatorRef::new(TrackingAllocator(tracker.clone())),
165 tracker,
166 )
167 }
168
169 pub(crate) fn counting_allocator() -> (BufferAllocatorRef, Arc<AtomicUsize>) {
170 let allocations = Arc::new(AtomicUsize::new(0));
171 (
172 BufferAllocatorRef::new(CountingAllocator(Arc::clone(&allocations))),
173 allocations,
174 )
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use vortex_buffer::BufferAllocatorRef;
181
182 use super::MemorySession;
183
184 #[test]
185 fn memory_session_replaces_allocator() {
186 let allocator = BufferAllocatorRef::statically_allocated();
187 let mut session = MemorySession::default();
188 session.set_allocator(allocator);
189 let buffer = session.allocator().copy_from([1u32, 2, 3]);
190 assert_eq!(buffer.as_slice(), [1, 2, 3]);
191 }
192}