Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf964c4dcc | ||
|
|
b3638c3c00 | ||
|
|
1fd96ff1ce | ||
|
|
0cde95de64 | ||
|
|
ac8193eb8b | ||
|
|
279618f52d | ||
|
|
90f72e9593 | ||
|
|
c71788ba72 | ||
|
|
f8c0db9937 | ||
|
|
bf73807e40 | ||
|
|
680936296f | ||
|
|
7ccb5bed74 | ||
|
|
42cb5d0c4e | ||
|
|
98b67e1a53 | ||
|
|
846ef41cd4 | ||
|
|
e0a98b0f08 | ||
|
|
22fb643c0f | ||
|
|
d5fea00170 | ||
|
|
1fad6fed5d | ||
|
|
e0e363a189 | ||
|
|
b35f63ccc5 | ||
|
|
6d9059aefc |
2
.clang-format
Normal file
2
.clang-format
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
BasedOnStyle: WebKit
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
obj/
|
||||||
|
test/
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
67
Makefile
67
Makefile
@@ -1,27 +1,72 @@
|
|||||||
|
|
||||||
CC := gcc
|
all:
|
||||||
CFLAGS := -ggdb -O0 -std=c99 -Wall -Wextra -Wpedantic
|
|
||||||
|
|
||||||
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) $^
|
$(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) $<
|
$(CC) -o $@ -c $(CFLAGS) $<
|
||||||
|
|
||||||
obj:
|
$(BUILD_DIR)/fpic/%.o : src/%.c | $(BUILD_DIR)/fpic
|
||||||
|
$(CC) -o $@ -c -fPIC $(CFLAGS) $<
|
||||||
|
|
||||||
|
$(BUILD_DIR):
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
bin:
|
$(BUILD_DIR)/fpic:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
test:
|
$(BIN_DIR) :
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
$(TEST_DIR) :
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
clean:
|
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
13
README.md
Normal 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
5
compile_flags.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-std=c2x
|
||||||
|
-Iinclude
|
||||||
24
include/arena.h
Normal file
24
include/arena.h
Normal 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
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#ifndef ALLOC_BACKEND_H
|
|
||||||
#define ALLOC_BACKEND_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
void* call_alloc_backend(size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
176
src/arena.c
176
src/arena.c
@@ -1,60 +1,158 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "alloc_backend.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.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))
|
||||||
|
|
||||||
/*
|
#define BIG_PAGE (SYS_PAGE_SIZE + 1)
|
||||||
* Allocates and returns new arena
|
|
||||||
*/
|
typedef unsigned char byte_t;
|
||||||
struct arena* arena_new()
|
|
||||||
|
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;
|
struct page* p = malloc(sizeof *p);
|
||||||
|
|
||||||
unsigned char *p = call_alloc_backend(size);
|
|
||||||
|
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return NULL;
|
exit(errno);
|
||||||
|
|
||||||
struct arena a = {
|
p->next = NULL;
|
||||||
.begin = p + sizeof(struct arena),
|
p->offset = 0;
|
||||||
.next = p + sizeof(struct arena),
|
p->prev_offset = 0;
|
||||||
.cap = size
|
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 a;
|
||||||
|
|
||||||
return (struct arena*)p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _arena_new_page(arena_t* a, size_t size)
|
||||||
/*
|
|
||||||
* Frees all memory in arena
|
|
||||||
*/
|
|
||||||
void arena_reset(struct arena *a)
|
|
||||||
{
|
{
|
||||||
a->next = a->begin;
|
/* potentially reuse page from previously
|
||||||
}
|
reset arena */
|
||||||
|
if (a->head->next != NULL) {
|
||||||
|
a->head = a->head->next;
|
||||||
/*
|
a->head->offset = 0;
|
||||||
* Allocate new memory using arena
|
a->head->prev_offset = 0;
|
||||||
*/
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
19
src/arena.h
19
src/arena.h
@@ -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
|
|
||||||
@@ -1,44 +1,42 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// _start test_arena_alloc.c
|
// _start test_arena_alloc.c
|
||||||
#include "arena.h"
|
#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()
|
int main()
|
||||||
{
|
{
|
||||||
default_arena = arena_new();
|
arena_t default_arena = arena_new();
|
||||||
|
|
||||||
if (default_arena == NULL) {
|
printf("\nAttempting to allocate and write to 1 byte 1024 times");
|
||||||
err(errno, "failed to allocate arena");
|
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);
|
if (c == NULL)
|
||||||
char *ten_B = arena_alloc(default_arena, 11 * sizeof *ten_B);
|
err(EXIT_FAILURE, "failed to allocate memory");
|
||||||
char *ten_C = arena_alloc(default_arena, 11 * sizeof *ten_C);
|
|
||||||
|
|
||||||
for (size_t i=0; i<10; ++i) {
|
for (size_t i = 0; i < psz; i++)
|
||||||
ten_A[i] = 'A';
|
*c = i & 0xFF;
|
||||||
ten_B[i] = 'B';
|
|
||||||
ten_C[i] = 'C';
|
|
||||||
}
|
|
||||||
|
|
||||||
ten_A[10] = '\0';
|
printf("\n OK!\n");
|
||||||
ten_B[10] = '\0';
|
|
||||||
ten_C[10] = '\0';
|
|
||||||
|
|
||||||
printf("\n%s %s %s", ten_A, ten_B, ten_C);
|
arena_delete(&default_arena);
|
||||||
printf("\n%p\n", default_arena->next);
|
|
||||||
|
|
||||||
arena_reset(default_arena);
|
|
||||||
|
|
||||||
printf("\n%p\n", default_arena->next);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user