strbuf.h (2477B)
1 #ifndef KIT_STRBUF_H 2 #define KIT_STRBUF_H 3 4 /* Tiny bounded text builder, freestanding-friendly. 5 * 6 * Caller owns the backing buffer. strbuf_init reserves one slot for the 7 * NUL terminator; appends past `cap-1` characters silently truncate and 8 * `truncated` flips to 1 so the caller can detect overflow without 9 * scanning the result. Every helper keeps the buffer NUL-terminated 10 * after every append, so the buffer is always a valid C string. */ 11 12 #include <stddef.h> 13 14 #include "core/core.h" 15 #include "core/slice.h" 16 17 typedef struct StrBuf { 18 char* base; /* start of buffer (caller owned) */ 19 char* p; /* next write slot */ 20 char* end; /* one past last writable byte (reserves NUL slot) */ 21 u8 truncated; 22 u8 pad[7]; 23 } StrBuf; 24 25 static inline void strbuf_init(StrBuf* sb, char* buf, size_t cap) { 26 sb->base = buf; 27 sb->p = buf; 28 sb->end = (cap == 0) ? buf : (buf + cap - 1); 29 sb->truncated = 0; 30 if (cap) buf[0] = '\0'; 31 } 32 33 static inline void strbuf_reset(StrBuf* sb) { 34 sb->p = sb->base; 35 sb->truncated = 0; 36 if (sb->base != sb->end || sb->base != NULL) { 37 if (sb->base) *sb->base = '\0'; 38 } 39 } 40 41 static inline size_t strbuf_len(const StrBuf* sb) { 42 return (size_t)(sb->p - sb->base); 43 } 44 45 static inline const char* strbuf_cstr(const StrBuf* sb) { return sb->base; } 46 47 /* View the accumulated bytes as a Slice. The backing buffer stays 48 * NUL-terminated, so the slice's `.s` is safe to hand to NUL-requiring 49 * boundaries while `.len` carries the exact length. */ 50 static inline Slice strbuf_slice(const StrBuf* sb) { 51 return (Slice){.s = sb->base, .len = strbuf_len(sb)}; 52 } 53 54 static inline void strbuf_putc(StrBuf* sb, char c) { 55 if (sb->p < sb->end) { 56 *sb->p++ = c; 57 *sb->p = '\0'; 58 } else { 59 sb->truncated = 1; 60 } 61 } 62 63 static inline void strbuf_puts(StrBuf* sb, const char* s) { 64 while (*s) strbuf_putc(sb, *s++); 65 } 66 67 /* Append `n` bytes verbatim (may contain embedded NULs; the builder still 68 * appends a trailing NUL byte after them so strbuf_cstr() works for the 69 * common all-printable case). */ 70 static inline void strbuf_putn(StrBuf* sb, const char* s, size_t n) { 71 for (size_t i = 0; i < n; ++i) strbuf_putc(sb, s[i]); 72 } 73 74 /* Append a slice's bytes verbatim (length-explicit, no NUL scan). */ 75 static inline void strbuf_put_slice(StrBuf* sb, Slice s) { 76 strbuf_putn(sb, s.s, s.len); 77 } 78 79 void strbuf_put_u64(StrBuf*, u64 v); 80 void strbuf_put_i64(StrBuf*, i64 v); 81 void strbuf_put_hex_u64(StrBuf*, u64 v); /* "0x" + hex, no padding */ 82 83 #endif