Add arena_free()

This commit is contained in:
Ole Morud
2023-06-04 12:33:44 +02:00
committed by Ole Morud
parent 45e6023d42
commit e5fa743d85
4 changed files with 160 additions and 55 deletions

View File

@@ -1,6 +1,28 @@
CC := gcc all:
CFLAGS := -g -O3 -std=c2x -Wall -Wextra -Wpedantic -Werror -Iinclude
BUILD ?= debug
COMPILER ?= gcc
# ==== set compiler flags ====
# credits Maxim Egorushkin:
# https://stackoverflow.com/questions/48791883/best-practice-for-building-a-make-file/48793058#48793058
CC := $(COMPILER)
# -fsanitize={address,undefined}
CFLAGS.gcc.debug := -Og -ggdb -pg -fsanitize=address -fno-omit-frame-pointer
CFLAGS.gcc.release := -O3 -g -march=native -DNDEBUG
CFLAGS.gcc := -fanalyzer
CFLAGS.clang.debug :=-O0 -g3 -DBACKTRACE -rdynamic
CFLAGS.clang.release :=-O3 -g -march=native -DNDEBUG
CFLAGS.clang :=
CFLAGS := ${CFLAGS.${COMPILER}} ${CFLAGS.${COMPILER}.${BUILD}} \
-Iinclude \
-std=c2x \
-Wall -Wextra -Wpedantic -Werror
BUILD_DIR := build BUILD_DIR := build
BIN_DIR := lib BIN_DIR := lib
@@ -9,6 +31,8 @@ TEST_DIR := test
OBJS := $(BUILD_DIR)/arena.o OBJS := $(BUILD_DIR)/arena.o
FPIC_OBJS := $(BUILD_DIR)/fpic/arena.o FPIC_OBJS := $(BUILD_DIR)/fpic/arena.o
all : static
static : $(BIN_DIR)/libarena.a static : $(BIN_DIR)/libarena.a
dynamic : $(BIN_DIR)/libarena.so dynamic : $(BIN_DIR)/libarena.so
@@ -22,7 +46,7 @@ $(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR)
ar cr $@ $^ ar cr $@ $^
$(BIN_DIR)/libarena.so : $(FPIC_OBJS) | $(BIN_DIR) $(BIN_DIR)/libarena.so : $(FPIC_OBJS) | $(BIN_DIR)
$(CC) -shared -o $@ $^ $(CC) -o $@ -shared $(CFLAGS) $^
$(BUILD_DIR)/%.o : src/%.c | $(BUILD_DIR) $(BUILD_DIR)/%.o : src/%.c | $(BUILD_DIR)
$(CC) -o $@ -c $(CFLAGS) $< $(CC) -o $@ -c $(CFLAGS) $<

View File

@@ -3,19 +3,22 @@
#define ARENA_H #define ARENA_H
#include <stddef.h> // ptrdiff_t #include <stddef.h> // ptrdiff_t
#include <stdint.h> // uintptr_t
typedef unsigned char byte_t; #define _WORD_SIZE (sizeof(intptr_t))
// linked list terminology:
// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png
typedef struct arena { typedef struct arena {
size_t next, prev, cap; struct page *head, *last;
void* data; } __attribute__((aligned(_WORD_SIZE))) arena_t;
} __attribute__((aligned(sizeof(void*)))) arena_t;
arena_t arena_new(); arena_t arena_new(void);
void arena_reset(arena_t* a); void arena_reset(arena_t* a);
void* arena_alloc(arena_t* a, size_t len); void* arena_alloc(arena_t* a, size_t len);
void* arena_calloc(arena_t* a, size_t nmemb, size_t size); void* arena_calloc(arena_t* a, size_t nmemb, size_t size);
void* arena_realloc_tail(arena_t* a, size_t len); void* arena_realloc_tail(arena_t* a, size_t len);
void arena_delete(arena_t* a); void arena_delete(arena_t* a);
void arena_free(arena_t* a, void* p);
#endif #endif

View File

@@ -7,79 +7,147 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h>
// (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most // (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most
// compilers it is) // compilers it is)
#define WORD_SIZE (sizeof(intptr_t)) #define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE))
#define ARENA_SIZE ((size_t)(1 * sysconf(_SC_PAGE_SIZE))) #define BIG_PAGE (SYS_PAGE_SIZE + 1)
arena_t arena_new() typedef unsigned char byte_t;
struct page {
void* data;
size_t offset, prev_offset;
struct page* next;
};
// linked list terminology:
// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png
arena_t arena_new(void)
{ {
arena_t a = { struct page* p = malloc(sizeof *p);
.next = 0,
.prev = 0,
.cap = ARENA_SIZE,
.data = malloc(ARENA_SIZE)
};
if (a.data == NULL) if (p == NULL)
exit(errno); exit(errno);
p->next = NULL;
p->offset = 0;
p->prev_offset = 0;
p->data = malloc(SYS_PAGE_SIZE);
if (p->data == NULL)
exit(errno);
arena_t a = {
.head = p,
.last = p,
};
return a; return a;
} }
void _arena_realloc_or_panic(arena_t* a, size_t len) void _arena_new_page(arena_t* a, size_t size)
{ {
// TODO: gracefully recover. /* potentially reuse page from previously
// although unlikely to be recoverable in most use cases reset arena */
a->data = realloc(a->data, len); if (a->head->next != NULL) {
a->head = a->head->next;
a->head->offset = 0;
a->head->prev_offset = 0;
return;
}
if (a->data == NULL) void* tmp = calloc(1, sizeof *(a->head->next));
if (tmp == NULL)
exit(errno); exit(errno);
a->cap = len; a->head->next = tmp;
a->head = a->head->next;
a->head->data = malloc(size);
if (a->head->data == NULL)
exit(errno);
} }
void arena_reset(arena_t* a) void arena_reset(arena_t* a)
{ {
a->next = 0; a->head = a->last;
a->prev = 0; a->head->offset = 0;
a->head->prev_offset = 0;
} }
void* arena_alloc(arena_t* a, size_t len) void* _arena_big_alloc(arena_t* a, size_t size)
{ {
// align len to machine word size _arena_new_page(a, size);
len = (len + WORD_SIZE - 1) & ~(WORD_SIZE - 1); a->head->offset = BIG_PAGE;
fprintf(stderr, "allocating %zu bytes\n", len); a->head->prev_offset = BIG_PAGE;
a->prev = a->next; return a->head->data;
a->next += len; }
if (a->next > a->cap) void* arena_alloc(arena_t* a, size_t size)
_arena_realloc_or_panic(a, a->cap * 2); {
if (size > SYS_PAGE_SIZE)
return _arena_big_alloc(a, size);
return (byte_t*)(a->data) + a->prev; // align size to machine word size
size = (size + _WORD_SIZE - 1) & ~(_WORD_SIZE - 1);
if (a->head->offset > SYS_PAGE_SIZE - size) {
_arena_new_page(a, SYS_PAGE_SIZE);
return arena_alloc(a, size);
}
a->head->prev_offset = a->head->offset;
a->head->offset += size;
return (byte_t*)(a->head->data) + a->head->prev_offset;
} }
void* arena_calloc(arena_t* a, size_t nmemb, size_t size) void* arena_calloc(arena_t* a, size_t nmemb, size_t size)
{ {
void* p = arena_alloc(a, nmemb * size); void* p = arena_alloc(a, nmemb * size);
memset((byte_t*)(a->data) + a->prev, 0, size); memset(p, 0, nmemb * size);
return p; return p;
} }
void* arena_realloc_tail(arena_t* a, size_t len) void* arena_realloc_tail(arena_t* a, size_t len)
{ {
a->next = a->prev; if (a->head->offset == BIG_PAGE) {
void* tmp = realloc(a->head->data, len);
if (tmp == NULL)
exit(errno);
a->head->data = tmp;
return tmp;
}
a->head->offset = a->head->prev_offset;
return arena_alloc(a, len); return arena_alloc(a, len);
} }
void arena_delete(arena_t* a) void arena_delete(arena_t* a)
{ {
free(a->data); struct page* p = a->last;
arena_reset(a);
a->data = NULL; while (p != NULL) {
struct page* next = p->next;
free(p->data);
free(p);
p = next;
}
}
// included for completeness
void arena_free(arena_t* a, void* p)
{
(void)p;
(void)a;
return;
} }

View File

@@ -7,26 +7,36 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
static struct arena default_arena = { 0 };
int main() int main()
{ {
default_arena = arena_new(); arena_t default_arena = arena_new();
printf("\nAttempt to do allocations of 1 byte, cap is %zu, ", default_arena.cap); printf("\nAttempting to allocate and write to 1 byte 1024 times");
for (size_t i = 0; i < 1024; i++) {
char* c = arena_alloc(&default_arena, sizeof *c);
size_t i; if (c == NULL)
for (i = 0; default_arena.next < default_arena.cap; i++) {
char* c = arena_alloc(&default_arena, 1);
*c = i & 0xff;
if (c == NULL) {
err(EXIT_FAILURE, "failed to allocate memory"); err(EXIT_FAILURE, "failed to allocate memory");
}
}
printf("did %zu allocations", i);
printf("\n OK"); *c = i & 0xFF;
}
printf("\n OK!\n");
printf("\nAttempting to allocate and write to `_SC_PAGESIZE+1` bytes");
size_t psz = sysconf(_SC_PAGESIZE) + 1;
char* c = arena_alloc(&default_arena, psz);
if (c == NULL)
err(EXIT_FAILURE, "failed to allocate memory");
for (size_t i = 0; i < psz; i++)
*c = i & 0xFF;
printf("\n OK!\n");
arena_delete(&default_arena);
return 0; return 0;
} }