Replace the single file-structure.md with a stratified layout designed
for AI/agent skill consumption: tables and concrete identifiers over
prose, files loaded on demand, content separated by churn rate.
Layers:
- architecture/ stable structural reference (overview, source-map,
test-map, data-flow)
- patterns/ memory, commands, autocomplete, events, xmpp,
encryption, ui, plugins
- testing/ unit-tests, stubs, functional-tests, bench
- build/ local, docker, ci
- playbooks/ add-command, add-test, add-autocomplete,
add-event-handler, add-encryption
- gotchas.md append-only dated entries (seven seed entries)
- wip/ branch-specific notes; deleted on merge to master
Stable layers describe cproof on master only. In-flight feature
branches (currently feat/ai) get a single file under wip/.
INDEX.md is the entry map with churn labels; SKILL.md is the
always-loaded skill hint pointing to it.
66 lines
2.7 KiB
Markdown
66 lines
2.7 KiB
Markdown
# Memory management
|
|
|
|
## `auto_*` macros
|
|
|
|
GCC/Clang `__cleanup__` attribute, declared in `src/common.h` (one exception:
|
|
`auto_jid` lives in `src/xmpp/jid.h` because it needs the `Jid` type).
|
|
|
|
| Macro | Type | Cleanup | Use for |
|
|
|---|---|---|---|
|
|
| `auto_gchar` | `gchar*` | `auto_free_gchar()` | GLib strings (`g_strdup`, `g_strdup_printf`, etc.) |
|
|
| `auto_gcharv` | `gchar**` | `auto_free_gcharv()` | GLib string arrays (`g_strsplit`, `g_strjoinv` arg) |
|
|
| `auto_char` | `char*` | `auto_free_char()` | C strings (`strdup`, `malloc`'d) |
|
|
| `auto_guchar` | `guchar*` | `auto_free_guchar()` | Unsigned char buffers (e.g. `g_base64_decode`) |
|
|
| `auto_gfd` | `gint` | `auto_close_gfd()` | File descriptors held in a `gint` |
|
|
| `auto_FILE` | `FILE*` | `auto_close_FILE()` | `FILE*` from `fopen` |
|
|
| `auto_jid` | `Jid*` | `jid_auto_destroy()` | `Jid` structs (XMPP) |
|
|
|
|
Place the macro **before** the type, like `gboolean`-style attribute:
|
|
|
|
```c
|
|
auto_gchar gchar* msg = g_strdup_printf("hello %s", name);
|
|
auto_gcharv gchar** parts = g_strsplit(line, " ", -1);
|
|
```
|
|
|
|
The cleanup runs at scope exit, including early returns. Do **not** call the
|
|
matching `g_free`/`g_strfreev`/`fclose` manually — that double-frees.
|
|
|
|
## Manual cleanup helpers
|
|
|
|
In `common.h`:
|
|
|
|
| Macro | Behaviour |
|
|
|---|---|
|
|
| `FREE_SET_NULL(ptr)` | `free(ptr); ptr = NULL;` (use for `malloc`'d) |
|
|
| `GFREE_SET_NULL(ptr)` | `g_free(ptr); ptr = NULL;` (use for GLib-allocated) |
|
|
|
|
Use these when the pointer is a struct field that must remain accessible
|
|
after the free (so a later `if (x->p)` is safe).
|
|
|
|
## GLib free-function reference
|
|
|
|
| Allocator | Free with |
|
|
|---|---|
|
|
| `g_strdup`, `g_strdup_printf`, `g_strconcat`, ... | `g_free` (or `auto_gchar`) |
|
|
| `g_strsplit`, `g_strdupv` | `g_strfreev` (or `auto_gcharv`) |
|
|
| `g_list_*` of allocated items | `g_list_free_full(list, free_fn)` |
|
|
| `g_hash_table_new[_full]` | `g_hash_table_destroy` (uses key/value destroy fns if supplied) |
|
|
| `g_base64_decode` | `g_free` (or `auto_guchar`) |
|
|
| `g_key_file_*` | `g_key_file_free` |
|
|
|
|
**Common pitfall:** mixing `g_free` and `free`. GLib uses its own allocator
|
|
shim; never cross the boundary. If you got the buffer from a GLib function,
|
|
free it with the matching GLib function.
|
|
|
|
**Common pitfall:** `g_strsplit` returns `gchar**` — free with `g_strfreev`,
|
|
not `g_free`. (See `gotchas.md`.)
|
|
|
|
## Adding a new `auto_*`
|
|
|
|
1. Declare cleanup function: `void auto_close_foo(Foo** p);`
|
|
2. Define the macro: `#define auto_foo __attribute__((__cleanup__(auto_close_foo)))`
|
|
3. Place both in the header that owns the type — `common.h` for project-wide,
|
|
the type's own header otherwise.
|
|
4. The cleanup must tolerate `NULL` and idempotent re-entry; assign `*p = NULL`
|
|
inside if you keep the variable accessible after free.
|