diff options
author | Nicholas <nbnoll@eml.cc> | 2021-11-11 17:07:44 -0800 |
---|---|---|
committer | Nicholas <nbnoll@eml.cc> | 2021-11-11 17:07:44 -0800 |
commit | b375f3cdedb5b0e08745d100b40e38d2f8396a58 (patch) | |
tree | a51d5763646ff482c26e79bfc1dcebccbe4812a7 | |
parent | 44b30ea0883af2827e618a45d472b47cf041a842 (diff) | |
parent | 9695ea005d4af93dcd60f74f10fd3c54499a182f (diff) |
chore: merge master into laptop
163 files changed, 5618 insertions, 2293 deletions
@@ -17,10 +17,10 @@ include/libimage.h sys/cc sys/nixos sys/libdraw -sys/libfont sys/libimage sys/libterm -sys/libutf8 + +sys/libutf/*-14.0.0.c sys/cmd/muc sys/cmd/wm @@ -71,7 +71,6 @@ ipaddr() { } dostatus() { - # xsetroot -name "$VOL│$MEM│$IP│$CPU│$NML│$WTR│$TIM" exec xsetroot -name "$VOL│$BAT│$MEM│$IP│$CPU│$NML│$WTR│$TIM" } diff --git a/include/base.h b/include/base.h index b841040..ca229b3 100644 --- a/include/base.h +++ b/include/base.h @@ -51,7 +51,6 @@ void* ·bufgrow(void*, vlong, vlong); // ----------------------------------------------------------------------------- // memory allocation -// TODO(nnoll): Allow for nil iterfaces? /* allocator interface */ typedef struct mem·Allocator { void *(*alloc)(void *heap, uint n, ulong size); @@ -66,7 +65,7 @@ typedef struct mem·Reallocator { void (*free)(void *iface, void *ptr); } mem·Reallocator; -extern mem·Reallocator sys·Relocator; +extern mem·Reallocator sys·FullMemory; /* simple memory arena */ typedef struct mem·Arena mem·Arena; @@ -94,23 +93,23 @@ error coro·free(Coro *c); typedef byte* string; -/* string helpers */ -string str·makecap(const byte *s, vlong len, vlong cap); -string str·makelen(const byte *s, vlong len); -string str·make(const byte *s); -string str·makef(const byte *fmt, ...); +/* augmented string functions */ +string str·makecap(const char *s, vlong len, vlong cap); +string str·makelen(const char *s, vlong len); +string str·make(const char *s); +string str·makef(const char *fmt, ...); void str·free(string s); int str·len(const string s); int str·cap(const string s); void str·clear(string *s); void str·grow(string *s, vlong delta); void str·fit(string *s); -int str·appendlen(string *s, vlong len, const byte *b); -int str·append(string *s, const byte* b); -int str·appendf(string *s, const byte* fmt, ...); -int str·appendbyte(string *s, const byte b); +int str·appendlen(string *s, vlong len, const char *b); +int str·append(string *s, const char *b); +int str·appendf(string *s, const char *fmt, ...); +int str·appendbyte(string *s, const char b); bool str·equals(const string s, const string t); -int str·find(string s, const byte* substr); +int str·find(string s, const char* substr); void str·lower(string s); void str·upper(string s); int str·read(string s, int size, int n, void *buf); @@ -118,6 +117,9 @@ void str·replace(string s, const byte* from, const byte* to); string* str·split(string s, const byte* tok); string str·join(vlong len, byte** fields, const byte* sep); +/* raw C string functions */ +char *str·copyn(char *dst, char *src, int n); + // ----------------------------------------------------------------------------- // i/o @@ -366,22 +368,22 @@ void vpanicf(byte *fmt, va_list args); // ----------------------------------------------------------------------------- // sorting -void sort·ints(uintptr n, int arr[]); -void sort·int8s(uintptr n, int8 arr[]); -void sort·int16s(uintptr n, int16 arr[]); -void sort·int32s(uintptr n, int32 arr[]); -void sort·int64s(uintptr n, int64 arr[]); +void sort·int(uintptr n, int arr[]); +void sort·int8(uintptr n, int8 arr[]); +void sort·int16(uintptr n, int16 arr[]); +void sort·int32(uintptr n, int32 arr[]); +void sort·int64(uintptr n, int64 arr[]); -void sort·uints(uintptr n, uint arr[]); -void sort·uint8s(uintptr n, uint8 arr[]); -void sort·uint16s(uintptr n, uint16 arr[]); -void sort·uint32s(uintptr n, uint32 arr[]); -void sort·uint64s(uintptr n, uint64 arr[]); +void sort·uint(uintptr n, uint arr[]); +void sort·uint8(uintptr n, uint8 arr[]); +void sort·uint16(uintptr n, uint16 arr[]); +void sort·uint32(uintptr n, uint32 arr[]); +void sort·uint64(uintptr n, uint64 arr[]); -void sort·floats(uintptr n, float arr[]); -void sort·doubles(uintptr n, double arr[]); +void sort·float(uintptr n, float arr[]); +void sort·double(uintptr n, double arr[]); -void sort·strings(uintptr n, byte* arr[]); +void sort·string(uintptr n, byte* arr[]); // ----------------------------------------------------------------------------- // fast random number generation @@ -390,6 +392,7 @@ error rng·init(uint64 seed); double rng·random(void); double rng·exponential(double lambda); bool rng·bernoulli(double f); +double rng·normal(void); uint64 rng·randi(int max); uint64 rng·poisson(double mean); diff --git a/include/libfmt.h b/include/libfmt.h index 17effc6..7ba40a0 100644 --- a/include/libfmt.h +++ b/include/libfmt.h @@ -9,13 +9,21 @@ struct fmt·State char *cur; char *end; } buffer; - int (*flush)(fmt·State *); int n; + va_list args; rune verb; ulong flag; int width; int prec; + char *thousands, *groups, *decimal; + + void *file; + int (*flush)(fmt·State *); + struct { + void *heap; + mem·Reallocator mem; + }; }; #define iota(x) (1 << (x)) @@ -41,22 +49,24 @@ enum #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 *); +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, ...); +char *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·vnsprint(int len, char *buf, char *fmt, va_list args); +char *fmt·vesprint(char *buf, char *end, char *fmt, va_list args); + +/* low-level interface: used for custom printing verbs */ +int fmt·open(int fd, int len, char *buf, fmt·State *); // creates a buffer on a file +int fmt·make(mem·Reallocator mem, void *heap, fmt·State *); // creates an in-memory buffer +void fmt·free(fmt·State *); // releases an in-memory buffer + +int fmt·do(fmt·State *, char *fmt); +int fmt·write(fmt·State *, char *fmt, ...); +int fmt·vwrite(fmt·State *, char *fmt, va_list args); +int fmt·install(int c, int (*format)(fmt·State *)); diff --git a/include/libutf.h b/include/libutf.h index d6618eb..25d6dee 100644 --- a/include/libutf.h +++ b/include/libutf.h @@ -27,7 +27,7 @@ char *utf8·findlast(char* s, rune); // find last rune in char stream int utf8·canfit(char *, int); // XXX: odd function... -int utf8·isletter(rune r); +int utf8·isalpha(rune r); int utf8·isdigit(rune r); int utf8·isspace(rune r); int utf8·istitle(rune r); diff --git a/sys/base/bufio.c b/sys/base/bufio.c deleted file mode 100644 index cc08f66..0000000 --- a/sys/base/bufio.c +++ /dev/null @@ -1,192 +0,0 @@ -#include <u.h> -#include <base.h> - -// ----------------------------------------------------------------------- -// reader - -error -bufio·initreader(io·Buffer *buf, io·Reader rdr, void *h) -{ - if (buf->state) { - errorf("attemped to initialize an active buffer, state is '%d'", buf->state); - return bufio·err; - } - buf->state = bufio·rdr; - buf->runesize = 0; - buf->h = h; - buf->rdr = rdr; - buf->beg = buf->buf + bufio·ungets; - buf->pos = buf->beg; - buf->end = buf->pos; - buf->size = bufio·size - bufio·ungets; - - return 0; -} - -void -bufio·finireader(io·Buffer *buf) -{ - buf->state = bufio·nil; - buf->runesize = 0; - buf->rdr = (io·Reader){ .read = nil }; -} - -static -int -refill(io·Buffer *buf) -{ - int n; - - if (buf->state & bufio·end) { - return bufio·err; - } - memcpy(buf->buf, buf->pos - bufio·ungets, bufio·ungets); - - n = buf->rdr.read(buf->h, 1, buf->size, buf->beg); - if (n < 0) - return bufio·err; - if (n == 0) { - buf->state |= bufio·end; - return 0; - } - - buf->pos = buf->beg; - buf->end = buf->pos + n; - - // TEST: put a physical EOF byte at the end - // this would allow for an unget operation - if (n < buf->size) - *buf->end++ = EOF; - - return n; -} - -int -bufio·getbyte(io·Buffer *buf) -{ -getbyte: - if (buf->pos < buf->end) { - return *buf->pos++; - } - - memmove(buf->buf, buf->end - bufio·ungets, bufio·ungets); - - if (refill(buf) <= 0) - return bufio·eof; - - goto getbyte; -} - -error -bufio·ungetbyte(io·Buffer *buf, byte c) -{ - if (!(buf->state & bufio·rdr)) { - errorf("attempted to unget on non-active reader"); - return bufio·err; - } - - if (buf->pos == buf->buf) { - errorf("attempted to unget past end of buffer"); - return bufio·err; - } - - buf->pos--; - return 0; -} - -#if 0 -rune -bufio·getrune(io·Buffer *buf) -{ - ubyte b; - int i; - byte str[UTFmax+1]; - rune r; - - // NOTE: I'm worried about the sign here... - b = bufio·getbyte(buf); - if (b < RuneSelf) { - buf->runesize = 1; - return b; - } - - i = 0; - str[i++] = b; - -nextbyte: - b = bufio·getbyte(buf); - if (b < 0) return b; - if (i >= arrlen(str)) return RuneErr; - str[i++] = b; - if (!utf8·fullrune(str, i)) - goto nextbyte; - - buf->runesize = utf8·bytetorune(&r, str); - if (r == RuneErr && b == 1) { - errorf("illegal UTF-8 sequence"); - for (; i >= 0; i--) - errorf("%s%.2x", i > 0 ? " " : "", *(ubyte*)(str+i)); - errorf("\n"); - - buf->runesize = 0; - } else - for (; i > buf->runesize; i--) - bufio·ungetbyte(buf, str[i]); - - return r; -} - -// TODO: Check that we are given the correct rune! -error -bufio·ungetrune(io·Buffer *buf, rune r) -{ - if (buf->state & bufio·rdr) { - errorf("attempted to unget on non-active reader"); - return bufio·err; - } - - if (buf->pos == buf->buf) { - errorf("attempted to unget past end of buffer"); - return bufio·err; - } - - buf->pos -= buf->runesize; - return 0; -} -#endif - -int -bufio·read(io·Buffer *buf, int sz, int n, void *out) -{ - byte *wtr; - int nr, rem, diff; - - if (n == 0 || buf->state & bufio·end) - return bufio·err; - - assert(buf->state & bufio·rdr); - - wtr = out; - rem = n*sz; - while (rem > 0) { - diff = buf->end - buf->pos; - nr = MIN(diff, rem); - if (!nr) { - if (buf->state & bufio·end) - break; - if (refill(buf) <= 0) - break; - - continue; - } - memmove(wtr, buf->pos, nr); - wtr += nr; - buf->pos += nr; - rem -= nr; - } - - return n - rem/sz; -} - -// ----------------------------------------------------------------------- -// writer diff --git a/sys/base/bufio/dump.c b/sys/base/bufio/dump.c new file mode 100644 index 0000000..0b527e2 --- /dev/null +++ b/sys/base/bufio/dump.c @@ -0,0 +1,66 @@ +// ----------------------------------------------------------------------- +// reader + +#if 0 +rune +bufio·getrune(io·Buffer *buf) +{ + ubyte b; + int i; + byte str[UTFmax+1]; + rune r; + + // NOTE: I'm worried about the sign here... + b = bufio·getbyte(buf); + if (b < RuneSelf) { + buf->runesize = 1; + return b; + } + + i = 0; + str[i++] = b; + +nextbyte: + b = bufio·getbyte(buf); + if (b < 0) return b; + if (i >= arrlen(str)) return RuneErr; + str[i++] = b; + if (!utf8·fullrune(str, i)) + goto nextbyte; + + buf->runesize = utf8·bytetorune(&r, str); + if (r == RuneErr && b == 1) { + errorf("illegal UTF-8 sequence"); + for (; i >= 0; i--) + errorf("%s%.2x", i > 0 ? " " : "", *(ubyte*)(str+i)); + errorf("\n"); + + buf->runesize = 0; + } else + for (; i > buf->runesize; i--) + bufio·ungetbyte(buf, str[i]); + + return r; +} + +// TODO: Check that we are given the correct rune! +error +bufio·ungetrune(io·Buffer *buf, rune r) +{ + if (buf->state & bufio·rdr) { + errorf("attempted to unget on non-active reader"); + return bufio·err; + } + + if (buf->pos == buf->buf) { + errorf("attempted to unget past end of buffer"); + return bufio·err; + } + + buf->pos -= buf->runesize; + return 0; +} +#endif + +// ----------------------------------------------------------------------- +// writer diff --git a/sys/base/bufio/get.c b/sys/base/bufio/get.c new file mode 100644 index 0000000..9f10c88 --- /dev/null +++ b/sys/base/bufio/get.c @@ -0,0 +1,17 @@ +#include "internal.h" +#include "refill.h" + +int +bufio·getbyte(io·Buffer *buf) +{ +getbyte: + if(buf->pos < buf->end) + return *buf->pos++; + + memmove(buf->buf, buf->end - bufio·ungets, bufio·ungets); + + if(refill(buf) <= 0) + return bufio·eof; + + goto getbyte; +} diff --git a/sys/base/bufio/internal.h b/sys/base/bufio/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/sys/base/bufio/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include <u.h> +#include <base.h> diff --git a/sys/base/bufio/read.c b/sys/base/bufio/read.c new file mode 100644 index 0000000..09a9f83 --- /dev/null +++ b/sys/base/bufio/read.c @@ -0,0 +1,36 @@ +#include "internal.h" +#include "refill.h" + +int +bufio·read(io·Buffer *buf, int sz, int n, void *out) +{ + byte *wtr; + int nr, rem, diff; + + if(n == 0 || buf->state & bufio·end) + return bufio·err; + + assert(buf->state & bufio·rdr); + + wtr = out; + rem = n*sz; + + while(rem > 0){ + diff = buf->end - buf->pos; + nr = MIN(diff, rem); + if(!nr){ + if(buf->state & bufio·end) + break; + if(refill(buf) <= 0) + break; + + continue; + } + memmove(wtr, buf->pos, nr); + wtr += nr; + buf->pos += nr; + rem -= nr; + } + + return n - rem/sz; +} diff --git a/sys/base/bufio/reader.c b/sys/base/bufio/reader.c new file mode 100644 index 0000000..afdaf60 --- /dev/null +++ b/sys/base/bufio/reader.c @@ -0,0 +1,28 @@ +#include "internal.h" + +error +bufio·initreader(io·Buffer *buf, io·Reader rdr, void *h) +{ + if (buf->state) { + errorf("attemped to initialize an active buffer, state is '%d'", buf->state); + return bufio·err; + } + buf->state = bufio·rdr; + buf->runesize = 0; + buf->h = h; + buf->rdr = rdr; + buf->beg = buf->buf + bufio·ungets; + buf->pos = buf->beg; + buf->end = buf->pos; + buf->size = bufio·size - bufio·ungets; + + return 0; +} + +void +bufio·finireader(io·Buffer *buf) +{ + buf->state = bufio·nil; + buf->runesize = 0; + buf->rdr = (io·Reader){ .read = nil }; +} diff --git a/sys/base/bufio/refill.h b/sys/base/bufio/refill.h new file mode 100644 index 0000000..41e357e --- /dev/null +++ b/sys/base/bufio/refill.h @@ -0,0 +1,28 @@ +int +refill(io·Buffer *buf) +{ + int n; + + if(buf->state & bufio·end) + return bufio·err; + + memcpy(buf->buf, buf->pos - bufio·ungets, bufio·ungets); + + n = buf->rdr.read(buf->h, 1, buf->size, buf->beg); + if(n < 0) + return bufio·err; + if(n == 0){ + buf->state |= bufio·end; + return 0; + } + + buf->pos = buf->beg; + buf->end = buf->pos + n; + + // TEST: put a physical EOF byte at the end + // this would allow for an unget operation + if(n < buf->size) + *buf->end++ = EOF; + + return n; +} diff --git a/sys/base/bufio/rules.mk b/sys/base/bufio/rules.mk new file mode 100644 index 0000000..84f283f --- /dev/null +++ b/sys/base/bufio/rules.mk @@ -0,0 +1,5 @@ +SRCS_$(d)+=\ + $(d)/bufio/get.c\ + $(d)/bufio/read.c\ + $(d)/bufio/reader.c\ + $(d)/bufio/unget.c\ diff --git a/sys/base/bufio/unget.c b/sys/base/bufio/unget.c new file mode 100644 index 0000000..3fd16de --- /dev/null +++ b/sys/base/bufio/unget.c @@ -0,0 +1,18 @@ +#include "internal.h" + +error +bufio·ungetbyte(io·Buffer *buf, byte c) +{ + if(!(buf->state & bufio·rdr)) { + errorf("attempted to unget on non-active reader"); + return bufio·err; + } + + if(buf->pos == buf->buf) { + errorf("attempted to unget past end of buffer"); + return bufio·err; + } + + buf->pos--; + return 0; +} diff --git a/sys/base/coro.c b/sys/base/coro/coro.c index 953dee0..2255c99 100644 --- a/sys/base/coro.c +++ b/sys/base/coro/coro.c @@ -1,29 +1,6 @@ -#include <u.h> -#include <base.h> - -// ----------------------------------------------------------------------- -// Assembly routines - -extern void _newcoro(Coro *co, uintptr (*func)(Coro*, uintptr), void *stk); -extern uintptr _coroyield(Coro *co, uintptr arg); - -// ----------------------------------------------------------------------- -// Globals - -// static thread_local coro *CONTEXT; - -// ----------------------------------------------------------------------- -// C interface +#include "internal.h" /* Co-routine context */ -struct Coro -{ - void* sp; - void* bp; - uintptr size; - void* user; -}; - Coro* coro·make(uintptr stk, uintptr (*func)(Coro*, uintptr)) { diff --git a/sys/base/coro/internal.h b/sys/base/coro/internal.h new file mode 100644 index 0000000..f57d27b --- /dev/null +++ b/sys/base/coro/internal.h @@ -0,0 +1,15 @@ +#pragma once + +#include <u.h> +#include <base.h> + +extern void _newcoro(Coro *co, uintptr (*func)(Coro*, uintptr), void *stk); +extern uintptr _coroyield(Coro *co, uintptr arg); + +struct Coro +{ + void *sp; + void *bp; + uintptr size; + void *user; +}; diff --git a/sys/base/coro/rules.mk b/sys/base/coro/rules.mk new file mode 100644 index 0000000..c2ee89f --- /dev/null +++ b/sys/base/coro/rules.mk @@ -0,0 +1,3 @@ +SRCS_$(d)+=\ + $(d)/coro/coro.c\ + $(d)/coro/unix_x64.s\ diff --git a/sys/base/coro_unix_x64.s b/sys/base/coro/unix_x64.s index d7de2a2..d7de2a2 100644 --- a/sys/base/coro_unix_x64.s +++ b/sys/base/coro/unix_x64.s diff --git a/sys/base/error.c b/sys/base/error.c deleted file mode 100644 index dc6421d..0000000 --- a/sys/base/error.c +++ /dev/null @@ -1,57 +0,0 @@ -#include <u.h> -#include <base.h> - -void -exits(char *s) -{ - if (s == nil || *s == 0) - exit(0); - - fputs(s, stderr); - exit(1); -} - -void -errorf(byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, args); - - va_end(args); -} - -void -verrorf(byte* fmt, va_list args) -{ - printf("error: "); - vprintf(fmt, args); - printf("\n"); -} - -void -panicf(byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - printf("panic: "); - vprintf(fmt, args); - printf("\n"); - - va_end(args); - - exit(1); -} - -void -vpanicf(byte* fmt, va_list args) -{ - printf("panic: "); - vprintf(fmt, args); - printf("\n"); - - exit(1); -} diff --git a/sys/base/error/errorf.c b/sys/base/error/errorf.c new file mode 100644 index 0000000..193dd9d --- /dev/null +++ b/sys/base/error/errorf.c @@ -0,0 +1,13 @@ +#include "internal.h" + +void +errorf(byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, args); + + va_end(args); +} diff --git a/sys/base/error/exits.c b/sys/base/error/exits.c new file mode 100644 index 0000000..6be7d3b --- /dev/null +++ b/sys/base/error/exits.c @@ -0,0 +1,11 @@ +#include "internal.h" + +void +exits(char *s) +{ + if(s == nil || *s == 0) + exit(0); + + fputs(s, stderr); + exit(1); +} diff --git a/sys/base/error/internal.h b/sys/base/error/internal.h new file mode 100644 index 0000000..88a8895 --- /dev/null +++ b/sys/base/error/internal.h @@ -0,0 +1,3 @@ +#include <u.h> +#include <base.h> + diff --git a/sys/base/error/panicf.c b/sys/base/error/panicf.c new file mode 100644 index 0000000..d698576 --- /dev/null +++ b/sys/base/error/panicf.c @@ -0,0 +1,16 @@ +#include "internal.h" + +void +panicf(byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + printf("panic: "); + vprintf(fmt, args); + printf("\n"); + + va_end(args); + + exit(1); +} diff --git a/sys/base/error/rules.mk b/sys/base/error/rules.mk new file mode 100644 index 0000000..e3a9ce0 --- /dev/null +++ b/sys/base/error/rules.mk @@ -0,0 +1,6 @@ +SRCS_$(d)+=\ + $(d)/error/exits.c \ + $(d)/error/errorf.c \ + $(d)/error/panicf.c \ + $(d)/error/verrorf.c \ + $(d)/error/vpanicf.c \ diff --git a/sys/base/error/verrorf.c b/sys/base/error/verrorf.c new file mode 100644 index 0000000..15af064 --- /dev/null +++ b/sys/base/error/verrorf.c @@ -0,0 +1,9 @@ +#include "internal.h" + +void +verrorf(byte* fmt, va_list args) +{ + printf("error: "); + vprintf(fmt, args); + printf("\n"); +} diff --git a/sys/base/error/vpanicf.c b/sys/base/error/vpanicf.c new file mode 100644 index 0000000..bea97ac --- /dev/null +++ b/sys/base/error/vpanicf.c @@ -0,0 +1,11 @@ +#include "internal.h" + +void +vpanicf(byte* fmt, va_list args) +{ + printf("panic: "); + vprintf(fmt, args); + printf("\n"); + + exit(1); +} diff --git a/sys/base/flate.c b/sys/base/flate.c deleted file mode 100644 index 5930fd1..0000000 --- a/sys/base/flate.c +++ /dev/null @@ -1,243 +0,0 @@ -#include <u.h> -#include <base.h> - -#include <zlib.h> - -typedef struct buffer -{ - union { - struct z_stream_s; - z_stream z; - }; - - ubyte buf[4098]; -} buffer; - -// ----------------------------------------------------------------------- -// Reading - -typedef struct flate·Reader -{ - io·Reader rdr; - void* impl; - - union { - struct buffer; - buffer b; - }; -} flate·Reader; - -flate·Reader* -flate·openreader(io·Reader rdr, void* r, mem·Allocator mem, void* m) -{ - error err; - flate·Reader *zrdr; - - zrdr = mem.alloc(m, 1, sizeof(*zrdr)); - - zrdr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; - zrdr->zfree = mem.free; - zrdr->opaque = m; - zrdr->avail_in = rdr.read(r, 1, arrlen(zrdr->buf), zrdr->buf); - zrdr->next_in = zrdr->buf; - - err = inflateInit(&zrdr->b.z); - - switch (err) { - case Z_OK: - return zrdr; - - case Z_MEM_ERROR: - errorf("zlib: not enough memory"); - goto ERROR; - - case Z_VERSION_ERROR: - errorf("zlib: incompatible version"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: incorrect input parameters"); - goto ERROR; - - default: - errorf("zlib: unrecognized error code"); - } -ERROR: - errorf("zlib: msg: %s", zrdr->msg); - mem.free(m, zrdr); - return nil; -} - -error -flate·closereader(flate·Reader *rdr) -{ - int err; - flate·Reader zrdr; - - zrdr = *rdr; - err = inflateEnd(&zrdr.b.z); - if (err != Z_OK) { - errorf("zlib: failed to cleanup"); - return err; - } - rdr->zfree(rdr->opaque, rdr); - - return 0; -} - -int -flate·read(flate·Reader *rdr, int sz, int n, void *buf) -{ - int r; - int err; - flate·Reader zrdr; - - zrdr = *rdr; - zrdr.next_out = buf; - zrdr.avail_out = n*sz; - -READ: - err = inflate(&zrdr.b.z, Z_STREAM_END); - switch (err) { - case Z_OK: - return n; - - case Z_STREAM_END: - r = zrdr.next_out - (ubyte*)buf; - n -= r; - zrdr.avail_in = zrdr.rdr.read(zrdr.impl, 1, arrlen(zrdr.buf), zrdr.buf); - if (!zrdr.avail_in) { - return r; - } - zrdr.next_in = zrdr.buf; - goto READ; - - case Z_NEED_DICT: - errorf("zlib: need input dictionary"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: inconsistent stream structure"); - goto ERROR; - } -ERROR: - flate·closereader(rdr); - return -1; -} - -// ----------------------------------------------------------------------- -// Writing - -struct flate·Writer -{ - io·Writer wtr; - void* impl; - - union { - struct buffer; - buffer b; - }; -}; - -flate·Writer* -flate·openwriter(io·Writer wtr, void* w, mem·Allocator mem, void* m) -{ - error err; - flate·Writer *zwtr; - - zwtr = mem.alloc(m, 1, sizeof(*zwtr)); - zwtr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; - zwtr->zfree = mem.free; - zwtr->opaque = m; - zwtr->avail_in = 0; - - err = deflateInit(&zwtr->b.z, Z_DEFAULT_COMPRESSION); - - switch (err) { - case Z_OK: - return zwtr; - - case Z_MEM_ERROR: - errorf("zlib: not enough memory"); - goto ERROR; - - case Z_VERSION_ERROR: - errorf("zlib: incompatible version"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: incorrect compression level"); - goto ERROR; - - default: - errorf("zlib: unrecognized error code"); - } -ERROR: - errorf("zlib: msg: %s", zwtr->msg); - mem.free(m, zwtr); - return nil; -} - -error -flate·closewriter(flate·Writer *wtr) -{ - int err; - flate·Writer zwtr; - - zwtr = *wtr; - err = deflateEnd(&zwtr.b.z); - if (err != Z_OK) { - errorf("zlib: failed to cleanup"); - return err; - } - zwtr.zfree(zwtr.opaque, wtr); - - return 0; -} - -int -flate·write(flate·Writer *wtr, int sz, int n, void *buf) -{ - int r; - int err; - flate·Writer zwtr; - - zwtr = *wtr; - zwtr.next_out = buf; -DEFLATE: - zwtr.avail_out = n*sz; - err = deflate(&zwtr.z, Z_NO_FLUSH); - - switch (err) { - case Z_STREAM_END: - return n; - - case Z_OK: - r = (zwtr.next_out - (ubyte*)buf)/sz; - n -= r; - if (!n) { - return r; - } - buf += n; - goto DEFLATE; - - case Z_STREAM_ERROR: - errorf("zlib: bad input"); - goto ERROR; - - case Z_BUF_ERROR: - if (!zwtr.avail_in) { - zwtr.avail_in += zwtr.wtr.write(zwtr.impl, 1, arrlen(zwtr.buf), buf); - if (!zwtr.avail_in) { - errorf("reader: failed read"); - goto ERROR; - } - goto DEFLATE; - } - } - - return 0; -ERROR: - errorf("zlib: %s", zwtr.msg); - return -1; -} diff --git a/sys/base/flate/internal.h b/sys/base/flate/internal.h new file mode 100644 index 0000000..794c7c2 --- /dev/null +++ b/sys/base/flate/internal.h @@ -0,0 +1,39 @@ +#pragma once + +#include <u.h> +#include <base.h> + +#include <zlib.h> + +typedef struct buffer +{ + union { + struct z_stream_s; + z_stream z; + }; + + ubyte buf[4098]; +} buffer; + +typedef struct flate·Reader +{ + io·Reader rdr; + void* impl; + + union { + struct buffer; + buffer b; + }; +} flate·Reader; + +typedef struct flate·Writer +{ + io·Writer wtr; + void* impl; + + union { + struct buffer; + buffer b; + }; +} flate·Writer; + diff --git a/sys/base/flate/read.c b/sys/base/flate/read.c new file mode 100644 index 0000000..9a42070 --- /dev/null +++ b/sys/base/flate/read.c @@ -0,0 +1,41 @@ +#include "internal.h" + +int +flate·read(flate·Reader *rdr, int sz, int n, void *buf) +{ + int r; + int err; + flate·Reader zrdr; + + zrdr = *rdr; + zrdr.next_out = buf; + zrdr.avail_out = n*sz; + +READ: + err = inflate(&zrdr.b.z, Z_STREAM_END); + switch (err) { + case Z_OK: + return n; + + case Z_STREAM_END: + r = zrdr.next_out - (ubyte*)buf; + n -= r; + zrdr.avail_in = zrdr.rdr.read(zrdr.impl, 1, arrlen(zrdr.buf), zrdr.buf); + if (!zrdr.avail_in) { + return r; + } + zrdr.next_in = zrdr.buf; + goto READ; + + case Z_NEED_DICT: + errorf("zlib: need input dictionary"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: inconsistent stream structure"); + goto ERROR; + } +ERROR: + flate·closereader(rdr); + return -1; +} diff --git a/sys/base/flate/reader.c b/sys/base/flate/reader.c new file mode 100644 index 0000000..84f0d80 --- /dev/null +++ b/sys/base/flate/reader.c @@ -0,0 +1,59 @@ +#include "internal.h" + +flate·Reader* +flate·openreader(io·Reader rdr, void* r, mem·Allocator mem, void* m) +{ + error err; + flate·Reader *zrdr; + + zrdr = mem.alloc(m, 1, sizeof(*zrdr)); + + zrdr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; + zrdr->zfree = mem.free; + zrdr->opaque = m; + zrdr->avail_in = rdr.read(r, 1, arrlen(zrdr->buf), zrdr->buf); + zrdr->next_in = zrdr->buf; + + err = inflateInit(&zrdr->b.z); + + switch (err) { + case Z_OK: + return zrdr; + + case Z_MEM_ERROR: + errorf("zlib: not enough memory"); + goto ERROR; + + case Z_VERSION_ERROR: + errorf("zlib: incompatible version"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: incorrect input parameters"); + goto ERROR; + + default: + errorf("zlib: unrecognized error code"); + } +ERROR: + errorf("zlib: msg: %s", zrdr->msg); + mem.free(m, zrdr); + return nil; +} + +error +flate·closereader(flate·Reader *rdr) +{ + int err; + flate·Reader zrdr; + + zrdr = *rdr; + err = inflateEnd(&zrdr.b.z); + if (err != Z_OK) { + errorf("zlib: failed to cleanup"); + return err; + } + rdr->zfree(rdr->opaque, rdr); + + return 0; +} diff --git a/sys/base/flate/rules.mk b/sys/base/flate/rules.mk new file mode 100644 index 0000000..54d8c14 --- /dev/null +++ b/sys/base/flate/rules.mk @@ -0,0 +1,6 @@ +SRCS_$(d)+=\ + $(d)/flate/read.c\ + $(d)/flate/reader.c\ + $(d)/flate/write.c\ + $(d)/flate/writer.c\ + $(d)/flate/writer.c\ diff --git a/sys/base/flate/write.c b/sys/base/flate/write.c new file mode 100644 index 0000000..3f07b94 --- /dev/null +++ b/sys/base/flate/write.c @@ -0,0 +1,48 @@ +#include "internal.h" + +int +flate·write(flate·Writer *wtr, int sz, int n, void *buf) +{ + int r; + int err; + flate·Writer zwtr; + + zwtr = *wtr; + zwtr.next_out = buf; +DEFLATE: + zwtr.avail_out = n*sz; + err = deflate(&zwtr.z, Z_NO_FLUSH); + + switch (err) { + case Z_STREAM_END: + return n; + + case Z_OK: + r = (zwtr.next_out - (ubyte*)buf)/sz; + n -= r; + if (!n) { + return r; + } + buf += n; + goto DEFLATE; + + case Z_STREAM_ERROR: + errorf("zlib: bad input"); + goto ERROR; + + case Z_BUF_ERROR: + if (!zwtr.avail_in) { + zwtr.avail_in += zwtr.wtr.write(zwtr.impl, 1, arrlen(zwtr.buf), buf); + if (!zwtr.avail_in) { + errorf("reader: failed read"); + goto ERROR; + } + goto DEFLATE; + } + } + + return 0; +ERROR: + errorf("zlib: %s", zwtr.msg); + return -1; +} diff --git a/sys/base/flate/writer.c b/sys/base/flate/writer.c new file mode 100644 index 0000000..f339ae0 --- /dev/null +++ b/sys/base/flate/writer.c @@ -0,0 +1,57 @@ +#include "internal.h" + +flate·Writer* +flate·openwriter(io·Writer wtr, void* w, mem·Allocator mem, void* m) +{ + error err; + flate·Writer *zwtr; + + zwtr = mem.alloc(m, 1, sizeof(*zwtr)); + zwtr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; + zwtr->zfree = mem.free; + zwtr->opaque = m; + zwtr->avail_in = 0; + + err = deflateInit(&zwtr->b.z, Z_DEFAULT_COMPRESSION); + + switch (err) { + case Z_OK: + return zwtr; + + case Z_MEM_ERROR: + errorf("zlib: not enough memory"); + goto ERROR; + + case Z_VERSION_ERROR: + errorf("zlib: incompatible version"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: incorrect compression level"); + goto ERROR; + + default: + errorf("zlib: unrecognized error code"); + } +ERROR: + errorf("zlib: msg: %s", zwtr->msg); + mem.free(m, zwtr); + return nil; +} + +error +flate·closewriter(flate·Writer *wtr) +{ + int err; + flate·Writer zwtr; + + zwtr = *wtr; + err = deflateEnd(&zwtr.b.z); + if (err != Z_OK) { + errorf("zlib: failed to cleanup"); + return err; + } + zwtr.zfree(zwtr.opaque, wtr); + + return 0; +} diff --git a/sys/base/fs/internal.h b/sys/base/fs/internal.h new file mode 100644 index 0000000..7fde093 --- /dev/null +++ b/sys/base/fs/internal.h @@ -0,0 +1,18 @@ +#include <u.h> +#include <base.h> +#include <base/macro/map.h> +#include <dirent.h> + +/* + * path history + */ +struct Key +{ + ino_t ino; + dev_t dev; +}; + +struct fs·History +{ + SET_STRUCT_BODY(struct Key); +}; diff --git a/sys/base/fs/rules.mk b/sys/base/fs/rules.mk new file mode 100644 index 0000000..3927ae3 --- /dev/null +++ b/sys/base/fs/rules.mk @@ -0,0 +1,3 @@ +SRCS_$(d)+=\ + $(d)/fs/walk.c\ + $(d)/fs/walker.c\ diff --git a/sys/base/fs.c b/sys/base/fs/walk.c index 6d5ee0f..d528896 100644 --- a/sys/base/fs.c +++ b/sys/base/fs/walk.c @@ -1,25 +1,8 @@ -#include <u.h> -#include <base.h> -#include <base/macro/map.h> -#include <dirent.h> - -/* - * path history - */ -struct Key -{ - ino_t ino; - dev_t dev; -}; +#include "internal.h" #define hash(k) ((int32)k.ino ^ (int32)k.dev) #define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev) -struct fs·History -{ - SET_STRUCT_BODY(struct Key); -}; - static int morehistory(fs·History *h, int n) @@ -44,62 +27,6 @@ forget(fs·History *h) SET_RESET(h); } -static -void -delete(fs·History *h) -{ - SET_FREE(h, sys·Memory, nil); -} - -#undef hash -#undef equal - -static -char * -strcpyn(char *dst, char *src, int n) -{ - while(*src && n-- > 0) - *dst++ = *src++; - - *dst = 0; - return dst; -} - -/* - * main functions - */ - -int -fs·init(fs·Walker *fs, char *path) -{ - fs->base = fs->end = fs->path; - - if (!path || !path[0]) { - path = getcwd(fs->path, arrlen(fs->path)); - if (!path) - return 1; - fs->end += strlen(path); - } else - fs->end = strcpyn(fs->base, path, arrlen(fs->path)); - - if (fs->path[0] != '/') - fs->fd = AT_FDCWD; - - if (!fs->hist && !(fs->flags & fs·nolinks)) - fs->hist = calloc(1, sizeof(*fs->hist)); - - return 0; -} - -void -fs·fini(fs·Walker *fs) -{ - if (fs->hist) { - delete(fs->hist); - free(fs->hist); - } -} - void fs·walk(fs·Walker *fs) { @@ -166,12 +93,12 @@ fs·walk(fs·Walker *fs) fs->base = fs->end; while((d = readdir(dir))) { - if (*d->d_name == '.') - if (d->d_name[1] == 0 || /* . */ + if(*d->d_name == '.') + if(d->d_name[1] == 0 || /* . */ (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */ continue; - fs->end = strcpyn(fs->base, d->d_name, arrend(fs->path) - fs->base); + fs->end = str·copyn(fs->base, d->d_name, arrend(fs->path) - fs->base); fs->lev++; fs·walk(fs); diff --git a/sys/base/fs/walker.c b/sys/base/fs/walker.c new file mode 100644 index 0000000..65ff391 --- /dev/null +++ b/sys/base/fs/walker.c @@ -0,0 +1,39 @@ +#include "internal.h" + +static +void +delete(fs·History *h) +{ + SET_FREE(h, sys·Memory, nil); +} + +int +fs·init(fs·Walker *fs, char *path) +{ + fs->base = fs->end = fs->path; + + if(!path || !path[0]){ + path = getcwd(fs->path, arrlen(fs->path)); + if (!path) + return 1; + fs->end += strlen(path); + }else + fs->end = str·copyn(fs->base, path, arrlen(fs->path)); + + if(fs->path[0] != '/') + fs->fd = AT_FDCWD; + + if(!fs->hist && !(fs->flags & fs·nolinks)) + fs->hist = calloc(1, sizeof(*fs->hist)); + + return 0; +} + +void +fs·fini(fs·Walker *fs) +{ + if(fs->hist){ + delete(fs->hist); + free(fs->hist); + } +} diff --git a/sys/base/gz.c b/sys/base/gz.c deleted file mode 100644 index d84397f..0000000 --- a/sys/base/gz.c +++ /dev/null @@ -1,108 +0,0 @@ -#include <u.h> -#include <base.h> - -#include <zlib.h> - -// ----------------------------------------------------------------------- -// interface implementations - -/* actual interfaces */ -io·Reader gz·Reader = (io·Reader){ gz·read }; -io·Peeker gz·Peeker = (io·Peeker){ gz·getbyte, gz·ungetbyte }; -io·Seeker gz·Seeker = (io·Seeker){ gz·seek, gz·tell }; -io·PeekReader gz·Peekreader = (io·PeekReader){ gz·read, gz·getbyte, gz·ungetbyte }; - -io·Writer gz·Writer = (io·Writer){ gz·write }; -io·Putter gz·Putter = (io·Putter){ gz·putbyte, gz·putstring }; -io·PutWriter gz·PutWriter = (io·PutWriter){ gz·write, gz·putbyte, gz·putstring }; - -io·ReadWriter gz·ReadWriter = (io·ReadWriter){ gz·read, gz·write }; - -// ----------------------------------------------------------------------- -// functions implementations - -gz·Stream* -gz·open(byte *path, byte *mode) -{ - return gzopen(path, mode); -} - -error -gz·close(gz·Stream* s) -{ - return gzclose(s); -} - -int -gz·read(gz·Stream *s, int sz, int n, void* buf) -{ - return gzread(s, buf, n*sz); -} - -int -gz·readln(gz·Stream *s, int n, byte *buf) -{ - byte* b; - b = gzgets(s, buf, n); - - return strlen(b); -} - -byte -gz·getbyte(gz·Stream *s) -{ - // NOTE: Can't call macro - byte b[2]; - gzread(s, b, 1); - - return b[0]; -} - -error -gz·ungetbyte(gz·Stream *s, byte c) -{ - return gzungetc(c, s); -} - -int -gz·write(gz·Stream *s, int sz, int n, void* buf) -{ - return gzwrite(s, buf, n*sz); -} - -error -gz·putbyte(gz·Stream *s, byte c) -{ - return gzputc(s, c); -} - -error -gz·putstring(gz·Stream *s, byte *str) -{ - return gzputs(s, str); -} - -int -gz·printf(gz·Stream *s, byte *fmt, ...) -{ - error err; - - va_list args; - va_start(args, fmt); - err = gzprintf(s, fmt, args); - va_end(args); - - return err; -} - -error -gz·flush(gz·Stream *s) -{ - return gzflush(s, Z_FINISH); -} - -int -gz·seek(gz·Stream *s, long off, enum SeekPos whence) -{ - return gzseek(s, off, whence); -} diff --git a/sys/base/gz/flush.c b/sys/base/gz/flush.c new file mode 100644 index 0000000..011a3ab --- /dev/null +++ b/sys/base/gz/flush.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +gz·flush(gz·Stream *s) +{ + return gzflush(s, Z_FINISH); +} diff --git a/sys/base/gz/get.c b/sys/base/gz/get.c new file mode 100644 index 0000000..24ba23a --- /dev/null +++ b/sys/base/gz/get.c @@ -0,0 +1,17 @@ +#include "internal.h" + +byte +gz·getbyte(gz·Stream *s) +{ + // NOTE: Can't call macro + byte b[2]; + gzread(s, b, 1); + + return b[0]; +} + +error +gz·ungetbyte(gz·Stream *s, byte c) +{ + return gzungetc(c, s); +} diff --git a/sys/base/gz/interface.c b/sys/base/gz/interface.c new file mode 100644 index 0000000..15b8f10 --- /dev/null +++ b/sys/base/gz/interface.c @@ -0,0 +1,12 @@ +#include "internal.h" + +io·Reader gz·Reader = (io·Reader){ gz·read }; +io·Peeker gz·Peeker = (io·Peeker){ gz·getbyte, gz·ungetbyte }; +io·Seeker gz·Seeker = (io·Seeker){ gz·seek, gz·tell }; +io·PeekReader gz·Peekreader = (io·PeekReader){ gz·read, gz·getbyte, gz·ungetbyte }; + +io·Writer gz·Writer = (io·Writer){ gz·write }; +io·Putter gz·Putter = (io·Putter){ gz·putbyte, gz·putstring }; +io·PutWriter gz·PutWriter = (io·PutWriter){ gz·write, gz·putbyte, gz·putstring }; + +io·ReadWriter gz·ReadWriter = (io·ReadWriter){ gz·read, gz·write }; diff --git a/sys/base/gz/internal.h b/sys/base/gz/internal.h new file mode 100644 index 0000000..6a268c4 --- /dev/null +++ b/sys/base/gz/internal.h @@ -0,0 +1,6 @@ +#pragma once + +#include <u.h> +#include <base.h> + +#include <zlib.h> diff --git a/sys/base/gz/open.c b/sys/base/gz/open.c new file mode 100644 index 0000000..c84ce5e --- /dev/null +++ b/sys/base/gz/open.c @@ -0,0 +1,13 @@ +#include "internal.h" + +gz·Stream* +gz·open(byte *path, byte *mode) +{ + return gzopen(path, mode); +} + +error +gz·close(gz·Stream* s) +{ + return gzclose(s); +} diff --git a/sys/base/gz/printf.c b/sys/base/gz/printf.c new file mode 100644 index 0000000..d7f75cf --- /dev/null +++ b/sys/base/gz/printf.c @@ -0,0 +1,15 @@ +#include "internal.h" + +int +gz·printf(gz·Stream *s, byte *fmt, ...) +{ + error err; + + va_list args; + va_start(args, fmt); + err = gzprintf(s, fmt, args); + va_end(args); + + return err; +} + diff --git a/sys/base/gz/put.c b/sys/base/gz/put.c new file mode 100644 index 0000000..fa9807d --- /dev/null +++ b/sys/base/gz/put.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +gz·putbyte(gz·Stream *s, byte c) +{ + return gzputc(s, c); +} diff --git a/sys/base/gz/putstring.c b/sys/base/gz/putstring.c new file mode 100644 index 0000000..64ff470 --- /dev/null +++ b/sys/base/gz/putstring.c @@ -0,0 +1,8 @@ +#include "internal.h" + +error +gz·putstring(gz·Stream *s, byte *str) +{ + return gzputs(s, str); +} + diff --git a/sys/base/gz/read.c b/sys/base/gz/read.c new file mode 100644 index 0000000..112fe4d --- /dev/null +++ b/sys/base/gz/read.c @@ -0,0 +1,16 @@ +#include "internal.h" + +int +gz·read(gz·Stream *s, int sz, int n, void* buf) +{ + return gzread(s, buf, n*sz); +} + +int +gz·readln(gz·Stream *s, int n, byte *buf) +{ + byte* b; + b = gzgets(s, buf, n); + + return strlen(b); +} diff --git a/sys/base/gz/rules.mk b/sys/base/gz/rules.mk new file mode 100644 index 0000000..a933291 --- /dev/null +++ b/sys/base/gz/rules.mk @@ -0,0 +1,11 @@ +SRCS_$(d)+=\ + $(d)/gz/flush.c\ + $(d)/gz/get.c\ + $(d)/gz/interface.c\ + $(d)/gz/open.c\ + $(d)/gz/printf.c\ + $(d)/gz/put.c\ + $(d)/gz/putstring.c\ + $(d)/gz/read.c\ + $(d)/gz/seek.c\ + $(d)/gz/write.c\ diff --git a/sys/base/gz/seek.c b/sys/base/gz/seek.c new file mode 100644 index 0000000..328886d --- /dev/null +++ b/sys/base/gz/seek.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +gz·seek(gz·Stream *s, long off, enum SeekPos whence) +{ + return gzseek(s, off, whence); +} diff --git a/sys/base/gz/write.c b/sys/base/gz/write.c new file mode 100644 index 0000000..862d833 --- /dev/null +++ b/sys/base/gz/write.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +gz·write(gz·Stream *s, int sz, int n, void* buf) +{ + return gzwrite(s, buf, n*sz); +} diff --git a/sys/base/io.c b/sys/base/io.c deleted file mode 100644 index 137b668..0000000 --- a/sys/base/io.c +++ /dev/null @@ -1,176 +0,0 @@ -#include <u.h> -#include <base.h> - -// ----------------------------------------------------------------------- -// interface implementations - -/* casting functions */ -static -int -·read(void *rdr, int size, int n, void *buf) -{ - return io·read((io·Stream *)rdr, size, n, buf); -} - -static -byte -·get(void *rdr) -{ - return io·getbyte((io·Stream *)rdr); -} - -static -error -·unget(void *rdr, byte c) -{ - return io·ungetbyte((io·Stream *)rdr, c); -} - -static -int -·write(void *wtr, int sz, int n, void *buf) -{ - return io·write((io·Stream *)wtr, sz, n, buf); -} - -static -error -·put(void *wtr, byte c) -{ - return io·putbyte((io·Stream *)wtr, c); -} - -static -int -·puts(void *wtr, string s) -{ - return io·putstring((io·Stream *)wtr, s); -} - -static -int -·seek(void *skr, long off, enum SeekPos whence) -{ - return io·seek((io·Stream *)skr, off, whence); -} - -static -long -·tell(void *skr) -{ - return io·tell((io·Stream *)skr); -} - -/* actual interfaces */ -io·Reader sys·Reader = (io·Reader){ ·read }; -io·Seeker sys·Seeker = (io·Seeker){ ·seek, ·tell }; -io·Peeker sys·Peeker = (io·Peeker){ ·get, ·unget }; -io·SeekReader sys·SeekReader = (io·SeekReader){ ·seek, ·tell, ·read }; -io·PeekReader sys·PeekReader = (io·PeekReader){ ·read, ·get, ·unget }; - -io·Writer sys·Writer = (io·Writer){ ·write }; -io·Putter sys·Putter = (io·Putter){ ·put, ·puts }; -io·PutWriter sys·PutWriter = (io·PutWriter){ ·write, ·put, ·puts }; - -io·ReadWriter sys·ReadWriter = (io·ReadWriter){ ·read, ·write }; - -// ----------------------------------------------------------------------- -// open/close - -io·Stream* -io·open(byte *name, byte *mode) -{ - return fopen(name, mode); -} - -int -io·fd(io·Stream *s) -{ - return fileno(s); -} - -error -io·stat(io·Stream *s, io·Stat *buf) -{ - return fstat(fileno(s), buf); -} - -error -io·close(io·Stream *s) -{ - return fclose(s); -} - -// ----------------------------------------------------------------------- -// reading - -byte -io·getbyte(io·Stream *s) -{ - return fgetc(s); -} - -error -io·ungetbyte(io·Stream *s, byte c) -{ - return ungetc(c, s); -} - -int -io·read(io·Stream *s, int sz, int n, void *buf) -{ - return fread(buf, sz, n, s); -} - -int -io·readln(io·Stream *s, int n, byte* buf) -{ - byte* b; - b = fgets(buf, n+1, s); - if(b == nil) - return -1; - - return strlen(buf); -} - -// ----------------------------------------------------------------------- -// writing - -error -io·putbyte(io·Stream *s, byte c) -{ - return fputc(c, s); -} - -int -io·putstring(io·Stream *s, string str) -{ - return fputs(str, s); -} - -int -io·write(io·Stream *s, int sz, int n, void *buf) -{ - return fwrite(buf, sz, n, s); -} - -int -io·flush(io·Stream *s) -{ - return fflush(s); -} - -// ----------------------------------------------------------------------- -// seek - -int -io·seek(io·Stream *s, long off, enum SeekPos origin) -{ - return fseek(s, off, origin); -} - -long -io·tell(io·Stream *s) -{ - return ftell(s); -} diff --git a/sys/base/io/fd.c b/sys/base/io/fd.c new file mode 100644 index 0000000..ded1b02 --- /dev/null +++ b/sys/base/io/fd.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·fd(io·Stream *s) +{ + return fileno(s); +} diff --git a/sys/base/io/flush.c b/sys/base/io/flush.c new file mode 100644 index 0000000..0f1217a --- /dev/null +++ b/sys/base/io/flush.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·flush(io·Stream *s) +{ + return fflush(s); +} diff --git a/sys/base/io/get.c b/sys/base/io/get.c new file mode 100644 index 0000000..d4e52f8 --- /dev/null +++ b/sys/base/io/get.c @@ -0,0 +1,7 @@ +#include "internal.h" + +byte +io·getbyte(io·Stream *s) +{ + return fgetc(s); +} diff --git a/sys/base/io/interface.c b/sys/base/io/interface.c new file mode 100644 index 0000000..bead9e1 --- /dev/null +++ b/sys/base/io/interface.c @@ -0,0 +1,70 @@ +#include "internal.h" + +static +int +·read(void *rdr, int size, int n, void *buf) +{ + return io·read((io·Stream *)rdr, size, n, buf); +} + +static +byte +·get(void *rdr) +{ + return io·getbyte((io·Stream *)rdr); +} + +static +error +·unget(void *rdr, byte c) +{ + return io·ungetbyte((io·Stream *)rdr, c); +} + +static +int +·write(void *wtr, int sz, int n, void *buf) +{ + return io·write((io·Stream *)wtr, sz, n, buf); +} + +static +error +·put(void *wtr, byte c) +{ + return io·putbyte((io·Stream *)wtr, c); +} + +static +int +·puts(void *wtr, string s) +{ + return io·putstring((io·Stream *)wtr, s); +} + +static +int +·seek(void *skr, long off, enum SeekPos whence) +{ + return io·seek((io·Stream *)skr, off, whence); +} + +static +long +·tell(void *skr) +{ + return io·tell((io·Stream *)skr); +} + +/* actual interfaces */ +io·Reader sys·Reader = (io·Reader){ ·read }; +io·Seeker sys·Seeker = (io·Seeker){ ·seek, ·tell }; +io·Peeker sys·Peeker = (io·Peeker){ ·get, ·unget }; +io·SeekReader sys·SeekReader = (io·SeekReader){ ·seek, ·tell, ·read }; +io·PeekReader sys·PeekReader = (io·PeekReader){ ·read, ·get, ·unget }; + +io·Writer sys·Writer = (io·Writer){ ·write }; +io·Putter sys·Putter = (io·Putter){ ·put, ·puts }; +io·PutWriter sys·PutWriter = (io·PutWriter){ ·write, ·put, ·puts }; + +io·ReadWriter sys·ReadWriter = (io·ReadWriter){ ·read, ·write }; diff --git a/sys/base/io/internal.h b/sys/base/io/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/sys/base/io/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include <u.h> +#include <base.h> diff --git a/sys/base/io/open.c b/sys/base/io/open.c new file mode 100644 index 0000000..e50e334 --- /dev/null +++ b/sys/base/io/open.c @@ -0,0 +1,13 @@ +#include "internal.h" + +io·Stream* +io·open(byte *name, byte *mode) +{ + return fopen(name, mode); +} + +error +io·close(io·Stream *s) +{ + return fclose(s); +} diff --git a/sys/base/io/putbyte.c b/sys/base/io/putbyte.c new file mode 100644 index 0000000..2350a8d --- /dev/null +++ b/sys/base/io/putbyte.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·putbyte(io·Stream *s, byte c) +{ + return fputc(c, s); +} diff --git a/sys/base/io/putstring.c b/sys/base/io/putstring.c new file mode 100644 index 0000000..53fa993 --- /dev/null +++ b/sys/base/io/putstring.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·putstring(io·Stream *s, string str) +{ + return fputs(str, s); +} diff --git a/sys/base/io/read.c b/sys/base/io/read.c new file mode 100644 index 0000000..b0ed3d2 --- /dev/null +++ b/sys/base/io/read.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·read(io·Stream *s, int sz, int n, void *buf) +{ + return fread(buf, sz, n, s); +} diff --git a/sys/base/io/readln.c b/sys/base/io/readln.c new file mode 100644 index 0000000..283472d --- /dev/null +++ b/sys/base/io/readln.c @@ -0,0 +1,12 @@ +#include "internal.h" + +int +io·readln(io·Stream *s, int n, byte* buf) +{ + byte* b; + b = fgets(buf, n+1, s); + if(b == nil) + return -1; + + return strlen(buf); +} diff --git a/sys/base/io/rules.mk b/sys/base/io/rules.mk new file mode 100644 index 0000000..2e03ca5 --- /dev/null +++ b/sys/base/io/rules.mk @@ -0,0 +1,14 @@ +SRCS_$(d)+=\ + $(d)/io/fd.c\ + $(d)/io/flush.c\ + $(d)/io/interface.c\ + $(d)/io/open.c\ + $(d)/io/putbyte.c\ + $(d)/io/putstring.c\ + $(d)/io/read.c\ + $(d)/io/readln.c\ + $(d)/io/seek.c\ + $(d)/io/stat.c\ + $(d)/io/tell.c\ + $(d)/io/unget.c\ + $(d)/io/write.c\ diff --git a/sys/base/io/seek.c b/sys/base/io/seek.c new file mode 100644 index 0000000..d0e7488 --- /dev/null +++ b/sys/base/io/seek.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·seek(io·Stream *s, long off, enum SeekPos origin) +{ + return fseek(s, off, origin); +} diff --git a/sys/base/io/stat.c b/sys/base/io/stat.c new file mode 100644 index 0000000..d86f1ee --- /dev/null +++ b/sys/base/io/stat.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +io·stat(io·Stream *s, io·Stat *buf) +{ + return fstat(fileno(s), buf); +} diff --git a/sys/base/io/tell.c b/sys/base/io/tell.c new file mode 100644 index 0000000..1c50439 --- /dev/null +++ b/sys/base/io/tell.c @@ -0,0 +1,7 @@ +#include "internal.h" + +long +io·tell(io·Stream *s) +{ + return ftell(s); +} diff --git a/sys/base/io/unget.c b/sys/base/io/unget.c new file mode 100644 index 0000000..5ec3536 --- /dev/null +++ b/sys/base/io/unget.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +io·ungetbyte(io·Stream *s, byte c) +{ + return ungetc(c, s); +} diff --git a/sys/base/io/write.c b/sys/base/io/write.c new file mode 100644 index 0000000..63df664 --- /dev/null +++ b/sys/base/io/write.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·write(io·Stream *s, int sz, int n, void *buf) +{ + return fwrite(buf, sz, n, s); +} diff --git a/sys/base/memory.c b/sys/base/mem/arena.c index a1a47c5..b2ce044 100644 --- a/sys/base/memory.c +++ b/sys/base/mem/arena.c @@ -1,85 +1,4 @@ -#include <u.h> -#include <base.h> - -static -void -·free(void *_, void *ptr) { - return free(ptr); -} - -static -void * -·alloc(void *_, uint n, ulong size) { - return malloc(n*size); -} - -static -void * -·calloc(void *_, uint n, ulong size) { - return calloc(n, size); -} - -static -void * -·realloc(void *_, void *ptr, uint n, ulong size) { - return realloc(ptr, n*size); -} - -mem·Allocator sys·Memory = { - .alloc = ·calloc, - .free = ·free -}; - - -// ------------------------------------------------------------------------- -// Dynamic buffer. - -/* Grow to particular size */ -void* -·bufgrow(void* buf, vlong newLen, vlong eltsize) -{ - assert(bufcap(buf) <= (SIZE_MAX - 1) / 2); - - vlong newCap = MAX(16, MAX(1 + 2 * bufcap(buf), newLen)); - - assert(newLen <= newCap); - assert(newCap <= (SIZE_MAX - offsetof(BufHdr, buf)) / eltsize); - - vlong newSize = offsetof(BufHdr, buf) + newCap * eltsize; - - BufHdr* newHdr; - if (buf) { - newHdr = bufhdr(buf); - newHdr = (BufHdr*)realloc((void*)newHdr, newSize); - } else { - newHdr = (BufHdr*)malloc(newSize); - newHdr->len = 0; - } - - newHdr->cap = newCap; - return (void*)newHdr->buf; -} - -/* Pop out a value */ -void -·bufdel(void *buf, int i, vlong eltsize) -{ - int n; - byte *b; - byte stk[1024]; - assert(eltsize < sizeof(stk)); - - b = (byte*)buf; - if(n = buflen(buf), i < n) { - memcpy(stk, b+eltsize*i, eltsize); - memcpy(b+eltsize*i, b+eltsize*(i+1), eltsize*(n-i-1)); - memcpy(b+eltsize*(n-1), stk, eltsize); - } - bufhdr(buf)->len--; -} - -// ------------------------------------------------------------------------- -// Arena allocator +#include "internal.h" #define ARENA_ALIGN 8 #define ARENA_BLOCK_SIZE 1024 * 1024 @@ -104,22 +23,28 @@ struct mem·Arena byte *end; struct Block *curr; struct Block first; -}; +}; -mem·Arena* -mem·makearena(mem·Allocator from, void *impl) +static +void* +·arenaalloc(void *heap, uint n, ulong size) { - mem·Arena *a = from.alloc(impl, 1, sizeof(*a) + ARENA_BLOCK_SIZE); - a->mem = from; - a->heap = impl; - a->off = a->first.buf; - a->end = a->first.buf + ARENA_BLOCK_SIZE; - a->curr = &a->first; - a->first.next = nil; + return mem·arenaalloc(heap, n, size); +} - return a; +static +void +·arenafree(void *heap, void *ptr) +{ + /* no-op */ } +mem·Allocator mem·ArenaAllocator = { + .alloc = ·arenaalloc, + .free = ·arenafree, +}; + + static void grow(mem·Arena *a, vlong min) @@ -139,29 +64,18 @@ grow(mem·Arena *a, vlong min) a->curr = blk; } -void* -mem·arenaalloc(mem·Arena *a, uint n, ulong size) +mem·Arena* +mem·makearena(mem·Allocator from, void *impl) { - if(!n) { - return nil; - } - - void *ptr; - // TODO(nnoll): check for overflow - size = n * size; - - if (size > (ulong)(a->end - a->off)) { - grow(a, size); - assert(size <= (uintptr)(a->end - a->off)); - } - - ptr = a->off; - a->off = ALIGN_UP_PTR(a->off + size, ARENA_ALIGN); - - assert(a->off <= a->end); - assert(ptr == ALIGN_DOWN_PTR(ptr, ARENA_ALIGN)); + mem·Arena *a = from.alloc(impl, 1, sizeof(*a) + ARENA_BLOCK_SIZE); + a->mem = from; + a->heap = impl; + a->off = a->first.buf; + a->end = a->first.buf + ARENA_BLOCK_SIZE; + a->curr = &a->first; + a->first.next = nil; - return ptr; + return a; } void @@ -179,38 +93,27 @@ mem·freearena(mem·Arena *a) a->mem.free(a->heap, a); } -static void* -·arenaalloc(void *heap, uint n, ulong size) -{ - return mem·arenaalloc(heap, n, size); -} - -static -void -·arenafree(void *heap, void *ptr) +mem·arenaalloc(mem·Arena *a, uint n, ulong size) { - /* no-op */ -} + if(!n) { + return nil; + } -mem·Allocator mem·ArenaAllocator = { - .alloc = ·arenaalloc, - .free = ·arenafree, -}; + void *ptr; + // TODO(nnoll): check for overflow + size = n * size; -// ------------------------------------------------------------------------- -// Generalized memory helpers + if (size > (ulong)(a->end - a->off)) { + grow(a, size); + assert(size <= (uintptr)(a->end - a->off)); + } -void -memset64(void *dst, uint64 val, uintptr size) -{ - intptr i; + ptr = a->off; + a->off = ALIGN_UP_PTR(a->off + size, ARENA_ALIGN); - for (i = 0; i < (size & (~7)); i += 8) { - memcpy((byte*)dst + i, &val, 8); - } + assert(a->off <= a->end); + assert(ptr == ALIGN_DOWN_PTR(ptr, ARENA_ALIGN)); - for (; i < size; i++) { - ((byte*)dst)[i] = ((byte*)&val)[i&7]; - } + return ptr; } diff --git a/sys/base/mem/buffer.c b/sys/base/mem/buffer.c new file mode 100644 index 0000000..b684d35 --- /dev/null +++ b/sys/base/mem/buffer.c @@ -0,0 +1,45 @@ +#include "internal.h" + +/* Grow to particular size */ +void* +·bufgrow(void* buf, vlong newLen, vlong eltsize) +{ + assert(bufcap(buf) <= (SIZE_MAX - 1) / 2); + + vlong newCap = MAX(16, MAX(1 + 2 * bufcap(buf), newLen)); + + assert(newLen <= newCap); + assert(newCap <= (SIZE_MAX - offsetof(BufHdr, buf)) / eltsize); + + vlong newSize = offsetof(BufHdr, buf) + newCap * eltsize; + + BufHdr* newHdr; + if (buf) { + newHdr = bufhdr(buf); + newHdr = (BufHdr*)realloc((void*)newHdr, newSize); + } else { + newHdr = (BufHdr*)malloc(newSize); + newHdr->len = 0; + } + + newHdr->cap = newCap; + return (void*)newHdr->buf; +} + +/* Pop out a value */ +void +·bufdel(void *buf, int i, vlong eltsize) +{ + int n; + byte *b; + byte stk[1024]; + assert(eltsize < sizeof(stk)); + + b = (byte*)buf; + if(n = buflen(buf), i < n) { + memcpy(stk, b+eltsize*i, eltsize); + memcpy(b+eltsize*i, b+eltsize*(i+1), eltsize*(n-i-1)); + memcpy(b+eltsize*(n-1), stk, eltsize); + } + bufhdr(buf)->len--; +} diff --git a/sys/base/mem/interface.c b/sys/base/mem/interface.c new file mode 100644 index 0000000..4d7d1ce --- /dev/null +++ b/sys/base/mem/interface.c @@ -0,0 +1,36 @@ +#include "internal.h" + +static +void +·free(void *_, void *ptr) { + return free(ptr); +} + +static +void * +·alloc(void *_, uint n, ulong size) { + return malloc(n*size); +} + +static +void * +·calloc(void *_, uint n, ulong size) { + return calloc(n, size); +} + +static +void * +·realloc(void *_, void *ptr, uint n, ulong size) { + return realloc(ptr, n*size); +} + +mem·Allocator sys·Memory = { + .alloc = ·calloc, + .free = ·free +}; + +mem·Reallocator sys·FullMemory = { + .alloc = ·calloc, + .realloc = ·realloc, + .free = ·free +}; diff --git a/sys/base/mem/internal.h b/sys/base/mem/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/sys/base/mem/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include <u.h> +#include <base.h> diff --git a/sys/base/mem/rules.mk b/sys/base/mem/rules.mk new file mode 100644 index 0000000..b912d0c --- /dev/null +++ b/sys/base/mem/rules.mk @@ -0,0 +1,5 @@ +SRCS_$(d)+=\ + $(d)/mem/arena.c\ + $(d)/mem/buffer.c\ + $(d)/mem/interface.c\ + $(d)/mem/set64.c\ diff --git a/sys/base/mem/set64.c b/sys/base/mem/set64.c new file mode 100644 index 0000000..464b3ad --- /dev/null +++ b/sys/base/mem/set64.c @@ -0,0 +1,13 @@ +#include "internal.h" + +void +mem·set64(void *dst, uint64 val, uintptr size) +{ + intptr i; + + for(i = 0; i < (size & (~7)); i += 8) + memcpy((byte*)dst + i, &val, 8); + + for(; i < size; i++) + ((byte*)dst)[i] = ((byte*)&val)[i&7]; +} diff --git a/sys/base/mmap/internal.h b/sys/base/mmap/internal.h new file mode 100644 index 0000000..7606c7e --- /dev/null +++ b/sys/base/mmap/internal.h @@ -0,0 +1,5 @@ +#pragma once + +#include <u.h> +#include <base.h> +#include <sys/mman.h> diff --git a/sys/base/mmap.c b/sys/base/mmap/mmap.c index 7f3062d..ce3011c 100644 --- a/sys/base/mmap.c +++ b/sys/base/mmap/mmap.c @@ -1,7 +1,4 @@ -#include <u.h> -#include <base.h> - -#include <sys/mman.h> +#include "internal.h" mmap·Reader mmap·open(byte *filename) diff --git a/sys/base/mmap/rules.mk b/sys/base/mmap/rules.mk new file mode 100644 index 0000000..fb3cab5 --- /dev/null +++ b/sys/base/mmap/rules.mk @@ -0,0 +1,2 @@ +SRCS_$(d)+=\ + $(d)/mmap/mmap.c\ diff --git a/sys/base/os.c b/sys/base/os.c deleted file mode 100644 index a76e183..0000000 --- a/sys/base/os.c +++ /dev/null @@ -1,30 +0,0 @@ -#include <u.h> -#include <base.h> - -int -os·exists(byte *path, int flag) -{ - return access(path, flag) == 0; -} - -int -os·sep(void) -{ -#if defined(UNIX) || defined(__linux__) - return '/'; -#elif defined(WIN32) - return '\\'; -#else - panicf("unrecognized operating system"); - return '\0'; -#endif -} - -byte* -os·basename(byte *path) -{ - byte *sep; - - sep = strrchr(path, os·sep()); - return (sep == nil) ? path : sep+1; -} diff --git a/sys/base/os/basename.c b/sys/base/os/basename.c new file mode 100644 index 0000000..b5bb343 --- /dev/null +++ b/sys/base/os/basename.c @@ -0,0 +1,10 @@ +#include "internal.h" + +char* +os·basename(char *path) +{ + char *sep; + + sep = strrchr(path, os·sep()); + return (sep == nil) ? path : sep+1; +} diff --git a/sys/base/os/exists.c b/sys/base/os/exists.c new file mode 100644 index 0000000..a3c8935 --- /dev/null +++ b/sys/base/os/exists.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +os·exists(byte *path, int flag) +{ + return access(path, flag) == 0; +} diff --git a/sys/base/os/internal.h b/sys/base/os/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/sys/base/os/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include <u.h> +#include <base.h> diff --git a/sys/base/os/rules.mk b/sys/base/os/rules.mk new file mode 100644 index 0000000..bf1e71d --- /dev/null +++ b/sys/base/os/rules.mk @@ -0,0 +1,4 @@ +SRCS_$(d)+=\ + $(d)/os/basename.c\ + $(d)/os/exists.c\ + $(d)/os/sep.c\ diff --git a/sys/base/os/sep.c b/sys/base/os/sep.c new file mode 100644 index 0000000..750e627 --- /dev/null +++ b/sys/base/os/sep.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +os·sep(void) +{ +#if defined(UNIX) || defined(__linux__) + return '/'; +#elif defined(WIN32) + return '\\'; +#else + panicf("unrecognized operating system"); + return '\0'; +#endif +} diff --git a/sys/base/random.c b/sys/base/random.c deleted file mode 100644 index 16a8737..0000000 --- a/sys/base/random.c +++ /dev/null @@ -1,303 +0,0 @@ -#include <u.h> -#include <base.h> - -// ---------------------------------------------------------------------------- -// Internal structure - -uint64 -rol64(uint64 x, int k) -{ - return (x << k) | (x >> (64 - k)); -} - -typedef struct Rng { - uint64 s[4]; -} Rng; - -uint64 -xoshiro256ss(Rng *state) -{ - uint64 *s = state->s; - uint64 result = rol64(s[1] * 5, 7) * 9; - uint64 t = s[1] << 17; - - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; - - s[2] ^= t; - s[3] = rol64(s[3], 45); - - return result; -} - -typedef struct Mix -{ - uint64 s; -} Mix; - -uint64 -splitmix64(struct Mix *state) { - uint64 result = state->s; - - state->s = result + 0x9E3779B97f4A7C15; - result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; - result = (result ^ (result >> 27)) * 0x94D049BB133111EB; - return result ^ (result >> 31); -} - -static Rng RNG; - -// ---------------------------------------------------------------------------- -// Exported functions - -/* Initializes the global RNG */ - -error -rng·init(uint64 seed) -{ - Mix smstate = {seed}; - - for (int i=0; i < 4; i++) - RNG.s[i] = splitmix64(&smstate); - - return 0; -} - -/* Returns a random float64 between 0 and 1 */ -double -rng·random(void) -{ - uint64 r = xoshiro256ss(&RNG); - return (double)r / (double)UINT64_MAX; -} - -double -rng·exponential(double lambda) -{ - double f; - - f = rng·random(); - return -log(1 - f)/lambda; -} - -static inline -double -erfinv(double x) -{ - /* useful constants */ - static double - a0 = 1.1975323115670912564578e0, a1 = 4.7072688112383978012285e1, - a2 = 6.9706266534389598238465e2, a3 = 4.8548868893843886794648e3, - a4 = 1.6235862515167575384252e4, a5 = 2.3782041382114385731252e4, - a6 = 1.1819493347062294404278e4, a7 = 8.8709406962545514830200e2, - - b0 = 1.0000000000000000000e0, b1 = 4.2313330701600911252e1, - b2 = 6.8718700749205790830e2, b3 = 5.3941960214247511077e3, - b4 = 2.1213794301586595867e4, b5 = 3.9307895800092710610e4, - b6 = 2.8729085735721942674e4, b7 = 5.2264952788528545610e3, - - c0 = 1.42343711074968357734e0, c1 = 4.63033784615654529590e0, - c2 = 5.76949722146069140550e0, c3 = 3.64784832476320460504e0, - c4 = 1.27045825245236838258e0, c5 = 2.41780725177450611770e-1, - c6 = 2.27238449892691845833e-2, c7 = 7.74545014278341407640e-4, - - d0 = 1.4142135623730950488016887e0, d1 = 2.9036514445419946173133295e0, - d2 = 2.3707661626024532365971225e0, d3 = 9.7547832001787427186894837e-1, - d4 = 2.0945065210512749128288442e-1, d5 = 2.1494160384252876777097297e-2, - d6 = 7.7441459065157709165577218e-4, d7 = 1.4859850019840355905497876e-9, - - e0 = 6.65790464350110377720e0, e1 = 5.46378491116411436990e0, - e2 = 1.78482653991729133580e0, e3 = 2.96560571828504891230e-1, - e4 = 2.65321895265761230930e-2, e5 = 1.24266094738807843860e-3, - e6 = 2.71155556874348757815e-5, e7 = 2.01033439929228813265e-7, - - f0 = 1.414213562373095048801689e0, f1 = 8.482908416595164588112026e-1, - f2 = 1.936480946950659106176712e-1, f3 = 2.103693768272068968719679e-2, - f4 = 1.112800997078859844711555e-3, f5 = 2.611088405080593625138020e-5, - f6 = 2.010321207683943062279931e-7, f7 = 2.891024605872965461538222e-15, - - Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009; - - int s; - double r, z1, z2; - - if(x < 0) { - s = -1; - x = -x; - } else { - s = +1; - } - - if(x <= 0.85) { - r = 0.180625 - 0.25*x*x; - z1 = ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0; - z2 = ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0; - return s*(x*z1) / z2; - } - r = sqrt(Ln2 - log(1.0-x)); - if(r <= 5.0) { - r -= 1.6; - z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0; - z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0; - } else { - r -= 5.0; - z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0; - z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0; - } - - return s*z1/z2; -} - - -double -rng·normal(void) -{ - double f; - f = rng·random(); - - return sqrt(2)*erfinv(2*f-1); -} - -/* Returns true or false on success of trial */ -bool -rng·bernoulli(double f) -{ - return rng·random() < f; -} - -/* Returns a random integer between 0 and max - * TODO: Modulo throws off uniformity - */ -uint64 -rng·randi(int max) -{ - uint64 r = xoshiro256ss(&RNG); - return r % max; -} - -/* - * Ahrens, J. H., & Dieter, U. (1982). - * Computer Generation of Poisson Deviates from Modified Normal Distributions. - */ -static double factorial[10] = {1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.}; -static double coeffs[9] = { - -.500000000, +.333333333, -.249999856, - +.200011780, -.166684875, +.142187833, - -.124196313, +.125005956, -.114265030, -}; - -static inline -double -log1pmx(double x, double off) -{ - int i; - double r, t; - - if(-0.25 < x && x < 0.25) { - r = 0; - t = 1; - for(i=0;i<arrlen(coeffs);i++) { - r += coeffs[i]*t; - t *= x; - } - - return x*x*r; - } - return log(1+x) - off; -} - -static inline -double -procf(double mu, double s, int64 K, double *px, double *py, double *fx, double *fy) -{ - double d, V, X; - double w, b1, b2, c1, c2, c3, c0, c; - - w = 0.3989422804014327/s; - b1 = 0.041666666666666664/mu; - b2 = 0.3*b1*b1; - c3 = 0.14285714285714285*b1*b2; - c2 = b2 - 15.*c3; - c1 = b1 - 6.*b2 + 45.*c3; - c0 = 1 - b1 + 3.*b2 - 15.*c3; - c = .1069/mu; - - if(K < 10) { - *px = -mu; - *py = pow(mu,K) / factorial[K]; - }else{ - d = 0.08333333333333333/K; - d = d - 4.8*d*d*d; - V = (mu - K) / K; - - *px = K*log1pmx(V,mu-K) - d; - *py = 0.3989422804014327/sqrt(K); - } - - X = (K - mu + 0.5)/s; - *fx = -0.5*X*X; - *fy = w*(((c3*X*X + c2)*X*X + c1)*X*X + c0); - - return c; -} - -static inline -uint64 -bigpoisson(double mu) -{ - int64 L,K; - double G,s,d,U,E,T; - double px,py,fx,fy,c; - - s = sqrt(mu); - d = 6*mu*mu; - L = floor(mu - 1.1484); - -stepN: - G = mu + s*rng·normal(); - K = floor(G); - if(K<0) - goto stepP; -stepI: - if(K>=L) - return K; -stepS: - U = rng·random(); - if(d*U >= (mu-K)*(mu-K)*(mu-K)) - return K; -stepP: - if(G < 0) - goto stepE; -stepQ: - c = procf(mu, s, K, &px, &py, &fx, &fy); -stepE: - E = rng·exponential(1.0); - U = rng·random(); - U = U + U - 1; - T = 1.8 + copysign(E,U); - if(T < 0.6744) - goto stepE; - K = floor(mu + s*T); - c = procf(mu, s, K, &px, &py, &fx, &fy); -stepH: - if(c*fabs(U) > (py*exp(px + E) - fy*exp(fx + E))) - goto stepE; - return K; -} - -uint64 -rng·poisson(double mean) -{ - int64 n; - double z; - - if(mean<10.0) { - for(n=0, z=rng·exponential(1.0); z<mean; ++n, z+=rng·exponential(1.0)) - ; - return n; - } - - return bigpoisson(mean); -} diff --git a/sys/base/rng/base.c b/sys/base/rng/base.c new file mode 100644 index 0000000..9ec496e --- /dev/null +++ b/sys/base/rng/base.c @@ -0,0 +1,24 @@ +#include "internal.h" + +static uint64 +splitmix64(struct Mix *state) +{ + uint64 result = state->s; + + state->s = result + 0x9E3779B97f4A7C15; + result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; + result = (result ^ (result >> 27)) * 0x94D049BB133111EB; + return result ^ (result >> 31); +} + +int +rng·init(uint64 seed) +{ + int i; + Mix smstate = {seed}; + + for(i=0; i < 4; i++) + rng·RNG.s[i] = splitmix64(&smstate); + + return 0; +} diff --git a/sys/base/rng/bernoulli.c b/sys/base/rng/bernoulli.c new file mode 100644 index 0000000..02f531e --- /dev/null +++ b/sys/base/rng/bernoulli.c @@ -0,0 +1,7 @@ +#include "internal.h" + +bool +rng·bernoulli(double f) +{ + return rng·random() < f; +} diff --git a/sys/base/rng/exponential.c b/sys/base/rng/exponential.c new file mode 100644 index 0000000..c07e007 --- /dev/null +++ b/sys/base/rng/exponential.c @@ -0,0 +1,11 @@ +#include "internal.h" + +/* Returns a random float64 between 0 and 1 */ +double +rng·exponential(double lambda) +{ + double f; + + f = rng·random(); + return -log(1 - f)/lambda; +} diff --git a/sys/base/rng/internal.h b/sys/base/rng/internal.h new file mode 100644 index 0000000..9cf5f41 --- /dev/null +++ b/sys/base/rng/internal.h @@ -0,0 +1,19 @@ +#pragma once + +#include <u.h> +#include <base.h> + +#define rol64(x, k) ((x) << (k) | ((x) >> (64-(k)))) + +typedef struct Rng +{ + uint64 s[4]; +} Rng; + +typedef struct Mix +{ + uint64 s; +} Mix; + + +extern Rng rng·RNG; diff --git a/sys/base/rng/normal.c b/sys/base/rng/normal.c new file mode 100644 index 0000000..aab5731 --- /dev/null +++ b/sys/base/rng/normal.c @@ -0,0 +1,77 @@ +#include "internal.h" + +static inline double +erfinv(double x) +{ + /* useful constants */ + static double + a0 = 1.1975323115670912564578e0, a1 = 4.7072688112383978012285e1, + a2 = 6.9706266534389598238465e2, a3 = 4.8548868893843886794648e3, + a4 = 1.6235862515167575384252e4, a5 = 2.3782041382114385731252e4, + a6 = 1.1819493347062294404278e4, a7 = 8.8709406962545514830200e2, + + b0 = 1.0000000000000000000e0, b1 = 4.2313330701600911252e1, + b2 = 6.8718700749205790830e2, b3 = 5.3941960214247511077e3, + b4 = 2.1213794301586595867e4, b5 = 3.9307895800092710610e4, + b6 = 2.8729085735721942674e4, b7 = 5.2264952788528545610e3, + + c0 = 1.42343711074968357734e0, c1 = 4.63033784615654529590e0, + c2 = 5.76949722146069140550e0, c3 = 3.64784832476320460504e0, + c4 = 1.27045825245236838258e0, c5 = 2.41780725177450611770e-1, + c6 = 2.27238449892691845833e-2, c7 = 7.74545014278341407640e-4, + + d0 = 1.4142135623730950488016887e0, d1 = 2.9036514445419946173133295e0, + d2 = 2.3707661626024532365971225e0, d3 = 9.7547832001787427186894837e-1, + d4 = 2.0945065210512749128288442e-1, d5 = 2.1494160384252876777097297e-2, + d6 = 7.7441459065157709165577218e-4, d7 = 1.4859850019840355905497876e-9, + + e0 = 6.65790464350110377720e0, e1 = 5.46378491116411436990e0, + e2 = 1.78482653991729133580e0, e3 = 2.96560571828504891230e-1, + e4 = 2.65321895265761230930e-2, e5 = 1.24266094738807843860e-3, + e6 = 2.71155556874348757815e-5, e7 = 2.01033439929228813265e-7, + + f0 = 1.414213562373095048801689e0, f1 = 8.482908416595164588112026e-1, + f2 = 1.936480946950659106176712e-1, f3 = 2.103693768272068968719679e-2, + f4 = 1.112800997078859844711555e-3, f5 = 2.611088405080593625138020e-5, + f6 = 2.010321207683943062279931e-7, f7 = 2.891024605872965461538222e-15, + + Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009; + + int s; + double r, z1, z2; + + if(x < 0) { + s = -1; + x = -x; + } else { + s = +1; + } + + if(x <= 0.85) { + r = 0.180625 - 0.25*x*x; + z1 = ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0; + z2 = ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0; + return s*(x*z1) / z2; + } + r = sqrt(Ln2 - log(1.0-x)); + if(r <= 5.0) { + r -= 1.6; + z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0; + z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0; + } else { + r -= 5.0; + z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0; + z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0; + } + + return s*z1/z2; +} + +double +rng·normal(void) +{ + double f; + f = rng·random(); + + return sqrt(2)*erfinv(2*f-1); +} diff --git a/sys/base/rng/poisson.c b/sys/base/rng/poisson.c new file mode 100644 index 0000000..3ec15c9 --- /dev/null +++ b/sys/base/rng/poisson.c @@ -0,0 +1,126 @@ +#include "internal.h" + +/* + * Ahrens, J. H., & Dieter, U. (1982). + * Computer Generation of Poisson Deviates from Modified Normal Distributions. + */ +static double factorial[10] = {1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.}; +static double coeffs[9] = { + -.500000000, +.333333333, -.249999856, + +.200011780, -.166684875, +.142187833, + -.124196313, +.125005956, -.114265030, +}; + +static inline +double +log1pmx(double x, double off) +{ + int i; + double r, t; + + if(-0.25 < x && x < 0.25) { + r = 0; + t = 1; + for(i=0;i<arrlen(coeffs);i++) { + r += coeffs[i]*t; + t *= x; + } + + return x*x*r; + } + return log(1+x) - off; +} + +static inline +double +procf(double mu, double s, int64 K, double *px, double *py, double *fx, double *fy) +{ + double d, V, X; + double w, b1, b2, c1, c2, c3, c0, c; + + w = 0.3989422804014327/s; + b1 = 0.041666666666666664/mu; + b2 = 0.3*b1*b1; + c3 = 0.14285714285714285*b1*b2; + c2 = b2 - 15.*c3; + c1 = b1 - 6.*b2 + 45.*c3; + c0 = 1 - b1 + 3.*b2 - 15.*c3; + c = .1069/mu; + + if(K < 10) { + *px = -mu; + *py = pow(mu,K) / factorial[K]; + }else{ + d = 0.08333333333333333/K; + d = d - 4.8*d*d*d; + V = (mu - K) / K; + + *px = K*log1pmx(V,mu-K) - d; + *py = 0.3989422804014327/sqrt(K); + } + + X = (K - mu + 0.5)/s; + *fx = -0.5*X*X; + *fy = w*(((c3*X*X + c2)*X*X + c1)*X*X + c0); + + return c; +} + +static inline +uint64 +bigpoisson(double mu) +{ + int64 L,K; + double G,s,d,U,E,T; + double px,py,fx,fy,c; + + s = sqrt(mu); + d = 6*mu*mu; + L = floor(mu - 1.1484); + +stepN: + G = mu + s*rng·normal(); + K = floor(G); + if(K<0) + goto stepP; +stepI: + if(K>=L) + return K; +stepS: + U = rng·random(); + if(d*U >= (mu-K)*(mu-K)*(mu-K)) + return K; +stepP: + if(G < 0) + goto stepE; +stepQ: + c = procf(mu, s, K, &px, &py, &fx, &fy); +stepE: + E = rng·exponential(1.0); + U = rng·random(); + U = U + U - 1; + T = 1.8 + copysign(E,U); + if(T < 0.6744) + goto stepE; + K = floor(mu + s*T); + c = procf(mu, s, K, &px, &py, &fx, &fy); +stepH: + if(c*fabs(U) > (py*exp(px + E) - fy*exp(fx + E))) + goto stepE; + return K; +} + +uint64 +rng·poisson(double mean) +{ + int64 n; + double z; + + if(mean<10.0) { + for(n=0, z=rng·exponential(1.0); z<mean; ++n, z+=rng·exponential(1.0)) + ; + return n; + } + + return bigpoisson(mean); +} diff --git a/sys/base/rng/random.c b/sys/base/rng/random.c new file mode 100644 index 0000000..bd1bd6b --- /dev/null +++ b/sys/base/rng/random.c @@ -0,0 +1,33 @@ +#include "internal.h" + +static uint64 +xoshiro256ss(Rng *state) +{ + uint64 *s = state->s; + uint64 result = rol64(s[1] * 5, 7) * 9; + uint64 t = s[1] << 17; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + + s[2] ^= t; + s[3] = rol64(s[3], 45); + + return result; +} + +double +rng·random(void) +{ + uint64 r = xoshiro256ss(&rng·RNG); + return (double)r / (double)UINT64_MAX; +} + +uint64 +rng·randi(int max) +{ + uint64 r = xoshiro256ss(&rng·RNG); + return r % max; +} diff --git a/sys/base/rng/rules.mk b/sys/base/rng/rules.mk new file mode 100644 index 0000000..407b1bf --- /dev/null +++ b/sys/base/rng/rules.mk @@ -0,0 +1,7 @@ +SRCS_$(d)+=\ + $(d)/rng/base.c\ + $(d)/rng/bernoulli.c\ + $(d)/rng/exponential.c\ + $(d)/rng/normal.c\ + $(d)/rng/poisson.c\ + $(d)/rng/random.c\ diff --git a/sys/base/rules.mk b/sys/base/rules.mk index 1300f4c..1726aa3 100644 --- a/sys/base/rules.mk +++ b/sys/base/rules.mk @@ -2,26 +2,24 @@ include share/push.mk # Iterate through subdirectory tree -# Local sources -SRCS_$(d) := \ - $(d)/arg.c \ - $(d)/bufio.c \ - $(d)/coro_unix_x64.s \ - $(d)/coro.c \ - $(d)/error.c \ - $(d)/flate.c \ - $(d)/fs.c \ - $(d)/gz.c \ - $(d)/io.c \ - $(d)/os.c \ - $(d)/memory.c \ - $(d)/mmap.c \ - $(d)/random.c \ - $(d)/sort.c \ - $(d)/string.c - -TSTS_$(d) := \ - $(d)/test.c +# Local sources +SRCS_$(d) := $(d)/arg.c +include $(d)/bufio/rules.mk +include $(d)/coro/rules.mk +include $(d)/error/rules.mk +include $(d)/flate/rules.mk +include $(d)/fs/rules.mk +include $(d)/gz/rules.mk +include $(d)/io/rules.mk +include $(d)/mem/rules.mk +include $(d)/mmap/rules.mk +include $(d)/os/rules.mk +include $(d)/rng/rules.mk +include $(d)/sort/rules.mk +include $(d)/string/rules.mk + + +TSTS_$(d) := $(d)/test.c LIBS_$(d) := $(d)/base.a BINS_$(d) := diff --git a/sys/base/sort.c b/sys/base/sort.c deleted file mode 100644 index 1143494..0000000 --- a/sys/base/sort.c +++ /dev/null @@ -1,155 +0,0 @@ -#include <u.h> -#include <base.h> -#include <base/macro/qsort.h> - -// ----------------------------------------------------------------------- -// ints - -void -sort·ints(uintptr sz, int arr[]) -{ - int tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·int8s(uintptr sz, int8 arr[]) -{ - int8 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·int16s(uintptr sz, int16 arr[]) -{ - int16 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·int32s(uintptr sz, int32 arr[]) -{ - int32 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·int64s(uintptr sz, int64 arr[]) -{ - int64 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·uints(uintptr sz, uint arr[]) -{ - uint tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·uint8s(uintptr sz, uint8 arr[]) -{ - uint8 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·uint16s(uintptr sz, uint16 arr[]) -{ - uint16 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·uint32s(uintptr sz, uint32 arr[]) -{ - uint32 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·uint64s(uintptr sz, uint64 arr[]) -{ - uint64 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -// ----------------------------------------------------------------------- -// floats - -void -sort·floats(uintptr sz, float arr[]) -{ - float tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -void -sort·doubles(uintptr sz, double arr[]) -{ - double tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} - -// ----------------------------------------------------------------------- -// strings - -void -sort·strings(uintptr sz, byte* arr[]) -{ - byte *tmp; -#define LESS(i, j) (strcmp(arr[i], arr[j]) < 0) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/double.c b/sys/base/sort/double.c new file mode 100644 index 0000000..c3feac2 --- /dev/null +++ b/sys/base/sort/double.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·double(uintptr sz, double arr[]) +{ + double tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/float.c b/sys/base/sort/float.c new file mode 100644 index 0000000..57bd482 --- /dev/null +++ b/sys/base/sort/float.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·float(uintptr sz, float arr[]) +{ + float tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/int.c b/sys/base/sort/int.c new file mode 100644 index 0000000..33e1def --- /dev/null +++ b/sys/base/sort/int.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int(uintptr sz, int arr[]) +{ + int tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/int16.c b/sys/base/sort/int16.c new file mode 100644 index 0000000..072a3eb --- /dev/null +++ b/sys/base/sort/int16.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int16(uintptr sz, int16 arr[]) +{ + int16 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/int32.c b/sys/base/sort/int32.c new file mode 100644 index 0000000..27b3b7b --- /dev/null +++ b/sys/base/sort/int32.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int32(uintptr sz, int32 arr[]) +{ + int32 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/int64.c b/sys/base/sort/int64.c new file mode 100644 index 0000000..b3fa5d4 --- /dev/null +++ b/sys/base/sort/int64.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int64(uintptr sz, int64 arr[]) +{ + int64 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/int8.c b/sys/base/sort/int8.c new file mode 100644 index 0000000..5848e6e --- /dev/null +++ b/sys/base/sort/int8.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int8(uintptr sz, int8 arr[]) +{ + int8 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/internal.h b/sys/base/sort/internal.h new file mode 100644 index 0000000..ac569de --- /dev/null +++ b/sys/base/sort/internal.h @@ -0,0 +1,5 @@ +#pragma once + +#include <u.h> +#include <base.h> +#include <base/macro/qsort.h> diff --git a/sys/base/sort/rules.mk b/sys/base/sort/rules.mk new file mode 100644 index 0000000..780d6ea --- /dev/null +++ b/sys/base/sort/rules.mk @@ -0,0 +1,14 @@ +SRCS_$(d)+=\ + $(d)/sort/double.c\ + $(d)/sort/float.c\ + $(d)/sort/int.c\ + $(d)/sort/int16.c\ + $(d)/sort/int32.c\ + $(d)/sort/int64.c\ + $(d)/sort/int8.c\ + $(d)/sort/string.c\ + $(d)/sort/uint.c\ + $(d)/sort/uint16.c\ + $(d)/sort/uint32.c\ + $(d)/sort/uint64.c\ + $(d)/sort/uint8.c\ diff --git a/sys/base/sort/string.c b/sys/base/sort/string.c new file mode 100644 index 0000000..b511efa --- /dev/null +++ b/sys/base/sort/string.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·string(uintptr sz, byte* arr[]) +{ + byte *tmp; +#define LESS(i, j) (strcmp(arr[i], arr[j]) < 0) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/uint.c b/sys/base/sort/uint.c new file mode 100644 index 0000000..5b27330 --- /dev/null +++ b/sys/base/sort/uint.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint(uintptr sz, uint arr[]) +{ + uint tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/uint16.c b/sys/base/sort/uint16.c new file mode 100644 index 0000000..2b635b4 --- /dev/null +++ b/sys/base/sort/uint16.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint16(uintptr sz, uint16 arr[]) +{ + uint16 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/uint32.c b/sys/base/sort/uint32.c new file mode 100644 index 0000000..99a58cf --- /dev/null +++ b/sys/base/sort/uint32.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint32(uintptr sz, uint32 arr[]) +{ + uint32 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/uint64.c b/sys/base/sort/uint64.c new file mode 100644 index 0000000..2769825 --- /dev/null +++ b/sys/base/sort/uint64.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint64(uintptr sz, uint64 arr[]) +{ + uint64 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/sort/uint8.c b/sys/base/sort/uint8.c new file mode 100644 index 0000000..ff02b3c --- /dev/null +++ b/sys/base/sort/uint8.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint8(uintptr sz, uint8 arr[]) +{ + uint8 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/sys/base/string.c b/sys/base/string.c deleted file mode 100644 index 1ef9068..0000000 --- a/sys/base/string.c +++ /dev/null @@ -1,386 +0,0 @@ -#include <u.h> -#include <base.h> - -#define MAX_STRING_ALLOC 1024 * 1024 - -typedef struct Hdr -{ - vlong len; - vlong cap; - byte buf[]; -} Hdr; - -// ------------------------------------------------------------------------- -// Dynamic string functions - -// New returns a new dynamic string object, initialized from the given C string. -// len defines the length of the C substring that we will copy into our buffer. -// The backing buffer will have capacity cap. -string -str·makecap(const byte *s, vlong len, vlong cap) -{ - struct Hdr* h; - - h = malloc(sizeof(*h) + cap + 1); - if (s == nil) memset(h, 0, sizeof(*h)); - - if (h == nil) return nil; // Allocation failed. - - h->len = (s == nil) ? 0 : len; - h->cap = cap; - - if (cap < h->len) goto cleanup; - - if (s != nil && cap > 0) { - memcpy(h->buf, s, h->len); - memset(h->buf + h->len, '\0', h->cap - h->len + 1); - } - - return h->buf; - -cleanup: - free(h); - panicf("Attempted to create a string with less capacity than length"); - return nil; -} - -// New returns a new dynamic string object, initialized from the given C string. -// The backing buffer capacity is equivalent to the string length. -string -str·makelen(const byte *s, vlong len) -{ - vlong sl = (!s) ? 0 : strlen(s); - if (sl < len) panicf("attempted to take a bigger substring than string length"); - - vlong cap = (len == 0) ? 1 : len; - return str·makecap(s, len, cap); -} - -// New returns a new dynamic string object, initialized from the given C string. -// The backing buffer capacity is equivalent to the string length. -string -str·make(const byte *s) -{ - vlong len = (!s) ? 0 : strlen(s); - return str·makelen(s, len); -} - -// Newf returns a new dynamic string object -string -str·makef(const byte *fmt, ...) -{ - vlong n; - string s; - va_list args; - - va_start(args, fmt); - n = vsnprintf(nil, 0, fmt, args); - va_end(args); - - s = str·makecap(nil, 0, n); - - va_start(args, fmt); - vsnprintf(s, n + 1, fmt, args); - va_end(args); - - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - h->len = n; - - return s; -} - -// Free returns memory associated to the buffer. -void -str·free(string s) -{ - free(s - sizeof(Hdr)); -} - -// Len returns the length of the string. -int -str·len(const string s) -{ - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - return h->len; -} - -// Cap returns the capacity of the string buffer. -int -str·cap(const string s) -{ - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - return h->cap; -} - -void -str·clear(string *s) -{ - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - h->len = 0; - *s[0] = '\0'; -} - -// Grow ensures that the string can encompass AT LEAST delta bytes. -// If it already can, this is a NO OP. -// If it can't, the string will be reallocated. -void -str·grow(string *s, vlong delta) -{ - Hdr *h, *newh; - vlong cap = str·cap(*s); - vlong len = str·len(*s); - assert(cap >= len); // To prevent unsigned behavior - - if (cap - len >= delta) return; - - h = (Hdr*)(*s - sizeof(Hdr)); - - vlong newCap = cap + delta; - assert(newCap >= cap); // To prevent unsigned behavior - if (newCap < MAX_STRING_ALLOC) { - newCap *= 2; - } else - newCap += MAX_STRING_ALLOC; - - newh = (Hdr*)realloc(h, sizeof(*h) + newCap + 1); - if (newh == nil) return; - - memset(newh->buf + len, '\0', newCap - len); - newh->cap = newCap; - newh->len = len; - - *s = newh->buf; -} - -// Fit reallocates the string such that the buffer is exactly sized for the -// buffer. If the capacity equals the length, then the function is a NOOP. The -// byte array is unchanged. -void -str·fit(string *s) -{ - Hdr* h; - vlong cap = str·cap(*s); - vlong len = str·len(*s); - - if (cap == len) return; - - h = (Hdr*)(s - sizeof(Hdr)); - h = realloc(h, sizeof(*h) + len + 1); - h->cap = len; - - *s = h->buf; -} - -// Append will append the given null terminated C string to the string data -// structure. This variant can append a substring of length len of the given -// string to our buffer. The result is reallocated if not enough room is present -// in the buffer. -int -str·appendlen(string *s, vlong n, const byte* b) -{ - /* - bl = strlen(b); - if (n > bl) panicf("attempted to make a substring longer than string"); - */ - - str·grow(s, n); - if (*s == nil) return 0; - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - - memcpy(*s + str·len(*s), b, n); - h->len += n; - (*s)[h->len] = '\0'; - - return n; -} - -// Append will append the given null terminated C string to the string data -// structure. This variant will append the entire string. -int -str·append(string *s, const byte* b) -{ - return str·appendlen(s, strlen(b), b); -} - -// AppendByte will append the given byte to our string. -// NOTE: As the byte is on the stack, it is not null-terminated. -// Can not pass to the above functions. -int -str·appendbyte(string *s, const byte b) -{ - str·grow(s, 1); - if (*s == nil) return 0; - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - - *(*s + str·len(*s)) = b; - h->len++; - (*s)[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..? - - return 1; -} - -/* - * Appendf will append the given formatted string to our buffer. - * Returns the newly minted string - */ - -int -str·appendf(string *s, const byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int remain = str·cap(*s) - str·len(*s); - int n = vsnprintf(*s + str·len(*s), remain + 1, fmt, args); - va_end(args); - - if (n > remain) { - // If the first write was incomplete, we overwite the data again. - str·grow(s, n); - va_list args; - va_start(args, fmt); - n = vsnprintf(*s + str·len(*s), n + 1, fmt, args); - assert(n - remain <= str·cap(*s)); - va_end(args); - } - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - h->len += n; - - return n; -} - -// Equals returns true if string s and t are equivalent. -bool -str·equals(const string s, const string t) -{ - vlong sL = str·len(s); - vlong tL = str·len(t); - if (sL != tL) return false; - - return memcmp(s, t, sL) == 0; -} - -//------------------------------------------------------------------------ -// Utility Methods - -int -str·read(string s, int size, int n, void *buf) -{ - int len; - - len = MIN(n * size, str·len(s)); - memcpy(buf, s, len); - - return len; -} - -// Find will find the first occurence of -// substr in the string Returns -1 if nothing was found. -int -str·find(string s, const byte* substr) -{ - byte* loc = strstr(s, substr); - if (loc == nil) return -1; - return (int)(loc - s); -} - -// -// Lower will force all runes in the string to be lowercase. -void -str·lower(string s) -{ - byte *b, *e; - b = s; - e = b + str·len(s); - while (b++ != e) - *b = tolower(*b); -} - -// Upper will force all runes in the string to be uppercase. -void -str·upper(string s) -{ - byte *b, *e; - b = s; - e = b + str·len(s); - while (b++ != e) - *b = toupper(*b); -} - -// Replace will replace all occurences of the given bytes 'from' to bytes 'to' -// Edits are done in place and modify the string. -// NOTE: As of now strings from and to must be the same size. -void -str·replace(string s, const byte* from, const byte* to) -{ - vlong fromL = strlen(from); - vlong toL = strlen(to); - if (toL != fromL) { panicf("different sized replacement string not supported"); } - - vlong l = str·len(s); - vlong i = l; - vlong j = l; - - for (i = 0; i < l; i++) { - for (j = 0; j < toL; j++) { - if (s[i] == from[j]) { - s[i] = to[j]; - break; - } - } - } -} - -// Split will split the string by the given token. -// Returns a stretchy buffer of strings that result from the partition. -// It is the caller's responsibility to clean the memory. -string* -str·split(string s, const byte* tok) -{ - string* fields = nil; - vlong start = 0; - - vlong sL = str·len(s); - vlong tokL = strlen(tok); - if (sL == 0 || tokL == 0) return nil; - - buffit(fields, 5); - - for (vlong i = 0; i < sL - tokL; i++) { - if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) { - bufpush(fields, str·makelen(s + start, i - start)); - if (fields[buflen(fields) - 1] == nil) goto cleanup; - - start = i + tokL; - i += tokL - 1; - } - } - - bufpush(fields, str·makelen(s + start, sL - start)); - - return fields; - -cleanup: - for (vlong i = 0; i < buflen(fields); i++) { - str·free(fields[i]); - } - buffree(fields); - return nil; -} - -string -str·join(vlong len, byte** fields, const byte* sep) -{ - string s = str·makecap("", 0, 10); - int j = 0; - - for (j = 0; j < len; j++) { - str·append(&s, fields[j]); - if (j < len - 1) - str·appendlen(&s, 1, sep); - } - - return s; -} diff --git a/sys/base/string/append.c b/sys/base/string/append.c new file mode 100644 index 0000000..d4d0396 --- /dev/null +++ b/sys/base/string/append.c @@ -0,0 +1,53 @@ +#include "internal.h" + +// append will append the given null terminated c string to the string data +// structure. this variant can append a substring of length len of the given +// string to our buffer. the result is reallocated if not enough room is present +// in the buffer. +int +str·appendlen(string *s, vlong n, const byte* b) +{ + /* + bl = strlen(b); + if (n > bl) panicf("attempted to make a substring longer than string"); + */ + + str·grow(s, n); + if(*s == nil) + return 0; + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + + memcpy(*s + str·len(*s), b, n); + h->len += n; + (*s)[h->len] = '\0'; + + return n; +} + +// append will append the given null terminated c string to the string data +// structure. this variant will append the entire string. +int +str·append(string *s, const byte* b) +{ + return str·appendlen(s, strlen(b), b); +} + +// appendbyte will append the given byte to our string. +// NOTE: as the byte is on the stack, it is not null-terminated. +// can not pass to the above functions. +int +str·appendbyte(string *s, const byte b) +{ + str·grow(s, 1); + if(*s == nil) + return 0; + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + + *(*s + str·len(*s)) = b; + h->len++; + (*s)[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..? + + return 1; +} diff --git a/sys/base/string/appendf.c b/sys/base/string/appendf.c new file mode 100644 index 0000000..4b8d76c --- /dev/null +++ b/sys/base/string/appendf.c @@ -0,0 +1,31 @@ +#include "internal.h" + +/* + * appendf will append the given formatted string to our buffer. + * returns the newly minted string + */ + +int +str·appendf(string *s, const byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int remain = str·cap(*s) - str·len(*s); + int n = vsnprintf(*s + str·len(*s), remain + 1, fmt, args); + va_end(args); + + if(n > remain){ + // If the first write was incomplete, we overwite the data again. + str·grow(s, n); + va_list args; + va_start(args, fmt); + n = vsnprintf(*s + str·len(*s), n + 1, fmt, args); + assert(n - remain <= str·cap(*s)); + va_end(args); + } + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + h->len += n; + + return n; +} diff --git a/sys/base/string/clear.c b/sys/base/string/clear.c new file mode 100644 index 0000000..986f809 --- /dev/null +++ b/sys/base/string/clear.c @@ -0,0 +1,9 @@ +#include "internal.h" + +void +str·clear(string *s) +{ + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + h->len = 0; + *s[0] = '\0'; +} diff --git a/sys/base/string/copyn.c b/sys/base/string/copyn.c new file mode 100644 index 0000000..09c2879 --- /dev/null +++ b/sys/base/string/copyn.c @@ -0,0 +1,11 @@ +#include "internal.h" + +char * +str·copyn(char *dst, char *src, int n) +{ + while(*src && n-- > 0) + *dst++ = *src++; + + *dst = 0; + return dst; +} diff --git a/sys/base/string/equals.c b/sys/base/string/equals.c new file mode 100644 index 0000000..a975cf5 --- /dev/null +++ b/sys/base/string/equals.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// Equals returns true if string s and t are equivalent. +bool +str·equals(const string s, const string t) +{ + vlong sL = str·len(s); + vlong tL = str·len(t); + if (sL != tL) return false; + + return memcmp(s, t, sL) == 0; +} diff --git a/sys/base/string/find.c b/sys/base/string/find.c new file mode 100644 index 0000000..20f990e --- /dev/null +++ b/sys/base/string/find.c @@ -0,0 +1,11 @@ +#include "internal.h" + +// find will find the first occurence of +// substr in the string returns -1 if nothing was found. +int +str·find(string s, const byte* substr) +{ + byte* loc = strstr(s, substr); + if (loc == nil) return -1; + return (int)(loc - s); +} diff --git a/sys/base/string/fit.c b/sys/base/string/fit.c new file mode 100644 index 0000000..56ab041 --- /dev/null +++ b/sys/base/string/fit.c @@ -0,0 +1,20 @@ +#include "internal.h" + +// fit reallocates the string such that the buffer is exactly sized for the +// buffer. if the capacity equals the length, then the function is a noop. the +// byte array is unchanged. +void +str·fit(string *s) +{ + Hdr* h; + vlong cap = str·cap(*s); + vlong len = str·len(*s); + + if (cap == len) return; + + h = (Hdr*)(s - sizeof(Hdr)); + h = realloc(h, sizeof(*h) + len + 1); + h->cap = len; + + *s = h->buf; +} diff --git a/sys/base/string/free.c b/sys/base/string/free.c new file mode 100644 index 0000000..7b5ee98 --- /dev/null +++ b/sys/base/string/free.c @@ -0,0 +1,8 @@ +#include "internal.h" + +// free returns memory associated to the buffer. +void +str·free(string s) +{ + free(s - sizeof(Hdr)); +} diff --git a/sys/base/string/grow.c b/sys/base/string/grow.c new file mode 100644 index 0000000..39a9d2f --- /dev/null +++ b/sys/base/string/grow.c @@ -0,0 +1,33 @@ +#include "internal.h" + +// grow ensures that the string can encompass at least delta bytes. +// if it already can, this is a no op. +// if it can't, the string will be reallocated. +void +str·grow(string *s, vlong delta) +{ + Hdr *h, *newh; + vlong cap = str·cap(*s); + vlong len = str·len(*s); + assert(cap >= len); // To prevent unsigned behavior + + if (cap - len >= delta) return; + + h = (Hdr*)(*s - sizeof(Hdr)); + + vlong newCap = cap + delta; + assert(newCap >= cap); // To prevent unsigned behavior + if (newCap < MAX_STRING_ALLOC) { + newCap *= 2; + } else + newCap += MAX_STRING_ALLOC; + + newh = (Hdr*)realloc(h, sizeof(*h) + newCap + 1); + if (newh == nil) return; + + memset(newh->buf + len, '\0', newCap - len); + newh->cap = newCap; + newh->len = len; + + *s = newh->buf; +} diff --git a/sys/base/string/internal.h b/sys/base/string/internal.h new file mode 100644 index 0000000..8c16c64 --- /dev/null +++ b/sys/base/string/internal.h @@ -0,0 +1,12 @@ +#pragma once +#include <u.h> +#include <base.h> + +#define MAX_STRING_ALLOC 1024 * 1024 + +typedef struct Hdr +{ + vlong len; + vlong cap; + byte buf[]; +} Hdr; diff --git a/sys/base/string/join.c b/sys/base/string/join.c new file mode 100644 index 0000000..fb97b6c --- /dev/null +++ b/sys/base/string/join.c @@ -0,0 +1,16 @@ +#include "internal.h" + +string +str·join(vlong len, byte** fields, const byte* sep) +{ + string s = str·makecap("", 0, 10); + int j = 0; + + for (j = 0; j < len; j++) { + str·append(&s, fields[j]); + if (j < len - 1) + str·appendlen(&s, 1, sep); + } + + return s; +} diff --git a/sys/base/string/len.c b/sys/base/string/len.c new file mode 100644 index 0000000..5e42919 --- /dev/null +++ b/sys/base/string/len.c @@ -0,0 +1,17 @@ +#include "internal.h" + +// len returns the length of the string. +int +str·len(const string s) +{ + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + return h->len; +} + +// cap returns the capacity of the string buffer. +int +str·cap(const string s) +{ + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + return h->cap; +} diff --git a/sys/base/string/lower.c b/sys/base/string/lower.c new file mode 100644 index 0000000..c6935f8 --- /dev/null +++ b/sys/base/string/lower.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// lower will force all runes in the string to be lowercase +void +str·lower(string s) +{ + byte *b, *e; + b = s; + e = b + str·len(s); + while (b++ != e) + *b = tolower(*b); +} diff --git a/sys/base/string/make.c b/sys/base/string/make.c new file mode 100644 index 0000000..eb71543 --- /dev/null +++ b/sys/base/string/make.c @@ -0,0 +1,53 @@ +#include "internal.h" + +// new returns a new dynamic string object, initialized from the given c string. +// len defines the length of the c substring that we will copy into our buffer. +// the backing buffer will have capacity cap. +string +str·makecap(const byte *s, vlong len, vlong cap) +{ + struct Hdr* h; + + h = malloc(sizeof(*h) + cap + 1); + if (s == nil) memset(h, 0, sizeof(*h)); + + if (h == nil) return nil; // Allocation failed. + + h->len = (s == nil) ? 0 : len; + h->cap = cap; + + if (cap < h->len) goto cleanup; + + if (s != nil && cap > 0) { + memcpy(h->buf, s, h->len); + memset(h->buf + h->len, '\0', h->cap - h->len + 1); + } + + return h->buf; + +cleanup: + free(h); + panicf("Attempted to create a string with less capacity than length"); + return nil; +} + +// new returns a new dynamic string object, initialized from the given c string. +// the backing buffer capacity is equivalent to the string length. +string +str·makelen(const byte *s, vlong len) +{ + vlong sl = (!s) ? 0 : strlen(s); + if (sl < len) panicf("attempted to take a bigger substring than string length"); + + vlong cap = (len == 0) ? 1 : len; + return str·makecap(s, len, cap); +} + +// new returns a new dynamic string object, initialized from the given c string. +// the backing buffer capacity is equivalent to the string length. +string +str·make(const byte *s) +{ + vlong len = (!s) ? 0 : strlen(s); + return str·makelen(s, len); +} diff --git a/sys/base/string/makef.c b/sys/base/string/makef.c new file mode 100644 index 0000000..8fb9c38 --- /dev/null +++ b/sys/base/string/makef.c @@ -0,0 +1,25 @@ +#include "internal.h" + +// Newf returns a new dynamic string object +string +str·makef(const byte *fmt, ...) +{ + vlong n; + string s; + va_list args; + + va_start(args, fmt); + n = vsnprintf(nil, 0, fmt, args); + va_end(args); + + s = str·makecap(nil, 0, n); + + va_start(args, fmt); + vsnprintf(s, n + 1, fmt, args); + va_end(args); + + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + h->len = n; + + return s; +} diff --git a/sys/base/string/read.c b/sys/base/string/read.c new file mode 100644 index 0000000..df2028f --- /dev/null +++ b/sys/base/string/read.c @@ -0,0 +1,12 @@ +#include "internal.h" + +int +str·read(string s, int size, int n, void *buf) +{ + int len; + + len = MIN(n * size, str·len(s)); + memcpy(buf, s, len); + + return len; +} diff --git a/sys/base/string/replace.c b/sys/base/string/replace.c new file mode 100644 index 0000000..127daed --- /dev/null +++ b/sys/base/string/replace.c @@ -0,0 +1,26 @@ +#include "internal.h" + +// replace will replace all occurences of the given bytes 'from' to bytes 'to' +// edits are done in place and modify the string. +// NOTE: as of now strings from and to must be the same size. +void +str·replace(string s, const byte* from, const byte* to) +{ + vlong fromL = strlen(from); + vlong toL = strlen(to); + if (toL != fromL) { panicf("different sized replacement string not supported"); } + + vlong l = str·len(s); + vlong i = l; + vlong j = l; + + for (i = 0; i < l; i++) { + for (j = 0; j < toL; j++) { + if (s[i] == from[j]) { + s[i] = to[j]; + break; + } + } + } +} + diff --git a/sys/base/string/rules.mk b/sys/base/string/rules.mk new file mode 100644 index 0000000..e517ca5 --- /dev/null +++ b/sys/base/string/rules.mk @@ -0,0 +1,19 @@ +SRCS_$(d)+=\ + $(d)/string/append.c\ + $(d)/string/appendf.c\ + $(d)/string/clear.c\ + $(d)/string/copyn.c\ + $(d)/string/equals.c\ + $(d)/string/find.c\ + $(d)/string/fit.c\ + $(d)/string/free.c\ + $(d)/string/grow.c\ + $(d)/string/join.c\ + $(d)/string/len.c\ + $(d)/string/lower.c\ + $(d)/string/make.c\ + $(d)/string/makef.c\ + $(d)/string/read.c\ + $(d)/string/replace.c\ + $(d)/string/split.c\ + $(d)/string/upper.c\ diff --git a/sys/base/string/split.c b/sys/base/string/split.c new file mode 100644 index 0000000..2aa68b4 --- /dev/null +++ b/sys/base/string/split.c @@ -0,0 +1,39 @@ +#include "internal.h" + +// split will split the string by the given token. +// returns a stretchy buffer of strings that result from the partition. +// it is the caller's responsibility to clean the memory. +string* +str·split(string s, const byte* tok) +{ + string* fields = nil; + vlong start = 0; + + vlong sL = str·len(s); + vlong tokL = strlen(tok); + if (sL == 0 || tokL == 0) return nil; + + buffit(fields, 5); + + for (vlong i = 0; i < sL - tokL; i++) { + if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) { + bufpush(fields, str·makelen(s + start, i - start)); + if (fields[buflen(fields) - 1] == nil) goto cleanup; + + start = i + tokL; + i += tokL - 1; + } + } + + bufpush(fields, str·makelen(s + start, sL - start)); + + return fields; + +cleanup: + for (vlong i = 0; i < buflen(fields); i++) { + str·free(fields[i]); + } + buffree(fields); + return nil; +} + diff --git a/sys/base/string/upper.c b/sys/base/string/upper.c new file mode 100644 index 0000000..ab692c1 --- /dev/null +++ b/sys/base/string/upper.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// Upper will force all runes in the string to be uppercase. +void +str·upper(string s) +{ + byte *b, *e; + b = s; + e = b + str·len(s); + while (b++ != e) + *b = toupper(*b); +} diff --git a/sys/base/test.c b/sys/base/test.c index 972f25a..a29be1d 100644 --- a/sys/base/test.c +++ b/sys/base/test.c @@ -129,7 +129,7 @@ test·sort() } t = clock(); - sort·ints(arrlen(test), test); + sort·int(arrlen(test), test); t = clock() - t; printf("inlined code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); diff --git a/sys/cmd/rc/input.c b/sys/cmd/rc/input.c index 08363f0..cc2383d 100644 --- a/sys/cmd/rc/input.c +++ b/sys/cmd/rc/input.c @@ -8,7 +8,7 @@ enum {
NonPrintable,
Alnum,
- Punctation,
+ Punctuation,
Space
};
@@ -32,7 +32,8 @@ static int ascii[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-struct Mode {
+struct Mode
+{
ushort raw : 1;
ushort multiline : 1;
ushort mask : 1;
@@ -43,9 +44,6 @@ struct Mode { } vi ;
};
-static struct Mode mode;
-static struct termios originalterm;
-
/*
* the structure represents the state during line editing.
* we pass this state to functions implementing specific editing functionalities
@@ -82,8 +80,22 @@ struct TerminalState } yank; /* yank buffer */
intptr maxrows; /* maximum num of rows used so far (multiline mode) */
+ intptr history; /* index of history we are currently editing */
+};
+
+/*
+ * line history (circular buffer)
+ */
+struct History
+{
+ char **bot, **top, *entry[1024];
};
+/* globals */
+static struct Mode mode;
+static struct History history;
+static struct termios originalterm;
+
enum
{
KeyNil = 0, /* nil */
@@ -120,6 +132,22 @@ typedef Position (*Noun)(struct TerminalState*, int); typedef void (*Verb)(struct TerminalState*, Position);
static
+int
+runetype(rune r)
+{
+ if(r<128)
+ return ascii[r];
+ if(utf8·isspace(r))
+ return Space;
+ if(utf8·isdigit(r) || utf8·isalpha(r))
+ return Alnum;
+ if(utf8·ispunct(r))
+ return Punctuation;
+
+ return NonPrintable;
+}
+
+static
void
normalcursor(int fd)
{
@@ -277,14 +305,120 @@ beep(void) fflush(stderr);
}
-/* =========================== Line editing ================================= */
+// -----------------------------------------------------------------------
+// command history
+
+void
+inithistory(void)
+{
+ history.bot = history.top = history.entry;
+}
+
+int
+addhistory(char *line)
+{
+ char *copy;
+
+ copy = strdup(line);
+ if(!copy)
+ return 0;
+
+ *history.top++ = copy;
+ if(history.top == arrend(history.entry))
+ history.top = history.entry;
+
+ if(history.top == history.bot){
+ efree(history.bot);
+ history.bot++;
+ }
+
+ return 1;
+}
+
+static
+void
+pophistory(void)
+{
+ if(--history.top < history.entry)
+ history.top = arrend(history.entry)-1;
+ efree(*history.top);
+}
+
+static void refreshline(struct TerminalState *);
+
+static
+char **
+currenthistory(struct TerminalState *term, intptr *size)
+{
+ char **entry;
+ intptr len, head;
+
+ if(history.top > history.bot){
+ len = history.top - history.bot;
+ entry = history.top - term->history - 1;
+ }else if(history.top < history.bot){
+ len = (arrend(history.entry) - history.bot) + (history.top - history.entry);
+ if((head=history.top - history.entry) < term->history)
+ entry = arrend(history.entry) - head;
+ else
+ entry = history.top - term->history - 1;
+ }else
+ return nil;
+
+ *size = len;
+ return entry;
+}
+
+static
+void
+usehistory(struct TerminalState *term, int d)
+{
+ rune r;
+ intptr w, len;
+ char *b, *e, **entry;
+
+ if(!(entry = currenthistory(term, &len)))
+ return;
+
+ efree(*entry);
+ *entry = strdup(term->edit.buf);
+
+ term->history += d;
+ if(term->history < 0){
+ term->history = 0;
+ return;
+ }else if(term->history >= len){
+ term->history = len - 1;
+ return;
+ }
+ entry = currenthistory(term, &len);
+
+ strncpy(term->edit.buf, *entry, term->edit.cap);
+ term->edit.buf[term->edit.cap-1] = 0;
+
+ /* update cursor/buffer positions */
+ term->edit.len = term->edit.pos = strlen(term->edit.buf);
+ for(w=0, b=term->edit.buf, e=term->edit.buf+term->edit.len; b < e; ){
+ b += utf8·decode(b, &r);
+ w += utf8·runewidth(r);
+ }
+ term->cursor.len = term->cursor.pos = w;
+
+ refreshline(term);
+}
+
+// -----------------------------------------------------------------------
+// line editing
-/* We define a very simple "append buffer" structure, that is an heap
- * allocated string where we can append to. This is useful in order to
+/*
+ * we define a very simple "append buffer" structure, that is an heap
+ * allocated string where we can append to. this is useful in order to
* write all the escape sequences in a buffer and flush them to the standard
- * output in a single call, to avoid flickering effects. */
+ * output in a single call, to avoid flickering effects.
+ */
-struct Buffer {
+struct Buffer
+{
int len;
char *b;
};
@@ -341,14 +475,16 @@ refreshsingleline(struct TerminalState *term) w = utf8·runewidth(r);
buf+=n, len-=n;
- pos-=w, col-=w;
+ pos-=w, col-=w;
}
assert(buf <= term->edit.buf + len);
while(off+col > term->cursor.cap){
n = utf8·decodeprev(buf+len-1, &r);
- len-=n, col--;
+ w = utf8·runewidth(r);
+
+ len-=n, col-=w;
}
assert(len >= 0);
@@ -507,11 +643,11 @@ insertrune(struct TerminalState *term, int n, char *c) }
refreshline(term);
}else{
- memmove(term->edit.buf+term->edit.len+n, term->edit.buf+term->edit.len, term->edit.len-term->edit.pos);
+ memmove(term->edit.buf+term->edit.pos+n, term->edit.buf+term->edit.pos, term->edit.len-term->edit.pos);
memcpy(term->edit.buf+term->edit.pos, c, n);
- term->edit.pos+=n, term->edit.len+=n;
- term->cursor.pos++, term->cursor.len++;
+ term->edit.pos += n, term->edit.len += n;
+ term->cursor.pos += w, term->cursor.len += w;
term->edit.buf[term->edit.len] = '\0';
refreshline(term);
@@ -649,14 +785,8 @@ refresh: term->cursor.len -= diff;
refreshline(term);
}
-
/* movements */
-void
-movehistory(struct TerminalState *term, int dir)
-{
-}
-
#define CURRENT(term) (Position){ .buffer=(term)->edit.pos, .cursor=(term)->cursor.pos };
// move cursor to the left n boxes
@@ -665,14 +795,20 @@ Position left(struct TerminalState *term, int n)
{
rune r;
+ int w, d;
Position pos = CURRENT(term);
char *buf = term->edit.buf + term->edit.pos;
- pos.cursor = MAX(pos.cursor-n, 0);
-
- while(n-- > 0 && buf > term->edit.buf)
+ d = 0;
+ while(n > 0 && buf > term->edit.buf){
buf -= utf8·decodeprev(buf-1, &r);
+ w = utf8·runewidth(r);
+ n -= w;
+ d += w;
+ }
+
+ pos.cursor = MAX(pos.cursor-d, 0);
pos.buffer = MAX(buf-term->edit.buf, 0);
return pos;
}
@@ -683,135 +819,301 @@ Position right(struct TerminalState *term, int n)
{
rune r;
+ int w, d;
Position pos = CURRENT(term);
- char *end = term->edit.buf + term->edit.len;
- char *buf = term->edit.buf + term->edit.pos;
- pos.cursor = MIN(pos.cursor+n, term->cursor.len);
+ char *buf = term->edit.buf + term->edit.pos;
+ char *end = term->edit.buf + term->edit.len;
- while(n-- > 0 && buf < end)
+ d = 0;
+ while(n > 0 && buf < end){
buf += utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+ n -= w;
+ d += w;
+ }
+
+ pos.cursor = MIN(pos.cursor+d, term->cursor.len);
pos.buffer = MIN(buf-term->edit.buf, term->edit.len);
return pos;
}
-#define HOME(term) (Position){0}
-
-#if 0
static
Position
prevword(struct TerminalState *term, int n)
{
- int c;
+ rune r;
+ int c, w, b, d;
Position pos = CURRENT(term);
+
char *buf = term->edit.buf + term->edit.pos;
+ d = 0;
while(n-- > 0 && buf > term->edit.buf){
- while(buf > term->edit.buf && ascii[buf[-1]] == Space)
- --buf;
+ eatspace:
+ b = utf8·decodeprev(buf-1, &r);
+ w = utf8·runewidth(r);
+ if((c=runetype(r)) == Space){
+ buf -= b;
+ d += w;
+
+ if(buf <= term->edit.buf)
+ break;
+
+ goto eatspace;
+ }
- c = ascii[buf[-1]];
- while(buf> term->edit.buf && ascii[buf[-1]] == c)
- --buf;
+ eatword:
+ if(runetype(r) == c){
+ buf -= b;
+ d += w;
+
+ if(buf <= term->edit.buf)
+ break;
+
+ b = utf8·decodeprev(buf-1, &r);
+ w = utf8·runewidth(r);
+
+ goto eatword;
+ }
}
- return buf-term->edit.buf;
+ pos.cursor = MAX(pos.cursor-d, 0);
+ pos.buffer = MAX(buf-term->edit.buf, 0);
+ return pos;
}
static
Position
-prevWord(struct TerminalState *term, int n)
+nextword(struct TerminalState *term, int n)
{
- char *it = term->edit.buf + term->edit.pos;
+ rune r;
+ int c, b, w, d;
+ Position pos = CURRENT(term);
- while(n-- > 0 && it > term->edit.buf){
- while(it > term->edit.buf && ascii[it[-1]] == Space)
- --it;
+ char *buf = term->edit.buf + term->edit.pos;
+ char *end = term->edit.buf + term->edit.len;
- while(it > term->edit.buf && ascii[it[-1]] != Space)
- --it;
+ d = 0;
+ while(n-- > 0 && buf < end){
+ b = utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+ c = runetype(r);
+ eatword:
+ if(runetype(r) == c){
+ buf += b;
+ d += w;
+
+ if(buf >= end)
+ break;
+
+ b = utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+ goto eatword;
+ }
+ eatspace:
+ while((c=runetype(r)) == Space){
+ buf += b;
+ d += w;
+
+ if(buf >= end)
+ break;
+
+ b = utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+ goto eatspace;
+ }
}
- return it-term->edit.buf;
+ pos.cursor = MIN(pos.cursor+d, term->cursor.len);
+ pos.buffer = MIN(buf-term->edit.buf, term->edit.len);
+ return pos;
}
+
static
Position
-nextword(struct TerminalState *term, int n)
+prevWord(struct TerminalState *term, int n)
{
- int c;
- char *it = term->edit.buf + term->edit.pos;
- char *end = term->edit.buf + term->edit.len;
+ rune r;
+ int c, w, b, d;
+ Position pos = CURRENT(term);
+
+ char *buf = term->edit.buf + term->edit.pos;
+
+ d = 0;
+ while(n-- > 0 && buf > term->edit.buf){
+ eatspace:
+ b = utf8·decodeprev(buf-1, &r);
+ w = utf8·runewidth(r);
+ if((c=runetype(r)) == Space){
+ buf -= b;
+ d += w;
+
+ if(buf <= term->edit.buf)
+ break;
+
+ goto eatspace;
+ }
+
+ eatword:
+ if((c=runetype(r)) != Space){
+ buf -= b;
+ d += w;
- while(n-- > 0 && it < end){
- c = ascii[*it];
- while(it < end && ascii[*it] == c)
- ++it;
+ if(buf <= term->edit.buf)
+ break;
- while(it < end && ascii[*it] == Space)
- ++it;
+ b = utf8·decodeprev(buf-1, &r);
+ w = utf8·runewidth(r);
+
+ goto eatword;
+ }
}
- return it-term->edit.buf;
+ pos.cursor = MAX(pos.cursor-d, 0);
+ pos.buffer = MAX(buf-term->edit.buf, 0);
+ return pos;
}
static
Position
nextWord(struct TerminalState *term, int n)
{
- char *it = term->edit.buf + term->edit.pos;
+ rune r;
+ int b, w, d;
+ Position pos = CURRENT(term);
+
+ char *buf = term->edit.buf + term->edit.pos;
char *end = term->edit.buf + term->edit.len;
- while(n-- > 0 && it < end){
- while(it < end && ascii[*it] != Space)
- ++it;
+ d = 0;
+ while(n-- > 0 && buf < end){
+ eatword:
+ b = utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+ if(runetype(r) != Space){
+ buf += b;
+ d += w;
- while(it < end && ascii[*it] == Space)
- ++it;
+ if(buf > end)
+ break;
+
+ goto eatword;
+ }
+
+ eatspace:
+ if(runetype(r) == Space){
+ buf += b;
+ d += w;
+
+ if(buf > end)
+ break;
+
+ b = utf8·decode(buf, &r);
+ w = utf8·runewidth(r);
+
+ goto eatspace;
+ }
}
- return it-term->edit.buf;
+ pos.cursor = MIN(pos.cursor+d, term->cursor.len);
+ pos.buffer = MIN(buf-term->edit.buf, term->edit.len);
+ return pos;
}
static
Position
nextend(struct TerminalState *term, int n)
{
- int c;
- char *it = term->edit.buf + term->edit.pos;
+ rune r;
+ int c, b, w, d;
+ Position pos = CURRENT(term);
+
+ char *buf = term->edit.buf + term->edit.pos;
char *end = term->edit.buf + term->edit.len;
- while(n-- > 0 && it+1 < end){
- while(it+1 < end && ascii[it[1]] == Space)
- ++it;
+ d = 0;
+ while(n-- > 0 && buf+1 < end){
+ eatspace:
+ b = utf8·decode(buf+1, &r);
+ w = utf8·runewidth(r);
+ while((c=runetype(r)) == Space){
+ buf += b;
+ d += w;
+
+ if(buf+1 >= end)
+ break;
+
+ goto eatspace;
+ }
+ eatword:
+ if(runetype(r) == c){
+ buf += b;
+ d += w;
+
+ if(buf+1 >= end)
+ break;
- c = ascii[it[1]];
- while(it+1 < end && ascii[it[1]] == c)
- ++it;
+ b = utf8·decode(buf+1, &r);
+ w = utf8·runewidth(r);
+ goto eatword;
+ }
}
- return it-term->edit.buf;
+ pos.cursor = MIN(pos.cursor+d, term->cursor.len);
+ pos.buffer = MIN(buf-term->edit.buf, term->edit.len);
+ return pos;
}
static
Position
nextEnd(struct TerminalState *term, int n)
{
- char *it = term->edit.buf + term->edit.pos;
+ rune r;
+ int b, w, d;
+ Position pos = CURRENT(term);
+
+ char *buf = term->edit.buf + term->edit.pos;
char *end = term->edit.buf + term->edit.len;
- while(n-- > 0 && it+1 < end){
- while(it+1 < end && ascii[it[1]] == Space)
- ++it;
+ d = 0;
+ while(n-- > 0 && buf+1 < end){
+ eatspace:
+ b = utf8·decode(buf+1, &r);
+ w = utf8·runewidth(r);
+ if(runetype(r) == Space){
+ buf += b;
+ d += w;
+
+ if(buf+1 > end)
+ break;
+
+ goto eatspace;
+ }
+
+ eatword:
+ if(runetype(r) != Space){
+ buf += b;
+ d += w;
- while(it < end && ascii[it[1]] != Space)
- ++it;
+ if(buf+1 > end)
+ break;
+
+ b = utf8·decode(buf+1, &r);
+ w = utf8·runewidth(r);
+
+ goto eatword;
+ }
}
- return it-term->edit.buf;
+ pos.cursor = MIN(pos.cursor+d, term->cursor.len);
+ pos.buffer = MIN(buf-term->edit.buf, term->edit.len);
+ return pos;
}
+#define HOME(term) (Position){0}
#define END(term) (Position){(term)->edit.len, (term)->cursor.len}
static
@@ -964,7 +1266,6 @@ action: return 0;
}
#undef END
-#endif
#define END(term) (Position){(term).edit.len, (term).cursor.len}
@@ -1034,6 +1335,7 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) term.cursor.cap = columns(ifd, ofd);
term.maxrows = 0;
+ term.history = 0;
term.yank.buf = nil;
term.yank.cap = term.yank.len = 0;
@@ -1042,6 +1344,9 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) term.edit.buf[0] = '\0';
term.edit.cap--; /* make sure there is always space for the nulterm */
+ /* push current (empty) command onto history stack */
+ addhistory("");
+
if(write(term.ofd,prompt,term.prompt.len) == -1)
return -1;
@@ -1070,6 +1375,7 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) switch(r){
case KeyEnter:
+ pophistory();
if(mode.multiline)
move(&term, END(term));
goto finish;
@@ -1111,11 +1417,11 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) break;
case KeyCtrlP: /* ctrl-p */
- /* TODO next history */
+ usehistory(&term, +1);
break;
case KeyCtrlN: /* ctrl-n */
- /* TODO prev history */
+ usehistory(&term, -1);
break;
case KeyEsc: /* escape sequence */
@@ -1138,7 +1444,6 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) }
}
case 1:
-#if 0
if(mode.vi.on){
if(mode.vi.insert){
normalmode(term.ofd);
@@ -1149,7 +1454,6 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) continue;
}
}
-#endif
default: // 2
;
}
@@ -1171,10 +1475,10 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) }else{
switch(esc[1]) {
case 'A': /* up */
- movehistory(&term, 1);
+ usehistory(&term, +1);
break;
case 'B': /* down */
- movehistory(&term, 0);
+ usehistory(&term, -1);
break;
case 'C': /* right */
move(&term, right(&term, 1));
@@ -1202,22 +1506,15 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) break;
}
}
-
break;
default:
-#if 0
- if(mode.vi.on && !mode.vi.insert){
- if(vi(&term,c) < 0){
+ if(mode.vi.on && !mode.vi.insert && n == 1){
+ if(vi(&term,c[0]) < 0){
term.edit.len = -1;
goto finish;
}
- }else if(!insertchar(&term,c)){
- term.edit.len = -1;
- goto finish;
- }
-#endif
- if(!insertrune(&term, n, c)){
+ }else if(!insertrune(&term,n,c)){
term.edit.len = -1;
goto finish;
}
@@ -1252,7 +1549,7 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) break;
case KeyCtrlW: /* ctrl+w, delete previous word */
- /* delete(&term, prevword(&term,1)); */
+ delete(&term, prevword(&term,1));
break;
}
}
diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c index 12f0859..2c0aa42 100644 --- a/sys/cmd/rc/main.c +++ b/sys/cmd/rc/main.c @@ -58,8 +58,9 @@ main(int argc, char *argv[]) initpath(); initkeywords(); initshell(); + inithistory(); - // enablevi(); + enablevi(); xboot(argc, argv); /* unreachable */ } diff --git a/sys/cmd/rc/prompt.c b/sys/cmd/rc/prompt.c index ebda9fb..1122d54 100644 --- a/sys/cmd/rc/prompt.c +++ b/sys/cmd/rc/prompt.c @@ -19,8 +19,13 @@ prompt(ushort *flag) runner->flag.eof = 1; return 0; } - write(mapfd(0), "\n\r", 2); + if(runner->cmd.io->e[-1] == '\n'){ + runner->cmd.io->e[-1] = 0; + addhistory(runner->cmd.io->b); + runner->cmd.io->e[-1] = '\n'; + } + write(mapfd(0), "\n\r", 2); promptstr = " "; runner->line++; diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h index aa32a3a..9b415fc 100644 --- a/sys/cmd/rc/rc.h +++ b/sys/cmd/rc/rc.h @@ -172,10 +172,13 @@ void *emalloc(uintptr); void *erealloc(void*, uintptr); void efree(void*); -/* read.c */ +/* input.c */ int readline(char *); void enablevi(void); +void inithistory(void); +int addhistory(char *); + /* prompt.c */ void resetprompt(void); int prompt(ushort *); @@ -237,7 +240,7 @@ void background(Thread *, int); /* exec.c */ // XXX: odd place for this -int count(Word *); +int count(Word *); Word *makeword(char *str, Word *link); void freeword(Word *w); diff --git a/sys/cmd/term/term.c b/sys/cmd/term/term.c index 3cd99c3..50ab29c 100644 --- a/sys/cmd/term/term.c +++ b/sys/cmd/term/term.c @@ -2096,7 +2096,7 @@ tputc(rune u) } /* combining characters */ - if(!width) { + if(!width){ if(term.c.x > 0) gp = &term.line[term.c.y][term.c.x-1]; else if(term.c.y > 0) diff --git a/sys/libfmt/buffer.c b/sys/libfmt/buffer.c new file mode 100644 index 0000000..0099e72 --- /dev/null +++ b/sys/libfmt/buffer.c @@ -0,0 +1,60 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n; + char *s; + + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + if(!io->buffer.beg) + return 0; + + n = 2*(uintptr)io->file; + s = io->buffer.beg; + + io->buffer.beg = mem.realloc(heap, io->buffer.beg, n, 1); + if(!io->buffer.beg){ + io->file = io->buffer.cur = io->buffer.end = nil; + mem.free(heap, s); + return 0; + } + io->file = (void*)(uintptr)n; + io->buffer.cur = io->buffer.beg + (io->buffer.cur - s); + io->buffer.end = io->buffer.beg + n - 1; + + return 1; +} + +int +fmt·make(mem·Reallocator mem, void *heap, fmt·State *io) +{ + int n; + + memset(io, 0, sizeof(*io)); + + n = 32; + io->buffer.beg = io->buffer.cur = mem.alloc(heap, n, 1); + if(!io->buffer.beg) + return -1; + io->buffer.end = io->buffer.beg + n - 1; + + io->flush = flush; + io->file = (void*)(uintptr)n; + io->n = 0; + + fmt·setlocale(io, nil, nil, nil); + return 0; +} + +void +fmt·free(fmt·State *io) +{ + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + mem.free(heap, io->buffer.beg); + io->buffer.beg = io->buffer.cur = io->buffer.end = nil; +} diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c index 3b8ff5a..eaac0a3 100644 --- a/sys/libfmt/do.c +++ b/sys/libfmt/do.c @@ -6,45 +6,68 @@ #define atomic·load atomic_load #define atomic·store atomic_store +// ----------------------------------------------------------------------- +// globals + +/* built in verbs */ +static int fmtflag(fmt·State *); +static int fmtpercent(fmt·State *); +static int fmtrune(fmt·State *); +static int fmtfloat(fmt·State *); +static int fmtutf8(fmt·State *); +static int fmtint(fmt·State *); +static int fmtchar(fmt·State *); +static int fmtcount(fmt·State *); +static int fmtstring(fmt·State *); +static int fmterror(fmt·State *); + +static int badfmt(fmt·State *); + static struct { atomic int len; Verb verb[MaxFmt]; } formatter = { - ATOMIC_VAR_INIT(27), + ATOMIC_VAR_INIT(30), { - {' ', ·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}, + {' ', fmtflag}, + {'#', fmtflag}, + {'%', fmtpercent}, + {'\'',fmtflag}, + {'+', fmtflag}, + {',', fmtflag}, + {'-', fmtflag}, + {'C', fmtrune}, + {'E', fmtfloat}, + {'F', fmtfloat}, + {'G', fmtfloat}, + {'L', fmtflag}, + {'S', fmtutf8}, + {'X', fmtint}, + {'b', fmtint}, + {'c', fmtchar}, + {'d', fmtint}, + {'e', fmtfloat}, + {'f', fmtfloat}, + {'g', fmtfloat}, + {'h', fmtflag}, + {'i', fmtint}, + {'l', fmtflag}, + {'n', fmtcount}, + {'o', fmtint}, + {'p', fmtint}, + {'r', fmterror}, + {'s', fmtstring}, + {'U', fmtflag}, + {'u', fmtint}, + {'x', fmtint}, } }; +// ----------------------------------------------------------------------- +// internal functions + static Formatter format(int c) { @@ -55,7 +78,7 @@ format(int c) return v->fmt; } - return ·fmterr; + return badfmt; } static char * @@ -115,8 +138,9 @@ dispatch(fmt·State *io, char *fmt) i = va_arg(io->args, int); if(i < 0){ if(io->flag&fmt·Prec){ - io->flag ^= fmt·Prec; + io->flag &= ~fmt·Prec; io->prec = 0; + continue; } i = -i; io->flag |= fmt·Left; @@ -143,6 +167,500 @@ flush(fmt·State *io, char *b, int len) return io->buffer.cur; } +static int +pad(fmt·State *io, int n) +{ + int i; + char *b=io->buffer.cur, *e=io->buffer.end; + + for(i=0; i<n; i++){ + if(b>=e){ + if(!(b=flush(io, b, 1))) + return -1; + e = io->buffer.end; + } + *b++ = ' '; + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + return 0; +} + +static int +copy(fmt·State *io, char *m, int sz, int n) +{ + ulong f; + rune r; + int nc, w, nb; + char *b, *e, *me; + + w = 0; + f = io->flag; + me = m + sz; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(nc=n; nc>0; nc--){ + r = *(uchar *)m; + if(utf8·onebyte(r)){ + nb=1; + m++; + }else if((me-m) >= UTFmax || utf8·canfit(m, me-m)){ + nb=utf8·decode(m, &r); + m+=n; + }else + break; + + if(b+n>e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copyrune(fmt·State *io, rune *m, int n) +{ + ulong f; + rune r, *me; + int w, nb; + char *b, *e; + + w = 0; + f = io->flag; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(me=m+n; m < me; m++){ + r = *m; + nb = utf8·runelen(r); + if(b + nb > e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copystring(fmt·State *io, char *s) +{ + rune r; + int i,j; + + if(!s) + return copy(io, "<nil>", 5, 5); + + if(io->flag&fmt·Prec){ + i = 0; + for(j=0; j < io->prec && s[i]; j++) + i += utf8·decode(s+i, &r); + + return copy(io, s, i, j); + } + return copy(io, s, strlen(s), utf8·len(s)); +} + +static int +copyutf8(fmt·State *io, rune *s) +{ + rune *e; + int n,p; + + if(!s) + return copy(io, "<nil>", 5, 5); + + if(io->flag & fmt·Prec){ + p = io->prec; + for(n=0; n<p; n++) + if(!s[n]) + break; + }else{ + for(e=s; *e; e++) + ; + n = e - s; + } + + return copyrune(io, s, n); +} + +// ----------------------------------------------------------------------- +// format helpers + +static int +needseperate(int *digits, char **groups) +{ + int group; + + (*digits)++; + group = *(uchar *)*groups; + + if(group == 0xFF || group == 0x7f || group == 0x00) + return 0; + if(*digits > group){ + if((*groups)[1] != 0) + (*groups)++; + *digits = 1; + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------- +// formatters + +static int +fmtchar(fmt·State *io) +{ + char x[1]; + x[0] = va_arg(io->args, int); + io->prec = 1; + + return copy(io, x, 1, 1); +} + +static int +fmtstring(fmt·State *io) +{ + char *s; + s = va_arg(io->args, char *); + return copystring(io, s); +} + +static int +fmterror(fmt·State *io) +{ + char *s; + s = strerror(errno); + return copystring(io, s); +} + +static int +fmtrune(fmt·State *io) +{ + rune x[1]; + + x[0] = va_arg(io->args, int); + return copyrune(io, x, 1); +} + +static int +fmtutf8(fmt·State *io) +{ + rune *s; + + s = va_arg(io->args, rune *); + return copyutf8(io, s); +} + +static int +fmtpercent(fmt·State *io) +{ + rune x[1]; + + x[0] = io->verb; + io->prec = 1; + return copyrune(io, x, 1); +} + +static int +fmtint(fmt·State *io) +{ + union{ + ulong u; + uvlong v; + } val; + int neg, base, i, n, f, w, isv; + int digits, bytes, runes, excess; + char *groups, *thousands; + char *p, *conv, buf[140]; + + f = io->flag; + neg = 0; + isv = 0; + val.u = 0; + + switch(io->verb){ + case 'o': case 'p': case 'u': case 'x': case 'X': + f |= fmt·Unsigned; + f &= ~(fmt·Sign|fmt·Space); + } + + /* set flags */ + if(io->verb=='p'){ + val.u = (ulong)va_arg(io->args, void*); + io->verb = 'x'; + f |= fmt·Unsigned; + }else if(f&fmt·Vlong){ + isv=1; + if(f&fmt·Unsigned) + val.v = va_arg(io->args, uvlong); + else + val.v = va_arg(io->args, vlong); + }else if(f&fmt·Long){ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, ulong); + else + val.u = va_arg(io->args, long); + }else if(f&fmt·Byte){ + if(f&fmt·Unsigned) + val.u = (uchar)va_arg(io->args, int); + else + val.u = (char)va_arg(io->args, int); + }else if(f&fmt·Short){ + if(f&fmt·Unsigned) + val.u = (ushort)va_arg(io->args, int); + else + val.u = (short)va_arg(io->args, int); + }else{ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, uint); + else + val.u = va_arg(io->args, int); + } + + conv = "0123456789abcdef"; + groups = "\4"; + thousands = io->thousands; + /* get base */ + switch(io->verb){ + case 'd': case 'i': case 'u': + base = 10; + groups = io->groups; + break; + case 'X': + conv = "0123456789ABCDEF"; + /*fallthrough*/ + case 'x': + base = 16; + thousands = ":"; + break; + case 'b': + base = 2; + thousands = ":"; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + + /* check for negativity */ + if(!(f&fmt·Unsigned)){ + if(isv && (vlong)val.v < 0){ + val.v = -(vlong)val.v; + neg = 1; + }else if(!isv && (long)val.u < 0){ + val.u = -(long)val.u; + neg = 1; + } + } + + p = buf + sizeof(buf) - 1; + n = 0; + digits = 0; + excess = 0; + runes = utf8·len(thousands); + bytes = strlen(thousands); + +#define PARSE(VALUE) \ + while((VALUE)){ \ + i = (VALUE) % base; \ + (VALUE) /= base; \ + if((f&fmt·Comma) && n%4 == 3){ \ + *p-- = ','; \ + n++; \ + } \ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ \ + n += runes; \ + excess += bytes - runes; \ + p -= bytes; \ + memmove(p+1, thousands, bytes); \ + } \ + *p-- = conv[i]; \ + n++; \ + } + if(isv) + PARSE(val.v) + else + PARSE(val.u) +#undef PARSE + + if(!n){ + if(!(f&fmt·Prec) || io->prec != 0 || (io->verb == 'o' && (f&fmt·Sharp))){ + *p-- = '0'; + n = 1; + if(f&fmt·Apost) + needseperate(&digits,&groups); + } + + if(io->verb == 'x' || io->verb == 'X') + f &= ~fmt·Sharp; + } + + for(w = io->prec; n < w && p > buf+3; n++){ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + + if(neg || (f&(fmt·Sign|fmt·Space))) + n++; + + if(f&fmt·Sharp){ + if(base==16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + f &= ~fmt·Sharp; + else + n++; + } + } + + if(f&fmt·Zero && !(f & (fmt·Left|fmt·Prec))){ + w = 0; + if(f & fmt·Width) + w = io->width; + for(; n < w && p > buf+3; n++){ + if((f & fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + io->flag &= ~fmt·Width; + } + + if(f&fmt·Sharp){ + if(base==16) + *p-- = io->verb; + if(base==16 || base == 8) + *p-- = '0'; + } + + if(neg) + *p-- = '-'; + else if(f & fmt·Sign) + *p-- = '+'; + else if (f & fmt·Space) + *p-- = ' '; + + io->flag &= ~fmt·Prec; + return copy(io, p+1, n+excess, n); +} + +static int +fmtcount(fmt·State *io) +{ + void *p; + ulong f; + + f = io->flag; + p = va_arg(io->args, void*); + + if(f&fmt·Vlong) + *(vlong*)p = io->n; + else if(f&fmt·Long) + *(long*)p = io->n; + else if(f&fmt·Byte) + *(char*)p = io->n; + else if(f&fmt·Short) + *(short*)p = io->n; + else + *(int*)p = io->n; + + return 0; +} + +static int +fmtflag(fmt·State *io) +{ + switch(io->verb){ + case ',': io->flag |= fmt·Comma; break; + case '-': io->flag |= fmt·Left; break; + case '+': io->flag |= fmt·Sign; break; + case '#': io->flag |= fmt·Sharp; break; + case '\'': io->flag |= fmt·Apost; break; + case ' ': io->flag |= fmt·Space; break; + case 'u': io->flag |= fmt·Unsigned; break; + case 'L': io->flag |= fmt·Ldouble; break; + case 'h': + if(io->flag&fmt·Short) + io->flag |= fmt·Byte; + io->flag |= fmt·Short; + break; + case 'l': + if(io->flag&fmt·Long) + io->flag |= fmt·Vlong; + io->flag |= fmt·Long; + break; + } + return 1; +} + +static int +badfmt(fmt·State *io) +{ + int n; + char x[UTFmax+2]; + + x[0] = '%'; + n = 1 + utf8·encode(&io->verb, x+1); + x[n++] = '%'; + io->prec = n; + copy(io, x, n, n); + + return 0; +} + +#include "float.c" + +// ----------------------------------------------------------------------- +// exports + int fmt·do(fmt·State *io, char *fmt) { @@ -153,11 +671,10 @@ fmt·do(fmt·State *io, char *fmt) 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)))) + if(b >= e){ + if(!(b=flush(io, b, 1))) return -1; e = io->buffer.end; } @@ -165,22 +682,49 @@ fmt·do(fmt·State *io, char *fmt) }else{ n = utf8·decode(fmt, &r); if(b + n > e){ - if(!(b=flush(io, b, sizeof(*b)))) + if(!(b=flush(io, b, n))) 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; } + fmt++; + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!c) /* we hit our nul terminator */ + return io->n - n; + io->buffer.end = e; if(!(fmt=dispatch(io, fmt))) return -1; } } + +int +fmt·install(int verb, Formatter func) +{ + Verb *v; + int i, ret; + +lock: + if(verb <= 0 || verb >= 65536){ + ret = -1; + goto unlock; + } + if(!func) + func = badfmt; + + if((i = atomic·load(&formatter.len))==MaxFmt) + return -1; + + v = &formatter.verb[i]; + v->c = verb; + v->fmt = func; + + atomic·store(&formatter.len, i+1); + ret = 0; +unlock: + return ret; +} diff --git a/sys/libfmt/esprint.c b/sys/libfmt/esprint.c new file mode 100644 index 0000000..6d97340 --- /dev/null +++ b/sys/libfmt/esprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +char * +fmt·esprint(char *buf, char *end, char *fmt, ...) +{ + char *p; + va_list args; + + va_start(args, fmt); + p = fmt·vesprint(buf, end, fmt, args); + va_end(args); + + return p; +} diff --git a/sys/libfmt/float.c b/sys/libfmt/float.c new file mode 100644 index 0000000..63ea80f --- /dev/null +++ b/sys/libfmt/float.c @@ -0,0 +1,1077 @@ +#define FDIGIT 30 +#define FDEFLT 6 +#define NSIGNIF 17 + +static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; +static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; +static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; + +static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; + +static int +isNaN(double val) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + return (x.i&uvinf) == uvinf && (x.i&~uvneginf) != 0; +} + +static double +NaN(void) +{ + union{ + uvlong i; + double f; + }x; + x.i = uvnan; + return x.f; +} + +static int +isInf(double val, int sign) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + if(sign == 0) + return x.i == uvinf || x.i == uvneginf; + else if(sign == 1) + return x.i == uvinf; + else + return x.i == uvneginf; +} + +static double pows10[] = +{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, +}; + +static double +fpow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + neg = 1; + n = -n; + } + + if(n<arrlen(pows10)) + d = pows10[n]; + else{ + d = pows10[arrlen(pows10)-1]; + for(;;){ + n -= arrlen(pows10)- 1; + if(n < arrlen(pows10)){ + d *= pows10[n]; + break; + } + d *= pows10[arrlen(pows10)- 1]; + } + } + if(neg) + return 1./d; + return d; +} + +static int +add1(char *a, int n) +{ + int c; + char *b; + + if(n < 0 || n > NSIGNIF) + return 0; + + for(b = a+n-1; b >= a; b--){ + c = *b + 1; + if(c <= '9'){ + *b = c; + return 0; + } + *b = '0'; + } + /* + * need to overflow adding digit. + * shift number down and insert 1 at beginning. + * decimal is known to be 0s or we wouldn't + * have gotten this far. (e.g., 99999+1 => 00000) + */ + a[0] = '1'; + return 1; +} + +static int +sub1(char *a, int n) +{ + int c; + char *b; + + if(n < 0 || n > NSIGNIF) + return 0; + for(b = a+n-1; b >= a; b--){ + c = *b - 1; + if(c >= '0'){ + if(c == '0' && b == a){ + /* + * just zeroed the top digit; shift everyone up. + * decimal is known to be 9s or we wouldn't + * have gotten this far. (e.g., 10000-1 => 09999) + */ + *b = '9'; + return 1; + } + *b = c; + return 0; + } + *b = '9'; + } + /* + * can't get here. the number a is always normalized + * so that it has a nonzero first digit. + */ + abort(); +} + +// ----------------------------------------------------------------------- +// strtod + +#define Nbits 28 +#define Nmant 53 +#define Prec ((Nmant+Nbits+1)/Nbits) + +#define Sigbit (1<<(Prec*Nbits-Nmant)) /* first significant bit of Prec-th word */ +#define Ndig 1500 +#define One (ulong)(1<<Nbits) +#define Half (ulong)(One>>1) +#define Maxe 310 + +#define Fsign (1<<0) /* found - */ +#define Fesign (1<<1) /* found e- */ +#define Fdpoint (1<<2) /* found . */ + +#define S0 0 /* _ _S0 +S1 #S2 .S3 */ +#define S1 1 /* _+ #S2 .S3 */ +#define S2 2 /* _+# #S2 .S4 eS5 */ +#define S3 3 /* _+. #S4 */ +#define S4 4 /* _+#.# #S4 eS5 */ +#define S5 5 /* _+#.#e +S6 #S7 */ +#define S6 6 /* _+#.#e+ #S7 */ +#define S7 7 /* _+#.#e+# #S7 */ + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char *cmp; +}; + +static ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return (ulong)d; +} + +static void +frnorm(ulong *f) +{ + int i, c; + + c = 0; + for(i=Prec-1; i>0; i--) { + f[i] += c; + c = f[i] >> Nbits; + f[i] &= One-1; + } + f[0] += c; +} + +static int +fpcmp(char *a, ulong* f) +{ + ulong tf[Prec]; + int i, d, c; + + for(i=0; i<Prec; i++) + tf[i] = f[i]; + + for(;;) { + /* tf *= 10 */ + for(i=0; i<Prec; i++) + tf[i] = tf[i]*10; + frnorm(tf); + d = (tf[0] >> Nbits) + '0'; + tf[0] &= One-1; + + /* compare next digit */ + c = *a; + if(c == 0) { + if('0' < d) + return -1; + if(tf[0] != 0) + goto cont; + for(i=1; i<Prec; i++) + if(tf[i] != 0) + goto cont; + return 0; + } + if(c > d) + return +1; + if(c < d) + return -1; + a++; + cont:; +} +} + +static void +divby(char *a, int *na, int b) +{ + int n, c; + char *p; + + p = a; + n = 0; + while(n>>b == 0){ + c = *a++; + if(c == 0) { + while(n) { + c = n*10; + if(c>>b) + break; + n = c; + } + goto xx; + } + n = n*10 + c-'0'; + (*na)--; + } + for(;;){ + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + c = *a++; + if(c == 0) + break; + n = n*10 + c-'0'; + } + (*na)++; + xx: + while(n){ + n = n*10; + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + (*na)++; + } + *p = 0; +} + +static Tab tab1[] = +{ + 1, 0, "", + 3, 1, "7", + 6, 2, "63", + 9, 3, "511", + 13, 4, "8191", + 16, 5, "65535", + 19, 6, "524287", + 23, 7, "8388607", + 26, 8, "67108863", + 27, 9, "134217727", +}; + +static void +divascii(char *a, int *na, int *dp, int *bp) +{ + int b, d; + Tab *t; + + d = *dp; + if(d >= (int)(arrlen(tab1))) + d = (int)(arrlen(tab1))-1; + t = tab1 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) > 0) + d--; + *dp -= d; + *bp += b; + divby(a, na, b); +} + +static void +mulby(char *a, char *p, char *q, int b) +{ + int n, c; + + n = 0; + *p = 0; + for(;;) { + q--; + if(q < a) + break; + c = *q - '0'; + c = (c<<b) + n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } + while(n) { + c = n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } +} + +static Tab tab2[] = +{ + 1, 1, "", /* dp = 0-0 */ + 3, 3, "125", + 6, 5, "15625", + 9, 7, "1953125", + 13, 10, "1220703125", + 16, 12, "152587890625", + 19, 14, "19073486328125", + 23, 17, "11920928955078125", + 26, 19, "1490116119384765625", + 27, 19, "7450580596923828125", /* dp 8-9 */ +}; + +static void +mulascii(char *a, int *na, int *dp, int *bp) +{ + char *p; + int d, b; + Tab *t; + + d = -*dp; + if(d >= (int)(arrlen(tab2))) + d = (int)(arrlen(tab2))-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +cmp(char *a, char *b) +{ + int c1, c2; + + while((c1 = *b++) != '\0') { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} + +double +fmtstrtod(char *as, char **aas) +{ + int na, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec]; + double d; + char *s, a[Ndig]; + + flag = 0; /* Fsign, Fesign, Fdpoint */ + na = 0; /* number of digits of a[] */ + dp = 0; /* na of decimal point */ + ex = 0; /* exonent */ + + state = S0; + for(s=as;;s++){ + c = *s; + if('0' <= c && c <= '9'){ + switch(state){ + case S0: case S1: case S2: + state = S2; + break; + case S3: case S4: + state = S4; + break; + case S5: case S6: case S7: + state = S7; + ex = ex*10 + (c-'0'); + continue; + } + + if(na == 0 && c == '0'){ + dp--; + continue; + } + if(na < Ndig-50) + a[na++] = c; + continue; + } + switch(c){ + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + if(state == S0) + continue; + break; + case '-': + if(state == S0) + flag |= Fsign; + else + flag |= Fesign; + case '+': + if(state == S0) + state = S1; + else + if(state == S5) + state = S6; + else + break; /* syntax */ + continue; + case '.': + flag |= Fdpoint; + dp = na; + if(state == S0 || state == S1){ + state = S3; + continue; + } + if(state == S2){ + state = S4; + continue; + } + break; + case 'e': case 'E': + if(state == S2 || state == S4){ + state = S5; + continue; + } + break; + } + break; + } + + /* clean up return char-pointer */ + switch(state) { + case S0: + if(cmp(s, "nan") == 0){ + if(aas != nil) + *aas = s+3; + goto retnan; + } + case S1: + if(cmp(s, "infinity") == 0){ + if(aas != nil) + *aas = s+8; + goto retinf; + } + if(cmp(s, "inf") == 0){ + if(aas != nil) + *aas = s+3; + goto retinf; + } + case S3: + if(aas != nil) + *aas = as; + goto ret0; /* no digits found */ + case S6: + s--; /* back over +- */ + case S5: + s--; /* back over e */ + break; + } + if(aas != nil) + *aas = s; + + if(flag & Fdpoint) + while(na > 0 && a[na-1] == '0') + na--; + if(na == 0) + goto ret0; /* zero */ + a[na] = 0; + if(!(flag & Fdpoint)) + dp = na; + if(flag & Fesign) + ex = -ex; + dp += ex; + if(dp < -Maxe){ + errno = ERANGE; + goto ret0; /* underflow by exp */ + } else + if(dp > +Maxe) + goto retinf; /* overflow by exp */ + + /* + * normalize the decimal ascii number + * to range .[5-9][0-9]* e0 + */ + bp = 0; /* binary exponent */ + while(dp > 0) + divascii(a, &na, &dp, &bp); + while(dp < 0 || a[0] < '5') + mulascii(a, &na, &dp, &bp); + + /* close approx by naive conversion */ + mid[0] = 0; + mid[1] = 1; + for(i=0; (c=a[i]) != '\0'; i++) { + mid[0] = mid[0]*10 + (c-'0'); + mid[1] = mid[1]*10; + if(i >= 8) + break; + } + low[0] = umuldiv(mid[0], One, mid[1]); + hig[0] = umuldiv(mid[0]+1, One, mid[1]); + for(i=1; i<Prec; i++) { + low[i] = 0; + hig[i] = One-1; + } + + /* binary search for closest mantissa */ + for(;;) { + /* mid = (hig + low) / 2 */ + c = 0; + for(i=0; i<Prec; i++) { + mid[i] = hig[i] + low[i]; + if(c) + mid[i] += One; + c = mid[i] & 1; + mid[i] >>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i<Prec; i++) + if(low[i] != mid[i]) { + c = 0; + low[i] = mid[i]; + } + if(c) + break; /* between mid and hig */ + continue; + } + if(c < 0) { + for(i=0; i<Prec; i++) + hig[i] = mid[i]; + continue; + } + + /* only hard part is if even/odd roundings wants to go up */ + c = mid[Prec-1] & (Sigbit-1); + if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0) + mid[Prec-1] -= c; + break; /* exactly mid */ + } + + /* normal rounding applies */ + c = mid[Prec-1] & (Sigbit-1); + mid[Prec-1] -= c; + if(c >= Sigbit/2) { + mid[Prec-1] += Sigbit; + frnorm(mid); + } + goto out; + +ret0: + return 0; + +retnan: + return NaN(); + +retinf: + /* Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ + errno = ERANGE; + if(flag & Fsign) + return -HUGE_VAL; + return HUGE_VAL; + +out: + d = 0; + for(i=0; i<Prec; i++) + d = d*One + mid[i]; + if(flag & Fsign) + d = -d; + d = ldexp(d, bp - Prec*Nbits); + if(d == 0) /* underflow */ + errno = ERANGE; + + return d; +} + +#undef Nbits +#undef Nmant +#undef Prec + +#undef Sigbit +#undef Ndig +#undef One +#undef Half +#undef Maxe + +#undef Fsign +#undef Fesign +#undef Fdpoint + +#undef S0 +#undef S1 +#undef S2 +#undef S3 +#undef S4 +#undef S5 +#undef S6 +#undef S7 + +static void +fmtexp(char *p, int e, int ucase) +{ + int i; + char se[9]; + + *p++ = ucase ? 'E' : 'e'; + if(e < 0){ + *p++ = '-'; + e = -e; + }else + *p++ = '+'; + + i = 0; + while(e){ + se[i++] = e % 10 + '0'; + e /= 10; + } + + while(i < 2) + se[i++] = '0'; + while(i > 0) + *p++ = se[--i]; + + *p++ = '\0'; +} + +/* + * compute decimal integer m, exp such that: + * f = m*10^exp + * m is as short as possible with losing exactness + * assumes special cases (NaN, +Inf, -Inf) have been handled. + */ +static void +dtoa(double f, char *s, int *exp, int *neg, int *len) +{ + int c, d, e2, e, ee, i, ndigit, oerrno; + char buf[NSIGNIF+10]; + double g; + + oerrno = errno; + + *neg = 0; + if(f < 0){ + f = -f; + *neg = 1; + } + + if(f == 0){ + *exp = 0; + s[0] = '0'; + s[1] = 0; + *len = 1; + return; + } + + frexp(f, &e2); + e = (int)(e2 * .301029995664); + g = f * fpow10(-e); + while(g < 1) { + e--; + g = f * fpow10(-e); + } + while(g >= 10){ + e++; + g = f * fpow10(-e); + } + + /* convert nsignif digits as a first approximation */ + for(i=0; i<NSIGNIF; i++){ + d = (int)g; + s[i] = d+'0'; + g = (g-d)*10; + } + s[i] = 0; + + e -= NSIGNIF-1; + fmtexp(s+NSIGNIF, e, 0); + + for(i=0; i<10; i++) { + g=fmtstrtod(s, nil); + if(f > g) { + if(add1(s, NSIGNIF)){ + /* gained a digit */ + e--; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + if(f < g){ + if(sub1(s, NSIGNIF)){ + /* lost a digit */ + e++; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + break; + } + + /* + * bump last few digits down to 0 as we can. + */ + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--){ + c = s[i]; + if(c != '0'){ + s[i] = '0'; + g=fmtstrtod(s, nil); + if(g != f){ + s[i] = c; + break; + } + } + } + + /* + * remove trailing zeros. + */ + ndigit = NSIGNIF; + while(ndigit > 1 && s[ndigit-1] == '0'){ + e++; + --ndigit; + } + s[ndigit] = 0; + *exp = e; + *len = ndigit; + + errno = oerrno; +} + + +static int +fmtfloat(fmt·State *io) +{ + char buf[NSIGNIF+10], *dot, *digits, *p, *end, suf[10], *cur; + double val; + int c, verb, ndot, e, exp, f, ndigits, neg, newndigits; + int npad, pt, prec, realverb, sign, nsuf, ucase, n, z1, z2; + + if(io->flag&fmt·Long) + val = va_arg(io->args, long double); + else + val = va_arg(io->args, double); + + /* extract formatting flags */ + f = io->flag; + io->flag = 0; + prec = FDEFLT; + if(f & fmt·Prec) + prec = io->prec; + + verb = io->verb; + ucase = 0; + switch(verb) { + case 'A': + case 'E': + case 'F': + case 'G': + verb += 'a'-'A'; + ucase = 1; + break; + } + + /* pick off special numbers. */ + if(isNaN(val)) { + end = special[0+ucase]; + special: + io->flag = f & (fmt·Width|fmt·Left); + return copy(io, end, strlen(end), strlen(end)); + } + if(isInf(val, 1)) { + end = special[2+ucase]; + goto special; + } + if(isInf(val, -1)) { + end = special[4+ucase]; + goto special; + } + + /* get exact representation. */ + digits = buf; + dtoa(val, digits, &exp, &neg, &ndigits); + + /* get locale's decimal point. */ + dot = io->decimal; + if(dot == nil) + dot = "."; + ndot = utf8·len(dot); + + /* + * now the formatting fun begins. + * compute parameters for actual fmt: + * + * pad: number of spaces to insert before/after field. + * z1: number of zeros to insert before digits + * z2: number of zeros to insert after digits + * point: number of digits to print before decimal point + * ndigits: number of digits to use from digits[] + * suf: trailing suffix, like "e-5" + */ + realverb = verb; + switch(verb){ + case 'g': + /* convert to at most prec significant digits. (prec=0 means 1) */ + if(prec == 0) + prec = 1; + if(ndigits > prec) { + if(digits[prec] >= '5' && add1(digits, prec)) + exp++; + exp += ndigits-prec; + ndigits = prec; + } + + /* + * extra rules for %g (implemented below): + * trailing zeros removed after decimal unless FmtSharp. + * decimal point only if digit follows. + */ + + /* fall through to %e */ + default: + case 'e': + /* one significant digit before decimal, no leading zeros. */ + pt = 1; + z1 = 0; + + /* + * decimal point is after ndigits digits right now. + * slide to be after first. + */ + e = exp + (ndigits-1); + + /* if this is %g, check exponent and convert prec */ + if(realverb == 'g') { + if(-4 <= e && e < prec) + goto casef; + prec--; /* one digit before decimal; rest after */ + } + + /* compute trailing zero padding or truncate digits. */ + if(1+prec >= ndigits) + z2 = 1+prec - ndigits; + else { + /* truncate digits */ + assert(realverb != 'g'); + newndigits = 1+prec; + if(digits[newndigits] >= '5' && add1(digits, newndigits)) { + /* had 999e4, now have 100e5 */ + e++; + } + ndigits = newndigits; + z2 = 0; + } + fmtexp(suf, e, ucase); + nsuf = strlen(suf); + break; + + casef: + case 'f': + /* determine where digits go with respect to decimal point */ + if(ndigits+exp > 0) { + pt = ndigits+exp; + z1 = 0; + } else { + pt = 1; + z1 = 1 + -(ndigits+exp); + } + + /* + * %g specifies prec = number of significant digits + * convert to number of digits after decimal point + */ + if(realverb == 'g') + prec += z1 - pt; + + /* compute trailing zero padding or truncate digits. */ + if(pt+prec >= z1+ndigits) + z2 = pt+prec - (z1+ndigits); + else{ + /* truncate digits */ + assert(realverb != 'g'); + newndigits = pt+prec - z1; + if(newndigits < 0){ + z1 += newndigits; + newndigits = 0; + }else if(newndigits == 0){ + /* perhaps round up */ + if(digits[0] >= '5'){ + digits[0] = '1'; + newndigits = 1; + goto newdigit; + } + }else if(digits[newndigits] >= '5' && add1(digits, newndigits)){ + /* digits was 999, is now 100; make it 1000 */ + digits[newndigits++] = '0'; + newdigit: + /* account for new digit */ + if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ + z1--; + else /* 9.99 => 10.00 */ + pt++; + } + z2 = 0; + ndigits = newndigits; + } + nsuf = 0; + break; + } + + /* + * if %g is given without FmtSharp, remove trailing zeros. + * must do after truncation, so that e.g. print %.3g 1.001 + * produces 1, not 1.00. sorry, but them's the rules. + */ + if(realverb == 'g' && !(f & fmt·Sharp)) { + if(z1+ndigits+z2 >= pt) { + if(z1+ndigits < pt) + z2 = pt - (z1+ndigits); + else{ + z2 = 0; + while(z1+ndigits > pt && digits[ndigits-1] == '0') + ndigits--; + } + } + } + + /* + * compute width of all digits and decimal point and suffix if any + */ + n = z1+ndigits+z2; + if(n > pt) + n += ndot; + else if(n == pt){ + if(f & fmt·Sharp) + n += ndot; + else + pt++; /* do not print any decimal point */ + } + n += nsuf; + + /* + * determine sign + */ + sign = 0; + if(neg) + sign = '-'; + else if(f & fmt·Sign) + sign = '+'; + else if(f & fmt·Space) + sign = ' '; + if(sign) + n++; + + /* compute padding */ + npad = 0; + if((f & fmt·Width) && io->width > n) + npad = io->width - n; + if(npad && !(f & fmt·Left) && (f & fmt·Zero)){ + z1 += npad; + pt += npad; + npad = 0; + } + + /* format the actual field. too bad about doing this twice. */ + if(npad && !(f & fmt·Left) && pad(io, npad < 0)) + return -1; + + cur = io->buffer.cur; + end = io->buffer.end; + + if(sign){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = sign; + } + + while(z1>0 || ndigits>0 || z2>0){ + if(z1 > 0){ + z1--; + c = '0'; + }else if(ndigits > 0){ + ndigits--; + c = *digits++; + }else{ + z2--; + c = '0'; + } + + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = c; + + if(--pt == 0) + for(p=dot; *p; p++){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = *p; + } + } + io->n += cur - (char*)io->buffer.cur; + io->buffer.cur = cur; + if(nsuf && copy(io, suf, nsuf, nsuf) < 0) + return -1; + if(npad && (f & fmt·Left) && pad(io, npad < 0)) + return -1; + + return 0; +} diff --git a/sys/libfmt/fprint.c b/sys/libfmt/fprint.c new file mode 100644 index 0000000..26343f7 --- /dev/null +++ b/sys/libfmt/fprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(fd, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/internal.h b/sys/libfmt/internal.h index 56e64c8..725cfff 100644 --- a/sys/libfmt/internal.h +++ b/sys/libfmt/internal.h @@ -13,3 +13,5 @@ struct Verb int c; Formatter fmt; }; + +void fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups); diff --git a/sys/libfmt/locale.c b/sys/libfmt/locale.c new file mode 100644 index 0000000..437c61e --- /dev/null +++ b/sys/libfmt/locale.c @@ -0,0 +1,16 @@ +#include "internal.h" + +void +fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups) +{ + if(decimal == nil || decimal[0] == '\0') + decimal = "."; + if(thousands == nil) + thousands = ","; + if(groups == nil) + groups = "\3"; + + io->groups = groups; + io->decimal = decimal; + io->thousands = thousands; +} diff --git a/sys/libfmt/nsprint.c b/sys/libfmt/nsprint.c new file mode 100644 index 0000000..90489e0 --- /dev/null +++ b/sys/libfmt/nsprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fmt·nsprint(int len, char *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/open.c b/sys/libfmt/open.c new file mode 100644 index 0000000..8aadef5 --- /dev/null +++ b/sys/libfmt/open.c @@ -0,0 +1,34 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n, fd; + + fd = (uintptr)io->file; + n = io->buffer.cur - io->buffer.beg; + if(n && write(fd, io->buffer.beg, n) != n) + return -1; + + io->buffer.cur = io->buffer.beg; + return io->n; +} + +int +fmt·open(int fd, int len, char *buf, fmt·State *io) +{ + io->buffer.beg = buf; + io->buffer.cur = buf; + io->buffer.end = buf+len; + io->flush = flush; + io->file = (void*)(uintptr)fd; + io->flag = 0; + io->n = 0; + /* no heap needed */ + io->heap = nil; + io->mem = (mem·Reallocator){ 0 }; + + fmt·setlocale(io, nil, nil, nil); + + return 0; +} diff --git a/sys/libfmt/print.c b/sys/libfmt/print.c index e69de29..20b8e00 100644 --- a/sys/libfmt/print.c +++ b/sys/libfmt/print.c @@ -0,0 +1,13 @@ +#include "internal.h" + +int +fmt·print(char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(1, fmt, args); + va_end(args); + return n; +} diff --git a/sys/libfmt/rules.mk b/sys/libfmt/rules.mk new file mode 100644 index 0000000..2b1b431 --- /dev/null +++ b/sys/libfmt/rules.mk @@ -0,0 +1,36 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# Local sources +SRCS_$(d) :=\ + $(d)/buffer.c\ + $(d)/do.c\ + $(d)/esprint.c\ + $(d)/fprint.c\ + $(d)/locale.c\ + $(d)/nsprint.c\ + $(d)/open.c\ + $(d)/print.c\ + $(d)/sprint.c\ + $(d)/vesprint.c\ + $(d)/vfprint.c\ + $(d)/vnsprint.c\ + $(d)/vprint.c\ + $(d)/vwrite.c\ + $(d)/write.c + +LIBS_$(d) := $(d)/libfmt.a + +TSTS_$(d) := \ + $(d)/test.c + +include share/paths.mk + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/sys/libutf/libutf.a $(OBJ_DIR)/sys/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/sys/libfmt/sprint.c b/sys/libfmt/sprint.c new file mode 100644 index 0000000..f1be6dd --- /dev/null +++ b/sys/libfmt/sprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·sprint(char *buf, char *fmt, ...) +{ + int n; + uint len; + va_list args; + + len = 1 << 30; + if(buf+len < buf) + len = -(uintptr)buf-1; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/test.c b/sys/libfmt/test.c new file mode 100644 index 0000000..d81a62e --- /dev/null +++ b/sys/libfmt/test.c @@ -0,0 +1,72 @@ +#include <u.h> +#include <base.h> +#include <libutf.h> +#include <libfmt.h> + +typedef struct Complex +{ + double r, i; +} Complex; + +int +Xfmt(fmt·State *io) +{ + Complex c; + c = va_arg(io->args, Complex); + + return fmt·write(io, "(real=%g,imag=%g)", c.r, c.i); +} + +int +main(int argc, char *argv[]) +{ + fmt·print("basic tests\n"); + fmt·print("\tx: %x\n", 0x87654321); + fmt·print("\tu: %u\n", 0x87654321); + fmt·print("\td: %d\n", 0x87654321); + fmt·print("\ts: %s\n", "hi there"); + fmt·print("\tc: %c\n", '!'); + fmt·print("\tg: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\te: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tf: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tsmiley: %C\n", (rune)0x263a); + fmt·print("\t%g %.18g\n", 2e25, 2e25); + fmt·print("\t%2.18g\n", 1.0); + fmt·print("\t%2.18f\n", 1.0); + fmt·print("\t%f\n", 3.1415927/4); + fmt·print("\t%d\n", 23); + fmt·print("\t%i\n", 23); + fmt·print("\t%0.10d\n", 12345); + + fmt·print("%%4%%d tests\n"); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20); + fmt·print("\t%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20); + fmt·print("\t%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20); + + /* test %'d formats */ + fmt·print("%%'%%d tests\n"); + fmt·print("\t%'d %'d %'d\n", 1, 2222, 33333333); + fmt·print("\t%'019d\n", 0); + fmt·print("\t%08d %08d %08d\n", 1, 2222, 33333333); + fmt·print("\t%'08d %'08d %'08d\n", 1, 2222, 33333333); + fmt·print("\t%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345); + fmt·print("\t%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL); + + /* test precision */ + fmt·print("precision tests\n"); + fmt·print("%020.10d\n", 100); + + /* test install */ + fmt·install('X', Xfmt); + Complex c = { 1.5, -2.3 }; + fmt·print("x = %X\n", c); + + return 0; + +} diff --git a/sys/libfmt/vesprint.c b/sys/libfmt/vesprint.c new file mode 100644 index 0000000..18f4dd2 --- /dev/null +++ b/sys/libfmt/vesprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +char* +fmt·vesprint(char *buf, char *end, char *fmt, va_list args) +{ + fmt·State io; + + if(end <= buf) + return nil; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = end-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur; +} diff --git a/sys/libfmt/vfprint.c b/sys/libfmt/vfprint.c new file mode 100644 index 0000000..4306ea7 --- /dev/null +++ b/sys/libfmt/vfprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vfprint(int fd, char *fmt, va_list args) +{ + int n; + fmt·State io; + char buf[256]; + + fmt·open(fd, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/sys/libfmt/vnsprint.c b/sys/libfmt/vnsprint.c new file mode 100644 index 0000000..7ded908 --- /dev/null +++ b/sys/libfmt/vnsprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vnsprint(int len, char *buf, char *fmt, va_list args) +{ + fmt·State io; + + if(len <= 0) + return -1; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = buf+len-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur - io.buffer.beg; +} diff --git a/sys/libfmt/vprint.c b/sys/libfmt/vprint.c new file mode 100644 index 0000000..bb3076b --- /dev/null +++ b/sys/libfmt/vprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vprint(char *fmt, va_list args) +{ + fmt·State io; + int n; + char buf[256]; + + fmt·open(1, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/sys/libfmt/vwrite.c b/sys/libfmt/vwrite.c new file mode 100644 index 0000000..cacdef2 --- /dev/null +++ b/sys/libfmt/vwrite.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vwrite(fmt·State *io, char *fmt, va_list args) +{ + int n; + va_list tmp; + + io->flag = io->width = io->prec = 0; + + va_copy(tmp, io->args); + va_end(io->args); + + va_copy(io->args,args); + n = fmt·do(io, fmt); + va_end(io->args); + + va_copy(io->args, tmp); + va_end(tmp); + + io->flag = io->width = io->prec = 0; + + if(n >= 0) + return 0; + return n; +} diff --git a/sys/libfmt/write.c b/sys/libfmt/write.c new file mode 100644 index 0000000..9a77223 --- /dev/null +++ b/sys/libfmt/write.c @@ -0,0 +1,22 @@ +#include "internal.h" + +int +fmt·write(fmt·State *io, char *fmt, ...) +{ + int n; + va_list args; + + io->flag = io->width = io->prec = 0; + + va_copy(args, io->args); + va_end(io->args); + + va_start(io->args, fmt); + n = fmt·do(io, fmt); + va_end(io->args); + + io->flag = io->width = io->prec = 0; + if(n >= 0) + return 0; + return n; +} diff --git a/sys/libutf/rules.mk b/sys/libutf/rules.mk index 8372519..53ff8cf 100644 --- a/sys/libutf/rules.mk +++ b/sys/libutf/rules.mk @@ -42,12 +42,16 @@ $(d)/vendor/EastAsianWidth-$(UNICODE).txt: @echo "GET EastAsianWidth.txt";\ curl https://www.unicode.org/Public/$(UNICODE)/ucd/EastAsianWidth.txt > $@ +$(d)/vendor/EmojiData-$(UNICODE).txt: + @echo "GET EmojiData.txt";\ + curl https://www.unicode.org/Public/$(UNICODE)/ucd/emoji/emoji-data.txt > $@ + $(d)/vendor/mkrunewidth: $(d)/vendor/mkrunewidth.c $(d)/vendor/common.o $(OBJ_DIR)/sys/base/base.a $(COMPLINK) GENS += $(d)/vendor/mkrunewidth -$(d)/runewidth-$(UNICODE).c: $(d)/vendor/mkrunewidth $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/EastAsianWidth-$(UNICODE).txt +$(d)/runewidth-$(UNICODE).c: $(d)/vendor/mkrunewidth $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/EastAsianWidth-$(UNICODE).txt $(d)/vendor/EmojiData-$(UNICODE).txt @$(dir $@)vendor/mkrunewidth $(filter-out $<, $^) > $@ # grapheme boundaries diff --git a/sys/libutf/runetype-14.0.0.c b/sys/libutf/runetype-14.0.0.c deleted file mode 100644 index 6f4469d..0000000 --- a/sys/libutf/runetype-14.0.0.c +++ /dev/null @@ -1,111 +0,0 @@ -#include <u.h> -#include <libutf.h> - -static -rune* -rangesearch(rune c, rune *t, int n, int ne) -{ - rune *p; - int m; - while(n > 1) { - m = n >> 1; - p = t + m*ne; - if(c >= p[0]){ - t = p; - n = n-m; - }else - n = m; - } - if(n && c >= t[0]) - return t; - return 0; -} - -static rune isspace_range[] = { - 0x0009, 0x000d, - 0x0085, 0x0085, -}; - -int -utf8·isspace(rune c) -{ - rune *p; - - p = rangesearch(c, isspace_range, arrlen(isspace_range)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - return 0; -} - -int -utf8·isdigit(rune c) -{ - rune *p; - - return 0; -} - -int -utf8·isalpha(rune c) -{ - rune *p; - - return 0; -} - -int -utf8·isupper(rune c) -{ - rune *p; - - return 0; -} - -int -utf8·islower(rune c) -{ - rune *p; - - return 0; -} - -int -utf8·istitle(rune c) -{ - rune *p; - - return 0; -} - -int -utf8·ispunct(rune c) -{ - rune *p; - - return 0; -} - -rune -utf8·toupper(rune c) -{ - rune *p; - - return c; -} - -rune -utf8·tolower(rune c) -{ - rune *p; - - return c; -} - -rune -utf8·totitle(rune c) -{ - rune *p; - - return c; -} - diff --git a/sys/libutf/runewidth-14.0.0.c b/sys/libutf/runewidth-14.0.0.c deleted file mode 100644 index 113c35e..0000000 --- a/sys/libutf/runewidth-14.0.0.c +++ /dev/null @@ -1,71 +0,0 @@ -#include <u.h> -#include <libutf.h> - -static -rune* -rangesearch(rune c, rune *t, int n, int ne) -{ - rune *p; - int m; - while(n > 1) { - m = n >> 1; - p = t + m*ne; - if(c >= p[0]){ - t = p; - n = n-m; - }else - n = m; - } - if(n && c >= t[0]) - return t; - return 0; -} - -static rune width0_range[] = { - 0x2028, 0x2029, -}; - -static int -iswidth0(rune c) -{ - rune *p; - - p = rangesearch(c, width0_range, arrlen(width0_range)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - return 0; -} - -static rune width1_single[] = { - 0x00ad, -}; - -static int -iswidth1(rune c) -{ - rune *p; - - p = rangesearch(c, width1_single, arrlen(width1_single), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -static int -iswidth2(rune c) -{ - rune *p; - - return 0; -} - - -int -utf8·runewidth(rune c) -{ - if(iswidth1(c)) - return 1; - if(iswidth2(c)) - return 2; - return 0; -} diff --git a/sys/libutf/vendor/common.c b/sys/libutf/vendor/common.c new file mode 100644 index 0000000..5a03a50 --- /dev/null +++ b/sys/libutf/vendor/common.c @@ -0,0 +1,220 @@ +#include "common.h" + +// ----------------------------------------------------------------------- +// input functions + +int +parse(io·Stream *io, int nfield, char **field, int len, char *line) +{ + int n; + if((n=io·readln(io, len, line)) <= 0) + return ParseEOF; + + if(n == len) + panicf("line too long"); + + if(line[n-1] != '\n') + panicf("invalid line: expected '\n', found '%c'", line[n]); + + line[n-1] = 0; + + if(line[0] == '#' || line[0] == 0) + return ParseSkip; + + /* tokenize line into fields */ + n = 0; + field[n] = line; + while(*line){ + if(*line == ';'){ + *line = 0; + field[++n] = line+1; + } + line++; + } + + if(n != nfield-1) + panicf("expected %d number of fields, got %d: %s", nfield, n, line); + + return ParseOK; +} + +int +codepoint(char *s) +{ + int c, b; + + c = 0; + while((b=*s++)){ + c <<= 4; + if(b >= '0' && b <= '9') + c += b - '0'; + else if(b >= 'A' && b <= 'F') + c += b - 'A' + 10; + else + panicf("bad codepoint char '%c'", b); + } + + return c; +} + +void +codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop) +{ + int e, c; + char *other[NumFields], line[1024]; + + // XXX: the stop variable passes in the previous stopping character + e = *stop; + c = codepoint(field[Fcode]); + + if(c >= NumRunes) + panicf("unexpected large codepoint %x", c); + if(c <= e) + panicf("bad code sequence: %x then %x", e, c); + e = c; + + if(strstr(field[Fname], ", First>") != nil){ + if(!parse(utf8, arrlen(other), other, arrlen(line), line)) + panicf("range start at end of file"); + if(strstr(other[Fname], ", Last>") == nil) + panicf("range start not followed by range end"); + + e = codepoint(other[Fcode]); + + if(e <= c) + panicf("bad code sequence: %x then %x", c, e); + if(strcmp(field[Fcategory], other[Fcategory]) != 0) + panicf("range with mismatched category"); + } + + *start = c; + *stop = e; +} + +// ----------------------------------------------------------------------- +// output functions + +void +putsearch(void) +{ + puts( + "#include <u.h>\n" + "#include <libutf.h>\n" + "\n" + "static\n" + "rune*\n" + "rangesearch(rune c, rune *t, int n, int ne)\n" + "{\n" + " rune *p;\n" + " int m;\n" + " while(n > 1) {\n" + " m = n >> 1;\n" + " p = t + m*ne;\n" + " if(c >= p[0]){\n" + " t = p;\n" + " n = n-m;\n" + " }else\n" + " n = m;\n" + " }\n" + " if(n && c >= t[0])\n" + " return t;\n" + " return 0;\n" + "}\n" + ); + +} + +int +putrange(char *ident, char *prop, int force) +{ + int l, r, start; + + start = 0; + for(l = 0; l < NumRunes;) { + if(!prop[l]){ + l++; + continue; + } + + for(r = l+1; r < NumRunes; r++){ + if(!prop[r]) + break; + prop[r] = 0; + } + + if(force || r > l + 1){ + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[l] = 0; + printf("\t0x%.4x, 0x%.4x,\n", l, r-1); + } + + l = r; + } + + if(start) + printf("};\n\n"); + + return start; +} + +int +putpair(char *ident, char *prop) +{ + int l, r, start; + + start = 0; + for(l=0; l+2 < NumRunes; ){ + if(!prop[l]){ + l++; + continue; + } + + for(r = l + 2; r < NumRunes; r += 2){ + if(!prop[r]) + break; + prop[r] = 0; + } + + if(r != l + 2){ + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[l] = 0; + printf("\t0x%.4x, 0x%.4x,\n", l, r - 2); + } + + l = r; + } + + if(start) + printf("};\n\n"); + return start; +} + +int +putsingle(char *ident, char *prop) +{ + int i, start; + + start = 0; + for(i = 0; i < NumRunes; i++) { + if(!prop[i]) + continue; + + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[i] = 0; + printf("\t0x%.4x,\n", i); + } + + if(start) + printf("};\n\n"); + + return start; +} diff --git a/sys/libutf/vendor/common.h b/sys/libutf/vendor/common.h new file mode 100644 index 0000000..62f6c5b --- /dev/null +++ b/sys/libutf/vendor/common.h @@ -0,0 +1,46 @@ +#pragma once + +#include <u.h> +#include <base.h> +#include <libutf.h> + +enum +{ + // Fields inside UnicodeData.txt + Fcode, + Fname, + Fcategory, + Fcombine, + Fbidir, + Fdecomp, + Fdecimal, + Fdigit, + Fnumeric, + Fmirror, + Foldname, + Fcomment, + Fupper, + Flower, + Ftitle, + + NumFields, + NumRunes = 1 << 21, +}; + +/* input functions */ +enum +{ + ParseEOF, + ParseOK, + ParseSkip, +}; + +int parse(io·Stream *io, int nfield, char **field, int len, char *line); +int codepoint(char *s); +void codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop); + +/* output functions */ +void putsearch(void); +int putrange(char *ident, char *prop, int force); +int putpair(char *ident, char *prop); +int putsingle(char *ident, char *prop); diff --git a/sys/libutf/vendor/mkgraphemedata.c b/sys/libutf/vendor/mkgraphemedata.c new file mode 100644 index 0000000..ce5a952 --- /dev/null +++ b/sys/libutf/vendor/mkgraphemedata.c @@ -0,0 +1,24 @@ +#include <u.h> +#include <base.h> +#include <libutf.h> + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkgraphemedata <GraphemeBreakProperty.txt>\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + io·Stream *utf8; + char line[1024]; + + ARGBEGIN{ + }ARGEND; +} diff --git a/sys/libutf/vendor/mkrunetype.c b/sys/libutf/vendor/mkrunetype.c new file mode 100644 index 0000000..9f939f4 --- /dev/null +++ b/sys/libutf/vendor/mkrunetype.c @@ -0,0 +1,388 @@ +#include "common.h" + +// ----------------------------------------------------------------------- +// globals + +#define OFFSET (1 << 20) +#define DELTA(mapx, x) ((1 << 20) + (mapx) - (x)) + +// TODO: use bitarrays. will reduce executable size 8x +struct Table +{ + /* properties */ + char isspace[NumRunes]; + char isalpha[NumRunes]; + char ismark[NumRunes]; + char isdigit[NumRunes]; + char isupper[NumRunes]; + char islower[NumRunes]; + char istitle[NumRunes]; + char ispunct[NumRunes]; + char issymbl[NumRunes]; + char iscntrl[NumRunes]; + + char combine[NumRunes]; + + /* transformations */ + int toupper[NumRunes]; + int tolower[NumRunes]; + int totitle[NumRunes]; +}; + +static struct Table table; + +// ----------------------------------------------------------------------- +// internal functions + +static +int +isrange(char *label, char *prop, int force) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_range", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putrange(ident, prop, force); +} + +static +int +ispair(char *label, char *prop) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_pair", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putpair(ident, prop); +} + +static +int +issingle(char *label, char *prop) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_single", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putsingle(ident, prop); +} + +static +void +makeis(char *label, char *table, int pairs, int onlyranges) +{ + int hasr, hasp=0, hass=0; + + hasr = isrange(label, table, onlyranges); + if(!onlyranges && pairs) + hasp = ispair(label, table); + if(!onlyranges) + hass = issingle(label, table); + + printf( + "int\n" + "utf8·is%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(hasr){ + printf( + " p = rangesearch(c, is%s_range, arrlen(is%s_range)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return 1;\n", + label, label); + } + + if(hasp){ + printf( + " p = rangesearch(c, is%s_pair, arrlen(is%s_pair)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return 1;\n", + label, label); + } + + if(hass) + printf( + " p = rangesearch(c, is%s_single, arrlen(is%s_single), 1);\n" + " if(p && c == p[0])\n" + " return 1;\n", + label, label); + + printf( + " return 0;\n" + "}\n" + "\n"); +} + +static +int +torange(char *label, int *index, int force) +{ + int l, r, d, start = 0; + + for(l = 0; l < NumRunes; ){ + if(index[l] == l){ + l++; + continue; + } + + d = DELTA(index[l], l); + if(d != (rune)d) + panicf("bad map delta %d", d); + + for(r = l+1; r < NumRunes; r++){ + if(DELTA(index[r], r) != d) + break; + index[r] = r; + } + + if(force || r != l + 1){ + if(!start){ + printf("static rune to%s_range[] = {\n", label); + start = 1; + } + index[l] = l; + printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-1, d); + } + l = r; + } + if(start) + printf("};\n\n"); + + return start; +} + +static +int +topair(char *label, int *index) +{ + int l, r, d, start = 0; + + for(l = 0; l + 2 < NumRunes; ){ + if(index[l] == l){ + l++; + continue; + } + + d = DELTA(index[l], l); + if(d != (rune)d) + panicf("bad delta %d", d); + + for(r = l+2; r < NumRunes; r += 2){ + if(DELTA(index[r], r) != d) + break; + index[r] = r; + } + + if(r > l+2){ + if(!start){ + printf("static rune to%s_pair[] = {\n", label); + start = 1; + } + index[l] = l; + printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-2, d); + } + + l = r; + } + if(start) + printf("};\n\n"); + + return start; +} + +static +int +tosingle(char *label, int *index) +{ + int i, d, start = 0; + + for(i=0; i < NumRunes; i++) { + if(index[i] == i) + continue; + + d = DELTA(index[i], i); + if(d != (rune)d) + panicf("bad map delta %d", d); + + if(!start){ + printf("static rune to%s_single[] = {\n", label); + start = 1; + } + index[i] = i; + printf("\t0x%.4x, %d,\n", i, d); + } + if(start) + printf("};\n\n"); + + return start; +} + +static +void +mkto(char *label, int *index, int pairs, int onlyrange) +{ + int hasr, hasp=0, hass=0; + + hasr = torange(label, index, !onlyrange); + if(!onlyrange && pairs) + hasp = topair(label, index); + if(!onlyrange) + hass = tosingle(label, index); + + printf( + "rune\n" + "utf8·to%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(hasr) + printf( + " p = rangesearch(c, to%s_range, arrlen(to%s_range)/3, 3);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return c + p[2] - %d;\n", + label, label, OFFSET); + + if(hasp) + printf( + " p = rangesearch(c, to%s_pair, arrlen(to%s_pair)/3, 3);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return c + p[2] - %d;\n", + label, label, OFFSET); + + if(hass) + printf( + " p = rangesearch(c, to%s_single, arrlen(to%s_single)/2, 2);\n" + " if(p && c == p[0])\n" + " return c + p[1] - %d;\n", + label, label, OFFSET); + + + printf( + " return c;\n" + "}\n" + "\n" + ); +} + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkrunetype <UnicodeData.txt>\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i, sc, c, ec; + io·Stream *utf8; + char *prop, *field[NumFields], line[1024]; + + ARGBEGIN{ + }ARGEND; + + if(argc != 1) + usage(); + + if(!(utf8 = io·open(argv[0], "r"))) + panicf("can't open %s\n", argv[0]); + + /* by default each character maps to itself */ + for(i = 0; i < NumRunes; i++) { + table.toupper[i] = i; + table.tolower[i] = i; + table.totitle[i] = i; + } + + /* ensure all C local white space characters pass */ + table.isspace['\t'] = 1; + table.isspace['\n'] = 1; + table.isspace['\r'] = 1; + table.isspace['\f'] = 1; + table.isspace['\v'] = 1; + table.isspace[0x85] = 1; + + ec = -1; + // NOTE: we don't check for comments here: assume UnicodeData.txt doesn't have any + while(parse(utf8, arrlen(field), field, arrlen(line), line)){ + /* parse unicode range */ + codepointrange(utf8, field, &sc, &ec); + prop = field[Fcategory]; + + for(c = sc; c <= ec; c++){ + /* grab properties */ + switch(prop[0]){ + case 'L': + table.isalpha[c] = 1; + switch(prop[1]){ + case 'u': table.isupper[c] = 1; break; + case 'l': table.islower[c] = 1; break; + case 't': table.istitle[c] = 1; break; + case 'm': break; // modifier letters + case 'o': break; // ideograph letters + default: + goto badproperty; + } + break; + + case 'Z': + table.isspace[c] = 1; + break; + + case 'M': + table.ismark[c] = 1; + break; + + case 'N': + table.isdigit[c] = 1; + break; + + case 'P': + table.ispunct[c] = 1; + break; + + case 'S': + table.issymbl[c] = 1; + break; + + case 'C': + table.iscntrl[c] = 1; + break; + + default: badproperty: + panicf("unrecognized category '%s'", prop); + } + /* grab transformations */ + if(*field[Fupper]) + table.toupper[c] = codepoint(field[Fupper]); + if(*field[Flower]) + table.tolower[c] = codepoint(field[Flower]); + if(*field[Ftitle]) + table.totitle[c] = codepoint(field[Ftitle]); + } + } + io·close(utf8); + + putsearch(); + + makeis("space", table.isspace, 0, 1); + makeis("digit", table.isdigit, 0, 1); + makeis("alpha", table.isalpha, 0, 0); + makeis("upper", table.isupper, 1, 0); + makeis("lower", table.islower, 1, 0); + makeis("title", table.istitle, 1, 0); + makeis("punct", table.ispunct, 1, 0); + + mkto("upper", table.toupper, 1, 0); + mkto("lower", table.tolower, 1, 0); + mkto("title", table.totitle, 1, 0); +} diff --git a/sys/libutf/vendor/mkrunewidth.c b/sys/libutf/vendor/mkrunewidth.c new file mode 100644 index 0000000..14e6973 --- /dev/null +++ b/sys/libutf/vendor/mkrunewidth.c @@ -0,0 +1,325 @@ +#include "common.h" + +/* + * inspired by design choices in utf8proc/charwidths.jl + * all widths default to 1 unless they fall within the categories: + * 1. Mn 2. Mc 3. Me 4. Zl + * 5. Zp 6. Cc 7. Cf 8. Cs + * these default to zero width + */ +enum +{ + /* width ? */ + WidthNeutral, /* (N) practially treated like narrow but unclear ... */ + WidthAmbiguous, /* (A) sometimes wide and sometimes not... */ + /* width 1 */ + WidthHalf, /* (H) = to narrow (compatability equivalent) */ + WidthNarrow, /* (Na) ASCII width */ + /* width 2 */ + WidthWide, /* (W) 2x width */ + WidthFull, /* (F) = to wide (compatability equivalent) */ +}; + +struct Table +{ + char width[3][NumRunes]; +}; + +static struct Table table; + +// ----------------------------------------------------------------------- +// internal functions + +static +void +parse_category(char *path) +{ + int sc, c, ec, w; + io·Stream *utf8; + char *prop, *field[NumFields], line[1024]; + + if(!(utf8 = io·open(path, "r"))) + panicf("can't open %s\n", path); + + // NOTE: we don't check for comments here + ec = -1; + while(parse(utf8, arrlen(field), field, arrlen(line), line)){ + codepointrange(utf8, field, &sc, &ec); + + prop = field[Fcategory]; + + switch(prop[0]){ + case 'M': + switch(prop[1]){ + case 'n': case 'c': case 'e': + w = 0; + break; + default: + w = 1; + break; + } + break; + case 'Z': + switch(prop[1]){ + case 'l': case 'p': + w = 0; + break; + default: + w = 1; + break; + } + break; + case 'C': + switch(prop[1]){ + case 'c': case 'f': case 's': + w = 0; + break; + default: + w = 1; + break; + } + default: + w = 1; + } + + for(c = sc; c <= ec; c++) + table.width[w][c] = 1; + } + + io·close(utf8); +} + +static +void +coderange(char *field, int *l, int *r) +{ + char *s; + + if(!(s = strstr(field, ".."))) + *l=*r=codepoint(field); + else{ + *s++ = 0, *s++ = 0; + *l=codepoint(field); + *r=codepoint(s); + } +} + +static +void +parse_eawidths(char *path) +{ + int at, w; + int l, c, r; + io·Stream *utf8; + char *field[2], line[1024]; + + utf8 = io·open(path, "r"); + while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ + if(at == ParseSkip) + continue; + + switch(field[1][0]){ + case 'A': continue; + case 'N': + if(field[1][1] != 'a') + continue; + /* fallthrough */ + case 'H': w = 1; break; + + case 'W': /* fallthrough */ + case 'F': w = 2; break; + + default: + panicf("malformed east asian width class: %s\n", field[1]); + } + + coderange(field[0], &l, &r); + + for(c=l; c <= r; c++){ + /* ensure it only exists in one table */ + table.width[w][c] = 1; + table.width[(w+1)%3][c] = 0; + table.width[(w+2)%3][c] = 0; + } + } + io·close(utf8); +} + +static +void +parse_emoji(char *path) +{ + int at, w; + int l, c, r; + io·Stream *utf8; + char *s, *field[2], line[1024]; + + utf8 = io·open(path, "r"); + while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ + if(at == ParseSkip) + continue; + + /* only override emoji presentation */ + if(!strstr(field[1], "Emoji_Presentation")) + continue; + + /* trim trailing space */ + for(s=field[0]; *s; s++){ + if(*s == ' ') + *s = 0; + } + + coderange(field[0], &l, &r); + + for(c=l; c <= r; c++){ + table.width[0][c] = 0; + table.width[1][c] = 0; + table.width[2][c] = 1; + } + } + + io·close(utf8); +} + +/* output functions */ +static +void +maketable(char *label, char *table, int pairs, int onlyranges) +{ + int r, p=0, s=0; + char ident[3][128]; + + enum + { + Irange, + Ipair, + Isingle, + }; + + /* ranges */ + if(snprintf(ident[Irange], arrlen(ident[Irange]), "%s_range", label) == arrlen(ident[Irange])) + panicf("out of identifier space\n"); + r = putrange(ident[Irange], table, onlyranges); + + if(!onlyranges && pairs){ + if(snprintf(ident[Ipair], arrlen(ident[Ipair]), "%s_pair", label) == arrlen(ident[Ipair])) + panicf("out of identifier space\n"); + p = putpair(ident[Ipair], table); + } + if(!onlyranges){ + if(snprintf(ident[Isingle], arrlen(ident[Isingle]), "%s_single", label) == arrlen(ident[Isingle])) + panicf("out of identifier space\n"); + + s = putsingle(ident[Isingle], table); + } + + printf( + "static int\n" + "is%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(r){ + printf( + " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return 1;\n", + ident[Irange], ident[Irange]); + } + + if(p){ + printf( + " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return 1;\n", + ident[Ipair], ident[Ipair]); + } + + if(s) + printf( + " p = rangesearch(c, %s, arrlen(%s), 1);\n" + " if(p && c == p[0])\n" + " return 1;\n", + ident[Isingle], ident[Isingle]); + + printf( + " return 0;\n" + "}\n" + "\n"); +} + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkrunewidth <UnicodeData.txt> <EastAsianWidth.txt> <EmojiData.txt>\n"); + exit(1); +} + +#define SETW0(c) \ + table.width[0][(c)] = 1, \ + table.width[1][(c)] = 0, \ + table.width[2][(c)] = 0; + +#define SETW1(c) \ + table.width[0][(c)] = 0, \ + table.width[1][(c)] = 1, \ + table.width[2][(c)] = 0; + +#define SETW2(c) \ + table.width[0][(c)] = 0, \ + table.width[1][(c)] = 0, \ + table.width[2][(c)] = 1; + + +int +main(int argc, char *argv[]) +{ + int c; + + ARGBEGIN{ + }ARGEND; + + if(argc != 3) + usage(); + + parse_category(*argv++); + parse_eawidths(*argv++); + parse_emoji(*argv); + + /* overrides */ + SETW0(0x2028); + SETW0(0x2029); + + SETW1(0x00AD); + + /* simple checking */ + for(c=0; c<NumRunes; c++){ + if(table.width[0][c] + table.width[1][c] + table.width[2][c] > 1) + panicf("improper table state"); + } + + putsearch(); + + maketable("width0", table.width[0], 1, 0); + maketable("width1", table.width[1], 1, 0); + maketable("width2", table.width[2], 1, 0); + + puts( + "\n" + "int\n" + "utf8·runewidth(rune c)\n" + "{\n" + " if(iswidth1(c))\n" + " return 1;\n" + " if(iswidth2(c))\n" + " return 2;\n" + " return 0;\n" + "}" + ); +} diff --git a/sys/rules.mk b/sys/rules.mk index cefa4a9..6d1dfa5 100644 --- a/sys/rules.mk +++ b/sys/rules.mk @@ -11,6 +11,9 @@ include $(DIR)/rules.mk DIR := $(d)/libutf include $(DIR)/rules.mk +DIR := $(d)/libfmt +include $(DIR)/rules.mk + DIR := $(d)/libmath include $(DIR)/rules.mk |