/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <cursor.h>#include <mouse.h>#include <keyboard.h>#include <frame.h>#include <fcall.h>#include <plumb.h>#include "dat.h"#include "fns.h" enum{ Slop = 100, /* room to grow with reallocation */}; staticvoidsizecache(Buffer *b, uint n){ if(n <= b->cmax) return; b->cmax = n+Slop; b->c = runerealloc(b->c, b->cmax);} staticvoidaddblock(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++;} staticvoiddelblock(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. */ staticvoidflush(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; }} staticvoidsetcache(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)->n <= q0 && q+(*blp)->n < b->nc){ q += (*blp)->n; i++; blp++; if(i >= b->nbl) error("block not found"); } bl = *blp; /* remember position */ b->cbi = i; b->cq = q; sizecache(b, bl->n); b->cnc = bl->n; /*read block*/ diskread(disk, bl, b->c, b->cnc);} voidbufinsert(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; }} voidbufdelete(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 intbufloader(void *v, uint q0, Rune *r, int nr){ bufinsert(v, q0, r, nr); return nr;} uintloadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg){ 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; } 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;} uintbufload(Buffer *b, uint q0, int fd, int *nulls){ if(q0 > b->nc) error("internal error: bufload"); return loadfile(fd, q0, nulls, bufloader, b);} voidbufread(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; }} voidbufreset(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);} voidbufclose(Buffer *b){ bufreset(b); free(b->c); b->c = nil; b->cnc = 0; free(b->bl); b->bl = nil; b->nbl = 0;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#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)->n <= q0 && q+(*blp)->n < b->nc){
q += (*blp)->n;
i++;
blp++;
if(i >= b->nbl)
error("block not found");
}
bl = *blp;
/* remember position */
b->cbi = i;
b->cq = q;
sizecache(b, bl->n);
b->cnc = bl->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)
{
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;
}
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)
{
if(q0 > b->nc)
error("internal error: bufload");
return loadfile(fd, q0, nulls, bufloader, b);
}
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;
}