/* * fy-emit-accum.h - internal YAML emitter accumulator header * * Copyright (c) 2019 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifndef FY_EMIT_ACCUM_H #define FY_EMIT_ACCUM_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "fy-utf8.h" #include "fy-event.h" struct fy_emit_accum { char *accum; size_t alloc; size_t next; char *inplace; size_t inplacesz; int col, row; int ts; enum fy_lb_mode lb_mode; }; static inline void fy_emit_accum_init(struct fy_emit_accum *ea, void *inplace, size_t inplacesz, int ts, enum fy_lb_mode lb_mode) { memset(ea, 0, sizeof(*ea)); ea->inplace = inplace; ea->inplacesz = inplacesz; ea->accum = ea->inplace; ea->alloc = ea->inplacesz; ea->ts = ts ? ts : 8; ea->lb_mode = lb_mode; } static inline void fy_emit_accum_reset(struct fy_emit_accum *ea) { ea->next = 0; ea->col = 0; ea->row = 0; } static inline void fy_emit_accum_cleanup(struct fy_emit_accum *ea) { if (ea->accum && ea->accum != ea->inplace) free(ea->accum); ea->accum = ea->inplace; ea->alloc = ea->inplacesz; fy_emit_accum_reset(ea); } static inline void fy_emit_accum_start(struct fy_emit_accum *ea, int col, enum fy_lb_mode lb_mode) { fy_emit_accum_reset(ea); ea->col = col; ea->lb_mode = lb_mode; } static inline void fy_emit_accum_finish(struct fy_emit_accum *ea) { fy_emit_accum_reset(ea); } static inline int fy_emit_accum_grow(struct fy_emit_accum *ea, size_t need) { size_t atleast, asz; char *new_accum; atleast = ea->alloc + need; asz = ea->alloc; /* minimum buffer is 32 */ if (asz < 32) asz = 32; do { asz *= 2; } while (asz < atleast); assert(asz > ea->inplacesz); new_accum = realloc(ea->accum == ea->inplace ? NULL : ea->accum, asz); if (!new_accum) /* out of memory */ return -1; if (ea->accum && ea->accum == ea->inplace) memcpy(new_accum, ea->accum, ea->next); ea->alloc = asz; ea->accum = new_accum; return 0; } static inline int fy_emit_accum_utf8_put_raw(struct fy_emit_accum *ea, int c) { size_t w, avail; int ret; /* grow if needed */ w = fy_utf8_width(c); if (w > (avail = (ea->alloc - ea->next))) { ret = fy_emit_accum_grow(ea, w - avail); if (ret != 0) return ret; } (void)fy_utf8_put_unchecked(ea->accum + ea->next, c); ea->next += w; return 0; } static inline int fy_emit_accum_put_raw(struct fy_emit_accum *ea, int c) { int ret; /* only lower ascii please */ if (c >= 0x80) return -1; /* grow if needed */ if (ea->next >= ea->alloc) { ret = fy_emit_accum_grow(ea, 1); if (ret != 0) return ret; } *(ea->accum + ea->next) = (char)c; ea->next++; return 0; } static inline int fy_emit_accum_utf8_put(struct fy_emit_accum *ea, int c) { int ret; if (!fy_utf8_is_valid(c)) return -1; if (fy_is_lb_m(c, ea->lb_mode)) { ret = fy_emit_accum_put_raw(ea, '\n'); if (ret) return ret; ea->col = 0; ea->row++; } else if (fy_is_tab(c)) { ret = fy_emit_accum_put_raw(ea, '\t'); if (ret) return ret; ea->col += (ea->ts - (ea->col % ea->ts)); } else { if (c < 0x80) { ret = fy_emit_accum_put_raw(ea, c); if (ret) return ret; } else { ret = fy_emit_accum_utf8_put_raw(ea, c); } ea->col++; } return 0; } static inline int fy_emit_accum_utf8_write_raw(struct fy_emit_accum *ea, const void *data, size_t len) { size_t avail; int ret; /* grow if needed */ if (len > (avail = (ea->alloc - ea->next))) { ret = fy_emit_accum_grow(ea, len - avail); if (ret != 0) return ret; } memcpy(ea->accum + ea->next, data, len); ea->next += len; return 0; } static inline int fy_emit_accum_utf8_write(struct fy_emit_accum *ea, const void *data, size_t len) { const char *s, *e; int c, w, ret; for (s = data, e = s + len; (c = fy_utf8_get(s, (e - s), &w)) >= 0; s += w) { ret = fy_emit_accum_utf8_put(ea, c); if (ret) break; } return c == FYUG_EOF ? 0 : -1; } static inline int fy_emit_accum_utf8_printf_raw(struct fy_emit_accum *ea, const char *fmt, ...) FY_FORMAT(printf, 2, 3); static inline int fy_emit_accum_utf8_printf_raw(struct fy_emit_accum *ea, const char *fmt, ...) { va_list ap; size_t avail, len; int ret; /* get the size of the string */ va_start(ap, fmt); len = vsnprintf(NULL, 0, fmt, ap); va_end(ap); /* grow if needed */ if ((len + 1) > (avail = (ea->alloc - ea->next))) { ret = fy_emit_accum_grow(ea, (len + 1) - avail); if (ret != 0) return ret; } va_start(ap, fmt); (void)vsnprintf(ea->accum + ea->next, len + 1, fmt, ap); va_end(ap); ea->next += len; return 0; } static inline const char * fy_emit_accum_get(struct fy_emit_accum *ea, size_t *lenp) { *lenp = ea->next; if (!ea->next) { return ""; } return ea->accum; } static inline int fy_emit_accum_make_0_terminated(struct fy_emit_accum *ea) { int ret; /* the empty case is special cased */ if (!ea->next) return 0; /* grow if needed for the '\0' */ if (ea->next >= ea->alloc) { ret = fy_emit_accum_grow(ea, 1); if (ret != 0) return ret; } assert(ea->next < ea->alloc); *(ea->accum + ea->next) = '\0'; return 0; } static inline const char * fy_emit_accum_get0(struct fy_emit_accum *ea) { int ret; ret = fy_emit_accum_make_0_terminated(ea); if (ret) return NULL; return ea->accum; } static inline char * fy_emit_accum_steal(struct fy_emit_accum *ea, size_t *lenp) { int ret; char *buf; /* empty, return a malloc'ed buffer to "" */ if (!ea->next) { buf = strdup(""); if (!buf) { *lenp = 0; return NULL; } *lenp = ea->next; } else if (ea->inplace && ea->accum == ea->inplace) { buf = malloc(ea->next + 1); if (!buf) { *lenp = 0; return NULL; } memcpy(buf, ea->accum, ea->next); buf[ea->next] = '\0'; *lenp = ea->next; } else { ret = fy_emit_accum_make_0_terminated(ea); if (ret) { *lenp = 0; return NULL; } assert(ea->accum && ea->accum != ea->inplace); buf = ea->accum; *lenp = ea->next; /* reset to inplace */ ea->accum = ea->inplace; ea->alloc = ea->inplacesz; } fy_emit_accum_cleanup(ea); return buf; } static inline char * fy_emit_accum_steal0(struct fy_emit_accum *ea) { size_t len; return fy_emit_accum_steal(ea, &len); } static inline bool fy_emit_accum_empty(struct fy_emit_accum *ea) { return ea->next == 0; } static inline size_t fy_emit_accum_size(struct fy_emit_accum *ea) { return ea->next; } static inline int fy_emit_accum_column(struct fy_emit_accum *ea) { return ea->col; } static inline int fy_emit_accum_row(struct fy_emit_accum *ea) { return ea->row; } struct fy_emit_accum_state { int col; int row; size_t next; }; static inline void fy_emit_accum_get_state(struct fy_emit_accum *ea, struct fy_emit_accum_state *s) { s->col = ea->col; s->row = ea->row; s->next = ea->next; } static inline void fy_emit_accum_rewind_state(struct fy_emit_accum *ea, const struct fy_emit_accum_state *s) { /* we can only go back */ assert(s->next <= ea->next); ea->col = s->col; ea->row = s->row; ea->next = s->next; } #endif