diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/arena.c | 203 | ||||
-rw-r--r-- | lib/arena.h | 51 | ||||
-rw-r--r-- | lib/sv.c | 158 | ||||
-rw-r--r-- | lib/sv.h | 70 | ||||
-rw-r--r-- | lib/vec.c | 38 | ||||
-rw-r--r-- | lib/vec.h | 30 |
6 files changed, 550 insertions, 0 deletions
diff --git a/lib/arena.c b/lib/arena.c new file mode 100644 index 0000000..aa2d775 --- /dev/null +++ b/lib/arena.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-05 + * Description: Implementations for memory models. + */ + +#include <lib/arena.h> + +#include <malloc.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +page_t *page_create(u64 size) +{ + size = MAX(size, PAGE_DEFAULT_SIZE); + page_t *page = calloc(1, sizeof(*page) + size); + page->next = NULL; + page->size = 0; + page->capacity = size; + return page; +} + +void page_resize(page_t **page, u64 size) +{ + if (!page) + return; + else if (!(*page)) + *page = page_create(size); + else if (page[0]->capacity < size) + { + page[0]->capacity = MAX(size, page[0]->capacity * 1.5); + page[0] = realloc(page[0], sizeof(*page[0]) + page[0]->capacity); + } +} + +i64 page_append(page_t *page, void *data, u64 size) +{ + if (!page || page->size + size >= page->capacity) + return -1; + if (data) + memcpy(page->data + page->size, data, size); + u64 ptr = page->size; + page->size += size; + return ptr; +} + +u64 page_rappend(page_t **page, void *data, u64 size) +{ + page_resize(page, page[0]->size + size); + if (data) + memcpy(page[0]->data + page[0]->size, data, size); + u64 ptr = page[0]->size; + page[0]->size += size; + return ptr; +} + +void *arena_alloc(arena_t *arena, u64 size) +{ + if (!arena) + return NULL; + else if (!arena->start) + { + arena->start = page_create(1); + arena->end = arena->start; + } + + page_t *best_fit; + for (best_fit = arena->start; best_fit; best_fit = best_fit->next) + if (best_fit->size + size + MEMORY_ALIGNMENT < best_fit->capacity) + break; + + if (!best_fit) + { + best_fit = page_create(size); + arena->end->next = best_fit; + arena->end = best_fit; + } + + // NOTE: Fsanitize has a hissy fit if we don't align memory "correctly" + u64 offset = 0; + + if (size % MEMORY_ALIGNMENT == 0) + { + u64 div = (((u64)(best_fit->data + best_fit->size)) % MEMORY_ALIGNMENT); + if (div != 0) + offset = MEMORY_ALIGNMENT - div; + } + + void *start = best_fit->data + best_fit->size + offset; + + best_fit->size += size + offset; + + return start; +} + +void *arena_realloc(arena_t *arena, void *ptr, u64 oldsize, u64 newsize) +{ + if (!ptr) + return arena_alloc(arena, newsize); + else if (!arena || !arena->start) + return NULL; + else if (newsize <= oldsize) + // No need to change anything. + return ptr; + + bool copy_into_new = true; + void *start = NULL; + page_t *old_page = NULL, *best_fit = NULL; + + for (page_t *page = arena->start; page; page = page->next) + { + if (!best_fit && page->size + newsize < page->capacity) + best_fit = page; + if (page->data <= (u8 *)ptr && + (u8 *)(ptr) + oldsize <= page->data + page->capacity) + old_page = page; + } + + // If the old page exists, ptr is the latest allocation in it, and it has + // enough space to contain the new size, just resize and return. + if (old_page && old_page->data + old_page->size - oldsize == ptr && + old_page->size - oldsize + newsize < old_page->capacity) + { + start = ptr; + old_page->size += newsize - oldsize; + copy_into_new = false; + } + else + { + if (!old_page) + copy_into_new = false; + if (!best_fit) + { + best_fit = page_create(newsize); + arena->end->next = best_fit; + arena->end = best_fit; + } + + start = best_fit->data + best_fit->size; + best_fit->size += newsize; + } + + if (copy_into_new) + { + memcpy(start, ptr, oldsize); + memset(start + oldsize, 0, newsize - oldsize); + } + + return start; +} + +void arena_attach(arena_t *arena, page_t *page) +{ + if (!arena || !page) + return; + else if (!arena->start || !arena->end) + { + arena->start = page; + arena->end = page; + } + else + { + page->next = arena->start; + arena->start = page; + } +} + +void arena_reset(arena_t *arena) +{ + if (!arena || !arena->start) + return; + + for (page_t *cur = arena->start; cur; cur = cur->next) + { + if (cur->size == 0) + continue; + cur->size = 0; + memset(cur->data, 0, cur->capacity); + } +} + +void arena_cleanup(arena_t *arena) +{ + if (!arena || !arena->start) + return; + + for (page_t *cur = arena->start, *next = NULL; cur; + next = cur->next, free(cur), cur = next) + continue; + + memset(arena, 0, sizeof(*arena)); +} diff --git a/lib/arena.h b/lib/arena.h new file mode 100644 index 0000000..3b0724f --- /dev/null +++ b/lib/arena.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-05 + * Description: Arena + */ + +#ifndef ARENA_H +#define ARENA_H + +#include <base.h> +#include <stdio.h> + +#define PAGE_DEFAULT_SIZE 1024 +#define MEMORY_ALIGNMENT 8 + +typedef struct Page +{ + struct Page *next; + u64 size, capacity; + u8 data[]; +} page_t; + +page_t *page_create(u64 size); +void page_resize(page_t **page, u64 newsize); +// Append - fail (by returning <0) if not enough space. +i64 page_append(page_t *page, void *data, u64 size); +// Append - will resize if necessary +u64 page_rappend(page_t **page, void *data, u64 size); + +typedef struct Aren +{ + page_t *start, *end; +} arena_t; + +void *arena_alloc(arena_t *arena, u64 size); +void *arena_realloc(arena_t *arena, void *ptr, u64 oldsize, u64 newsize); +void *arena_copy(arena_t *arena, void *ptr, u64 size); +void arena_attach(arena_t *arena, page_t *page); +void arena_reset(arena_t *arena); +void arena_cleanup(arena_t *arena); + +#endif diff --git a/lib/sv.c b/lib/sv.c new file mode 100644 index 0000000..b2425b8 --- /dev/null +++ b/lib/sv.c @@ -0,0 +1,158 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-14 + * Description: String View implementation + */ + +#include <lib/sv.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +sv_t sv_make(arena_t *allocator, const char *data, u64 size) +{ + sv_t s = {0}; + if (data) + s = sv_append(allocator, s, data, size); + else + { + s.data = arena_alloc(allocator, size); + s.size = size; + } + return s; +} + +sv_t sv_copy(arena_t *allocator, sv_t sv) +{ + return sv_make(allocator, sv.data, sv.size); +} + +sv_t sv_substr(sv_t sv, u64 index, u64 size) +{ + sv_t newsv = {0}; + if (index + size > sv.size) + return newsv; + newsv.data = sv.data + index; + newsv.size = size; + return newsv; +} + +sv_t sv_cut(sv_t sv, u64 index) +{ + return sv_substr(sv, index, sv.size < index ? 0 : sv.size - index); +} + +sv_t sv_chop(sv_t sv, u64 size) +{ + return sv_substr(sv, 0, size); +} + +sv_t sv_concat(arena_t *allocator, sv_t a, sv_t b) +{ + sv_t c = sv_make(allocator, a.data, a.size + b.size); + memcpy(c.data + a.size, b.data, b.size); + return c; +} + +sv_t sv_append(arena_t *allocator, sv_t sv, const char *data, u64 size) +{ + if (!allocator) + return (sv_t){0}; + + sv_t newsv = {0}; + newsv.size = sv.size + size; + newsv.data = arena_realloc(allocator, sv.data, sv.size, newsv.size); + if (data) + memcpy(newsv.data + sv.size, data, size); + return newsv; +} + +sv_t sv_prepend(arena_t *allocator, sv_t sv, const char *data, u64 size) +{ + if (!allocator) + return (sv_t){0}; + + // TODO: Can we make this cheaper to do? + sv_t newsv = sv_make(allocator, NULL, size + sv.size); + // Copy over `data` to the left side + memcpy(newsv.data, data, size); + // Copy old string to the right side + if (sv.data) + memcpy(newsv.data + size, sv.data, sv.size); + + return newsv; +} + +sv_t sv_fmt(arena_t *allocator, char *fmt, ...) +{ + if (!allocator) + return (sv_t){0}; + + va_list ap_1, ap_2; + va_start(ap_1, fmt); + + va_copy(ap_2, ap_1); + u64 size = vsnprintf(NULL, 0, fmt, ap_2); + va_end(ap_2); + + sv_t sv = sv_make(allocator, NULL, size); + vsprintf(sv.data, fmt, ap_1); + va_end(ap_1); + + return sv; +} + +i64 sv_find_substr(const sv_t sv, const sv_t substr) +{ + if (substr.size == 0) + return 0; + else if (sv.size < substr.size) + return -1; + else if (sv.size == substr.size) + return strncmp(sv.data, substr.data, sv.size) == 0 ? 0 : -1; + + for (u64 i = 0; i < (sv.size - substr.size); ++i) + if (strncmp(sv.data + i, substr.data, substr.size) == 0) + return i; + return -1; +} + +i64 sv_find_subcstr(const sv_t sv, const char *substr, u64 size) +{ + return sv_find_substr(sv, SV((char *)substr, size)); +} + +i64 sv_find_any(const sv_t sv, const char *bag) +{ + for (u64 i = 0; i < sv.size; ++i) + if (strchr(bag, sv.data[i])) + return i; + return -1; +} + +u64 sv_while(const sv_t sv, bool (*pred)(char)) +{ + u64 i; + for (i = 0; i < sv.size && pred(sv.data[i]); ++i) + continue; + return i; +} + +u64 sv_till(const sv_t sv, bool (*pred)(char)) +{ + u64 i; + for (i = 0; i < sv.size && !pred(sv.data[i]); ++i) + continue; + return i; +} diff --git a/lib/sv.h b/lib/sv.h new file mode 100644 index 0000000..3293b1c --- /dev/null +++ b/lib/sv.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-14 + * Description: String Views + */ + +#ifndef SV_H +#define SV_H + +#include <lib/arena.h> + +typedef struct SV +{ + u64 size; + char *data; +} sv_t; + +#define SV(DATA, SIZE) ((const sv_t){.size = (SIZE), .data = (DATA)}) +#define SV_FMT(SV) (int)(SV).size, (SV).data +#define PR_SV "%.*s" + +sv_t sv_make(arena_t *allocator, const char *data, u64 size); +sv_t sv_copy(arena_t *allocator, sv_t sv); +sv_t sv_append(arena_t *allocator, sv_t sv, const char *data, u64 size); +sv_t sv_prepend(arena_t *allocator, sv_t sv, const char *data, u64 size); + +/** + * @brief Concatenate two string views, returning that concatenation. + + * Allocates memory. + */ +sv_t sv_concat(arena_t *allocator, sv_t a, sv_t b); + +/** + * @brief Allocates a string view with the given `printf` format. + */ +sv_t sv_fmt(arena_t *allocator, char *fmt, ...); + +/** + * @brief Constructs a new string view at a different offset. Does not allocate + * new memory. + */ +sv_t sv_substr(sv_t sv, u64 index, u64 size); +/** + * @brief Return a string view INDEX characters ahead (i.e. cut the string from + * the left). + */ +sv_t sv_cut(sv_t sv, u64 index); +/** + * @brief Return a string view with SIZE (i.e. chopping the string from the + * right) + */ +sv_t sv_chop(sv_t sv, u64 size); + +i64 sv_find_substr(const sv_t sv, const sv_t substr); +i64 sv_find_subcstr(const sv_t sv, const char *substr, u64 size); +i64 sv_find_any(const sv_t sv, const char *bag); +u64 sv_while(const sv_t sv, bool (*pred)(char)); +u64 sv_till(const sv_t sv, bool (*pred)(char)); + +#endif diff --git a/lib/vec.c b/lib/vec.c new file mode 100644 index 0000000..551f679 --- /dev/null +++ b/lib/vec.c @@ -0,0 +1,38 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-15 + * Description: Vector implementation + */ + +#include <lib/vec.h> + +#include <malloc.h> +#include <string.h> + +void vec_reserve(arena_t *allocator, vec_t *vec, u32 size) +{ + if (vec->cap - vec->size < size) + { + u32 old_cap = vec->cap; + vec->cap = MAX(vec->cap * 1.5, vec->size + size); + vec->data = arena_realloc(allocator, vec->data, old_cap, vec->cap); + } +} + +u32 vec_append(arena_t *allocator, vec_t *vec, const void *data, u32 size) +{ + vec_reserve(allocator, vec, size); + memcpy(vec->data + vec->size, data, size); + u32 ptr = vec->size; + vec->size += size; + return ptr; +} diff --git a/lib/vec.h b/lib/vec.h new file mode 100644 index 0000000..20a3523 --- /dev/null +++ b/lib/vec.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-15 + * Description: Vectors (dynamic sized arrays) + */ + +#ifndef VEC_H +#define VEC_H + +#include <lib/arena.h> + +typedef struct +{ + u32 size, cap; + u8 *data; +} vec_t; + +void vec_reserve(arena_t *alloactor, vec_t *vec, u32 size); +u32 vec_append(arena_t *allocator, vec_t *vec, const void *data, u32 size); + +#endif |