5#define DEBUGF(...) fprintf(stderr, __VA_ARGS__)
23#define F_CAN_UNPUTC (1 << 0)
31#define F_CAN_RESTORE (1 << 2)
33#define F_CAN_RESTORE 0
36#define F_OVERWRITE (1 << 3)
37#if !STRB_STATIC_ALLOC && !STRB_FREESTANDING
38#define F_ALLOCATED (1 << 4)
40#define F_EXTERNAL (1 << 5)
41#define F_AUTOFREE (1 << 6)
43#define F_IS_CONST (1 << 7)
54#elif STRB_STATIC_ALLOC
65static uint8_t nbufs, buf_map;
68#if STRB_STATIC_ALLOC || STRB_FREESTANDING
70size_t strnlen(
const char *s,
size_t n)
80static _Optional
strb_t *alloc_metadata(
void)
87 for (; free_idx <
STRB_MAX; ++free_idx) {
88 if (!(buf_map & (1u << free_idx)))
93 buf_map |= (1u << free_idx);
96 return &bufs[free_idx];
98#define alloc_metadata(size) alloc_metadata()
100static void free_metadata(_Optional
strb_t *sb)
103 ptrdiff_t alloc_idx = sb - bufs;
104 assert(alloc_idx >= 0);
106 buf_map &= ~(1u << alloc_idx);
110#elif !STRB_FREESTANDING
114 return malloc(
sizeof(
strb_t) + (size *
sizeof(((
strb_t *)0)->internal[0])));
117static void free_metadata(_Optional
strb_t *sb)
127 sbs->
p.len = sbs->p.pos = len;
130 sbs->p.flags = F_EXTERNAL | F_AUTOFREE;
134 sbs->p.flags |= F_CAN_UNPUTC;
145 DEBUGF(
"Use buffer %p of size %zu\n", (
void *)buf, size);
151 return init_use(sbs, size, buf, 0);
160 DEBUGF(
"Reuse buffer %p of size %zu\n", (
void *)buf, size);
166 size_t len = strnlen(buf, size);
174 return init_use(sbs, size, buf, len);
184 DEBUGF(
"Reuse const buffer %p\n", (
void *)buf);
196 sb = init_use(sbs, len + 1u, (
char *)buf, len);
198 sb->
p.flags |= F_IS_CONST;
206#if !STRB_FREESTANDING
212 DEBUGF(
"Use buffer %p of size %zu\n", (
void *)buf, size);
218 _Optional
strb_t *sb = alloc_metadata(0);
222 sb->p.len = sb->p.pos = 0;
225 sb->p.flags = F_EXTERNAL;
233 _Optional
strb_t *sb = NULL;
237 DEBUGF(
"Reuse buffer %p of size %zu\n", (
void *)buf, size);
243 size_t len = strnlen(buf, size);
251 sb = alloc_metadata(0);
255 sb->p.len = sb->p.pos = len;
258 sb->p.flags = F_EXTERNAL;
262 sb->p.flags |= F_CAN_UNPUTC;
271 DEBUGF(
"Alloc buffer of size %zu\n", n);
290#if !STRB_STATIC_ALLOC
292 DEBUGF(
"Oversize buffer of %zu characters\n", n);
293 _Optional
char *buf = malloc(n *
sizeof(*sb->p.buf));
299 sb->p.flags = F_ALLOCATED;
303 DEBUGF(
"Internal buffer of %zu characters\n", n);
304 sb->p.buf = &*sb->internal;
308 sb->p.len = sb->p.pos = 0;
317 size_t len = strnlen(str, n);
326 memcpy(sb->p.buf, str, len);
327 sb->p.buf[len] =
'\0';
328 sb->p.len = sb->p.pos = len;
330 assert(!(sb->p.flags & F_OVERWRITE));
332 sb->p.flags |= F_CAN_UNPUTC;
340 _Optional
strb_t *sb = NULL;
342 va_copy(args_copy, args);
345 int len = vsnprintf(NULL, 0, format, args);
353 assert(sb->p.size > len);
354 vsprintf(sb->p.buf, format, args_copy);
358 assert(!(sb->p.flags & F_OVERWRITE));
360 sb->p.flags |= F_CAN_UNPUTC;
376 va_start(args, format);
389 if (sb->p.flags & F_AUTOFREE)
392#if !STRB_STATIC_ALLOC
393 if (sb->p.flags & F_ALLOCATED)
405 assert(!(sb->
p.flags & F_IS_CONST));
421static int set_err(
strb_t *sb)
423 assert(!(sb->
p.flags & F_IS_CONST));
424 sb->
p.flags |= F_ERR;
431 assert(!(sb->
p.flags & F_IS_CONST));
433 sb->
p.flags &= ~(F_CAN_UNPUTC | F_OVERWRITE);
435 sb->
p.flags |= F_OVERWRITE;
438 DEBUGF(
"Bad mode %d\n", mode);
456 assert(!(sb->
p.flags & F_IS_CONST));
457 DEBUGF(
"Seek to %zu\n", pos);
458 assert(sb->
p.pos < sb->
p.size);
461#if STRB_UNPUTC || STRB_RESTORE
462 sb->
p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
466 DEBUGF(
"Bad seek %zu\n", pos);
478 pos, sb->
p.len, sb->
p.size);
479 assert(pos < sb->p.size);
505 assert(!(sb->
p.flags & F_IS_CONST));
506 if (!(sb->
p.flags & F_CAN_UNPUTC))
509 assert(sb->
p.pos > 0);
511 assert(sb->
p.pos < sb->
p.size);
512 assert(sb->
p.pos <= sb->
p.len);
516 char removed = sb->
p.buf[new_pos];
517 if (!(sb->
p.flags & F_OVERWRITE)) {
518 memmove(sb->
p.buf + new_pos, sb->
p.buf + sb->
p.pos,
519 sb->
p.len - new_pos);
522 sb->
p.buf[new_pos] = sb->
p.unputc_char;
526 sb->
p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
534 size_t len = strnlen(str, n);
539 memcpy(&*buf, str, len);
550#if !STRB_FREESTANDING
555 va_copy(args_copy, args);
557 const int len = vsnprintf(NULL, 0, format, args);
562 int const tmp = buf[len];
563 vsnprintf(buf, (
size_t)len + 1u, format, args_copy);
565 DEBUGF(
"String is now %s\n",
strb_ptr(sb));
578 va_start(args, format);
593 DEBUGF(
"Integer range exhausted (top=%" PRIstrbsize ", n=%zu)\n", top,
599 const strbsize_t room = sb->
p.size > top ? sb->
p.size - top : 0;
600 DEBUGF(
"Need %zu bytes, have %" PRIstrbsize " bytes\n", n, room);
605#if STRB_STATIC_ALLOC || STRB_FREESTANDING
606 DEBUGF(
"Fixed buffer exhausted\n");
609 if (sb->
p.flags & F_EXTERNAL) {
610 DEBUGF(
"External buffer exhausted\n");
620 if (new_size <= top + n)
621 new_size = top + n + 1u;
623 _Optional
char *new_buf = NULL;
624 assert(new_size >= 1u);
625 if (sb->
p.flags & F_ALLOCATED) {
626 new_buf = realloc(sb->
p.buf, new_size);
630 new_buf = malloc(new_size);
633 memcpy(&*new_buf, sb->
internal, sb->
p.len + 1u);
636 sb->
p.flags |= F_ALLOCATED;
637 sb->
p.buf = &*new_buf;
638 sb->
p.size = new_size;
639 DEBUGF(
"Substituted buffer %p of %" PRIstrbsize " bytes\n", (
void *)new_buf,
648 assert(!(sb->
p.flags & F_IS_CONST));
649 assert(sb->
p.len < sb->
p.size);
650 assert(sb->
p.pos < sb->
p.size);
651 assert(sb->
p.buf[sb->
p.len] ==
'\0');
652 DEBUGF(
"About to write %zu chars\n", n);
655 const strbsize_t old_len = sb->
p.len, old_pos = sb->
p.pos;
656 const strbsize_t top = (sb->
p.flags & F_OVERWRITE) || old_pos > old_len
660 if (!strb_ensure(sb, n, top)) {
666 assert(old_pos < sb->p.size);
668 if (old_pos > old_len) {
671 old_len, old_pos + 1u);
673 memset(sb->
p.buf + old_len,
'\0', old_pos - old_len + 1u);
677 assert(old_pos <= sb->p.len);
680 char *buf = sb->
p.buf + old_pos;
682 if (!(sb->
p.flags & F_OVERWRITE)) {
683 DEBUGF(
"Moving tail '%s' (%d) from %p to %p\n", buf, *buf,
684 (
void *)buf, (
void *)(buf + n));
685 memmove(buf + n, buf, sb->
p.len + 1u - old_pos);
693 old_pos + n - 1u > old_len ?
'\0' : buf[n - 1];
697 sb->
p.pos = old_pos + n;
698 DEBUGF(
"Pos advanced by %zu to %" PRIstrbsize "\n", n, sb->
p.pos);
699 assert(sb->
p.pos < sb->
p.size);
701 if (sb->
p.pos > sb->
p.len) {
704 sb->
p.len, sb->
p.pos);
705 sb->
p.len = sb->
p.pos;
710 sb->
p.restore_char = buf[n];
711 sb->
p.flags |= F_CAN_RESTORE;
712 DEBUGF(
"Stored %d ('%c') at %" PRIstrbsize "\n", sb->
p.restore_char,
713 sb->
p.restore_char, sb->
p.pos);
718 sb->
p.flags |= F_CAN_UNPUTC;
736 assert(!(sb->
p.flags & F_IS_CONST));
737 if (sb->
p.flags & F_CAN_RESTORE) {
738 DEBUGF(
"Restored %d ('%c') at %" PRIstrbsize "\n", sb->
p.restore_char,
739 sb->
p.restore_char, sb->
p.pos);
740 sb->
p.buf[sb->
p.pos] = sb->
p.restore_char;
741 sb->
p.flags &= ~F_CAN_RESTORE;
751 assert(!(sb->
p.flags & F_IS_CONST));
752 assert(sb->
p.pos < sb->
p.size);
754 if (sb->
p.pos > pos) {
763 if (!(sb->
p.flags & F_OVERWRITE)) {
767 chi = hi > len ? len : hi;
768 clo = lo > len ? len : lo;
771 memmove(sb->
p.buf + clo, sb->
p.buf + chi, len + 1u - chi);
772 sb->
p.len = len - (chi - clo);
776#if STRB_UNPUTC || STRB_RESTORE
777 sb->
p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
781static void strb_empty(
strb_t *sb)
784 assert(!(sb->
p.flags & F_IS_CONST));
785 sb->
p.len = sb->
p.pos = 0;
787#if STRB_UNPUTC || STRB_RESTORE
788 sb->
p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
803#if !STRB_FREESTANDING
814 va_start(args, format);
827 return (sb->
p.flags & F_ERR) != 0;
833 assert(!(sb->
p.flags & F_IS_CONST));
834 sb->
p.flags &= ~F_ERR;
#define STRB_SIZE_HINT(X)
int strb_seek(strb_t *sb, size_t pos)
Set the editing position of a string buffer.
int strb_vprintf(strb_t *restrict sb, const char *restrict format, va_list args)
Print a generated string into a string buffer.
_Optional strb_t * strb_ndup(const char *str, size_t n)
Create a string buffer object with internal storage by duplicating a sequence of characters.
int strb_getmode(const strb_t *sb)
Get the editing mode of a string buffer.
_Optional strb_t * strb_vaprintf(const char *restrict format, va_list args)
Create a string buffer object with internal storage by parsing a format string.
int strb_printf(strb_t *restrict sb, const char *restrict format,...)
Print a generated string into a string buffer.
size_t strb_tell(strb_t const *sb)
Get the editing position of a string buffer.
void strb_delto(strb_t *sb, size_t pos)
Delete characters to a specified position in a string buffer.
_Optional strb_t * strb_alloc(size_t n)
Create a string buffer object with internal storage.
void strb_free(_Optional strb_t *sb)
Destroy a string buffer object.
_Optional strb_t * strb_dup(const char *str)
Create a string buffer object with internal storage by duplicating a string.
int strb_nputs(strb_t *restrict sb, const char *restrict str, size_t n)
Put a sequence of characters into a string buffer.
int strb_cpy(strb_t *restrict sb, const char *restrict str)
Copy a string into a string buffer.
_Optional const strb_t * strb_reuse_const(strbstate_t *restrict sbs, const char buf[STRB_SIZE_HINT(1)])
Create a string buffer object that wraps a constant string.
void strb_restore(strb_t *sb)
Restore a character that may have been overwritten by a preceding write.
size_t strb_len(strb_t const *sb)
Get the number of characters stored in a string buffer.
char * strb_ptr(strb_t *sb)
Get a pointer to the character array underlying a string buffer.
#define STRB_MAX_INTERNAL_SIZE
bool strb_error(strb_t const *sb)
Get the error indicator of a string buffer.
int strb_unputc(strb_t *sb)
Undo the last put character operation.
int strb_nputc(strb_t *sb, int c, size_t n)
Put a character into a string buffer multiple times.
const char * strb_cptr(strb_t const *sb)
int strb_putf(strb_t *restrict sb, const char *restrict format,...)
Put a generated string into a string buffer.
int strb_puts(strb_t *restrict sb, const char *restrict str)
Put a string into a string buffer.
void strb_clearerr(strb_t *sb)
Clear the error indicator of a string buffer.
void strb_split(strb_t *sb)
Split a string at the current position.
int strb_vputf(strb_t *restrict sb, const char *restrict format, va_list args)
Put a generated string into a string buffer.
_Optional char * strb_write(strb_t *sb, size_t n)
Prepare to write characters directly into a string buffer.
strb_t * strb_use(strbstate_t *restrict sbs, size_t size, char buf[STRB_SIZE_HINT(size)])
Create a string buffer object for managing an external character array.
int strb_putc(strb_t *sb, int c)
Put a character into a string buffer.
int strb_ncpy(strb_t *restrict sb, const char *restrict str, size_t n)
Copy a sequence of characters into a string buffer.
int strb_setmode(strb_t *sb, int mode)
Set the editing mode of a string buffer.
_Optional strb_t * strb_aprintf(const char *restrict format,...)
Create a string buffer object with internal storage by parsing a format string.
_Optional strb_t * strb_reuse(strbstate_t *restrict sbs, size_t size, char buf[STRB_SIZE_HINT(size)])
Create a string buffer object by reusing an external character array.