prinf formats

This commit is contained in:
bumbread 2022-06-16 16:15:45 +11:00
parent f533aa779c
commit f80957583b
10 changed files with 873 additions and 360 deletions

View File

@ -51,5 +51,5 @@ del build\*.obj
:skip_crt_compilation :skip_crt_compilation
echo Compiling test.. echo Compiling test..
clang -fno-builtin test\test_math.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS% clang -fno-builtin test\test_printf.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS%
::cl test\test_math.c /Iinc -D_CRT_SECURE_NO_WARNINGS /Z7 /link ciabatta.lib kernel32.lib user32.lib shell32.lib -nostdlib -nodefaultlibs ::cl test\test_math.c /Iinc -D_CRT_SECURE_NO_WARNINGS /Z7 /link ciabatta.lib kernel32.lib user32.lib shell32.lib -nostdlib -nodefaultlibs

8
code/math/exp/exp.c Normal file
View File

@ -0,0 +1,8 @@
#include <math.h>
#include <stdint.h>
#include <float.h>
#include <fenv.h>
#define rln2 1.4426950408889634073599246810018921374266459541529859341354494069

View File

@ -86,6 +86,11 @@ void _os_file_write(void* ctx, size_t n, const char str[]) {
WriteFile((HANDLE) ctx, str, n, &written, NULL); WriteFile((HANDLE) ctx, str, n, &written, NULL);
} }
void _os_file_write_char(void* ctx, char ch) {
DWORD written = 0;
WriteFile((HANDLE) ctx, &ch, 1, &written, NULL);
}
int system(const char* string) { int system(const char* string) {
int wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0); int wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0);
wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t))); wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t)));

View File

@ -1,220 +0,0 @@
#include <math.h>
//TODO: verify printf("%d", 0). From the code it looked like it would print
// an empty string.
// NOTE: this file doesn't exist in a vacuum, it's a template for generating
// the formatted print, you should define FMT_CHAR_TYPE before including it
inline static int FMT_FUNC_NAME (void *ctx, OutputFunc out, const FMT_CHAR_TYPE *fmt, va_list args) {
size_t full_length = 0;
while (*fmt) {
// skip any normal non percent text
const FMT_CHAR_TYPE *start = fmt;
while (*fmt && *fmt != '%') fmt++;
// print all those non percent text
out(ctx, fmt - start, start);
full_length += (fmt - start);
// null terminated
if (*fmt == '\0') break;
fmt += 1;
if (*fmt == '%') {
out(ctx, 1, "%");
fmt++;
continue;
}
unsigned int precision = 0;
if (*fmt == '.') {
// custom precision
fmt++;
if (isdigit(*fmt)) {
// just a small atoi
// TODO: handle overflow, just in case(?)
while (isdigit(*fmt)) {
precision *= 10u;
precision += (*fmt - '0');
fmt++;
}
} else if (*fmt == '*') {
int p = va_arg(args, int);
precision = p >= 0 ? ((unsigned int)p) : 0;
fmt++;
}
}
// integer length specifiers
enum {
CHAR,
SHORT,
INT,
LONG,
LLONG
} int_length = INT;
if (*fmt == 'l') {
fmt++;
if (*fmt == 'l') {
int_length = LLONG;
fmt++;
} else {
int_length = LONG;
}
} else if (*fmt == 'h') {
fmt++;
if (*fmt == 'h') {
int_length = CHAR;
fmt++;
} else {
int_length = SHORT;
}
} else if (*fmt == 'z') {
fmt++;
int_length = _Generic((size_t)0,
unsigned char: CHAR,
unsigned short: SHORT,
unsigned int: INT,
unsigned long: LONG,
unsigned long long: LLONG);
}
FMT_CHAR_TYPE ch = *fmt++;
const char* characters = "0123456789abcdef";
if (ch == 'X') characters = "0123456789ABCDEF";
switch (ch) {
case 'c': {
const char chr = va_arg(args, int);
out(ctx, 1, &chr);
full_length ++;
break;
}
case 'f':
case 'L': {
double d = va_arg(args, double);
if(signbit(d)) { // TODO: negative zero
out(ctx, 1, "-");
d = -d;
}
if(isinf(d)) {
out(ctx, sizeof"inf"-1, "inf");
break;
}
else if(isnan(d)) {
out(ctx, sizeof"nan"-1, "nan");
break;
}
uint64_t w = (uint64_t)d;
d -= w;
FMT_CHAR_TYPE buffer[20];
size_t len = sizeof(buffer);
do {
buffer[--len] = characters[w % 10];
w /= 10;
} while(w != 0);
out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len);
char dot = '.';
out(ctx, 1, &dot);
for(int i = 0; i != 6; ++i) {
d *= 10;
int dv = (int)d;
d -= dv;
char digit = characters[dv];
out(ctx, 1, &digit);
}
} break;
case 's': {
const FMT_CHAR_TYPE *str = va_arg(args, FMT_CHAR_TYPE*);
size_t len = FMT_STRLEN_S(str, precision ? precision : SIZE_MAX);
out(ctx, len, str);
full_length += len;
break;
}
case 'b':
case 'o':
case 'i':
case 'u':
case 'd':
case 'x':
case 'X': {
int base = 10;
switch (ch) {
case 'X': case 'x': base = 16; break;
case 'o': base = 8; break;
case 'b': base = 2; break;
default: base = 10; break;
}
uintmax_t i;
if (ch == 'd' || ch == 'i') {
intmax_t num = 0;
switch (int_length) {
case CHAR: num = (char) va_arg(args, int); break;
case SHORT: num = (short) va_arg(args, int); break;
case INT: num = va_arg(args, int); break;
case LONG: num = va_arg(args, long); break;
case LLONG: num = va_arg(args, long long); break;
default: break;
}
if (num < 0) {
out(ctx, 1, "-");
i = -num;
full_length += 1;
} else {
i = num;
}
} else {
switch (int_length) {
case CHAR: i = (char) va_arg(args, int); break;
case SHORT: i = (short) va_arg(args, int); break;
case INT: i = va_arg(args, int); break;
case LONG: i = va_arg(args, long); break;
case LLONG: i = va_arg(args, long long); break;
default: i = 0; break;
}
}
if (i == 0) {
out(ctx, 1, "0");
full_length += 1;
} else {
FMT_CHAR_TYPE buffer[20];
size_t len = sizeof(buffer);
// we build the number in reverse
while (i != 0) {
buffer[--len] = characters[i % base];
i /= base;
}
out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len);
full_length += (sizeof(buffer) - len);
}
break;
}
default: break;
}
}
return full_length;
}

View File

@ -1,139 +0,0 @@
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdbool.h>
#include <math.h>
#include <_os.h>
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
int remove(const char *filename)
{
return _os_del_file(filename);
}
int rename(const char *old, const char *new)
{
return _os_mov_file(old, new);
}
char *tmpnam(char *s)
{
static char static_buffer[L_tmpnam];
char *buffer = s;
if(s == NULL) buffer = static_buffer;
return _os_tmpname(buffer);
}
FILE *fopen(const char *restrict filename, const char *restrict mode)
{
// Basically defined UB here by introducing missing modes
// It is simpler to implement that way I think.
int base_mode = mode[0];
int binary = 0;
int exclusive = 0;
int update = 0;
for(; *mode != 0; ++mode) {
if(*mode == 'x') exclusive = 1;
if(*mode == 'b') binary = 1;
if(*mode == '+') update = 1;
}
if(base_mode == 'r' && exclusive) return NULL;
if(base_mode == 'a' && exclusive) return NULL;
_OS_ModeFlags mode_flags = {
.base_mode = base_mode,
.binary = binary,
.update = update,
.exclusive = exclusive,
};
return _os_fopen(filename, mode_flags);
}
int fclose(FILE *stream)
{
return _os_fclose(stream);
}
// TODO:kekw:
FILE *freopen(
const char *restrict filename,
const char *restrict mode,
FILE *restrict stream)
{
fclose(stream);
return NULL;
}
typedef void(*OutputFunc)(void* ctx, size_t n, const char str[]);
///////////////////////////////////////////////
// Instantiate formatted print functions
///////////////////////////////////////////////
// TODO: instantiate wide char variants of print
#define FMT_FUNC_NAME fmt_print_char
#define FMT_CHAR_TYPE char
#define FMT_STRLEN_S(s, maxsize) strnlen_s(s, maxsize)
#include "printf.h"
typedef struct {
size_t used, capacity;
char* string;
} StrPrintCtx;
FILE *stdout, *stderr, *stdin;
#define CALL_PRINTF(fmt_func, ctx, out, fmt) \
va_list args; \
va_start(args, fmt); \
int result = fmt_func(ctx, out, fmt, args); \
va_end(args)
static void string_write(void *ctx, size_t n, const char *restrict str) {
StrPrintCtx *c = ctx;
memcpy(c->string + c->used, str, n);
c->used += n;
}
int fprintf(FILE *file, const char *restrict fmt, ...) {
CALL_PRINTF(fmt_print_char, file, _os_file_write, fmt);
return result;
}
int printf(const char *restrict fmt, ...) {
CALL_PRINTF(fmt_print_char, stdout, _os_file_write, fmt);
return result;
}
int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) {
StrPrintCtx ctx = { 0, n, s };
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
return result;
}
int sprintf(char *restrict s, const char *restrict fmt, ...) {
StrPrintCtx ctx = { 0, SIZE_MAX, s };
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
return result;
}
int vfprintf(FILE *file, const char *restrict fmt, va_list args) {
return fmt_print_char(file, _os_file_write, fmt, args);
}
int vprintf(const char *restrict fmt, va_list args) {
return fmt_print_char(stdout, _os_file_write, fmt, args);
}
int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) {
StrPrintCtx ctx = { 0, n, s };
return fmt_print_char(&ctx, string_write, fmt, args);
}
int vsprintf(char *restrict s, const char *restrict fmt, va_list args) {
StrPrintCtx ctx = { 0, SIZE_MAX, s };
return fmt_print_char(&ctx, string_write, fmt, args);
}

65
code/stdio/files.c Normal file
View File

@ -0,0 +1,65 @@
#include <stdio.h>
#include <_os.h>
FILE *stdout;
FILE *stderr;
FILE *stdin;
int remove(const char *filename)
{
return _os_del_file(filename);
}
int rename(const char *old, const char *new)
{
return _os_mov_file(old, new);
}
char *tmpnam(char *s) {
static char static_buffer[L_tmpnam];
char *buffer = s;
if(s == NULL) buffer = static_buffer;
return _os_tmpname(buffer);
}
FILE *fopen(const char *restrict filename, const char *restrict mode)
{
// Basically defined UB here by introducing missing modes
// It is simpler to implement that way I think.
int base_mode = mode[0];
int binary = 0;
int exclusive = 0;
int update = 0;
for(; *mode != 0; ++mode) {
if(*mode == 'x') exclusive = 1;
if(*mode == 'b') binary = 1;
if(*mode == '+') update = 1;
}
if(base_mode == 'r' && exclusive) return NULL;
if(base_mode == 'a' && exclusive) return NULL;
_OS_ModeFlags mode_flags = {
.base_mode = base_mode,
.binary = binary,
.update = update,
.exclusive = exclusive,
};
return _os_fopen(filename, mode_flags);
}
int fclose(FILE *stream)
{
return _os_fclose(stream);
}
// TODO:kekw:
FILE *freopen(
const char *restrict filename,
const char *restrict mode,
FILE *restrict stream)
{
fclose(stream);
return NULL;
}

718
code/stdio/fmt_print.h Normal file
View File

@ -0,0 +1,718 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>
// This stuff is kinda related to what's going on in this file, so I left it
// in a rather ugly manner here.
#if !defined(once)
#define once
#define countof(arr) (sizeof(arr)/sizeof(arr[0]))
#define CALL_PRINTF(fmt_func, ctx, out, fmt) \
va_list args; \
va_start(args, fmt); \
int result = fmt_func(ctx, out, fmt, args); \
va_end(args)
enum ArgLength {
LEN_HH,
LEN_H,
LEN_I,
LEN_L,
LEN_LL,
LEN_J,
LEN_Z,
LEN_T,
} typedef ArgLength;
enum ArgConv {
CONV_INT,
CONV_FLT,
CONV_EXP,
CONV_SCI,
CONV_CHAR,
CONV_STR,
CONV_PTR,
CONV_N,
CONV_PERCENT,
} typedef ArgConv;
struct Format typedef Format;
struct Format {
bool flag_left;
bool flag_sign;
bool flag_space;
bool flag_form;
bool flag_zero;
bool flag_upper;
bool flag_unsigned;
int base;
int width;
int prec;
ArgLength arg_len;
ArgConv arg_conv;
};
inline static uint64_t get_int_arg(Format *fmt, va_list args, bool *signp) {
bool sign = 0;
uint64_t num = 0;
// Read the integer from the args and split it into tuple of (sign, digits)
// If we've got HH or H we need to parse as int, and do the necessary conv
// to unsigned if needed.
if(fmt->arg_len < LEN_I) {
int ch = va_arg(args, int);
if(ch < 0) {
if(fmt->flag_unsigned) {
num = ch + (fmt->arg_len == LEN_HH? UCHAR_MAX : USHRT_MAX);
}
else {
num = -ch;
sign = 1;
}
}
}
// Otherwise we see whether the number is signed or unsigned. For unsigned
// numbers we just parse according to the length, for signed we do that
// plus handle the sign
else if(fmt->flag_unsigned) {
switch(fmt->arg_len) {
case LEN_I: num = va_arg(args, unsigned int); break;
case LEN_L: num = va_arg(args, unsigned long); break;
case LEN_LL: num = va_arg(args, unsigned long long); break;
case LEN_J: num = va_arg(args, uintmax_t); break;
case LEN_Z: num = va_arg(args, size_t); break;
case LEN_T: num = va_arg(args, size_t); break;
default:;
}
}
else {
// TODO: signed size_t
int64_t i = 0;
switch(fmt->arg_len) {
case LEN_I: i = va_arg(args, int); break;
case LEN_L: i = va_arg(args, long); break;
case LEN_LL: i = va_arg(args, long long); break;
case LEN_J: i = va_arg(args, uintmax_t); break;
case LEN_Z: i = va_arg(args, int64_t); break;
case LEN_T: i = va_arg(args, ptrdiff_t); break;
default:;
}
if(i < 0) {
num = -i;
sign = 1;
}
else {
num = i;
}
}
*signp = sign;
return num;
}
#endif
// Generic printf function that is used to print to stdout, strings and files.
// The basic idea is to make generic printf that uses output function as
// parameter and that function is distinct for printing to files, strings etc.
// Furthermore, this generic printf is abbstracted over character type, and
// depending on the macro fchar it could print in char's or wchar_t's.
typedef void (suffix(out_func_t))(void* ctx, fchar ch);
inline static int suffix(fmt_atoi)(fchar const *str, int *value) {
int i = 0;
int val = 0;
while('0' <= str[i] && str[i] <= '9') {
int digit = str[i] - '0';
if(val > (INT_MAX - digit) / 10) {
return -1;
}
val = 10*val + digit;
++i;
}
*value = val;
return i;
}
inline static int suffix(fmt_parse)(
Format *format,
fchar const *str,
va_list args
) {
*format = (Format){0};
// Parse flags
int i = 0;
for(;;) {
if (str[i] == '-') format->flag_left = true;
else if(str[i] == '+') format->flag_sign = true;
else if(str[i] == ' ') format->flag_space = true;
else if(str[i] == '#') format->flag_form = true;
else if(str[i] == '0') format->flag_zero = true;
else break;
++i;
}
// Optional width specifier
int width = 0;
if(str[i] == '*') {
++i;
width = va_arg(args, int);
if(width < 0) {
width = -width;
format->flag_zero = true;
}
}
else {
int width_len = suffix(fmt_atoi)(str+i, &width);
if(width_len < 0) return -1;
i += width_len;
}
// Optional precision specifier
int prec = 0;
if(str[i] == '.') {
++i;
if(str[i] == '*') {
++i;
prec = va_arg(args, int);
if(prec < 0) {
prec = 0;
}
}
else {
int prec_len = suffix(fmt_atoi)(str+i, &prec);
if(prec_len < 0) return -1;
i += prec_len;
}
}
format->width = width;
format->prec = prec;
// Optional length modifier
format->arg_len = LEN_I;
if(str[i] == 'h') {
++i;
format->arg_len = LEN_H;
if(str[i] == 'h') {
++i;
format->arg_len = LEN_HH;
}
}
else if(str[i] == 'l') {
++i;
format->arg_len = LEN_L;
if(str[i] == 'l') {
++i;
format->arg_len = LEN_LL;
}
}
else if(str[i] == 'j') {
++i;
format->arg_len = LEN_J;
}
else if(str[i] == 'z') {
++i;
format->arg_len = LEN_Z;
}
else if(str[i] == 't') {
++i;
format->arg_len = LEN_T;
}
else if(str[i] == 'L') {
++i;
format->arg_len = LEN_L;
}
// Conversion specifier
switch(str[i]) {
case 'd':
case 'i':
format->arg_conv = CONV_INT;
format->base = 10;
break;
case 'u':
format->arg_conv = CONV_INT;
format->flag_unsigned = true;
format->base = 10;
break;
case 'o':
format->arg_conv = CONV_INT;
format->flag_unsigned = true;
format->base = 8;
break;
case 'x':
format->arg_conv = CONV_INT;
format->flag_unsigned = true;
format->base = 16;
break;
case 'X':
format->arg_conv = CONV_INT;
format->flag_unsigned = true;
format->flag_upper = true;
format->base = 16;
break;
case 'f':
format->arg_conv = CONV_FLT;
format->base = 10;
break;
case 'F':
format->arg_conv = CONV_FLT;
format->flag_upper = true;
format->base = 10;
break;
case 'e':
format->arg_conv = CONV_EXP;
format->base = 10;
break;
case 'E':
format->arg_conv = CONV_EXP;
format->flag_upper = true;
format->base = 10;
break;
case 'g':
format->arg_conv = CONV_SCI;
format->base = 10;
break;
case 'G':
format->arg_conv = CONV_SCI;
format->flag_upper = true;
format->base = 10;
break;
case 'a':
format->arg_conv = CONV_FLT;
format->base = 16;
break;
case 'A':
format->arg_conv = CONV_FLT;
format->flag_upper = true;
format->base = 16;
break;
case 'c':
format->arg_conv = CONV_CHAR;
break;
case 's':
format->arg_conv = CONV_STR;
break;
case 'p':
format->arg_conv = CONV_PTR;
break;
case 'n':
format->arg_conv = CONV_N;
break;
case '%':
format->arg_conv = CONV_PERCENT;
break;
default: return -1;
}
++i;
// Ignoring flags etc
if(format->flag_sign && format->flag_zero) {
format->flag_zero = 0;
}
if(format->flag_sign && format->flag_space) {
format->flag_space = 0;
}
if(format->prec != 0) {
format->flag_zero = 0;
}
else if(format->flag_zero) {
// HACK: NOTE: I hate printf formats
format->prec = format->width;
format->width = 0;
}
if(format->arg_conv == CONV_FLT && format->prec == 0) {
format->prec = 6;
}
return i;
}
inline static int suffix(fmt_fprint_int)(
void *ctx,
suffix(out_func_t) *out,
Format *fmt,
va_list args
) {
int w = 0;
bool sign;
uint64_t num = get_int_arg(fmt, args, &sign);
// Find the actual length the number takes up
int prefix_len = 0;
int digits_num = 0;
if(fmt->flag_space || fmt->flag_sign || sign) {
prefix_len = 1;
}
if(num != 0 && fmt->flag_form) {
if(fmt->base == 16) prefix_len += 2; // 0x
if(fmt->base == 8) digits_num += 1; // 0
}
{
uint64_t digits = num;
do {
digits_num += 1;
digits /= fmt->base;
} while(digits != 0);
}
int s_len = prefix_len + digits_num; // length of significant chars
int d_len = s_len; // length of all displayed chars
int zpad = 0; // pre-pad of zeroes from precision
if(digits_num < fmt->prec) {
zpad = fmt->prec - digits_num;
d_len = s_len + zpad;
}
// See if padding is necessary and extract info about padding
bool left_pad = false;
bool right_pad = true;
char pad_ch = ' ';
int pad_len = 0;
if(!sign && fmt->flag_space) { // make space part of pad
d_len += 1;
pad_len = 1;
}
if(d_len < fmt->width) {
pad_len = fmt->width - d_len;
}
if(fmt->flag_left) {
left_pad = true;
right_pad = false;
}
if(fmt->flag_zero) {
pad_ch = '0';
}
// Fill up an array of significant digits
char digits_arr[33] = {0};
char *digits = digits_arr + countof(digits_arr);
{
uint64_t temp = num;
do {
int digit = temp % fmt->base;
char ch = 0;
if(digit < 10) ch = digit + '0';
else if(fmt->flag_upper) ch = digit - 10 + 'A';
else ch = digit - 10 + 'a';
*--digits = ch;
temp /= fmt->base;
} while(temp != 0);
}
// Start displaying number
// Left-pad if necessary
if(left_pad) while(pad_len--) {
out(ctx, pad_ch);
++w;
}
// Print sign
if(sign) { out(ctx, '-'); ++w; }
else if(fmt->flag_sign) { out(ctx, '+'); ++w; }
else if(fmt->flag_space) { out(ctx, ' '); ++w; }
// Print 0x, 0X
if(num != 0 && fmt->base == 16 && fmt->flag_form) {
out(ctx, '0');
if(fmt->flag_upper) out(ctx, 'X');
else out(ctx, 'x');
w += 2;
}
// Print zpad
while(zpad--) {
out(ctx, '0');
++ w;
}
// Print digit string
while(*digits != 0) {
out(ctx, *digits);
++digits;
++w;
}
// Right-pad if necessary
if(right_pad) while(pad_len--) {
out(ctx, pad_ch);
++w;
}
return w;
}
inline static int suffix(fmt_fprint_ptr)(
void *ctx,
suffix(out_func_t) *out,
Format *fmt,
va_list args
) {
int w = 0;
uintptr_t num = va_arg(args, uintptr_t);
int s_len = 2 + 16; // length of significant chars
int d_len = s_len; // length of all displayed chars
int zpad = 0; // pre-pad of zeroes from precision
if(16 < fmt->prec) {
zpad = fmt->prec - 16;
d_len = s_len + zpad;
}
// See if padding is necessary and extract info about padding
bool left_pad = true;
bool right_pad = false;
char pad_ch = ' ';
int pad_len = 0;
if(d_len < fmt->width) {
pad_len = fmt->width - d_len;
}
if(fmt->flag_left) {
left_pad = false;
right_pad = true;
}
if(fmt->flag_zero) {
pad_ch = '0';
}
// Fill up an array of significant digits
char digits_arr[33] = {0};
char *digits = digits_arr + countof(digits_arr);
{
uint64_t temp = num;
do {
int digit = temp % 16;
char ch = 0;
if(digit < 10) ch = digit + '0';
else if(fmt->flag_upper) ch = digit - 10 + 'A';
else ch = digit - 10 + 'a';
*--digits = ch;
temp /= 16;
} while(s_len-- != 0);
}
// Start displaying number
// Left-pad if necessary
if(left_pad) while(pad_len--) {
out(ctx, pad_ch);
++w;
}
out(ctx, '0');
out(ctx, 'x');
w += 2;
// Print zpad
while(zpad--) {
out(ctx, '0');
++ w;
}
// Print digit string
while(*digits != 0) {
out(ctx, *digits);
++digits;
++w;
}
// Right-pad if necessary
if(right_pad) while(pad_len--) {
out(ctx, pad_ch);
++w;
}
return w;
}
inline static int suffix(fmt_fprint_char)(
void *ctx,
suffix(out_func_t) *out,
Format *fmt,
va_list args
) {
int w = 0;
bool sign;
uint64_t num = get_int_arg(fmt, args, &sign);
// Calculate padding
bool left_pad = true;
bool right_pad = false;
uint64_t pad = 0;
if(1 < fmt->width) {
pad = fmt->width - 1;
}
if(fmt->flag_left) {
left_pad = false;
right_pad = true;
}
// Print left pad
if(left_pad) while(pad--) {
out(ctx, ' ');
++w;
}
out(ctx, num);
++w;
// Print right pad
if(right_pad) while(pad--) {
out(ctx, ' ');
++w;
}
return w;
}
inline static int suffix(fmt_just_fucking_print_float_someone_improve_this_later)(
void *ctx,
suffix(out_func_t) *out,
Format *fmt,
va_list args
) {
int w = 0;
double f = va_arg(args, double);
uint64_t i = (uint64_t)f;
char digits_arr[33] = {0};
char *digits = digits_arr + countof(digits_arr);
{
uint64_t t = i;
do {
char ch = (t % 10) + '0';
*--digits = ch;
t /= 10;
} while(t-- != 0);
}
while(*digits != 0) {
out(ctx, *digits);
++digits;
++w;
}
f -= i;
uint64_t nprec = fmt->prec;
if(nprec != 0) {
out(ctx, '.');
w++;
}
for(int i = 0; i != fmt->prec; ++i) {
f *= 10;
uint64_t digit = (uint64_t)f;
out(ctx, digit + '0');
++w;
f -= digit;
}
return w;
}
inline static int suffix(fmt_fprint_str)(
void *ctx,
suffix(out_func_t) *out,
Format *fmt,
va_list args
) {
int w = 0;
char *str = va_arg(args, char *);
uint64_t len = strlen(str);
// Calculate padding
bool left_pad = true;
bool right_pad = false;
uint64_t pad = 0;
if(len < fmt->width) {
pad = fmt->width - len;
}
if(fmt->flag_left) {
left_pad = false;
right_pad = true;
}
// Print left pad
if(left_pad) while(pad--) {
out(ctx, ' ');
++w;
}
while(*str) {
out(ctx, *str++);
++w;
}
// Print right pad
if(right_pad) while(pad--) {
out(ctx, ' ');
++w;
}
return w;
}
inline static int suffix(fmt_print)(
void *ctx,
suffix(out_func_t) *out,
const fchar *fmt,
va_list args
) {
int w = 0; // N chars output
int i = 0; // N chars read from fmt
while(fmt[i] != 0) {
if(fmt[i] == '%') {
++i;
Format format;
int fmt_len = suffix(fmt_parse)(&format, fmt+i, args);
if(fmt_len < 0) return -1;
i += fmt_len;
int written;
switch(format.arg_conv) {
case CONV_INT: {
written = suffix(fmt_fprint_int)(ctx, out, &format, args);
} break;
case CONV_FLT: {
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
} break;
case CONV_EXP: {
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
} break;
case CONV_SCI: {
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
} break;
case CONV_CHAR: {
written = suffix(fmt_fprint_char)(ctx, out, &format, args);
} break;
case CONV_STR: {
written = suffix(fmt_fprint_str)(ctx, out, &format, args);
} break;
case CONV_PTR: {
written = suffix(fmt_fprint_ptr)(ctx, out, &format, args);
} break;
case CONV_N: {
int *n = va_arg(args, int *);
*n = w;
written = 0;
} break;
case CONV_PERCENT: {
out(ctx, '%');
written = 1;
} break;
if(written < 0) {
return -1;
}
w += written;
}
}
// Print non-formatted characters as god intended
else while(fmt[i] != '%' && fmt[i] != 0) {
out(ctx, fmt[i]);
++i;
++w;
}
}
return i;
}

67
code/stdio/stdio.c Normal file
View File

@ -0,0 +1,67 @@
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdbool.h>
#include <math.h>
#include <_os.h>
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
// Instantiate generic printf function for byte strings
// TODO: instantiate wide char variants of print
#define suffix(name) name ## _char
#define fchar char
#define fstrlen(s, maxsize) strnlen_s(s, maxsize)
#include "fmt_print.h"
typedef struct {
size_t used, capacity;
char* string;
} StrPrintCtx;
static void string_write(void *ctx, char ch) {
StrPrintCtx *c = ctx;
c->string[c->used++] = ch;
}
int fprintf(FILE *file, const char *restrict fmt, ...) {
CALL_PRINTF(fmt_print_char, file, _os_file_write_char, fmt);
return result;
}
int printf(const char *restrict fmt, ...) {
CALL_PRINTF(fmt_print_char, stdout, _os_file_write_char, fmt);
return result;
}
int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) {
StrPrintCtx ctx = { 0, n, s };
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
return result;
}
int sprintf(char *restrict s, const char *restrict fmt, ...) {
StrPrintCtx ctx = { 0, SIZE_MAX, s };
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
return result;
}
int vfprintf(FILE *file, const char *restrict fmt, va_list args) {
return fmt_print_char(file, _os_file_write_char, fmt, args);
}
int vprintf(const char *restrict fmt, va_list args) {
return fmt_print_char(stdout, _os_file_write_char, fmt, args);
}
int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) {
StrPrintCtx ctx = { 0, n, s };
return fmt_print_char(&ctx, string_write, fmt, args);
}
int vsprintf(char *restrict s, const char *restrict fmt, va_list args) {
StrPrintCtx ctx = { 0, SIZE_MAX, s };
return fmt_print_char(&ctx, string_write, fmt, args);
}

View File

@ -36,6 +36,7 @@ char *_os_tmpname(char *buffer);
FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags); FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags);
int _os_fclose(FILE *file); int _os_fclose(FILE *file);
void _os_file_write(void* ctx, size_t n, const char str[]); void _os_file_write(void* ctx, size_t n, const char str[]);
void _os_file_write_char(void* ctx, char ch);
void _os_init_eh(); void _os_init_eh();

8
test/test_printf.c Normal file
View File

@ -0,0 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("%f\n", 3.1415);
return 0;
}