This commit is contained in:
snarmph 2024-11-29 16:10:48 +01:00
parent 82aee127b0
commit a92b119549
99 changed files with 6922 additions and 5723 deletions

131
docs/arena.md Normal file
View file

@ -0,0 +1,131 @@
---
title = Arena
---
# Arena
-----------
An arena is a bump allocator, under the hood it can use one of 3 strategies to allocate its data:
* `Virtual`: allocates with virtual memory, meaning that it reserves the data upfront, but only allocates one page at a time (usually 64 Kb). This is the preferred way to use the arena as it can freely allocate gigabytes of memory for free
* `Malloc`: allocates the memory upfront using malloc
* `Static`: uses a buffer passed by the user instead of allocating
To help with allocating big chunks of data, `arena.h` defines the macros `KB`, `MB`, and `GB`. If you don't need them you can define `ARENA_NO_SIZE_HELPERS`
To create an arena use the macro `arenaMake`:
```c
arena_t varena = arenaMake(ARENA_VIRTUAL, GB(1));
uint8 buffer[1024];
arena_t sarena = arenaMake(ARENA_STATIC, sizeof(buffer), buffer);
```
To allocate use the `alloc` macro. The parameters to allocate are:
* A pointer to the arena
* The type to allocate
* (optional) the number of items to allocate
* (optional) flags:
* `ALLOC_NOZERO`: by default the arena sets the memory to zero before returning, use this if you want to skip it
* `ALLOC_SOFT_FAIL`: by default the arena panics when an allocation fails, meaning that it never returns `NULL`.
* (automatic) the align of the type
* (automatic) the size of the type
Example usage:
```c
// allocate 15 strings
str_t *string_list = alloc(arena, str_t, 15);
// allocate a 1024 bytes buffer
uint8 *buffer = alloc(arena, uint8, 1024);
// allocate a structure
game_t *game = alloc(arena, game_t);
```
The strength of the arena is that it makes it much easier to reason about lifetimes, for instance:
```c
// pass a pointer to the arena for the data that we need to return
WCHAR *win32_get_full_path(arena_t *arena, const char *rel_path) {
// we need a small scratch arena for allocations that
// will be thrown away at the end of this function
uint8 scratch_buf[1024];
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(scratch_buf, scratch_buf));
WCHAR *win32_rel_path = str_to_wchar(&scratch, rel_path);
DWORD pathlen = GetFullPathName(win32_rel_path, 0, NULL, NULL);
WCHAR *fullpath = alloc(arena, WCHAR, pathlen + 1);
GetFullPath(win32_rel_path, pathlen + 1, fullpath, NULL);
// notice how we don't need to free anything at the end
return fullpath;
}
```
There are a few helper functions:
* `arenaScratch`: sub allocate an arena from another arena
* `arenaTell`: returns the number of bytes allocated
* `arenaRemaining`: returns the number of bytes that can still be allocated
* `arenaRewind`: rewinds the arena to N bytes from the start (effectively "freeing" that memory)
* `arenaPop`: pops N bytes from the arena (effectively "freeing" that memory)
Here is an example usage of a full program:
```c
typedef struct {
char *buf;
usize len;
} str_t;
str_t read_file(arena_t *arena, const char *filename) {
str_t out = {0};
FILE *fp = fopen(filename, "rb");
if (!fp) goto error;
fseek(fp, 0, SEEK_END);
out.len = ftell(fp);
fseek(fp, 0, SEEK_SET);
out.buf = alloc(arena, char, out.len + 1);
fread(out.buf, 1, out.len, fp);
error:
return out;
}
void write_file(const char *filename, str_t data) {
FILE *fp = fopen(filename, "wb");
if (!fp) return;
fwrite(data.buf, 1, data.len, fp);
}
int main() {
arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
str_t title = {0};
{
uint8 tmpbuf[KB(5)];
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
str_t file = read_file(&scratch, "file.json");
json_t json = json_parse(&scratch, file);
title = str_dup(arena, json_get(json, "title"));
}
{
// copying an arena by value effectively makes it a scratch arena,
// as long as you don't use the original inside the same scope!
arena_t scratch = arena;
str_t to_write = str_fmt(
&scratch,
"{ \"title\": \"%s\" }", title.buf
);
write_file("out.json", to_write);
}
// cleanup all allocations at once
arenaCleanup(&arena);
}
```

32
docs/base64.md Normal file
View file

@ -0,0 +1,32 @@
---
title = Base 64
---
# Base 64
----------
The `base64.h` header has only two functions, one to encode and one to decode to/from base 64.
Example usage:
```c
char *recv_callback(arena_t *arena, buffer_t msg) {
buffer_t decoded = base64Decode(arena, msg);
alloc(arena, char); // allocate an extra char for the null pointer
return (char *)decoded.data;
}
buffer_t send_callback(arena_t *arena) {
char msg[] = "Hello World!";
buffer_t buf = {
.data = msg,
.len = arrlen(msg) - 1, // don't include the null pointer
};
buffer_t encoded = base64Encode(arena, buf);
return encoded;
}
int main() {
arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
run_server(&arena, 8080, recv_callback, send_callback);
arenaCleanup(&arena);
}
```

49
docs/cthreads.md Normal file
View file

@ -0,0 +1,49 @@
---
title = Threads
---
# Threads
----------
Cross platform threads, mutexes and conditional variables.
The api is very similar to pthreads, here is a small example program that uses threads and mutexes.
```c
struct {
bool exit;
cmutex_t mtx;
} state = {0};
int f1(void *) {
mtxLock(state.mtx);
state.exit = true;
mtxUnlock(state.mtx);
return 0;
}
int f2(void *) {
while (true) {
bool exit = false;
if (mtxTryLock(state.mtx)) {
exit = state.exit;
mtxUnlock(state.mtx);
}
if (exit) {
break;
}
}
return 0;
}
int main() {
state.mtx = mtxInit();
cthread_t t1 = thrCreate(f1, NULL);
thrDetach(t1);
cthread_t t2 = thrCreate(f2, NULL);
thrJoin(t2, NULL);
mtxFree(state.mtx);
}
```

52
docs/dir.md Normal file
View file

@ -0,0 +1,52 @@
---
title = Dir
---
# Dir
----------
This header provides a simple directory walker, here is an example usage:
```c
typedef struct source_t {
str_t filename;
struct source_t *next;
} source_t;
sources_t get_sources(arena_t *arena, strview_t path) {
uint8 tmpbuf[KB(5)] = {0};
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
dir_t *dir = dirOpen(&scratch, path);
dir_entry_t *entry = NULL;
source_t *sources = NULL;
while ((entry = dirNext(&scratch, dir))) {
if (entry->type != DIRTYPE_FILE) {
continue;
}
strview_t ext = fileGetExtension(strv(entry->name));
if (!strvEquals(ext, strv(".c"))) {
continue;
}
source_t *new_source = alloc(arena, source_t);
new_source->filename = strFmt(arena, "%v/%v", path, entry->name);
new_source->next = sources;
sources = new_source;
}
return sources;
}
int main() {
arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
source_t *sources = get_sources(&arena, strv("src/colla"));
while (sources) {
info("> %v", sources->filename);
sources = sources->next;
}
arenaCleanup(&arena);
}
```

BIN
docs/docs.com Normal file

Binary file not shown.

41
docs/file.md Normal file
View file

@ -0,0 +1,41 @@
---
title = File
---
# File
----------
This header provides cross platform file functionality.
It has all the basics that you can expect which work exactly like the stdio counterparts:
* `fileOpen`
* `fileClose`
* `fileIsValid`
* `fileRead`
* `fileWrite`
* `fileSeekEnd`
* `fileRewind`
* `fileTell`
Then there are a few helpers functions for reading / writing:
* Writing:
* `filePutc`
* `filePuts`
* `filePrintf`
* `fileWriteWhole`
* Reading:
* `fileReadWhole`
* `fileReadWholeStr`
There are also some functions to get info about a file without having to open it:
* `fileExists`
* `fileSize`
* `fileGetTime`
* `fileHasChanged`
And finally, there are some helper functions:
* `fileGetFullPath` (for windows)
* `fileSplitPath` / `fileGetFilename` / `fileGetExtension`
* `fileDelete`

28
docs/format.md Normal file
View file

@ -0,0 +1,28 @@
---
title = Format
---
# Format
----------
Small formatting utility, it has 2 functions (and the `va_list` alternatives):
* `fmtPrint`: equivalent to
* `fmtBuffer`
It uses [stb_sprintf](https://github.com/nothings/stb/blob/master/stb_sprintf.h) under the hood, but it also has support for printing buffers using `%v` (structures that are a pair of pointer/size)
This means it can print strings and [string views](/str).
In
Usage example:
```c
int main() {
strview_t v = strv("world");
char buffer[1024] = {0};
fmtPrint("Hello %v!", v);
fmtBuffer(buffer, sizeof(buffer), "Hello %v!", v);
}
```

37
docs/highlight.md Normal file
View file

@ -0,0 +1,37 @@
---
title = Highlight
---
# Highlight
----------
This header provides an highlighter for c-like languages (mostly c).
The usage is simple, first create a highlight context using hlInit, for which you need an hl_config_t. The only mandatory argument is colors, which are the strings put before the keywords in the highlighted text.
You can also pass some flags:
* `HL_FLAG_HTML`: escapes html characters
If you're using the offline documentation, this is the code used to highlight inside the markdown code blocks (simplified to remove actual markdown parsing):
```c
str_t highlight_code_block(arena_t *arena, strview_t code) {
uint8 tmpbuf[KB(5)];
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
hl_ctx_t *hl = hlInit(&scratch, &(hl_config_t){
.colors = {
[HL_COLOR_NORMAL] = strv("</span>"),
[HL_COLOR_PREPROC] = strv("<span class=\"c-preproc\">"),
[HL_COLOR_TYPES] = strv("<span class=\"c-types\">"),
[HL_COLOR_CUSTOM_TYPES] = strv("<span class=\"c-custom-types\">"),
[HL_COLOR_KEYWORDS] = strv("<span class=\"c-kwrds\">"),
[HL_COLOR_NUMBER] = strv("<span class=\"c-num\">"),
[HL_COLOR_STRING] = strv("<span class=\"c-str\">"),
[HL_COLOR_COMMENT] = strv("<span class=\"c-cmnt\">"),
[HL_COLOR_FUNC] = strv("<span class=\"c-func\">"),
[HL_COLOR_SYMBOL] = strv("<span class=\"c-sym\">"),
[HL_COLOR_MACRO] = strv("<span class=\"c-macro\">"),
},
.flags = HL_FLAG_HTML,
});
}
```

79
docs/hot_reload.md Normal file
View file

@ -0,0 +1,79 @@
---
title = Hot Reload
---
# Hot Reload
----------
This header provides cross-platform library hot-reloading. To use it you need to have to entry points, one for the host and one for the client.
In the client you can then implement these functions:
* `hr_init`: called when the library is loaded (or reloaded)
* `hr_loop`: called every "tick" (or whenever the host decides)
* `hr_close`: called when the host finishes
In the client you need to call these functions:
* `hrOpen`: load the library and call `hr_init`
* `hrStep`: call `hr_loop`
* `hrReload`: check if the library has changed, and if so reload it and call `hr_init` again
* `hrClose`: call `hr_close` and cleanup
Example usage:
### Client
```c
int hr_init(hr_t *ctx) {
uint8 tmpbuf[KB(5)];
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
state_t *state = ctx->userdata;
uint64 timestamp = fileGetTime(scratch, strv("sprite.png"));
if (timestamp > state->last_write) {
state->last_write = timestamp;
destroy_image(state->sprite);
state->sprite = load_image(strv("sprite.png"));
}
}
int hr_loop(hr_t *ctx) {
state_t *state = ctx->userdata;
draw_image(state->sprite, state->posx, state->posy);
}
int hr_close(hr_t *ctx) {
state_t *state = ctx->userdata;
destroy_image(state->sprite);
}
```
### Host
```c
typedef struct {
hr_t hr;
image_t sprite;
int posx;
int posy;
uint64 last_write;
} state_t;
int main() {
arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
state_t state = {0};
state.hr.userdata = &state;
if (!hrOpen(&state.hr, strv("bin/client.dll"))) {
return 1;
}
while (game_poll()) {
hrReload(&state.hr);
hrStep(&state.hr);
}
hrClose(&state.hr, true);
}
```

45
docs/html.md Normal file
View file

@ -0,0 +1,45 @@
---
title = HTML
---
# HTML
----------
This header provides an easy (although debatably sane) way to generate html in c.
There are three types of tags:
* One and done tags, like `<img>` or `<hr>` which only have an opening tag
* Basic tags which follow this format: `<tag>content</tag>`
* Tags where the content is probably other tags
You can open and close any tags using `tagBeg` and `tagEnd`, you can also set attributes like this:
```c
tagBeg("div", .class="content", .id="main");
```
Finally, you can use the `htmlRaw` macro to write raw html.
Example code:
```c
str_t generate_page(arena_t *arena, page_t *data) {
str_t out = STR_EMPTY;
htmlBeg(arena, &out);
headBeg();
title(data->title);
htmlRaw(<script src="script.js"></script>)
style(data->css);
headEnd();
bodyBeg();
divBeg(.id="main");
h1("Hello World!");
hr();
p("Some content blah blah");
img(.src="image.png");
divEnd();
bodyEnd();
htmlEnd();
return out;
}
```

5
docs/http.md Normal file
View file

@ -0,0 +1,5 @@
---
title = HTTP
---
# HTTP
----------

5
docs/ini.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Ini
---
# Ini
----------

5
docs/json.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Json
---
# Json
----------

5
docs/markdown.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Markdown
---
# Markdown
----------

5
docs/readme.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Readme
---
# Readme
----------

5
docs/server.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Server
---
# Server
----------

5
docs/sha1.md Normal file
View file

@ -0,0 +1,5 @@
---
title = SHA-1
---
# SHA-1
----------

5
docs/socket.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Socket
---
# Socket
----------

5
docs/str.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Str
---
# Str
----------

5
docs/strstream.md Normal file
View file

@ -0,0 +1,5 @@
---
title = StrStream
---
# StrStream
----------

5
docs/tracelog.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Tracelog
---
# Tracelog
----------

5
docs/utf8.md Normal file
View file

@ -0,0 +1,5 @@
---
title = UTF-8
---
# UTF-8
----------

5
docs/vec.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Vec
---
# Vec
----------

5
docs/vmem.md Normal file
View file

@ -0,0 +1,5 @@
---
title = VMem
---
# VMem
----------

5
docs/websocket.md Normal file
View file

@ -0,0 +1,5 @@
---
title = WebSocket
---
# WebSocket
----------

5
docs/xml.md Normal file
View file

@ -0,0 +1,5 @@
---
title = Xml
---
# Xml
----------