synthphonia/galloc.rs
1use std::str::{from_utf8, from_utf8_unchecked};
2use bumpalo::collections::{String as BString, CollectIn};
3use bumpalo::Bump;
4use ext_trait::extension;
5use bumpalo::collections::Vec as BVec;
6
7
8thread_local! {
9 static THR_ARENA: Bump = Bump::new() // Use Bumpalo for speed. Global is too slow.
10}
11
12#[extension(pub trait AllocForAny)]
13impl<T> T {
14 #[inline(always)]
15 /// Provides a method to allocate an instance of `T` on the heap with a static lifetime.
16 ///
17 /// This implementation of `galloc` takes ownership of the `T` instance and uses the `alloc` function to place it in a location with a static lifetime, presumably managing it in a way that ensures its persistence for the duration of the program.
18 /// This can be particularly useful for scenarios where a static lifetime is required, such as when interfacing with systems or patterns that necessitate global state or long-lived data.
19 ///
20 fn galloc(self) -> &'static T {
21 alloc(self)
22 }
23 #[inline(always)]
24 /// Provides a method that moves the instance and returns a reference to it allocated with a static lifetime.
25 ///
26 /// This method utilizes `alloc_mut` to perform the allocation, likely involving allocating the resource in a manner that ensures it lives for the entire duration of the application.
27 /// These semantics allow the user to safely assume that the reference will not expire during the program's execution, making it suitable for long-lived data structures or operations that require such guarantees.
28 ///
29 fn galloc_mut(self) -> &'static T {
30 alloc_mut(self)
31 }
32}
33
34#[extension(pub trait AllocForExactSizeIter)]
35impl<T: ExactSizeIterator> T {
36 #[inline(always)]
37 /// Transforms the implementor type by invoking `alloc_iter` and returns a static slice of items.
38 ///
39 /// This method consumes the instance of the type, calling `alloc_iter` which is expected to allocate or collect items into a static slice.
40 /// This process typically involves aggregating items from the iterator and providing a reference that persists for the 'static lifetime, enabling access to the collected items beyond the scope of the function execution.
41 ///
42 fn galloc_scollect(self) -> &'static [T::Item] {
43 alloc_iter(self)
44 }
45}
46
47#[extension(pub trait TryAllocForExactSizeIter)]
48impl<T: ExactSizeIterator<Item=Option<F>>, F> T {
49 #[inline(always)]
50 /// Provides a method to attempt to allocate an iterator's result and collect it into a static slice.
51 ///
52 /// It utilizes `try_alloc_iter` to perform the allocation, returning an `Option` containing a reference to a slice of type `F` if successful or `None` if the allocation fails.
53 /// This operation is likely part of a collection mechanism within the `T` type, which supports the controlled transformation of iterator results into persistent slices, possibly for optimized data handling in memory-sensitive contexts.
54 ///
55 fn galloc_try_scollect(self) -> Option<&'static [F]> {
56 try_alloc_iter(self)
57 }
58}
59
60#[extension(pub trait AllocForIter)]
61impl<T: Iterator> T {
62 #[inline(always)]
63 /// Collects the items of the iterator and stores them in a static slice.
64 ///
65 ///
66 /// This method consumes the iterator from which it is called and collects all of its items using an allocation function.
67 /// The items are stored in a statically allocated slice, which means that the lifetime of the collected items is tied to the entire duration of the program.
68 /// This could be particularly useful in contexts where the collected items need to be immutable and available for the entire runtime, though caution should be taken to ensure memory usage is within acceptable bounds considering the static lifetime.
69 fn galloc_collect(self) -> &'static [T::Item] {
70 alloc_iter2(self)
71 }
72}
73
74
75#[extension(pub trait AllocForStr)]
76impl str {
77 #[inline(always)]
78 /// Provides a method to convert a `str` slice into a `BString` with a static lifetime.
79 ///
80 /// It does so by calling the `as_owned` function on the string slice, which is likely intended to produce an owned version of the string that can be utilized where a borrowed string is not sufficient.
81 /// This transformation is useful in scenarios where a persistent, non-borrowed string is required, for example, for storage in data structures that manage their own memory lifecycle.
82 ///
83 ///
84 fn galloc_owned_str(&self) -> BString<'static> {
85 as_owned(self)
86 }
87 #[inline(always)]
88 /// Returns a reference to a static string by allocating storage for the given string slice.
89 ///
90 /// This method takes a reference to a string slice (`&self`) and allocates it using the `alloc_str` function.
91 /// The returned value is a reference to a static string, which implies that the allocated storage has a `'static` lifetime and remains valid for the entire duration of the program.
92 /// This function is useful when a longer-lived string reference is needed, potentially at the cost of additional memory allocation.
93 ///
94 fn galloc_str(&self) -> &'static str {
95 alloc_str(self)
96 }
97}
98
99#[inline(always)]
100/// Allocates a given value in a thread-local arena.
101///
102///
103/// This function takes a generic value and allocates it within a thread-local storage arena, then returns a static reference to the allocated object.
104/// It leverages the `THR_ARENA` thread-local variable, using its `alloc` method to store the value.
105/// The pointer to the allocated memory is then cast to a mutable pointer, and an unsafe operation is performed to convert it into a static reference.
106/// As Rust's safety guarantees are bypassed here, this code snippet assumes that the reference's lifetime requirements will be properly managed to prevent undefined behavior.
107fn alloc<T>(t: T) -> &'static T {
108 THR_ARENA.with(|arena| {
109 let p = arena.alloc(t) as *mut T;
110 unsafe { p.as_ref::<'static>().unwrap() }
111 })
112}
113
114#[inline(always)]
115/// Allocates a mutable reference to a value on a thread-local storage arena.
116///
117///
118/// This function takes an input of any type `T`, allocates it on a thread-local arena, and returns a mutable reference with a static lifetime.
119/// It uses a thread-local storage mechanism provided by `THR_ARENA` to ensure that the allocated object resides in memory local to the thread, improving performance for concurrent processes.
120/// The allocation is done by casting the allocated value to a mutable raw pointer, and then converting it to a mutable reference with a static lifetime using unsafe operations.
121/// The use of `unsafe` indicates that it is the programmer's responsibility to uphold the memory safety guarantees manually.
122/// This approach is typically employed when fine-grained control over memory allocation and lifetimes is necessary for performance-critical code.
123fn alloc_mut<T>(t: T) -> &'static mut T {
124 THR_ARENA.with(|arena| {
125 let p = arena.alloc(t) as *mut T;
126 unsafe { p.as_mut::<'static>().unwrap() }
127 })
128}
129
130#[inline(always)]
131/// Allocates a slice from an iterator and returns a reference with a static lifetime.
132///
133///
134/// This function takes an iterator implementing `ExactSizeIterator`, allocates a slice using elements from this iterator, and returns a reference to this slice with a `'static` lifetime.
135/// It utilizes a thread-local storage arena (`THR_ARENA`) to handle memory allocation.
136/// Inside the thread-local arena, it calls `alloc_slice_fill_iter` to allocate and fill the slice based on the provided iterator.
137/// The memory address of this allocated slice is then manipulated as a mutable pointer, which is unsafely coerced into a reference with a static lifetime and returned.
138/// This requires caution, as improper lifetime management may lead to undefined behavior.
139fn alloc_iter<T>(iter: impl ExactSizeIterator<Item= T>) -> &'static [T] {
140 THR_ARENA.with(|arena| {
141 let p = arena.alloc_slice_fill_iter(iter) as *mut [T];
142 unsafe { p.as_ref::<'static>().unwrap() }
143 })
144}
145
146#[inline(always)]
147/// Attempts to allocate items from an iterator within a thread-local memory arena.
148///
149/// The function takes an iterator of optional items and tries to collect these items into a `Bump`-allocated vector within a thread-local storage `arena`.
150/// This is achieved by using the `collect_in` method, which collects items into a `Bump` allocator.
151/// If the collection is successful, the function converts the vector into a bump slice, returning a static reference to the slice.
152/// If any item in the iterator is `None`, or if the allocation fails, the function returns `None`, indicating that the items could not be allocated in the bump arena.
153///
154fn try_alloc_iter<T>(iter: impl ExactSizeIterator<Item= Option<T>>) -> Option<&'static [T]> {
155 THR_ARENA.with(|arena| {
156 let p = arena as *const Bump;
157 let vec: Option<BVec<_>> = unsafe { iter.collect_in(p.as_ref::<'static>().unwrap()) };
158 vec.map(|x| x.into_bump_slice())
159 })
160}
161
162#[inline(always)]
163/// Allocates an iterator's items into a bump-allocated slice.
164///
165///
166/// This function leverages a thread-local bump arena to efficiently allocate memory for storing the items produced by the provided iterator.
167/// It uses `collect_in` to gather the iterator's elements within the context of the arena's bump allocation, ensuring the allocation remains valid for the program's lifetime.
168/// A reference to the bump allocator is obtained through unsafe pointer dereferencing, and the iterator's elements are collected into a bump vector (`BVec`).
169/// This vector is then converted into a static reference to a slice stored in the arena memory, offering a performance advantage by reducing heap allocation overhead.
170fn alloc_iter2<T>(iter: impl Iterator<Item= T>) -> &'static [T] {
171 THR_ARENA.with(|arena| {
172 let p = arena as *const Bump;
173 let vec: BVec<_> = unsafe { iter.collect_in(p.as_ref::<'static>().unwrap()) };
174 vec.into_bump_slice()
175 })
176}
177#[inline(always)]
178/// Creates a new `BVec` with a specified capacity.
179///
180/// This function utilizes thread-local storage to access a bump allocator, referred to as `THR_ARENA`, and constructs a `BVec` with a given capacity (`cap`).
181/// It obtains a reference to this arena, which is a bump allocator instance, and uses it to allocate and manage the memory for the `BVec`.
182/// This approach leverages unsafe code to perform pointer dereferencing and ensures that the memory management is efficient for temporary allocations within concurrent environments.
183///
184pub fn new_bvec<T>(cap: usize) -> BVec<'static, T> {
185 THR_ARENA.with(|arena| {
186 let p = arena as *const Bump;
187 unsafe { BVec::with_capacity_in(cap, p.as_ref().unwrap()) }
188 })
189}
190
191#[inline(always)]
192/// Allocates a string in a thread-local arena and returns a reference with a `'static` lifetime.
193///
194/// This function takes a string slice as input and uses a thread-local storage arena to allocate memory for it, ensuring that the memory is associated with the current thread's local context.
195/// The string is then converted into a raw mutable pointer, which is dereferenced into a reference with a `'static` lifetime.
196/// Unsafe operations are required to perform this operation due to manual memory management and lifetime extension, ensuring that the memory is valid throughout the entire program execution within that thread, without being subject to Rust's usual borrowing constraints.
197///
198fn alloc_str(s: &str) -> &'static str {
199 THR_ARENA.with(|arena| {
200 let p = arena.alloc_str(s) as *mut str;
201 unsafe { p.as_ref::<'static>().unwrap() }
202 })
203}
204
205#[inline(always)]
206/// Creates an owned `BString` from a borrowed string slice using a thread-local memory arena.
207///
208/// This function leverages the thread-local `THR_ARENA`, which provides access to a memory allocator (`Bump`) for efficient memory management.
209/// It temporarily takes a reference to this allocator, and within an unsafe block, uses it to convert the input string slice into a `BString`.
210/// The allocator is dereferenced as a static lifetime to ensure it persists as long as necessary, allowing for the creation of a `BString` that is not bound to the typical lifetime of the input slice.
211/// This approach improves performance by reducing heap allocations for short-lived objects in multi-threaded scenarios.
212///
213fn as_owned(s: &str) -> BString<'static> {
214 THR_ARENA.with(|arena| {
215 let p = arena as *const Bump;
216 unsafe { BString::from_str_in(s, p.as_ref::<'static>().unwrap()) }
217 })
218}
219
220// fn alloc_str_u8(s: impl ExactSizeIterator<Item=u8>) -> &'static str {
221// THR_ARENA.with(|arena| {
222// let p = unsafe { from_utf8_unchecked(arena.alloc_slice_fill_iter(s)) } as *const str;
223// unsafe { p.as_ref::<'static>().unwrap() }
224// })
225// }
226
227// fn alloc_iter_mut<T>(iter: impl ExactSizeIterator<Item= T>) -> &'static mut [T] {
228// THR_ARENA.with(|arena| {
229// let p = arena.alloc_slice_fill_iter(iter) as *mut [T];
230// unsafe { p.as_mut::<'static>().unwrap() }
231// })
232// }
233
234/// Converts an iterator of characters into a static string stored in a memory arena.
235///
236///
237/// This function uses a thread-local arena to safely collect characters from an iterator into a `BString`, a specialized string container.
238/// The `collec_in` method is invoked with an unsafe context to ensure that memory allocation aligns properly within the arena's boundaries.
239/// The function ultimately returns a static string reference derived from converting the `BString` into a bump-allocated string, ensuring efficient memory usage and lifetime management through the arena while retaining a 'static lifetime.
240fn collect_str_in_arena0(iter: impl Iterator<Item= char>) -> &'static str {
241 THR_ARENA.with(|arena| {
242 let p = arena as *const Bump;
243 let vec: BString<'static> = unsafe { iter.collect_in::<BString>(p.as_ref::<'static>().unwrap()) };
244 vec.into_bump_str()
245 })
246}
247
248#[extension(pub trait AllocForCharIter)]
249impl<T: Iterator<Item=char>> T {
250 /// Provides a method for collecting a string representation of the implementing item into a global memory arena and returning a static string reference.
251 ///
252 /// The `galloc_collect_str` function utilizes an underlying function, `collect_str_in_arena0`, to manage the allocation and lifetime of the string within a predefined arena, enabling efficient memory usage that allows the returned string to have a `'static` lifetime.
253 /// This approach is particularly useful in scenarios where the string's long-term immutability and accessibility are needed across different parts of the program without reallocation.
254 ///
255 ///
256 fn galloc_collect_str(self) -> &'static str {
257 collect_str_in_arena0(self)
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 #[test]
265 fn test_alloc() {
266 let i = alloc(1isize);
267 assert!(*i == 1)
268 }
269}
270