String formatter that allocates memory when necessary.

#pragma once

#include <stdbool.h>
#include <stdarg.h>

struct sb
{
    char* c_str;
    int size;
    int capacity;
    bool heap;
} ;

#define SB_INIT(N)  {.size = 0, .capacity = N, .c_str = (char[N + 1]){0}, .heap = 0 }

void sb_free(struct sb* p);
int sb_reserve(struct sb* p, int nelements);
int sb_printf(struct sb* stream, const char* fmt, ...);
int ss_vafprintf(struct sb* stream, const char* fmt, va_list args);


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sb.h"

void sb_free(struct sb* p)
{
    if (p->heap)
        free(p->c_str);
}

int sb_reserve(struct sb* p, int nelements)
{
    int result = 0;
    if (nelements > p->capacity)
    {
        char* pnew = NULL;
        if (!p->heap)
        {
            pnew = (char*)malloc((nelements + 1) * sizeof(char));
            if (pnew)
            {
                memcpy(pnew, p->c_str, nelements);
                p->heap = true;
            }
        }
        else
        {
            pnew = (char*)realloc(p->c_str, (nelements + 1) * sizeof(char));
        }
        if (pnew)
        {
            p->c_str = pnew;
            p->capacity = nelements;
            result = p->capacity;
        }
    }
    else
    {
        result = p->capacity;
    }

    return result;
}



int ss_vafprintf(struct sb* stream, const char* fmt, va_list args)
{
    va_list tmpa;
    va_copy(tmpa, args);

    int size = vsnprintf(stream->c_str, stream->capacity, fmt, tmpa);

    va_end(tmpa);

    if (size < 0)
    {
        return -1;
    }

    if (size > stream->capacity)
    {
        if (sb_reserve(stream, size) == 0)
        {
            return -1;
        }

        size = vsprintf(stream->c_str, fmt, args);
        if (size > 0)
        {
            stream->size = size;
        }
    }
   


    return size;
}

int sb_printf(struct sb* stream, const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int size = ss_vafprintf(stream, fmt, args);
    va_end(args);
    return size;
}

#include <stdio.h>
#include "sb.h"

int main()
{
    struct sb sb = SB_INIT(10);

    sb_printf(&sb, "%s", "test");

    sb_printf(&sb, "%s", "big string test");

    sb_printf(&sb, "%s", "small");

    sb_reserve(&sb, 400);

    sb_free(&sb);
}