lib/std/lang/alloc.rad 4.2 KiB raw
1
//! Bump allocator for compiler data structures.
2
//!
3
//! Provides a simple arena-style allocator that allocates from a contiguous
4
//! byte buffer. Memory is never freed individually - the entire arena is
5
//! reset at once. This is ideal for compiler passes where all allocations
6
//! have the same lifetime.
7
@test mod tests;
8
9
use std::mem;
10
11
/// Error thrown by allocator.
12
pub union AllocError {
13
    /// Allocator is out of memory.
14
    OutOfMemory,
15
}
16
17
/// Bump allocator backed by a byte slice.
18
///
19
/// Allocations are made by advancing an offset pointer. Individual allocations
20
/// cannot be freed; instead, the entire arena is reset at once via [`reset`].
21
pub record Arena {
22
    /// Backing storage.
23
    data: *mut [u8],
24
    /// Current allocation offset in bytes.
25
    offset: u32,
26
}
27
28
/// Create a new arena backed by the given byte slice.
29
pub fn new(data: *mut [u8]) -> Arena {
30
    return Arena { data, offset: 0 };
31
}
32
33
/// Allocate `size` bytes with the given alignment.
34
///
35
/// Returns an opaque pointer to the allocated memory. Throws `AllocError` if
36
/// the arena is exhausted. The caller is responsible for casting to the
37
/// appropriate type and initializing the memory.
38
pub fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
39
    assert alignment > 0;
40
    assert size > 0;
41
42
    let aligned = mem::alignUp(arena.offset, alignment);
43
    let newOffset = aligned + size;
44
45
    if newOffset > arena.data.len as u32 {
46
        throw AllocError::OutOfMemory;
47
    }
48
    let base: *mut u8 = &mut arena.data[aligned];
49
    arena.offset = newOffset;
50
51
    return base as *mut opaque;
52
}
53
54
/// Reset the arena, allowing all memory to be reused.
55
///
56
/// Does not zero the memory.
57
pub fn reset(arena: *mut Arena) {
58
    arena.offset = 0;
59
}
60
61
/// Save the current arena state for later restoration.
62
pub fn save(arena: *Arena) -> u32 {
63
    return arena.offset;
64
}
65
66
/// Restore the arena to a previously saved state, reclaiming all
67
/// allocations made since that point.
68
pub fn restore(arena: *mut Arena, savedOffset: u32) {
69
    arena.offset = savedOffset;
70
}
71
72
/// Returns the number of bytes currently allocated.
73
pub fn used(arena: *Arena) -> u32 {
74
    return arena.offset;
75
}
76
77
/// Returns the number of bytes remaining in the arena.
78
pub fn remaining(arena: *Arena) -> u32 {
79
    return arena.data.len as u32 - arena.offset;
80
}
81
82
/// Returns the remaining buffer as a mutable slice.
83
pub fn remainingBuf(arena: *mut Arena) -> *mut [u8] {
84
    return &mut arena.data[arena.offset..];
85
}
86
87
/// Commits `size` bytes of allocation, advancing the offset.
88
/// Use after writing to the buffer returned by [`remainingBuf`].
89
pub fn commit(arena: *mut Arena, size: u32) {
90
    arena.offset += size;
91
}
92
93
/// Allocate a slice of `count` elements, each of `size` bytes with given alignment.
94
///
95
/// Returns a type-erased slice that should be cast to the appropriate `*[T]`.
96
/// The slice length is set to `count` (element count, not bytes).
97
/// Throws `AllocError` if the arena is exhausted.
98
pub fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
99
    if count == 0 {
100
        return &mut [];
101
    }
102
    let ptr = try alloc(arena, size * count, alignment);
103
104
    return @sliceOf(ptr, count);
105
}
106
107
/// Generic allocator interface.
108
///
109
/// Bundles an allocation function with an opaque context pointer so that
110
/// any allocation strategy (arena, free-list, mmap, pool) can be used
111
/// through a uniform interface. The `func` field is called with the context
112
/// pointer, a byte size and an alignment, and must return a pointer to
113
/// the allocated memory or panic on failure.
114
pub record Allocator {
115
    /// Allocation function. Returns a pointer to `size` bytes
116
    /// aligned to `alignment`, or panics on failure.
117
    func: fn(*mut opaque, u32, u32) -> *mut opaque,
118
    /// Opaque context pointer passed to `func`.
119
    ctx: *mut opaque,
120
}
121
122
/// Create an `Allocator` backed by an `Arena`.
123
pub fn arenaAllocator(arena: *mut Arena) -> Allocator {
124
    return Allocator {
125
        func: arenaAllocFn,
126
        ctx: arena as *mut opaque,
127
    };
128
}
129
130
/// Arena allocation function conforming to the `Allocator` interface.
131
fn arenaAllocFn(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque {
132
    let arena = ctx as *mut Arena;
133
    return try! alloc(arena, size, alignment);
134
}