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.
2.7 KiB
2.7 KiB
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:
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_*
- Declare cleanup function:
void auto_close_foo(Foo** p); - Define the macro:
#define auto_foo __attribute__((__cleanup__(auto_close_foo))) - Place both in the header that owns the type —
common.hfor project-wide, the type's own header otherwise. - The cleanup must tolerate
NULLand idempotent re-entry; assign*p = NULLinside if you keep the variable accessible after free.