aboutsummaryrefslogtreecommitdiff
path: root/src/print.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/print.c')
-rw-r--r--src/print.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/print.c b/src/print.c
new file mode 100644
index 00000000..c065dbb2
--- /dev/null
+++ b/src/print.c
@@ -0,0 +1,235 @@
+// This file defines some of the helper functions used for printing values
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "print.h"
+#include "stdlib/fpconv.h"
+#include "util.h"
+
+public
+int _print_uint(FILE *f, uint64_t n) {
+ char buf[21] = {[20] = 0}; // Big enough for UINT64_MAX + '\0'
+ char *p = &buf[19];
+
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
+}
+
+public
+int _print_hex(FILE *f, hex_format_t hex) {
+ int printed = 0;
+ if (!hex.no_prefix) printed += fputs("0x", f);
+ if (hex.digits > 0) {
+ hex.digits -= (hex.n == 0); // Don't need a leading zero for a zero
+ for (uint64_t n = hex.n; n > 0 && hex.digits > 0; n /= 16) {
+ hex.digits -= 1;
+ }
+ for (; hex.digits > 0; hex.digits -= 1) {
+ printed += fputc('0', f);
+ }
+ }
+ char buf[9] = {[8] = '\0'}; // Enough space for FFFFFFFF + '\0'
+ char *p = &buf[7];
+ do {
+ uint8_t digit = hex.n % 16;
+ if (digit <= 9) *(p--) = '0' + digit;
+ else if (hex.uppercase) *(p--) = 'A' + digit - 10;
+ else *(p--) = 'a' + digit - 10;
+ hex.n /= 16;
+ } while (hex.n > 0);
+ printed += (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[7] - p), f);
+ return printed;
+}
+
+public
+int _print_oct(FILE *f, oct_format_t oct) {
+ int printed = 0;
+ if (!oct.no_prefix) printed += fputs("0o", f);
+ if (oct.digits > 0) {
+ oct.digits -= (oct.n == 0); // Don't need a leading zero for a zero
+ for (uint64_t n = oct.n; n > 0 && oct.digits > 0; n /= 8)
+ oct.digits -= 1;
+ for (; oct.digits > 0; oct.digits -= 1)
+ printed += fputc('0', f);
+ }
+ char buf[12] = {[11] = '\0'}; // Enough space for octal UINT64_MAX + '\0'
+ char *p = &buf[10];
+ do {
+ *(p--) = '0' + (oct.n % 8);
+ oct.n /= 8;
+ } while (oct.n > 0);
+ printed += (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[10] - p), f);
+ return printed;
+}
+
+public
+int _print_double(FILE *f, double n) {
+ static char buf[24];
+ int len = fpconv_dtoa(n, buf);
+ return (int)fwrite(buf, sizeof(char), (size_t)len, f);
+}
+
+public
+int _print_hex_double(FILE *f, hex_double_t hex) {
+ if (hex.d != hex.d) return fputs("NAN", f);
+ else if (hex.d == 1.0 / 0.0) return fputs("INF", f);
+ else if (hex.d == -1.0 / 0.0) return fputs("-INF", f);
+ else if (hex.d == 0.0) return fputs("0.0", f);
+
+ union {
+ double d;
+ uint64_t u;
+ } bits = {.d = hex.d};
+
+ int sign = (bits.u >> 63) & 1ull;
+ int exp = (int)((bits.u >> 52) & 0x7FF) - 1023ull;
+ uint64_t frac = bits.u & 0xFFFFFFFFFFFFFull;
+
+ char buf[25];
+ char *p = buf;
+
+ if (sign) *p++ = '-';
+ *p++ = '0';
+ *p++ = 'x';
+
+ uint64_t mantissa = (1ull << 52) | frac; // implicit 1
+ int mantissa_shift = 52;
+
+ while ((mantissa & 0xF) == 0 && mantissa_shift > 0) {
+ mantissa >>= 4;
+ mantissa_shift -= 4;
+ }
+
+ uint64_t int_part = mantissa >> mantissa_shift;
+ *p++ = "0123456789abcdef"[int_part];
+
+ *p++ = '.';
+
+ while (mantissa_shift > 0) {
+ mantissa_shift -= 4;
+ uint64_t digit = (mantissa >> mantissa_shift) & 0xF;
+ *p++ = "0123456789abcdef"[digit];
+ }
+
+ *p++ = 'p';
+
+ if (exp >= 0) {
+ *p++ = '+';
+ } else {
+ *p++ = '-';
+ exp = -exp;
+ }
+
+ char expbuf[6];
+ int ei = 5;
+ expbuf[ei--] = '\0';
+ do {
+ expbuf[ei--] = '0' + (exp % 10);
+ exp /= 10;
+ } while (exp && ei >= 0);
+
+ ei++;
+ while (expbuf[ei])
+ *p++ = expbuf[ei++];
+
+ *p = '\0';
+ return fwrite(buf, sizeof(char), (size_t)(p - buf), f);
+}
+
+public
+int _print_char(FILE *f, char c) {
+#define ESC(e) "'\\" e "'"
+ const char *named[256] = {
+ ['\''] = ESC("'"), ['\\'] = ESC("\\"), ['\n'] = ESC("n"), ['\t'] = ESC("t"), ['\r'] = ESC("r"),
+ ['\033'] = ESC("e"), ['\v'] = ESC("v"), ['\a'] = ESC("a"), ['\b'] = ESC("b")};
+ const char *name = named[(uint8_t)c];
+ if (name != NULL) return fputs(name, f);
+ else if (isprint(c)) return fputc('\'', f) + fputc(c, f) + fputc('\'', f);
+ else
+ return (fputs("'\\x", f) + _print_hex(f, hex((uint64_t)c, .digits = 2, .no_prefix = true, .uppercase = true))
+ + fputs("'", f));
+#undef ESC
+}
+
+public
+int _print_quoted(FILE *f, quoted_t quoted) {
+#define ESC(e) "\\" e
+ const char *named[256] = {
+ ['"'] = ESC("\""), ['\\'] = ESC("\\"), ['\n'] = ESC("n"), ['\t'] = ESC("t"), ['\r'] = ESC("r"),
+ ['\033'] = ESC("e"), ['\v'] = ESC("v"), ['\a'] = ESC("a"), ['\b'] = ESC("b")};
+ int printed = fputc('"', f);
+ for (const char *p = quoted.str; *p; p++) {
+ const char *name = named[(uint8_t)*p];
+ if (name != NULL) {
+ printed += fputs(name, f);
+ } else if (isprint(*p) || (uint8_t)*p > 0x7F) {
+ printed += fputc(*p, f);
+ } else {
+ printed +=
+ fputs("\\x", f) + _print_hex(f, hex((uint64_t)*p, .digits = 2, .no_prefix = true, .uppercase = true));
+ }
+ }
+ printed += fputc('"', f);
+#undef ESC
+ return printed;
+}
+
+#if defined(__GLIBC__) && defined(_GNU_SOURCE)
+// GLIBC has fopencookie()
+static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) {
+ gc_stream_t *stream = (gc_stream_t *)cookie;
+ if (stream->position + size + 1 > *stream->size)
+ *stream->buffer =
+ GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL)));
+ memcpy(&(*stream->buffer)[stream->position], buf, size);
+ stream->position += size;
+ (*stream->buffer)[stream->position] = '\0';
+ return (ssize_t)size;
+}
+
+public
+FILE *gc_memory_stream(char **buf, size_t *size) {
+ gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
+ stream->size = size;
+ stream->buffer = buf;
+ *stream->size = 16;
+ *stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
+ (*stream->buffer)[0] = '\0';
+ stream->position = 0;
+ cookie_io_functions_t functions = {.write = _gc_stream_write};
+ return fopencookie(stream, "w", functions);
+}
+#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+// BSDs have funopen() and fwopen()
+static int _gc_stream_write(void *cookie, const char *buf, int size) {
+ gc_stream_t *stream = (gc_stream_t *)cookie;
+ if (stream->position + size + 1 > *stream->size)
+ *stream->buffer =
+ GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL)));
+ memcpy(&(*stream->buffer)[stream->position], buf, size);
+ stream->position += size;
+ (*stream->buffer)[stream->position] = '\0';
+ return size;
+}
+
+public
+FILE *gc_memory_stream(char **buf, size_t *size) {
+ gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
+ stream->size = size;
+ stream->buffer = buf;
+ *stream->size = 16;
+ *stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
+ (*stream->buffer)[0] = '\0';
+ stream->position = 0;
+ return fwopen(stream, _gc_stream_write);
+}
+#else
+#error "This platform doesn't support fopencookie() or funopen()!"
+#endif