#include #include #include #include #include #include #include #include "libframe/frame.h" #include #include #include #include "dat.h" #include "fns.h" enum { Slop = 100 /* room to grow with reallocation */ }; static void sizecache(Buffer* b, uint n) { if (n <= b->cmax) return; b->cmax = n + Slop; b->c = runerealloc(b->c, b->cmax); } static void addblock(Buffer* b, uint i, uint n) { if (i > b->nbl) error("internal error: addblock"); b->bl = realloc(b->bl, (b->nbl + 1) * sizeof b->bl[0]); if (i < b->nbl) memmove(b->bl + i + 1, b->bl + i, (b->nbl - i) * sizeof(Block*)); b->bl[i] = disknewblock(disk, n); b->nbl++; } static void delblock(Buffer* b, uint i) { if (i >= b->nbl) error("internal error: delblock"); diskrelease(disk, b->bl[i]); b->nbl--; if (i < b->nbl) memmove(b->bl + i, b->bl + i + 1, (b->nbl - i) * sizeof(Block*)); b->bl = realloc(b->bl, b->nbl * sizeof b->bl[0]); } /* * Move cache so b->cq <= q0 < b->cq+b->cnc. * If at very end, q0 will fall on end of cache block. */ static void flush(Buffer* b) { if (b->cdirty || b->cnc == 0) { if (b->cnc == 0) delblock(b, b->cbi); else diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); b->cdirty = FALSE; } } static void setcache(Buffer* b, uint q0) { Block **blp, *bl; uint i, q; if (q0 > b->nc) error("internal error: setcache"); /* * flush and reload if q0 is not in cache. */ if (b->nc == 0 || (b->cq <= q0 && q0 < b->cq + b->cnc)) return; /* * if q0 is at end of file and end of cache, continue to grow this block */ if (q0 == b->nc && q0 == b->cq + b->cnc && b->cnc < Maxblock) return; flush(b); /* find block */ if (q0 < b->cq) { q = 0; i = 0; } else { q = b->cq; i = b->cbi; } blp = &b->bl[i]; while (q + (*blp)->u.n <= q0 && q + (*blp)->u.n < b->nc) { q += (*blp)->u.n; i++; blp++; if (i >= b->nbl) error("block not found"); } bl = *blp; /* remember position */ b->cbi = i; b->cq = q; sizecache(b, bl->u.n); b->cnc = bl->u.n; /*read block*/ diskread(disk, bl, b->c, b->cnc); } void bufinsert(Buffer* b, uint q0, Rune* s, uint n) { uint i, m, t, off; if (q0 > b->nc) error("internal error: bufinsert"); while (n > 0) { setcache(b, q0); off = q0 - b->cq; if (b->cnc + n <= Maxblock) { /* Everything fits in one block. */ t = b->cnc + n; m = n; if (b->bl == nil) { /* allocate */ if (b->cnc != 0) error("internal error: bufinsert1 cnc!=0"); addblock(b, 0, t); b->cbi = 0; } sizecache(b, t); runemove(b->c + off + m, b->c + off, b->cnc - off); runemove(b->c + off, s, m); b->cnc = t; goto Tail; } /* * We must make a new block. If q0 is at * the very beginning or end of this block, * just make a new block and fill it. */ if (q0 == b->cq || q0 == b->cq + b->cnc) { if (b->cdirty) flush(b); m = min(n, Maxblock); if (b->bl == nil) { /* allocate */ if (b->cnc != 0) error("internal error: bufinsert2 cnc!=0"); i = 0; } else { i = b->cbi; if (q0 > b->cq) i++; } addblock(b, i, m); sizecache(b, m); runemove(b->c, s, m); b->cq = q0; b->cbi = i; b->cnc = m; goto Tail; } /* * Split the block; cut off the right side and * let go of it. */ m = b->cnc - off; if (m > 0) { i = b->cbi + 1; addblock(b, i, m); diskwrite(disk, &b->bl[i], b->c + off, m); b->cnc -= m; } /* * Now at end of block. Take as much input * as possible and tack it on end of block. */ m = min(n, Maxblock - b->cnc); sizecache(b, b->cnc + m); runemove(b->c + b->cnc, s, m); b->cnc += m; Tail: b->nc += m; q0 += m; s += m; n -= m; b->cdirty = TRUE; } } void bufdelete(Buffer* b, uint q0, uint q1) { uint m, n, off; if (!(q0 <= q1 && q0 <= b->nc && q1 <= b->nc)) error("internal error: bufdelete"); while (q1 > q0) { setcache(b, q0); off = q0 - b->cq; if (q1 > b->cq + b->cnc) n = b->cnc - off; else n = q1 - q0; m = b->cnc - (off + n); if (m > 0) runemove(b->c + off, b->c + off + n, m); b->cnc -= n; b->cdirty = TRUE; q1 -= n; b->nc -= n; } } static int bufloader(void* v, uint q0, Rune* r, int nr) { bufinsert(v, q0, r, nr); return nr; } uint loadfile( int fd, uint q0, int* nulls, int (*f)(void*, uint, Rune*, int), void* arg, DigestState* h) { char* p; Rune* r; int l, m, n, nb, nr; uint q1; p = emalloc((Maxblock + UTFmax + 1) * sizeof p[0]); r = runemalloc(Maxblock); m = 0; n = 1; q1 = q0; /* * At top of loop, may have m bytes left over from * last pass, possibly representing a partial rune. */ while (n > 0) { n = read(fd, p + m, Maxblock); if (n < 0) { warning(nil, "read error in Buffer.load"); break; } if (h != nil) sha1((uchar*)p + m, n, nil, h); m += n; p[m] = 0; l = m; if (n > 0) l -= UTFmax; cvttorunes(p, l, r, &nb, &nr, nulls); memmove(p, p + nb, m - nb); m -= nb; q1 += (*f)(arg, q1, r, nr); } free(p); free(r); return q1 - q0; } uint bufload(Buffer* b, uint q0, int fd, int* nulls, DigestState* h) { if (q0 > b->nc) error("internal error: bufload"); return loadfile(fd, q0, nulls, bufloader, b, h); } void bufread(Buffer* b, uint q0, Rune* s, uint n) { uint m; if (!(q0 <= b->nc && q0 + n <= b->nc)) error("bufread: internal error"); while (n > 0) { setcache(b, q0); m = min(n, b->cnc - (q0 - b->cq)); runemove(s, b->c + (q0 - b->cq), m); q0 += m; s += m; n -= m; } } void bufreset(Buffer* b) { int i; b->nc = 0; b->cnc = 0; b->cq = 0; b->cdirty = 0; b->cbi = 0; /* delete backwards to avoid n² behavior */ for (i = b->nbl - 1; --i >= 0;) delblock(b, i); } void bufclose(Buffer* b) { bufreset(b); free(b->c); b->c = nil; b->cnc = 0; free(b->bl); b->bl = nil; b->nbl = 0; }