#ifndef STRIO_H #define STRIO_H #include "str.h" #include "arena.h" int read_all(FILE *f, Str *buf, Arena *a); int next_line(Str *src, Str *line); void str_putf(Str s, FILE *f); void str_put(Str s); int str_to_u64(Str s, uint64_t *out); int str_to_i64(Str s, int64_t *out); void str_cat_i64(Str *out, int64_t c, char pad_char, int min_width, Arena *a); void str_cat_u64(Str *out, uint64_t c, char pad_char, int min_width, Arena *a); void str_cat_fmtv(Str *out, Arena *arena, const char *fmt, va_list ap); void str_cat_fmt(Str *out, Arena *arena, const char *fmt, ...); Str str_fmtv(Arena *arena, const char *fmt, va_list ap); Str str_fmt(Arena *arena, const char *fmt, ...); const char *cstr_fmt(Arena *arena, const char *fmt, ...); #ifdef STRIO_IMPL #include #include #include static inline long read_all_file_size(FILE *f) { fseek(f, 0, SEEK_END); long t = ftell(f); fseek(f, 0, SEEK_SET); return t > 0 ? t : -1; } int read_all(FILE *f, Str *buf, Arena *a) { if (!f) return -1; long sz = read_all_file_size(f); if (sz < 1) { ptrdiff_t cap = 4096; buf->s = new_arr(a, char, cap); buf->n = 0; while (!feof(f)) { size_t n = fread(&buf->s[buf->n], 1, cap - buf->n, f); if (n < 1) break; buf->n += n; if (buf->n >= cap) { size_t c = cap; while (buf->n >= cap) cap <<= 1; buf->s = resize(a, buf->s, c, cap); } } } else { buf->n = sz; buf->s = new_arr(a, char, sz); size_t sz = fread(buf->s, 1, buf->n, f); if (sz < (size_t)buf->n) return -1; } return ferror(f) ? -1 : 0; } int next_line(Str *src, Str *line) { if (src->n < 1) return 0; line->s = src->s; char *newln = memchr(src->s, '\n', src->n); line->n = newln ? newln - src->s : src->n; src->s += line->n + 1; src->n -= line->n + 1; if (line->n > 0 && line->s[line->n-1] == '\r') line->n--; return 1; } void str_putf(Str s, FILE *f) { fwrite(s.s, 1, s.n, f); } void str_put(Str s) { str_putf(s, stdout); } /* formatted conversion */ int str_to_u64(Str s, uint64_t *out) { if (s.n < 1) return -1; uint64_t acc = 0; for (int i = 0; i < s.n; i++) { char c = s.s[i]; if (!(c >= '0' && c <= '9')) return -1; acc = (acc * 10) + (c - '0'); } *out = acc; return 0; } int str_to_i64(Str s, int64_t *out) { int64_t sign = 1; if (s.n > 0 && s.s[0] == '-') { s = str_skip(s, 1); sign = -1; } uint64_t u64; if (str_to_u64(s, &u64)) return -1; *out = u64 * sign; return 0; } static void str_cat_u64_(char buf[32], int *n, uint64_t c) { int i = 0; buf[31] = '\0'; do { buf[32 - ++i] = (c % 10) + '0'; c /= 10; } while (c); *n = i; } void str_cat_u64(Str *out, uint64_t c, char pad_char, int min_width, Arena *a) { int n; /* more than enough for the largest 64-bit number * log_10(1 << 64) ~= 19.3 digits max */ char buf[32]; str_cat_u64_(buf, &n, c); while (n < min_width && ++n < 32) buf[32-n] = pad_char; str_cat(out, (Str) { &buf[sizeof(buf) - n], n }, a); } void str_cat_i64(Str *out, int64_t c, char pad_char, int min_width, Arena *a) { /* more than enough for the largest 64-bit number * log_10(1 << 64) ~= 19.3 digits max */ int n, neg = 0; char buf[32]; if (c < 0) neg = 1, c = -c; str_cat_u64_(buf, &n, c); if (neg) buf[sizeof(buf) - ++n] = '-'; while (n < min_width && ++n < 32) buf[32-n] = pad_char; str_cat(out, (Str) { &buf[sizeof(buf) - n], n }, a); } /* IMPORTANT: this is not and will not be printf() compatible * * %s - c string * %S - Str * %i - int32 * %I - int64 * %u - uint32 * %U - uin64 * **/ void str_cat_fmtv(Str *out, Arena *arena, const char *fmt, va_list ap) { size_t n = strlen(fmt); for (size_t i = 0; i < n; i++) { const char *mch = memchr(&fmt[i], '%', n - i); if (!mch) { str_cat(out, (Str) { (char*)&fmt[i], n - i }, arena); break; } size_t skip = mch - &fmt[i]; if (mch != &fmt[i]) { str_cat(out, (Str) { (char*)&fmt[i], skip }, arena); i += skip; } if (i + 1 < n) { int zero_pad = 0, min_width = 0; i++; if (fmt[i] == '0') { zero_pad = 1; i++; } while (i < n && fmt[i] >= '0' && fmt[i] <= '9') { min_width = min_width * 10 + (fmt[i] - '0'); i++; } if (i >= n) break; switch (fmt[i]) { case 's': str_cat(out, str_from_cstr(va_arg(ap, const char *)), arena); break; case 'S': str_cat(out, va_arg(ap, Str), arena); break; case 'i': str_cat_i64(out, va_arg(ap, int32_t), zero_pad?'0':' ', min_width, arena); break; case 'I': str_cat_i64(out, va_arg(ap, int64_t), zero_pad?'0':' ', min_width, arena); break; case 'u': str_cat_u64(out, va_arg(ap, uint32_t), zero_pad?'0':' ', min_width, arena); break; case 'U': str_cat_u64(out, va_arg(ap, uint64_t), zero_pad?'0':' ', min_width, arena); break; default: str_catc(out, fmt[i], arena); break; } } } } void str_cat_fmt(Str *out, Arena *arena, const char *fmt, ...) { va_list ap; va_start(ap, fmt); str_cat_fmtv(out, arena, fmt, ap); va_end(ap); } Str str_fmtv(Arena *arena, const char *fmt, va_list ap) { Str s = { 0 }; str_cat_fmtv(&s, arena, fmt, ap); return s; } Str str_fmt(Arena *arena, const char *fmt, ...) { va_list ap; va_start(ap, fmt); Str r = str_fmtv(arena, fmt, ap); va_end(ap); return r; } const char *cstr_fmt(Arena *arena, const char *fmt, ...) { va_list ap; va_start(ap, fmt); Str r = str_fmtv(arena, fmt, ap); str_catc(&r, '\0', arena); va_end(ap); return r.s; } #endif #endif