Usually r->nused < r->nalloc and the read is in bounds. But it could in theory be right on the line and reading past the end of the allocation. Make it safe but preserve as much of the old semantics as possible. This use of rterm appears to be only for optimization purposes so the result does not matter for correctness.
339 lines
5.7 KiB
C
339 lines
5.7 KiB
C
#include "sam.h"
|
|
/*
|
|
* GROWDATASIZE must be big enough that all errors go out as Hgrowdata's,
|
|
* so they will be scrolled into visibility in the ~~sam~~ window (yuck!).
|
|
*/
|
|
#define GROWDATASIZE 50 /* if size is <= this, send data with grow */
|
|
|
|
void rcut(List*, Posn, Posn);
|
|
int rterm(List*, Posn);
|
|
void rgrow(List*, Posn, Posn);
|
|
|
|
static Posn growpos;
|
|
static Posn grown;
|
|
static Posn shrinkpos;
|
|
static Posn shrunk;
|
|
|
|
/*
|
|
* rasp routines inform the terminal of changes to the file.
|
|
*
|
|
* a rasp is a list of spans within the file, and an indication
|
|
* of whether the terminal knows about the span.
|
|
*
|
|
* optimize by coalescing multiple updates to the same span
|
|
* if it is not known by the terminal.
|
|
*
|
|
* other possible optimizations: flush terminal's rasp by cut everything,
|
|
* insert everything if rasp gets too large.
|
|
*/
|
|
|
|
/*
|
|
* only called for initial load of file
|
|
*/
|
|
void
|
|
raspload(File *f)
|
|
{
|
|
if(f->rasp == nil)
|
|
return;
|
|
grown = f->b.nc;
|
|
growpos = 0;
|
|
if(f->b.nc)
|
|
rgrow(f->rasp, 0, f->b.nc);
|
|
raspdone(f, 1);
|
|
}
|
|
|
|
void
|
|
raspstart(File *f)
|
|
{
|
|
if(f->rasp == nil)
|
|
return;
|
|
grown = 0;
|
|
shrunk = 0;
|
|
outbuffered = 1;
|
|
}
|
|
|
|
void
|
|
raspdone(File *f, int toterm)
|
|
{
|
|
if(f->dot.r.p1 > f->b.nc)
|
|
f->dot.r.p1 = f->b.nc;
|
|
if(f->dot.r.p2 > f->b.nc)
|
|
f->dot.r.p2 = f->b.nc;
|
|
if(f->mark.p1 > f->b.nc)
|
|
f->mark.p1 = f->b.nc;
|
|
if(f->mark.p2 > f->b.nc)
|
|
f->mark.p2 = f->b.nc;
|
|
if(f->rasp == nil)
|
|
return;
|
|
if(grown)
|
|
outTsll(Hgrow, f->tag, growpos, grown);
|
|
else if(shrunk)
|
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
|
if(toterm)
|
|
outTs(Hcheck0, f->tag);
|
|
outflush();
|
|
outbuffered = 0;
|
|
if(f == cmd){
|
|
cmdpt += cmdptadv;
|
|
cmdptadv = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
raspflush(File *f)
|
|
{
|
|
if(grown){
|
|
outTsll(Hgrow, f->tag, growpos, grown);
|
|
grown = 0;
|
|
}
|
|
else if(shrunk){
|
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
|
shrunk = 0;
|
|
}
|
|
outflush();
|
|
}
|
|
|
|
void
|
|
raspdelete(File *f, uint p1, uint p2, int toterm)
|
|
{
|
|
long n;
|
|
|
|
n = p2 - p1;
|
|
if(n == 0)
|
|
return;
|
|
|
|
if(p2 <= f->dot.r.p1){
|
|
f->dot.r.p1 -= n;
|
|
f->dot.r.p2 -= n;
|
|
}
|
|
if(p2 <= f->mark.p1){
|
|
f->mark.p1 -= n;
|
|
f->mark.p2 -= n;
|
|
}
|
|
|
|
if(f->rasp == nil)
|
|
return;
|
|
|
|
if(f==cmd && p1<cmdpt){
|
|
if(p2 <= cmdpt)
|
|
cmdpt -= n;
|
|
else
|
|
cmdpt = p1;
|
|
}
|
|
if(toterm){
|
|
if(grown){
|
|
outTsll(Hgrow, f->tag, growpos, grown);
|
|
grown = 0;
|
|
}else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){
|
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
|
shrunk = 0;
|
|
}
|
|
if(!shrunk || shrinkpos==p2)
|
|
shrinkpos = p1;
|
|
shrunk += n;
|
|
}
|
|
rcut(f->rasp, p1, p2);
|
|
}
|
|
|
|
void
|
|
raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm)
|
|
{
|
|
Range r;
|
|
|
|
if(n == 0)
|
|
return;
|
|
|
|
if(p1 < f->dot.r.p1){
|
|
f->dot.r.p1 += n;
|
|
f->dot.r.p2 += n;
|
|
}
|
|
if(p1 < f->mark.p1){
|
|
f->mark.p1 += n;
|
|
f->mark.p2 += n;
|
|
}
|
|
|
|
|
|
if(f->rasp == nil)
|
|
return;
|
|
if(f==cmd && p1<cmdpt)
|
|
cmdpt += n;
|
|
if(toterm){
|
|
if(shrunk){
|
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
|
shrunk = 0;
|
|
}
|
|
if(n>GROWDATASIZE || !rterm(f->rasp, p1)){
|
|
rgrow(f->rasp, p1, n);
|
|
if(grown && growpos+grown!=p1 && growpos!=p1){
|
|
outTsll(Hgrow, f->tag, growpos, grown);
|
|
grown = 0;
|
|
}
|
|
if(!grown)
|
|
growpos = p1;
|
|
grown += n;
|
|
}else{
|
|
if(grown){
|
|
outTsll(Hgrow, f->tag, growpos, grown);
|
|
grown = 0;
|
|
}
|
|
rgrow(f->rasp, p1, n);
|
|
r = rdata(f->rasp, p1, n);
|
|
if(r.p1!=p1 || r.p2!=p1+n)
|
|
panic("rdata in toterminal");
|
|
outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n));
|
|
}
|
|
}else{
|
|
rgrow(f->rasp, p1, n);
|
|
r = rdata(f->rasp, p1, n);
|
|
if(r.p1!=p1 || r.p2!=p1+n)
|
|
panic("rdata in toterminal");
|
|
}
|
|
}
|
|
|
|
#define M 0x80000000L
|
|
#define P(i) r->posnptr[i]
|
|
#define T(i) (P(i)&M) /* in terminal */
|
|
#define L(i) (P(i)&~M) /* length of this piece */
|
|
|
|
void
|
|
rcut(List *r, Posn p1, Posn p2)
|
|
{
|
|
Posn p, x;
|
|
int i;
|
|
|
|
if(p1 == p2)
|
|
panic("rcut 0");
|
|
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
|
;
|
|
if(i == r->nused)
|
|
panic("rcut 1");
|
|
if(p < p1){ /* chop this piece */
|
|
if(p+L(i) < p2){
|
|
x = p1-p;
|
|
p += L(i);
|
|
}else{
|
|
x = L(i)-(p2-p1);
|
|
p = p2;
|
|
}
|
|
if(T(i))
|
|
P(i) = x|M;
|
|
else
|
|
P(i) = x;
|
|
i++;
|
|
}
|
|
while(i<r->nused && p+L(i)<=p2){
|
|
p += L(i);
|
|
dellist(r, i);
|
|
}
|
|
if(p < p2){
|
|
if(i == r->nused)
|
|
panic("rcut 2");
|
|
x = L(i)-(p2-p);
|
|
if(T(i))
|
|
P(i) = x|M;
|
|
else
|
|
P(i) = x;
|
|
}
|
|
/* can we merge i and i-1 ? */
|
|
if(i>0 && i<r->nused && T(i-1)==T(i)){
|
|
x = L(i-1)+L(i);
|
|
dellist(r, i--);
|
|
if(T(i))
|
|
P(i)=x|M;
|
|
else
|
|
P(i)=x;
|
|
}
|
|
}
|
|
|
|
void
|
|
rgrow(List *r, Posn p1, Posn n)
|
|
{
|
|
Posn p;
|
|
int i;
|
|
|
|
if(n == 0)
|
|
panic("rgrow 0");
|
|
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
|
;
|
|
if(i == r->nused){ /* stick on end of file */
|
|
if(p!=p1)
|
|
panic("rgrow 1");
|
|
if(i>0 && !T(i-1))
|
|
P(i-1)+=n;
|
|
else
|
|
inslist(r, i, n);
|
|
}else if(!T(i)) /* goes in this empty piece */
|
|
P(i)+=n;
|
|
else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies life */
|
|
P(i-1)+=n;
|
|
else if(p==p1)
|
|
inslist(r, i, n);
|
|
else{ /* must break piece in terminal */
|
|
inslist(r, i+1, (L(i)-(p1-p))|M);
|
|
inslist(r, i+1, n);
|
|
P(i) = (p1-p)|M;
|
|
}
|
|
}
|
|
|
|
int
|
|
rterm(List *r, Posn p1)
|
|
{
|
|
Posn p;
|
|
int i;
|
|
|
|
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
|
;
|
|
if(i==r->nused)
|
|
return i > 0 && T(i-1);
|
|
return T(i);
|
|
}
|
|
|
|
Range
|
|
rdata(List *r, Posn p1, Posn n)
|
|
{
|
|
Posn p;
|
|
int i;
|
|
Range rg;
|
|
|
|
if(n==0)
|
|
panic("rdata 0");
|
|
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
|
;
|
|
if(i==r->nused)
|
|
panic("rdata 1");
|
|
if(T(i)){
|
|
n-=L(i)-(p1-p);
|
|
if(n<=0){
|
|
rg.p1 = rg.p2 = p1;
|
|
return rg;
|
|
}
|
|
p+=L(i++);
|
|
p1 = p;
|
|
}
|
|
if(T(i) || i==r->nused)
|
|
panic("rdata 2");
|
|
if(p+L(i)<p1+n)
|
|
n = L(i)-(p1-p);
|
|
rg.p1 = p1;
|
|
rg.p2 = p1+n;
|
|
if(p!=p1){
|
|
inslist(r, i+1, L(i)-(p1-p));
|
|
P(i)=p1-p;
|
|
i++;
|
|
}
|
|
if(L(i)!=n){
|
|
inslist(r, i+1, L(i)-n);
|
|
P(i)=n;
|
|
}
|
|
P(i)|=M;
|
|
/* now i is set; can we merge? */
|
|
if(i<r->nused-1 && T(i+1)){
|
|
P(i)=(n+=L(i+1))|M;
|
|
dellist(r, i+1);
|
|
}
|
|
if(i>0 && T(i-1)){
|
|
P(i)=(n+L(i-1))|M;
|
|
dellist(r, i-1);
|
|
}
|
|
return rg;
|
|
}
|