sources/charbuf.c (111 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "cql.h" #include "charbuf.h" cql_data_defn( int32_t charbuf_open_count ); cql_noexport void bopen(charbuf* b) { b->max = CHARBUF_INTERNAL_SIZE; b->ptr = &b->internal[0]; bclear(b); charbuf_open_count++; } cql_noexport void bclose(charbuf *b) { if (b->ptr != &b->internal[0]) { free(b->ptr); } b->ptr = NULL; b->max = 0; b->used = 0; charbuf_open_count--; } cql_noexport void bclear(charbuf *b) { // an empty buffer has the null terminator b->used = 1; b->ptr[0] = 0; } cql_noexport void vbprintf(charbuf *b, const char *format, va_list args) { va_list pass1, pass2; va_copy(pass1, args); va_copy(pass2, args); // invariant is that there is already a null in the buffer // we can re-use that one. uint32_t avail = b->max - b->used; // does not include the trailing null uint32_t needed = (uint32_t)vsnprintf(NULL, 0, format, pass1); if (needed > avail) { b->max += needed + CHARBUF_GROWTH_SIZE; char *newptr = _new_array(char, b->max); // note that b->used includes the current null terminator memcpy(newptr, b->ptr, b->used); if (b->ptr != &b->internal[0]) { free(b->ptr); } avail = b->max - b->used; b->ptr = newptr; } // clobber starting from the current null, there is one more byte // than avail available to vsnprintf because we're backing off to // globber the old null. The result is always null terminated. vsnprintf(b->ptr + b->used - 1, avail + 1, format, pass2); b->used += needed; va_end(pass1); va_end(pass2); } cql_noexport void bprintf(charbuf *b, const char *format, ...) { va_list args; va_start(args, format); vbprintf(b, format, args); va_end(args); } cql_noexport CSTR dup_printf(const char *format, ...) { CSTR result; va_list args; va_start(args, format); CHARBUF_OPEN(tmp); vbprintf(&tmp, format, args); result = Strdup(tmp.ptr); CHARBUF_CLOSE(tmp); va_end(args); return result; } cql_noexport void bputc(charbuf *b, char c) { // invariant is that there is already a null in the buffer // we can re-use that one. uint32_t avail = b->max - b->used; if (avail < 1) { b->max += CHARBUF_GROWTH_SIZE; char *newptr = _new_array(char, b->max); // note that b->used includes the current null terminator memcpy(newptr, b->ptr, b->used); if (b->ptr != &b->internal[0]) { free(b->ptr); } avail = b->max - b->used; b->ptr = newptr; } b->ptr[b->used-1] = c; // clobber the previous null b->ptr[b->used++] = 0; // put a new null in place, for sure room for this } cql_noexport void bindent(charbuf *output, charbuf *input, int32_t indent) { if (indent == 0) { bprintf(output, "%s", input->ptr); return; } CHARBUF_OPEN(spaces); for (int32_t i = 0; i < indent; i++) bputc(&spaces, ' '); const char *p = input->ptr; for (;;) { if (!*p) break; // skip indenting blank lines if (*p != '\n') { bprintf(output, "%s", spaces.ptr); } while (*p) { char ch = *p++; bputc(output, ch); if (ch == '\n') break; } } CHARBUF_CLOSE(spaces); } // read a line from the incoming CSTR and move it forward to // the start of the next line. cql_noexport bool_t breadline(charbuf *output, CSTR *data) { // clean buffer bclear(output); CSTR p = *data; // no more lines if (p[0] == '\0') { return false; } // emit up to the next linefeed or end of the buffer, whichever comes first for (; *p != '\n' && *p != '\0'; p++) { bputc(output, *p); } // if we ended at a linefeed, skip over that if (p[0] == '\n') { p++; } *data = p; return true; }