nbos

nbos

https://git.tonybtw.com/nbos.git git://git.tonybtw.com/nbos.git
4,470 bytes raw
1
#define _GNU_SOURCE
2
#include "arena.h"
3
4
#include <stdarg.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9
10
typedef struct block block;
11
struct block {
12
    block  *next;
13
    size_t  capacity;
14
    size_t  used;
15
};
16
17
struct arena {
18
    block  *head;
19
    block  *first;
20
    size_t  total_used;
21
    size_t  total_cap;
22
};
23
24
static block *block_create(size_t capacity) {
25
    block *b = malloc(sizeof(block) + capacity);
26
    if (b == nullptr) return nullptr;
27
    b->next = nullptr;
28
    b->capacity = capacity;
29
    b->used = 0;
30
    return b;
31
}
32
33
/**
34
 * arena_create() - Allocate a new arena with one initial block.
35
 * @initial_capacity: Bytes of usable space in the first block.
36
 *
37
 * Return: Arena pointer, or NULL on allocation failure.
38
 */
39
arena *arena_create(size_t initial_capacity) {
40
    arena *a = malloc(sizeof(arena));
41
    if (a == nullptr) return nullptr;
42
43
    block *b = block_create(initial_capacity);
44
    if (b == nullptr) {
45
        free(a);
46
        return nullptr;
47
    }
48
49
    a->head = b;
50
    a->first = b;
51
    a->total_used = 0;
52
    a->total_cap = initial_capacity;
53
    return a;
54
}
55
56
/**
57
 * arena_destroy() - Free every block held by the arena.
58
 * @a: Arena to free. NULL is permitted.
59
 */
60
void arena_destroy(arena *a) {
61
    if (a == nullptr) return;
62
    block *b = a->first;
63
    while (b != nullptr) {
64
        block *next = b->next;
65
        free(b);
66
        b = next;
67
    }
68
    free(a);
69
}
70
71
/**
72
 * arena_alloc() - Bump-allocate an aligned chunk from the arena.
73
 * @a: Arena to allocate from.
74
 * @size: Number of bytes to reserve.
75
 * @align: Required alignment in bytes (power of two).
76
 *
77
 * A new block at twice the current capacity (or larger if the request
78
 * itself is larger) is allocated when the head block is exhausted.
79
 *
80
 * Return: Pointer to @size aligned bytes, or NULL on allocation failure.
81
 */
82
void *arena_alloc(arena *a, size_t size, size_t align) {
83
    if (a == nullptr || size == 0) return nullptr;
84
85
    uintptr_t base = (uintptr_t)((char *)(a->head + 1) + a->head->used);
86
    uintptr_t aligned = (base + (align - 1)) & ~(align - 1);
87
    size_t pad = aligned - base;
88
89
    if (a->head->used + pad + size <= a->head->capacity) {
90
        a->head->used += pad + size;
91
        a->total_used += pad + size;
92
        return (void *)aligned;
93
    }
94
95
    size_t new_cap = a->head->capacity * 2;
96
    if (new_cap < size + align) new_cap = size + align;
97
98
    block *b = block_create(new_cap);
99
    if (b == nullptr) return nullptr;
100
101
    b->next = a->head;
102
    a->head = b;
103
    a->total_cap += new_cap;
104
105
    base = (uintptr_t)(b + 1);
106
    aligned = (base + (align - 1)) & ~(align - 1);
107
    pad = aligned - base;
108
109
    b->used = pad + size;
110
    a->total_used += pad + size;
111
    return (void *)aligned;
112
}
113
114
/**
115
 * arena_strdup() - Duplicate a NUL-terminated string into the arena.
116
 * @a: Arena to allocate from.
117
 * @s: Source string. NULL yields NULL.
118
 *
119
 * Return: Owned-by-arena copy of @s, or NULL on allocation failure.
120
 */
121
char *arena_strdup(arena *a, const char *s) {
122
    if (s == nullptr) return nullptr;
123
    size_t len = strlen(s) + 1;
124
    char *out = arena_alloc(a, len, 1);
125
    if (out == nullptr) return nullptr;
126
    memcpy(out, s, len);
127
    return out;
128
}
129
130
/**
131
 * arena_sprintf() - printf into a fresh arena allocation.
132
 * @a: Arena to allocate from.
133
 * @fmt: printf format string.
134
 *
135
 * Return: Owned-by-arena formatted string, or NULL on allocation failure.
136
 */
137
char *arena_sprintf(arena *a, const char *fmt, ...) {
138
    va_list ap;
139
140
    va_start(ap, fmt);
141
    int len = vsnprintf(nullptr, 0, fmt, ap);
142
    va_end(ap);
143
    if (len < 0) return nullptr;
144
145
    char *out = arena_alloc(a, (size_t)len + 1, 1);
146
    if (out == nullptr) return nullptr;
147
148
    va_start(ap, fmt);
149
    vsnprintf(out, (size_t)len + 1, fmt, ap);
150
    va_end(ap);
151
    return out;
152
}
153
154
/**
155
 * arena_reset() - Rewind the arena to empty without releasing memory.
156
 * @a: Arena to reset. NULL is permitted.
157
 *
158
 * Frees every block except the first; the first block's used pointer is
159
 * reset to zero.
160
 */
161
void arena_reset(arena *a) {
162
    if (a == nullptr) return;
163
164
    block *b = a->head;
165
    while (b != a->first) {
166
        block *next = b->next;
167
        free(b);
168
        b = next;
169
    }
170
    a->head = a->first;
171
    a->first->used = 0;
172
    a->total_used = 0;
173
    a->total_cap = a->first->capacity;
174
}
175
176
size_t arena_used(const arena *a)     { return a == nullptr ? 0 : a->total_used; }
177
size_t arena_capacity(const arena *a) { return a == nullptr ? 0 : a->total_cap; }