Andrew Kesterson

"Love God. Live Righteously. Die Well."
Source Code || LinkedIn
38 Posts 9 Categories 0 Tags
Categories
Books 14
Philosophy 9
Faith 7
Technology 17
Outdoors 1
Current-Events 7
Leadership 8
History 1
Liberal-Education 1
hot tags

# Devlog Entry - 27 June 2026

2026-06-27 Technology 1.8k words

Development log entry for the week ending 27 June 2026. Improvements to libakerror logging, libakstdlib gets linked list and binary tree implementations, and I’m banging my head against libakgl to get binary space partitioning implemented correctly, as well as documentation improvements to all of these libraries.

# libakerror logging cleanup

First, I fixed a couple of minor bugs that impacted the correct installation of `libakerror`. I almost never use `libakerror` standalone, it’s basically always a cmake dependency, and these only impacted the standalone behavior, so I never saw it. I’ve since added a CI build that runs on every push to main and a badge on the README.

The traceback logging from libakerror was needlessly noisy. Some lines were duplicated, others just weren’t particularly helpful. For example:

    err_trace.c:func2:7: 134 (Null Pointer Error) : This is a failure in func2
    err_trace.c:func2:10
    err_trace.c:func1:18: Detected error 0 from array (refcount 1)
    err_trace.c:func1:18
    err_trace.c:func1:21
    err_trace.c:main:30: Detected error 0 from array (refcount 1)
    err_trace.c:main:30
    err_trace.c:main:33: Unhandled Error 134 (Null Pointer Error): This is a failure in func2

There are three lines from func1. There are two lines for the `CATCH()` call that actually detected the error coming out of `func2()`, and there is a line for the `FINISH()` statement. Similarly, for func2, there are two lines - one for the `FAIL()` that generated the exception and one for the `FINISH()` that emitted it. Then in `main()` we see the same kind of noise; two lines for the `CATCH()` that detected the error, and one for the `FINISH()` that emitted it. We don’t need all that. I think I put the extra lines in there when I was initially building the library and was having some trouble determining where an exception was leaving the control flow - I don’t need that anymore. I just want to know where the exception actually got generated and the call stack directly to that. The new logging behavior is thus:

    err_trace.c:func2:7: 134 (Null Pointer Error) : This is a failure in func2
    err_trace.c:func1:18
    err_trace.c:main:30
    err_trace.c:main:33: Unhandled Error 134 (Null Pointer Error): This is a failure in func2

This is much neater. We still get two lines from `main()`, but I’m willing to live with that. The second `main()` line comes from `FINISH_NORETURN` which summarizes the error from the deepest exception frame, and then calls `akerr_handler_unhandled_error()` which (generally) aborts the program. I’m fine with that getting its own line.

If you haven’t seen `libakerror` before, you can read about it here.

# libakstdlib

My C standard library libakstdlib got some new features. I started adding Doxygen tags to the source code to start improving the documentation around this. It’s incomplete, I’m adding as I go, but it’ll get there.

## Linked List Handling

    typedef struct aksl_ListNode {
        void *data;
        struct aksl_ListNode *next;
        struct aksl_ListNode *prev;
    } aksl_ListNode;

Linked Lists are fairly intuitive if you’ve done any linked list handling in C before. You have a list node structure that contains pointers to the next and previous items in the list; if `prev` is NULL, that is the head node, and if `next` is NULL, that is the tail. Each node contains a `void *` to some `data`, and it is the library user’s responsibility to know how to set, retrieve, and properly manage that data. You have functions for pushing a new object on to the END of an existing list, and for removing a node from whatever list it is currently in.

    akerr_ErrorContext AKERR_NOIGNORE *aksl_list_append(aksl_ListNode *list, aksl_ListNode *obj);
    akerr_ErrorContext AKERR_NOIGNORE *aksl_list_pop(aksl_ListNode *node);

You can iterate over each node in a list with an iterator function. The iterator function accepts a pointer to a node, and a pointer to an optional data object. The iteration proceeds forward through the list (reverse iteration is not supported yet), and the iterator function is called for each node in the list, with the user-provided data.

    typedef akerr_ErrorContext AKERR_NOIGNORE *(*aksl_ListNodeIterator)(aksl_ListNode *node, void *data);

    akerr_ErrorContext AKERR_NOIGNORE *aksl_list_iterate(aksl_ListNode *list, aksl_ListNodeIterator iter, void *data);

If you want to stop iteration before the end of the list is reached, you can raise an AKERR_ITERATOR_BREAK exception to halt the iteration early.

    PREPARE_ERROR(e);
    FAIL_RETURN(e, AKERR_ITERATOR_BREAK, "stop");

The library includes built-in circular reference detection using Floyd’s Cycle-Finding Algorithm. Both `append()` and `iterate()` perform cycle detection. `push()` does it while searching for the tail, `iterate()` does it before beginning the iteration cycle.

A complete sample program is in the tests directory.

## Binary Trees

    typedef struct aksl_TreeNode {
        struct aksl_TreeNode *parent;
        struct aksl_TreeNode *left;
        struct aksl_TreeNode *right;
        void *leaf;
    } aksl_TreeNode;

    #define AKSL_TREE_SEARCH_BFS           0    /** Breadth-first search mode for tree nodes. Currently unsupported. */
    #define AKSL_TREE_SEARCH_BFS_RIGHT     1    /** Right-hand breadth-first search mode for tree nodes. Currentl unsupported. */
    #define AKSL_TREE_SEARCH_DFS           2    /** Alias for AKSL_TREE_SEARCH_DFS_PREORDER */
    #define AKSL_TREE_SEARCH_DFS_PREORDER  2    /** Depth first pre-order (root, left, right) search mode for tree nodes */
    #define AKSL_TREE_SEARCH_DFS_INORDER   3    /** Depth first in-order (left, root, right) search mode for tree nodes. Currently unsupported. */
    #define AKSL_TREE_SEARCH_DFS_POSTORDER 4    /** Depth first post-order (left, right, root) search mode for tree nodes. Currently unsupported. */
    #define AKSL_TREE_SEARCH_VISIT         5    /** Used when iterating through a tree structure as a control flag: don't traverse the children, just visit the node */

The library only supports binary trees (though I can see a use for trees with arbitrary branches from a given node, I haven’t taken that leap yet). You can populate them and iterate over them using a few varieties of Depth-First Search.

    akerr_ErrorContext AKERR_NOIGNORE *aksl_tree_iterate(aksl_TreeNode *root, aksl_TreeNodeIterator iter, aksl_AllocFunc lalloc, aksl_FreeFunc lfree, uint8_t searchmode, void *data, aksl_ListNode *queue);

Currently the library has some known bugs. I would’ve fixed them this week, but I built the library functions in concert with some in-progress libakgl functionality that uses this library to perform binary space partitioning on the game scene, to prepare the scene for collision detection. So I prioritized the interfaces between those two, and the bits that most directly enabled the BSP partitioning. But…

The way in which search (iteration) is performed isn’t really right. In `AKSL_TREE_SEARCH_DFS_INORDER`, some nodes get visited by the iterator twice. In `AKSL_TREE_SEARCH_DFS_POSTORDER`, some nodes get visited three times. This is because the proper way to do tree search is to use a linked list to build a line of all nodes in the tree, in search order, and then iterate over those lists. Doing this allows you to ensure that there is only one link to a given node in the list before you start iterating.

The akstdlib tree library doesn’t use that method yet. We just process the nodes as soon as we see them. And because of that, for any gievn node of the tree, we wind up processing the root nodes more than once. Now that I’ve worked out some of the other behavioral processes, I’m going to implement the linked list handling.

On the tree iterator method, there are two function pointers and one list node provided by the user. The function pointers are for a linked list allocator and free function; the defaults are `aksl_malloc` and `aksl_free`. Certain implementations (like mine in `libakgl`) want control over how objects are allocated or released in memory; in `libakgl`, I use a series of static arrays allocated in the data segment, and I provide a couple of functions that find unused resources from there, allocate them, and release them back to the pool later.

This part of the library has quite a bit more work to do. But there is a complete example (with a test case that currently fails for the aforementioned reasons) in the tests directory.

# libakgl

All of the work in libakerror and libakstdlib this week was supporting a larger effort in libakgl: I’m trying to add binary space partitioning of the world so that I can arrange actors into linked lists of near neighbors for collision detection. This will allow me to compare collisions for only a few actors in a given screen area that are near each other, rather than checking every actor against every other actor. On small simple games it’s no big deal, but on a bullet hell or something similar, it’s a problem.

Unfortunately it’s not working quite right yet. At this point I have:

- A new `Stage` object has been introduced to represent and track the larger game world (which may or may not have the same extents as the camera, the screen, or the tilemap)
- A partitioning function on the Stage object that performs the job of partitioning the stage
- The screen gets subdivided into 32 areas consisting of a 4-deep BSP tree.
- All actors on the screen are partitioned into the correct area of the tree on level load
- The area containing the player is currently drawn with a red outline in the `sdl3-gametest` sample application
- The physics simulation of actor movement attempts to move the actor through the BSP on the Stage

However lots of it still doesn’t work like it should:

- When an actor moves, it’s supposed to traverse the BSP tree as it moves in world coordinates. The actors aren’t doing this.
- Sometimes the algorithm will attempt to get more tree node objects from the data segment than it should, which results in an `AKGL_ERR_OOHEAP` exception

And then there’s the fact that I’m just not happy with some of the original design assumptions. For example, right now I only divide the visible screen; off-screen actors will never collide. It also is not very intelligent about how it chooses what to subdivide; if it encounters half of the screen that has no actors in it, it knows this almost immediately, but stubbornly divides it anyway. A more efficient method might be to focus on the areas of greatest actor population (heat mapping). But that would require changing the partitioning process and actor movement logic from “partition once at the beginning, and then update when the actor moves”. Choices, choices.

I’m going to have to spend some more time with this one to get it right. But that’s probably going to have to wait a couple weeks; I’ve got a trip planned, and I’m onboarding onto a different team at work, plus I’ve got embedded projects I need to keep moving. So there’s no shortage of distractions.

Also, if you go poking around libakgl you will notice a new README with some compact usage instructions, as well as new doxygen comment blocks scattered around the code. I’m trying to bring it all up to speed in that regard.

Recent Posts
2026-07-10
News-2026-Week-2
2026-07-09
News - 2026 - Week - 28
2026-06-30
Just Be Yourself ... Whoever that is
2026-06-30
You Cant Go Home Again