summaryrefslogtreecommitdiff
path: root/strio.h
diff options
context:
space:
mode:
authorWormHeamer2025-07-31 22:37:38 -0400
committerWormHeamer2025-07-31 22:37:38 -0400
commit842e22e9eb0f3dff7dabdaa41bcc2133e8f015f5 (patch)
tree78f42e68da656698526ff6099e78d82adab1d582 /strio.h
initial commit
Diffstat (limited to 'strio.h')
-rw-r--r--strio.h234
1 files changed, 234 insertions, 0 deletions
diff --git a/strio.h b/strio.h
new file mode 100644
index 0000000..0d6c6f5
--- /dev/null
+++ b/strio.h
@@ -0,0 +1,234 @@
+#ifndef STRIO_H
+#define STRIO_H
+
+#include <stdarg.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 <stdio.h>
+#include <stdint.h>
+
+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