rarena_allocator/options.rs
1use core::mem;
2
3use crate::align_offset;
4
5use super::{Allocator, Error};
6
7/// Unknown freelist error.
8#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
9#[repr(transparent)]
10pub struct UnknownFreelist(());
11
12impl core::fmt::Display for UnknownFreelist {
13 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14 write!(f, "unknown freelist")
15 }
16}
17
18#[cfg(feature = "std")]
19impl std::error::Error for UnknownFreelist {}
20
21/// The freelist configuration for the ARENA.
22#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)]
23#[repr(u8)]
24#[non_exhaustive]
25pub enum Freelist {
26 /// Disable freelist, once main memory is consumed out, then this ARENA cannot allocate anymore.
27 None = 0,
28
29 /// A lock-free linked list which ordered by segment size (descending), when the main memory is consumed out, the following allocation will just use the head (largest segment) from freelist.
30 #[default]
31 Optimistic = 1,
32
33 /// A lock-free linked list which ordered by segment size (ascending), when the main memory is consumed out, the following allocation will find the most suitable segment from freelist.
34 Pessimistic = 2,
35}
36
37impl TryFrom<u8> for Freelist {
38 type Error = UnknownFreelist;
39
40 fn try_from(value: u8) -> Result<Self, Self::Error> {
41 Ok(match value {
42 0 => Self::None,
43 1 => Self::Optimistic,
44 2 => Self::Pessimistic,
45 _ => return Err(UnknownFreelist(())),
46 })
47 }
48}
49
50/// Options for creating an ARENA
51#[derive(Debug, Clone, Copy)]
52pub struct Options {
53 maximum_alignment: usize,
54 pub(crate) capacity: Option<u32>,
55 minimum_segment_size: u32,
56 maximum_retries: u8,
57 magic_version: u16,
58 unify: bool,
59 freelist: Freelist,
60 reserved: u32,
61
62 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
63 lock_meta: bool,
64 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
65 create_new: bool,
66 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
67 create: bool,
68 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
69 read: bool,
70 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
71 write: bool,
72 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
73 append: bool,
74 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
75 truncate: bool,
76 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
77 pub(crate) offset: u64,
78 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
79 stack: bool,
80 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
81 huge: Option<u8>,
82 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
83 populate: bool,
84}
85
86impl Default for Options {
87 #[inline]
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl Options {
94 /// Create an options for creating an ARENA with default values.
95 #[inline]
96 pub const fn new() -> Self {
97 Self {
98 maximum_alignment: 8,
99 capacity: None,
100 minimum_segment_size: 20,
101 maximum_retries: 5,
102 unify: false,
103 magic_version: 0,
104 freelist: Freelist::Optimistic,
105 reserved: 0,
106
107 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
108 lock_meta: false,
109 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
110 create_new: false,
111 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
112 create: false,
113 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
114 read: false,
115 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
116 write: false,
117 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
118 append: false,
119 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
120 truncate: false,
121 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
122 offset: 0,
123 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
124 stack: false,
125 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
126 huge: None,
127 #[cfg(all(feature = "memmap", not(target_family = "wasm")))]
128 populate: false,
129 }
130 }
131
132 /// Returns the data offset of the ARENA if the ARENA is in unified memory layout.
133 ///
134 /// See also [`Options::data_offset`].
135 ///
136 /// ## Example
137 ///
138 /// ```rust
139 /// use rarena_allocator::{sync, unsync, Options, Allocator};
140 ///
141 /// // Create a sync ARENA.
142 /// let opts = Options::new().with_capacity(100);
143 /// let data_offset_from_opts = opts.data_offset::<sync::Arena>();
144 /// let arena = opts.alloc::<sync::Arena>().unwrap();
145 /// assert_eq!(data_offset_from_opts, arena.data_offset());
146 ///
147 /// let data_offset_from_opts = opts.data_offset_unify::<sync::Arena>();
148 /// let arena = opts.with_unify(true).alloc::<sync::Arena>().unwrap();
149 /// assert_eq!(data_offset_from_opts, arena.data_offset());
150 ///
151 /// // Create a unsync ARENA.
152 /// let opts = Options::new().with_capacity(100);
153 /// let data_offset_from_opts = opts.data_offset::<unsync::Arena>();
154 /// let arena = opts.alloc::<unsync::Arena>().unwrap();
155 /// assert_eq!(data_offset_from_opts, arena.data_offset());
156 ///
157 /// let data_offset_from_opts = opts.data_offset_unify::<unsync::Arena>();
158 /// let arena = opts.with_unify(true).alloc::<unsync::Arena>().unwrap();
159 /// assert_eq!(data_offset_from_opts, arena.data_offset());
160 /// ```
161 pub fn data_offset_unify<A: Allocator>(&self) -> usize {
162 let reserved = self.reserved() as usize;
163 Self::data_offset_in::<A::Header>(reserved, true)
164 }
165
166 /// Returns the data offset of the ARENA if the ARENA is not in unified memory layout.
167 ///
168 /// As the file backed ARENA will only use the unified memory layout and ignore the unify configuration of `Options`,
169 /// so see also [`Options::data_offset_unify`], if you want to get the data offset of the ARENA in unified memory layout.
170 ///
171 /// ## Example
172 ///
173 /// ```rust
174 /// use rarena_allocator::{sync, unsync, Options, Allocator};
175 ///
176 /// // Create a sync ARENA.
177 /// let opts = Options::new().with_capacity(100);
178 /// let data_offset_from_opts = opts.data_offset::<sync::Arena>();
179 /// let arena = opts.alloc::<sync::Arena>().unwrap();
180 /// assert_eq!(data_offset_from_opts, arena.data_offset());
181 ///
182 /// let data_offset_from_opts = opts.data_offset_unify::<sync::Arena>();
183 /// let arena = opts.with_unify(true).alloc::<sync::Arena>().unwrap();
184 /// assert_eq!(data_offset_from_opts, arena.data_offset());
185 ///
186 /// // Create a unsync ARENA.
187 /// let opts = Options::new().with_capacity(100);
188 /// let data_offset_from_opts = opts.data_offset::<unsync::Arena>();
189 /// let arena = opts.alloc::<unsync::Arena>().unwrap();
190 /// assert_eq!(data_offset_from_opts, arena.data_offset());
191 ///
192 /// let data_offset_from_opts = opts.data_offset_unify::<unsync::Arena>();
193 /// let arena = opts.with_unify(true).alloc::<unsync::Arena>().unwrap();
194 /// assert_eq!(data_offset_from_opts, arena.data_offset());
195 /// ```
196 pub fn data_offset<A: Allocator>(&self) -> usize {
197 let reserved = self.reserved() as usize;
198 Self::data_offset_in::<A::Header>(reserved, false)
199 }
200
201 #[inline]
202 fn data_offset_in<H>(reserved: usize, unify: bool) -> usize {
203 if unify {
204 let offset = align_offset::<H>(reserved as u32) as usize + mem::align_of::<H>();
205 offset + mem::size_of::<H>()
206 } else {
207 reserved + 1
208 }
209 }
210
211 /// Set the reserved of the ARENA.
212 ///
213 /// The reserved is used to configure the start position of the ARENA. This is useful
214 /// when you want to add some bytes before the ARENA, e.g. when using the memory map file backed ARENA,
215 /// you can set the reserved to the size to `8` to store a 8 bytes checksum.
216 ///
217 /// The default reserved is `0`.
218 ///
219 /// ## Example
220 ///
221 /// ```rust
222 /// use rarena_allocator::Options;
223 ///
224 /// let opts = Options::new().with_reserved(8);
225 /// ```
226 #[inline]
227 pub const fn with_reserved(mut self, reserved: u32) -> Self {
228 self.reserved = reserved;
229 self
230 }
231
232 /// Set the maximum alignment of the ARENA.
233 ///
234 /// If you are trying to allocate a `T` which requires a larger alignment than this value,
235 /// then will lead to `read_unaligned`, which is undefined behavior on some platforms.
236 ///
237 /// The alignment must be a power of 2.
238 /// The default maximum alignment is `8`.
239 ///
240 /// ## Example
241 ///
242 /// ```
243 /// use rarena_allocator::Options;
244 ///
245 /// let opts = Options::new().with_maximum_alignment(16);
246 /// ```
247 #[inline]
248 pub const fn with_maximum_alignment(mut self, alignment: usize) -> Self {
249 assert!(
250 alignment.is_power_of_two(),
251 "alignment must be a power of 2"
252 );
253 self.maximum_alignment = alignment;
254 self
255 }
256
257 /// Set the capacity of the ARENA. If the ARENA is backed by a memory map and the original file size is less than the capacity,
258 /// then the file will be resized to the capacity.
259 ///
260 /// For vec backed ARENA and anonymous memory map backed ARENA, this configuration is required.
261 ///
262 /// For file backed ARENA, this configuration is optional, if the capacity is not set, then the capacity will be the original file size.
263 ///
264 /// The capacity must be greater than the minimum capacity of the ARENA.
265 ///
266 /// ## Example
267 ///
268 /// ```
269 /// use rarena_allocator::Options;
270 ///
271 /// let opts = Options::new().with_capacity(2048);
272 /// ```
273 #[inline]
274 pub const fn with_capacity(mut self, capacity: u32) -> Self {
275 self.capacity = Some(capacity);
276 self
277 }
278
279 /// Set or unset the capacity of the ARENA. If the ARENA is backed by a memory map and the original file size is less than the capacity,
280 /// then the file will be resized to the capacity.
281 ///
282 /// For vec backed ARENA and anonymous memory map backed ARENA, this configuration is required.
283 ///
284 /// For file backed ARENA, this configuration is optional, if the capacity is not set, then the capacity will be the original file size.
285 ///
286 /// The capacity must be greater than the minimum capacity of the ARENA.
287 ///
288 /// ## Example
289 ///
290 /// ```
291 /// use rarena_allocator::Options;
292 ///
293 /// let opts = Options::new().with_capacity(2048);
294 ///
295 /// /// some business logic
296 /// let opts = opts.maybe_capacity(None);
297 /// ```
298 #[inline]
299 pub const fn maybe_capacity(mut self, capacity: Option<u32>) -> Self {
300 self.capacity = capacity;
301 self
302 }
303
304 /// Set the minimum segment size of the ARENA.
305 ///
306 /// This value controls the size of the holes.
307 ///
308 /// The default minimum segment size is `48 bytes`.
309 ///
310 /// ## Example
311 ///
312 /// ```
313 /// use rarena_allocator::Options;
314 ///
315 /// let opts = Options::new().with_minimum_segment_size(64);
316 /// ```
317 #[inline]
318 pub const fn with_minimum_segment_size(mut self, minimum_segment_size: u32) -> Self {
319 self.minimum_segment_size = minimum_segment_size;
320 self
321 }
322
323 /// Set the maximum retries of the ARENA.
324 ///
325 /// This value controls how many times the ARENA will retry to allocate from slow path.
326 ///
327 /// The default maximum retries is `5`.
328 ///
329 /// ## Example
330 ///
331 /// ```
332 /// use rarena_allocator::Options;
333 ///
334 /// let opts = Options::new().with_maximum_retries(10);
335 /// ```
336 #[inline]
337 pub const fn with_maximum_retries(mut self, maximum_retries: u8) -> Self {
338 self.maximum_retries = maximum_retries;
339 self
340 }
341
342 /// Set if use the unify memory layout of the ARENA.
343 ///
344 /// File backed ARENA has different memory layout with other kind backed ARENA,
345 /// set this value to `true` will unify the memory layout of the ARENA, which means
346 /// all kinds of backed ARENA will have the same memory layout.
347 ///
348 /// This value will be ignored if the ARENA is backed by a file backed memory map.
349 ///
350 /// The default value is `false`.
351 ///
352 /// ## Example
353 ///
354 /// ```
355 /// use rarena_allocator::Options;
356 ///
357 /// let opts = Options::new().with_unify(true);
358 /// ```
359 #[inline]
360 pub const fn with_unify(mut self, unify: bool) -> Self {
361 self.unify = unify;
362 self
363 }
364
365 /// Set the external version of the ARENA,
366 /// this is used by the application using [`Allocator`](crate::Allocator)
367 /// to ensure that it doesn't open the [`Allocator`](crate::Allocator)
368 /// with incompatible data format.
369 ///
370 /// The default value is `0`.
371 ///
372 /// ## Example
373 ///
374 /// ```rust
375 /// use rarena_allocator::Options;
376 ///
377 /// let opts = Options::new().with_magic_version(1);
378 /// ```
379 #[inline]
380 pub const fn with_magic_version(mut self, magic_version: u16) -> Self {
381 self.magic_version = magic_version;
382 self
383 }
384
385 /// Set the freelist configuration for the ARENA.
386 /// The default freelist is `Freelist::Optimistic`.
387 ///
388 /// ## Example
389 ///
390 /// ```rust
391 /// use rarena_allocator::{Options, Freelist};
392 ///
393 /// let opts = Options::new().with_freelist(Freelist::Pessimistic);
394 /// ```
395 #[inline]
396 pub const fn with_freelist(mut self, freelist: Freelist) -> Self {
397 self.freelist = freelist;
398 self
399 }
400
401 /// Get the reserved of the ARENA.
402 ///
403 /// The reserved is used to configure the start position of the ARENA. This is useful
404 /// when you want to add some bytes before the ARENA, e.g. when using the memory map file backed ARENA,
405 /// you can set the reserved to the size to `8` to store a 8 bytes checksum.
406 ///
407 /// The default reserved is `0`.
408 ///
409 /// ## Example
410 ///
411 /// ```rust
412 /// use rarena_allocator::Options;
413 ///
414 /// let opts = Options::new().with_reserved(8);
415 ///
416 /// assert_eq!(opts.reserved(), 8);
417 /// ```
418 #[inline]
419 pub const fn reserved(&self) -> u32 {
420 self.reserved
421 }
422
423 /// Get the maximum alignment of the ARENA.
424 ///
425 /// ## Example
426 ///
427 /// ```
428 /// use rarena_allocator::Options;
429 ///
430 /// let opts = Options::new().with_maximum_alignment(16);
431 ///
432 /// assert_eq!(opts.maximum_alignment(), 16);
433 /// ```
434 #[inline]
435 pub const fn maximum_alignment(&self) -> usize {
436 self.maximum_alignment
437 }
438
439 /// Get the capacity of the ARENA.
440 ///
441 /// ## Example
442 ///
443 /// ```
444 /// use rarena_allocator::Options;
445 ///
446 /// let opts = Options::new().with_capacity(2048);
447 ///
448 /// assert_eq!(opts.capacity(), 2048);
449 /// ```
450 #[inline]
451 pub const fn capacity(&self) -> u32 {
452 match self.capacity {
453 Some(capacity) => capacity,
454 None => 0,
455 }
456 }
457
458 /// Get the minimum segment size of the ARENA.
459 ///
460 /// ## Example
461 ///
462 /// ```
463 /// use rarena_allocator::Options;
464 ///
465 /// let opts = Options::new().with_minimum_segment_size(64);
466 ///
467 /// assert_eq!(opts.minimum_segment_size(), 64);
468 /// ```
469 #[inline]
470 pub const fn minimum_segment_size(&self) -> u32 {
471 self.minimum_segment_size
472 }
473
474 /// Get the maximum retries of the ARENA.
475 /// This value controls how many times the ARENA will retry to allocate from slow path.
476 ///
477 /// The default maximum retries is `5`.
478 ///
479 /// ## Example
480 ///
481 /// ```
482 /// use rarena_allocator::Options;
483 ///
484 /// let opts = Options::new().with_maximum_retries(10);
485 ///
486 /// assert_eq!(opts.maximum_retries(), 10);
487 /// ```
488 #[inline]
489 pub const fn maximum_retries(&self) -> u8 {
490 self.maximum_retries
491 }
492
493 /// Get if use the unify memory layout of the ARENA.
494 ///
495 /// File backed ARENA has different memory layout with other kind backed ARENA,
496 /// set this value to `true` will unify the memory layout of the ARENA, which means
497 /// all kinds of backed ARENA will have the same memory layout.
498 ///
499 /// This value will be ignored if the ARENA is backed by a file backed memory map.
500 ///
501 /// The default value is `false`.
502 ///
503 /// ## Example
504 ///
505 /// ```rust
506 /// use rarena_allocator::Options;
507 ///
508 /// let opts = Options::new().with_unify(true);
509 ///
510 /// assert_eq!(opts.unify(), true);
511 /// ```
512 #[inline]
513 pub const fn unify(&self) -> bool {
514 self.unify
515 }
516
517 /// Get the external version of the ARENA,
518 /// this is used by the application using [`Allocator`](crate::Allocator)
519 /// to ensure that it doesn't open the [`Allocator`](crate::Allocator)
520 /// with incompatible data format.
521 ///
522 /// The default value is `0`.
523 ///
524 /// ## Example
525 ///
526 /// ```rust
527 /// use rarena_allocator::Options;
528 ///
529 /// let opts = Options::new().with_magic_version(1);
530 ///
531 /// assert_eq!(opts.magic_version(), 1);
532 /// ```
533 #[inline]
534 pub const fn magic_version(&self) -> u16 {
535 self.magic_version
536 }
537
538 /// Get the freelist configuration for the ARENA.
539 ///
540 /// ## Example
541 ///
542 /// ```rust
543 /// use rarena_allocator::{Options, Freelist};
544 ///
545 /// let opts = Options::new().with_freelist(Freelist::Pessimistic);
546 ///
547 /// assert_eq!(opts.freelist(), Freelist::Pessimistic);
548 /// ```
549 #[inline]
550 pub const fn freelist(&self) -> Freelist {
551 self.freelist
552 }
553}
554
555macro_rules! constructor {
556 ($this:ident.$fn:ident ($($path:ident)?)) => {{
557 $crate::memory::Memory::<A::RefCounter, A::PathRefCounter, A::Header>::$fn($($path,)? $this).map(::core::convert::Into::into)
558 }};
559}
560
561impl Options {
562 /// Create a new [`Allocator`](super::Allocator) which is backed by a `Vec`.
563 ///
564 /// ## Example
565 ///
566 /// ```rust
567 /// use rarena_allocator::{sync, unsync, Options};
568 ///
569 /// // Create a sync ARENA.
570 /// let arena = Options::new().with_capacity(100).alloc::<sync::Arena>().unwrap();
571 ///
572 /// // Create a unsync ARENA.
573 /// let arena = Options::new().with_capacity(100).alloc::<unsync::Arena>().unwrap();
574 /// ```
575 #[inline]
576 pub fn alloc<A: Allocator>(self) -> Result<A, Error> {
577 constructor!(self.alloc())
578 }
579}
580
581#[cfg(all(feature = "memmap", not(target_family = "wasm")))]
582mod open_options;