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;