MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdarg.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "output.h"
9
10#define ANT_OUTPUT_INITIAL_CAP 65536
11
12static ant_output_stream_t g_stdout_writer = { .stream = NULL };
13static ant_output_stream_t g_stderr_writer = { .stream = NULL };
14
15ant_output_stream_t *ant_output_stream(FILE *stream) {
16 ant_output_stream_t *out = (stream == stderr) ? &g_stderr_writer : &g_stdout_writer;
17 out->stream = stream;
18 return out;
19}
20
21void ant_output_stream_begin(ant_output_stream_t *out) {
22 if (!out) return;
23 out->buffer.len = 0;
24 out->buffer.failed = false;
25}
26
27bool ant_output_stream_reserve(ant_output_stream_t *out, size_t extra) {
28 size_t needed = 0;
29 size_t next_cap = 0;
30 char *next = NULL;
31
32 if (!out || out->buffer.failed) return false;
33
34 needed = out->buffer.len + extra;
35 if (needed <= out->buffer.cap) return true;
36
37 next_cap = out->buffer.cap ? out->buffer.cap * 2 : ANT_OUTPUT_INITIAL_CAP;
38 while (next_cap < needed) next_cap *= 2;
39
40 next = realloc(out->buffer.data, next_cap);
41 if (!next) {
42 out->buffer.failed = true;
43 return false;
44 }
45
46 out->buffer.data = next;
47 out->buffer.cap = next_cap;
48 return true;
49}
50
51bool ant_output_stream_append(ant_output_stream_t *out, const void *data, size_t len) {
52 if (!ant_output_stream_reserve(out, len)) return false;
53 if (len > 0) memcpy(out->buffer.data + out->buffer.len, data, len);
54 out->buffer.len += len;
55 return true;
56}
57
58bool ant_output_stream_append_cstr(ant_output_stream_t *out, const char *str) {
59 return ant_output_stream_append(out, str, strlen(str));
60}
61
62bool ant_output_stream_putc(ant_output_stream_t *out, char ch) {
63 return ant_output_stream_append(out, &ch, 1);
64}
65
66#pragma GCC diagnostic push
67#pragma GCC diagnostic ignored "-Wformat-nonliteral"
68
69bool ant_output_stream_appendfv(ant_output_stream_t *out, const char *fmt, va_list ap) {
70 char stack[256];
71 va_list ap_copy;
72 int written = 0;
73
74 if (!out || out->buffer.failed) return false;
75
76 va_copy(ap_copy, ap);
77 written = vsnprintf(stack, sizeof(stack), fmt, ap_copy);
78 va_end(ap_copy);
79
80 if (written < 0) {
81 out->buffer.failed = true;
82 return false;
83 }
84
85 if ((size_t)written < sizeof(stack))
86 return ant_output_stream_append(out, stack, (size_t)written);
87
88 if (!ant_output_stream_reserve(out, (size_t)written + 1)) return false;
89 vsnprintf(out->buffer.data + out->buffer.len, out->buffer.cap - out->buffer.len, fmt, ap);
90 out->buffer.len += (size_t)written;
91
92 return true;
93}
94
95bool ant_output_stream_appendf(ant_output_stream_t *out, const char *fmt, ...) {
96 va_list ap;
97 bool ok = false;
98
99 va_start(ap, fmt);
100 ok = ant_output_stream_appendfv(out, fmt, ap);
101 va_end(ap);
102 return ok;
103}
104
105#pragma GCC diagnostic pop
106
107bool ant_output_stream_flush(ant_output_stream_t *out) {
108 size_t len = 0;
109 size_t wrote = 0;
110
111 if (!out || !out->stream) return false;
112 if (out->buffer.failed) {
113 out->buffer.len = 0;
114 out->buffer.failed = false;
115 return false;
116 }
117 if (out->buffer.len == 0) return fflush(out->stream) == 0;
118
119 len = out->buffer.len;
120 wrote = fwrite(out->buffer.data, 1, len, out->stream);
121 out->buffer.len = 0;
122
123 return wrote == len && fflush(out->stream) == 0;
124}