diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-12-31 15:32:47 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-12-31 15:38:51 -0500 |
| commit | 8c8444a03d98eb2b9262b14986859a8f4a0a23f4 (patch) | |
| tree | f37c7d1e90637840d145980cd4ac45863dada347 /src/stdlib | |
| parent | 41f92837bebae1de783dc7f4a8f880011c72757c (diff) | |
Move print.c/h back into stdlib
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/bigint.c | 2 | ||||
| -rw-r--r-- | src/stdlib/cli.c | 2 | ||||
| -rw-r--r-- | src/stdlib/files.c | 2 | ||||
| -rw-r--r-- | src/stdlib/memory.c | 2 | ||||
| -rw-r--r-- | src/stdlib/paths.c | 2 | ||||
| -rw-r--r-- | src/stdlib/print.c | 236 | ||||
| -rw-r--r-- | src/stdlib/print.h | 172 | ||||
| -rw-r--r-- | src/stdlib/stacktrace.c | 2 | ||||
| -rw-r--r-- | src/stdlib/stdlib.c | 2 |
9 files changed, 415 insertions, 7 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index 11270848..f36cbe6a 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -10,11 +10,11 @@ #include <stdio.h> #include <stdlib.h> -#include "../print.h" #include "../util.h" #include "datatypes.h" #include "integers.h" #include "optionals.h" +#include "print.h" #include "siphash.h" #include "text.h" #include "types.h" diff --git a/src/stdlib/cli.c b/src/stdlib/cli.c index d424f316..e30f7ced 100644 --- a/src/stdlib/cli.c +++ b/src/stdlib/cli.c @@ -11,7 +11,6 @@ #include <time.h> #include "../config.h" -#include "../print.h" #include "bools.h" #include "bytes.h" #include "c_strings.h" @@ -21,6 +20,7 @@ #include "nums.h" #include "optionals.h" #include "paths.h" +#include "print.h" #include "stdlib.h" #include "tables.h" #include "text.h" diff --git a/src/stdlib/files.c b/src/stdlib/files.c index 78c1bc94..7d56fcfc 100644 --- a/src/stdlib/files.c +++ b/src/stdlib/files.c @@ -12,8 +12,8 @@ #include <string.h> #include <sys/param.h> -#include "../print.h" #include "files.h" +#include "print.h" #include "util.h" static const int tabstop = 4; diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index 5b23e1b5..fcb1d5a6 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -5,9 +5,9 @@ #include <stdint.h> #include <sys/param.h> -#include "../print.h" #include "memory.h" #include "metamethods.h" +#include "print.h" #include "text.h" #include "types.h" #include "util.h" diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index 0ef452d2..26e43dfa 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -17,7 +17,6 @@ #include <sys/types.h> #include <unistd.h> -#include "../print.h" #include "../unistr-fixed.h" #include "../util.h" #include "enums.h" @@ -25,6 +24,7 @@ #include "lists.h" #include "optionals.h" #include "paths.h" +#include "print.h" #include "structs.h" #include "text.h" #include "types.h" diff --git a/src/stdlib/print.c b/src/stdlib/print.c new file mode 100644 index 00000000..bc5b01c6 --- /dev/null +++ b/src/stdlib/print.c @@ -0,0 +1,236 @@ +// This file defines some of the helper functions used for printing values + +#include <ctype.h> +#include <gc.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "fpconv.h" +#include "print.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 diff --git a/src/stdlib/print.h b/src/stdlib/print.h new file mode 100644 index 00000000..5d789ead --- /dev/null +++ b/src/stdlib/print.h @@ -0,0 +1,172 @@ +// This file defines some functions to make it easy to do formatted text +// without using printf style specifiers: +// +// print(...) - print text +// fprint(file, ...) - print text to file +// print_err(...) - print an error message and exit with EXIT_FAILURE +// String(...) - return an allocated string + +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <unistd.h> + +#include "bigint.h" // IWYU pragma: export +#include "datatypes.h" // IWYU pragma: export +#include "integers.h" // IWYU pragma: export +#include "mapmacro.h" +#include "paths.h" // IWYU pragma: export +#include "text.h" // IWYU pragma: export + +// GCC lets you define macro-like functions which are always inlined and never +// compiled using this combination of flags. See: https://gcc.gnu.org/onlinedocs/gcc/Inline.html +#ifndef PRINT_FN +#ifdef __TINYC__ +#define PRINT_FN static inline __attribute__((gnu_inline, always_inline)) int +#else +#define PRINT_FN extern inline __attribute__((gnu_inline, always_inline)) int +#endif +#endif + +typedef struct { + uint64_t n; + bool no_prefix; + bool uppercase; + int digits; +} hex_format_t; +#define hex(x, ...) ((hex_format_t){.n = x, __VA_ARGS__}) + +typedef struct { + double d; +} hex_double_t; +#define hex_double(x, ...) ((hex_double_t){.d = x, __VA_ARGS__}) + +typedef struct { + uint64_t n; + bool no_prefix; + int digits; +} oct_format_t; +#define oct(x, ...) ((oct_format_t){.n = x, __VA_ARGS__}) + +typedef struct { + const char *str; +} quoted_t; +#define quoted(s) ((quoted_t){s}) + +typedef struct { + const char *str; + size_t length; +} string_slice_t; +#define string_slice(...) ((string_slice_t){__VA_ARGS__}) + +typedef struct { + char c; + int length; +} repeated_char_t; +#define repeated_char(ch, len) ((repeated_char_t){.c = ch, .length = len}) + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define FMT64 "ll" +#else +#define FMT64 "l" +#endif + +int _print_uint(FILE *f, uint64_t x); +int _print_double(FILE *f, double x); +int _print_hex(FILE *f, hex_format_t hex); +int _print_hex_double(FILE *f, hex_double_t hex); +int _print_oct(FILE *f, oct_format_t oct); +PRINT_FN _print_float(FILE *f, float x) { + return _print_double(f, (double)x); +} +PRINT_FN _print_pointer(FILE *f, void *p) { + return _print_hex(f, hex((uint64_t)p)); +} +PRINT_FN _print_bool(FILE *f, bool b) { + return fputs(b ? "yes" : "no", f); +} +PRINT_FN _print_str(FILE *f, const char *s) { + return fputs(s ? s : "(null)", f); +} +int _print_char(FILE *f, char c); +int _print_quoted(FILE *f, quoted_t quoted); +PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { + return slice.str ? fwrite(slice.str, 1, slice.length, f) : (size_t)fputs("(null)", f); +} +PRINT_FN _print_repeated_char(FILE *f, repeated_char_t repeated) { + int len = 0; + for (int n = 0; n < repeated.length; n++) + len += fputc(repeated.c, f); + return len; +} + +#ifndef _fprint1 +#define _fprint1(f, x) \ + _Generic((x), \ + char *: _print_str, \ + const char *: _print_str, \ + char: _print_char, \ + bool: _print_bool, \ + int64_t: Int64$print, \ + int32_t: Int64$print, \ + int16_t: Int64$print, \ + int8_t: Int64$print, \ + uint64_t: _print_uint, \ + uint32_t: _print_uint, \ + uint16_t: _print_uint, \ + uint8_t: _print_uint, \ + float: _print_float, \ + double: _print_double, \ + hex_format_t: _print_hex, \ + hex_double_t: _print_hex_double, \ + oct_format_t: _print_oct, \ + quoted_t: _print_quoted, \ + string_slice_t: _print_string_slice, \ + repeated_char_t: _print_repeated_char, \ + Text_t: Text$print, \ + Path_t: Path$print, \ + Int_t: Int$print, \ + void *: _print_pointer)(f, x) +#endif + +typedef struct { + char **buffer; + size_t *size; + size_t position; +} gc_stream_t; + +FILE *gc_memory_stream(char **buf, size_t *size); + +#define _print(x) _n += _fprint1(_printing, x) +#define _fprint(f, ...) \ + ({ \ + FILE *_printing = f; \ + int _n = 0; \ + MAP_LIST(_print, __VA_ARGS__); \ + _n; \ + }) +#define fprint(f, ...) _fprint(f, __VA_ARGS__, "\n") +#define print(...) fprint(stdout, __VA_ARGS__) +#define fprint_inline(f, ...) _fprint(f, __VA_ARGS__) +#define print_inline(...) fprint_inline(stdout, __VA_ARGS__) +#define String(...) \ + ({ \ + char *_buf = NULL; \ + size_t _size = 0; \ + FILE *_stream = gc_memory_stream(&_buf, &_size); \ + assert(_stream); \ + _fprint(_stream, __VA_ARGS__); \ + fflush(_stream); \ + _buf; \ + }) +#define print_err(...) \ + ({ \ + fprint(stderr, "\033[31;1m", __VA_ARGS__, "\033[m"); \ + exit(EXIT_FAILURE); \ + }) diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c index 7bc42755..1eba8188 100644 --- a/src/stdlib/stacktrace.c +++ b/src/stdlib/stacktrace.c @@ -15,7 +15,7 @@ #include <unistd.h> #include "../config.h" -#include "../print.h" +#include "print.h" #include "simpleparse.h" #include "util.h" diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index b156692c..aabeb6b0 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -14,12 +14,12 @@ #include <time.h> #include "../config.h" -#include "../print.h" #include "../util.h" #include "files.h" #include "metamethods.h" #include "optionals.h" #include "paths.h" +#include "print.h" #include "siphash.h" #include "stacktrace.h" #include "stdlib.h" |
