mirror of https://github.com/flysand7/ciabatta.git
degrade fmt but also reorganize it
This commit is contained in:
parent
7de01d66f2
commit
1ab4169567
|
@ -50,7 +50,7 @@
|
|||
#include "conv/strpfx.c"
|
||||
#include "conv/int.c"
|
||||
#include "conv/float.c"
|
||||
#include "fmt/gen_fmt.c"
|
||||
#include "fmt/fmt.c"
|
||||
#include "stdlib/algorithm.c"
|
||||
#include "stdlib/multibyte.c"
|
||||
#include "stdlib/random.c"
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
|
||||
#define FLAG_ZERO 0x0001 // "0"
|
||||
#define FLAG_LEFT 0x0002 // "-"
|
||||
#define FLAG_PLUS 0x0004 // "+"
|
||||
#define FLAG_SPACE 0x0008 // " "
|
||||
#define FLAG_HASH 0x0010 // "#"
|
||||
#define FLAG_PREC 0x0020 // ".precision"
|
||||
#define FLAG_LONG 0x0040 // "l"
|
||||
#define FLAG_LLONG 0x0080 // "ll"
|
||||
#define FLAG_SHORT 0x0100 // "h"
|
||||
#define FLAG_CHAR 0x0200 // "hh"
|
||||
#define FLAG_UPPER 0x0400 // for hex digits 0xa-0xf
|
||||
|
||||
struct fmt_t typedef fmt_t;
|
||||
struct fmt_t {
|
||||
int prec;
|
||||
int width;
|
||||
int flags;
|
||||
};
|
||||
|
||||
#define ctype char
|
||||
#define pfx(f) f
|
||||
#include "fmt_string.h"
|
||||
#include "fmt_stream.h"
|
||||
#include "ints.h"
|
||||
#include "fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
#define ctype wchar_t
|
||||
#define pfx(f) w ## f
|
||||
#include "fmt_string.h"
|
||||
#include "fmt_stream.h"
|
||||
#include "ints.h"
|
||||
#include "fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
// fprintf family
|
||||
|
||||
static inline int fstream_outc(void *ctx, char c) {
|
||||
FILE *file = ctx;
|
||||
int result = fputc(c, file);
|
||||
if(result == EOF) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int fstream_outw(void *ctx, wchar c) {
|
||||
FILE *file = ctx;
|
||||
int result = fputc((char)c, file); // TODO: utf8
|
||||
if(result == EOF) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline fmt_stream_t fstream_new(FILE *file) {
|
||||
fmt_stream_t stream;
|
||||
stream.w = 0;
|
||||
stream.ctx = file;
|
||||
stream.out_ctype = fstream_outc;
|
||||
stream.out_wchar = fstream_outw;
|
||||
stream.out_char = fstream_outc;
|
||||
return stream;
|
||||
}
|
||||
|
||||
int vfprintf(FILE *restrict file, const char *restrict string, va_list va) {
|
||||
fmt_stream_t stream = fstream_new(file);
|
||||
return fmt(&stream, string, va);
|
||||
}
|
||||
|
||||
int vprintf(const char *restrict string, va_list va) {
|
||||
return vfprintf(stdout, string, va);
|
||||
}
|
||||
|
||||
int fprintf(FILE *restrict file, const char *restrict string, ...) {
|
||||
va_list va;
|
||||
va_start(va, string);
|
||||
int result = vfprintf(file, string, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
int printf(const char *restrict string, ...) {
|
||||
va_list va;
|
||||
va_start(va, string);
|
||||
int result = vfprintf(stdout, string, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
// s(n)printf family
|
||||
|
||||
struct str_slice_t typedef str_slice_t;
|
||||
struct str_slice_t {
|
||||
char *str;
|
||||
size_t max_size;
|
||||
size_t written;
|
||||
};
|
||||
|
||||
static inline int sstream_outc(void *ctx, char c) {
|
||||
str_slice_t *slice = ctx;
|
||||
if(slice->written+1 < slice->max_size) {
|
||||
return -1;
|
||||
}
|
||||
slice->str[slice->written++] = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int sstream_outw(void *ctx, wchar c) {
|
||||
str_slice_t *slice = ctx;
|
||||
// TODO: utf8
|
||||
if(slice->written+1 < slice->max_size) {
|
||||
return -1;
|
||||
}
|
||||
slice->str[slice->written++] = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline fmt_stream_t sstream_new(str_slice_t *slice) {
|
||||
fmt_stream_t stream;
|
||||
stream.w = 0;
|
||||
stream.ctx = slice;
|
||||
stream.out_ctype = sstream_outc;
|
||||
stream.out_wchar = sstream_outw;
|
||||
stream.out_char = sstream_outc;
|
||||
return stream;
|
||||
}
|
||||
|
||||
int vsnprintf(char *restrict buf, size_t cbbuf, const char *restrict str, va_list va) {
|
||||
str_slice_t slice;
|
||||
slice.str = buf;
|
||||
slice.max_size = cbbuf;
|
||||
slice.written = 0;
|
||||
fmt_stream_t stream = sstream_new(&slice);
|
||||
return fmt(&stream, str, va);
|
||||
}
|
||||
|
||||
int vsprintf(char *restrict buf, const char *restrict str, va_list va) {
|
||||
str_slice_t slice;
|
||||
slice.str = buf;
|
||||
slice.max_size = SIZE_MAX;
|
||||
slice.written = 0;
|
||||
fmt_stream_t stream = sstream_new(&slice);
|
||||
return fmt(&stream, str, va);
|
||||
}
|
||||
|
||||
int snprintf(char *restrict buf, size_t cbbuf, const char *restrict str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
int result = vsnprintf(buf, cbbuf, str, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sprintf(char *restrict buf, const char *restrict str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
int result = vsprintf(buf, str, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
static inline int pfx(fmt_arg)(fmt_stream_t *stream, ctype const **strp, va_list va) {
|
||||
ctype const *str = *strp;
|
||||
fmt_t fmt = {0};
|
||||
if(!pfx(fmt_read)(str, &fmt, va)) {
|
||||
return 0;
|
||||
}
|
||||
switch(*str) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'b':
|
||||
case 'o':
|
||||
case 'd':
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
int base;
|
||||
int sign;
|
||||
u64 abs = pfx(fmt_int_arg)(stream, &fmt, &str, &sign, &base, va);
|
||||
if(pfx(fmt_int)(stream, fmt, sign, abs, base) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
*strp = str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt)(fmt_stream_t *stream, ctype const *str, va_list va) {
|
||||
stream->w = 0;
|
||||
while(*str) {
|
||||
if(*str == '%') {
|
||||
++str;
|
||||
int ok = pfx(fmt_arg)(stream, &str, va);
|
||||
if(!ok) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
stream_out(stream, *str++);
|
||||
}
|
||||
}
|
||||
return stream->w;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
struct pfx(fmt_stream_t) typedef pfx(fmt_stream_t);
|
||||
struct pfx(fmt_stream_t) {
|
||||
int w;
|
||||
void *ctx;
|
||||
int (*pfx(out_ctype))(void *ctx, ctype ch);
|
||||
int (*pfx(out_wchar))(void *ctx, wchar ch);
|
||||
int (*pfx(out_char)) (void *ctx, char ch);
|
||||
};
|
||||
|
||||
#define stream_out(s, ch) if((s->w++, !s->out_ctype(s->ctx, ch))) return -1
|
||||
#define stream_outc(s, ch) if((s->w++, !s->out_char (s->ctx, ch))) return -1
|
||||
#define stream_outw(s, ch) if((s->w++, !s->out_wchar(s->ctx, ch))) return -1
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
static inline int pfx(fmt_isdigit)(ctype ch) {
|
||||
return '0' <= ch && ch <= '9';
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_atoi)(ctype const **sp, int *err) {
|
||||
int i = 0;
|
||||
ctype const *s = *sp;
|
||||
while(pfx(fmt_isdigit(*s))) {
|
||||
int d = *s-'0';
|
||||
if(i > (INT_MAX - d)/10) {
|
||||
*err = 1;
|
||||
return 0;
|
||||
}
|
||||
i = 10*i + d;
|
||||
++s;
|
||||
}
|
||||
*sp = s;
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_read)(const ctype *str, fmt_t *fmt, va_list va) {
|
||||
int flags = 0;
|
||||
// Parse flag field
|
||||
{
|
||||
int isflag = 0;
|
||||
do {
|
||||
isflag = 1;
|
||||
switch(*str) {
|
||||
case '0': flags |= FLAG_ZERO; break;
|
||||
case '-': flags |= FLAG_LEFT; break;
|
||||
case '+': flags |= FLAG_PLUS; break;
|
||||
case ' ': flags |= FLAG_SPACE; break;
|
||||
case '#': flags |= FLAG_HASH; break;
|
||||
default: isflag = 0;
|
||||
}
|
||||
if(isflag) ++str;
|
||||
} while(isflag);
|
||||
// If '-' and '0' are together we just discard '0'
|
||||
if(flags & FLAG_LEFT) flags &= ~FLAG_ZERO;
|
||||
}
|
||||
// Parse width field
|
||||
int width = 0;
|
||||
if(pfx(fmt_isdigit(*str))) {
|
||||
int err = 0;
|
||||
width = pfx(fmt_atoi(&str, &err));
|
||||
if(err) return 0;
|
||||
}
|
||||
else if(*str == '*') {
|
||||
++str;
|
||||
width = va_arg(va, int);
|
||||
if(width < 0) {
|
||||
width = -width;
|
||||
flags |= FLAG_LEFT;
|
||||
}
|
||||
}
|
||||
// If present, parse the precision field
|
||||
int prec = 0;
|
||||
if(*str == '.') {
|
||||
flags |= FLAG_PREC;
|
||||
++str;
|
||||
if(pfx(fmt_isdigit(*str))) {
|
||||
int err = 0;
|
||||
prec = pfx(fmt_atoi(&str, &err));
|
||||
if(err) return -1;
|
||||
}
|
||||
else if(*str == '*') {
|
||||
++str;
|
||||
prec = va_arg(va, int);
|
||||
prec = (prec > 0? prec : 0);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
|
||||
static inline u64 pfx(fmt_int_arg)(
|
||||
fmt_stream_t *stream,
|
||||
fmt_t *fmt,
|
||||
ctype const **strp,
|
||||
int *signp,
|
||||
int *basep,
|
||||
va_list va
|
||||
) {
|
||||
ctype const *str = *strp;
|
||||
// Get integer properties
|
||||
int base;
|
||||
int is_signed;
|
||||
switch(*str) {
|
||||
case 'o': base = 8; break;
|
||||
case 'b': base = 2; break;
|
||||
case 'x':
|
||||
case 'X': base = 16; break;
|
||||
default: base = 10;
|
||||
}
|
||||
switch(*str) {
|
||||
case 'd': is_signed = 1; break;
|
||||
case 'i': is_signed = 1; break;
|
||||
default: is_signed = 0;
|
||||
}
|
||||
if(*str == 'X') {
|
||||
fmt->flags |= FLAG_UPPER;
|
||||
}
|
||||
// Discard some flags
|
||||
if(base == 10) {
|
||||
fmt->flags &= ~FLAG_HASH;
|
||||
}
|
||||
if(!is_signed) {
|
||||
fmt->flags &= ~(FLAG_PLUS|FLAG_SPACE);
|
||||
}
|
||||
if(fmt->flags & FLAG_PREC) {
|
||||
fmt->flags &= ~FLAG_ZERO;
|
||||
}
|
||||
// Parse sign and abs value of an integer
|
||||
int sign = 0;
|
||||
u64 abs_value = 0;
|
||||
if(is_signed) {
|
||||
i64 num;
|
||||
if(fmt->flags & FLAG_LLONG) {
|
||||
num = (i64)va_arg(va, intll);
|
||||
}
|
||||
else if(fmt->flags & FLAG_LONG) {
|
||||
num = (i64)va_arg(va, intl);
|
||||
}
|
||||
else {
|
||||
num = (i64)va_arg(va, int);
|
||||
}
|
||||
// Handle overflow on '-'
|
||||
if(num == LLONG_MIN) {
|
||||
sign = 1;
|
||||
abs_value = (u64)LLONG_MAX + 1;
|
||||
}
|
||||
else {
|
||||
sign = num >= 0? 0 : 1;
|
||||
num = num >= 0? num : -num;
|
||||
abs_value = (u64)num;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(fmt->flags & FLAG_LLONG) {
|
||||
abs_value = (u64)va_arg(va, intull);
|
||||
}
|
||||
else if(fmt->flags & FLAG_LONG) {
|
||||
abs_value = (u64)va_arg(va, intul);
|
||||
}
|
||||
else {
|
||||
abs_value = (u64)va_arg(va, intu);
|
||||
}
|
||||
}
|
||||
++str;
|
||||
*strp = str;
|
||||
*basep = base;
|
||||
*signp = sign;
|
||||
return abs_value;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_int)(
|
||||
fmt_stream_t *stream,
|
||||
fmt_t fmt,
|
||||
int neg,
|
||||
u64 value,
|
||||
int base) {
|
||||
// We'll Write digits into buf in reverse and then call _out_rbuf
|
||||
int ndigits = 0;
|
||||
ctype digits[64] = {0};
|
||||
// Remove hash flag for 0 values
|
||||
if(value == 0) fmt.flags &= ~FLAG_HASH;
|
||||
// Write digits to buffer in reverse (starting from least significant)
|
||||
do {
|
||||
int d = (int)(value%base);
|
||||
value /= base;
|
||||
if(d < 10)
|
||||
d += '0';
|
||||
else if(fmt.flags & FLAG_UPPER)
|
||||
d += 'A' - 10;
|
||||
else d += 'a' - 10;
|
||||
digits[ndigits++] = d;
|
||||
} while(value);
|
||||
// Figure out the length of the prefix (part of number before digit stirng)
|
||||
int pref_len = 0;
|
||||
if(fmt.flags & FLAG_HASH) pref_len = base == 8? 1 : 2;
|
||||
else if(neg) pref_len = 1;
|
||||
else if(fmt.flags & FLAG_PLUS) pref_len = 1;
|
||||
else if(fmt.flags & FLAG_SPACE) pref_len = 1;
|
||||
if(ndigits > fmt.prec) fmt.prec = ndigits;
|
||||
int num_len = pref_len + fmt.prec;
|
||||
int pad_len = fmt.width - num_len;
|
||||
if(!(fmt.flags & FLAG_LEFT)) {
|
||||
ctype pad_ch = ' ';
|
||||
if(fmt.flags & FLAG_ZERO) pad_ch = '0';
|
||||
while(pad_len-- > 0) {
|
||||
stream_out(stream, pad_ch);
|
||||
}
|
||||
}
|
||||
// Print width left-pad if it's made out of space
|
||||
if(!(fmt.flags & FLAG_LEFT) && !(fmt.flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
stream_out(stream, ' ');
|
||||
}
|
||||
// Print prefix
|
||||
if(fmt.flags & FLAG_HASH) {
|
||||
stream_out(stream, '0');
|
||||
if(base == 16) { stream_out(stream, 'x'); }
|
||||
else if(base == 2) { stream_out(stream, 'b'); }
|
||||
}
|
||||
else if(neg) { stream_out(stream, '-'); }
|
||||
else if(fmt.flags & FLAG_PLUS) { stream_out(stream, '+'); }
|
||||
else if(fmt.flags & FLAG_SPACE) { stream_out(stream, ' '); }
|
||||
// Print width left-pad if it's made out of zero
|
||||
if(!(fmt.flags & FLAG_LEFT) && (fmt.flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
stream_out(stream, '0');
|
||||
}
|
||||
// Print zero-pad due to precision
|
||||
for(int i = ndigits; i < fmt.prec; ++i) {
|
||||
stream_out(stream, '0');
|
||||
}
|
||||
// Output buffer in reverse
|
||||
if(ndigits) while(ndigits--) {
|
||||
stream_out(stream, digits[ndigits]);
|
||||
}
|
||||
// Print right-pad
|
||||
if(fmt.flags & FLAG_LEFT) while(pad_len-- > 0) {
|
||||
stream_out(stream, ' ');
|
||||
}
|
||||
return stream->w;
|
||||
}
|
2
test.cmd
2
test.cmd
|
@ -1,2 +0,0 @@
|
|||
|
||||
clang -std=c11 test\%1 utf8.obj -Iinc -g -lciabatta.lib -nostdlib -fstack-protector
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main() {
|
||||
printf("%i\n", INT_MAX);
|
||||
printf("%i\n", INT_MIN);
|
||||
printf("%x\n", UINT_MAX);
|
||||
printf("%x\n", 0);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue