C/C++ Arena

Step 10 of 10

Challenge: a string builder

This challenge brings the whole module together: a struct that owns a growing heap buffer, with functions to create, use and destroy it. This is how real C libraries are designed.

Building text with repeated strcat into a fixed-size array is how buffer overflows happen. A string builder instead keeps:

typedef struct {
    char *buf;   /* the text, always '\0'-terminated */
    size_t len;  /* characters used, not counting '\0' */
    size_t cap;  /* bytes allocated */
} Builder;

Invariants

An invariant is a rule that's always true between calls. For the builder: buf holds len characters followed by '\0', and len + 1 <= cap. Every function may rely on it at the start and must restore it before returning. Writing invariants down makes this kind of code much easier to get right.

Appending

To append s (of length n), you need len + n + 1 bytes. If cap is smaller, keep doubling a new capacity until it's enough, realloc to it, and only then update the struct. If realloc fails, return an error and leave everything as it was (the old buffer is still valid). Then memcpy the new characters (plus the terminator) to buf + len, and update len.

Here's the same "grow by doubling until it fits" calculation on its own:

#include <stdio.h>
#include <stddef.h>

size_t grown(size_t cap, size_t needed) {
    while (cap < needed) {
        cap *= 2;
    }
    return cap;
}

int main(void) {
    printf("%zu %zu %zu\n", grown(8, 5), grown(8, 9), grown(8, 70));
    return 0;
}
8 16 128

Lifecycle

sb_init must be called before use and sb_free after, exactly like malloc and free. After freeing, reset the fields (buf = NULL, len = cap = 0) so an accidental second sb_free is harmless.

Your turn: write the four functions:

Previous: Where a program's data lives