summary refs log tree commit diff
path: root/strio.h
diff options
context:
space:
mode:
Diffstat (limited to 'strio.h')
-rw-r--r--strio.h217
1 files changed, 217 insertions, 0 deletions
diff --git a/strio.h b/strio.h
new file mode 100644
index 0000000..c8bb21f
--- /dev/null
+++ b/strio.h
@@ -0,0 +1,217 @@
+#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);
+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;
+}
+
+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