From 45f9449ae0b904917110d3f6937d0266daa84769 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Thu, 11 Nov 2021 08:10:09 -0800 Subject: feat: added libfmt --- include/libfmt.h | 62 +++++++++++++++++ sys/libfmt/do.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++ sys/libfmt/internal.h | 15 ++++ sys/libfmt/print.c | 0 4 files changed, 263 insertions(+) create mode 100644 include/libfmt.h create mode 100644 sys/libfmt/do.c create mode 100644 sys/libfmt/internal.h create mode 100644 sys/libfmt/print.c diff --git a/include/libfmt.h b/include/libfmt.h new file mode 100644 index 0000000..17effc6 --- /dev/null +++ b/include/libfmt.h @@ -0,0 +1,62 @@ +#pragma once + +typedef struct fmt·State fmt·State; + +struct fmt·State +{ + struct{ + char *beg; + char *cur; + char *end; + } buffer; + int (*flush)(fmt·State *); + int n; + va_list args; + rune verb; + ulong flag; + int width; + int prec; +}; + +#define iota(x) (1 << (x)) +enum +{ + fmt·Width = iota(0), + fmt·Left = iota(1), + fmt·Prec = iota(2), + fmt·Sharp = iota(3), + fmt·Space = iota(4), + fmt·Sign = iota(5), + fmt·Apost = iota(6), + fmt·Zero = iota(7), + fmt·Unsigned = iota(8), + fmt·Short = iota(9), + fmt·Long = iota(10), + fmt·Vlong = iota(11), + fmt·Comma = iota(12), + fmt·Byte = iota(13), + fmt·Ldouble = iota(14), + fmt·Flag = iota(15), +}; +#undef iota + +/* normal printing interface */ +int fmt·print(char *fmt, ...); +int fmt·fprint(int fd, char *fmt, ...); + +int fmt·sprint(char *buf, char *fmt, ...); +int fmt·nsprint(int len, char *buf, char *fmt, ...); +int fmt·esprint(char *buf, char *end, char *fmt, ...); + +int fmt·vprint(char *fmt, va_list args); +int fmt·vfprint(int fd, char *fmt, va_list args); +int fmt·vwrite(char *buf, char *fmt, va_list args); +int fmt·vnwrite(int len, char *buf, char *fmt, va_list args); +int fmt·vewrite(char *buf, char *end, char *fmt, va_list args); + +/* low-level interface: custom printing verbs */ +int fmt·do(fmt·State *, char *fmt); +int fmt·put(fmt·State *, char *fmt, ...); +int fmt·vput(fmt·State *, char *fmt, va_list args); +int fmt·install(fmt·State *, int (*put)(fmt·State*)); +int fmt·flush(fmt·State *); diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c new file mode 100644 index 0000000..3b8ff5a --- /dev/null +++ b/sys/libfmt/do.c @@ -0,0 +1,186 @@ +#include "internal.h" +#include + +#define atomic _Atomic +#define MaxFmt 128 +#define atomic·load atomic_load +#define atomic·store atomic_store + +static struct +{ + atomic int len; + Verb verb[MaxFmt]; +} formatter = +{ + ATOMIC_VAR_INIT(27), + { + {' ', ·fmtflag}, + {'#', ·fmtflag}, + {'%', ·fmtpercent}, + {'\'',·fmtflag}, + {'+', ·fmtflag}, + {',', ·fmtflag}, + {'-', ·fmtflag}, + {'C', ·fmtrune}, + {'E', ·fmtfloat}, + {'G', ·fmtfloat}, + {'S', ·fmtutf8}, + {'X', ·fmtint}, + {'b', ·fmtint}, + {'c', ·fmtchar}, + {'d', ·fmtint}, + {'e', ·fmtfloat}, + {'f', ·fmtfloat}, + {'g', ·fmtfloat}, + {'h', ·fmtflag}, + {'l', ·fmtflag}, + {'n', ·fmtcount}, + {'o', ·fmtint}, + {'p', ·fmtint}, + {'r', ·fmterr}, + {'s', ·fmtstr}, + {'u', ·fmtflag}, + {'u', ·fmtint}, + {'x', ·fmtint}, + } +}; + +static Formatter +format(int c) +{ + Verb *v, *e; + e = &formatter.verb[atomic·load(&formatter.len)]; + for(v=e; v > formatter.verb; --v){ + if(v->c == c) + return v->fmt; + } + + return ·fmterr; +} + +static char * +dispatch(fmt·State *io, char *fmt) +{ + rune r; + int i, n; + + io->flag = 0; + io->width = io->prec = 0; + + /* + * the form of each print verb: + * % [flags] verb + * + the verb is a single character + * + each flag is either + * - a single character + * - a decimal numeric string + * - up to 2 decimal strings can be used + * - [width|*].[prec|*] + * - if missing, set to 0 + * - if *, grab from varargs + */ + for(;;){ + fmt += utf8·decode(fmt, &r); + io->verb = r; + switch(r){ + case 0: + return nil; + case '.': + io->flag |= fmt·Width|fmt·Prec; + continue; + case '0': + if(!(io->flag & fmt·Width)){ + io->flag |= fmt·Zero; + continue; + } + /* fallthrough */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = 0; + while('0' <= r && r <= '9'){ + i = 10*i + (r-'0'); + r = *fmt++; + } + fmt--; + number: + if(io->flag & fmt·Width){ + io->flag |= fmt·Prec; + io->prec = i; + }else{ + io->flag |= fmt·Width; + io->width = i; + } + continue; + case '*': + i = va_arg(io->args, int); + if(i < 0){ + if(io->flag&fmt·Prec){ + io->flag ^= fmt·Prec; + io->prec = 0; + } + i = -i; + io->flag |= fmt·Left; + } + goto number; + } + n = format(r)(io); + if(n < 0) + return nil; + if(!n) + return fmt; + } +} + +static char * +flush(fmt·State *io, char *b, int len) +{ + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!io->flush || !(*io->flush)(io) || io->buffer.cur + len >= io->buffer.end) { + io->buffer.end = io->buffer.cur; + return nil; + } + return io->buffer.cur; +} + +int +fmt·do(fmt·State *io, char *fmt) +{ + rune r; + int c, n; + char *b, *e; + + for(;;){ + b = io->buffer.cur; + e = io->buffer.end; + + while((c = *(uchar *)fmt) && c != '%'){ + if(utf8·onebyte(c)){ + if(b + 1 > e){ + if(!(b=flush(io, b, sizeof(*b)))) + return -1; + e = io->buffer.end; + } + *b++ = *fmt++; + }else{ + n = utf8·decode(fmt, &r); + if(b + n > e){ + if(!(b=flush(io, b, sizeof(*b)))) + return -1; + e = io->buffer.end; + } + while(n--) + *b++ = *fmt++; + } + fmt++; + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(c=='\0') /* we hit our nul terminator */ + return io->n = n; + io->buffer.end = e; + } + + if(!(fmt=dispatch(io, fmt))) + return -1; + } +} diff --git a/sys/libfmt/internal.h b/sys/libfmt/internal.h new file mode 100644 index 0000000..56e64c8 --- /dev/null +++ b/sys/libfmt/internal.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +typedef int (*Formatter)(fmt·State *io); +typedef struct Verb Verb; + +struct Verb +{ + int c; + Formatter fmt; +}; diff --git a/sys/libfmt/print.c b/sys/libfmt/print.c new file mode 100644 index 0000000..e69de29 -- cgit v1.2.1