A follow-up on this. To quote by copy/paste:
Lots of languages have something that lets you build up a dynamically-sized string with minimal overhead. C doesn't, and I found myself using code that did that manually in a couple of places, so I packaged it into a class. Note that I've only implemented functionality I'm using.
stringbuilder.h
It's the same as last time.
#ifndef CONCATEN_STRINGBUILDER_H
#define CONCATEN_STRINGBUILDER_H
#include <stddef.h>
#include <stdbool.h>
struct StringBuilder;
typedef struct StringBuilder *StringBuilder;
StringBuilder sb_new(size_t);
bool sb_append(StringBuilder, char);
char *sb_as_string(StringBuilder);
char *sb_free_copy(StringBuilder);
size_t sb_size(StringBuilder);
void sb_free(StringBuilder);
#endif //CONCATEN_STRINGBUILDER_H
stringbuilder.c
#include <stdlib.h>
#include <string.h>
#include "stringbuilder.h"
struct StringBuilder {
char *mem;
size_t count;
size_t cap;
};
StringBuilder sb_new(size_t init_cap) {
if (init_cap == 0) {
return NULL;
}
StringBuilder ret = malloc(sizeof(struct StringBuilder));
if (!ret) return NULL;
ret->mem = calloc(init_cap, sizeof(char));
if (!ret->mem) {
free(ret);
return NULL;
}
ret->cap = init_cap;
ret->count = 0;
return ret;
}
#define LOAD_FACTOR 2
bool sb_append(StringBuilder to, char c) {
if (to->count + 1 == to->cap) {
char *new_mem = realloc(to->mem, to->cap * LOAD_FACTOR);
if (!new_mem) {
return false;
}
memset(new_mem + to->cap, 0, to->cap);
to->mem = new_mem;
to->cap *= LOAD_FACTOR;
}
++to->count;
to->mem[to->count - 1] = c;
return true;
}
char *sb_as_string(StringBuilder sb) {
return sb->mem;
}
char *sb_free_copy(StringBuilder sb) {
char *ret = malloc((sb->count + 1) * sizeof(char));
if (!ret) {
return NULL;
}
strcpy(ret, sb->mem);
ret[sb->count] = 0;
sb_free(sb);
return ret;
}
size_t sb_size(StringBuilder sb) {
return sb->count;
}
void sb_free(StringBuilder sb) {
if (sb) {
if (sb->mem) free(sb->mem);
free(sb);
}
}
This time around, I'm concerned most about... exactly the same things:
- Performance. This code gets called a lot. I want it to be as fast as possible.
- Memory safety. While I'm fairly sure that this doesn't leak memory (assuming it's used properly), I'm not confident, and I'm not sure how to check.
- Edge cases. It works, as far as I can tell, but that doesn't mean that it's bug-free.
Also,
- Portability. Will this code be compileable on any (C11-conforming and/or major) compiler? I want the code that uses this to be as simple to deploy on as many platforms as possible.
Note that I specifically am not asking for advice on functions that would be nice to have. I'm only spending the time to implementing things I'm going to use; this isn't part of a general-purpose utility library, so I really don't care about supporting all the features when I use a tiny fraction of them.