strb_t
String buffer interface for C
Loading...
Searching...
No Matches
strb.c
1// Copyright 2024 Christopher Bazley
2// SPDX-License-Identifier: MIT
3
4#if DEBUGOUT
5#define DEBUGF(...) fprintf(stderr, __VA_ARGS__)
6#else
7#define DEBUGF(...)
8#endif
9
10#define _GNU_SOURCE
11#include <assert.h>
12#include <stdarg.h>
13#include <stdbool.h>
14#include <stddef.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include "strb.h"
21
22#if STRB_UNPUTC
23#define F_CAN_UNPUTC (1 << 0)
24#else
25#define F_CAN_UNPUTC 0
26#endif
27
28#define F_ERR (1 << 1)
29
30#if STRB_RESTORE
31#define F_CAN_RESTORE (1 << 2)
32#else
33#define F_CAN_RESTORE 0
34#endif
35
36#define F_OVERWRITE (1 << 3)
37#if !STRB_STATIC_ALLOC && !STRB_FREESTANDING
38#define F_ALLOCATED (1 << 4)
39#endif
40#define F_EXTERNAL (1 << 5)
41#define F_AUTOFREE (1 << 6)
42#if STRB_REUSE_CONST
43#define F_IS_CONST (1 << 7)
44#else
45#define F_IS_CONST 0
46#endif
47
49struct strb_t {
51 strbprivate_t p;
52#if STRB_FREESTANDING
53 // No internal storage
54#elif STRB_STATIC_ALLOC
57#else
59 char internal[];
60#endif
61};
62
63#if STRB_STATIC_ALLOC
64static strb_t bufs[STRB_MAX];
65static uint8_t nbufs, buf_map;
66#endif
67
68#if STRB_STATIC_ALLOC || STRB_FREESTANDING
69// not provided by cc65
70size_t strnlen(const char *s, size_t n)
71{
72 size_t p = 0;
73 while (p < n && s[p])
74 p++;
75 return p;
76}
77#endif
78
79#if STRB_STATIC_ALLOC
80static _Optional strb_t *alloc_metadata(void)
81{
82 int free_idx = 0;
83
84 if (nbufs == STRB_MAX)
85 return NULL;
86
87 for (; free_idx < STRB_MAX; ++free_idx) {
88 if (!(buf_map & (1u << free_idx)))
89 break;
90 }
91
92 assert(free_idx < STRB_MAX);
93 buf_map |= (1u << free_idx);
94 nbufs++;
95
96 return &bufs[free_idx];
97}
98#define alloc_metadata(size) alloc_metadata()
99
100static void free_metadata(_Optional strb_t *sb)
101{
102 if (sb) {
103 ptrdiff_t alloc_idx = sb - bufs;
104 assert(alloc_idx >= 0);
105 assert(alloc_idx < STRB_MAX);
106 buf_map &= ~(1u << alloc_idx);
107 nbufs--;
108 }
109}
110#elif !STRB_FREESTANDING
111static _Optional strb_t *alloc_metadata(strbsize_t size)
112{
113 assert(size <= STRB_MAX_INTERNAL_SIZE);
114 return malloc(sizeof(strb_t) + (size * sizeof(((strb_t *)0)->internal[0])));
115}
116
117static void free_metadata(_Optional strb_t *sb)
118{
119 free(sb);
120}
121#endif
122
123#if STRB_EXT_STATE
124static strb_t *init_use(strbstate_t *restrict sbs, strbsize_t size,
125 char buf[STRB_SIZE_HINT(size)], strbsize_t len)
126{
127 sbs->p.len = sbs->p.pos = len;
128 sbs->p.size = size;
129 sbs->p.buf = buf;
130 sbs->p.flags = F_EXTERNAL | F_AUTOFREE;
131
132#if STRB_UNPUTC
133 if (len)
134 sbs->p.flags |= F_CAN_UNPUTC;
135#endif
136 return (void *)sbs;
137}
138
139strb_t *strb_use(strbstate_t *restrict sbs, size_t size,
140 char buf[STRB_SIZE_HINT(size)])
141{
142 assert(sbs);
143 assert(buf);
144 assert(size > 0);
145 DEBUGF("Use buffer %p of size %zu\n", (void *)buf, size);
146
147 if (size > STRB_MAX_SIZE)
148 size = STRB_MAX_SIZE;
149
150 buf[0] = '\0';
151 return init_use(sbs, size, buf, 0);
152}
153
154_Optional strb_t *strb_reuse(strbstate_t *restrict sbs, size_t size,
155 char buf[STRB_SIZE_HINT(size)])
156{
157 assert(sbs);
158 assert(buf);
159 assert(size > 0);
160 DEBUGF("Reuse buffer %p of size %zu\n", (void *)buf, size);
161
162 if (size > STRB_MAX_SIZE)
163 size = STRB_MAX_SIZE;
164
165 {
166 size_t len = strnlen(buf, size);
167 if (len == size) {
168 // Could be outside of the caller's control because of
169 // STRB_MAX_SIZE. Don't want to force use of strb_error after any
170 // call.
171 return NULL;
172 }
173
174 return init_use(sbs, size, buf, len);
175 }
176}
177
178#if STRB_REUSE_CONST
179_Optional const strb_t *strb_reuse_const(strbstate_t *restrict sbs,
180 const char buf[STRB_SIZE_HINT(1)])
181{
182 assert(sbs);
183 assert(buf);
184 DEBUGF("Reuse const buffer %p\n", (void *)buf);
185
186 {
187 size_t len = strnlen(buf, STRB_MAX_SIZE);
188 strb_t *sb;
189 if (len == STRB_MAX_SIZE) {
190 // Could be outside of the caller's control because of
191 // STRB_MAX_SIZE. Don't want to force use of strb_error after any
192 // call.
193 return NULL;
194 }
195
196 sb = init_use(sbs, len + 1u, (char *)buf, len);
197#ifndef NDEBUG
198 sb->p.flags |= F_IS_CONST;
199#endif
200 return sb;
201 }
202}
203#endif // STRB_REUSE_CONST
204#endif // STRB_EXT_STATE
205
206#if !STRB_FREESTANDING
207#if !STRB_EXT_STATE
208_Optional strb_t *strb_use(size_t size, char buf[STRB_SIZE_HINT(size)])
209{
210 assert(buf);
211 assert(size > 0);
212 DEBUGF("Use buffer %p of size %zu\n", (void *)buf, size);
213
214 if (size > STRB_MAX_SIZE)
215 size = STRB_MAX_SIZE;
216
217 {
218 _Optional strb_t *sb = alloc_metadata(0);
219 if (!sb)
220 return NULL;
221
222 sb->p.len = sb->p.pos = 0;
223 sb->p.size = size;
224 sb->p.buf = buf;
225 sb->p.flags = F_EXTERNAL;
226 buf[0] = '\0';
227 return sb;
228 }
229}
230
231_Optional strb_t *strb_reuse(size_t size, char buf[STRB_SIZE_HINT(size)])
232{
233 _Optional strb_t *sb = NULL;
234
235 assert(buf);
236 assert(size > 0);
237 DEBUGF("Reuse buffer %p of size %zu\n", (void *)buf, size);
238
239 if (size > STRB_MAX_SIZE)
240 size = STRB_MAX_SIZE;
241
242 {
243 size_t len = strnlen(buf, size);
244 if (len == size) {
245 // Could be outside of the caller's control because of
246 // STRB_MAX_SIZE. Don't want to force use of strb_error after any
247 // call.
248 return NULL;
249 }
250
251 sb = alloc_metadata(0);
252 if (!sb)
253 return NULL;
254
255 sb->p.len = sb->p.pos = len;
256 sb->p.size = size;
257 sb->p.buf = buf;
258 sb->p.flags = F_EXTERNAL;
259
260#if STRB_UNPUTC
261 if (len)
262 sb->p.flags |= F_CAN_UNPUTC;
263#endif
264 }
265 return sb;
266}
267#endif // !STRB_EXT_STATE
268
269_Optional strb_t *strb_alloc(size_t n)
270{
271 DEBUGF("Alloc buffer of size %zu\n", n);
272
273#if STRB_STATIC_ALLOC
274 n = STRB_MAX_SIZE;
275#else
276 if (n < STRB_DFL_SIZE)
277 n = STRB_DFL_SIZE;
278 else if (n >= STRB_MAX_SIZE)
279 n = STRB_MAX_SIZE;
280 else
281 ++n; // allow space for a null terminator
282#endif
283 {
284 // Don't allocate huge internal strings because the storage can't be
285 // recovered
286 _Optional strb_t *sb =
287 alloc_metadata(n > STRB_MAX_INTERNAL_SIZE ? 0 : n);
288 if (!sb)
289 return NULL;
290#if !STRB_STATIC_ALLOC
291 if (n > STRB_MAX_INTERNAL_SIZE) {
292 DEBUGF("Oversize buffer of %zu characters\n", n);
293 _Optional char *buf = malloc(n * sizeof(*sb->p.buf));
294 if (!buf) {
295 free_metadata(sb);
296 return NULL;
297 }
298 sb->p.buf = &*buf;
299 sb->p.flags = F_ALLOCATED;
300 } else
301#endif
302 {
303 DEBUGF("Internal buffer of %zu characters\n", n);
304 sb->p.buf = &*sb->internal;
305 sb->p.flags = 0;
306 }
307
308 sb->p.len = sb->p.pos = 0;
309 sb->p.size = n;
310 sb->p.buf[0] = '\0';
311 return sb;
312 }
313}
314
315_Optional strb_t *strb_ndup(const char *str, size_t n)
316{
317 size_t len = strnlen(str, n);
318 if (len >= STRB_MAX_SIZE)
319 return NULL;
320
321 {
322 _Optional strb_t *sb = strb_alloc(len + 1u);
323 if (!sb)
324 return NULL;
325
326 memcpy(sb->p.buf, str, len); // more efficient than strncpy
327 sb->p.buf[len] = '\0';
328 sb->p.len = sb->p.pos = len;
329#if STRB_UNPUTC
330 assert(!(sb->p.flags & F_OVERWRITE)); // needn't set unputc_char
331 if (len)
332 sb->p.flags |= F_CAN_UNPUTC;
333#endif
334 return sb;
335 }
336}
337
338_Optional strb_t *strb_vaprintf(const char *restrict format, va_list args)
339{
340 _Optional strb_t *sb = NULL;
341 va_list args_copy;
342 va_copy(args_copy, args);
343
344 do {
345 int len = vsnprintf(NULL, 0, format, args);
346 if (len < 0 || (size_t)len >= STRB_MAX_SIZE)
347 break; // formatting failed or result too long
348
349 sb = strb_alloc((size_t)len + 1u);
350 if (!sb)
351 break;
352
353 assert(sb->p.size > len);
354 vsprintf(sb->p.buf, format, args_copy);
355
356 sb->p.len = sb->p.pos = (strbsize_t)len;
357#if STRB_UNPUTC
358 assert(!(sb->p.flags & F_OVERWRITE)); // needn't set unputc_char
359 if (len)
360 sb->p.flags |= F_CAN_UNPUTC;
361#endif
362 } while (0);
363
364 va_end(args_copy);
365 return sb;
366}
367
368_Optional strb_t *strb_dup(const char *str)
369{
370 return strb_ndup(str, STRB_MAX_SIZE);
371}
372
373_Optional strb_t *strb_aprintf(const char *restrict format, ...)
374{
375 va_list args;
376 va_start(args, format);
377 {
378 _Optional strb_t *sb = strb_vaprintf(format, args);
379 va_end(args);
380 return sb;
381 }
382}
383
384void strb_free(_Optional strb_t *sb)
385{
386 if (!sb)
387 return;
388
389 if (sb->p.flags & F_AUTOFREE)
390 return;
391
392#if !STRB_STATIC_ALLOC
393 if (sb->p.flags & F_ALLOCATED)
394 free(sb->p.buf);
395#endif
396
397 free_metadata(sb);
398}
399#endif // !STRB_FREESTANDING
400
401#undef strb_ptr
402char *strb_ptr(strb_t *sb)
403{
404 assert(sb);
405 assert(!(sb->p.flags & F_IS_CONST));
406 return sb->p.buf;
407}
408
409const char *strb_cptr(strb_t const *sb)
410{
411 assert(sb);
412 return sb->p.buf;
413}
414
415size_t strb_len(strb_t const *sb)
416{
417 assert(sb);
418 return sb->p.len;
419}
420
421static int set_err(strb_t *sb)
422{
423 assert(!(sb->p.flags & F_IS_CONST));
424 sb->p.flags |= F_ERR;
425 return EOF;
426}
427
428int strb_setmode(strb_t *sb, int mode)
429{
430 assert(sb);
431 assert(!(sb->p.flags & F_IS_CONST));
432 if (mode == strb_insert || mode == strb_overwrite) {
433 sb->p.flags &= ~(F_CAN_UNPUTC | F_OVERWRITE);
434 if (mode == strb_overwrite)
435 sb->p.flags |= F_OVERWRITE;
436 return 0;
437 } else {
438 DEBUGF("Bad mode %d\n", mode);
439 return set_err(sb);
440 }
441}
442
443int strb_getmode(const strb_t *sb)
444{
445 assert(sb);
446 {
447 int mode = (sb->p.flags & F_OVERWRITE) ? strb_overwrite : strb_insert;
448 assert(mode == strb_insert || mode == strb_overwrite);
449 return mode;
450 }
451}
452
453int strb_seek(strb_t *sb, size_t pos)
454{
455 assert(sb);
456 assert(!(sb->p.flags & F_IS_CONST));
457 DEBUGF("Seek to %zu\n", pos);
458 assert(sb->p.pos < sb->p.size);
459 if (pos < STRB_MAX_SIZE) {
460 sb->p.pos = pos;
461#if STRB_UNPUTC || STRB_RESTORE
462 sb->p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
463#endif
464 return 0;
465 } else {
466 DEBUGF("Bad seek %zu\n", pos);
467 return set_err(sb);
468 }
469}
470
471size_t strb_tell(strb_t const *sb)
472{
473 assert(sb);
474 {
475 strbsize_t pos = sb->p.pos;
476 DEBUGF("Pos %" PRIstrbsize ", len %" PRIstrbsize ", size %" PRIstrbsize
477 "\n",
478 pos, sb->p.len, sb->p.size);
479 assert(pos < sb->p.size);
480 return pos;
481 }
482}
483
484int strb_putc(strb_t *sb, int c)
485{
486 return strb_nputc(sb, c, 1);
487}
488
489int strb_nputc(strb_t *sb, int c, size_t n)
490{
491 _Optional char *buf = strb_write(sb, n);
492 if (!buf)
493 return EOF;
494
495 memset(&*buf, c, n);
496 // assume F_CAN_RESTORE isn't user-visible. Don't bother calling
497 // strb_restore.
498 return c;
499}
500
501#if STRB_UNPUTC
503{
504 assert(sb);
505 assert(!(sb->p.flags & F_IS_CONST));
506 if (!(sb->p.flags & F_CAN_UNPUTC))
507 return set_err(sb);
508
509 assert(sb->p.pos > 0);
510 assert(sb->p.pos < STRB_MAX_SIZE);
511 assert(sb->p.pos < sb->p.size);
512 assert(sb->p.pos <= sb->p.len);
513
514 {
515 const strbsize_t new_pos = sb->p.pos - 1;
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);
520 --sb->p.len;
521 } else {
522 sb->p.buf[new_pos] = sb->p.unputc_char;
523 }
524
525 sb->p.pos = new_pos;
526 sb->p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
527 return removed;
528 }
529}
530#endif
531
532int strb_nputs(strb_t *restrict sb, const char *restrict str, size_t n)
533{
534 size_t len = strnlen(str, n);
535 _Optional char *buf = strb_write(sb, len);
536 if (!buf)
537 return EOF;
538
539 memcpy(&*buf, str, len); // more efficient than strncpy
540 // assume F_CAN_RESTORE isn't user-visible. Don't bother calling
541 // strb_restore.
542 return 0;
543}
544
545int strb_puts(strb_t *restrict sb, const char *restrict str)
546{
547 return strb_nputs(sb, str, STRB_MAX_SIZE);
548}
549
550#if !STRB_FREESTANDING
551
552int strb_vputf(strb_t *restrict sb, const char *restrict format, va_list args)
553{
554 va_list args_copy;
555 va_copy(args_copy, args);
556 {
557 const int len = vsnprintf(NULL, 0, format, args);
558 if (len >= 0) {
559 _Optional char *buf = strb_write(
560 sb, (size_t)len); // move tail by +len and keep buf[len]
561 if (buf) {
562 int const tmp = buf[len];
563 vsnprintf(buf, (size_t)len + 1u, format, args_copy);
564 buf[len] = tmp;
565 DEBUGF("String is now %s\n", strb_ptr(sb));
566 va_end(args_copy);
567 return 0;
568 }
569 }
570 }
571 va_end(args_copy);
572 return set_err(sb);
573}
574
575int strb_putf(strb_t *restrict sb, const char *restrict format, ...)
576{
577 va_list args;
578 va_start(args, format);
579 {
580 int e = strb_vputf(sb, format, args);
581 va_end(args);
582 return e;
583 }
584}
585
586#endif // !STRB_FREESTANDING
587
588static bool strb_ensure(strb_t *sb, size_t n, strbsize_t top)
589{
590 assert(sb);
591
592 if (n >= (size_t)STRB_MAX_SIZE - top) {
593 DEBUGF("Integer range exhausted (top=%" PRIstrbsize ", n=%zu)\n", top,
594 n);
595 return false; // can't represent new length
596 }
597
598 {
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);
601 if (n < room)
602 return true; // enough room for n chars and terminator
603 }
604
605#if STRB_STATIC_ALLOC || STRB_FREESTANDING
606 DEBUGF("Fixed buffer exhausted\n");
607 return false;
608#else
609 if (sb->p.flags & F_EXTERNAL) {
610 DEBUGF("External buffer exhausted\n");
611 return false;
612 }
613
614 strbsize_t new_size = sb->p.size <= (STRB_MAX_SIZE / STRB_GROW_FACTOR)
615 ? sb->p.size * STRB_GROW_FACTOR
617
618 assert(top + n + 1u <= STRB_MAX_SIZE);
619
620 if (new_size <= top + n)
621 new_size = top + n + 1u; // +1 for terminator
622
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);
627 if (!new_buf)
628 return false;
629 } else {
630 new_buf = malloc(new_size);
631 if (!new_buf)
632 return false;
633 memcpy(&*new_buf, sb->internal, sb->p.len + 1u);
634 }
635
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,
640 new_size);
641 return true;
642#endif
643}
644
645_Optional char *strb_write(strb_t *sb, size_t n)
646{
647 assert(sb);
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);
653
654 {
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
657 ? old_pos
658 : old_len;
659
660 if (!strb_ensure(sb, n, top)) {
661 DEBUGF("No room\n");
662 set_err(sb);
663 return NULL;
664 }
665
666 assert(old_pos < sb->p.size);
667
668 if (old_pos > old_len) {
669 DEBUGF("Zeroing between len %" PRIstrbsize " and pos %" PRIstrbsize
670 "\n",
671 old_len, old_pos + 1u);
672 // +1 because there is no null terminator at pos yet
673 memset(sb->p.buf + old_len, '\0', old_pos - old_len + 1u);
674 sb->p.len = old_pos;
675 }
676
677 assert(old_pos <= sb->p.len);
678
679 {
680 char *buf = sb->p.buf + old_pos;
681
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);
686 sb->p.len += n;
687 } else {
688#if STRB_UNPUTC
689 // Behave as if the write were implemented by multiple
690 // putc operations, which would imply null termination at
691 // each successive position.
692 sb->p.unputc_char =
693 old_pos + n - 1u > old_len ? '\0' : buf[n - 1];
694#endif
695 }
696
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);
700
701 if (sb->p.pos > sb->p.len) {
702 DEBUGF("Bumping length from %" PRIstrbsize " to %" PRIstrbsize
703 "\n",
704 sb->p.len, sb->p.pos);
705 sb->p.len = sb->p.pos;
706 buf[n] = '\0';
707 }
708
709#if STRB_RESTORE
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);
714#endif
715
716#if STRB_UNPUTC
717 if (n)
718 sb->p.flags |= F_CAN_UNPUTC;
719#endif
720 return buf;
721 }
722 }
723}
724
726{
727 _Optional char *p = strb_write(sb, 0);
728 assert(p);
729 *(char *)p = '\0';
730}
731
732#if STRB_RESTORE
734{
735 assert(sb);
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;
742 }
743}
744#endif
745
746void strb_delto(strb_t *sb, size_t pos)
747{
748 strbsize_t hi, lo;
749
750 assert(sb);
751 assert(!(sb->p.flags & F_IS_CONST));
752 assert(sb->p.pos < sb->p.size);
753
754 if (sb->p.pos > pos) {
755 lo = pos;
756 hi = sb->p.pos;
757 } else {
758 hi = pos;
759 lo = sb->p.pos;
760 }
761 assert(lo <= hi);
762
763 if (!(sb->p.flags & F_OVERWRITE)) {
764 strbsize_t clo, chi, len;
765
766 len = sb->p.len;
767 chi = hi > len ? len : hi;
768 clo = lo > len ? len : lo;
769 assert(clo <= chi);
770
771 memmove(sb->p.buf + clo, sb->p.buf + chi, len + 1u - chi);
772 sb->p.len = len - (chi - clo);
773 }
774
775 sb->p.pos = lo;
776#if STRB_UNPUTC || STRB_RESTORE
777 sb->p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
778#endif
779}
780
781static void strb_empty(strb_t *sb)
782{
783 assert(sb);
784 assert(!(sb->p.flags & F_IS_CONST));
785 sb->p.len = sb->p.pos = 0;
786 sb->p.buf[0] = '\0';
787#if STRB_UNPUTC || STRB_RESTORE
788 sb->p.flags &= ~(F_CAN_UNPUTC | F_CAN_RESTORE);
789#endif
790}
791
792int strb_ncpy(strb_t *restrict sb, const char *restrict str, size_t n)
793{
794 strb_empty(sb);
795 return strb_nputs(sb, str, n);
796}
797
798int strb_cpy(strb_t *restrict sb, const char *restrict str)
799{
800 return strb_ncpy(sb, str, STRB_MAX_SIZE);
801}
802
803#if !STRB_FREESTANDING
804
805int strb_vprintf(strb_t *restrict sb, const char *restrict format, va_list args)
806{
807 strb_empty(sb);
808 return strb_vputf(sb, format, args);
809}
810
811int strb_printf(strb_t *restrict sb, const char *restrict format, ...)
812{
813 va_list args;
814 va_start(args, format);
815 {
816 int e = strb_vprintf(sb, format, args);
817 va_end(args);
818 return e;
819 }
820}
821
822#endif // !STRB_FREESTANDING
823
824bool strb_error(strb_t const *sb)
825{
826 assert(sb);
827 return (sb->p.flags & F_ERR) != 0;
828}
829
831{
832 assert(sb);
833 assert(!(sb->p.flags & F_IS_CONST));
834 sb->p.flags &= ~F_ERR;
835}
@ strb_insert
Definition strb.h:663
@ strb_overwrite
Definition strb.h:669
#define STRB_SIZE_HINT(X)
Definition strb.h:57
int strb_seek(strb_t *sb, size_t pos)
Set the editing position of a string buffer.
Definition strb.c:453
int strb_vprintf(strb_t *restrict sb, const char *restrict format, va_list args)
Print a generated string into a string buffer.
Definition strb.c:805
_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.
Definition strb.c:315
int strb_getmode(const strb_t *sb)
Get the editing mode of a string buffer.
Definition strb.c:443
_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.
Definition strb.c:338
int strb_printf(strb_t *restrict sb, const char *restrict format,...)
Print a generated string into a string buffer.
Definition strb.c:811
size_t strb_tell(strb_t const *sb)
Get the editing position of a string buffer.
Definition strb.c:471
void strb_delto(strb_t *sb, size_t pos)
Delete characters to a specified position in a string buffer.
Definition strb.c:746
_Optional strb_t * strb_alloc(size_t n)
Create a string buffer object with internal storage.
Definition strb.c:269
void strb_free(_Optional strb_t *sb)
Destroy a string buffer object.
Definition strb.c:384
_Optional strb_t * strb_dup(const char *str)
Create a string buffer object with internal storage by duplicating a string.
Definition strb.c:368
int strb_nputs(strb_t *restrict sb, const char *restrict str, size_t n)
Put a sequence of characters into a string buffer.
Definition strb.c:532
int strb_cpy(strb_t *restrict sb, const char *restrict str)
Copy a string into a string buffer.
Definition strb.c:798
_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.
Definition strb.c:179
void strb_restore(strb_t *sb)
Restore a character that may have been overwritten by a preceding write.
Definition strb.c:733
size_t strb_len(strb_t const *sb)
Get the number of characters stored in a string buffer.
Definition strb.c:415
char * strb_ptr(strb_t *sb)
Get a pointer to the character array underlying a string buffer.
Definition strb.c:402
#define STRB_MAX_SIZE
Definition strb.h:125
#define STRB_MAX_INTERNAL_SIZE
Definition strb.h:137
#define STRB_GROW_FACTOR
Definition strb.h:142
bool strb_error(strb_t const *sb)
Get the error indicator of a string buffer.
Definition strb.c:824
int strb_unputc(strb_t *sb)
Undo the last put character operation.
Definition strb.c:502
int strb_nputc(strb_t *sb, int c, size_t n)
Put a character into a string buffer multiple times.
Definition strb.c:489
const char * strb_cptr(strb_t const *sb)
Definition strb.c:409
int strb_putf(strb_t *restrict sb, const char *restrict format,...)
Put a generated string into a string buffer.
Definition strb.c:575
int strb_puts(strb_t *restrict sb, const char *restrict str)
Put a string into a string buffer.
Definition strb.c:545
void strb_clearerr(strb_t *sb)
Clear the error indicator of a string buffer.
Definition strb.c:830
#define STRB_DFL_SIZE
Definition strb.h:131
#define STRB_MAX
Definition strb.h:109
void strb_split(strb_t *sb)
Split a string at the current position.
Definition strb.c:725
int strb_vputf(strb_t *restrict sb, const char *restrict format, va_list args)
Put a generated string into a string buffer.
Definition strb.c:552
_Optional char * strb_write(strb_t *sb, size_t n)
Prepare to write characters directly into a string buffer.
Definition strb.c:645
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.
Definition strb.c:139
int strb_putc(strb_t *sb, int c)
Put a character into a string buffer.
Definition strb.c:484
int strb_ncpy(strb_t *restrict sb, const char *restrict str, size_t n)
Copy a sequence of characters into a string buffer.
Definition strb.c:792
int strb_setmode(strb_t *sb, int mode)
Set the editing mode of a string buffer.
Definition strb.c:428
uint16_t strbsize_t
Definition strb.h:115
#define PRIstrbsize
Definition strb.h:120
_Optional strb_t * strb_aprintf(const char *restrict format,...)
Create a string buffer object with internal storage by parsing a format string.
Definition strb.c:373
_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.
Definition strb.c:154
Definition strb.c:49
strbprivate_t p
Definition strb.c:51
char internal[]
Definition strb.c:59
String buffer state.
Definition strb.h:237