Compare commits

22 Commits
master ... old

Author SHA1 Message Date
Ole Morud
cf964c4dcc optimize shrinking reallocs 2023-06-17 12:14:28 +02:00
Ole Morud
b3638c3c00 Update makefile and fix analzer complaints 2023-06-05 22:36:42 +02:00
Ole Morud
1fd96ff1ce Fix arena_tail_realloc memory bug
`arena_realloc_tail` exceeding PAGE_SIZE would create a big page,
preceeding calls to `arena_realloc_tail` would then cause memory bugs.
2023-06-05 22:26:10 +02:00
Ole Morud
0cde95de64 Rewrite arena as linked list of pages 2023-06-05 22:26:10 +02:00
Ole Morud
ac8193eb8b Remove print statement 2023-06-05 22:26:08 +02:00
Ole Morud
279618f52d Add arena_free() 2023-06-05 22:26:04 +02:00
Ole Morud
90f72e9593 Add arena_delete() 2023-06-05 22:26:00 +02:00
Ole Morud
c71788ba72 Rewrite everything 2023-06-05 22:26:00 +02:00
Ole Morud
f8c0db9937 Add arena_realloc_tail() 2023-06-03 11:37:06 +02:00
olemorud
bf73807e40 Move header files to include/ 2023-06-02 18:06:27 +02:00
olemorud
680936296f add *.so, *.a to .gitignore 2023-05-15 23:55:55 +02:00
olemorud
7ccb5bed74 Add dynamic and static as makefile targets 2023-05-15 23:04:44 +02:00
olemorud
42cb5d0c4e Add and run WebKit based clang-format 2023-05-15 22:25:54 +02:00
olemorud
98b67e1a53 Makefile: save as ar file 2023-05-15 21:19:53 +02:00
olemorud
846ef41cd4 Update test_arena.c 2023-05-15 20:00:34 +02:00
olemorud
e0a98b0f08 Add compile_flags.txt 2023-05-15 20:00:22 +02:00
olemorud
22fb643c0f Update arena.c, arena.h 2023-05-15 20:00:12 +02:00
olemorud
d5fea00170 Update Makefile 2023-05-15 19:57:56 +02:00
olemorud
1fad6fed5d Add README 2023-05-15 01:04:37 +02:00
olemorud
e0e363a189 Makefile: update CFLAGS 2023-05-15 01:02:09 +02:00
olemorud
b35f63ccc5 add .gitignore 2023-05-15 01:02:09 +02:00
olemorud
6d9059aefc remove memcpy use 2023-05-15 01:02:09 +02:00
11 changed files with 266 additions and 134 deletions

2
.clang-format Normal file
View File

@@ -0,0 +1,2 @@
BasedOnStyle: WebKit

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
obj/
test/
*.o
*.a
*.so

View File

@@ -1,27 +1,72 @@
CC := gcc
CFLAGS := -ggdb -O0 -std=c99 -Wall -Wextra -Wpedantic
all:
OBJS := obj/arena.o obj/alloc_backend.o
BUILD ?= debug
COMPILER ?= gcc
all : test/test_arena
# ==== set compiler flags ====
# credits Maxim Egorushkin:
# https://stackoverflow.com/questions/48791883/best-practice-for-building-a-make-file/48793058#48793058
test/test_arena : src/test_arena.c $(OBJS) | test
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
BIN_DIR := lib
TEST_DIR := test
OBJS := $(BUILD_DIR)/arena.o
FPIC_OBJS := $(BUILD_DIR)/fpic/arena.o
all : static
static : $(BIN_DIR)/libarena.a
dynamic : $(BIN_DIR)/libarena.so
tests : test/test_arena
$(TEST_DIR)/test_arena : src/test_arena.c $(BIN_DIR)/libarena.a | $(TEST_DIR)
$(CC) -o $@ $(CFLAGS) $^
obj/%.o : src/%.c | obj
$(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR)
ar cr $@ $^
$(BIN_DIR)/libarena.so : $(FPIC_OBJS) | $(BIN_DIR)
$(CC) -o $@ -shared $(CFLAGS) $^
$(BUILD_DIR)/%.o : src/%.c | $(BUILD_DIR)
$(CC) -o $@ -c $(CFLAGS) $<
obj:
$(BUILD_DIR)/fpic/%.o : src/%.c | $(BUILD_DIR)/fpic
$(CC) -o $@ -c -fPIC $(CFLAGS) $<
$(BUILD_DIR):
mkdir -p $@
bin:
$(BUILD_DIR)/fpic:
mkdir -p $@
test:
$(BIN_DIR) :
mkdir -p $@
$(TEST_DIR) :
mkdir -p $@
clean:
rm -rf obj bin test
rm -rf $(BUILD_DIR) $(BIN_DIR) $(TEST_DIR)
.PHONY: clean obj test bin all
.PHONY: clean $(BUILD_DIR) test bin all

13
README.md Normal file
View File

@@ -0,0 +1,13 @@
`arena_t* arena_new()`
Initializes and returns new arena
`void arena_reset(arena_t *a)`
Free memory allocated in arena
`void* arena_alloc(arena_t *a, size_t len)`
Allocate new memory using arena

5
compile_flags.txt Normal file
View File

@@ -0,0 +1,5 @@
-Wall
-Wextra
-Wpedantic
-std=c2x
-Iinclude

24
include/arena.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef ARENA_H
#define ARENA_H
#include <stddef.h> // ptrdiff_t
#include <stdint.h> // uintptr_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 {
struct page *head, *last;
} __attribute__((aligned(_WORD_SIZE))) arena_t;
arena_t arena_new(void);
void arena_reset(arena_t* a);
void* arena_alloc(arena_t* a, size_t len);
void* arena_calloc(arena_t* a, size_t nmemb, size_t size);
void* arena_realloc_tail(arena_t* a, size_t len);
void arena_delete(arena_t* a);
void arena_free(arena_t* a, void* p);
#endif

View File

@@ -1,30 +0,0 @@
#include "alloc_backend.h"
#define _GNU_SOURCE
#include <sys/mman.h>
/*
* Separate function to make switching
* allocation backend easier
*
* must take size_t size as an argument
* must return NULL on failure
*/
void* call_alloc_backend(size_t size)
{
void *p = mmap(
NULL,
size,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1, /* man mmap(2): "[...], some implementations require fd to be
-1 if MAP_ANONYMOUS is specified [...]" */
0
);
if (p == MAP_FAILED)
return NULL;
return p;
}

View File

@@ -1,9 +0,0 @@
#ifndef ALLOC_BACKEND_H
#define ALLOC_BACKEND_H
#include <stddef.h>
void* call_alloc_backend(size_t size);
#endif

View File

@@ -1,60 +1,158 @@
#include "arena.h"
#include "alloc_backend.h"
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ARENA_SIZE ((size_t)(128*sysconf(_SC_PAGE_SIZE)))
// (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most
// compilers it is)
#define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE))
/*
* Allocates and returns new arena
*/
struct arena* arena_new()
#define BIG_PAGE (SYS_PAGE_SIZE + 1)
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)
{
size_t size = ARENA_SIZE;
unsigned char *p = call_alloc_backend(size);
struct page* p = malloc(sizeof *p);
if (p == NULL)
return NULL;
exit(errno);
struct arena a = {
.begin = p + sizeof(struct arena),
.next = p + sizeof(struct arena),
.cap = size
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,
};
memcpy(p, &a, sizeof a);
return (struct arena*)p;
return a;
}
/*
* Frees all memory in arena
*/
void arena_reset(struct arena *a)
void _arena_new_page(arena_t* a, size_t size)
{
a->next = a->begin;
}
/*
* Allocate new memory using arena
*/
void* arena_alloc(struct arena *a, size_t len)
{
void *p = a->next;
a->next += len;
if (a->next - a->begin >= a->cap) {
errno = ENOMEM;
return NULL;
/* potentially reuse page from previously
reset arena */
if (a->head->next != NULL) {
a->head = a->head->next;
a->head->offset = 0;
a->head->prev_offset = 0;
return;
}
void* tmp = calloc(1, sizeof *(a->head->next));
if (tmp == NULL)
exit(errno);
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)
{
a->head = a->last;
a->head->offset = 0;
a->head->prev_offset = 0;
}
void* _arena_big_alloc(arena_t* a, size_t size)
{
_arena_new_page(a, size);
a->head->offset = BIG_PAGE;
a->head->prev_offset = BIG_PAGE;
return a->head->data;
}
void* arena_alloc(arena_t* a, size_t size)
{
if (size > SYS_PAGE_SIZE)
return _arena_big_alloc(a, size);
// 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* p = arena_alloc(a, nmemb * size);
memset(p, 0, nmemb * size);
return p;
}
void* arena_realloc_tail(arena_t* a, size_t len)
{
if (len <= (a->head->offset - a->head->prev_offset)) {
a->head->offset = a->head->prev_offset + len;
return (byte_t*)(a->head->data) + a->head->prev_offset;
}
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);
}
void arena_delete(arena_t* a)
{
struct page* p = a->last;
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

@@ -1,19 +0,0 @@
#ifndef ARENA_H
#define ARENA_H
#include <stddef.h> // ptrdiff_t
struct arena {
unsigned char *begin,
*next;
ptrdiff_t cap;
};
struct arena* arena_new();
void arena_reset(struct arena *a);
void* arena_alloc(struct arena *a, size_t len);
#endif

View File

@@ -1,44 +1,42 @@
// _start test_arena_alloc.c
#include "arena.h"
#include <err.h>
#include <stdio.h>
#include <errno.h>
static struct arena *default_arena = NULL;
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
default_arena = arena_new();
arena_t default_arena = arena_new();
if (default_arena == NULL) {
err(errno, "failed to allocate arena");
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);
if (c == NULL)
err(EXIT_FAILURE, "failed to allocate memory");
*c = i & 0xFF;
}
printf("\n OK!\n");
printf("\n%p\n", default_arena->next);
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);
char *ten_A = arena_alloc(default_arena, 11 * sizeof *ten_A);
char *ten_B = arena_alloc(default_arena, 11 * sizeof *ten_B);
char *ten_C = arena_alloc(default_arena, 11 * sizeof *ten_C);
if (c == NULL)
err(EXIT_FAILURE, "failed to allocate memory");
for (size_t i=0; i<10; ++i) {
ten_A[i] = 'A';
ten_B[i] = 'B';
ten_C[i] = 'C';
}
for (size_t i = 0; i < psz; i++)
*c = i & 0xFF;
ten_A[10] = '\0';
ten_B[10] = '\0';
ten_C[10] = '\0';
printf("\n OK!\n");
printf("\n%s %s %s", ten_A, ten_B, ten_C);
printf("\n%p\n", default_arena->next);
arena_reset(default_arena);
printf("\n%p\n", default_arena->next);
arena_delete(&default_arena);
return 0;
}