feat: add auto-expanding buffer implementation for html writer
parent
33824534bc
commit
5c96031834
|
@ -11,6 +11,14 @@
|
|||
*out = temp; \
|
||||
}
|
||||
|
||||
#define MRK_MALLOC(out, size) \
|
||||
{ \
|
||||
void *temp = malloc(size); \
|
||||
if (temp == NULL) \
|
||||
return mrk_err_failed_alloc; \
|
||||
*out = temp; \
|
||||
}
|
||||
|
||||
#define MRK_RES(x) \
|
||||
{ \
|
||||
mrk_err res = x; \
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef MRK_BUF
|
||||
#define MRK_BUF
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mrk/common.h"
|
||||
|
||||
/**
|
||||
* Wrapper around a nul-terminated char buffer that grows as needed
|
||||
*/
|
||||
typedef struct mrk_buf {
|
||||
char *s;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
} mrk_buf;
|
||||
|
||||
/**
|
||||
* Initialize a (usually stack-allocated) buffer
|
||||
*/
|
||||
mrk_err mrk_buf_init(mrk_buf *buf);
|
||||
|
||||
/**
|
||||
* Double the buffer's internal capacity until it has enough
|
||||
* room for the additional elements.
|
||||
*/
|
||||
mrk_err mrk_buf_expand(mrk_buf *buf, size_t required);
|
||||
|
||||
/**
|
||||
* Shrink the internal buffer so that its capacity equals the stored length.
|
||||
*/
|
||||
mrk_err mrk_buf_compact(mrk_buf *buf);
|
||||
|
||||
/**
|
||||
* Append the given nul-terminated string to the stored string.
|
||||
*/
|
||||
mrk_err mrk_buf_append(mrk_buf *buf, const char *s);
|
||||
|
||||
/**
|
||||
* Append the char buffer with the given length to the stored string.
|
||||
*/
|
||||
mrk_err mrk_buf_append_n(mrk_buf *buf, const char *s, size_t len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "mrk/buf.h"
|
||||
|
||||
mrk_err mrk_buf_init(mrk_buf *buf) {
|
||||
MRK_MALLOC(&buf->s, 2);
|
||||
|
||||
// 2 ^ 0
|
||||
buf->cap = 1;
|
||||
buf->len = 0;
|
||||
buf->s[buf->len] = '\0';
|
||||
|
||||
return mrk_err_ok;
|
||||
}
|
||||
|
||||
mrk_err mrk_buf_expand(mrk_buf *buf, size_t required) {
|
||||
size_t new_cap = buf->cap;
|
||||
|
||||
while (buf->len + required >= new_cap) {
|
||||
// Grow buffer size in powers of two
|
||||
new_cap <<= 1;
|
||||
}
|
||||
|
||||
// No need to reallocate if there's already enough space
|
||||
if (new_cap == buf->cap) {
|
||||
return mrk_err_ok;
|
||||
}
|
||||
|
||||
char *new_buf = realloc(buf->s, new_cap + 1);
|
||||
|
||||
if (new_buf == NULL) {
|
||||
return mrk_err_failed_alloc;
|
||||
}
|
||||
|
||||
buf->s = new_buf;
|
||||
buf->cap = new_cap;
|
||||
|
||||
return mrk_err_ok;
|
||||
}
|
||||
|
||||
mrk_err mrk_buf_compact(mrk_buf *buf) {
|
||||
char *new_buf = realloc(buf->s, buf->len + 1);
|
||||
|
||||
if (new_buf == NULL) {
|
||||
return mrk_err_failed_alloc;
|
||||
}
|
||||
|
||||
buf->s = new_buf;
|
||||
buf->cap = buf->len;
|
||||
|
||||
return mrk_err_ok;
|
||||
}
|
||||
|
||||
mrk_err mrk_buf_append(mrk_buf *buf, const char *s) {
|
||||
size_t len = strlen(s);
|
||||
|
||||
return mrk_buf_append_n(buf, s, len);
|
||||
}
|
||||
|
||||
mrk_err mrk_buf_append_n(mrk_buf *buf, const char *s, size_t len) {
|
||||
MRK_RES(mrk_buf_expand(buf, len));
|
||||
|
||||
memcpy(&buf->s[buf->len], s, len);
|
||||
buf->len += len;
|
||||
|
||||
// Keep the buffer nul-terminated
|
||||
buf->s[buf->len] = '\0';
|
||||
|
||||
return mrk_err_ok;
|
||||
}
|
Loading…
Reference in New Issue