ref: ca17b8e78d4ff54ecfca3985c7acc16a09f543dc
parent: e1bc44b385260be39793806042eb66a56d8ec646
author: mag <[email protected]>
date: Sun May 21 15:08:04 EDT 2023
llt stuff moved into the project root - lltinit.c renamed llt.c - int2str.c moved into llt.c.
--- a/3rd/wcwidth.c
+++ b/3rd/wcwidth.c
@@ -15,7 +15,7 @@
* https://github.com/termux/termux-packages/tree/master/packages/libandroid-support
*/
-#include "llt.h"
+#include "../llt.h"
struct width_interval {
int start;
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
TARG=flisp
CFLAGS?=-O2 -g
-CFLAGS+=-Wall -Wextra -Wno-parentheses -std=c99 -I3rd -Illt -Iposix
+CFLAGS+=-Wall -Wextra -Wno-parentheses -std=c99 -I3rd -Iposix
LDFLAGS?=
OBJS=\
@@ -21,18 +21,17 @@
print.o\
equal.o\
types.o\
- llt/bitvector-ops.o\
- llt/bitvector.o\
- llt/dump.o\
- llt/hashing.o\
- llt/htable.o\
- llt/int2str.o\
- llt/ios.o\
- llt/lltinit.o\
- llt/ptrhash.o\
- llt/random.o\
- llt/timefuncs.o\
- llt/utf8.o\
+ bitvector-ops.o\
+ bitvector.o\
+ dump.o\
+ hashing.o\
+ htable.o\
+ ios.o\
+ llt.o\
+ ptrhash.o\
+ random.o\
+ timefuncs.o\
+ utf8.o\
3rd/mp/mpadd.o\
3rd/mp/mpaux.o\
3rd/mp/mpcmp.o\
--- /dev/null
+++ b/bitvector-ops.c
@@ -1,0 +1,477 @@
+#include "llt.h"
+
+#define ONES32 ((uint32_t)0xffffffffUL)
+
+// greater than this # of words we use malloc instead of alloca
+#define MALLOC_CUTOFF 2000
+
+static inline
+uint32_t count_bits(uint32_t b)
+{
+ b = b - ((b>>1)&0x55555555);
+ b = ((b>>2)&0x33333333) + (b&0x33333333);
+ b = ((b>>4)+b)&0x0f0f0f0f;
+ b += (b>>8);
+ b += (b>>16);
+ return b & 0x3f;
+}
+
+uint32_t
+bitreverse(uint32_t x)
+{
+ uint32_t m;
+
+#ifdef __INTEL_COMPILER
+ x = _bswap(x);
+#else
+ x = (x >> 16) | (x << 16); m = 0xff00ff00;
+ x = ((x & m) >> 8) | ((x & ~m) << 8);
+#endif
+ m = 0xf0f0f0f0;
+ x = ((x & m) >> 4) | ((x & ~m) << 4); m = 0xcccccccc;
+ x = ((x & m) >> 2) | ((x & ~m) << 2); m = 0xaaaaaaaa;
+ x = ((x & m) >> 1) | ((x & ~m) << 1);
+
+ return x;
+}
+
+// shift all bits in a long bit vector
+// n is # of int32s to consider, s is shift distance
+// lowest bit-index is bit 0 of word 0
+// TODO: handle boundary case of shift distance >= data size?
+void
+bitvector_shr(uint32_t *b, size_t n, uint32_t s)
+{
+ uint32_t i;
+ if(s == 0 || n == 0)
+ return;
+ i = s >> 5;
+ if(i){
+ n -= i;
+ memmove(b, &b[i], n*4);
+ memset(&b[n], 0, i*4);
+ s &= 31;
+ }
+ for(i = 0; i < n-1; i++)
+ b[i] = (b[i] >> s) | (b[i+1] << (32-s));
+ b[i] >>= s;
+}
+
+// out-of-place version, good for re-aligning a strided submatrix to
+// linear representation when a copy is needed
+// assumes that dest has the same amount of space as source, even if it
+// wouldn't have been necessary to hold the shifted bits
+void
+bitvector_shr_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s)
+{
+ uint32_t i, j;
+ if(n == 0)
+ return;
+ if(s == 0){
+ memmove(dest, b, n*4);
+ return;
+ }
+ j = s >> 5;
+ if(j){
+ n -= j;
+ memset(&dest[n], 0, j*4);
+ s &= 31;
+ b = &b[j];
+ }
+ for(i = 0; i < n-1; i++)
+ dest[i] = (b[i] >> s) | (b[i+1] << (32-s));
+ dest[i] = b[i]>>s;
+}
+
+void
+bitvector_shl(uint32_t *b, size_t n, uint32_t s)
+{
+ uint32_t i, scrap = 0, temp;
+ if(s == 0 || n == 0)
+ return;
+ i = s >> 5;
+ if(i){
+ n -= i;
+ memmove(&b[i], b, n*4);
+ memset(b, 0, i*4);
+ s &= 31;
+ b = &b[i];
+ }
+ for(i = 0; i < n; i++){
+ temp = (b[i] << s) | scrap;
+ scrap = b[i] >> (32-s);
+ b[i] = temp;
+ }
+}
+
+// if dest has more space than source, set scrap to true to keep the
+// top bits that would otherwise be shifted out
+void
+bitvector_shl_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s, int scrap)
+{
+ uint32_t i, j, sc = 0;
+ if(n == 0)
+ return;
+ if(s == 0){
+ memmove(dest, b, n*4);
+ return;
+ }
+ j = s >> 5;
+ if(j){
+ n -= j;
+ memset(dest, 0, j*4);
+ s &= 31;
+ dest = &dest[j];
+ }
+ for(i = 0; i < n; i++){
+ dest[i] = (b[i] << s) | sc;
+ sc = b[i] >> (32-s);
+ }
+ if(scrap)
+ dest[i] = sc;
+}
+
+// set nbits to c, starting at given bit offset
+// assumes offs < 32
+void
+bitvector_fill(uint32_t *b, uint32_t offs, uint32_t c, uint32_t nbits)
+{
+ uint32_t i, nw, tail, mask;
+
+ if(nbits == 0)
+ return;
+ nw = (offs+nbits+31)>>5;
+
+ if(nw == 1){
+ mask = (lomask(nbits)<<offs);
+ if(c)
+ b[0] |= mask;
+ else
+ b[0] &= ~mask;
+ return;
+ }
+
+ mask = lomask(offs);
+ if(c)
+ b[0] |= ~mask;
+ else
+ b[0] &= mask;
+
+ mask = c ? ONES32 : 0;
+
+ for(i = 1; i < nw-1; i++)
+ b[i] = mask;
+
+ tail = (offs+nbits) & 31;
+ if(tail == 0)
+ b[i] = mask;
+ else{
+ mask = lomask(tail);
+ if(c)
+ b[i] |= mask;
+ else
+ b[i] &= ~mask;
+ }
+}
+
+void
+bitvector_not(uint32_t *b, uint32_t offs, uint32_t nbits)
+{
+ uint32_t i, nw, tail, mask;
+
+ if(nbits == 0)
+ return;
+ nw = (offs+nbits+31)>>5;
+
+ if(nw == 1){
+ mask = lomask(nbits)<<offs;
+ b[0] ^= mask;
+ return;
+ }
+
+ mask = ~lomask(offs);
+ b[0] ^= mask;
+
+ for(i = 1; i < nw-1; i++)
+ b[i] = ~b[i];
+
+ tail = (offs+nbits)&31;
+ if(tail == 0)
+ b[i] = ~b[i];
+ else{
+ mask = lomask(tail);
+ b[i] ^= mask;
+ }
+}
+
+// constant-space bit vector copy in a single pass, with arbitrary
+// offsets and lengths. to get this right, there are 16 cases to handle!
+#define BITVECTOR_COPY_OP(name, OP) \
+void \
+bitvector_##name(uint32_t *dest, uint32_t doffs, uint32_t *src, uint32_t soffs, uint32_t nbits) \
+{ \
+ uint32_t i, s, nw, tail, snw, mask, scrap; \
+ if(nbits == 0) \
+ return; \
+ nw = (doffs+nbits+31)>>5; \
+ if(soffs == doffs){ \
+ if(nw == 1){ \
+ mask = (lomask(nbits)<<doffs); \
+ dest[0] = (dest[0] & ~mask) | (OP(src[0]) & mask); \
+ return; \
+ } \
+ mask = ~lomask(doffs); \
+ dest[0] = (dest[0] & ~mask) | (OP(src[0]) & mask); \
+ for(i = 1; i < nw-1; i++) \
+ dest[i] = OP(src[i]); \
+ tail = (doffs+nbits)&31; \
+ if(tail == 0) \
+ dest[i] = src[i]; \
+ else { \
+ mask = lomask(tail); \
+ dest[i] = (dest[i] & ~mask) | (OP(src[i]) & mask); \
+ } \
+ return; \
+ } \
+ snw = (soffs+nbits+31)>>5; \
+ if(soffs < doffs){ \
+ s = doffs-soffs; \
+ if(nw == 1){ \
+ mask = lomask(nbits) << doffs; \
+ dest[0] = (dest[0] & ~mask) | ((OP(src[0])<<s) & mask); \
+ return; \
+ } \
+ mask = ~lomask(doffs); \
+ dest[0] = (dest[0] & ~mask) | ((OP(src[0])<<s) & mask); \
+ scrap = OP(src[0])>>(32-s); \
+ for(i = 1; i < snw-1; i++){ \
+ dest[i] = (OP(src[i])<<s) | scrap; \
+ scrap = OP(src[i])>>(32-s); \
+ } \
+ tail = (doffs+nbits)&31; \
+ mask = tail ? lomask(tail) : ONES32; \
+ if(snw == nw) \
+ dest[i] = (dest[i] & ~mask) | (((OP(src[i])<<s)|scrap) & mask); \
+ else{ /* snw < nw */ \
+ if(snw == 1) \
+ dest[i] = (dest[i] & ~mask) | (((OP(src[i])<<s) | scrap) & mask); \
+ else{ \
+ dest[i] = (OP(src[i])<<s) | scrap; \
+ scrap = OP(src[i])>>(32-s); \
+ i++; \
+ dest[i] = (dest[i] & ~mask) | (scrap & mask); \
+ } \
+ } \
+ }else{ \
+ s = soffs-doffs; \
+ if(snw == 1){ \
+ mask = (lomask(nbits)<<doffs); \
+ dest[0] = (dest[0] & ~mask) | ((OP(src[0])>>s) & mask); \
+ return; \
+ } \
+ if(nw == 1){ \
+ mask = (lomask(nbits)<<doffs); \
+ dest[0] = (dest[0] & ~mask) | \
+ (((OP(src[0])>>s)|(OP(src[1])<<(32-s))) & mask); \
+ return; \
+ } \
+ mask = ~lomask(doffs); \
+ dest[0] = (dest[0] & ~mask) | (((OP(src[0])>>s)|(OP(src[1])<<(32-s))) & mask); \
+ for(i = 1; i < nw-1; i++) \
+ dest[i] = (OP(src[i])>>s) | (OP(src[i+1])<<(32-s)); \
+ tail = (doffs+nbits)&31; \
+ mask = tail ? lomask(tail) : ONES32; \
+ if(snw == nw){ \
+ dest[i] = (dest[i] & ~mask) | ((OP(src[i])>>s) & mask); \
+ } \
+ else /* snw > nw */ { \
+ dest[i] = (dest[i] & ~mask) | \
+ (((OP(src[i])>>s)|(OP(src[i+1])<<(32-s))) & mask); \
+ } \
+ } \
+}
+
+#define BV_COPY(a) (a)
+#define BV_NOT(a) (~(a))
+BITVECTOR_COPY_OP(copy, BV_COPY)
+BITVECTOR_COPY_OP(not_to, BV_NOT)
+
+// copy from source to dest while reversing bit-order
+// assumes dest offset == 0
+// assumes source and dest don't overlap
+// assumes offset < 32
+void
+bitvector_reverse_to(uint32_t *dest, uint32_t *src, uint32_t soffs, uint32_t nbits)
+{
+ uint32_t i, nw, tail;
+
+ if(nbits == 0)
+ return;
+
+ nw = (soffs+nbits+31)>>5;
+ // first, reverse the words while reversing bit order within each word
+ for(i = 0; i < nw/2; i++){
+ dest[i] = bitreverse(src[nw-i-1]);
+ dest[nw-i-1] = bitreverse(src[i]);
+ }
+ if(nw&0x1)
+ dest[i] = bitreverse(src[i]);
+
+ tail = (soffs+nbits)&31;
+ if(tail)
+ bitvector_shr(dest, nw, 32-tail);
+}
+
+void
+bitvector_reverse(uint32_t *b, uint32_t offs, uint32_t nbits)
+{
+ uint32_t i, nw, tail, *temp, a[MALLOC_CUTOFF];
+
+ if(nbits == 0)
+ return;
+
+ nw = (offs+nbits+31)>>5;
+ temp = (nw > MALLOC_CUTOFF) ? malloc(nw*4) : a;
+ for(i = 0; i < nw/2; i++){
+ temp[i] = bitreverse(b[nw-i-1]);
+ temp[nw-i-1] = bitreverse(b[i]);
+ }
+ if(nw & 1)
+ temp[i] = bitreverse(b[i]);
+
+ tail = (offs+nbits)&31;
+ bitvector_copy(b, offs, temp, (32-tail)&31, nbits);
+ if(nw > MALLOC_CUTOFF)
+ free(temp);
+}
+
+uint64_t
+bitvector_count(uint32_t *b, uint32_t offs, uint64_t nbits)
+{
+ size_t i, nw;
+ uint32_t ntail;
+ uint64_t ans;
+
+ if(nbits == 0)
+ return 0;
+ nw = ((uint64_t)offs+nbits+31)>>5;
+
+ if(nw == 1)
+ return count_bits(b[0] & (lomask(nbits)<<offs));
+
+ ans = count_bits(b[0]>>offs); // first end cap
+
+ for(i = 1; i < nw-1; i++)
+ ans += count_bits(b[i]);
+
+ ntail = (offs + (uint32_t)nbits) & 31;
+ ans += count_bits(b[i] & (ntail > 0 ? lomask(ntail) : ONES32)); // last end cap
+
+ return ans;
+}
+
+uint32_t
+bitvector_any0(uint32_t *b, uint32_t offs, uint32_t nbits)
+{
+ uint32_t i, nw, tail, mask;
+
+ if(nbits == 0)
+ return 0;
+ nw = (offs+nbits+31)>>5;
+
+ if(nw == 1){
+ mask = (lomask(nbits)<<offs);
+ if((b[0] & mask) != mask)
+ return 1;
+ return 0;
+ }
+
+ mask = ~lomask(offs);
+ if((b[0] & mask) != mask)
+ return 1;
+
+ for(i = 1; i < nw-1; i++)
+ if(b[i] != ONES32)
+ return 1;
+
+ tail = (offs+nbits)&31;
+ if(tail == 0)
+ return b[i] != ONES32;
+ mask = lomask(tail);
+ return (b[i] & mask) != mask;
+}
+
+uint32_t
+bitvector_any1(uint32_t *b, uint32_t offs, uint32_t nbits)
+{
+ uint32_t i, nw, tail, mask;
+
+ if(nbits == 0)
+ return 0;
+ nw = (offs+nbits+31)>>5;
+
+ if(nw == 1){
+ mask = lomask(nbits)<<offs;
+ return (b[0] & mask) != 0;
+ }
+
+ mask = ~lomask(offs);
+ if((b[0] & mask) != 0)
+ return 1;
+
+ for(i = 1; i < nw-1; i++){
+ if(b[i] != 0)
+ return 1;
+ }
+
+ tail = (offs+nbits)&31;
+ if(tail == 0)
+ return b[i] != 0;
+ return (b[i] & lomask(tail)) != 0;
+}
+
+static void
+adjust_offset_to(uint32_t *dest, uint32_t *src, uint32_t nw, uint32_t soffs, uint32_t newoffs)
+{
+ if(newoffs > soffs)
+ bitvector_shl_to(dest, src, nw, newoffs-soffs, 1);
+ else
+ bitvector_shr_to(dest, src, nw, soffs-newoffs);
+}
+
+#define BITVECTOR_BINARY_OP_TO(opname, OP) \
+void \
+bitvector_##opname##_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits) \
+{ \
+ uint32_t nw = (doffs+nbits+31)>>5; \
+ uint32_t atmp[MALLOC_CUTOFF+1]; \
+ uint32_t *temp = nw>MALLOC_CUTOFF ? malloc((nw+1)*4) : atmp; \
+ uint32_t i, anw, bnw; \
+ if(aoffs == boffs){ \
+ anw = (aoffs+nbits+31)>>5; \
+ }else if(aoffs == doffs){ \
+ bnw = (boffs+nbits+31)>>5; \
+ adjust_offset_to(temp, b, bnw, boffs, aoffs); \
+ b = temp; \
+ anw = nw; \
+ }else{ \
+ anw = (aoffs+nbits+31)>>5; \
+ bnw = (boffs+nbits+31)>>5; \
+ adjust_offset_to(temp, a, anw, aoffs, boffs); \
+ a = temp; \
+ aoffs = boffs; \
+ anw = bnw; \
+ } \
+ for(i = 0; i < anw; i++) \
+ temp[i] = OP(a[i], b[i]); \
+ bitvector_copy(dest, doffs, temp, aoffs, nbits); \
+ if(nw>MALLOC_CUTOFF) \
+ free(temp); \
+}
+
+#define BV_AND(a, b) ((a)&(b))
+#define BV_OR(a, b) ((a)|(b))
+#define BV_XOR(a, b) ((a)^(b))
+BITVECTOR_BINARY_OP_TO(and, BV_AND)
+BITVECTOR_BINARY_OP_TO(or, BV_OR)
+BITVECTOR_BINARY_OP_TO(xor, BV_XOR)
--- /dev/null
+++ b/bitvector.c
@@ -1,0 +1,140 @@
+/*
+ bit vector primitives
+
+ todo:
+ * reverse
+ * nreverse
+ (- rotate left/right)
+ * shl_to
+ * not
+ - shr_row, shl_row
+
+ These routines are the back end supporting bit matrices. Many operations
+ on bit matrices are slow (such as accessing or setting a single element!)
+ but certain operations are privileged and lend themselves to extremely
+ efficient implementation due to the bit-vector nature of machine integers.
+ These are:
+ done:
+ & | $ ~ copy reverse fill sum prod
+ todo:
+ shift trans rowswap
+ would be nice:
+ channel interleave
+
+ Important note:
+ Out-of-place functions always assume dest and source have the same amount
+ of space available.
+
+ shr_to, shl_to, not_to, and reverse_to assume source and dest don't overlap
+ and_to, or_to, and xor_to allow overlap.
+*/
+
+#include "llt.h"
+
+uint32_t *
+bitvector_resize(uint32_t *b, uint64_t oldsz, uint64_t newsz, int initzero)
+{
+ uint32_t *p;
+ size_t sz = ((newsz+31)>>5) * sizeof(uint32_t);
+ p = LLT_REALLOC(b, sz);
+ if(p == nil)
+ return nil;
+ if(initzero && newsz>oldsz){
+ size_t osz = ((oldsz+31)>>5) * sizeof(uint32_t);
+ memset(&p[osz/sizeof(uint32_t)], 0, sz-osz);
+ }
+ return p;
+}
+
+uint32_t *
+bitvector_new(uint64_t n, int initzero)
+{
+ return bitvector_resize(nil, 0, n, initzero);
+}
+
+size_t
+bitvector_nwords(uint64_t nbits)
+{
+ return (nbits+31)>>5;
+}
+
+void
+bitvector_set(uint32_t *b, uint64_t n, uint32_t c)
+{
+ if(c)
+ b[n>>5] |= 1<<(n&31);
+ else
+ b[n>>5] &= ~(1<<(n&31));
+}
+
+uint32_t
+bitvector_get(uint32_t *b, uint64_t n)
+{
+ return b[n>>5] & (1<<(n&31));
+}
+
+static int
+ntz(uint32_t x)
+{
+ int n;
+
+ if(x == 0)
+ return 32;
+ n = 1;
+ if((x & 0x0000FFFF) == 0){
+ n = n +16;
+ x = x >>16;
+ }
+ if((x & 0x000000FF) == 0){
+ n = n + 8;
+ x = x >> 8;
+ }
+ if((x & 0x0000000F) == 0){
+ n = n + 4;
+ x = x >> 4;
+ }
+ if((x & 0x00000003) == 0){
+ n = n + 2;
+ x = x >> 2;
+ }
+ return n - (x & 1);
+}
+
+// given a bitvector of n bits, starting at bit n0 find the next
+// set bit, including n0.
+// returns n if no set bits.
+uint32_t
+bitvector_next(uint32_t *b, uint64_t n0, uint64_t n)
+{
+ if(n0 >= n)
+ return n;
+
+ uint32_t i = n0>>5;
+ uint32_t nb = n0&31;
+ uint32_t nw = (n+31)>>5;
+ uint32_t w;
+
+ if(i < nw-1 || (n&31) == 0)
+ w = b[i]>>nb;
+ else
+ w = (b[i]&lomask(n&31)) >> nb;
+ if(w != 0)
+ return ntz(w) + n0;
+ if(i == nw-1)
+ return n;
+ i++;
+ while(i < nw-1){
+ w = b[i];
+ if(w != 0)
+ return ntz(w) + (i<<5);
+ i++;
+ }
+ w = b[i];
+ nb = n&31;
+ i = ntz(w);
+ if(nb == 0)
+ return i + (n-32);
+ if(i >= nb)
+ return n;
+ return i + (n-nb);
+}
--- /dev/null
+++ b/bitvector.h
@@ -1,0 +1,23 @@
+uint32_t bitreverse(uint32_t x);
+uint32_t *bitvector_new(uint64_t n, int initzero);
+uint32_t *bitvector_resize(uint32_t *b, uint64_t oldsz, uint64_t newsz, int initzero);
+size_t bitvector_nwords(uint64_t nbits);
+void bitvector_set(uint32_t *b, uint64_t n, uint32_t c);
+uint32_t bitvector_get(uint32_t *b, uint64_t n);
+uint32_t bitvector_next(uint32_t *b, uint64_t n0, uint64_t n);
+void bitvector_shr(uint32_t *b, size_t n, uint32_t s);
+void bitvector_shr_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s);
+void bitvector_shl(uint32_t *b, size_t n, uint32_t s);
+void bitvector_shl_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s, int scrap);
+void bitvector_fill(uint32_t *b, uint32_t offs, uint32_t c, uint32_t nbits);
+void bitvector_copy(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t nbits);
+void bitvector_not(uint32_t *b, uint32_t offs, uint32_t nbits);
+void bitvector_not_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t nbits);
+void bitvector_reverse(uint32_t *b, uint32_t offs, uint32_t nbits);
+void bitvector_reverse_to(uint32_t *dest, uint32_t *src, uint32_t soffs, uint32_t nbits);
+void bitvector_and_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
+void bitvector_or_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
+void bitvector_xor_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
+uint64_t bitvector_count(uint32_t *b, uint32_t offs, uint64_t nbits);
+uint32_t bitvector_any0(uint32_t *b, uint32_t offs, uint32_t nbits);
+uint32_t bitvector_any1(uint32_t *b, uint32_t offs, uint32_t nbits);
--- a/builtins.c
+++ b/builtins.c
@@ -6,6 +6,8 @@
#include "flisp.h"
#include "operators.h"
#include "cvalues.h"
+#include "timefuncs.h"
+#include "random.h"
size_t
llength(value_t v)
--- /dev/null
+++ b/dump.c
@@ -1,0 +1,35 @@
+#include "llt.h"
+
+static char hexdig[] = "0123456789abcdef";
+
+/*
+ display a given number of bytes from a buffer, with the first
+ address label being startoffs
+*/
+void
+hexdump(ios_t *dest, const char *buffer, size_t len, size_t startoffs)
+{
+ size_t offs = 0;
+ size_t i, pos;
+ char ch, linebuffer[16], hexc[4];
+ static const char *spc50 = " ";
+
+ hexc[2] = hexc[3] = ' ';
+ do{
+ ios_printf(dest, "%.8x ", offs+startoffs);
+ pos = 10;
+ for(i = 0; i < 16 && offs < len; i++, offs++){
+ ch = buffer[offs];
+ linebuffer[i] = (ch < 32 || ch >= 0x7f) ? '.' : ch;
+ hexc[0] = hexdig[((uint8_t)ch)>>4];
+ hexc[1] = hexdig[ch & 0x0f];
+ pos += ios_write(dest, hexc, (i == 7 || i == 15) ? 4 : 3);
+ }
+ for(; i < 16; i++)
+ linebuffer[i] = ' ';
+ ios_write(dest, spc50, 60-pos);
+ ios_putc('|', dest);
+ ios_write(dest, linebuffer, 16);
+ ios_write(dest, "|\n", 2);
+ }while(offs < len);
+}
--- a/equal.c
+++ b/equal.c
@@ -4,6 +4,7 @@
#include "opcodes.h"
#include "cvalues.h"
#include "equal.h"
+#include "hashing.h"
#define BOUNDED_COMPARE_BOUND 128
#define BOUNDED_HASH_BOUND 16384
--- a/flisp.c
+++ b/flisp.c
@@ -14,6 +14,7 @@
#include "print.h"
#include "read.h"
#include "equal.h"
+#include "hashing.h"
typedef struct {
char *name;
--- /dev/null
+++ b/hashing.c
@@ -1,0 +1,75 @@
+#include "llt.h"
+
+lltuint_t
+nextipow2(lltuint_t i)
+{
+ if(i == 0)
+ return 1;
+ i |= i >> 1;
+ i |= i >> 2;
+ i |= i >> 4;
+ i |= i >> 8;
+ i |= i >> 16;
+#ifdef BITS64
+ i |= i >> 32;
+#endif
+ i++;
+ return i ? i : TOP_BIT;
+}
+
+uint32_t
+int32hash(uint32_t a)
+{
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+ return a;
+}
+
+uint64_t
+int64hash(uint64_t key)
+{
+ key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+ key = key ^ (key >> 24);
+ key = (key + (key << 3)) + (key << 8); // key * 265
+ key = key ^ (key >> 14);
+ key = (key + (key << 2)) + (key << 4); // key * 21
+ key = key ^ (key >> 28);
+ key = key + (key << 31);
+ return key;
+}
+
+uint32_t
+int64to32hash(uint64_t key)
+{
+ key = (~key) + (key << 18); // key = (key << 18) - key - 1;
+ key = key ^ (key >> 31);
+ key = key * 21; // key = (key + (key << 2)) + (key << 4);
+ key = key ^ (key >> 11);
+ key = key + (key << 6);
+ key = key ^ (key >> 22);
+ return (uint32_t)key;
+}
+
+#include "lookup3.c"
+
+uint64_t
+memhash(const char* buf, size_t n)
+{
+ uint32_t c = 0xcafe8881, b = 0x4d6a087c;
+
+ hashlittle2(buf, n, &c, &b);
+ return (uint64_t)c | (((uint64_t)b)<<32);
+}
+
+uint32_t
+memhash32(const char* buf, size_t n)
+{
+ uint32_t c = 0xcafe8881, b = 0x4d6a087c;
+
+ hashlittle2(buf, n, &c, &b);
+ return c;
+}
--- /dev/null
+++ b/hashing.h
@@ -1,0 +1,11 @@
+#ifndef HASHING_H_
+#define HASHING_H_
+
+lltuint_t nextipow2(lltuint_t i);
+uint32_t int32hash(uint32_t a);
+uint64_t int64hash(uint64_t key);
+uint32_t int64to32hash(uint64_t key);
+uint64_t memhash(const char* buf, size_t n);
+uint32_t memhash32(const char* buf, size_t n);
+
+#endif
--- /dev/null
+++ b/htable.c
@@ -1,0 +1,53 @@
+/*
+ functions common to all hash table instantiations
+*/
+
+#include "llt.h"
+#include "htable.h"
+#include "hashing.h"
+
+htable_t *
+htable_new(htable_t *h, size_t size)
+{
+ if(size <= HT_N_INLINE/2){
+ h->size = size = HT_N_INLINE;
+ h->table = &h->_space[0];
+ }else{
+ size = nextipow2(size);
+ size *= 2; // 2 pointers per key/value pair
+ size *= 2; // aim for 50% occupancy
+ h->size = size;
+ h->table = LLT_ALLOC(size*sizeof(void*));
+ }
+ if(h->table == nil)
+ return nil;
+ size_t i;
+ for(i = 0; i < size; i++)
+ h->table[i] = HT_NOTFOUND;
+ return h;
+}
+
+void
+htable_free(htable_t *h)
+{
+ if(h->table != &h->_space[0])
+ LLT_FREE(h->table);
+}
+
+// empty and reduce size
+void
+htable_reset(htable_t *h, size_t sz)
+{
+ sz = nextipow2(sz);
+ if(h->size > sz*4 && h->size > HT_N_INLINE){
+ size_t newsz = sz*4;
+ void **newtab = LLT_REALLOC(h->table, newsz*sizeof(void*));
+ if(newtab == nil)
+ return;
+ h->size = newsz;
+ h->table = newtab;
+ }
+ size_t i, hsz = h->size;
+ for(i = 0; i < hsz; i++)
+ h->table[i] = HT_NOTFOUND;
+}
--- /dev/null
+++ b/htable.h
@@ -1,0 +1,22 @@
+#ifndef __HTABLE_H_
+#define __HTABLE_H_
+
+#define HT_N_INLINE 32
+
+typedef struct {
+ size_t size;
+ void **table;
+ void *_space[HT_N_INLINE];
+}htable_t;
+
+// define this to be an invalid key/value
+#define HT_NOTFOUND ((void*)1)
+
+// initialize and free
+htable_t *htable_new(htable_t *h, size_t size);
+void htable_free(htable_t *h);
+
+// clear and (possibly) change size
+void htable_reset(htable_t *h, size_t sz);
+
+#endif
--- /dev/null
+++ b/htable.inc
@@ -1,0 +1,147 @@
+//-*- mode:c -*-
+
+/*
+ include this file and call HTIMPL to generate an implementation
+*/
+
+#define hash_size(h) ((h)->size/2)
+
+// compute empirical max-probe for a given size
+#define max_probe(size) ((size) <= (HT_N_INLINE*2) ? (HT_N_INLINE/2) : (size)>>3)
+
+#define HTIMPL(HTNAME, HFUNC, EQFUNC) \
+static void ** \
+HTNAME##_lookup_bp(htable_t *h, void *key) \
+{ \
+ lltuint_t hv; \
+ size_t i, orig, index, iter; \
+ size_t newsz, sz = hash_size(h); \
+ size_t maxprobe = max_probe(sz); \
+ void **tab = h->table; \
+ void **ol; \
+ \
+ hv = HFUNC((uintptr_t)key); \
+retry_bp: \
+ iter = 0; \
+ index = (hv & (sz-1)) * 2; \
+ sz *= 2; \
+ orig = index; \
+ \
+ do{ \
+ if(tab[index+1] == HT_NOTFOUND){ \
+ tab[index] = key; \
+ return &tab[index+1]; \
+ } \
+ if(EQFUNC(key, tab[index])) \
+ return &tab[index+1]; \
+ index = (index+2) & (sz-1); \
+ iter++; \
+ if(iter > maxprobe) \
+ break; \
+ }while(index != orig); \
+ \
+ /* table full */ \
+ /* quadruple size, rehash, retry the insert */ \
+ /* it's important to grow the table really fast; otherwise we waste */ \
+ /* lots of time rehashing all the keys over and over. */ \
+ sz = h->size; \
+ ol = h->table; \
+ if(sz >= (1<<19) || (sz <= (1<<8))) \
+ newsz = sz<<1; \
+ else if(sz <= HT_N_INLINE) \
+ newsz = HT_N_INLINE; \
+ else \
+ newsz = sz<<2; \
+ tab = (void**)LLT_ALLOC(newsz*sizeof(void*)); \
+ if(tab == nil) \
+ return nil; \
+ for(i = 0; i < newsz; i++) \
+ tab[i] = HT_NOTFOUND; \
+ h->table = tab; \
+ h->size = newsz; \
+ for(i = 0; i < sz; i += 2) { \
+ if(ol[i+1] != HT_NOTFOUND) \
+ (*HTNAME##_lookup_bp(h, ol[i])) = ol[i+1]; \
+ } \
+ if(ol != &h->_space[0]) \
+ LLT_FREE(ol); \
+ sz = hash_size(h); \
+ maxprobe = max_probe(sz); \
+ tab = h->table; \
+ goto retry_bp; \
+} \
+ \
+void \
+HTNAME##_put(htable_t *h, void *key, void *val) \
+{ \
+ void **bp = HTNAME##_lookup_bp(h, key); \
+ *bp = val; \
+} \
+ \
+void ** \
+HTNAME##_bp(htable_t *h, void *key) \
+{ \
+ return HTNAME##_lookup_bp(h, key); \
+} \
+ \
+/* returns bp if key is in hash, otherwise nil */ \
+/* if return is non-nil and *bp == HT_NOTFOUND then key was deleted */ \
+static void ** \
+HTNAME##_peek_bp(htable_t *h, void *key) \
+{ \
+ size_t sz = hash_size(h); \
+ size_t maxprobe = max_probe(sz); \
+ void **tab = h->table; \
+ size_t index = (HFUNC((uintptr_t)key) & (sz-1)) * 2; \
+ sz *= 2; \
+ size_t orig = index; \
+ size_t iter = 0; \
+ \
+ do { \
+ if(tab[index] == HT_NOTFOUND) \
+ return nil; \
+ if(EQFUNC(key, tab[index])) \
+ return &tab[index+1]; \
+ \
+ index = (index+2) & (sz-1); \
+ iter++; \
+ if(iter > maxprobe) \
+ break; \
+ }while(index != orig); \
+ \
+ return nil; \
+} \
+ \
+void *\
+HTNAME##_get(htable_t *h, void *key) \
+{ \
+ void **bp = HTNAME##_peek_bp(h, key); \
+ if(bp == nil) \
+ return HT_NOTFOUND; \
+ return *bp; \
+} \
+ \
+int \
+HTNAME##_has(htable_t *h, void *key) \
+{ \
+ return HTNAME##_get(h, key) != HT_NOTFOUND; \
+} \
+ \
+int \
+HTNAME##_remove(htable_t *h, void *key) \
+{ \
+ void **bp = HTNAME##_peek_bp(h, key); \
+ if(bp != nil){ \
+ *bp = HT_NOTFOUND; \
+ return 1; \
+ } \
+ return 0; \
+} \
+ \
+void \
+HTNAME##_adjoin(htable_t *h, void *key, void *val) \
+{ \
+ void **bp = HTNAME##_lookup_bp(h, key); \
+ if(*bp == HT_NOTFOUND) \
+ *bp = val; \
+}
--- /dev/null
+++ b/htableh.inc
@@ -1,0 +1,30 @@
+//-*- mode:c -*-
+
+#include "htable.h"
+
+#define HTPROT(HTNAME) \
+void *HTNAME##_get(htable_t *h, void *key); \
+void HTNAME##_put(htable_t *h, void *key, void *val); \
+void HTNAME##_adjoin(htable_t *h, void *key, void *val); \
+int HTNAME##_has(htable_t *h, void *key); \
+int HTNAME##_remove(htable_t *h, void *key); \
+void **HTNAME##_bp(htable_t *h, void *key);
+
+// return value, or HT_NOTFOUND if key not found
+
+// add key/value binding
+
+// add binding iff key is unbound
+
+// does key exist?
+
+// logically remove key
+
+// get a pointer to the location of the value for the given key.
+// creates the location if it doesn't exist. only returns nil
+// if memory allocation fails.
+// this should be used for updates, for example:
+// void **bp = ptrhash_bp(h, key);
+// *bp = f(*bp);
+// do not reuse bp if there might be intervening calls to ptrhash_put,
+// ptrhash_bp, ptrhash_reset, or ptrhash_free.
--- /dev/null
+++ b/ieee754.h
@@ -1,0 +1,66 @@
+#ifndef __IEEE754_H_
+#define __IEEE754_H_
+
+union ieee754_float {
+ float f;
+
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int negative:1;
+ unsigned int exponent:8;
+ unsigned int mantissa:23;
+#elif BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int mantissa:23;
+ unsigned int exponent:8;
+ unsigned int negative:1;
+#else
+#error which endian?
+#endif
+ }ieee;
+};
+
+#define IEEE754_FLOAT_BIAS 0x7f
+
+union ieee754_double {
+ double d;
+
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int negative:1;
+ unsigned int exponent:11;
+ unsigned int mantissa0:20;
+ unsigned int mantissa1:32;
+#else
+ unsigned int mantissa1:32;
+ unsigned int mantissa0:20;
+ unsigned int exponent:11;
+ unsigned int negative:1;
+#endif
+ }ieee;
+};
+
+#define IEEE754_DOUBLE_BIAS 0x3ff
+
+union ieee854_long_double {
+ long double d;
+
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int negative:1;
+ unsigned int exponent:15;
+ unsigned int empty:16;
+ unsigned int mantissa0:32;
+ unsigned int mantissa1:32;
+#else
+ unsigned int mantissa1:32;
+ unsigned int mantissa0:32;
+ unsigned int exponent:15;
+ unsigned int negative:1;
+ unsigned int empty:16;
+#endif
+ }ieee;
+};
+
+#define IEEE854_LONG_DOUBLE_BIAS 0x3fff
+
+#endif
--- /dev/null
+++ b/ios.c
@@ -1,0 +1,1019 @@
+#include "llt.h"
+#include "timefuncs.h"
+
+#define MOST_OF(x) ((x) - ((x)>>4))
+
+ios_t *ios_stdin = nil;
+ios_t *ios_stdout = nil;
+ios_t *ios_stderr = nil;
+
+/* OS-level primitive wrappers */
+
+void *
+llt_memrchr(const void *s, int c, size_t n)
+{
+ const uint8_t *src = (const uint8_t*)s + n;
+ uint8_t uc = c;
+ while(--src >= (uint8_t*)s)
+ if(*src == uc)
+ return (void *)src;
+ return nil;
+}
+
+#if !defined(__plan9__)
+static int
+_enonfatal(int err)
+{
+ return err == EAGAIN || err == EINPROGRESS || err == EINTR || err == EWOULDBLOCK;
+}
+#define SLEEP_TIME 5//ms
+#endif
+
+// return error code, #bytes read in *nread
+// these wrappers retry operations until success or a fatal error
+static int
+_os_read(long fd, char *buf, size_t n, size_t *nread)
+{
+ ssize_t r;
+
+ while(1){
+ r = read((int)fd, buf, n);
+ if(r > -1){
+ *nread = (size_t)r;
+ break;
+ }
+#if defined(__plan9__)
+ return r;
+#else
+ if(!_enonfatal(errno)){
+ *nread = 0;
+ return errno;
+ }
+ sleep_ms(SLEEP_TIME);
+#endif
+ }
+ return 0;
+}
+
+static int
+_os_read_all(long fd, char *buf, size_t n, size_t *nread)
+{
+ size_t got;
+
+ *nread = 0;
+
+ while(n > 0){
+ int err = _os_read(fd, buf, n, &got);
+ n -= got;
+ *nread += got;
+ buf += got;
+ if(err || got == 0)
+ return err;
+ }
+ return 0;
+}
+
+static int
+_os_write(long fd, const void *buf, size_t n, size_t *nwritten)
+{
+ ssize_t r;
+
+ while(1){
+ r = write((int)fd, buf, n);
+ if(r > -1){
+ *nwritten = (size_t)r;
+ break;
+ }
+#if defined(__plan9__)
+ return r;
+#else
+ if(!_enonfatal(errno)){
+ *nwritten = 0;
+ return errno;
+ }
+ sleep_ms(SLEEP_TIME);
+#endif
+ }
+ return 0;
+}
+
+static int
+_os_write_all(long fd, const char *buf, size_t n, size_t *nwritten)
+{
+ size_t wrote;
+
+ *nwritten = 0;
+ while(n > 0){
+ int err = _os_write(fd, buf, n, &wrote);
+ n -= wrote;
+ *nwritten += wrote;
+ buf += wrote;
+ if(err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* internal utility functions */
+
+static char *
+_buf_realloc(ios_t *s, size_t sz)
+{
+ char *temp;
+
+ if((s->buf == nil || s->buf == &s->local[0]) && sz <= IOS_INLSIZE){
+ /* TODO: if we want to allow shrinking, see if the buffer shrank
+ down to this size, in which case we need to copy. */
+ s->buf = &s->local[0];
+ s->maxsize = IOS_INLSIZE;
+ s->ownbuf = 1;
+ return s->buf;
+ }
+
+ if(sz <= s->maxsize)
+ return s->buf;
+
+ if(s->ownbuf && s->buf != &s->local[0]){
+ // if we own the buffer we're free to resize it
+ // always allocate 1 bigger in case user wants to add a NUL
+ // terminator after taking over the buffer
+ temp = LLT_REALLOC(s->buf, sz);
+ if(temp == nil)
+ return nil;
+ }else{
+ temp = LLT_ALLOC(sz);
+ if(temp == nil)
+ return nil;
+ s->ownbuf = 1;
+ if(s->size > 0)
+ memmove(temp, s->buf, s->size);
+ }
+
+ s->buf = temp;
+ s->maxsize = sz;
+ return s->buf;
+}
+
+// write a block of data into the buffer at the current position, resizing
+// if necessary. returns # written.
+static size_t
+_write_grow(ios_t *s, const char *data, size_t n)
+{
+ size_t amt;
+ size_t newsize;
+
+ if(n == 0)
+ return 0;
+
+ if(s->bpos + n > s->size){
+ if(s->bpos + n > s->maxsize){
+ /* TODO: here you might want to add a mechanism for limiting
+ the growth of the stream. */
+ newsize = s->maxsize ? s->maxsize * 2 : 8;
+ while(s->bpos + n > newsize)
+ newsize *= 2;
+ if(_buf_realloc(s, newsize) == nil){
+ /* no more space; write as much as we can */
+ amt = s->maxsize - s->bpos;
+ if(amt > 0){
+ memmove(&s->buf[s->bpos], data, amt);
+ }
+ s->bpos += amt;
+ s->size = s->maxsize;
+ return amt;
+ }
+ }
+ s->size = s->bpos + n;
+ }
+ memmove(s->buf + s->bpos, data, n);
+ s->bpos += n;
+
+ return n;
+}
+
+
+/* interface functions, low level */
+
+static size_t
+_ios_read(ios_t *s, char *dest, size_t n, int all)
+{
+ size_t tot = 0;
+ size_t got, avail;
+
+ while(n > 0){
+ avail = s->size - s->bpos;
+
+ if(avail > 0){
+ size_t ncopy = avail >= n ? n : avail;
+ memmove(dest, s->buf + s->bpos, ncopy);
+ s->bpos += ncopy;
+ if(ncopy >= n){
+ s->state = bst_rd;
+ return tot+ncopy;
+ }
+ }
+ if(s->bm == bm_mem || s->fd == -1){
+ // can't get any more data
+ s->state = bst_rd;
+ if(avail == 0)
+ s->_eof = 1;
+ return avail;
+ }
+
+ dest += avail;
+ n -= avail;
+ tot += avail;
+
+ ios_flush(s);
+ s->bpos = s->size = 0;
+ s->state = bst_rd;
+
+ s->fpos = -1;
+ if(n > MOST_OF(s->maxsize)){
+ // doesn't fit comfortably in buffer; go direct
+ if(all)
+ _os_read_all(s->fd, dest, n, &got);
+ else
+ _os_read(s->fd, dest, n, &got);
+ tot += got;
+ if(got == 0)
+ s->_eof = 1;
+ return tot;
+ }else{
+ // refill buffer
+ if(_os_read(s->fd, s->buf, s->maxsize, &got)){
+ s->_eof = 1;
+ return tot;
+ }
+ if(got == 0){
+ s->_eof = 1;
+ return tot;
+ }
+ s->size = got;
+ }
+ }
+
+ return tot;
+}
+
+size_t
+ios_read(ios_t *s, char *dest, size_t n)
+{
+ return _ios_read(s, dest, n, 0);
+}
+
+size_t
+ios_readall(ios_t *s, char *dest, size_t n)
+{
+ return _ios_read(s, dest, n, 1);
+}
+
+size_t
+ios_readprep(ios_t *s, size_t n)
+{
+ if(s->state == bst_wr && s->bm != bm_mem){
+ ios_flush(s);
+ s->bpos = s->size = 0;
+ }
+ size_t space = s->size - s->bpos;
+ s->state = bst_rd;
+ if(space >= n || s->bm == bm_mem || s->fd == -1)
+ return space;
+ if(s->maxsize < s->bpos+n){
+ // it won't fit. grow buffer or move data back.
+ if(n <= s->maxsize && space <= ((s->maxsize)>>2)){
+ if(space)
+ memmove(s->buf, s->buf+s->bpos, space);
+ s->size -= s->bpos;
+ s->bpos = 0;
+ }
+ else {
+ if(_buf_realloc(s, s->bpos + n) == nil)
+ return space;
+ }
+ }
+ size_t got;
+ int result = _os_read(s->fd, s->buf+s->size, s->maxsize - s->size, &got);
+ if(result)
+ return space;
+ s->size += got;
+ return s->size - s->bpos;
+}
+
+static void
+_write_update_pos(ios_t *s)
+{
+ if(s->bpos > s->ndirty)
+ s->ndirty = s->bpos;
+ if(s->bpos > s->size)
+ s->size = s->bpos;
+}
+
+size_t
+ios_write(ios_t *s, const char *data, size_t n)
+{
+ if(s->readonly)
+ return 0;
+ if(n == 0)
+ return 0;
+ size_t space;
+ size_t wrote = 0;
+
+ if(s->state == bst_none)
+ s->state = bst_wr;
+ if(s->state == bst_rd){
+ if(!s->rereadable){
+ s->size = 0;
+ s->bpos = 0;
+ }
+ space = s->size - s->bpos;
+ }else{
+ space = s->maxsize - s->bpos;
+ }
+
+ if(s->bm == bm_mem){
+ wrote = _write_grow(s, data, n);
+ }else if(s->bm == bm_none){
+ s->fpos = -1;
+ _os_write_all(s->fd, data, n, &wrote);
+ return wrote;
+ }else if(n <= space){
+ if(s->bm == bm_line){
+ char *nl;
+ if((nl = llt_memrchr(data, '\n', n)) != nil){
+ size_t linesz = nl-data+1;
+ s->bm = bm_block;
+ wrote += ios_write(s, data, linesz);
+ ios_flush(s);
+ s->bm = bm_line;
+ n -= linesz;
+ data += linesz;
+ }
+ }
+ memmove(s->buf + s->bpos, data, n);
+ s->bpos += n;
+ wrote += n;
+ }else{
+ s->state = bst_wr;
+ ios_flush(s);
+ if(n > MOST_OF(s->maxsize)){
+ _os_write_all(s->fd, data, n, &wrote);
+ return wrote;
+ }
+ return ios_write(s, data, n);
+ }
+ _write_update_pos(s);
+ return wrote;
+}
+
+off_t
+ios_seek(ios_t *s, off_t pos)
+{
+ s->_eof = 0;
+ if(s->bm == bm_mem){
+ if((size_t)pos > s->size)
+ return -1;
+ s->bpos = pos;
+ }else{
+ ios_flush(s);
+ off_t fdpos = lseek(s->fd, pos, SEEK_SET);
+ if(fdpos == (off_t)-1)
+ return fdpos;
+ s->bpos = s->size = 0;
+ }
+ return 0;
+}
+
+off_t
+ios_seek_end(ios_t *s)
+{
+ s->_eof = 1;
+ if(s->bm == bm_mem){
+ s->bpos = s->size;
+ }else{
+ ios_flush(s);
+ off_t fdpos = lseek(s->fd, 0, SEEK_END);
+ if(fdpos == (off_t)-1)
+ return fdpos;
+ s->bpos = s->size = 0;
+ }
+ return 0;
+}
+
+off_t
+ios_skip(ios_t *s, off_t offs)
+{
+ if(offs != 0){
+ if(offs > 0){
+ if(offs <= (off_t)(s->size-s->bpos)){
+ s->bpos += offs;
+ return 0;
+ }else if(s->bm == bm_mem){
+ // TODO: maybe grow buffer
+ return -1;
+ }
+ }else if(offs < 0){
+ if(-offs <= (off_t)s->bpos){
+ s->bpos += offs;
+ s->_eof = 0;
+ return 0;
+ }else if(s->bm == bm_mem){
+ return -1;
+ }
+ }
+ ios_flush(s);
+ if(s->state == bst_wr)
+ offs += s->bpos;
+ else if(s->state == bst_rd)
+ offs -= s->size - s->bpos;
+ off_t fdpos = lseek(s->fd, offs, SEEK_CUR);
+ if(fdpos == (off_t)-1)
+ return fdpos;
+ s->bpos = s->size = 0;
+ s->_eof = 0;
+ }
+ return 0;
+}
+
+off_t
+ios_pos(ios_t *s)
+{
+ if(s->bm == bm_mem)
+ return (off_t)s->bpos;
+
+ off_t fdpos = s->fpos;
+ if(fdpos == (off_t)-1){
+ fdpos = lseek(s->fd, 0, SEEK_CUR);
+ if(fdpos == (off_t)-1)
+ return fdpos;
+ s->fpos = fdpos;
+ }
+
+ if(s->state == bst_wr)
+ fdpos += s->bpos;
+ else if(s->state == bst_rd)
+ fdpos -= s->size - s->bpos;
+ return fdpos;
+}
+
+size_t
+ios_trunc(ios_t *s, size_t size)
+{
+ if(s->bm == bm_mem){
+ if(size == s->size)
+ return s->size;
+ if(size < s->size){
+ if(s->bpos > size)
+ s->bpos = size;
+ }else if(_buf_realloc(s, size) == nil)
+ return s->size;
+ s->size = size;
+ return size;
+ }
+ //todo
+ return 0;
+}
+
+int
+ios_eof(ios_t *s)
+{
+ if(s->bm == bm_mem)
+ return s->_eof;
+ return s->fd == -1 || s->_eof;
+}
+
+int
+ios_flush(ios_t *s)
+{
+ if(s->ndirty == 0 || s->bm == bm_mem || s->buf == nil)
+ return 0;
+ if(s->fd == -1)
+ return -1;
+
+ if(s->state == bst_rd){
+ if(lseek(s->fd, -(off_t)s->size, SEEK_CUR) == (off_t)-1){
+ // FIXME eh?
+ }
+ }
+
+ size_t nw, ntowrite = s->ndirty;
+ s->fpos = -1;
+ int err = _os_write_all(s->fd, s->buf, ntowrite, &nw);
+ // todo: try recovering from some kinds of errors (e.g. retry)
+
+ if(s->state == bst_rd){
+ if(lseek(s->fd, s->size - nw, SEEK_CUR) == (off_t)-1){
+ // FIXME eh?
+ }
+ }else if(s->state == bst_wr){
+ if(s->bpos != nw && lseek(s->fd, (off_t)s->bpos - (off_t)nw, SEEK_CUR) == (off_t)-1){
+ // FIXME eh?
+ }
+ // now preserve the invariant that data to write
+ // begins at the beginning of the buffer, and s->size refers
+ // to how much valid file data is stored in the buffer.
+ if(s->size > s->ndirty){
+ size_t delta = s->size - s->ndirty;
+ memmove(s->buf, s->buf + s->ndirty, delta);
+ }
+ s->size -= s->ndirty;
+ s->bpos = 0;
+ }
+
+ s->ndirty = 0;
+
+ if(err)
+ return err;
+ if(nw < ntowrite)
+ return -1;
+ return 0;
+}
+
+void
+ios_close(ios_t *s)
+{
+ ios_flush(s);
+ if(s->fd != -1 && s->ownfd)
+ close(s->fd);
+ s->fd = -1;
+ if(s->buf != nil && s->ownbuf && s->buf != &s->local[0])
+ LLT_FREE(s->buf);
+ s->buf = nil;
+ s->size = s->maxsize = s->bpos = 0;
+}
+
+static void
+_buf_init(ios_t *s, bufmode_t bm)
+{
+ s->bm = bm;
+ if(s->bm == bm_mem || s->bm == bm_none){
+ s->buf = &s->local[0];
+ s->maxsize = IOS_INLSIZE;
+ }else{
+ s->buf = nil;
+ _buf_realloc(s, IOS_BUFSIZE);
+ }
+ s->size = s->bpos = 0;
+}
+
+char *
+ios_takebuf(ios_t *s, size_t *psize)
+{
+ char *buf;
+
+ ios_flush(s);
+
+ if(s->buf == &s->local[0] || s->buf == nil || (!s->ownbuf && s->size == s->maxsize)){
+ buf = LLT_ALLOC(s->size+1);
+ if(buf == nil)
+ return nil;
+ if(s->size)
+ memmove(buf, s->buf, s->size);
+ }else if(s->size == s->maxsize){
+ buf = LLT_REALLOC(s->buf, s->size + 1);
+ if(buf == nil)
+ return nil;
+ }else{
+ buf = s->buf;
+ }
+ buf[s->size] = '\0';
+ *psize = s->size + 1;
+
+ /* empty stream and reinitialize */
+ _buf_init(s, s->bm);
+
+ return buf;
+}
+
+int
+ios_setbuf(ios_t *s, char *buf, size_t size, int own)
+{
+ ios_flush(s);
+ size_t nvalid;
+
+ nvalid = size < s->size ? size : s->size;
+ if(nvalid > 0)
+ memmove(buf, s->buf, nvalid);
+ if(s->bpos > nvalid){
+ // truncated
+ s->bpos = nvalid;
+ }
+ s->size = nvalid;
+
+ if(s->buf != nil && s->ownbuf && s->buf != &s->local[0])
+ LLT_FREE(s->buf);
+ s->buf = buf;
+ s->maxsize = size;
+ s->ownbuf = own;
+ return 0;
+}
+
+int
+ios_bufmode(ios_t *s, bufmode_t mode)
+{
+ // no fd; can only do mem-only buffering
+ if(s->fd == -1 && mode != bm_mem)
+ return -1;
+ s->bm = mode;
+ return 0;
+}
+
+void
+ios_set_readonly(ios_t *s)
+{
+ if(s->readonly)
+ return;
+ ios_flush(s);
+ s->state = bst_none;
+ s->readonly = 1;
+}
+
+static size_t
+ios_copy_(ios_t *to, ios_t *from, size_t nbytes, int all)
+{
+ size_t total = 0, avail;
+ if(!ios_eof(from)){
+ do{
+ avail = ios_readprep(from, IOS_BUFSIZE/2);
+ if(avail == 0){
+ from->_eof = 1;
+ break;
+ }
+ size_t written, ntowrite;
+ ntowrite = (avail <= nbytes || all) ? avail : nbytes;
+ written = ios_write(to, from->buf+from->bpos, ntowrite);
+ // TODO: should this be +=written instead?
+ from->bpos += ntowrite;
+ total += written;
+ if(!all){
+ nbytes -= written;
+ if(nbytes == 0)
+ break;
+ }
+ if(written < ntowrite)
+ break;
+ }while(!ios_eof(from));
+ }
+ return total;
+}
+
+size_t
+ios_copy(ios_t *to, ios_t *from, size_t nbytes)
+{
+ return ios_copy_(to, from, nbytes, 0);
+}
+
+size_t
+ios_copyall(ios_t *to, ios_t *from)
+{
+ return ios_copy_(to, from, 0, 1);
+}
+
+#define LINE_CHUNK_SIZE 160
+
+size_t
+ios_copyuntil(ios_t *to, ios_t *from, char delim)
+{
+ size_t total = 0, avail = from->size - from->bpos;
+ int first = 1;
+ if(!ios_eof(from)){
+ do{
+ if(avail == 0){
+ first = 0;
+ avail = ios_readprep(from, LINE_CHUNK_SIZE);
+ }
+ size_t written;
+ char *pd = memchr(from->buf+from->bpos, delim, avail);
+ if(pd == nil){
+ written = ios_write(to, from->buf+from->bpos, avail);
+ from->bpos += avail;
+ total += written;
+ avail = 0;
+ }else{
+ size_t ntowrite = pd - (from->buf+from->bpos) + 1;
+ written = ios_write(to, from->buf+from->bpos, ntowrite);
+ from->bpos += ntowrite;
+ total += written;
+ return total;
+ }
+ }while(!ios_eof(from) && (first || avail >= LINE_CHUNK_SIZE));
+ }
+ from->_eof = 1;
+ return total;
+}
+
+static void
+_ios_init(ios_t *s)
+{
+ // put all fields in a sane initial state
+ memset(s, 0, sizeof(*s));
+ s->bm = bm_block;
+ s->state = bst_none;
+ s->fpos = -1;
+ s->lineno = 1;
+ s->fd = -1;
+ s->ownbuf = 1;
+}
+
+/* stream object initializers. we do no allocation. */
+
+ios_t *
+ios_file(ios_t *s, char *fname, int rd, int wr, int creat, int trunc)
+{
+ int fd;
+ if(!(rd || wr)) // must specify read and/or write
+ goto open_file_err;
+ int flags = wr ? (rd ? O_RDWR : O_WRONLY) : O_RDONLY;
+ if(trunc)
+ flags |= O_TRUNC;
+#if defined(__plan9__)
+ fd = creat ? create(fname, flags, 0644) : open(fname, flags);
+#else
+ if(creat)
+ flags |= O_CREAT;
+ fd = open(fname, flags, 0644);
+#endif
+ s = ios_fd(s, fd, 1, 1);
+ if(fd < 0)
+ goto open_file_err;
+ if(!wr)
+ s->readonly = 1;
+ return s;
+open_file_err:
+ s->fd = -1;
+ return nil;
+}
+
+ios_t *
+ios_mem(ios_t *s, size_t initsize)
+{
+ _ios_init(s);
+ s->bm = bm_mem;
+ _buf_realloc(s, initsize);
+ return s;
+}
+
+ios_t *
+ios_str(ios_t *s, char *str)
+{
+ size_t n = strlen(str);
+ if(ios_mem(s, n+1) == nil)
+ return nil;
+ ios_write(s, str, n+1);
+ ios_seek(s, 0);
+ return s;
+}
+
+ios_t *
+ios_static_buffer(ios_t *s, const char *buf, size_t sz)
+{
+ ios_mem(s, 0);
+ ios_setbuf(s, (char*)buf, sz, 0);
+ s->size = sz;
+ ios_set_readonly(s);
+ return s;
+}
+
+ios_t *
+ios_fd(ios_t *s, long fd, int isfile, int own)
+{
+ _ios_init(s);
+ s->fd = fd;
+ if(isfile)
+ s->rereadable = 1;
+ _buf_init(s, bm_block);
+ s->ownfd = own;
+ if(fd == STDERR_FILENO)
+ s->bm = bm_none;
+ return s;
+}
+
+void
+ios_init_stdstreams(void)
+{
+ ios_stdin = LLT_ALLOC(sizeof(ios_t));
+ ios_fd(ios_stdin, STDIN_FILENO, 0, 0);
+
+ ios_stdout = LLT_ALLOC(sizeof(ios_t));
+ ios_fd(ios_stdout, STDOUT_FILENO, 0, 0);
+ ios_stdout->bm = bm_line;
+
+ ios_stderr = LLT_ALLOC(sizeof(ios_t));
+ ios_fd(ios_stderr, STDERR_FILENO, 0, 0);
+ ios_stderr->bm = bm_none;
+}
+
+/* higher level interface */
+
+int
+ios_putc(int c, ios_t *s)
+{
+ char ch = c;
+
+ if(s->state == bst_wr && s->bpos < s->maxsize && s->bm != bm_none){
+ s->buf[s->bpos++] = ch;
+ _write_update_pos(s);
+ if(s->bm == bm_line && ch == '\n')
+ ios_flush(s);
+ return 1;
+ }
+ return ios_write(s, &ch, 1);
+}
+
+int
+ios_getc(ios_t *s)
+{
+ char ch;
+ if(s->state == bst_rd && s->bpos < s->size)
+ ch = s->buf[s->bpos++];
+ else if(s->_eof)
+ return IOS_EOF;
+ else if(ios_read(s, &ch, 1) < 1)
+ return IOS_EOF;
+ if(ch == '\n')
+ s->lineno++;
+ return (uint8_t)ch;
+}
+
+int
+ios_peekc(ios_t *s)
+{
+ if(s->bpos < s->size)
+ return (uint8_t)s->buf[s->bpos];
+ if(s->_eof)
+ return IOS_EOF;
+ size_t n = ios_readprep(s, 1);
+ if(n == 0)
+ return IOS_EOF;
+ return (uint8_t)s->buf[s->bpos];
+}
+
+int
+ios_ungetc(int c, ios_t *s)
+{
+ if(s->state == bst_wr)
+ return IOS_EOF;
+ if(c == '\n')
+ s->lineno--;
+ if(s->bpos > 0){
+ s->bpos--;
+ if(s->buf[s->bpos] != (char)c)
+ s->buf[s->bpos] = (char)c;
+ s->_eof = 0;
+ return c;
+ }
+ if(s->size == s->maxsize && _buf_realloc(s, s->maxsize*2) == nil)
+ return IOS_EOF;
+ memmove(s->buf + 1, s->buf, s->size);
+ s->buf[0] = (char)c;
+ s->size++;
+ s->_eof = 0;
+ return c;
+}
+
+int
+ios_getutf8(ios_t *s, uint32_t *pwc)
+{
+ int c;
+ size_t sz;
+ char c0;
+ char buf[8];
+
+ c = ios_peekc(s);
+ if(c == IOS_EOF){
+ s->_eof = 1;
+ return IOS_EOF;
+ }
+ c0 = (char)c;
+ if((uint8_t)c0 < 0x80){
+ ios_getc(s);
+ *pwc = (uint32_t)(uint8_t)c0;
+ return 1;
+ }
+ sz = u8_seqlen(&c0)-1;
+ if(!isutf(c0) || sz > 3)
+ return 0;
+ if(ios_readprep(s, sz) < sz){
+ // NOTE: this returns EOF even though some bytes are available
+ // so we do not set s->_eof on this code path
+ return IOS_EOF;
+ }
+ if(u8_isvalid(&s->buf[s->bpos], sz+1)){
+ size_t i = s->bpos;
+ *pwc = u8_nextchar(s->buf, &i);
+ ios_read(s, buf, sz+1);
+ return 1;
+ }
+ return 0;
+}
+
+int
+ios_peekutf8(ios_t *s, uint32_t *pwc)
+{
+ int c;
+ size_t sz;
+ char c0;
+
+ c = ios_peekc(s);
+ if(c == IOS_EOF)
+ return IOS_EOF;
+ c0 = (char)c;
+ if((uint8_t)c0 < 0x80){
+ *pwc = (uint32_t)(uint8_t)c0;
+ return 1;
+ }
+ sz = u8_seqlen(&c0)-1;
+ if(!isutf(c0) || sz > 3)
+ return 0;
+ if(ios_readprep(s, sz) < sz)
+ return IOS_EOF;
+ if(u8_isvalid(&s->buf[s->bpos], sz+1)){
+ size_t i = s->bpos;
+ *pwc = u8_nextchar(s->buf, &i);
+ return 1;
+ }
+ return 0;
+}
+
+int
+ios_pututf8(ios_t *s, uint32_t wc)
+{
+ char buf[8];
+ if(wc < 0x80)
+ return ios_putc((int)wc, s);
+ size_t n = u8_toutf8(buf, 8, &wc, 1);
+ return ios_write(s, buf, n);
+}
+
+void
+ios_purge(ios_t *s)
+{
+ if(s->state == bst_rd)
+ s->bpos = s->size;
+}
+
+char *
+ios_readline(ios_t *s)
+{
+ ios_t dest;
+ ios_mem(&dest, 0);
+ ios_copyuntil(&dest, s, '\n');
+ size_t n;
+ return ios_takebuf(&dest, &n);
+}
+
+int
+ios_vprintf(ios_t *s, const char *format, va_list args)
+{
+ char *str;
+ int c;
+
+#if defined(__plan9__)
+ str = vsmprint(format, args);
+ if((c = strlen(str)) >= 0)
+ ios_write(s, str, c);
+ free(str);
+#else
+ va_list al;
+ va_copy(al, args);
+
+ if(s->state == bst_wr && s->bpos < s->maxsize && s->bm != bm_none){
+ int avail = s->maxsize - s->bpos;
+ char *start = s->buf + s->bpos;
+ c = vsnprintf(start, avail, format, args);
+ if(c < 0){
+ va_end(al);
+ return c;
+ }
+ if(c < avail){
+ s->bpos += (size_t)c;
+ _write_update_pos(s);
+ // TODO: only works right if newline is at end
+ if(s->bm == bm_line && llt_memrchr(start, '\n', (size_t)c))
+ ios_flush(s);
+ va_end(al);
+ return c;
+ }
+ }
+ c = vasprintf(&str, format, al);
+ if(c >= 0){
+ ios_write(s, str, c);
+ LLT_FREE(str);
+ }
+ va_end(al);
+#endif
+ return c;
+}
+
+int
+ios_printf(ios_t *s, const char *format, ...)
+{
+ va_list args;
+ int c;
+
+ va_start(args, format);
+ c = ios_vprintf(s, format, args);
+ va_end(args);
+ return c;
+}
--- /dev/null
+++ b/ios.h
@@ -1,0 +1,207 @@
+// this flag controls when data actually moves out to the underlying I/O
+// channel. memory streams are a special case of this where the data
+// never moves out.
+typedef enum {
+ bm_none,
+ bm_line,
+ bm_block,
+ bm_mem,
+}bufmode_t;
+
+typedef enum {
+ bst_none,
+ bst_rd,
+ bst_wr,
+}bufstate_t;
+
+#define IOS_INLSIZE 54
+#define IOS_BUFSIZE 131072
+
+typedef struct {
+ bufmode_t bm;
+
+ // the state only indicates where the underlying file position is relative
+ // to the buffer. reading: at the end. writing: at the beginning.
+ // in general, you can do any operation in any state.
+ bufstate_t state;
+
+ int errcode;
+
+ char *buf; // start of buffer
+ size_t maxsize; // space allocated to buffer
+ size_t size; // length of valid data in buf, >=ndirty
+ size_t bpos; // current position in buffer
+ size_t ndirty; // # bytes at &buf[0] that need to be written
+
+ off_t fpos; // cached file pos
+ size_t lineno; // current line number
+
+ int fd;
+
+ uint8_t readonly:1;
+ uint8_t ownbuf:1;
+ uint8_t ownfd:1;
+ uint8_t _eof:1;
+
+ // this means you can read, seek back, then read the same data
+ // again any number of times. usually only true for files and strings.
+ uint8_t rereadable:1;
+
+ // this enables "stenciled writes". you can alternately write and
+ // seek without flushing in between. this performs read-before-write
+ // to populate the buffer, so "rereadable" capability is required.
+ // this is off by default.
+ //uint8_t stenciled:1;
+
+ // request durable writes (fsync)
+ // uint8_t durable:1;
+
+ // todo: mutex
+ char local[IOS_INLSIZE];
+}ios_t;
+
+/* low-level interface functions */
+size_t ios_read(ios_t *s, char *dest, size_t n);
+size_t ios_readall(ios_t *s, char *dest, size_t n);
+size_t ios_write(ios_t *s, const char *data, size_t n);
+off_t ios_seek(ios_t *s, off_t pos); // absolute seek
+off_t ios_seek_end(ios_t *s);
+off_t ios_skip(ios_t *s, off_t offs); // relative seek
+off_t ios_pos(ios_t *s); // get current position
+size_t ios_trunc(ios_t *s, size_t size);
+int ios_eof(ios_t *s);
+int ios_flush(ios_t *s);
+void ios_close(ios_t *s);
+char *ios_takebuf(ios_t *s, size_t *psize); // null-terminate and release buffer to caller
+// set buffer space to use
+int ios_setbuf(ios_t *s, char *buf, size_t size, int own);
+int ios_bufmode(ios_t *s, bufmode_t mode);
+void ios_set_readonly(ios_t *s);
+size_t ios_copy(ios_t *to, ios_t *from, size_t nbytes);
+size_t ios_copyall(ios_t *to, ios_t *from);
+size_t ios_copyuntil(ios_t *to, ios_t *from, char delim);
+// ensure at least n bytes are buffered if possible. returns # available.
+size_t ios_readprep(ios_t *from, size_t n);
+//void ios_lock(ios_t *s);
+//int ios_trylock(ios_t *s);
+//int ios_unlock(ios_t *s);
+
+/* stream creation */
+ios_t *ios_file(ios_t *s, char *fname, int rd, int wr, int create, int trunc);
+ios_t *ios_mem(ios_t *s, size_t initsize);
+ios_t *ios_str(ios_t *s, char *str);
+ios_t *ios_static_buffer(ios_t *s, const char *buf, size_t sz);
+ios_t *ios_fd(ios_t *s, long fd, int isfile, int own);
+// todo: ios_socket
+extern ios_t *ios_stdin;
+extern ios_t *ios_stdout;
+extern ios_t *ios_stderr;
+void ios_init_stdstreams(void);
+
+/* high-level functions - output */
+int ios_putnum(ios_t *s, char *data, uint32_t type);
+int ios_putint(ios_t *s, int n);
+int ios_pututf8(ios_t *s, uint32_t wc);
+int ios_putstringz(ios_t *s, char *str, int do_write_nulterm);
+int ios_printf(ios_t *s, const char *format, ...);
+int ios_vprintf(ios_t *s, const char *format, va_list args);
+
+void hexdump(ios_t *dest, const char *buffer, size_t len, size_t startoffs);
+
+/* high-level stream functions - input */
+int ios_getnum(ios_t *s, char *data, uint32_t type);
+int ios_getutf8(ios_t *s, uint32_t *pwc);
+int ios_peekutf8(ios_t *s, uint32_t *pwc);
+int ios_ungetutf8(ios_t *s, uint32_t wc);
+int ios_getstringz(ios_t *dest, ios_t *src);
+int ios_getstringn(ios_t *dest, ios_t *src, size_t nchars);
+int ios_getline(ios_t *s, char **pbuf, size_t *psz);
+char *ios_readline(ios_t *s);
+
+// discard data buffered for reading
+void ios_purge(ios_t *s);
+
+// seek by utf8 sequence increments
+int ios_nextutf8(ios_t *s);
+int ios_prevutf8(ios_t *s);
+
+/* stdio-style functions */
+#define IOS_EOF (-1)
+int ios_putc(int c, ios_t *s);
+//wint_t ios_putwc(ios_t *s, wchar_t wc);
+int ios_getc(ios_t *s);
+int ios_peekc(ios_t *s);
+//wint_t ios_getwc(ios_t *s);
+int ios_ungetc(int c, ios_t *s);
+//wint_t ios_ungetwc(ios_t *s, wint_t wc);
+#define ios_puts(str, s) ios_write(s, str, strlen(str))
+
+/*
+ With memory streams, mixed reads and writes are equivalent to performing
+ sequences of *p++, as either an lvalue or rvalue. File streams behave
+ similarly, but other streams might not support this. Using unbuffered
+ mode makes this more predictable.
+
+ Note on "unget" functions:
+ There are two kinds of functions here: those that operate on sized
+ blocks of bytes and those that operate on logical units like "character"
+ or "integer". The "unget" functions only work on logical units. There
+ is no "unget n bytes". You can only do an unget after a matching get.
+ However, data pushed back by an unget is available to all read operations.
+ The reason for this is that unget is defined in terms of its effect on
+ the underlying buffer (namely, it rebuffers data as if it had been
+ buffered but not read yet). IOS reserves the right to perform large block
+ operations directly, bypassing the buffer. In such a case data was
+ never buffered, so "rebuffering" has no meaning (i.e. there is no
+ correspondence between the buffer and the physical stream).
+
+ Single-bit I/O is able to write partial bytes ONLY IF the stream supports
+ seeking. Also, line buffering is not well-defined in the context of
+ single-bit I/O, so it might not do what you expect.
+
+ implementation notes:
+ in order to know where we are in a file, we must ensure the buffer
+ is only populated from the underlying stream starting with p==buf.
+
+ to switch from writing to reading: flush, set p=buf, cnt=0
+ to switch from reading to writing: seek backwards cnt bytes, p=buf, cnt=0
+
+ when writing: buf starts at curr. physical stream pos, p - buf is how
+ many bytes we've written logically. cnt==0
+
+ dirty == (bitpos>0 && state==iost_wr), EXCEPT right after switching from
+ reading to writing, where we might be in the middle of a byte without
+ having changed it.
+
+ to write a bit: if !dirty, read up to maxsize-(p-buf) into buffer, then
+ seek back by the same amount (undo it). write onto those bits. now set
+ the dirty bit. in this state, we can bit-read up to the end of the byte,
+ then formally switch to the read state using flush.
+
+ design points:
+ - data-source independence, including memory streams
+ - expose buffer to user, allow user-owned buffers
+ - allow direct I/O, don't always go through buffer
+ - buffer-internal seeking. makes seeking back 1-2 bytes very fast,
+ and makes it possible for sockets where it otherwise wouldn't be
+ - tries to allow switching between reading and writing
+ - support 64-bit and large files
+ - efficient, low-latency buffering
+ - special support for utf8
+ - type-aware functions with byte-order swapping service
+ - position counter for meaningful data offsets with sockets
+
+ theory of operation:
+
+ the buffer is a view of part of a file/stream. you can seek, read, and
+ write around in it as much as you like, as if it were just a string.
+
+ we keep track of the part of the buffer that's invalid (written to).
+ we remember whether the position of the underlying stream is aligned
+ with the end of the buffer (reading mode) or the beginning (writing mode).
+
+ based on this info, we might have to seek back before doing a flush.
+
+ as optimizations, we do no writing if the buffer isn't "dirty", and we
+ do no reading if the data will only be overwritten.
+*/
--- /dev/null
+++ b/llt.c
@@ -1,0 +1,51 @@
+#include "llt.h"
+#include "random.h"
+
+double D_PNAN, D_NNAN, D_PINF, D_NINF;
+float F_PNAN, F_NNAN, F_PINF, F_NINF;
+
+void
+llt_init(void)
+{
+ D_PNAN = strtod("+NaN", nil);
+ D_NNAN = strtod("-NaN", nil);
+ D_PINF = strtod("+Inf", nil);
+ D_NINF = strtod("-Inf", nil);
+
+ *(uint32_t*)&F_PNAN = 0x7fc00000;
+ *(uint32_t*)&F_NNAN = 0xffc00000;
+ *(uint32_t*)&F_PINF = 0x7f800000;
+ *(uint32_t*)&F_NINF = 0xff800000;
+
+ randomize();
+ ios_init_stdstreams();
+}
+
+char *
+uint2str(char *dest, size_t len, uint64_t num, uint32_t base)
+{
+ int i = len-1;
+ uint64_t b = (uint64_t)base;
+ char ch;
+ dest[i--] = '\0';
+ while(i >= 0){
+ ch = (char)(num % b);
+ if(ch < 10)
+ ch += '0';
+ else
+ ch = ch-10+'a';
+ dest[i--] = ch;
+ num /= b;
+ if(num == 0)
+ break;
+ }
+ return &dest[i+1];
+}
+
+int
+isdigit_base(char c, int base)
+{
+ if(base < 11)
+ return c >= '0' && c < '0'+base;
+ return (c >= '0' && c <= '9') || (c >= 'a' && c < 'a'+base-10) || (c >= 'A' && c < 'A'+base-10);
+}
--- /dev/null
+++ b/llt.h
@@ -1,0 +1,72 @@
+#ifndef __LLT_H_
+#define __LLT_H_
+
+#include "platform.h"
+#include "utf8.h"
+#include "ios.h"
+#include "bitvector.h"
+
+#include "htableh.inc"
+HTPROT(ptrhash)
+
+#ifdef __GNUC__
+#define __unlikely(x) __builtin_expect(!!(x), 0)
+#define __likely(x) __builtin_expect(!!(x), 1)
+#else
+#define __unlikely(x) (x)
+#define __likely(x) (x)
+#endif
+
+#ifdef BOEHM_GC /* boehm GC allocator */
+#include <gc.h>
+#define LLT_ALLOC(n) GC_MALLOC(n)
+#define LLT_REALLOC(p, n) GC_REALLOC((p), (n))
+#define LLT_FREE(x) USED(x)
+#else /* standard allocator */
+#define LLT_ALLOC(n) malloc(n)
+#define LLT_REALLOC(p, n) realloc((p), (n))
+#define LLT_FREE(x) free(x)
+#endif
+
+#define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8)
+#define bswap_32(x) \
+ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#define bswap_64(x) \
+ (uint64_t)bswap_32((x) & 0xffffffffULL)<<32 | \
+ (uint64_t)bswap_32(((x)>>32) & 0xffffffffULL)
+
+#define DBL_MAXINT (1LL<<53)
+#define FLT_MAXINT (1<<24)
+#define BIT63 0x8000000000000000ULL
+#define BIT31 0x80000000UL
+
+#ifdef BITS64
+#define NBITS 64
+#define TOP_BIT BIT63
+typedef uint64_t lltuint_t;
+typedef int64_t lltint_t;
+#else
+#define NBITS 32
+#define TOP_BIT BIT31
+typedef uint32_t lltuint_t;
+typedef int32_t lltint_t;
+#endif
+
+#define LOG2_10 3.3219280948873626
+#define rel_zero(a, b) (fabs((a)/(b)) < DBL_EPSILON)
+#define LABS(n) (((n)^((n)>>(NBITS-1))) - ((n)>>(NBITS-1)))
+#define NBABS(n, nb) (((n)^((n)>>((nb)-1))) - ((n)>>((nb)-1)))
+#define LLT_ALIGN(x, sz) (((x) + (sz-1)) & (-sz))
+
+// a mask with n set lo or hi bits
+#define lomask(n) (uint32_t)((((uint32_t)1)<<(n))-1)
+
+extern double D_PNAN, D_NNAN, D_PINF, D_NINF;
+extern float F_PNAN, F_NNAN, F_PINF, F_NINF;
+
+char *uint2str(char *dest, size_t len, uint64_t num, uint32_t base);
+int isdigit_base(char c, int base);
+void llt_init(void);
+
+#endif
--- a/llt/bitvector-ops.c
+++ /dev/null
@@ -1,477 +1,0 @@
-#include "llt.h"
-
-#define ONES32 ((uint32_t)0xffffffffUL)
-
-// greater than this # of words we use malloc instead of alloca
-#define MALLOC_CUTOFF 2000
-
-static inline
-uint32_t count_bits(uint32_t b)
-{
- b = b - ((b>>1)&0x55555555);
- b = ((b>>2)&0x33333333) + (b&0x33333333);
- b = ((b>>4)+b)&0x0f0f0f0f;
- b += (b>>8);
- b += (b>>16);
- return b & 0x3f;
-}
-
-uint32_t
-bitreverse(uint32_t x)
-{
- uint32_t m;
-
-#ifdef __INTEL_COMPILER
- x = _bswap(x);
-#else
- x = (x >> 16) | (x << 16); m = 0xff00ff00;
- x = ((x & m) >> 8) | ((x & ~m) << 8);
-#endif
- m = 0xf0f0f0f0;
- x = ((x & m) >> 4) | ((x & ~m) << 4); m = 0xcccccccc;
- x = ((x & m) >> 2) | ((x & ~m) << 2); m = 0xaaaaaaaa;
- x = ((x & m) >> 1) | ((x & ~m) << 1);
-
- return x;
-}
-
-// shift all bits in a long bit vector
-// n is # of int32s to consider, s is shift distance
-// lowest bit-index is bit 0 of word 0
-// TODO: handle boundary case of shift distance >= data size?
-void
-bitvector_shr(uint32_t *b, size_t n, uint32_t s)
-{
- uint32_t i;
- if(s == 0 || n == 0)
- return;
- i = s >> 5;
- if(i){
- n -= i;
- memmove(b, &b[i], n*4);
- memset(&b[n], 0, i*4);
- s &= 31;
- }
- for(i = 0; i < n-1; i++)
- b[i] = (b[i] >> s) | (b[i+1] << (32-s));
- b[i] >>= s;
-}
-
-// out-of-place version, good for re-aligning a strided submatrix to
-// linear representation when a copy is needed
-// assumes that dest has the same amount of space as source, even if it
-// wouldn't have been necessary to hold the shifted bits
-void
-bitvector_shr_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s)
-{
- uint32_t i, j;
- if(n == 0)
- return;
- if(s == 0){
- memmove(dest, b, n*4);
- return;
- }
- j = s >> 5;
- if(j){
- n -= j;
- memset(&dest[n], 0, j*4);
- s &= 31;
- b = &b[j];
- }
- for(i = 0; i < n-1; i++)
- dest[i] = (b[i] >> s) | (b[i+1] << (32-s));
- dest[i] = b[i]>>s;
-}
-
-void
-bitvector_shl(uint32_t *b, size_t n, uint32_t s)
-{
- uint32_t i, scrap = 0, temp;
- if(s == 0 || n == 0)
- return;
- i = s >> 5;
- if(i){
- n -= i;
- memmove(&b[i], b, n*4);
- memset(b, 0, i*4);
- s &= 31;
- b = &b[i];
- }
- for(i = 0; i < n; i++){
- temp = (b[i] << s) | scrap;
- scrap = b[i] >> (32-s);
- b[i] = temp;
- }
-}
-
-// if dest has more space than source, set scrap to true to keep the
-// top bits that would otherwise be shifted out
-void
-bitvector_shl_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s, int scrap)
-{
- uint32_t i, j, sc = 0;
- if(n == 0)
- return;
- if(s == 0){
- memmove(dest, b, n*4);
- return;
- }
- j = s >> 5;
- if(j){
- n -= j;
- memset(dest, 0, j*4);
- s &= 31;
- dest = &dest[j];
- }
- for(i = 0; i < n; i++){
- dest[i] = (b[i] << s) | sc;
- sc = b[i] >> (32-s);
- }
- if(scrap)
- dest[i] = sc;
-}
-
-// set nbits to c, starting at given bit offset
-// assumes offs < 32
-void
-bitvector_fill(uint32_t *b, uint32_t offs, uint32_t c, uint32_t nbits)
-{
- uint32_t i, nw, tail, mask;
-
- if(nbits == 0)
- return;
- nw = (offs+nbits+31)>>5;
-
- if(nw == 1){
- mask = (lomask(nbits)<<offs);
- if(c)
- b[0] |= mask;
- else
- b[0] &= ~mask;
- return;
- }
-
- mask = lomask(offs);
- if(c)
- b[0] |= ~mask;
- else
- b[0] &= mask;
-
- mask = c ? ONES32 : 0;
-
- for(i = 1; i < nw-1; i++)
- b[i] = mask;
-
- tail = (offs+nbits) & 31;
- if(tail == 0)
- b[i] = mask;
- else{
- mask = lomask(tail);
- if(c)
- b[i] |= mask;
- else
- b[i] &= ~mask;
- }
-}
-
-void
-bitvector_not(uint32_t *b, uint32_t offs, uint32_t nbits)
-{
- uint32_t i, nw, tail, mask;
-
- if(nbits == 0)
- return;
- nw = (offs+nbits+31)>>5;
-
- if(nw == 1){
- mask = lomask(nbits)<<offs;
- b[0] ^= mask;
- return;
- }
-
- mask = ~lomask(offs);
- b[0] ^= mask;
-
- for(i = 1; i < nw-1; i++)
- b[i] = ~b[i];
-
- tail = (offs+nbits)&31;
- if(tail == 0)
- b[i] = ~b[i];
- else{
- mask = lomask(tail);
- b[i] ^= mask;
- }
-}
-
-// constant-space bit vector copy in a single pass, with arbitrary
-// offsets and lengths. to get this right, there are 16 cases to handle!
-#define BITVECTOR_COPY_OP(name, OP) \
-void \
-bitvector_##name(uint32_t *dest, uint32_t doffs, uint32_t *src, uint32_t soffs, uint32_t nbits) \
-{ \
- uint32_t i, s, nw, tail, snw, mask, scrap; \
- if(nbits == 0) \
- return; \
- nw = (doffs+nbits+31)>>5; \
- if(soffs == doffs){ \
- if(nw == 1){ \
- mask = (lomask(nbits)<<doffs); \
- dest[0] = (dest[0] & ~mask) | (OP(src[0]) & mask); \
- return; \
- } \
- mask = ~lomask(doffs); \
- dest[0] = (dest[0] & ~mask) | (OP(src[0]) & mask); \
- for(i = 1; i < nw-1; i++) \
- dest[i] = OP(src[i]); \
- tail = (doffs+nbits)&31; \
- if(tail == 0) \
- dest[i] = src[i]; \
- else { \
- mask = lomask(tail); \
- dest[i] = (dest[i] & ~mask) | (OP(src[i]) & mask); \
- } \
- return; \
- } \
- snw = (soffs+nbits+31)>>5; \
- if(soffs < doffs){ \
- s = doffs-soffs; \
- if(nw == 1){ \
- mask = lomask(nbits) << doffs; \
- dest[0] = (dest[0] & ~mask) | ((OP(src[0])<<s) & mask); \
- return; \
- } \
- mask = ~lomask(doffs); \
- dest[0] = (dest[0] & ~mask) | ((OP(src[0])<<s) & mask); \
- scrap = OP(src[0])>>(32-s); \
- for(i = 1; i < snw-1; i++){ \
- dest[i] = (OP(src[i])<<s) | scrap; \
- scrap = OP(src[i])>>(32-s); \
- } \
- tail = (doffs+nbits)&31; \
- mask = tail ? lomask(tail) : ONES32; \
- if(snw == nw) \
- dest[i] = (dest[i] & ~mask) | (((OP(src[i])<<s)|scrap) & mask); \
- else{ /* snw < nw */ \
- if(snw == 1) \
- dest[i] = (dest[i] & ~mask) | (((OP(src[i])<<s) | scrap) & mask); \
- else{ \
- dest[i] = (OP(src[i])<<s) | scrap; \
- scrap = OP(src[i])>>(32-s); \
- i++; \
- dest[i] = (dest[i] & ~mask) | (scrap & mask); \
- } \
- } \
- }else{ \
- s = soffs-doffs; \
- if(snw == 1){ \
- mask = (lomask(nbits)<<doffs); \
- dest[0] = (dest[0] & ~mask) | ((OP(src[0])>>s) & mask); \
- return; \
- } \
- if(nw == 1){ \
- mask = (lomask(nbits)<<doffs); \
- dest[0] = (dest[0] & ~mask) | \
- (((OP(src[0])>>s)|(OP(src[1])<<(32-s))) & mask); \
- return; \
- } \
- mask = ~lomask(doffs); \
- dest[0] = (dest[0] & ~mask) | (((OP(src[0])>>s)|(OP(src[1])<<(32-s))) & mask); \
- for(i = 1; i < nw-1; i++) \
- dest[i] = (OP(src[i])>>s) | (OP(src[i+1])<<(32-s)); \
- tail = (doffs+nbits)&31; \
- mask = tail ? lomask(tail) : ONES32; \
- if(snw == nw){ \
- dest[i] = (dest[i] & ~mask) | ((OP(src[i])>>s) & mask); \
- } \
- else /* snw > nw */ { \
- dest[i] = (dest[i] & ~mask) | \
- (((OP(src[i])>>s)|(OP(src[i+1])<<(32-s))) & mask); \
- } \
- } \
-}
-
-#define BV_COPY(a) (a)
-#define BV_NOT(a) (~(a))
-BITVECTOR_COPY_OP(copy, BV_COPY)
-BITVECTOR_COPY_OP(not_to, BV_NOT)
-
-// copy from source to dest while reversing bit-order
-// assumes dest offset == 0
-// assumes source and dest don't overlap
-// assumes offset < 32
-void
-bitvector_reverse_to(uint32_t *dest, uint32_t *src, uint32_t soffs, uint32_t nbits)
-{
- uint32_t i, nw, tail;
-
- if(nbits == 0)
- return;
-
- nw = (soffs+nbits+31)>>5;
- // first, reverse the words while reversing bit order within each word
- for(i = 0; i < nw/2; i++){
- dest[i] = bitreverse(src[nw-i-1]);
- dest[nw-i-1] = bitreverse(src[i]);
- }
- if(nw&0x1)
- dest[i] = bitreverse(src[i]);
-
- tail = (soffs+nbits)&31;
- if(tail)
- bitvector_shr(dest, nw, 32-tail);
-}
-
-void
-bitvector_reverse(uint32_t *b, uint32_t offs, uint32_t nbits)
-{
- uint32_t i, nw, tail, *temp, a[MALLOC_CUTOFF];
-
- if(nbits == 0)
- return;
-
- nw = (offs+nbits+31)>>5;
- temp = (nw > MALLOC_CUTOFF) ? malloc(nw*4) : a;
- for(i = 0; i < nw/2; i++){
- temp[i] = bitreverse(b[nw-i-1]);
- temp[nw-i-1] = bitreverse(b[i]);
- }
- if(nw & 1)
- temp[i] = bitreverse(b[i]);
-
- tail = (offs+nbits)&31;
- bitvector_copy(b, offs, temp, (32-tail)&31, nbits);
- if(nw > MALLOC_CUTOFF)
- free(temp);
-}
-
-uint64_t
-bitvector_count(uint32_t *b, uint32_t offs, uint64_t nbits)
-{
- size_t i, nw;
- uint32_t ntail;
- uint64_t ans;
-
- if(nbits == 0)
- return 0;
- nw = ((uint64_t)offs+nbits+31)>>5;
-
- if(nw == 1)
- return count_bits(b[0] & (lomask(nbits)<<offs));
-
- ans = count_bits(b[0]>>offs); // first end cap
-
- for(i = 1; i < nw-1; i++)
- ans += count_bits(b[i]);
-
- ntail = (offs + (uint32_t)nbits) & 31;
- ans += count_bits(b[i] & (ntail > 0 ? lomask(ntail) : ONES32)); // last end cap
-
- return ans;
-}
-
-uint32_t
-bitvector_any0(uint32_t *b, uint32_t offs, uint32_t nbits)
-{
- uint32_t i, nw, tail, mask;
-
- if(nbits == 0)
- return 0;
- nw = (offs+nbits+31)>>5;
-
- if(nw == 1){
- mask = (lomask(nbits)<<offs);
- if((b[0] & mask) != mask)
- return 1;
- return 0;
- }
-
- mask = ~lomask(offs);
- if((b[0] & mask) != mask)
- return 1;
-
- for(i = 1; i < nw-1; i++)
- if(b[i] != ONES32)
- return 1;
-
- tail = (offs+nbits)&31;
- if(tail == 0)
- return b[i] != ONES32;
- mask = lomask(tail);
- return (b[i] & mask) != mask;
-}
-
-uint32_t
-bitvector_any1(uint32_t *b, uint32_t offs, uint32_t nbits)
-{
- uint32_t i, nw, tail, mask;
-
- if(nbits == 0)
- return 0;
- nw = (offs+nbits+31)>>5;
-
- if(nw == 1){
- mask = lomask(nbits)<<offs;
- return (b[0] & mask) != 0;
- }
-
- mask = ~lomask(offs);
- if((b[0] & mask) != 0)
- return 1;
-
- for(i = 1; i < nw-1; i++){
- if(b[i] != 0)
- return 1;
- }
-
- tail = (offs+nbits)&31;
- if(tail == 0)
- return b[i] != 0;
- return (b[i] & lomask(tail)) != 0;
-}
-
-static void
-adjust_offset_to(uint32_t *dest, uint32_t *src, uint32_t nw, uint32_t soffs, uint32_t newoffs)
-{
- if(newoffs > soffs)
- bitvector_shl_to(dest, src, nw, newoffs-soffs, 1);
- else
- bitvector_shr_to(dest, src, nw, soffs-newoffs);
-}
-
-#define BITVECTOR_BINARY_OP_TO(opname, OP) \
-void \
-bitvector_##opname##_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits) \
-{ \
- uint32_t nw = (doffs+nbits+31)>>5; \
- uint32_t atmp[MALLOC_CUTOFF+1]; \
- uint32_t *temp = nw>MALLOC_CUTOFF ? malloc((nw+1)*4) : atmp; \
- uint32_t i, anw, bnw; \
- if(aoffs == boffs){ \
- anw = (aoffs+nbits+31)>>5; \
- }else if(aoffs == doffs){ \
- bnw = (boffs+nbits+31)>>5; \
- adjust_offset_to(temp, b, bnw, boffs, aoffs); \
- b = temp; \
- anw = nw; \
- }else{ \
- anw = (aoffs+nbits+31)>>5; \
- bnw = (boffs+nbits+31)>>5; \
- adjust_offset_to(temp, a, anw, aoffs, boffs); \
- a = temp; \
- aoffs = boffs; \
- anw = bnw; \
- } \
- for(i = 0; i < anw; i++) \
- temp[i] = OP(a[i], b[i]); \
- bitvector_copy(dest, doffs, temp, aoffs, nbits); \
- if(nw>MALLOC_CUTOFF) \
- free(temp); \
-}
-
-#define BV_AND(a, b) ((a)&(b))
-#define BV_OR(a, b) ((a)|(b))
-#define BV_XOR(a, b) ((a)^(b))
-BITVECTOR_BINARY_OP_TO(and, BV_AND)
-BITVECTOR_BINARY_OP_TO(or, BV_OR)
-BITVECTOR_BINARY_OP_TO(xor, BV_XOR)
--- a/llt/bitvector.c
+++ /dev/null
@@ -1,140 +1,0 @@
-/*
- bit vector primitives
-
- todo:
- * reverse
- * nreverse
- (- rotate left/right)
- * shl_to
- * not
- - shr_row, shl_row
-
- These routines are the back end supporting bit matrices. Many operations
- on bit matrices are slow (such as accessing or setting a single element!)
- but certain operations are privileged and lend themselves to extremely
- efficient implementation due to the bit-vector nature of machine integers.
- These are:
- done:
- & | $ ~ copy reverse fill sum prod
- todo:
- shift trans rowswap
- would be nice:
- channel interleave
-
- Important note:
- Out-of-place functions always assume dest and source have the same amount
- of space available.
-
- shr_to, shl_to, not_to, and reverse_to assume source and dest don't overlap
- and_to, or_to, and xor_to allow overlap.
-*/
-
-#include "llt.h"
-
-uint32_t *
-bitvector_resize(uint32_t *b, uint64_t oldsz, uint64_t newsz, int initzero)
-{
- uint32_t *p;
- size_t sz = ((newsz+31)>>5) * sizeof(uint32_t);
- p = LLT_REALLOC(b, sz);
- if(p == nil)
- return nil;
- if(initzero && newsz>oldsz){
- size_t osz = ((oldsz+31)>>5) * sizeof(uint32_t);
- memset(&p[osz/sizeof(uint32_t)], 0, sz-osz);
- }
- return p;
-}
-
-uint32_t *
-bitvector_new(uint64_t n, int initzero)
-{
- return bitvector_resize(nil, 0, n, initzero);
-}
-
-size_t
-bitvector_nwords(uint64_t nbits)
-{
- return (nbits+31)>>5;
-}
-
-void
-bitvector_set(uint32_t *b, uint64_t n, uint32_t c)
-{
- if(c)
- b[n>>5] |= 1<<(n&31);
- else
- b[n>>5] &= ~(1<<(n&31));
-}
-
-uint32_t
-bitvector_get(uint32_t *b, uint64_t n)
-{
- return b[n>>5] & (1<<(n&31));
-}
-
-static int
-ntz(uint32_t x)
-{
- int n;
-
- if(x == 0)
- return 32;
- n = 1;
- if((x & 0x0000FFFF) == 0){
- n = n +16;
- x = x >>16;
- }
- if((x & 0x000000FF) == 0){
- n = n + 8;
- x = x >> 8;
- }
- if((x & 0x0000000F) == 0){
- n = n + 4;
- x = x >> 4;
- }
- if((x & 0x00000003) == 0){
- n = n + 2;
- x = x >> 2;
- }
- return n - (x & 1);
-}
-
-// given a bitvector of n bits, starting at bit n0 find the next
-// set bit, including n0.
-// returns n if no set bits.
-uint32_t
-bitvector_next(uint32_t *b, uint64_t n0, uint64_t n)
-{
- if(n0 >= n)
- return n;
-
- uint32_t i = n0>>5;
- uint32_t nb = n0&31;
- uint32_t nw = (n+31)>>5;
- uint32_t w;
-
- if(i < nw-1 || (n&31) == 0)
- w = b[i]>>nb;
- else
- w = (b[i]&lomask(n&31)) >> nb;
- if(w != 0)
- return ntz(w) + n0;
- if(i == nw-1)
- return n;
- i++;
- while(i < nw-1){
- w = b[i];
- if(w != 0)
- return ntz(w) + (i<<5);
- i++;
- }
- w = b[i];
- nb = n&31;
- i = ntz(w);
- if(nb == 0)
- return i + (n-32);
- if(i >= nb)
- return n;
- return i + (n-nb);
-}
--- a/llt/bitvector.h
+++ /dev/null
@@ -1,23 +1,0 @@
-uint32_t bitreverse(uint32_t x);
-uint32_t *bitvector_new(uint64_t n, int initzero);
-uint32_t *bitvector_resize(uint32_t *b, uint64_t oldsz, uint64_t newsz, int initzero);
-size_t bitvector_nwords(uint64_t nbits);
-void bitvector_set(uint32_t *b, uint64_t n, uint32_t c);
-uint32_t bitvector_get(uint32_t *b, uint64_t n);
-uint32_t bitvector_next(uint32_t *b, uint64_t n0, uint64_t n);
-void bitvector_shr(uint32_t *b, size_t n, uint32_t s);
-void bitvector_shr_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s);
-void bitvector_shl(uint32_t *b, size_t n, uint32_t s);
-void bitvector_shl_to(uint32_t *dest, uint32_t *b, size_t n, uint32_t s, int scrap);
-void bitvector_fill(uint32_t *b, uint32_t offs, uint32_t c, uint32_t nbits);
-void bitvector_copy(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t nbits);
-void bitvector_not(uint32_t *b, uint32_t offs, uint32_t nbits);
-void bitvector_not_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t nbits);
-void bitvector_reverse(uint32_t *b, uint32_t offs, uint32_t nbits);
-void bitvector_reverse_to(uint32_t *dest, uint32_t *src, uint32_t soffs, uint32_t nbits);
-void bitvector_and_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
-void bitvector_or_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
-void bitvector_xor_to(uint32_t *dest, uint32_t doffs, uint32_t *a, uint32_t aoffs, uint32_t *b, uint32_t boffs, uint32_t nbits);
-uint64_t bitvector_count(uint32_t *b, uint32_t offs, uint64_t nbits);
-uint32_t bitvector_any0(uint32_t *b, uint32_t offs, uint32_t nbits);
-uint32_t bitvector_any1(uint32_t *b, uint32_t offs, uint32_t nbits);
--- a/llt/dump.c
+++ /dev/null
@@ -1,35 +1,0 @@
-#include "llt.h"
-
-static char hexdig[] = "0123456789abcdef";
-
-/*
- display a given number of bytes from a buffer, with the first
- address label being startoffs
-*/
-void
-hexdump(ios_t *dest, const char *buffer, size_t len, size_t startoffs)
-{
- size_t offs = 0;
- size_t i, pos;
- char ch, linebuffer[16], hexc[4];
- static const char *spc50 = " ";
-
- hexc[2] = hexc[3] = ' ';
- do{
- ios_printf(dest, "%.8x ", offs+startoffs);
- pos = 10;
- for(i = 0; i < 16 && offs < len; i++, offs++){
- ch = buffer[offs];
- linebuffer[i] = (ch < 32 || ch >= 0x7f) ? '.' : ch;
- hexc[0] = hexdig[((uint8_t)ch)>>4];
- hexc[1] = hexdig[ch & 0x0f];
- pos += ios_write(dest, hexc, (i == 7 || i == 15) ? 4 : 3);
- }
- for(; i < 16; i++)
- linebuffer[i] = ' ';
- ios_write(dest, spc50, 60-pos);
- ios_putc('|', dest);
- ios_write(dest, linebuffer, 16);
- ios_write(dest, "|\n", 2);
- }while(offs < len);
-}
--- a/llt/hashing.c
+++ /dev/null
@@ -1,75 +1,0 @@
-#include "llt.h"
-
-lltuint_t
-nextipow2(lltuint_t i)
-{
- if(i == 0)
- return 1;
- i |= i >> 1;
- i |= i >> 2;
- i |= i >> 4;
- i |= i >> 8;
- i |= i >> 16;
-#ifdef BITS64
- i |= i >> 32;
-#endif
- i++;
- return i ? i : TOP_BIT;
-}
-
-uint32_t
-int32hash(uint32_t a)
-{
- a = (a+0x7ed55d16) + (a<<12);
- a = (a^0xc761c23c) ^ (a>>19);
- a = (a+0x165667b1) + (a<<5);
- a = (a+0xd3a2646c) ^ (a<<9);
- a = (a+0xfd7046c5) + (a<<3);
- a = (a^0xb55a4f09) ^ (a>>16);
- return a;
-}
-
-uint64_t
-int64hash(uint64_t key)
-{
- key = (~key) + (key << 21); // key = (key << 21) - key - 1;
- key = key ^ (key >> 24);
- key = (key + (key << 3)) + (key << 8); // key * 265
- key = key ^ (key >> 14);
- key = (key + (key << 2)) + (key << 4); // key * 21
- key = key ^ (key >> 28);
- key = key + (key << 31);
- return key;
-}
-
-uint32_t
-int64to32hash(uint64_t key)
-{
- key = (~key) + (key << 18); // key = (key << 18) - key - 1;
- key = key ^ (key >> 31);
- key = key * 21; // key = (key + (key << 2)) + (key << 4);
- key = key ^ (key >> 11);
- key = key + (key << 6);
- key = key ^ (key >> 22);
- return (uint32_t)key;
-}
-
-#include "lookup3.c"
-
-uint64_t
-memhash(const char* buf, size_t n)
-{
- uint32_t c = 0xcafe8881, b = 0x4d6a087c;
-
- hashlittle2(buf, n, &c, &b);
- return (uint64_t)c | (((uint64_t)b)<<32);
-}
-
-uint32_t
-memhash32(const char* buf, size_t n)
-{
- uint32_t c = 0xcafe8881, b = 0x4d6a087c;
-
- hashlittle2(buf, n, &c, &b);
- return c;
-}
--- a/llt/htable.c
+++ /dev/null
@@ -1,52 +1,0 @@
-/*
- functions common to all hash table instantiations
-*/
-
-#include "llt.h"
-#include "htable.h"
-
-htable_t *
-htable_new(htable_t *h, size_t size)
-{
- if(size <= HT_N_INLINE/2){
- h->size = size = HT_N_INLINE;
- h->table = &h->_space[0];
- }else{
- size = nextipow2(size);
- size *= 2; // 2 pointers per key/value pair
- size *= 2; // aim for 50% occupancy
- h->size = size;
- h->table = LLT_ALLOC(size*sizeof(void*));
- }
- if(h->table == nil)
- return nil;
- size_t i;
- for(i = 0; i < size; i++)
- h->table[i] = HT_NOTFOUND;
- return h;
-}
-
-void
-htable_free(htable_t *h)
-{
- if(h->table != &h->_space[0])
- LLT_FREE(h->table);
-}
-
-// empty and reduce size
-void
-htable_reset(htable_t *h, size_t sz)
-{
- sz = nextipow2(sz);
- if(h->size > sz*4 && h->size > HT_N_INLINE){
- size_t newsz = sz*4;
- void **newtab = LLT_REALLOC(h->table, newsz*sizeof(void*));
- if(newtab == nil)
- return;
- h->size = newsz;
- h->table = newtab;
- }
- size_t i, hsz = h->size;
- for(i = 0; i < hsz; i++)
- h->table[i] = HT_NOTFOUND;
-}
--- a/llt/htable.h
+++ /dev/null
@@ -1,22 +1,0 @@
-#ifndef __HTABLE_H_
-#define __HTABLE_H_
-
-#define HT_N_INLINE 32
-
-typedef struct {
- size_t size;
- void **table;
- void *_space[HT_N_INLINE];
-}htable_t;
-
-// define this to be an invalid key/value
-#define HT_NOTFOUND ((void*)1)
-
-// initialize and free
-htable_t *htable_new(htable_t *h, size_t size);
-void htable_free(htable_t *h);
-
-// clear and (possibly) change size
-void htable_reset(htable_t *h, size_t sz);
-
-#endif
--- a/llt/htable.inc
+++ /dev/null
@@ -1,147 +1,0 @@
-//-*- mode:c -*-
-
-/*
- include this file and call HTIMPL to generate an implementation
-*/
-
-#define hash_size(h) ((h)->size/2)
-
-// compute empirical max-probe for a given size
-#define max_probe(size) ((size) <= (HT_N_INLINE*2) ? (HT_N_INLINE/2) : (size)>>3)
-
-#define HTIMPL(HTNAME, HFUNC, EQFUNC) \
-static void ** \
-HTNAME##_lookup_bp(htable_t *h, void *key) \
-{ \
- lltuint_t hv; \
- size_t i, orig, index, iter; \
- size_t newsz, sz = hash_size(h); \
- size_t maxprobe = max_probe(sz); \
- void **tab = h->table; \
- void **ol; \
- \
- hv = HFUNC((uintptr_t)key); \
-retry_bp: \
- iter = 0; \
- index = (hv & (sz-1)) * 2; \
- sz *= 2; \
- orig = index; \
- \
- do{ \
- if(tab[index+1] == HT_NOTFOUND){ \
- tab[index] = key; \
- return &tab[index+1]; \
- } \
- if(EQFUNC(key, tab[index])) \
- return &tab[index+1]; \
- index = (index+2) & (sz-1); \
- iter++; \
- if(iter > maxprobe) \
- break; \
- }while(index != orig); \
- \
- /* table full */ \
- /* quadruple size, rehash, retry the insert */ \
- /* it's important to grow the table really fast; otherwise we waste */ \
- /* lots of time rehashing all the keys over and over. */ \
- sz = h->size; \
- ol = h->table; \
- if(sz >= (1<<19) || (sz <= (1<<8))) \
- newsz = sz<<1; \
- else if(sz <= HT_N_INLINE) \
- newsz = HT_N_INLINE; \
- else \
- newsz = sz<<2; \
- tab = (void**)LLT_ALLOC(newsz*sizeof(void*)); \
- if(tab == nil) \
- return nil; \
- for(i = 0; i < newsz; i++) \
- tab[i] = HT_NOTFOUND; \
- h->table = tab; \
- h->size = newsz; \
- for(i = 0; i < sz; i += 2) { \
- if(ol[i+1] != HT_NOTFOUND) \
- (*HTNAME##_lookup_bp(h, ol[i])) = ol[i+1]; \
- } \
- if(ol != &h->_space[0]) \
- LLT_FREE(ol); \
- sz = hash_size(h); \
- maxprobe = max_probe(sz); \
- tab = h->table; \
- goto retry_bp; \
-} \
- \
-void \
-HTNAME##_put(htable_t *h, void *key, void *val) \
-{ \
- void **bp = HTNAME##_lookup_bp(h, key); \
- *bp = val; \
-} \
- \
-void ** \
-HTNAME##_bp(htable_t *h, void *key) \
-{ \
- return HTNAME##_lookup_bp(h, key); \
-} \
- \
-/* returns bp if key is in hash, otherwise nil */ \
-/* if return is non-nil and *bp == HT_NOTFOUND then key was deleted */ \
-static void ** \
-HTNAME##_peek_bp(htable_t *h, void *key) \
-{ \
- size_t sz = hash_size(h); \
- size_t maxprobe = max_probe(sz); \
- void **tab = h->table; \
- size_t index = (HFUNC((uintptr_t)key) & (sz-1)) * 2; \
- sz *= 2; \
- size_t orig = index; \
- size_t iter = 0; \
- \
- do { \
- if(tab[index] == HT_NOTFOUND) \
- return nil; \
- if(EQFUNC(key, tab[index])) \
- return &tab[index+1]; \
- \
- index = (index+2) & (sz-1); \
- iter++; \
- if(iter > maxprobe) \
- break; \
- }while(index != orig); \
- \
- return nil; \
-} \
- \
-void *\
-HTNAME##_get(htable_t *h, void *key) \
-{ \
- void **bp = HTNAME##_peek_bp(h, key); \
- if(bp == nil) \
- return HT_NOTFOUND; \
- return *bp; \
-} \
- \
-int \
-HTNAME##_has(htable_t *h, void *key) \
-{ \
- return HTNAME##_get(h, key) != HT_NOTFOUND; \
-} \
- \
-int \
-HTNAME##_remove(htable_t *h, void *key) \
-{ \
- void **bp = HTNAME##_peek_bp(h, key); \
- if(bp != nil){ \
- *bp = HT_NOTFOUND; \
- return 1; \
- } \
- return 0; \
-} \
- \
-void \
-HTNAME##_adjoin(htable_t *h, void *key, void *val) \
-{ \
- void **bp = HTNAME##_lookup_bp(h, key); \
- if(*bp == HT_NOTFOUND) \
- *bp = val; \
-}
--- a/llt/htableh.inc
+++ /dev/null
@@ -1,30 +1,0 @@
-//-*- mode:c -*-
-
-#include "htable.h"
-
-#define HTPROT(HTNAME) \
-void *HTNAME##_get(htable_t *h, void *key); \
-void HTNAME##_put(htable_t *h, void *key, void *val); \
-void HTNAME##_adjoin(htable_t *h, void *key, void *val); \
-int HTNAME##_has(htable_t *h, void *key); \
-int HTNAME##_remove(htable_t *h, void *key); \
-void **HTNAME##_bp(htable_t *h, void *key);
-
-// return value, or HT_NOTFOUND if key not found
-
-// add key/value binding
-
-// add binding iff key is unbound
-
-// does key exist?
-
-// logically remove key
-
-// get a pointer to the location of the value for the given key.
-// creates the location if it doesn't exist. only returns nil
-// if memory allocation fails.
-// this should be used for updates, for example:
-// void **bp = ptrhash_bp(h, key);
-// *bp = f(*bp);
-// do not reuse bp if there might be intervening calls to ptrhash_put,
-// ptrhash_bp, ptrhash_reset, or ptrhash_free.
--- a/llt/ieee754.h
+++ /dev/null
@@ -1,66 +1,0 @@
-#ifndef __IEEE754_H_
-#define __IEEE754_H_
-
-union ieee754_float {
- float f;
-
- struct {
-#if BYTE_ORDER == BIG_ENDIAN
- unsigned int negative:1;
- unsigned int exponent:8;
- unsigned int mantissa:23;
-#elif BYTE_ORDER == LITTLE_ENDIAN
- unsigned int mantissa:23;
- unsigned int exponent:8;
- unsigned int negative:1;
-#else
-#error which endian?
-#endif
- }ieee;
-};
-
-#define IEEE754_FLOAT_BIAS 0x7f
-
-union ieee754_double {
- double d;
-
- struct {
-#if BYTE_ORDER == BIG_ENDIAN
- unsigned int negative:1;
- unsigned int exponent:11;
- unsigned int mantissa0:20;
- unsigned int mantissa1:32;
-#else
- unsigned int mantissa1:32;
- unsigned int mantissa0:20;
- unsigned int exponent:11;
- unsigned int negative:1;
-#endif
- }ieee;
-};
-
-#define IEEE754_DOUBLE_BIAS 0x3ff
-
-union ieee854_long_double {
- long double d;
-
- struct {
-#if BYTE_ORDER == BIG_ENDIAN
- unsigned int negative:1;
- unsigned int exponent:15;
- unsigned int empty:16;
- unsigned int mantissa0:32;
- unsigned int mantissa1:32;
-#else
- unsigned int mantissa1:32;
- unsigned int mantissa0:32;
- unsigned int exponent:15;
- unsigned int negative:1;
- unsigned int empty:16;
-#endif
- }ieee;
-};
-
-#define IEEE854_LONG_DOUBLE_BIAS 0x3fff
-
-#endif
--- a/llt/int2str.c
+++ /dev/null
@@ -1,30 +1,0 @@
-#include "llt.h"
-
-char *
-uint2str(char *dest, size_t len, uint64_t num, uint32_t base)
-{
- int i = len-1;
- uint64_t b = (uint64_t)base;
- char ch;
- dest[i--] = '\0';
- while(i >= 0){
- ch = (char)(num % b);
- if(ch < 10)
- ch += '0';
- else
- ch = ch-10+'a';
- dest[i--] = ch;
- num /= b;
- if(num == 0)
- break;
- }
- return &dest[i+1];
-}
-
-int
-isdigit_base(char c, int base)
-{
- if(base < 11)
- return c >= '0' && c < '0'+base;
- return (c >= '0' && c <= '9') || (c >= 'a' && c < 'a'+base-10) || (c >= 'A' && c < 'A'+base-10);
-}
--- a/llt/ios.c
+++ /dev/null
@@ -1,1018 +1,0 @@
-#include "llt.h"
-
-#define MOST_OF(x) ((x) - ((x)>>4))
-
-ios_t *ios_stdin = nil;
-ios_t *ios_stdout = nil;
-ios_t *ios_stderr = nil;
-
-/* OS-level primitive wrappers */
-
-void *
-llt_memrchr(const void *s, int c, size_t n)
-{
- const uint8_t *src = (const uint8_t*)s + n;
- uint8_t uc = c;
- while(--src >= (uint8_t*)s)
- if(*src == uc)
- return (void *)src;
- return nil;
-}
-
-#if !defined(__plan9__)
-static int
-_enonfatal(int err)
-{
- return err == EAGAIN || err == EINPROGRESS || err == EINTR || err == EWOULDBLOCK;
-}
-#define SLEEP_TIME 5//ms
-#endif
-
-// return error code, #bytes read in *nread
-// these wrappers retry operations until success or a fatal error
-static int
-_os_read(long fd, char *buf, size_t n, size_t *nread)
-{
- ssize_t r;
-
- while(1){
- r = read((int)fd, buf, n);
- if(r > -1){
- *nread = (size_t)r;
- break;
- }
-#if defined(__plan9__)
- return r;
-#else
- if(!_enonfatal(errno)){
- *nread = 0;
- return errno;
- }
- sleep_ms(SLEEP_TIME);
-#endif
- }
- return 0;
-}
-
-static int
-_os_read_all(long fd, char *buf, size_t n, size_t *nread)
-{
- size_t got;
-
- *nread = 0;
-
- while(n > 0){
- int err = _os_read(fd, buf, n, &got);
- n -= got;
- *nread += got;
- buf += got;
- if(err || got == 0)
- return err;
- }
- return 0;
-}
-
-static int
-_os_write(long fd, const void *buf, size_t n, size_t *nwritten)
-{
- ssize_t r;
-
- while(1){
- r = write((int)fd, buf, n);
- if(r > -1){
- *nwritten = (size_t)r;
- break;
- }
-#if defined(__plan9__)
- return r;
-#else
- if(!_enonfatal(errno)){
- *nwritten = 0;
- return errno;
- }
- sleep_ms(SLEEP_TIME);
-#endif
- }
- return 0;
-}
-
-static int
-_os_write_all(long fd, const char *buf, size_t n, size_t *nwritten)
-{
- size_t wrote;
-
- *nwritten = 0;
- while(n > 0){
- int err = _os_write(fd, buf, n, &wrote);
- n -= wrote;
- *nwritten += wrote;
- buf += wrote;
- if(err)
- return err;
- }
- return 0;
-}
-
-
-/* internal utility functions */
-
-static char *
-_buf_realloc(ios_t *s, size_t sz)
-{
- char *temp;
-
- if((s->buf == nil || s->buf == &s->local[0]) && sz <= IOS_INLSIZE){
- /* TODO: if we want to allow shrinking, see if the buffer shrank
- down to this size, in which case we need to copy. */
- s->buf = &s->local[0];
- s->maxsize = IOS_INLSIZE;
- s->ownbuf = 1;
- return s->buf;
- }
-
- if(sz <= s->maxsize)
- return s->buf;
-
- if(s->ownbuf && s->buf != &s->local[0]){
- // if we own the buffer we're free to resize it
- // always allocate 1 bigger in case user wants to add a NUL
- // terminator after taking over the buffer
- temp = LLT_REALLOC(s->buf, sz);
- if(temp == nil)
- return nil;
- }else{
- temp = LLT_ALLOC(sz);
- if(temp == nil)
- return nil;
- s->ownbuf = 1;
- if(s->size > 0)
- memmove(temp, s->buf, s->size);
- }
-
- s->buf = temp;
- s->maxsize = sz;
- return s->buf;
-}
-
-// write a block of data into the buffer at the current position, resizing
-// if necessary. returns # written.
-static size_t
-_write_grow(ios_t *s, const char *data, size_t n)
-{
- size_t amt;
- size_t newsize;
-
- if(n == 0)
- return 0;
-
- if(s->bpos + n > s->size){
- if(s->bpos + n > s->maxsize){
- /* TODO: here you might want to add a mechanism for limiting
- the growth of the stream. */
- newsize = s->maxsize ? s->maxsize * 2 : 8;
- while(s->bpos + n > newsize)
- newsize *= 2;
- if(_buf_realloc(s, newsize) == nil){
- /* no more space; write as much as we can */
- amt = s->maxsize - s->bpos;
- if(amt > 0){
- memmove(&s->buf[s->bpos], data, amt);
- }
- s->bpos += amt;
- s->size = s->maxsize;
- return amt;
- }
- }
- s->size = s->bpos + n;
- }
- memmove(s->buf + s->bpos, data, n);
- s->bpos += n;
-
- return n;
-}
-
-
-/* interface functions, low level */
-
-static size_t
-_ios_read(ios_t *s, char *dest, size_t n, int all)
-{
- size_t tot = 0;
- size_t got, avail;
-
- while(n > 0){
- avail = s->size - s->bpos;
-
- if(avail > 0){
- size_t ncopy = avail >= n ? n : avail;
- memmove(dest, s->buf + s->bpos, ncopy);
- s->bpos += ncopy;
- if(ncopy >= n){
- s->state = bst_rd;
- return tot+ncopy;
- }
- }
- if(s->bm == bm_mem || s->fd == -1){
- // can't get any more data
- s->state = bst_rd;
- if(avail == 0)
- s->_eof = 1;
- return avail;
- }
-
- dest += avail;
- n -= avail;
- tot += avail;
-
- ios_flush(s);
- s->bpos = s->size = 0;
- s->state = bst_rd;
-
- s->fpos = -1;
- if(n > MOST_OF(s->maxsize)){
- // doesn't fit comfortably in buffer; go direct
- if(all)
- _os_read_all(s->fd, dest, n, &got);
- else
- _os_read(s->fd, dest, n, &got);
- tot += got;
- if(got == 0)
- s->_eof = 1;
- return tot;
- }else{
- // refill buffer
- if(_os_read(s->fd, s->buf, s->maxsize, &got)){
- s->_eof = 1;
- return tot;
- }
- if(got == 0){
- s->_eof = 1;
- return tot;
- }
- s->size = got;
- }
- }
-
- return tot;
-}
-
-size_t
-ios_read(ios_t *s, char *dest, size_t n)
-{
- return _ios_read(s, dest, n, 0);
-}
-
-size_t
-ios_readall(ios_t *s, char *dest, size_t n)
-{
- return _ios_read(s, dest, n, 1);
-}
-
-size_t
-ios_readprep(ios_t *s, size_t n)
-{
- if(s->state == bst_wr && s->bm != bm_mem){
- ios_flush(s);
- s->bpos = s->size = 0;
- }
- size_t space = s->size - s->bpos;
- s->state = bst_rd;
- if(space >= n || s->bm == bm_mem || s->fd == -1)
- return space;
- if(s->maxsize < s->bpos+n){
- // it won't fit. grow buffer or move data back.
- if(n <= s->maxsize && space <= ((s->maxsize)>>2)){
- if(space)
- memmove(s->buf, s->buf+s->bpos, space);
- s->size -= s->bpos;
- s->bpos = 0;
- }
- else {
- if(_buf_realloc(s, s->bpos + n) == nil)
- return space;
- }
- }
- size_t got;
- int result = _os_read(s->fd, s->buf+s->size, s->maxsize - s->size, &got);
- if(result)
- return space;
- s->size += got;
- return s->size - s->bpos;
-}
-
-static void
-_write_update_pos(ios_t *s)
-{
- if(s->bpos > s->ndirty)
- s->ndirty = s->bpos;
- if(s->bpos > s->size)
- s->size = s->bpos;
-}
-
-size_t
-ios_write(ios_t *s, const char *data, size_t n)
-{
- if(s->readonly)
- return 0;
- if(n == 0)
- return 0;
- size_t space;
- size_t wrote = 0;
-
- if(s->state == bst_none)
- s->state = bst_wr;
- if(s->state == bst_rd){
- if(!s->rereadable){
- s->size = 0;
- s->bpos = 0;
- }
- space = s->size - s->bpos;
- }else{
- space = s->maxsize - s->bpos;
- }
-
- if(s->bm == bm_mem){
- wrote = _write_grow(s, data, n);
- }else if(s->bm == bm_none){
- s->fpos = -1;
- _os_write_all(s->fd, data, n, &wrote);
- return wrote;
- }else if(n <= space){
- if(s->bm == bm_line){
- char *nl;
- if((nl = llt_memrchr(data, '\n', n)) != nil){
- size_t linesz = nl-data+1;
- s->bm = bm_block;
- wrote += ios_write(s, data, linesz);
- ios_flush(s);
- s->bm = bm_line;
- n -= linesz;
- data += linesz;
- }
- }
- memmove(s->buf + s->bpos, data, n);
- s->bpos += n;
- wrote += n;
- }else{
- s->state = bst_wr;
- ios_flush(s);
- if(n > MOST_OF(s->maxsize)){
- _os_write_all(s->fd, data, n, &wrote);
- return wrote;
- }
- return ios_write(s, data, n);
- }
- _write_update_pos(s);
- return wrote;
-}
-
-off_t
-ios_seek(ios_t *s, off_t pos)
-{
- s->_eof = 0;
- if(s->bm == bm_mem){
- if((size_t)pos > s->size)
- return -1;
- s->bpos = pos;
- }else{
- ios_flush(s);
- off_t fdpos = lseek(s->fd, pos, SEEK_SET);
- if(fdpos == (off_t)-1)
- return fdpos;
- s->bpos = s->size = 0;
- }
- return 0;
-}
-
-off_t
-ios_seek_end(ios_t *s)
-{
- s->_eof = 1;
- if(s->bm == bm_mem){
- s->bpos = s->size;
- }else{
- ios_flush(s);
- off_t fdpos = lseek(s->fd, 0, SEEK_END);
- if(fdpos == (off_t)-1)
- return fdpos;
- s->bpos = s->size = 0;
- }
- return 0;
-}
-
-off_t
-ios_skip(ios_t *s, off_t offs)
-{
- if(offs != 0){
- if(offs > 0){
- if(offs <= (off_t)(s->size-s->bpos)){
- s->bpos += offs;
- return 0;
- }else if(s->bm == bm_mem){
- // TODO: maybe grow buffer
- return -1;
- }
- }else if(offs < 0){
- if(-offs <= (off_t)s->bpos){
- s->bpos += offs;
- s->_eof = 0;
- return 0;
- }else if(s->bm == bm_mem){
- return -1;
- }
- }
- ios_flush(s);
- if(s->state == bst_wr)
- offs += s->bpos;
- else if(s->state == bst_rd)
- offs -= s->size - s->bpos;
- off_t fdpos = lseek(s->fd, offs, SEEK_CUR);
- if(fdpos == (off_t)-1)
- return fdpos;
- s->bpos = s->size = 0;
- s->_eof = 0;
- }
- return 0;
-}
-
-off_t
-ios_pos(ios_t *s)
-{
- if(s->bm == bm_mem)
- return (off_t)s->bpos;
-
- off_t fdpos = s->fpos;
- if(fdpos == (off_t)-1){
- fdpos = lseek(s->fd, 0, SEEK_CUR);
- if(fdpos == (off_t)-1)
- return fdpos;
- s->fpos = fdpos;
- }
-
- if(s->state == bst_wr)
- fdpos += s->bpos;
- else if(s->state == bst_rd)
- fdpos -= s->size - s->bpos;
- return fdpos;
-}
-
-size_t
-ios_trunc(ios_t *s, size_t size)
-{
- if(s->bm == bm_mem){
- if(size == s->size)
- return s->size;
- if(size < s->size){
- if(s->bpos > size)
- s->bpos = size;
- }else if(_buf_realloc(s, size) == nil)
- return s->size;
- s->size = size;
- return size;
- }
- //todo
- return 0;
-}
-
-int
-ios_eof(ios_t *s)
-{
- if(s->bm == bm_mem)
- return s->_eof;
- return s->fd == -1 || s->_eof;
-}
-
-int
-ios_flush(ios_t *s)
-{
- if(s->ndirty == 0 || s->bm == bm_mem || s->buf == nil)
- return 0;
- if(s->fd == -1)
- return -1;
-
- if(s->state == bst_rd){
- if(lseek(s->fd, -(off_t)s->size, SEEK_CUR) == (off_t)-1){
- // FIXME eh?
- }
- }
-
- size_t nw, ntowrite = s->ndirty;
- s->fpos = -1;
- int err = _os_write_all(s->fd, s->buf, ntowrite, &nw);
- // todo: try recovering from some kinds of errors (e.g. retry)
-
- if(s->state == bst_rd){
- if(lseek(s->fd, s->size - nw, SEEK_CUR) == (off_t)-1){
- // FIXME eh?
- }
- }else if(s->state == bst_wr){
- if(s->bpos != nw && lseek(s->fd, (off_t)s->bpos - (off_t)nw, SEEK_CUR) == (off_t)-1){
- // FIXME eh?
- }
- // now preserve the invariant that data to write
- // begins at the beginning of the buffer, and s->size refers
- // to how much valid file data is stored in the buffer.
- if(s->size > s->ndirty){
- size_t delta = s->size - s->ndirty;
- memmove(s->buf, s->buf + s->ndirty, delta);
- }
- s->size -= s->ndirty;
- s->bpos = 0;
- }
-
- s->ndirty = 0;
-
- if(err)
- return err;
- if(nw < ntowrite)
- return -1;
- return 0;
-}
-
-void
-ios_close(ios_t *s)
-{
- ios_flush(s);
- if(s->fd != -1 && s->ownfd)
- close(s->fd);
- s->fd = -1;
- if(s->buf != nil && s->ownbuf && s->buf != &s->local[0])
- LLT_FREE(s->buf);
- s->buf = nil;
- s->size = s->maxsize = s->bpos = 0;
-}
-
-static void
-_buf_init(ios_t *s, bufmode_t bm)
-{
- s->bm = bm;
- if(s->bm == bm_mem || s->bm == bm_none){
- s->buf = &s->local[0];
- s->maxsize = IOS_INLSIZE;
- }else{
- s->buf = nil;
- _buf_realloc(s, IOS_BUFSIZE);
- }
- s->size = s->bpos = 0;
-}
-
-char *
-ios_takebuf(ios_t *s, size_t *psize)
-{
- char *buf;
-
- ios_flush(s);
-
- if(s->buf == &s->local[0] || s->buf == nil || (!s->ownbuf && s->size == s->maxsize)){
- buf = LLT_ALLOC(s->size+1);
- if(buf == nil)
- return nil;
- if(s->size)
- memmove(buf, s->buf, s->size);
- }else if(s->size == s->maxsize){
- buf = LLT_REALLOC(s->buf, s->size + 1);
- if(buf == nil)
- return nil;
- }else{
- buf = s->buf;
- }
- buf[s->size] = '\0';
- *psize = s->size + 1;
-
- /* empty stream and reinitialize */
- _buf_init(s, s->bm);
-
- return buf;
-}
-
-int
-ios_setbuf(ios_t *s, char *buf, size_t size, int own)
-{
- ios_flush(s);
- size_t nvalid;
-
- nvalid = size < s->size ? size : s->size;
- if(nvalid > 0)
- memmove(buf, s->buf, nvalid);
- if(s->bpos > nvalid){
- // truncated
- s->bpos = nvalid;
- }
- s->size = nvalid;
-
- if(s->buf != nil && s->ownbuf && s->buf != &s->local[0])
- LLT_FREE(s->buf);
- s->buf = buf;
- s->maxsize = size;
- s->ownbuf = own;
- return 0;
-}
-
-int
-ios_bufmode(ios_t *s, bufmode_t mode)
-{
- // no fd; can only do mem-only buffering
- if(s->fd == -1 && mode != bm_mem)
- return -1;
- s->bm = mode;
- return 0;
-}
-
-void
-ios_set_readonly(ios_t *s)
-{
- if(s->readonly)
- return;
- ios_flush(s);
- s->state = bst_none;
- s->readonly = 1;
-}
-
-static size_t
-ios_copy_(ios_t *to, ios_t *from, size_t nbytes, int all)
-{
- size_t total = 0, avail;
- if(!ios_eof(from)){
- do{
- avail = ios_readprep(from, IOS_BUFSIZE/2);
- if(avail == 0){
- from->_eof = 1;
- break;
- }
- size_t written, ntowrite;
- ntowrite = (avail <= nbytes || all) ? avail : nbytes;
- written = ios_write(to, from->buf+from->bpos, ntowrite);
- // TODO: should this be +=written instead?
- from->bpos += ntowrite;
- total += written;
- if(!all){
- nbytes -= written;
- if(nbytes == 0)
- break;
- }
- if(written < ntowrite)
- break;
- }while(!ios_eof(from));
- }
- return total;
-}
-
-size_t
-ios_copy(ios_t *to, ios_t *from, size_t nbytes)
-{
- return ios_copy_(to, from, nbytes, 0);
-}
-
-size_t
-ios_copyall(ios_t *to, ios_t *from)
-{
- return ios_copy_(to, from, 0, 1);
-}
-
-#define LINE_CHUNK_SIZE 160
-
-size_t
-ios_copyuntil(ios_t *to, ios_t *from, char delim)
-{
- size_t total = 0, avail = from->size - from->bpos;
- int first = 1;
- if(!ios_eof(from)){
- do{
- if(avail == 0){
- first = 0;
- avail = ios_readprep(from, LINE_CHUNK_SIZE);
- }
- size_t written;
- char *pd = memchr(from->buf+from->bpos, delim, avail);
- if(pd == nil){
- written = ios_write(to, from->buf+from->bpos, avail);
- from->bpos += avail;
- total += written;
- avail = 0;
- }else{
- size_t ntowrite = pd - (from->buf+from->bpos) + 1;
- written = ios_write(to, from->buf+from->bpos, ntowrite);
- from->bpos += ntowrite;
- total += written;
- return total;
- }
- }while(!ios_eof(from) && (first || avail >= LINE_CHUNK_SIZE));
- }
- from->_eof = 1;
- return total;
-}
-
-static void
-_ios_init(ios_t *s)
-{
- // put all fields in a sane initial state
- memset(s, 0, sizeof(*s));
- s->bm = bm_block;
- s->state = bst_none;
- s->fpos = -1;
- s->lineno = 1;
- s->fd = -1;
- s->ownbuf = 1;
-}
-
-/* stream object initializers. we do no allocation. */
-
-ios_t *
-ios_file(ios_t *s, char *fname, int rd, int wr, int creat, int trunc)
-{
- int fd;
- if(!(rd || wr)) // must specify read and/or write
- goto open_file_err;
- int flags = wr ? (rd ? O_RDWR : O_WRONLY) : O_RDONLY;
- if(trunc)
- flags |= O_TRUNC;
-#if defined(__plan9__)
- fd = creat ? create(fname, flags, 0644) : open(fname, flags);
-#else
- if(creat)
- flags |= O_CREAT;
- fd = open(fname, flags, 0644);
-#endif
- s = ios_fd(s, fd, 1, 1);
- if(fd < 0)
- goto open_file_err;
- if(!wr)
- s->readonly = 1;
- return s;
-open_file_err:
- s->fd = -1;
- return nil;
-}
-
-ios_t *
-ios_mem(ios_t *s, size_t initsize)
-{
- _ios_init(s);
- s->bm = bm_mem;
- _buf_realloc(s, initsize);
- return s;
-}
-
-ios_t *
-ios_str(ios_t *s, char *str)
-{
- size_t n = strlen(str);
- if(ios_mem(s, n+1) == nil)
- return nil;
- ios_write(s, str, n+1);
- ios_seek(s, 0);
- return s;
-}
-
-ios_t *
-ios_static_buffer(ios_t *s, const char *buf, size_t sz)
-{
- ios_mem(s, 0);
- ios_setbuf(s, (char*)buf, sz, 0);
- s->size = sz;
- ios_set_readonly(s);
- return s;
-}
-
-ios_t *
-ios_fd(ios_t *s, long fd, int isfile, int own)
-{
- _ios_init(s);
- s->fd = fd;
- if(isfile)
- s->rereadable = 1;
- _buf_init(s, bm_block);
- s->ownfd = own;
- if(fd == STDERR_FILENO)
- s->bm = bm_none;
- return s;
-}
-
-void
-ios_init_stdstreams(void)
-{
- ios_stdin = LLT_ALLOC(sizeof(ios_t));
- ios_fd(ios_stdin, STDIN_FILENO, 0, 0);
-
- ios_stdout = LLT_ALLOC(sizeof(ios_t));
- ios_fd(ios_stdout, STDOUT_FILENO, 0, 0);
- ios_stdout->bm = bm_line;
-
- ios_stderr = LLT_ALLOC(sizeof(ios_t));
- ios_fd(ios_stderr, STDERR_FILENO, 0, 0);
- ios_stderr->bm = bm_none;
-}
-
-/* higher level interface */
-
-int
-ios_putc(int c, ios_t *s)
-{
- char ch = c;
-
- if(s->state == bst_wr && s->bpos < s->maxsize && s->bm != bm_none){
- s->buf[s->bpos++] = ch;
- _write_update_pos(s);
- if(s->bm == bm_line && ch == '\n')
- ios_flush(s);
- return 1;
- }
- return ios_write(s, &ch, 1);
-}
-
-int
-ios_getc(ios_t *s)
-{
- char ch;
- if(s->state == bst_rd && s->bpos < s->size)
- ch = s->buf[s->bpos++];
- else if(s->_eof)
- return IOS_EOF;
- else if(ios_read(s, &ch, 1) < 1)
- return IOS_EOF;
- if(ch == '\n')
- s->lineno++;
- return (uint8_t)ch;
-}
-
-int
-ios_peekc(ios_t *s)
-{
- if(s->bpos < s->size)
- return (uint8_t)s->buf[s->bpos];
- if(s->_eof)
- return IOS_EOF;
- size_t n = ios_readprep(s, 1);
- if(n == 0)
- return IOS_EOF;
- return (uint8_t)s->buf[s->bpos];
-}
-
-int
-ios_ungetc(int c, ios_t *s)
-{
- if(s->state == bst_wr)
- return IOS_EOF;
- if(c == '\n')
- s->lineno--;
- if(s->bpos > 0){
- s->bpos--;
- if(s->buf[s->bpos] != (char)c)
- s->buf[s->bpos] = (char)c;
- s->_eof = 0;
- return c;
- }
- if(s->size == s->maxsize && _buf_realloc(s, s->maxsize*2) == nil)
- return IOS_EOF;
- memmove(s->buf + 1, s->buf, s->size);
- s->buf[0] = (char)c;
- s->size++;
- s->_eof = 0;
- return c;
-}
-
-int
-ios_getutf8(ios_t *s, uint32_t *pwc)
-{
- int c;
- size_t sz;
- char c0;
- char buf[8];
-
- c = ios_peekc(s);
- if(c == IOS_EOF){
- s->_eof = 1;
- return IOS_EOF;
- }
- c0 = (char)c;
- if((uint8_t)c0 < 0x80){
- ios_getc(s);
- *pwc = (uint32_t)(uint8_t)c0;
- return 1;
- }
- sz = u8_seqlen(&c0)-1;
- if(!isutf(c0) || sz > 3)
- return 0;
- if(ios_readprep(s, sz) < sz){
- // NOTE: this returns EOF even though some bytes are available
- // so we do not set s->_eof on this code path
- return IOS_EOF;
- }
- if(u8_isvalid(&s->buf[s->bpos], sz+1)){
- size_t i = s->bpos;
- *pwc = u8_nextchar(s->buf, &i);
- ios_read(s, buf, sz+1);
- return 1;
- }
- return 0;
-}
-
-int
-ios_peekutf8(ios_t *s, uint32_t *pwc)
-{
- int c;
- size_t sz;
- char c0;
-
- c = ios_peekc(s);
- if(c == IOS_EOF)
- return IOS_EOF;
- c0 = (char)c;
- if((uint8_t)c0 < 0x80){
- *pwc = (uint32_t)(uint8_t)c0;
- return 1;
- }
- sz = u8_seqlen(&c0)-1;
- if(!isutf(c0) || sz > 3)
- return 0;
- if(ios_readprep(s, sz) < sz)
- return IOS_EOF;
- if(u8_isvalid(&s->buf[s->bpos], sz+1)){
- size_t i = s->bpos;
- *pwc = u8_nextchar(s->buf, &i);
- return 1;
- }
- return 0;
-}
-
-int
-ios_pututf8(ios_t *s, uint32_t wc)
-{
- char buf[8];
- if(wc < 0x80)
- return ios_putc((int)wc, s);
- size_t n = u8_toutf8(buf, 8, &wc, 1);
- return ios_write(s, buf, n);
-}
-
-void
-ios_purge(ios_t *s)
-{
- if(s->state == bst_rd)
- s->bpos = s->size;
-}
-
-char *
-ios_readline(ios_t *s)
-{
- ios_t dest;
- ios_mem(&dest, 0);
- ios_copyuntil(&dest, s, '\n');
- size_t n;
- return ios_takebuf(&dest, &n);
-}
-
-int
-ios_vprintf(ios_t *s, const char *format, va_list args)
-{
- char *str;
- int c;
-
-#if defined(__plan9__)
- str = vsmprint(format, args);
- if((c = strlen(str)) >= 0)
- ios_write(s, str, c);
- free(str);
-#else
- va_list al;
- va_copy(al, args);
-
- if(s->state == bst_wr && s->bpos < s->maxsize && s->bm != bm_none){
- int avail = s->maxsize - s->bpos;
- char *start = s->buf + s->bpos;
- c = vsnprintf(start, avail, format, args);
- if(c < 0){
- va_end(al);
- return c;
- }
- if(c < avail){
- s->bpos += (size_t)c;
- _write_update_pos(s);
- // TODO: only works right if newline is at end
- if(s->bm == bm_line && llt_memrchr(start, '\n', (size_t)c))
- ios_flush(s);
- va_end(al);
- return c;
- }
- }
- c = vasprintf(&str, format, al);
- if(c >= 0){
- ios_write(s, str, c);
- LLT_FREE(str);
- }
- va_end(al);
-#endif
- return c;
-}
-
-int
-ios_printf(ios_t *s, const char *format, ...)
-{
- va_list args;
- int c;
-
- va_start(args, format);
- c = ios_vprintf(s, format, args);
- va_end(args);
- return c;
-}
--- a/llt/ios.h
+++ /dev/null
@@ -1,207 +1,0 @@
-// this flag controls when data actually moves out to the underlying I/O
-// channel. memory streams are a special case of this where the data
-// never moves out.
-typedef enum {
- bm_none,
- bm_line,
- bm_block,
- bm_mem,
-}bufmode_t;
-
-typedef enum {
- bst_none,
- bst_rd,
- bst_wr,
-}bufstate_t;
-
-#define IOS_INLSIZE 54
-#define IOS_BUFSIZE 131072
-
-typedef struct {
- bufmode_t bm;
-
- // the state only indicates where the underlying file position is relative
- // to the buffer. reading: at the end. writing: at the beginning.
- // in general, you can do any operation in any state.
- bufstate_t state;
-
- int errcode;
-
- char *buf; // start of buffer
- size_t maxsize; // space allocated to buffer
- size_t size; // length of valid data in buf, >=ndirty
- size_t bpos; // current position in buffer
- size_t ndirty; // # bytes at &buf[0] that need to be written
-
- off_t fpos; // cached file pos
- size_t lineno; // current line number
-
- int fd;
-
- uint8_t readonly:1;
- uint8_t ownbuf:1;
- uint8_t ownfd:1;
- uint8_t _eof:1;
-
- // this means you can read, seek back, then read the same data
- // again any number of times. usually only true for files and strings.
- uint8_t rereadable:1;
-
- // this enables "stenciled writes". you can alternately write and
- // seek without flushing in between. this performs read-before-write
- // to populate the buffer, so "rereadable" capability is required.
- // this is off by default.
- //uint8_t stenciled:1;
-
- // request durable writes (fsync)
- // uint8_t durable:1;
-
- // todo: mutex
- char local[IOS_INLSIZE];
-}ios_t;
-
-/* low-level interface functions */
-size_t ios_read(ios_t *s, char *dest, size_t n);
-size_t ios_readall(ios_t *s, char *dest, size_t n);
-size_t ios_write(ios_t *s, const char *data, size_t n);
-off_t ios_seek(ios_t *s, off_t pos); // absolute seek
-off_t ios_seek_end(ios_t *s);
-off_t ios_skip(ios_t *s, off_t offs); // relative seek
-off_t ios_pos(ios_t *s); // get current position
-size_t ios_trunc(ios_t *s, size_t size);
-int ios_eof(ios_t *s);
-int ios_flush(ios_t *s);
-void ios_close(ios_t *s);
-char *ios_takebuf(ios_t *s, size_t *psize); // null-terminate and release buffer to caller
-// set buffer space to use
-int ios_setbuf(ios_t *s, char *buf, size_t size, int own);
-int ios_bufmode(ios_t *s, bufmode_t mode);
-void ios_set_readonly(ios_t *s);
-size_t ios_copy(ios_t *to, ios_t *from, size_t nbytes);
-size_t ios_copyall(ios_t *to, ios_t *from);
-size_t ios_copyuntil(ios_t *to, ios_t *from, char delim);
-// ensure at least n bytes are buffered if possible. returns # available.
-size_t ios_readprep(ios_t *from, size_t n);
-//void ios_lock(ios_t *s);
-//int ios_trylock(ios_t *s);
-//int ios_unlock(ios_t *s);
-
-/* stream creation */
-ios_t *ios_file(ios_t *s, char *fname, int rd, int wr, int create, int trunc);
-ios_t *ios_mem(ios_t *s, size_t initsize);
-ios_t *ios_str(ios_t *s, char *str);
-ios_t *ios_static_buffer(ios_t *s, const char *buf, size_t sz);
-ios_t *ios_fd(ios_t *s, long fd, int isfile, int own);
-// todo: ios_socket
-extern ios_t *ios_stdin;
-extern ios_t *ios_stdout;
-extern ios_t *ios_stderr;
-void ios_init_stdstreams(void);
-
-/* high-level functions - output */
-int ios_putnum(ios_t *s, char *data, uint32_t type);
-int ios_putint(ios_t *s, int n);
-int ios_pututf8(ios_t *s, uint32_t wc);
-int ios_putstringz(ios_t *s, char *str, int do_write_nulterm);
-int ios_printf(ios_t *s, const char *format, ...);
-int ios_vprintf(ios_t *s, const char *format, va_list args);
-
-void hexdump(ios_t *dest, const char *buffer, size_t len, size_t startoffs);
-
-/* high-level stream functions - input */
-int ios_getnum(ios_t *s, char *data, uint32_t type);
-int ios_getutf8(ios_t *s, uint32_t *pwc);
-int ios_peekutf8(ios_t *s, uint32_t *pwc);
-int ios_ungetutf8(ios_t *s, uint32_t wc);
-int ios_getstringz(ios_t *dest, ios_t *src);
-int ios_getstringn(ios_t *dest, ios_t *src, size_t nchars);
-int ios_getline(ios_t *s, char **pbuf, size_t *psz);
-char *ios_readline(ios_t *s);
-
-// discard data buffered for reading
-void ios_purge(ios_t *s);
-
-// seek by utf8 sequence increments
-int ios_nextutf8(ios_t *s);
-int ios_prevutf8(ios_t *s);
-
-/* stdio-style functions */
-#define IOS_EOF (-1)
-int ios_putc(int c, ios_t *s);
-//wint_t ios_putwc(ios_t *s, wchar_t wc);
-int ios_getc(ios_t *s);
-int ios_peekc(ios_t *s);
-//wint_t ios_getwc(ios_t *s);
-int ios_ungetc(int c, ios_t *s);
-//wint_t ios_ungetwc(ios_t *s, wint_t wc);
-#define ios_puts(str, s) ios_write(s, str, strlen(str))
-
-/*
- With memory streams, mixed reads and writes are equivalent to performing
- sequences of *p++, as either an lvalue or rvalue. File streams behave
- similarly, but other streams might not support this. Using unbuffered
- mode makes this more predictable.
-
- Note on "unget" functions:
- There are two kinds of functions here: those that operate on sized
- blocks of bytes and those that operate on logical units like "character"
- or "integer". The "unget" functions only work on logical units. There
- is no "unget n bytes". You can only do an unget after a matching get.
- However, data pushed back by an unget is available to all read operations.
- The reason for this is that unget is defined in terms of its effect on
- the underlying buffer (namely, it rebuffers data as if it had been
- buffered but not read yet). IOS reserves the right to perform large block
- operations directly, bypassing the buffer. In such a case data was
- never buffered, so "rebuffering" has no meaning (i.e. there is no
- correspondence between the buffer and the physical stream).
-
- Single-bit I/O is able to write partial bytes ONLY IF the stream supports
- seeking. Also, line buffering is not well-defined in the context of
- single-bit I/O, so it might not do what you expect.
-
- implementation notes:
- in order to know where we are in a file, we must ensure the buffer
- is only populated from the underlying stream starting with p==buf.
-
- to switch from writing to reading: flush, set p=buf, cnt=0
- to switch from reading to writing: seek backwards cnt bytes, p=buf, cnt=0
-
- when writing: buf starts at curr. physical stream pos, p - buf is how
- many bytes we've written logically. cnt==0
-
- dirty == (bitpos>0 && state==iost_wr), EXCEPT right after switching from
- reading to writing, where we might be in the middle of a byte without
- having changed it.
-
- to write a bit: if !dirty, read up to maxsize-(p-buf) into buffer, then
- seek back by the same amount (undo it). write onto those bits. now set
- the dirty bit. in this state, we can bit-read up to the end of the byte,
- then formally switch to the read state using flush.
-
- design points:
- - data-source independence, including memory streams
- - expose buffer to user, allow user-owned buffers
- - allow direct I/O, don't always go through buffer
- - buffer-internal seeking. makes seeking back 1-2 bytes very fast,
- and makes it possible for sockets where it otherwise wouldn't be
- - tries to allow switching between reading and writing
- - support 64-bit and large files
- - efficient, low-latency buffering
- - special support for utf8
- - type-aware functions with byte-order swapping service
- - position counter for meaningful data offsets with sockets
-
- theory of operation:
-
- the buffer is a view of part of a file/stream. you can seek, read, and
- write around in it as much as you like, as if it were just a string.
-
- we keep track of the part of the buffer that's invalid (written to).
- we remember whether the position of the underlying stream is aligned
- with the end of the buffer (reading mode) or the beginning (writing mode).
-
- based on this info, we might have to seek back before doing a flush.
-
- as optimizations, we do no writing if the buffer isn't "dirty", and we
- do no reading if the data will only be overwritten.
-*/
--- a/llt/llt.h
+++ /dev/null
@@ -1,96 +1,0 @@
-#ifndef __LLT_H_
-#define __LLT_H_
-
-#include "platform.h"
-#include "utf8.h"
-#include "ios.h"
-#include "bitvector.h"
-
-#include "htableh.inc"
-HTPROT(ptrhash)
-
-#ifdef __GNUC__
-#define __unlikely(x) __builtin_expect(!!(x), 0)
-#define __likely(x) __builtin_expect(!!(x), 1)
-#else
-#define __unlikely(x) (x)
-#define __likely(x) (x)
-#endif
-
-#ifdef BOEHM_GC /* boehm GC allocator */
-#include <gc.h>
-#define LLT_ALLOC(n) GC_MALLOC(n)
-#define LLT_REALLOC(p, n) GC_REALLOC((p), (n))
-#define LLT_FREE(x) USED(x)
-#else /* standard allocator */
-#define LLT_ALLOC(n) malloc(n)
-#define LLT_REALLOC(p, n) realloc((p), (n))
-#define LLT_FREE(x) free(x)
-#endif
-
-#define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8)
-#define bswap_32(x) \
- ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
- (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
-#define bswap_64(x) \
- (uint64_t)bswap_32((x) & 0xffffffffULL)<<32 | \
- (uint64_t)bswap_32(((x)>>32) & 0xffffffffULL)
-
-#define DBL_MAXINT (1LL<<53)
-#define FLT_MAXINT (1<<24)
-#define BIT63 0x8000000000000000ULL
-#define BIT31 0x80000000UL
-
-#ifdef BITS64
-#define NBITS 64
-#define TOP_BIT BIT63
-typedef uint64_t lltuint_t;
-typedef int64_t lltint_t;
-#else
-#define NBITS 32
-#define TOP_BIT BIT31
-typedef uint32_t lltuint_t;
-typedef int32_t lltint_t;
-#endif
-
-#define LOG2_10 3.3219280948873626
-#define rel_zero(a, b) (fabs((a)/(b)) < DBL_EPSILON)
-#define LABS(n) (((n)^((n)>>(NBITS-1))) - ((n)>>(NBITS-1)))
-#define NBABS(n, nb) (((n)^((n)>>((nb)-1))) - ((n)>>((nb)-1)))
-#define LLT_ALIGN(x, sz) (((x) + (sz-1)) & (-sz))
-
-// a mask with n set lo or hi bits
-#define lomask(n) (uint32_t)((((uint32_t)1)<<(n))-1)
-
-extern double D_PNAN, D_NNAN, D_PINF, D_NINF;
-extern float F_PNAN, F_NNAN, F_PINF, F_NINF;
-
-/* timefuncs.c */
-uint64_t i64time(void);
-double clock_now(void);
-void timestring(double seconds, char *buffer, size_t len);
-double parsetime(const char *str);
-void sleep_ms(int ms);
-
-/* hashing.c */
-lltuint_t nextipow2(lltuint_t i);
-uint32_t int32hash(uint32_t a);
-uint64_t int64hash(uint64_t key);
-uint32_t int64to32hash(uint64_t key);
-uint64_t memhash(const char* buf, size_t n);
-uint32_t memhash32(const char* buf, size_t n);
-
-/* random.c */
-void randomize(void);
-double genrand_double(void);
-uint64_t genrand_uint64(void);
-uint32_t genrand_uint32(void);
-int64_t genrand_int63(void);
-
-/* utils.c */
-char *uint2str(char *dest, size_t len, uint64_t num, uint32_t base);
-int isdigit_base(char c, int base);
-
-void llt_init(void);
-
-#endif
--- a/llt/lltinit.c
+++ /dev/null
@@ -1,21 +1,0 @@
-#include "llt.h"
-
-double D_PNAN, D_NNAN, D_PINF, D_NINF;
-float F_PNAN, F_NNAN, F_PINF, F_NINF;
-
-void
-llt_init(void)
-{
- D_PNAN = strtod("+NaN", nil);
- D_NNAN = strtod("-NaN", nil);
- D_PINF = strtod("+Inf", nil);
- D_NINF = strtod("-Inf", nil);
-
- *(uint32_t*)&F_PNAN = 0x7fc00000;
- *(uint32_t*)&F_NNAN = 0xffc00000;
- *(uint32_t*)&F_PINF = 0x7f800000;
- *(uint32_t*)&F_NINF = 0xff800000;
-
- randomize();
- ios_init_stdstreams();
-}
--- a/llt/ptrhash.c
+++ /dev/null
@@ -1,39 +1,0 @@
-/*
- pointer hash table
- optimized for storing info about particular values
-*/
-
-#include "llt.h"
-
-#define OP_EQ(x, y) ((x) == (y))
-
-#ifdef BITS64
-static uint64_t
-_pinthash(uint64_t key)
-{
- key = (~key) + (key << 21); // key = (key << 21) - key - 1;
- key = key ^ (key >> 24);
- key = (key + (key << 3)) + (key << 8); // key * 265
- key = key ^ (key >> 14);
- key = (key + (key << 2)) + (key << 4); // key * 21
- key = key ^ (key >> 28);
- key = key + (key << 31);
- return key;
-}
-#else
-static uint32_t
-_pinthash(uint32_t a)
-{
- a = (a+0x7ed55d16) + (a<<12);
- a = (a^0xc761c23c) ^ (a>>19);
- a = (a+0x165667b1) + (a<<5);
- a = (a+0xd3a2646c) ^ (a<<9);
- a = (a+0xfd7046c5) + (a<<3);
- a = (a^0xb55a4f09) ^ (a>>16);
- return a;
-}
-#endif
-
-#include "htable.inc"
-
-HTIMPL(ptrhash, _pinthash, OP_EQ)
--- a/llt/random.c
+++ /dev/null
@@ -1,38 +1,0 @@
-/*
- random numbers
-*/
-#include "llt.h"
-#include "mt19937-64.h"
-
-static mt19937_64 ctx;
-
-uint64_t
-genrand_uint64(void)
-{
- return genrand64_int64(&ctx);
-}
-
-uint32_t
-genrand_uint32(void)
-{
- return genrand64_int64(&ctx) >> 32;
-}
-
-int64_t
-genrand_int63(void)
-{
- return genrand64_int63(&ctx);
-}
-
-double
-genrand_double(void)
-{
- return genrand64_real1(&ctx);
-}
-
-void
-randomize(void)
-{
- unsigned long long tm = i64time();
- init_by_array64(&ctx, &tm, 1);
-}
--- a/llt/timefuncs.c
+++ /dev/null
@@ -1,116 +1,0 @@
-#include "platform.h"
-
-#if defined(__plan9__)
-double
-floattime(void)
-{
- return (double)nsec() / 1.0e9;
-}
-#else
-double
-tv2float(struct timeval *tv)
-{
- return (double)tv->tv_sec + (double)tv->tv_usec/1.0e6;
-}
-
-double
-diff_time(struct timeval *tv1, struct timeval *tv2)
-{
- return tv2float(tv1) - tv2float(tv2);
-}
-#endif
-
-// return as many bits of system randomness as we can get our hands on
-uint64_t
-i64time(void)
-{
- uint64_t a;
-#if defined(__plan9__)
- a = nsec();
-#else
- struct timeval now;
- gettimeofday(&now, nil);
- a = (((uint64_t)now.tv_sec)<<32) + (uint64_t)now.tv_usec;
-#endif
-
- return a;
-}
-
-double
-clock_now(void)
-{
-#if defined(__plan9__)
- return floattime();
-#else
- struct timeval now;
- gettimeofday(&now, nil);
- return tv2float(&now);
-#endif
-}
-
-void
-timestring(double seconds, char *buffer, size_t len)
-{
-#if defined(__plan9__)
- Tm tm;
- snprint(buffer, len, "%τ", tmfmt(tmtime(&tm, seconds, tzload("local")), nil));
-#else
- time_t tme = (time_t)seconds;
-
- char *fmt = "%c"; /* needed to suppress GCC warning */
- struct tm tm;
-
- localtime_r(&tme, &tm);
- strftime(buffer, len, fmt, &tm);
-#endif
-}
-
-#if defined(__plan9__)
-double
-parsetime(const char *str)
-{
- Tm tm;
- if(tmparse(&tm, "?WWW, ?MM ?DD hh:mm:ss ?Z YYYY", str, tzload("local"), nil) == nil)
- return -1;
- return tmnorm(&tm);
-}
-#else
-double
-parsetime(const char *str)
-{
- char *fmt = "%c"; /* needed to suppress GCC warning */
- char *res;
- time_t t;
- struct tm tm;
-
- res = strptime(str, fmt, &tm);
- if(res != nil){
- /* Not set by strptime(); tells mktime() to determine
- * whether daylight saving time is in effect
- */
- tm.tm_isdst = -1;
- t = mktime(&tm);
- if(t == (time_t)-1)
- return -1;
- return (double)t;
- }
- return -1;
-}
-#endif
-
-void
-sleep_ms(int ms)
-{
- if(ms == 0)
- return;
-
-#if defined(__plan9__)
- sleep(ms);
-#else
- struct timeval timeout;
-
- timeout.tv_sec = ms/1000;
- timeout.tv_usec = (ms % 1000) * 1000;
- select(0, nil, nil, nil, &timeout);
-#endif
-}
--- a/llt/utf8.c
+++ /dev/null
@@ -1,707 +1,0 @@
-/*
- Basic UTF-8 manipulation routines
- by Jeff Bezanson
- placed in the public domain Fall 2005
-
- This code is designed to provide the utilities you need to manipulate
- UTF-8 as an internal string encoding. These functions do not perform the
- error checking normally needed when handling UTF-8 data, so if you happen
- to be from the Unicode Consortium you will want to flay me alive.
- I do this because error checking can be performed at the boundaries (I/O),
- with these routines reserved for higher performance on data known to be
- valid.
- A UTF-8 validation routine is included.
-*/
-
-#include "llt.h"
-
-static const uint32_t offsetsFromUTF8[6] = {
- 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL
-};
-
-static const char trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
-};
-
-// straight from musl
-int
-u8_iswprint(uint32_t c)
-{
- if(c < 0xff)
- return ((c+1) & 0x7f) >= 0x21;
- if(c < 0x2028 || c-0x202a < 0xd800-0x202a || c-0xe000 < 0xfff9-0xe000)
- return 1;
- return !(c-0xfffc > 0x10ffff-0xfffc || (c&0xfffe) == 0xfffe);
-}
-
-/* returns length of next utf-8 sequence */
-size_t
-u8_seqlen(const char *s)
-{
- return trailingBytesForUTF8[(unsigned int)(uint8_t)s[0]] + 1;
-}
-
-/* returns the # of bytes needed to encode a certain character
- 0 means the character cannot (or should not) be encoded. */
-size_t
-u8_charlen(uint32_t ch)
-{
- if(ch < 0x80)
- return 1;
- if(ch < 0x800)
- return 2;
- if(ch < 0x10000)
- return 3;
- if(ch < 0x110000)
- return 4;
- return 0;
-}
-
-size_t
-u8_codingsize(uint32_t *wcstr, size_t n)
-{
- size_t i, c = 0;
-
- for(i = 0; i < n; i++)
- c += u8_charlen(wcstr[i]);
- return c;
-}
-
-/* conversions without error checking
- only works for valid UTF-8, i.e. no 5- or 6-byte sequences
- srcsz = source size in bytes
- sz = dest size in # of wide characters
-
- returns # characters converted
- if sz == srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
-*/
-size_t
-u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz)
-{
- uint32_t ch;
- const char *src_end = src + srcsz;
- size_t nb, i = 0;
-
- if(sz == 0 || srcsz == 0)
- return 0;
-
- while(i < sz){
- if(!isutf(*src)){ // invalid sequence
- dest[i++] = 0xFFFD;
- src++;
- if(src >= src_end)
- break;
- continue;
- }
- nb = trailingBytesForUTF8[(uint8_t)*src];
- if(src + nb >= src_end)
- break;
- ch = 0;
- switch(nb){
- case 5: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
- case 4: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
- case 3: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
- case 2: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
- case 1: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
- case 0: ch += (uint8_t)*src++;
- }
- ch -= offsetsFromUTF8[nb];
- dest[i++] = ch;
- }
- return i;
-}
-
-/*
- * srcsz = number of source characters
- * sz = size of dest buffer in bytes
- * returns # bytes stored in dest
- * the destination string will never be bigger than the source string.
-*/
-size_t
-u8_toutf8(char *dest, size_t sz, const uint32_t *src, size_t srcsz)
-{
- uint32_t ch;
- size_t i = 0;
- char *dest0 = dest;
- char *dest_end = dest + sz;
-
- while(i < srcsz){
- ch = src[i];
- if(ch < 0x80){
- if(dest >= dest_end)
- break;
- *dest++ = (char)ch;
- }else if(ch < 0x800){
- if(dest >= dest_end-1)
- break;
- *dest++ = (ch>>6) | 0xC0;
- *dest++ = (ch & 0x3F) | 0x80;
- }else if(ch < 0x10000){
- if(dest >= dest_end-2)
- break;
- *dest++ = (ch>>12) | 0xE0;
- *dest++ = ((ch>>6) & 0x3F) | 0x80;
- *dest++ = (ch & 0x3F) | 0x80;
- }else if(ch < 0x110000){
- if(dest >= dest_end-3)
- break;
- *dest++ = (ch>>18) | 0xF0;
- *dest++ = ((ch>>12) & 0x3F) | 0x80;
- *dest++ = ((ch>>6) & 0x3F) | 0x80;
- *dest++ = (ch & 0x3F) | 0x80;
- }
- i++;
- }
- return dest-dest0;
-}
-
-size_t
-u8_wc_toutf8(char *dest, uint32_t ch)
-{
- if(ch < 0x80){
- dest[0] = (char)ch;
- return 1;
- }
- if(ch < 0x800){
- dest[0] = (ch>>6) | 0xC0;
- dest[1] = (ch & 0x3F) | 0x80;
- return 2;
- }
- if(ch < 0x10000){
- dest[0] = (ch>>12) | 0xE0;
- dest[1] = ((ch>>6) & 0x3F) | 0x80;
- dest[2] = (ch & 0x3F) | 0x80;
- return 3;
- }
- if(ch < 0x110000){
- dest[0] = (ch>>18) | 0xF0;
- dest[1] = ((ch>>12) & 0x3F) | 0x80;
- dest[2] = ((ch>>6) & 0x3F) | 0x80;
- dest[3] = (ch & 0x3F) | 0x80;
- return 4;
- }
- return 0;
-}
-
-/* charnum => byte offset */
-size_t
-u8_offset(const char *s, size_t charnum)
-{
- size_t i = 0;
-
- while(charnum > 0){
- if(s[i++] & 0x80)
- (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
- charnum--;
- }
- return i;
-}
-
-/* byte offset => charnum */
-size_t
-u8_charnum(const char *s, size_t offset)
-{
- size_t charnum = 0, i = 0;
-
- while(i < offset){
- if((s[i++] & 0x80) != 0 && !isutf(s[++i]) && !isutf(s[++i]))
- i++;
- charnum++;
- }
- return charnum;
-}
-
-/* number of characters in NUL-terminated string */
-size_t
-u8_strlen(const char *s)
-{
- size_t count = 0;
- size_t i = 0, lasti;
-
- while(1) {
- lasti = i;
- while(s[i] > 0)
- i++;
- count += (i-lasti);
- if(s[i++] == 0)
- break;
- (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
- count++;
- }
- return count;
-}
-
-size_t
-u8_strwidth(const char *s)
-{
- uint32_t ch;
- size_t nb, tot = 0;
- int w;
- signed char sc;
-
- while((sc = (signed char)*s) != 0){
- if(sc >= 0){
- s++;
- if(sc)
- tot++;
- }else{
- if(!isutf(sc)){
- tot++;
- s++;
- continue;
- }
- nb = trailingBytesForUTF8[(uint8_t)sc];
- ch = 0;
- switch(nb){
- case 5: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
- case 4: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
- case 3: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
- case 2: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
- case 1: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
- case 0: ch += (uint8_t)*s++;
- }
- ch -= offsetsFromUTF8[nb];
- w = wcwidth(ch); // might return -1
- if(w > 0)
- tot += w;
- }
- }
- return tot;
-}
-
-/* reads the next utf-8 sequence out of a string, updating an index */
-uint32_t
-u8_nextchar(const char *s, size_t *i)
-{
- uint32_t ch = 0;
- size_t sz = 0;
-
- do{
- ch <<= 6;
- ch += (uint8_t)s[(*i)];
- sz++;
- }while(s[*i] && (++(*i)) && !isutf(s[*i]));
- return ch - offsetsFromUTF8[sz-1];
-}
-
-/* next character without NUL character terminator */
-uint32_t
-u8_nextmemchar(const char *s, size_t *i)
-{
- uint32_t ch = 0;
- size_t sz = 0;
-
- do{
- ch <<= 6;
- ch += (uint8_t)s[(*i)++];
- sz++;
- }while(!isutf(s[*i]));
- return ch - offsetsFromUTF8[sz-1];
-}
-
-void
-u8_inc(const char *s, size_t *i)
-{
- (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || isutf(s[++(*i)]) || ++(*i));
-}
-
-void
-u8_dec(const char *s, size_t *i)
-{
- (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i));
-}
-
-int
-octal_digit(char c)
-{
- return (c >= '0' && c <= '7');
-}
-
-int
-hex_digit(char c)
-{
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-char
-read_escape_control_char(char c)
-{
- switch(c){
- case 'n': return '\n';
- case 't': return '\t';
- case 'a': return '\a';
- case 'b': return '\b';
- case 'e': return '\e';
- case 'f': return '\f';
- case 'r': return '\r';
- case 'v': return '\v';
- }
- return c;
-}
-
-/* assumes that src points to the character after a backslash
- returns number of input characters processed, 0 if error */
-size_t
-u8_read_escape_sequence(const char *str, size_t ssz, uint32_t *dest)
-{
- assert(ssz > 0);
- uint32_t ch;
- char digs[10];
- int dno = 0, ndig;
- size_t i = 1;
- char c0 = str[0];
-
- if(octal_digit(c0)){
- i = 0;
- do{
- digs[dno++] = str[i++];
- }while(i < ssz && octal_digit(str[i]) && dno < 3);
- digs[dno] = '\0';
- ch = strtol(digs, nil, 8);
- }else if((c0 == 'x' && (ndig = 2)) || (c0 == 'u' && (ndig = 4)) || (c0 == 'U' && (ndig = 8))){
- while(i<ssz && hex_digit(str[i]) && dno < ndig)
- digs[dno++] = str[i++];
- if(dno == 0)
- return 0;
- digs[dno] = '\0';
- ch = strtol(digs, nil, 16);
- }else{
- ch = (uint32_t)read_escape_control_char(c0);
- }
- *dest = ch;
-
- return i;
-}
-
-/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
- example: u8_unescape(mybuf, 256, "hello\\u220e")
- note the double backslash is needed if called on a C string literal */
-size_t
-u8_unescape(char *buf, size_t sz, const char *src)
-{
- size_t c = 0, amt;
- uint32_t ch;
- char temp[4];
-
- while(*src && c < sz){
- if(*src == '\\'){
- src++;
- amt = u8_read_escape_sequence(src, 1000, &ch);
- }else{
- ch = (uint32_t)*src;
- amt = 1;
- }
- src += amt;
- amt = u8_wc_toutf8(temp, ch);
- if(amt > sz-c)
- break;
- memmove(&buf[c], temp, amt);
- c += amt;
- }
- if(c < sz)
- buf[c] = '\0';
- return c;
-}
-
-static inline int
-buf_put2c(char *buf, const char *src)
-{
- buf[0] = src[0];
- buf[1] = src[1];
- buf[2] = '\0';
- return 2;
-}
-
-int
-u8_escape_wchar(char *buf, size_t sz, uint32_t ch)
-{
- assert(sz > 2);
- if(ch >= 0x20 && ch < 0x7f){
- buf[0] = ch;
- buf[1] = '\0';
- return 1;
- }
- if(ch > 0xffff)
- return snprintf(buf, sz, "\\U%.8x", ch);
- if(ch >= 0x80)
- return snprintf(buf, sz, "\\u%04x", ch);
- switch(ch){
- case '\n': return buf_put2c(buf, "\\n");
- case '\t': return buf_put2c(buf, "\\t");
- case '\\': return buf_put2c(buf, "\\\\");
- case '\a': return buf_put2c(buf, "\\a");
- case '\b': return buf_put2c(buf, "\\b");
- case '\e': return buf_put2c(buf, "\\e");
- case '\f': return buf_put2c(buf, "\\f");
- case '\r': return buf_put2c(buf, "\\r");
- case '\v': return buf_put2c(buf, "\\v");
- }
- return snprintf(buf, sz, "\\x%02x", ch);
-}
-
-size_t
-u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, int escape_quotes, int ascii)
-{
- size_t i = *pi, i0;
- uint32_t ch;
- char *start = buf;
- char *blim = start + sz-11;
- assert(sz > 11);
-
- while(i < end && buf < blim){
- // sz-11: leaves room for longest escape sequence
- if(escape_quotes && src[i] == '"'){
- buf += buf_put2c(buf, "\\\"");
- i++;
- }else if(src[i] == '\\'){
- buf += buf_put2c(buf, "\\\\");
- i++;
- }else{
- i0 = i;
- ch = u8_nextmemchar(src, &i);
- if(ascii || !u8_iswprint(ch)){
- buf += u8_escape_wchar(buf, sz - (buf-start), ch);
- }else{
- i = i0;
- do{
- *buf++ = src[i++];
- }while(!isutf(src[i]));
- }
- }
- }
- *buf++ = '\0';
- *pi = i;
- return (buf-start);
-}
-
-char *
-u8_strchr(const char *s, uint32_t ch, size_t *charn)
-{
- size_t i = 0, lasti = 0;
- uint32_t c;
-
- *charn = 0;
- while(s[i]){
- c = u8_nextchar(s, &i);
- if(c == ch){
- /* it's const for us, but not necessarily the caller */
- return (char*)&s[lasti];
- }
- lasti = i;
- (*charn)++;
- }
- return nil;
-}
-
-char *
-u8_memchr(const char *s, uint32_t ch, size_t sz, size_t *charn)
-{
- size_t i = 0, lasti = 0;
- uint32_t c;
- int csz;
-
- *charn = 0;
- while(i < sz){
- c = csz = 0;
- do{
- c <<= 6;
- c += (uint8_t)s[i++];
- csz++;
- }while(i < sz && !isutf(s[i]));
- c -= offsetsFromUTF8[csz-1];
-
- if(c == ch)
- return (char*)&s[lasti];
- lasti = i;
- (*charn)++;
- }
- return nil;
-}
-
-char *
-u8_memrchr(const char *s, uint32_t ch, size_t sz)
-{
- size_t i = sz-1, tempi = 0;
- uint32_t c;
-
- if(sz == 0)
- return nil;
-
- while(i && !isutf(s[i]))
- i--;
-
- while(1){
- tempi = i;
- c = u8_nextmemchar(s, &tempi);
- if(c == ch)
- return (char*)&s[i];
- if(i == 0)
- break;
- tempi = i;
- u8_dec(s, &i);
- if(i > tempi)
- break;
- }
- return nil;
-}
-
-size_t
-u8_vprintf(const char *fmt, va_list ap)
-{
- size_t cnt, sz, nc, needfree = 0;
- char *buf, tmp[512];
- uint32_t *wcs;
-
- sz = 512;
- buf = tmp;
- cnt = vsnprintf(buf, sz, fmt, ap);
- if((ssize_t)cnt < 0)
- return 0;
- if(cnt >= sz){
- buf = (char*)malloc(cnt + 1);
- needfree = 1;
- vsnprintf(buf, cnt+1, fmt, ap);
- }
- wcs = (uint32_t*)malloc((cnt+1) * sizeof(uint32_t));
- nc = u8_toucs(wcs, cnt+1, buf, cnt);
- wcs[nc] = 0;
-#if defined(__plan9__)
- print("%S", (Rune*)wcs);
-#else
- printf("%ls", (wchar_t*)wcs);
-#endif
- free(wcs);
- if(needfree)
- free(buf);
- return nc;
-}
-
-size_t
-u8_printf(const char *fmt, ...)
-{
- size_t cnt;
- va_list args;
-
- va_start(args, fmt);
- cnt = u8_vprintf(fmt, args);
-
- va_end(args);
- return cnt;
-}
-
-/* based on the valid_utf8 routine from the PCRE library by Philip Hazel
-
- length is in bytes, since without knowing whether the string is valid
- it's hard to know how many characters there are! */
-int
-u8_isvalid(const char *str, int length)
-{
- const uint8_t *p, *pend = (uint8_t*)str + length;
- uint8_t c;
- int ab;
-
- for(p = (uint8_t*)str; p < pend; p++){
- c = *p;
- if(c < 128)
- continue;
- if((c & 0xc0) != 0xc0)
- return 0;
- ab = trailingBytesForUTF8[c];
- if(length < ab)
- return 0;
- length -= ab;
-
- p++;
- /* Check top bits in the second byte */
- if((*p & 0xc0) != 0x80)
- return 0;
-
- /* Check for overlong sequences for each different length */
- switch(ab){
- /* Check for xx00 000x */
- case 1:
- if((c & 0x3e) == 0)
- return 0;
- continue; /* We know there aren't any more bytes to check */
-
- /* Check for 1110 0000, xx0x xxxx */
- case 2:
- if(c == 0xe0 && (*p & 0x20) == 0)
- return 0;
- break;
-
- /* Check for 1111 0000, xx00 xxxx */
- case 3:
- if(c == 0xf0 && (*p & 0x30) == 0)
- return 0;
- break;
-
- /* Check for 1111 1000, xx00 0xxx */
- case 4:
- if(c == 0xf8 && (*p & 0x38) == 0)
- return 0;
- break;
-
- /* Check for leading 0xfe or 0xff and then for 1111 1100, xx00 00xx */
- case 5:
- if(c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0))
- return 0;
- break;
- }
-
- /* Check for valid bytes after the 2nd, if any; all must start 10 */
- while(--ab > 0)
- if((*(++p) & 0xc0) != 0x80)
- return 0;
- }
-
- return 1;
-}
-
-int
-u8_reverse(char *dest, char * src, size_t len)
-{
- size_t si = 0, di = len;
- uint8_t c;
-
- dest[di] = '\0';
- while(si < len){
- c = (uint8_t)src[si];
- if((~c) & 0x80){
- di--;
- dest[di] = c;
- si++;
- }else{
- switch(c>>4){
- case 0xc:
- case 0xd:
- di -= 2;
- *((int16_t*)&dest[di]) = *((int16_t*)&src[si]);
- si += 2;
- break;
- case 0xe:
- di -= 3;
- dest[di] = src[si];
- *((int16_t*)&dest[di+1]) = *((int16_t*)&src[si+1]);
- si += 3;
- break;
- case 0xf:
- di -= 4;
- *((int32_t*)&dest[di]) = *((int32_t*)&src[si]);
- si += 4;
- break;
- default:
- return 1;
- }
- }
- }
- return 0;
-}
--- a/llt/utf8.h
+++ /dev/null
@@ -1,111 +1,0 @@
-#ifndef __UTF8_H_
-#define __UTF8_H_
-
-/* is c the start of a utf8 sequence? */
-#define isutf(c) (((c)&0xC0) != 0x80)
-
-int u8_iswprint(uint32_t c);
-
-/* convert UTF-8 data to wide character */
-size_t u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz);
-
-/* the opposite conversion */
-size_t u8_toutf8(char *dest, size_t sz, const uint32_t *src, size_t srcsz);
-
-/* single character to UTF-8, returns # bytes written */
-size_t u8_wc_toutf8(char *dest, uint32_t ch);
-
-/* character number to byte offset */
-size_t u8_offset(const char *str, size_t charnum);
-
-/* byte offset to character number */
-size_t u8_charnum(const char *s, size_t offset);
-
-/* return next character, updating an index variable */
-uint32_t u8_nextchar(const char *s, size_t *i);
-
-/* next character without NUL character terminator */
-uint32_t u8_nextmemchar(const char *s, size_t *i);
-
-/* move to next character */
-void u8_inc(const char *s, size_t *i);
-
-/* move to previous character */
-void u8_dec(const char *s, size_t *i);
-
-/* returns length of next utf-8 sequence */
-size_t u8_seqlen(const char *s);
-
-/* returns the # of bytes needed to encode a certain character */
-size_t u8_charlen(uint32_t ch);
-
-/* computes the # of bytes needed to encode a WC string as UTF-8 */
-size_t u8_codingsize(uint32_t *wcstr, size_t n);
-
-char read_escape_control_char(char c);
-
-/* assuming src points to the character after a backslash, read an
- escape sequence, storing the result in dest and returning the number of
- input characters processed */
-size_t u8_read_escape_sequence(const char *src, size_t ssz, uint32_t *dest);
-
-/* given a wide character, convert it to an ASCII escape sequence stored in
- buf, where buf is "sz" bytes. returns the number of characters output.
- sz must be at least 3. */
-int u8_escape_wchar(char *buf, size_t sz, uint32_t ch);
-
-/* convert a string "src" containing escape sequences to UTF-8 */
-size_t u8_unescape(char *buf, size_t sz, const char *src);
-
-/* convert UTF-8 "src" to escape sequences.
-
- sz is buf size in bytes. must be at least 12.
-
- if escape_quotes is nonzero, quote characters will be escaped.
-
- if ascii is nonzero, the output is 7-bit ASCII, no UTF-8 survives.
-
- starts at src[*pi], updates *pi to point to the first unprocessed
- byte of the input.
-
- end is one more than the last allowable value of *pi.
-
- returns number of bytes placed in buf, including a NUL terminator.
-*/
-size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end,
- int escape_quotes, int ascii);
-
-/* utility predicates used by the above */
-int octal_digit(char c);
-int hex_digit(char c);
-
-/* return a pointer to the first occurrence of ch in s, or nil if not
- found. character index of found character returned in *charn. */
-char *u8_strchr(const char *s, uint32_t ch, size_t *charn);
-
-/* same as the above, but searches a buffer of a given size instead of
- a NUL-terminated string. */
-char *u8_memchr(const char *s, uint32_t ch, size_t sz, size_t *charn);
-
-char *u8_memrchr(const char *s, uint32_t ch, size_t sz);
-
-/* count the number of characters in a UTF-8 string */
-size_t u8_strlen(const char *s);
-
-/* number of columns occupied by a string */
-size_t u8_strwidth(const char *s);
-
-/* printf where the format string and arguments may be in UTF-8.
- you can avoid this function and just use ordinary printf() if the current
- locale is UTF-8. */
-size_t u8_vprintf(const char *fmt, va_list ap);
-size_t u8_printf(const char *fmt, ...);
-
-/* determine whether a sequence of bytes is valid UTF-8. length is in bytes */
-int u8_isvalid(const char *str, int length);
-
-/* reverse a UTF-8 string. len is length in bytes. dest and src must both
- be allocated to at least len+1 bytes. returns 1 for error, 0 otherwise */
-int u8_reverse(char *dest, char *src, size_t len);
-
-#endif
--- a/mkfile
+++ b/mkfile
@@ -2,7 +2,7 @@
BIN=/$objtype/bin
TARG=flisp
-CFLAGS=$CFLAGS -p -D__plan9__ -D__${objtype}__ -I3rd -Illt -Iplan9
+CFLAGS=$CFLAGS -p -D__plan9__ -D__${objtype}__ -I3rd -Iplan9
CLEANFILES=boot.h builtin_fns.h
HFILES=\
@@ -9,15 +9,6 @@
equalhash.h\
flisp.h\
-#CHFILES=\
-# cvalues.c\
-# equal.c\
-# operators.c\
-# print.c\
-# read.c\
-# types.c\
-
-
OFILES=\
builtins.$O\
equalhash.$O\
@@ -32,20 +23,19 @@
print.$O\
equal.$O\
types.$O\
+ bitvector-ops.$O\
+ bitvector.$O\
+ dump.$O\
+ hashing.$O\
+ htable.$O\
+ ios.$O\
+ llt.$O\
+ ptrhash.$O\
+ random.$O\
+ timefuncs.$O\
+ utf8.$O\
3rd/mt19937-64.$O\
3rd/wcwidth.$O\
- llt/bitvector-ops.$O\
- llt/bitvector.$O\
- llt/dump.$O\
- llt/hashing.$O\
- llt/htable.$O\
- llt/int2str.$O\
- llt/ios.$O\
- llt/lltinit.$O\
- llt/ptrhash.$O\
- llt/random.$O\
- llt/timefuncs.$O\
- llt/utf8.$O\
default:V: all
@@ -57,9 +47,7 @@
builtin_fns.h:
sed -n 's/^BUILTIN[_]?(\(".*)/BUILTIN_FN\1/gp' *.c >$target
-flmain.$O: boot.h
-
-#flisp.$O: maxstack.inc opcodes.h builtin_fns.h $CHFILES
+flmain.$O: boot.h builtin_fns.h
flisp.$O: maxstack.inc opcodes.h builtin_fns.h
%.$O: %.c
--- /dev/null
+++ b/ptrhash.c
@@ -1,0 +1,39 @@
+/*
+ pointer hash table
+ optimized for storing info about particular values
+*/
+
+#include "llt.h"
+
+#define OP_EQ(x, y) ((x) == (y))
+
+#ifdef BITS64
+static uint64_t
+_pinthash(uint64_t key)
+{
+ key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+ key = key ^ (key >> 24);
+ key = (key + (key << 3)) + (key << 8); // key * 265
+ key = key ^ (key >> 14);
+ key = (key + (key << 2)) + (key << 4); // key * 21
+ key = key ^ (key >> 28);
+ key = key + (key << 31);
+ return key;
+}
+#else
+static uint32_t
+_pinthash(uint32_t a)
+{
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+ return a;
+}
+#endif
+
+#include "htable.inc"
+
+HTIMPL(ptrhash, _pinthash, OP_EQ)
--- /dev/null
+++ b/random.c
@@ -1,0 +1,39 @@
+/*
+ random numbers
+*/
+#include "llt.h"
+#include "mt19937-64.h"
+#include "timefuncs.h"
+
+static mt19937_64 ctx;
+
+uint64_t
+genrand_uint64(void)
+{
+ return genrand64_int64(&ctx);
+}
+
+uint32_t
+genrand_uint32(void)
+{
+ return genrand64_int64(&ctx) >> 32;
+}
+
+int64_t
+genrand_int63(void)
+{
+ return genrand64_int63(&ctx);
+}
+
+double
+genrand_double(void)
+{
+ return genrand64_real1(&ctx);
+}
+
+void
+randomize(void)
+{
+ unsigned long long tm = i64time();
+ init_by_array64(&ctx, &tm, 1);
+}
--- /dev/null
+++ b/random.h
@@ -1,0 +1,10 @@
+#ifndef RANDOM_H_
+#define RANDOM_H_
+
+void randomize(void);
+double genrand_double(void);
+uint64_t genrand_uint64(void);
+uint32_t genrand_uint32(void);
+int64_t genrand_int63(void);
+
+#endif
--- /dev/null
+++ b/timefuncs.c
@@ -1,0 +1,116 @@
+#include "platform.h"
+
+#if defined(__plan9__)
+double
+floattime(void)
+{
+ return (double)nsec() / 1.0e9;
+}
+#else
+double
+tv2float(struct timeval *tv)
+{
+ return (double)tv->tv_sec + (double)tv->tv_usec/1.0e6;
+}
+
+double
+diff_time(struct timeval *tv1, struct timeval *tv2)
+{
+ return tv2float(tv1) - tv2float(tv2);
+}
+#endif
+
+// return as many bits of system randomness as we can get our hands on
+uint64_t
+i64time(void)
+{
+ uint64_t a;
+#if defined(__plan9__)
+ a = nsec();
+#else
+ struct timeval now;
+ gettimeofday(&now, nil);
+ a = (((uint64_t)now.tv_sec)<<32) + (uint64_t)now.tv_usec;
+#endif
+
+ return a;
+}
+
+double
+clock_now(void)
+{
+#if defined(__plan9__)
+ return floattime();
+#else
+ struct timeval now;
+ gettimeofday(&now, nil);
+ return tv2float(&now);
+#endif
+}
+
+void
+timestring(double seconds, char *buffer, size_t len)
+{
+#if defined(__plan9__)
+ Tm tm;
+ snprint(buffer, len, "%τ", tmfmt(tmtime(&tm, seconds, tzload("local")), nil));
+#else
+ time_t tme = (time_t)seconds;
+
+ char *fmt = "%c"; /* needed to suppress GCC warning */
+ struct tm tm;
+
+ localtime_r(&tme, &tm);
+ strftime(buffer, len, fmt, &tm);
+#endif
+}
+
+#if defined(__plan9__)
+double
+parsetime(const char *str)
+{
+ Tm tm;
+ if(tmparse(&tm, "?WWW, ?MM ?DD hh:mm:ss ?Z YYYY", str, tzload("local"), nil) == nil)
+ return -1;
+ return tmnorm(&tm);
+}
+#else
+double
+parsetime(const char *str)
+{
+ char *fmt = "%c"; /* needed to suppress GCC warning */
+ char *res;
+ time_t t;
+ struct tm tm;
+
+ res = strptime(str, fmt, &tm);
+ if(res != nil){
+ /* Not set by strptime(); tells mktime() to determine
+ * whether daylight saving time is in effect
+ */
+ tm.tm_isdst = -1;
+ t = mktime(&tm);
+ if(t == (time_t)-1)
+ return -1;
+ return (double)t;
+ }
+ return -1;
+}
+#endif
+
+void
+sleep_ms(int ms)
+{
+ if(ms == 0)
+ return;
+
+#if defined(__plan9__)
+ sleep(ms);
+#else
+ struct timeval timeout;
+
+ timeout.tv_sec = ms/1000;
+ timeout.tv_usec = (ms % 1000) * 1000;
+ select(0, nil, nil, nil, &timeout);
+#endif
+}
--- /dev/null
+++ b/timefuncs.h
@@ -1,0 +1,10 @@
+#ifndef TIMEFUNCS_H_
+#define TIMEFUNCS_H_
+
+uint64_t i64time(void);
+double clock_now(void);
+void timestring(double seconds, char *buffer, size_t len);
+double parsetime(const char *str);
+void sleep_ms(int ms);
+
+#endif
--- /dev/null
+++ b/utf8.c
@@ -1,0 +1,707 @@
+/*
+ Basic UTF-8 manipulation routines
+ by Jeff Bezanson
+ placed in the public domain Fall 2005
+
+ This code is designed to provide the utilities you need to manipulate
+ UTF-8 as an internal string encoding. These functions do not perform the
+ error checking normally needed when handling UTF-8 data, so if you happen
+ to be from the Unicode Consortium you will want to flay me alive.
+ I do this because error checking can be performed at the boundaries (I/O),
+ with these routines reserved for higher performance on data known to be
+ valid.
+ A UTF-8 validation routine is included.
+*/
+
+#include "llt.h"
+
+static const uint32_t offsetsFromUTF8[6] = {
+ 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+};
+
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+// straight from musl
+int
+u8_iswprint(uint32_t c)
+{
+ if(c < 0xff)
+ return ((c+1) & 0x7f) >= 0x21;
+ if(c < 0x2028 || c-0x202a < 0xd800-0x202a || c-0xe000 < 0xfff9-0xe000)
+ return 1;
+ return !(c-0xfffc > 0x10ffff-0xfffc || (c&0xfffe) == 0xfffe);
+}
+
+/* returns length of next utf-8 sequence */
+size_t
+u8_seqlen(const char *s)
+{
+ return trailingBytesForUTF8[(unsigned int)(uint8_t)s[0]] + 1;
+}
+
+/* returns the # of bytes needed to encode a certain character
+ 0 means the character cannot (or should not) be encoded. */
+size_t
+u8_charlen(uint32_t ch)
+{
+ if(ch < 0x80)
+ return 1;
+ if(ch < 0x800)
+ return 2;
+ if(ch < 0x10000)
+ return 3;
+ if(ch < 0x110000)
+ return 4;
+ return 0;
+}
+
+size_t
+u8_codingsize(uint32_t *wcstr, size_t n)
+{
+ size_t i, c = 0;
+
+ for(i = 0; i < n; i++)
+ c += u8_charlen(wcstr[i]);
+ return c;
+}
+
+/* conversions without error checking
+ only works for valid UTF-8, i.e. no 5- or 6-byte sequences
+ srcsz = source size in bytes
+ sz = dest size in # of wide characters
+
+ returns # characters converted
+ if sz == srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
+*/
+size_t
+u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz)
+{
+ uint32_t ch;
+ const char *src_end = src + srcsz;
+ size_t nb, i = 0;
+
+ if(sz == 0 || srcsz == 0)
+ return 0;
+
+ while(i < sz){
+ if(!isutf(*src)){ // invalid sequence
+ dest[i++] = 0xFFFD;
+ src++;
+ if(src >= src_end)
+ break;
+ continue;
+ }
+ nb = trailingBytesForUTF8[(uint8_t)*src];
+ if(src + nb >= src_end)
+ break;
+ ch = 0;
+ switch(nb){
+ case 5: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
+ case 4: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
+ case 3: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
+ case 2: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
+ case 1: ch += (uint8_t)*src++; ch <<= 6; // fallthrough
+ case 0: ch += (uint8_t)*src++;
+ }
+ ch -= offsetsFromUTF8[nb];
+ dest[i++] = ch;
+ }
+ return i;
+}
+
+/*
+ * srcsz = number of source characters
+ * sz = size of dest buffer in bytes
+ * returns # bytes stored in dest
+ * the destination string will never be bigger than the source string.
+*/
+size_t
+u8_toutf8(char *dest, size_t sz, const uint32_t *src, size_t srcsz)
+{
+ uint32_t ch;
+ size_t i = 0;
+ char *dest0 = dest;
+ char *dest_end = dest + sz;
+
+ while(i < srcsz){
+ ch = src[i];
+ if(ch < 0x80){
+ if(dest >= dest_end)
+ break;
+ *dest++ = (char)ch;
+ }else if(ch < 0x800){
+ if(dest >= dest_end-1)
+ break;
+ *dest++ = (ch>>6) | 0xC0;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }else if(ch < 0x10000){
+ if(dest >= dest_end-2)
+ break;
+ *dest++ = (ch>>12) | 0xE0;
+ *dest++ = ((ch>>6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }else if(ch < 0x110000){
+ if(dest >= dest_end-3)
+ break;
+ *dest++ = (ch>>18) | 0xF0;
+ *dest++ = ((ch>>12) & 0x3F) | 0x80;
+ *dest++ = ((ch>>6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }
+ i++;
+ }
+ return dest-dest0;
+}
+
+size_t
+u8_wc_toutf8(char *dest, uint32_t ch)
+{
+ if(ch < 0x80){
+ dest[0] = (char)ch;
+ return 1;
+ }
+ if(ch < 0x800){
+ dest[0] = (ch>>6) | 0xC0;
+ dest[1] = (ch & 0x3F) | 0x80;
+ return 2;
+ }
+ if(ch < 0x10000){
+ dest[0] = (ch>>12) | 0xE0;
+ dest[1] = ((ch>>6) & 0x3F) | 0x80;
+ dest[2] = (ch & 0x3F) | 0x80;
+ return 3;
+ }
+ if(ch < 0x110000){
+ dest[0] = (ch>>18) | 0xF0;
+ dest[1] = ((ch>>12) & 0x3F) | 0x80;
+ dest[2] = ((ch>>6) & 0x3F) | 0x80;
+ dest[3] = (ch & 0x3F) | 0x80;
+ return 4;
+ }
+ return 0;
+}
+
+/* charnum => byte offset */
+size_t
+u8_offset(const char *s, size_t charnum)
+{
+ size_t i = 0;
+
+ while(charnum > 0){
+ if(s[i++] & 0x80)
+ (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
+ charnum--;
+ }
+ return i;
+}
+
+/* byte offset => charnum */
+size_t
+u8_charnum(const char *s, size_t offset)
+{
+ size_t charnum = 0, i = 0;
+
+ while(i < offset){
+ if((s[i++] & 0x80) != 0 && !isutf(s[++i]) && !isutf(s[++i]))
+ i++;
+ charnum++;
+ }
+ return charnum;
+}
+
+/* number of characters in NUL-terminated string */
+size_t
+u8_strlen(const char *s)
+{
+ size_t count = 0;
+ size_t i = 0, lasti;
+
+ while(1) {
+ lasti = i;
+ while(s[i] > 0)
+ i++;
+ count += (i-lasti);
+ if(s[i++] == 0)
+ break;
+ (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
+ count++;
+ }
+ return count;
+}
+
+size_t
+u8_strwidth(const char *s)
+{
+ uint32_t ch;
+ size_t nb, tot = 0;
+ int w;
+ signed char sc;
+
+ while((sc = (signed char)*s) != 0){
+ if(sc >= 0){
+ s++;
+ if(sc)
+ tot++;
+ }else{
+ if(!isutf(sc)){
+ tot++;
+ s++;
+ continue;
+ }
+ nb = trailingBytesForUTF8[(uint8_t)sc];
+ ch = 0;
+ switch(nb){
+ case 5: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
+ case 4: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
+ case 3: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
+ case 2: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
+ case 1: ch += (uint8_t)*s++; ch <<= 6; // fallthrough
+ case 0: ch += (uint8_t)*s++;
+ }
+ ch -= offsetsFromUTF8[nb];
+ w = wcwidth(ch); // might return -1
+ if(w > 0)
+ tot += w;
+ }
+ }
+ return tot;
+}
+
+/* reads the next utf-8 sequence out of a string, updating an index */
+uint32_t
+u8_nextchar(const char *s, size_t *i)
+{
+ uint32_t ch = 0;
+ size_t sz = 0;
+
+ do{
+ ch <<= 6;
+ ch += (uint8_t)s[(*i)];
+ sz++;
+ }while(s[*i] && (++(*i)) && !isutf(s[*i]));
+ return ch - offsetsFromUTF8[sz-1];
+}
+
+/* next character without NUL character terminator */
+uint32_t
+u8_nextmemchar(const char *s, size_t *i)
+{
+ uint32_t ch = 0;
+ size_t sz = 0;
+
+ do{
+ ch <<= 6;
+ ch += (uint8_t)s[(*i)++];
+ sz++;
+ }while(!isutf(s[*i]));
+ return ch - offsetsFromUTF8[sz-1];
+}
+
+void
+u8_inc(const char *s, size_t *i)
+{
+ (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || isutf(s[++(*i)]) || ++(*i));
+}
+
+void
+u8_dec(const char *s, size_t *i)
+{
+ (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i));
+}
+
+int
+octal_digit(char c)
+{
+ return (c >= '0' && c <= '7');
+}
+
+int
+hex_digit(char c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+char
+read_escape_control_char(char c)
+{
+ switch(c){
+ case 'n': return '\n';
+ case 't': return '\t';
+ case 'a': return '\a';
+ case 'b': return '\b';
+ case 'e': return '\e';
+ case 'f': return '\f';
+ case 'r': return '\r';
+ case 'v': return '\v';
+ }
+ return c;
+}
+
+/* assumes that src points to the character after a backslash
+ returns number of input characters processed, 0 if error */
+size_t
+u8_read_escape_sequence(const char *str, size_t ssz, uint32_t *dest)
+{
+ assert(ssz > 0);
+ uint32_t ch;
+ char digs[10];
+ int dno = 0, ndig;
+ size_t i = 1;
+ char c0 = str[0];
+
+ if(octal_digit(c0)){
+ i = 0;
+ do{
+ digs[dno++] = str[i++];
+ }while(i < ssz && octal_digit(str[i]) && dno < 3);
+ digs[dno] = '\0';
+ ch = strtol(digs, nil, 8);
+ }else if((c0 == 'x' && (ndig = 2)) || (c0 == 'u' && (ndig = 4)) || (c0 == 'U' && (ndig = 8))){
+ while(i<ssz && hex_digit(str[i]) && dno < ndig)
+ digs[dno++] = str[i++];
+ if(dno == 0)
+ return 0;
+ digs[dno] = '\0';
+ ch = strtol(digs, nil, 16);
+ }else{
+ ch = (uint32_t)read_escape_control_char(c0);
+ }
+ *dest = ch;
+
+ return i;
+}
+
+/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
+ example: u8_unescape(mybuf, 256, "hello\\u220e")
+ note the double backslash is needed if called on a C string literal */
+size_t
+u8_unescape(char *buf, size_t sz, const char *src)
+{
+ size_t c = 0, amt;
+ uint32_t ch;
+ char temp[4];
+
+ while(*src && c < sz){
+ if(*src == '\\'){
+ src++;
+ amt = u8_read_escape_sequence(src, 1000, &ch);
+ }else{
+ ch = (uint32_t)*src;
+ amt = 1;
+ }
+ src += amt;
+ amt = u8_wc_toutf8(temp, ch);
+ if(amt > sz-c)
+ break;
+ memmove(&buf[c], temp, amt);
+ c += amt;
+ }
+ if(c < sz)
+ buf[c] = '\0';
+ return c;
+}
+
+static inline int
+buf_put2c(char *buf, const char *src)
+{
+ buf[0] = src[0];
+ buf[1] = src[1];
+ buf[2] = '\0';
+ return 2;
+}
+
+int
+u8_escape_wchar(char *buf, size_t sz, uint32_t ch)
+{
+ assert(sz > 2);
+ if(ch >= 0x20 && ch < 0x7f){
+ buf[0] = ch;
+ buf[1] = '\0';
+ return 1;
+ }
+ if(ch > 0xffff)
+ return snprintf(buf, sz, "\\U%.8x", ch);
+ if(ch >= 0x80)
+ return snprintf(buf, sz, "\\u%04x", ch);
+ switch(ch){
+ case '\n': return buf_put2c(buf, "\\n");
+ case '\t': return buf_put2c(buf, "\\t");
+ case '\\': return buf_put2c(buf, "\\\\");
+ case '\a': return buf_put2c(buf, "\\a");
+ case '\b': return buf_put2c(buf, "\\b");
+ case '\e': return buf_put2c(buf, "\\e");
+ case '\f': return buf_put2c(buf, "\\f");
+ case '\r': return buf_put2c(buf, "\\r");
+ case '\v': return buf_put2c(buf, "\\v");
+ }
+ return snprintf(buf, sz, "\\x%02x", ch);
+}
+
+size_t
+u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, int escape_quotes, int ascii)
+{
+ size_t i = *pi, i0;
+ uint32_t ch;
+ char *start = buf;
+ char *blim = start + sz-11;
+ assert(sz > 11);
+
+ while(i < end && buf < blim){
+ // sz-11: leaves room for longest escape sequence
+ if(escape_quotes && src[i] == '"'){
+ buf += buf_put2c(buf, "\\\"");
+ i++;
+ }else if(src[i] == '\\'){
+ buf += buf_put2c(buf, "\\\\");
+ i++;
+ }else{
+ i0 = i;
+ ch = u8_nextmemchar(src, &i);
+ if(ascii || !u8_iswprint(ch)){
+ buf += u8_escape_wchar(buf, sz - (buf-start), ch);
+ }else{
+ i = i0;
+ do{
+ *buf++ = src[i++];
+ }while(!isutf(src[i]));
+ }
+ }
+ }
+ *buf++ = '\0';
+ *pi = i;
+ return (buf-start);
+}
+
+char *
+u8_strchr(const char *s, uint32_t ch, size_t *charn)
+{
+ size_t i = 0, lasti = 0;
+ uint32_t c;
+
+ *charn = 0;
+ while(s[i]){
+ c = u8_nextchar(s, &i);
+ if(c == ch){
+ /* it's const for us, but not necessarily the caller */
+ return (char*)&s[lasti];
+ }
+ lasti = i;
+ (*charn)++;
+ }
+ return nil;
+}
+
+char *
+u8_memchr(const char *s, uint32_t ch, size_t sz, size_t *charn)
+{
+ size_t i = 0, lasti = 0;
+ uint32_t c;
+ int csz;
+
+ *charn = 0;
+ while(i < sz){
+ c = csz = 0;
+ do{
+ c <<= 6;
+ c += (uint8_t)s[i++];
+ csz++;
+ }while(i < sz && !isutf(s[i]));
+ c -= offsetsFromUTF8[csz-1];
+
+ if(c == ch)
+ return (char*)&s[lasti];
+ lasti = i;
+ (*charn)++;
+ }
+ return nil;
+}
+
+char *
+u8_memrchr(const char *s, uint32_t ch, size_t sz)
+{
+ size_t i = sz-1, tempi = 0;
+ uint32_t c;
+
+ if(sz == 0)
+ return nil;
+
+ while(i && !isutf(s[i]))
+ i--;
+
+ while(1){
+ tempi = i;
+ c = u8_nextmemchar(s, &tempi);
+ if(c == ch)
+ return (char*)&s[i];
+ if(i == 0)
+ break;
+ tempi = i;
+ u8_dec(s, &i);
+ if(i > tempi)
+ break;
+ }
+ return nil;
+}
+
+size_t
+u8_vprintf(const char *fmt, va_list ap)
+{
+ size_t cnt, sz, nc, needfree = 0;
+ char *buf, tmp[512];
+ uint32_t *wcs;
+
+ sz = 512;
+ buf = tmp;
+ cnt = vsnprintf(buf, sz, fmt, ap);
+ if((ssize_t)cnt < 0)
+ return 0;
+ if(cnt >= sz){
+ buf = (char*)malloc(cnt + 1);
+ needfree = 1;
+ vsnprintf(buf, cnt+1, fmt, ap);
+ }
+ wcs = (uint32_t*)malloc((cnt+1) * sizeof(uint32_t));
+ nc = u8_toucs(wcs, cnt+1, buf, cnt);
+ wcs[nc] = 0;
+#if defined(__plan9__)
+ print("%S", (Rune*)wcs);
+#else
+ printf("%ls", (wchar_t*)wcs);
+#endif
+ free(wcs);
+ if(needfree)
+ free(buf);
+ return nc;
+}
+
+size_t
+u8_printf(const char *fmt, ...)
+{
+ size_t cnt;
+ va_list args;
+
+ va_start(args, fmt);
+ cnt = u8_vprintf(fmt, args);
+
+ va_end(args);
+ return cnt;
+}
+
+/* based on the valid_utf8 routine from the PCRE library by Philip Hazel
+
+ length is in bytes, since without knowing whether the string is valid
+ it's hard to know how many characters there are! */
+int
+u8_isvalid(const char *str, int length)
+{
+ const uint8_t *p, *pend = (uint8_t*)str + length;
+ uint8_t c;
+ int ab;
+
+ for(p = (uint8_t*)str; p < pend; p++){
+ c = *p;
+ if(c < 128)
+ continue;
+ if((c & 0xc0) != 0xc0)
+ return 0;
+ ab = trailingBytesForUTF8[c];
+ if(length < ab)
+ return 0;
+ length -= ab;
+
+ p++;
+ /* Check top bits in the second byte */
+ if((*p & 0xc0) != 0x80)
+ return 0;
+
+ /* Check for overlong sequences for each different length */
+ switch(ab){
+ /* Check for xx00 000x */
+ case 1:
+ if((c & 0x3e) == 0)
+ return 0;
+ continue; /* We know there aren't any more bytes to check */
+
+ /* Check for 1110 0000, xx0x xxxx */
+ case 2:
+ if(c == 0xe0 && (*p & 0x20) == 0)
+ return 0;
+ break;
+
+ /* Check for 1111 0000, xx00 xxxx */
+ case 3:
+ if(c == 0xf0 && (*p & 0x30) == 0)
+ return 0;
+ break;
+
+ /* Check for 1111 1000, xx00 0xxx */
+ case 4:
+ if(c == 0xf8 && (*p & 0x38) == 0)
+ return 0;
+ break;
+
+ /* Check for leading 0xfe or 0xff and then for 1111 1100, xx00 00xx */
+ case 5:
+ if(c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0))
+ return 0;
+ break;
+ }
+
+ /* Check for valid bytes after the 2nd, if any; all must start 10 */
+ while(--ab > 0)
+ if((*(++p) & 0xc0) != 0x80)
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+u8_reverse(char *dest, char * src, size_t len)
+{
+ size_t si = 0, di = len;
+ uint8_t c;
+
+ dest[di] = '\0';
+ while(si < len){
+ c = (uint8_t)src[si];
+ if((~c) & 0x80){
+ di--;
+ dest[di] = c;
+ si++;
+ }else{
+ switch(c>>4){
+ case 0xc:
+ case 0xd:
+ di -= 2;
+ *((int16_t*)&dest[di]) = *((int16_t*)&src[si]);
+ si += 2;
+ break;
+ case 0xe:
+ di -= 3;
+ dest[di] = src[si];
+ *((int16_t*)&dest[di+1]) = *((int16_t*)&src[si+1]);
+ si += 3;
+ break;
+ case 0xf:
+ di -= 4;
+ *((int32_t*)&dest[di]) = *((int32_t*)&src[si]);
+ si += 4;
+ break;
+ default:
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+++ b/utf8.h
@@ -1,0 +1,111 @@
+#ifndef __UTF8_H_
+#define __UTF8_H_
+
+/* is c the start of a utf8 sequence? */
+#define isutf(c) (((c)&0xC0) != 0x80)
+
+int u8_iswprint(uint32_t c);
+
+/* convert UTF-8 data to wide character */
+size_t u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz);
+
+/* the opposite conversion */
+size_t u8_toutf8(char *dest, size_t sz, const uint32_t *src, size_t srcsz);
+
+/* single character to UTF-8, returns # bytes written */
+size_t u8_wc_toutf8(char *dest, uint32_t ch);
+
+/* character number to byte offset */
+size_t u8_offset(const char *str, size_t charnum);
+
+/* byte offset to character number */
+size_t u8_charnum(const char *s, size_t offset);
+
+/* return next character, updating an index variable */
+uint32_t u8_nextchar(const char *s, size_t *i);
+
+/* next character without NUL character terminator */
+uint32_t u8_nextmemchar(const char *s, size_t *i);
+
+/* move to next character */
+void u8_inc(const char *s, size_t *i);
+
+/* move to previous character */
+void u8_dec(const char *s, size_t *i);
+
+/* returns length of next utf-8 sequence */
+size_t u8_seqlen(const char *s);
+
+/* returns the # of bytes needed to encode a certain character */
+size_t u8_charlen(uint32_t ch);
+
+/* computes the # of bytes needed to encode a WC string as UTF-8 */
+size_t u8_codingsize(uint32_t *wcstr, size_t n);
+
+char read_escape_control_char(char c);
+
+/* assuming src points to the character after a backslash, read an
+ escape sequence, storing the result in dest and returning the number of
+ input characters processed */
+size_t u8_read_escape_sequence(const char *src, size_t ssz, uint32_t *dest);
+
+/* given a wide character, convert it to an ASCII escape sequence stored in
+ buf, where buf is "sz" bytes. returns the number of characters output.
+ sz must be at least 3. */
+int u8_escape_wchar(char *buf, size_t sz, uint32_t ch);
+
+/* convert a string "src" containing escape sequences to UTF-8 */
+size_t u8_unescape(char *buf, size_t sz, const char *src);
+
+/* convert UTF-8 "src" to escape sequences.
+
+ sz is buf size in bytes. must be at least 12.
+
+ if escape_quotes is nonzero, quote characters will be escaped.
+
+ if ascii is nonzero, the output is 7-bit ASCII, no UTF-8 survives.
+
+ starts at src[*pi], updates *pi to point to the first unprocessed
+ byte of the input.
+
+ end is one more than the last allowable value of *pi.
+
+ returns number of bytes placed in buf, including a NUL terminator.
+*/
+size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end,
+ int escape_quotes, int ascii);
+
+/* utility predicates used by the above */
+int octal_digit(char c);
+int hex_digit(char c);
+
+/* return a pointer to the first occurrence of ch in s, or nil if not
+ found. character index of found character returned in *charn. */
+char *u8_strchr(const char *s, uint32_t ch, size_t *charn);
+
+/* same as the above, but searches a buffer of a given size instead of
+ a NUL-terminated string. */
+char *u8_memchr(const char *s, uint32_t ch, size_t sz, size_t *charn);
+
+char *u8_memrchr(const char *s, uint32_t ch, size_t sz);
+
+/* count the number of characters in a UTF-8 string */
+size_t u8_strlen(const char *s);
+
+/* number of columns occupied by a string */
+size_t u8_strwidth(const char *s);
+
+/* printf where the format string and arguments may be in UTF-8.
+ you can avoid this function and just use ordinary printf() if the current
+ locale is UTF-8. */
+size_t u8_vprintf(const char *fmt, va_list ap);
+size_t u8_printf(const char *fmt, ...);
+
+/* determine whether a sequence of bytes is valid UTF-8. length is in bytes */
+int u8_isvalid(const char *str, int length);
+
+/* reverse a UTF-8 string. len is length in bytes. dest and src must both
+ be allocated to at least len+1 bytes. returns 1 for error, 0 otherwise */
+int u8_reverse(char *dest, char *src, size_t len);
+
+#endif