#include "lang/format.h"
#include "lang/env.h"
#include "table/sym.h"
#include "lang/eval.h"
#include "ops/ops.h"
#include "ops/internal.h"
#include "mem/heap.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <inttypes.h>
#include <limits.h>
typedef struct {
char* buf;
int32_t len;
int32_t cap;
ray_t* block;
bool err;
const char* err_msg;
FILE* sink;
bool io_err;
bool flushed;
} fmt_buf_t;
#define FMT_SINK_FLUSH ((int64_t)1 << 20)
static void fmt_init(fmt_buf_t* b) {
b->err = false;
b->err_msg = NULL;
b->sink = NULL;
b->io_err = false;
b->flushed = false;
b->block = ray_alloc(256);
if (!b->block) {
b->err = true; b->err_msg = "cannot allocate the initial output buffer";
b->buf = NULL; b->len = 0; b->cap = 0;
return;
}
b->buf = (char*)ray_data(b->block);
b->len = 0;
b->cap = 256;
}
static void fmt_destroy(fmt_buf_t* b) {
if (b->block) {
ray_free(b->block);
b->block = NULL;
b->buf = NULL;
b->len = 0;
b->cap = 0;
}
}
static void fmt_ensure(fmt_buf_t* b, int64_t extra) {
if (b->err) return;
if (extra < 0) { b->err = true; b->err_msg = "invalid negative format length"; return; }
if (b->sink && b->len > 0 && (int64_t)b->len + extra > FMT_SINK_FLUSH) {
if (fwrite(b->buf, 1, (size_t)b->len, b->sink) != (size_t)b->len) {
b->err = true; b->io_err = true; b->err_msg = "cannot write formatted output";
return;
}
b->len = 0;
b->flushed = true;
}
int64_t need = (int64_t)b->len + extra;
if (need <= (int64_t)b->cap) return;
if (need > (int64_t)INT32_MAX) {
b->err = true; b->err_msg = "formatted output exceeds the 2GiB buffer limit";
return;
}
int64_t new_cap = b->cap;
while (new_cap < need)
new_cap *= 2;
if (new_cap > (int64_t)INT32_MAX)
new_cap = INT32_MAX;
ray_t* new_block = ray_alloc((size_t)new_cap);
if (!new_block) {
b->err = true; b->err_msg = "out of memory growing the output buffer";
return;
}
char* new_buf = (char*)ray_data(new_block);
memcpy(new_buf, b->buf, (size_t)b->len);
ray_free(b->block);
b->block = new_block;
b->buf = new_buf;
b->cap = (int32_t)new_cap;
}
static void fmt_putc(fmt_buf_t* b, char c) {
fmt_ensure(b, 1);
if (b->err) return;
b->buf[b->len++] = c;
}
static void fmt_puts(fmt_buf_t* b, const char* s) {
int64_t slen = (int64_t)strlen(s);
fmt_ensure(b, slen);
if (b->err) return;
memcpy(b->buf + b->len, s, (size_t)slen);
b->len += (int32_t)slen;
}
static void fmt_printf(fmt_buf_t* b, const char* fmt, ...) {
if (b->err) return;
va_list ap;
va_start(ap, fmt);
int32_t avail = b->cap - b->len;
int n = vsnprintf(b->buf + b->len, (size_t)avail, fmt, ap);
va_end(ap);
if (n < 0) return;
if (n < avail) {
b->len += n;
return;
}
fmt_ensure(b, (int64_t)n + 1);
if (b->err) return;
va_start(ap, fmt);
vsnprintf(b->buf + b->len, (size_t)(b->cap - b->len), fmt, ap);
va_end(ap);
b->len += n;
}
static void fmt_putn(fmt_buf_t* b, const char* s, int64_t n) {
fmt_ensure(b, n);
if (b->err) return;
memcpy(b->buf + b->len, s, (size_t)n);
b->len += (int32_t)n;
}
static ray_t* fmt_to_str(fmt_buf_t* b) {
if (b->err) {
ray_t* e = ray_error("format", "%s", b->err_msg ? b->err_msg : "output buffer error");
fmt_destroy(b);
return e;
}
ray_t* result = ray_str(b->buf, (size_t)b->len);
fmt_destroy(b);
return result;
}
static int g_precision = FMT_DEFAULT_PRECISION;
static int g_row_width = FMT_DEFAULT_ROW_WIDTH;
void ray_fmt_set_precision(int digits) {
if (digits >= 0 && digits <= 20)
g_precision = digits;
}
void ray_fmt_set_width(int cols) {
if (cols > 0)
g_row_width = cols;
}
const char* ray_type_name(int8_t type) {
switch (type < 0 ? -type : type) {
case RAY_BOOL: return type < 0 ? "b8" : "B8";
case RAY_U8: return type < 0 ? "u8" : "U8";
case RAY_I16: return type < 0 ? "i16" : "I16";
case RAY_I32: return type < 0 ? "i32" : "I32";
case RAY_I64: return type < 0 ? "i64" : "I64";
case RAY_F32: return type < 0 ? "f32" : "F32";
case RAY_F64: return type < 0 ? "f64" : "F64";
case RAY_DATE: return type < 0 ? "date" : "DATE";
case RAY_TIME: return type < 0 ? "time" : "TIME";
case RAY_TIMESTAMP: return type < 0 ? "timestamp" : "TIMESTAMP";
case RAY_SYM: return type < 0 ? "sym" : "SYM";
case RAY_STR: return type < 0 ? "str" : "STR";
case RAY_GUID: return type < 0 ? "guid" : "GUID";
case RAY_TABLE: return "TABLE";
case RAY_DICT: return "DICT";
case RAY_LIST: return "LIST";
case RAY_INDEX: return "INDEX";
default: return "?";
}
}
static void fmt_bool(fmt_buf_t* b, uint8_t val) {
fmt_puts(b, val ? "true" : "false");
}
static void fmt_u8(fmt_buf_t* b, uint8_t val) {
fmt_printf(b, "0x%02x", val);
}
static void fmt_i16(fmt_buf_t* b, int16_t val) {
fmt_printf(b, "%d", (int)val);
}
static void fmt_i32(fmt_buf_t* b, int32_t val) {
fmt_printf(b, "%d", (int)val);
}
static void fmt_i64(fmt_buf_t* b, int64_t val) {
fmt_printf(b, "%" PRId64, val);
}
static void fmt_f64(fmt_buf_t* b, double val) {
val = clear_neg_zero(val);
if (val == 0.0) {
char tmp[16];
int n = snprintf(tmp, sizeof(tmp), "%.*f", g_precision, 0.0);
char* dot = strchr(tmp, '.');
if (dot) { char* end = tmp + n - 1; while (end > dot + 1 && *end == '0') end--; n = (int)(end - tmp + 1); }
fmt_putn(b, tmp, (int32_t)n);
return;
}
double absval = val < 0 ? -val : val;
double order = log10(absval);
char tmp[64];
int n;
if (val != 0.0 && (order > 6 || order < -1))
n = snprintf(tmp, sizeof(tmp), "%.*e", g_precision, val);
else
n = snprintf(tmp, sizeof(tmp), "%.*f", g_precision, val);
if (n <= 0 || n >= (int)sizeof(tmp)) {
fmt_puts(b, "?");
return;
}
char* dot = strchr(tmp, '.');
char* e = strchr(tmp, 'e');
if (dot && !e) {
char* end = tmp + n - 1;
while (end > dot + 1 && *end == '0')
end--;
n = (int)(end - tmp + 1);
}
fmt_putn(b, tmp, (int32_t)n);
}
static void fmt_f32(fmt_buf_t* b, float val) {
fmt_f64(b, (double)val);
}
static void fmt_guid(fmt_buf_t* b, const uint8_t* bytes) {
static const char hex[] = "0123456789abcdef";
static const int groups[] = {4, 2, 2, 2, 6};
int pos = 0;
for (int g = 0; g < 5; g++) {
if (g > 0) fmt_putc(b, '-');
for (int j = 0; j < groups[g]; j++) {
fmt_putc(b, hex[bytes[pos] >> 4]);
fmt_putc(b, hex[bytes[pos] & 0x0F]);
pos++;
}
}
}
static void fmt_sym_atom(fmt_buf_t* b, ray_t* s) {
if (s && !RAY_IS_ERR(s) && ray_str_len(s) > 0) {
fmt_putn(b, ray_str_ptr(s), (int64_t)ray_str_len(s));
} else {
fmt_putc(b, '\'');
}
}
static void fmt_sym(fmt_buf_t* b, int64_t sym_id) {
fmt_sym_atom(b, ray_sym_str(sym_id));
}
#include "lang/cal.h"
static void time_to_hms(int32_t ms, int* h, int* min, int* s, int* ms_out) {
int32_t mask = -(int32_t)((uint32_t)ms >> 31);
int32_t val = (mask ^ ms) - mask;
int32_t secs = val / 1000;
*ms_out = (int)(val % 1000);
*h = (int)(secs / 3600);
int32_t rem = secs % 3600;
*min = (int)(rem / 60);
*s = (int)(rem % 60);
}
#define NSECS_IN_DAY ((int64_t)24 * 60 * 60 * 1000000000LL)
static void ts_to_parts(int64_t ns, int* y, int* mo, int* d,
int* h, int* mi, int* s, int* nanos) {
int64_t days = ns / NSECS_IN_DAY;
int64_t span = ns % NSECS_IN_DAY;
if (span < 0) {
days -= 1;
span += NSECS_IN_DAY;
}
date_to_ymd((int32_t)days, y, mo, d);
int64_t secs = span / 1000000000LL;
*nanos = (int)(span % 1000000000LL);
*h = (int)(secs / 3600);
int64_t rem = secs % 3600;
*mi = (int)(rem / 60);
*s = (int)(rem % 60);
}
static void fmt_date(fmt_buf_t* b, int32_t val) {
int y, m, d;
date_to_ymd(val, &y, &m, &d);
fmt_printf(b, "%04d.%02d.%02d", y, m, d);
}
static void fmt_time(fmt_buf_t* b, int32_t val) {
int h, m, s, ms;
time_to_hms(val, &h, &m, &s, &ms);
if (val < 0) fmt_putc(b, '-');
fmt_printf(b, "%02d:%02d:%02d.%03d", h, m, s, ms);
}
static void fmt_timestamp(fmt_buf_t* b, int64_t val) {
int y, mo, d, h, mi, s, ns;
ts_to_parts(val, &y, &mo, &d, &h, &mi, &s, &ns);
fmt_printf(b, "%04d.%02d.%02dD%02d:%02d:%02d.%09d", y, mo, d, h, mi, s, ns);
}
static void fmt_str_atom(fmt_buf_t* b, ray_t* obj, int full) {
(void)full;
const char* p = ray_str_ptr(obj);
size_t n = ray_str_len(obj);
fmt_putc(b, '"');
fmt_putn(b, p, (int64_t)n);
fmt_putc(b, '"');
}
static void fmt_obj(fmt_buf_t* b, ray_t* obj, int mode);
static const char* null_literal(int8_t type) {
switch (type) {
case RAY_BOOL: return "0Nb";
case RAY_U8: return "0Nu";
case RAY_I16: return "0Nh";
case RAY_I32: return "0Ni";
case RAY_I64: return "0Nl";
case RAY_F64: return "0Nf";
case RAY_F32: return "0Ne";
case RAY_DATE: return "0Nd";
case RAY_TIME: return "0Nt";
case RAY_TIMESTAMP: return "0Np";
case RAY_GUID: return "0Ng";
default: return "null";
}
}
static void fmt_raw_elem(fmt_buf_t* b, ray_t* vec, int64_t idx) {
if (vec->type != RAY_STR && ray_vec_is_null(vec, idx)) {
fmt_puts(b, null_literal(vec->type));
return;
}
switch (vec->type) {
case RAY_BOOL: fmt_bool(b, ((bool*)ray_data(vec))[idx]); break;
case RAY_U8: fmt_u8(b, ((uint8_t*)ray_data(vec))[idx]); break;
case RAY_I16: fmt_i16(b, ((int16_t*)ray_data(vec))[idx]); break;
case RAY_I32: fmt_i32(b, ((int32_t*)ray_data(vec))[idx]); break;
case RAY_I64: fmt_i64(b, ((int64_t*)ray_data(vec))[idx]); break;
case RAY_F32: fmt_f32(b, ((float*)ray_data(vec))[idx]); break;
case RAY_F64: fmt_f64(b, ((double*)ray_data(vec))[idx]); break;
case RAY_DATE: fmt_date(b, ((int32_t*)ray_data(vec))[idx]); break;
case RAY_TIME: fmt_time(b, ((int32_t*)ray_data(vec))[idx]); break;
case RAY_TIMESTAMP: fmt_timestamp(b, ((int64_t*)ray_data(vec))[idx]); break;
case RAY_SYM:
fmt_sym_atom(b, ray_sym_vec_cell(vec, idx));
break;
case RAY_STR: {
size_t slen = 0;
const char* p = ray_str_vec_get(vec, idx, &slen);
fmt_putc(b, '"');
if (p) fmt_putn(b, p, (int64_t)slen);
fmt_putc(b, '"');
break;
}
case RAY_GUID:
fmt_guid(b, ((uint8_t*)ray_data(vec)) + idx * 16);
break;
case RAY_LIST: {
ray_t* child = ((ray_t**)ray_data(vec))[idx];
if (child) {
ray_t* s = ray_fmt(child, 1);
if (s && !RAY_IS_ERR(s)) {
fmt_putn(b, ray_str_ptr(s), (int64_t)ray_str_len(s));
ray_release(s);
} else {
fmt_puts(b, "?");
}
} else {
fmt_puts(b, "null");
}
break;
}
default:
fmt_puts(b, "?");
break;
}
}
static void fmt_vector(fmt_buf_t* b, ray_t* vec, int limit) {
int64_t len = ray_len(vec);
if (len == 0) { fmt_puts(b, "[]"); return; }
fmt_puts(b, "[");
int32_t start_len = b->len;
for (int64_t i = 0; i < len; i++) {
if (i > 0) fmt_putc(b, ' ');
int32_t before = b->len;
fmt_raw_elem(b, vec, i);
if (limit > 0 && (b->len - start_len) > limit) {
b->len = before;
fmt_puts(b, "..]");
return;
}
}
fmt_puts(b, "]");
}
static void fmt_list(fmt_buf_t* b, ray_t* list, int mode) {
int64_t len = ray_len(list);
if (len == 0) { fmt_puts(b, "()"); return; }
if (mode != 1) {
ray_t** items = (ray_t**)ray_data(list);
if (items && items[0] && !RAY_IS_ERR(items[0]) && ray_is_atom(items[0])) {
int8_t first_type = items[0]->type;
int homogeneous = 1;
for (int64_t i = 1; i < len; i++) {
if (!items[i] || RAY_IS_ERR(items[i]) || items[i]->type != first_type) {
homogeneous = 0; break;
}
}
if (homogeneous) {
fmt_puts(b, "[");
for (int64_t i = 0; i < len; i++) {
if (i > 0) fmt_putc(b, ' ');
fmt_obj(b, items[i], mode);
}
fmt_puts(b, "]");
return;
}
}
}
if (mode == 0)
fmt_puts(b, "(list ");
else
fmt_puts(b, "(");
int64_t max_elems = (mode == 1) ? FMT_LIST_MAX_HEIGHT : len;
int64_t show = len < max_elems ? len : max_elems;
for (int64_t i = 0; i < show; i++) {
if (i > 0) fmt_putc(b, ' ');
ray_t* elem = ray_list_get(list, i);
fmt_obj(b, elem, mode);
}
if (len > show) fmt_puts(b, " ..");
fmt_puts(b, ")");
}
static void fmt_dict_key(fmt_buf_t* b, ray_t* keys, int64_t i, int mode) {
bool k_is_null = (keys->type != RAY_LIST) && ray_vec_is_null(keys, i);
ray_t k_atom_storage;
ray_t* k_atom = NULL;
memset(&k_atom_storage, 0, sizeof(k_atom_storage));
bool k_owned = false;
if (keys->type == RAY_SYM) {
ray_t* ks = ray_sym_vec_cell(keys, i);
k_atom_storage.type = -RAY_SYM;
k_atom_storage.i64 = ks ? ray_sym_intern(ray_str_ptr(ks), ray_str_len(ks)) : -1;
k_atom = &k_atom_storage;
} else if (keys->type == RAY_STR) {
size_t slen = 0;
const char* sp = ray_str_vec_get(keys, i, &slen);
k_atom = ray_str(sp ? sp : "", sp ? slen : 0);
k_owned = true;
} else if (keys->type == RAY_I64 || keys->type == RAY_TIMESTAMP) {
k_atom_storage.type = (int8_t)-keys->type;
k_atom_storage.i64 = ((int64_t*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_I32 || keys->type == RAY_DATE || keys->type == RAY_TIME) {
k_atom_storage.type = (int8_t)-keys->type;
k_atom_storage.i32 = ((int32_t*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_I16) {
k_atom_storage.type = -RAY_I16;
k_atom_storage.i16 = ((int16_t*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_BOOL || keys->type == RAY_U8) {
k_atom_storage.type = (int8_t)-keys->type;
k_atom_storage.u8 = ((uint8_t*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_F64) {
k_atom_storage.type = -RAY_F64;
k_atom_storage.f64 = ((double*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_F32) {
k_atom_storage.type = -RAY_F32;
k_atom_storage.f64 = (double)((float*)ray_data(keys))[i];
k_atom = &k_atom_storage;
} else if (keys->type == RAY_GUID) {
k_atom = ray_guid(((const uint8_t*)ray_data(keys)) + i * 16);
k_owned = (k_atom && !RAY_IS_ERR(k_atom));
} else if (keys->type == RAY_LIST) {
k_atom = ((ray_t**)ray_data(keys))[i];
}
if (k_is_null && k_atom) k_atom->aux[0] |= 1;
if (k_atom) fmt_obj(b, k_atom, mode);
if (k_owned && k_atom) ray_release(k_atom);
}
static void fmt_dict_val(fmt_buf_t* b, ray_t* vals, int64_t i, int mode) {
if (vals && vals->type == RAY_LIST) {
ray_t* v = ray_list_get(vals, i);
fmt_obj(b, v, mode);
} else if (vals && i < vals->len) {
bool v_is_null = ray_vec_is_null(vals, i);
ray_t v_storage; memset(&v_storage, 0, sizeof(v_storage));
ray_t* v_atom = NULL;
bool v_owned = false;
switch (vals->type) {
case RAY_BOOL:
case RAY_U8: v_storage.type = (int8_t)-vals->type;
v_storage.u8 = ((uint8_t*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_I16: v_storage.type = -RAY_I16;
v_storage.i16 = ((int16_t*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_I32:
case RAY_DATE:
case RAY_TIME: v_storage.type = (int8_t)-vals->type;
v_storage.i32 = ((int32_t*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_I64:
case RAY_TIMESTAMP: v_storage.type = (int8_t)-vals->type;
v_storage.i64 = ((int64_t*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_F32: v_storage.type = -RAY_F32;
v_storage.f64 = (double)((float*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_F64: v_storage.type = -RAY_F64;
v_storage.f64 = ((double*)ray_data(vals))[i];
v_atom = &v_storage; break;
case RAY_SYM: {
ray_t* vs = ray_sym_vec_cell(vals, i);
v_storage.type = -RAY_SYM;
v_storage.i64 = vs ? ray_sym_intern(ray_str_ptr(vs), ray_str_len(vs)) : -1;
v_atom = &v_storage; break;
}
case RAY_STR: {
size_t vl = 0;
const char* vp = ray_str_vec_get(vals, i, &vl);
v_atom = ray_str(vp ? vp : "", vp ? vl : 0);
v_owned = true;
break;
}
case RAY_GUID:
v_atom = ray_guid(((const uint8_t*)ray_data(vals)) + i * 16);
v_owned = (v_atom && !RAY_IS_ERR(v_atom));
break;
default: break;
}
if (v_is_null && v_atom) v_atom->aux[0] |= 1;
if (v_atom) fmt_obj(b, v_atom, mode);
if (v_owned && v_atom) ray_release(v_atom);
}
}
static void fmt_dict(fmt_buf_t* b, ray_t* dict, int mode) {
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
int64_t npairs = keys ? keys->len : 0;
if (npairs == 0) { fmt_puts(b, "{}"); return; }
int64_t max_pairs = (mode == 1) ? FMT_LIST_MAX_HEIGHT : npairs;
int64_t show = npairs < max_pairs ? npairs : max_pairs;
fmt_puts(b, "{");
for (int64_t i = 0; i < show; i++) {
if (i > 0) fmt_putc(b, ' ');
fmt_dict_key(b, keys, i, mode);
fmt_putc(b, ':');
fmt_dict_val(b, vals, i, mode);
}
if (npairs > show) fmt_puts(b, " ..");
fmt_puts(b, "}");
}
#define G_TL "\xe2\x94\x8c"
#define G_TR "\xe2\x94\x90"
#define G_BL "\xe2\x94\x94"
#define G_BR "\xe2\x94\x98"
#define G_H "\xe2\x94\x80"
#define G_V "\xe2\x94\x82"
#define G_TT "\xe2\x94\xac"
#define G_BT "\xe2\x94\xb4"
#define G_LT "\xe2\x94\x9c"
#define G_RT "\xe2\x94\xa4"
#define G_X "\xe2\x94\xbc"
#define G_HDOTS "\xe2\x80\xa6"
#define G_VDOTS "\xe2\x94\x86"
static int32_t fmt_utf8_width(const char* s, int32_t slen) {
int32_t w = 0;
for (int32_t i = 0; i < slen;) {
uint8_t c = (uint8_t)s[i];
if (c < 0x80) { w += 1; i += 1; continue; }
uint32_t cp;
int32_t cont;
if ((c & 0xE0) == 0xC0) { cp = c & 0x1Fu; cont = 1; }
else if ((c & 0xF0) == 0xE0) { cp = c & 0x0Fu; cont = 2; }
else if ((c & 0xF8) == 0xF0) { cp = c & 0x07u; cont = 3; }
else { w += 1; i += 1; continue; }
if (i + cont >= slen) { w += 1; i += 1; continue; }
bool ok = true;
for (int32_t k = 1; k <= cont; k++) {
uint8_t cc = (uint8_t)s[i + k];
if ((cc & 0xC0) != 0x80) { ok = false; break; }
cp = (cp << 6) | (cc & 0x3Fu);
}
if (!ok) { w += 1; i += 1; continue; }
i += cont + 1;
if ((cp >= 0x0300 && cp <= 0x036F) ||
(cp >= 0x0483 && cp <= 0x0489) ||
(cp >= 0x0591 && cp <= 0x05BD) ||
cp == 0x05BF || cp == 0x05C1 || cp == 0x05C2 ||
cp == 0x05C4 || cp == 0x05C5 || cp == 0x05C7 ||
(cp >= 0x0610 && cp <= 0x061A) ||
(cp >= 0x064B && cp <= 0x065F) ||
cp == 0x0670 ||
(cp >= 0x06D6 && cp <= 0x06DC) ||
(cp >= 0x0E31 && cp <= 0x0E3A) ||
(cp >= 0x0E47 && cp <= 0x0E4E) ||
(cp >= 0x200B && cp <= 0x200F) ||
(cp >= 0x202A && cp <= 0x202E) ||
(cp >= 0x2060 && cp <= 0x2064) ||
(cp >= 0x2066 && cp <= 0x2069) ||
(cp >= 0xFE00 && cp <= 0xFE0F) ||
(cp >= 0xFE20 && cp <= 0xFE2F) ||
cp == 0xFEFF)
continue;
if ((cp >= 0x1100 && cp <= 0x115F) ||
(cp >= 0x2E80 && cp <= 0xA4CF) ||
(cp >= 0xAC00 && cp <= 0xD7A3) ||
(cp >= 0xF900 && cp <= 0xFAFF) ||
(cp >= 0xFE30 && cp <= 0xFE4F) ||
(cp >= 0xFF00 && cp <= 0xFF60) ||
(cp >= 0xFFE0 && cp <= 0xFFE6) ||
(cp >= 0x1F300 && cp <= 0x1FAFF) ||
(cp >= 0x20000 && cp <= 0x3FFFD))
w += 2;
else
w += 1;
}
return w;
}
static void fmt_centered(fmt_buf_t* b, const char* s, int32_t slen, int32_t width) {
int32_t dlen = fmt_utf8_width(s, slen);
int32_t left = (width - dlen) / 2;
int32_t right = width - dlen - left;
for (int32_t i = 0; i < left; i++) fmt_putc(b, ' ');
fmt_putn(b, s, slen);
for (int32_t i = 0; i < right; i++) fmt_putc(b, ' ');
}
#define FMT_CELL_BUF_SIZE 64
typedef struct {
char str[FMT_CELL_BUF_SIZE];
int32_t len;
} fmt_cell_t;
static void fmt_stream_cell(fmt_buf_t* tmp, ray_t* col, int64_t ri,
const char** s, int32_t* len) {
if (!col || ri >= ray_len(col)) { *s = "NA"; *len = 2; return; }
tmp->len = 0;
fmt_raw_elem(tmp, col, ri);
*s = tmp->buf;
*len = tmp->len;
}
static void fmt_stream_rule(fmt_buf_t* b, const int32_t* w, int64_t n,
const char* left, const char* mid, const char* right) {
fmt_puts(b, left);
for (int64_t ci = 0; ci < n; ci++) {
for (int32_t j = 0; j < w[ci]; j++) fmt_puts(b, G_H);
fmt_puts(b, ci < n - 1 ? mid : right);
}
}
#define FMT_SHOW_PAD_MAX 256
static void fmt_table_stream(fmt_buf_t* b, ray_t* tbl) {
int64_t ncols = ray_table_ncols(tbl);
int64_t nrows = ray_table_nrows(tbl);
if (ncols <= 0) { fmt_puts(b, "<table>"); return; }
ray_t* wblk = ray_alloc((size_t)ncols * sizeof(int32_t));
ray_t* nblk = ray_alloc((size_t)ncols * sizeof(ray_t*));
if (nblk) memset(ray_data(nblk), 0, (size_t)ncols * sizeof(ray_t*));
fmt_buf_t tmp;
fmt_init(&tmp);
if (!wblk || !nblk || tmp.err) {
b->err = true; b->err_msg = "out of memory formatting a table";
goto done;
}
int32_t* widths = (int32_t*)ray_data(wblk);
ray_t** names = (ray_t**)ray_data(nblk);
for (int64_t ci = 0; ci < ncols; ci++) {
ray_t* nm = ray_sym_str(ray_table_col_name(tbl, ci));
names[ci] = (nm && !RAY_IS_ERR(nm)) ? nm : NULL;
ray_t* col = ray_table_get_col_idx(tbl, ci);
const char* tname = ray_type_name(col ? col->type : 0);
int32_t w = names[ci] ? fmt_utf8_width(ray_str_ptr(names[ci]), (int32_t)ray_str_len(names[ci])) : 1;
if ((int32_t)strlen(tname) > w) w = (int32_t)strlen(tname);
int32_t cell_w = 0;
for (int64_t ri = 0; ri < nrows && cell_w < FMT_SHOW_PAD_MAX; ri++) {
const char* cs; int32_t cl;
fmt_stream_cell(&tmp, col, ri, &cs, &cl);
if (tmp.err) { b->err = true; b->err_msg = "out of memory formatting a table"; goto done; }
int32_t dw = fmt_utf8_width(cs, cl);
if (dw > cell_w) cell_w = dw;
}
if (cell_w > FMT_SHOW_PAD_MAX) cell_w = FMT_SHOW_PAD_MAX;
if (cell_w > w) w = cell_w;
widths[ci] = w + 2;
}
int32_t total = (int32_t)(ncols - 1);
for (int64_t ci = 0; ci < ncols; ci++) total += widths[ci];
char footer[128];
int footer_len = snprintf(footer, sizeof(footer),
" %" PRId64 " rows (%" PRId64 " shown) %" PRId64 " columns (%" PRId64 " shown)",
nrows, nrows, ncols, ncols);
if (total < footer_len) { widths[ncols - 1] += footer_len - total; total = footer_len; }
fmt_stream_rule(b, widths, ncols, G_TL, G_TT, G_TR);
fmt_putc(b, '\n');
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < ncols; ci++) {
if (names[ci]) fmt_centered(b, ray_str_ptr(names[ci]), (int32_t)ray_str_len(names[ci]), widths[ci]);
else fmt_centered(b, "?", 1, widths[ci]);
fmt_puts(b, G_V);
}
fmt_putc(b, '\n');
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < ncols; ci++) {
ray_t* col = ray_table_get_col_idx(tbl, ci);
const char* tname = ray_type_name(col ? col->type : 0);
fmt_centered(b, tname, (int32_t)strlen(tname), widths[ci]);
fmt_puts(b, G_V);
}
fmt_putc(b, '\n');
fmt_stream_rule(b, widths, ncols, G_LT, G_X, G_RT);
for (int64_t ri = 0; ri < nrows && !b->err; ri++) {
fmt_putc(b, '\n');
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < ncols; ci++) {
const char* cs; int32_t cl;
fmt_stream_cell(&tmp, ray_table_get_col_idx(tbl, ci), ri, &cs, &cl);
if (tmp.err) { b->err = true; b->err_msg = "out of memory formatting a table"; goto done; }
fmt_putc(b, ' ');
fmt_putn(b, cs, cl);
for (int32_t p = widths[ci] - fmt_utf8_width(cs, cl) - 1; p > 0; p--) fmt_putc(b, ' ');
fmt_puts(b, G_V);
}
}
fmt_putc(b, '\n');
fmt_stream_rule(b, widths, ncols, G_LT, G_BT, G_RT);
fmt_putc(b, '\n');
fmt_puts(b, G_V);
fmt_putn(b, footer, footer_len);
for (int32_t i = footer_len; i < total; i++) fmt_putc(b, ' ');
fmt_puts(b, G_V);
fmt_putc(b, '\n');
fmt_puts(b, G_BL);
for (int32_t i = 0; i < total; i++) fmt_puts(b, G_H);
fmt_puts(b, G_BR);
done:
if (nblk) {
ray_t** nm = (ray_t**)ray_data(nblk);
for (int64_t ci = 0; ci < ncols; ci++) if (nm[ci]) ray_release(nm[ci]);
ray_free(nblk);
}
if (wblk) ray_free(wblk);
fmt_destroy(&tmp);
}
static void fmt_table(fmt_buf_t* b, ray_t* tbl, int mode) {
int64_t ncols = ray_table_ncols(tbl);
int64_t nrows = ray_table_nrows(tbl);
if (mode == 0) {
fmt_puts(b, "(table [");
for (int64_t i = 0; i < ncols; i++) {
if (i > 0) fmt_putc(b, ' ');
int64_t name_id = ray_table_col_name(tbl, i);
ray_t* name_str = ray_sym_str(name_id);
if (name_str && !RAY_IS_ERR(name_str)) {
fmt_putn(b, ray_str_ptr(name_str), (int64_t)ray_str_len(name_str));
ray_release(name_str);
}
}
fmt_puts(b, "] (list ");
for (int64_t i = 0; i < ncols; i++) {
if (i > 0) fmt_putc(b, ' ');
ray_t* col = ray_table_get_col_idx(tbl, i);
if (col) {
fmt_obj(b, col, mode);
}
}
fmt_puts(b, "))");
return;
}
if (mode == 2 && b->sink) { fmt_table_stream(b, tbl); return; }
int64_t table_width = ncols;
int64_t table_height = nrows;
if (mode == 1) {
if (table_width > FMT_TABLE_MAX_WIDTH)
table_width = FMT_TABLE_MAX_WIDTH;
if (table_height > FMT_TABLE_MAX_HEIGHT)
table_height = FMT_TABLE_MAX_HEIGHT;
}
if (table_width == 0) {
fmt_puts(b, "<table>");
return;
}
bool has_hidden_cols = (table_width < ncols);
bool has_hidden_rows = (table_height < nrows);
bool heap_alloc = (table_width > FMT_TABLE_MAX_WIDTH ||
table_height > FMT_TABLE_MAX_HEIGHT);
int32_t col_widths_stack[FMT_TABLE_MAX_WIDTH];
const char* col_names_stack[FMT_TABLE_MAX_WIDTH];
int32_t col_name_lens_stack[FMT_TABLE_MAX_WIDTH];
const char* col_types_stack[FMT_TABLE_MAX_WIDTH];
int32_t col_type_lens_stack[FMT_TABLE_MAX_WIDTH];
ray_t* name_refs_stack[FMT_TABLE_MAX_WIDTH];
fmt_cell_t cells_stack[FMT_TABLE_MAX_WIDTH * FMT_TABLE_MAX_HEIGHT];
ray_t* heap_widths_blk = NULL;
ray_t* heap_names_blk = NULL;
ray_t* heap_nlen_blk = NULL;
ray_t* heap_types_blk = NULL;
ray_t* heap_tlen_blk = NULL;
ray_t* heap_refs_blk = NULL;
ray_t* heap_cells_blk = NULL;
int32_t* col_widths;
const char** col_names;
int32_t* col_name_lens;
const char** col_types;
int32_t* col_type_lens;
ray_t** name_refs;
fmt_cell_t* cells;
if (!heap_alloc) {
col_widths = col_widths_stack;
col_names = col_names_stack;
col_name_lens = col_name_lens_stack;
col_types = col_types_stack;
col_type_lens = col_type_lens_stack;
name_refs = name_refs_stack;
cells = cells_stack;
} else {
heap_widths_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(int32_t)));
heap_names_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(const char*)));
heap_nlen_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(int32_t)));
heap_types_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(const char*)));
heap_tlen_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(int32_t)));
heap_refs_blk = ray_alloc((size_t)(table_width * (int64_t)sizeof(ray_t*)));
heap_cells_blk = ray_alloc((size_t)(table_width * table_height * (int64_t)sizeof(fmt_cell_t)));
if (!heap_widths_blk || !heap_names_blk || !heap_nlen_blk || !heap_types_blk ||
!heap_tlen_blk || !heap_refs_blk || !heap_cells_blk) {
ray_t* blks[7] = { heap_widths_blk, heap_names_blk, heap_nlen_blk, heap_types_blk,
heap_tlen_blk, heap_refs_blk, heap_cells_blk };
for (int k = 0; k < 7; k++) if (blks[k]) ray_free(blks[k]);
b->err = true; b->err_msg = "out of memory formatting a table";
return;
}
col_widths = (int32_t*)ray_data(heap_widths_blk);
col_names = (const char**)ray_data(heap_names_blk);
col_name_lens = (int32_t*)ray_data(heap_nlen_blk);
col_types = (const char**)ray_data(heap_types_blk);
col_type_lens = (int32_t*)ray_data(heap_tlen_blk);
name_refs = (ray_t**)ray_data(heap_refs_blk);
cells = (fmt_cell_t*)ray_data(heap_cells_blk);
}
for (int64_t ci = 0; ci < table_width; ci++) {
int64_t name_id = ray_table_col_name(tbl, ci);
ray_t* name_str = ray_sym_str(name_id);
name_refs[ci] = name_str;
if (name_str && !RAY_IS_ERR(name_str)) {
col_names[ci] = ray_str_ptr(name_str);
col_name_lens[ci] = (int32_t)ray_str_len(name_str);
} else {
col_names[ci] = "?";
col_name_lens[ci] = 1;
name_refs[ci] = NULL;
}
ray_t* col_vec = ray_table_get_col_idx(tbl, ci);
const char* tname = ray_type_name(col_vec ? col_vec->type : 0);
col_types[ci] = tname;
col_type_lens[ci] = (int32_t)strlen(tname);
int32_t max_w = fmt_utf8_width(col_names[ci], col_name_lens[ci]);
if (col_type_lens[ci] > max_w) max_w = col_type_lens[ci];
int64_t col_len = col_vec ? ray_len(col_vec) : 0;
int64_t half = table_height / 2;
for (int64_t ri = 0; ri < half; ri++) {
fmt_cell_t* cell = &cells[ci * table_height + ri];
if (ri < col_len) {
fmt_buf_t tmp;
fmt_init(&tmp);
fmt_raw_elem(&tmp, col_vec, ri);
int32_t clen = tmp.len < FMT_CELL_BUF_SIZE - 1 ? tmp.len : FMT_CELL_BUF_SIZE - 1;
if (clen > 0) memcpy(cell->str, tmp.buf, (size_t)clen);
cell->str[clen] = '\0';
cell->len = clen;
fmt_destroy(&tmp);
} else {
memcpy(cell->str, "NA", 3);
cell->len = 2;
}
int32_t cell_dw = fmt_utf8_width(cell->str, cell->len);
if (cell_dw > max_w) max_w = cell_dw;
}
for (int64_t ri = half; ri < table_height; ri++) {
fmt_cell_t* cell = &cells[ci * table_height + ri];
int64_t src_idx;
if (table_height == col_len || !has_hidden_rows) {
src_idx = ri;
} else {
src_idx = col_len - table_height + ri;
}
if (src_idx >= 0 && src_idx < col_len) {
fmt_buf_t tmp;
fmt_init(&tmp);
fmt_raw_elem(&tmp, col_vec, src_idx);
int32_t clen = tmp.len < FMT_CELL_BUF_SIZE - 1 ? tmp.len : FMT_CELL_BUF_SIZE - 1;
if (clen > 0) memcpy(cell->str, tmp.buf, (size_t)clen);
cell->str[clen] = '\0';
cell->len = clen;
fmt_destroy(&tmp);
} else {
memcpy(cell->str, "NA", 3);
cell->len = 2;
}
int32_t cell_dw = fmt_utf8_width(cell->str, cell->len);
if (cell_dw > max_w) max_w = cell_dw;
}
col_widths[ci] = max_w + 2;
}
int32_t total_width = 0;
for (int64_t ci = 0; ci < table_width; ci++)
total_width += col_widths[ci];
total_width += (int32_t)(table_width - 1);
char footer[128];
int footer_len = snprintf(footer, sizeof(footer),
" %" PRId64 " rows (%" PRId64 " shown) %" PRId64 " columns (%" PRId64 " shown)",
nrows, table_height, ncols, table_width);
if (total_width < footer_len) {
col_widths[table_width - 1] += footer_len - total_width;
total_width = footer_len;
}
if (has_hidden_cols)
total_width += 4;
fmt_puts(b, G_TL);
for (int64_t ci = 0; ci < table_width; ci++) {
for (int32_t j = 0; j < col_widths[ci]; j++)
fmt_puts(b, G_H);
if (ci < table_width - 1 || has_hidden_cols)
fmt_puts(b, G_TT);
else
fmt_puts(b, G_TR);
}
if (has_hidden_cols) {
fmt_puts(b, G_H G_H G_H G_TR);
}
fmt_putc(b, '\n');
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < table_width; ci++) {
fmt_centered(b, col_names[ci], col_name_lens[ci], col_widths[ci]);
fmt_puts(b, G_V);
}
if (has_hidden_cols) {
fmt_puts(b, " " G_HDOTS " " G_V);
}
fmt_putc(b, '\n');
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < table_width; ci++) {
fmt_centered(b, col_types[ci], col_type_lens[ci], col_widths[ci]);
fmt_puts(b, G_V);
}
if (has_hidden_cols) {
fmt_puts(b, " " G_HDOTS " " G_V);
}
fmt_putc(b, '\n');
fmt_puts(b, G_LT);
for (int64_t ci = 0; ci < table_width; ci++) {
for (int32_t j = 0; j < col_widths[ci]; j++)
fmt_puts(b, G_H);
if (ci < table_width - 1 || has_hidden_cols)
fmt_puts(b, G_X);
else
fmt_puts(b, G_RT);
}
if (has_hidden_cols) {
fmt_puts(b, G_H G_H G_H G_RT);
}
int64_t half = table_height / 2;
for (int64_t ri = 0; ri < table_height; ri++) {
fmt_putc(b, '\n');
if (has_hidden_rows && ri == half) {
fmt_puts(b, G_VDOTS);
for (int64_t ci = 0; ci < table_width; ci++) {
int32_t left = (col_widths[ci] - 1) / 2;
int32_t right = col_widths[ci] - 1 - left;
for (int32_t p = 0; p < left; p++) fmt_putc(b, ' ');
fmt_puts(b, G_HDOTS);
for (int32_t p = 0; p < right; p++) fmt_putc(b, ' ');
fmt_puts(b, G_VDOTS);
}
if (has_hidden_cols) {
fmt_puts(b, " " G_HDOTS " " G_VDOTS);
}
fmt_putc(b, '\n');
}
fmt_puts(b, G_V);
for (int64_t ci = 0; ci < table_width; ci++) {
fmt_cell_t* cell = &cells[ci * table_height + ri];
fmt_putc(b, ' ');
fmt_putn(b, cell->str, cell->len);
int32_t pad = col_widths[ci] - fmt_utf8_width(cell->str, cell->len) - 1;
for (int32_t p = 0; p < pad; p++)
fmt_putc(b, ' ');
fmt_puts(b, G_V);
}
if (has_hidden_cols) {
fmt_puts(b, " " G_HDOTS " " G_V);
}
}
fmt_putc(b, '\n');
fmt_puts(b, G_LT);
for (int64_t ci = 0; ci < table_width; ci++) {
for (int32_t j = 0; j < col_widths[ci]; j++)
fmt_puts(b, G_H);
if (ci < table_width - 1 || has_hidden_cols)
fmt_puts(b, G_BT);
else
fmt_puts(b, G_RT);
}
if (has_hidden_cols) {
fmt_puts(b, G_H G_H G_H G_RT);
}
fmt_putc(b, '\n');
fmt_puts(b, G_V);
fmt_putn(b, footer, footer_len);
for (int32_t i = footer_len; i < total_width; i++)
fmt_putc(b, ' ');
fmt_puts(b, G_V);
fmt_putc(b, '\n');
fmt_puts(b, G_BL);
for (int32_t i = 0; i < total_width; i++)
fmt_puts(b, G_H);
fmt_puts(b, G_BR);
for (int64_t ci = 0; ci < table_width; ci++) {
if (name_refs[ci]) ray_release(name_refs[ci]);
}
if (heap_alloc) {
ray_free(heap_widths_blk);
ray_free(heap_names_blk);
ray_free(heap_nlen_blk);
ray_free(heap_types_blk);
ray_free(heap_tlen_blk);
ray_free(heap_refs_blk);
ray_free(heap_cells_blk);
}
}
static void fmt_obj(fmt_buf_t* b, ray_t* obj, int mode) {
if (!obj || RAY_IS_NULL(obj)) { fmt_puts(b, "null"); return; }
if (RAY_IS_ERR(obj)) {
char code[8] = {0};
memcpy(code, obj->sdata, obj->slen < 7 ? obj->slen : 7);
fmt_puts(b, "error: ");
fmt_puts(b, code);
return;
}
int8_t type = obj->type;
if (type < 0) {
if (-type != RAY_STR && -type != RAY_SYM && RAY_ATOM_IS_NULL(obj)) {
fmt_puts(b, null_literal(-type));
return;
}
switch (-type) {
case RAY_BOOL: fmt_bool(b, obj->b8); break;
case RAY_U8: fmt_u8(b, obj->u8); break;
case RAY_I16: fmt_i16(b, obj->i16); break;
case RAY_I32: fmt_i32(b, obj->i32); break;
case RAY_I64: fmt_i64(b, obj->i64); break;
case RAY_F32: fmt_f32(b, (float)obj->f64); break;
case RAY_F64: fmt_f64(b, obj->f64); break;
case RAY_DATE: fmt_date(b, obj->i32); break;
case RAY_TIME: fmt_time(b, obj->i32); break;
case RAY_TIMESTAMP: fmt_timestamp(b, obj->i64); break;
case RAY_SYM: fmt_sym(b, obj->i64); break;
case RAY_STR: fmt_str_atom(b, obj, mode > 0); break;
case RAY_GUID: fmt_guid(b, obj->obj ? (const uint8_t*)ray_data(obj->obj) : (const uint8_t*)ray_data(obj)); break;
default: fmt_puts(b, "?"); break;
}
} else if (ray_is_vec(obj)) {
int limit = (mode == 1) ? g_row_width : -1;
fmt_vector(b, obj, limit);
} else if (type == RAY_LIST) {
fmt_list(b, obj, mode);
} else if (type == RAY_TABLE) {
fmt_table(b, obj, mode);
} else if (type == RAY_DICT) {
fmt_dict(b, obj, mode);
} else if (type == RAY_LAMBDA) {
fmt_puts(b, "lambda");
} else if (type == RAY_UNARY || type == RAY_BINARY || type == RAY_VARY) {
const char* name = ray_fn_name(obj);
if (name[0]) { fmt_puts(b, "<"); fmt_puts(b, name); fmt_puts(b, ">"); }
else fmt_puts(b, type == RAY_UNARY ? "<builtin/1>" :
type == RAY_BINARY ? "<builtin/2>" : "<builtin/n>");
} else if (type == RAY_LAZY) {
ray_retain(obj);
ray_t* concrete = ray_lazy_materialize(obj);
fmt_obj(b, concrete, mode);
if (concrete) ray_release(concrete);
return;
} else {
fmt_printf(b, "<%s>", ray_type_name(type));
}
}
ray_t* ray_fmt(ray_t* obj, int mode) {
fmt_buf_t b;
fmt_init(&b);
fmt_obj(&b, obj, mode);
return fmt_to_str(&b);
}
static int32_t fmt_cur_col(fmt_buf_t* b) {
int32_t c = 0;
while (c < b->len && b->buf[b->len - 1 - c] != '\n') c++;
return c;
}
static void fmt_pp_indent(fmt_buf_t* b, int n) {
for (int i = 0; i < n; i++) fmt_putc(b, ' ');
}
static void fmt_pp(fmt_buf_t* b, ray_t* obj, int indent);
static void fmt_pp_dict_broken(fmt_buf_t* b, ray_t* dict, int indent) {
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
int64_t npairs = keys ? keys->len : 0;
int64_t show = npairs < FMT_PP_MAX_ROWS ? npairs : FMT_PP_MAX_ROWS;
fmt_puts(b, "{\n");
for (int64_t i = 0; i < show; i++) {
fmt_pp_indent(b, indent + 2);
fmt_dict_key(b, keys, i, 1);
fmt_puts(b, ": ");
if (vals && vals->type == RAY_LIST)
fmt_pp(b, ray_list_get(vals, i), indent + 2);
else
fmt_dict_val(b, vals, i, 1);
fmt_putc(b, '\n');
}
if (npairs > show) { fmt_pp_indent(b, indent + 2); fmt_puts(b, "...\n"); }
fmt_pp_indent(b, indent);
fmt_putc(b, '}');
}
static void fmt_pp_list_broken(fmt_buf_t* b, ray_t* list, int indent) {
int64_t len = ray_len(list);
int64_t show = len < FMT_PP_MAX_ROWS ? len : FMT_PP_MAX_ROWS;
fmt_puts(b, "(\n");
for (int64_t i = 0; i < show; i++) {
fmt_pp_indent(b, indent + 2);
fmt_pp(b, ray_list_get(list, i), indent + 2);
fmt_putc(b, '\n');
}
if (len > show) { fmt_pp_indent(b, indent + 2); fmt_puts(b, "...\n"); }
fmt_pp_indent(b, indent);
fmt_putc(b, ')');
}
static void fmt_pp(fmt_buf_t* b, ray_t* obj, int indent) {
if (!obj || RAY_IS_ERR(obj) ||
(obj->type != RAY_DICT && obj->type != RAY_LIST)) {
fmt_obj(b, obj, 1);
return;
}
int32_t col = fmt_cur_col(b);
int32_t saved = b->len;
fmt_obj(b, obj, 1);
if (col + (b->len - saved) <= g_row_width) return;
b->len = saved;
if (obj->type == RAY_DICT) fmt_pp_dict_broken(b, obj, indent);
else fmt_pp_list_broken(b, obj, indent);
}
ray_t* ray_fmt_pp(ray_t* obj) {
fmt_buf_t b;
fmt_init(&b);
fmt_pp(&b, obj, 0);
return fmt_to_str(&b);
}
void ray_fmt_print(FILE* fp, ray_t* obj, int mode) {
ray_t* s = ray_fmt(obj, mode);
if (s) {
fwrite(ray_str_ptr(s), 1, ray_str_len(s), fp);
ray_release(s);
}
}
void ray_fmt_pp_print(FILE* fp, ray_t* obj) {
ray_t* s = ray_fmt_pp(obj);
if (s) {
fwrite(ray_str_ptr(s), 1, ray_str_len(s), fp);
ray_release(s);
}
}
ray_err_t ray_fmt_write(FILE* fp, ray_t* obj) {
fmt_buf_t b;
fmt_init(&b);
if (b.err) return RAY_ERR_OOM;
b.sink = fp;
fmt_obj(&b, obj, 2);
if (!b.err && b.len > 0 && fwrite(b.buf, 1, (size_t)b.len, fp) != (size_t)b.len)
b.io_err = b.err = true;
ray_err_t e = !b.err ? RAY_OK : b.io_err ? RAY_ERR_IO : RAY_ERR_OOM;
if (e == RAY_ERR_OOM && b.flushed) {
fputs("\nerror: out of memory\n", fp);
e = RAY_ERR_IO;
}
fmt_destroy(&b);
return e;
}