implement Bufferm; include "common.m"; sys : Sys;dat : Dat;utils : Utils;diskm : Diskm;ecmd: Editcmd; FALSE, TRUE, XXX, Maxblock, Astring : import Dat;Block : import Dat;disk : import dat;Disk : import diskm;File: import Filem;error, warning, min : import utils; init(mods : ref Dat->Mods){ sys = mods.sys; dat = mods.dat; utils = mods.utils; diskm = mods.diskm; ecmd = mods.editcmd;} nullbuffer : Buffer; newbuffer() : ref Buffer{ b := ref nullbuffer; return b;} Slop : con 100; # room to grow with reallocation Buffer.sizecache(b : self ref Buffer, n : int){ if(n <= b.cmax) return; b.cmax = n+Slop; os := b.c; b.c = utils->stralloc(b.cmax); if (os != nil) { loss := len os.s; c := b.c; oss := os.s; for (i := 0; i < loss && i < b.cmax; i++) c.s[i] = oss[i]; utils->strfree(os); }} ## Move cache so b.cq <= q0 < b.cq+b.cnc.# If at very end, q0 will fall on end of cache block.# Buffer.flush(b : self ref Buffer){ if(b.cdirty || b.cnc==0){ if(b.cnc == 0) b.delblock(b.cbi); else b.bl[b.cbi] = disk.write(b.bl[b.cbi], b.c.s, b.cnc); b.cdirty = FALSE; }} Buffer.setcache(b : self ref Buffer, q0 : int){ blp, bl : ref Block; i, q : int; if (q0 > b.nc) error("bad assert in 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; b.flush(); # 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 = b.bl[i]; if(i >= b.nbl) error("block not found"); } bl = blp; # remember position b.cbi = i; b.cq = q; b.sizecache(bl.n); b.cnc = bl.n; #read block disk.read(bl, b.c, b.cnc);} Buffer.addblock(b : self ref Buffer, i : int, n : int){ if (i > b.nbl) error("bad assert in addblock"); obl := b.bl; b.bl = array[b.nbl+1] of ref Block; b.bl[0:] = obl[0:i]; if(i < b.nbl) b.bl[i+1:] = obl[i:b.nbl]; b.bl[i] = disk.new(n); b.nbl++; obl = nil;} Buffer.delblock(b : self ref Buffer, i : int){ if (i >= b.nbl) error("bad assert in delblock"); disk.release(b.bl[i]); obl := b.bl; b.bl = array[b.nbl-1] of ref Block; b.bl[0:] = obl[0:i]; if(i < b.nbl-1) b.bl[i:] = obl[i+1:b.nbl]; b.nbl--; obl = nil;} Buffer.insert(b : self ref Buffer, q0 : int, s : string, n : int){ i, j, m, t, off, p : int; if (q0>b.nc) error("bad assert in insert"); p = 0; while(n > 0){ b.setcache(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("bad assert in insert"); b.addblock(0, t); b.cbi = 0; } b.sizecache(t); c := b.c; # cs := c.s; for (j = b.cnc-1; j >= off; j--) c.s[j+m] = c.s[j]; for (j = 0; j < m; j++) c.s[off+j] = s[p+j]; b.cnc = t; } # # 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. # else if(q0==b.cq || q0==b.cq+b.cnc){ if(b.cdirty) b.flush(); m = min(n, Maxblock); if(b.bl == nil){ # allocate if (b.cnc != 0) error("bad assert in insert"); i = 0; }else{ i = b.cbi; if(q0 > b.cq) i++; } b.addblock(i, m); b.sizecache(m); c := b.c; for (j = 0; j < m; j++) c.s[j] = s[p+j]; b.cq = q0; b.cbi = i; b.cnc = m; } else { # # Split the block; cut off the right side and # let go of it. # m = b.cnc-off; if(m > 0){ i = b.cbi+1; b.addblock(i, m); b.bl[i] = disk.write(b.bl[i], b.c.s[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); b.sizecache(b.cnc+m); c := b.c; for (j = 0; j < m; j++) c.s[j+b.cnc] = s[p+j]; b.cnc += m; } b.nc += m; q0 += m; p += m; n -= m; b.cdirty = TRUE; }} Buffer.delete(b : self ref Buffer, q0 : int, q1 : int){ m, n, off : int; if (q0>q1 || q0>b.nc || q1>b.nc) error("bad assert in delete"); while(q1 > q0){ b.setcache(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) { c := b.c; # cs := c.s; p := m+off; for (j := off; j < p; j++) c.s[j] = c.s[j+n]; } b.cnc -= n; b.cdirty = TRUE; q1 -= n; b.nc -= n; }} # Buffer.replace(b: self ref Buffer, q0: int, q1: int, s: string, n: int)# {# if(q0>q1 || q0>b.nc || q1>b.nc || n != q1-q0)# error("bad assert in replace");# p := 0;# while(q1 > q0){# b.setcache(q0);# off := q0-b.cq;# if(q1 > b.cq+b.cnc)# n = b.cnc-off;# else# n = q1-q0;# c := b.c;# for(i := 0; i < n; i++)# c.s[i+off] = s[i+p];# b.cdirty = TRUE;# q0 += n;# p += n;# } # } pbuf : array of byte; bufloader(b: ref Buffer, q0: int, r: string, nr: int): int{ b.insert(q0, r, nr); return nr;} loadfile(fd: ref Sys->FD, q0: int, fun: int, b: ref Buffer, f: ref File): int{ p : array of byte; r : string; m, n, nb, nr : int; q1 : int; if (pbuf == nil) pbuf = array[Maxblock+Sys->UTFmax] of byte; p = pbuf; 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 = sys->read(fd, p[m:], Maxblock); if(n < 0){ warning(nil, "read error in Buffer.load"); break; } m += n; nb = sys->utfbytes(p, m); r = string p[0:nb]; p[0:] = p[nb:m]; m -= nb; nr = len r; if(fun == Dat->BUFL) q1 += bufloader(b, q1, r, nr); else q1 += ecmd->readloader(f, q1, r, nr); } p = nil; r = nil; return q1-q0;} Buffer.loadx(b : self ref Buffer, q0 : int, fd : ref Sys->FD) : int{ if (q0>b.nc) error("bad assert in load"); return loadfile(fd, q0, Dat->BUFL, b, nil);} Buffer.read(b : self ref Buffer, q0 : int, s : ref Astring, p : int, n : int){ m : int; if (q0>b.nc || q0+n>b.nc) error("bad assert in read"); while(n > 0){ b.setcache(q0); m = min(n, b.cnc-(q0-b.cq)); c := b.c; cs := c.s; for (j := 0; j < m; j++) s.s[p+j] = cs[j+q0-b.cq]; q0 += m; p += m; n -= m; }} Buffer.reset(b : self ref Buffer){ i : int; 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; ) b.delblock(i);} Buffer.close(b : self ref Buffer){ b.reset(); if (b.c != nil) { utils->strfree(b.c); b.c = nil; } b.cnc = 0; b.bl = nil; b.nbl = 0;}
implement Bufferm;
include "common.m";
sys : Sys;
dat : Dat;
utils : Utils;
diskm : Diskm;
ecmd: Editcmd;
FALSE, TRUE, XXX, Maxblock, Astring : import Dat;
Block : import Dat;
disk : import dat;
Disk : import diskm;
File: import Filem;
error, warning, min : import utils;
init(mods : ref Dat->Mods)
{
sys = mods.sys;
dat = mods.dat;
utils = mods.utils;
diskm = mods.diskm;
ecmd = mods.editcmd;
}
nullbuffer : Buffer;
newbuffer() : ref Buffer
{
b := ref nullbuffer;
return b;
}
Slop : con 100; # room to grow with reallocation
Buffer.sizecache(b : self ref Buffer, n : int)
{
if(n <= b.cmax)
return;
b.cmax = n+Slop;
os := b.c;
b.c = utils->stralloc(b.cmax);
if (os != nil) {
loss := len os.s;
c := b.c;
oss := os.s;
for (i := 0; i < loss && i < b.cmax; i++)
c.s[i] = oss[i];
utils->strfree(os);
}
}
#
# Move cache so b.cq <= q0 < b.cq+b.cnc.
# If at very end, q0 will fall on end of cache block.
#
Buffer.flush(b : self ref Buffer)
{
if(b.cdirty || b.cnc==0){
if(b.cnc == 0)
b.delblock(b.cbi);
else
b.bl[b.cbi] = disk.write(b.bl[b.cbi], b.c.s, b.cnc);
b.cdirty = FALSE;
}
}
Buffer.setcache(b : self ref Buffer, q0 : int)
{
blp, bl : ref Block;
i, q : int;
if (q0 > b.nc)
error("bad assert in 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;
b.flush();
# 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 = b.bl[i];
if(i >= b.nbl)
error("block not found");
}
bl = blp;
# remember position
b.cbi = i;
b.cq = q;
b.sizecache(bl.n);
b.cnc = bl.n;
#read block
disk.read(bl, b.c, b.cnc);
}
Buffer.addblock(b : self ref Buffer, i : int, n : int)
{
if (i > b.nbl)
error("bad assert in addblock");
obl := b.bl;
b.bl = array[b.nbl+1] of ref Block;
b.bl[0:] = obl[0:i];
if(i < b.nbl)
b.bl[i+1:] = obl[i:b.nbl];
b.bl[i] = disk.new(n);
b.nbl++;
obl = nil;
}
Buffer.delblock(b : self ref Buffer, i : int)
{
if (i >= b.nbl)
error("bad assert in delblock");
disk.release(b.bl[i]);
obl := b.bl;
b.bl = array[b.nbl-1] of ref Block;
b.bl[0:] = obl[0:i];
if(i < b.nbl-1)
b.bl[i:] = obl[i+1:b.nbl];
b.nbl--;
obl = nil;
}
Buffer.insert(b : self ref Buffer, q0 : int, s : string, n : int)
{
i, j, m, t, off, p : int;
if (q0>b.nc)
error("bad assert in insert");
p = 0;
while(n > 0){
b.setcache(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("bad assert in insert");
b.addblock(0, t);
b.cbi = 0;
}
b.sizecache(t);
c := b.c;
# cs := c.s;
for (j = b.cnc-1; j >= off; j--)
c.s[j+m] = c.s[j];
for (j = 0; j < m; j++)
c.s[off+j] = s[p+j];
b.cnc = t;
}
#
# 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.
#
else if(q0==b.cq || q0==b.cq+b.cnc){
if(b.cdirty)
b.flush();
m = min(n, Maxblock);
if(b.bl == nil){ # allocate
if (b.cnc != 0)
error("bad assert in insert");
i = 0;
}else{
i = b.cbi;
if(q0 > b.cq)
i++;
}
b.addblock(i, m);
b.sizecache(m);
c := b.c;
for (j = 0; j < m; j++)
c.s[j] = s[p+j];
b.cq = q0;
b.cbi = i;
b.cnc = m;
}
else {
#
# Split the block; cut off the right side and
# let go of it.
#
m = b.cnc-off;
if(m > 0){
i = b.cbi+1;
b.addblock(i, m);
b.bl[i] = disk.write(b.bl[i], b.c.s[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);
b.sizecache(b.cnc+m);
c := b.c;
for (j = 0; j < m; j++)
c.s[j+b.cnc] = s[p+j];
b.cnc += m;
}
b.nc += m;
q0 += m;
p += m;
n -= m;
b.cdirty = TRUE;
}
}
Buffer.delete(b : self ref Buffer, q0 : int, q1 : int)
{
m, n, off : int;
if (q0>q1 || q0>b.nc || q1>b.nc)
error("bad assert in delete");
while(q1 > q0){
b.setcache(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) {
c := b.c;
# cs := c.s;
p := m+off;
for (j := off; j < p; j++)
c.s[j] = c.s[j+n];
}
b.cnc -= n;
b.cdirty = TRUE;
q1 -= n;
b.nc -= n;
}
}
# Buffer.replace(b: self ref Buffer, q0: int, q1: int, s: string, n: int)
# {
# if(q0>q1 || q0>b.nc || q1>b.nc || n != q1-q0)
# error("bad assert in replace");
# p := 0;
# while(q1 > q0){
# b.setcache(q0);
# off := q0-b.cq;
# if(q1 > b.cq+b.cnc)
# n = b.cnc-off;
# else
# n = q1-q0;
# c := b.c;
# for(i := 0; i < n; i++)
# c.s[i+off] = s[i+p];
# b.cdirty = TRUE;
# q0 += n;
# p += n;
# }
# }
pbuf : array of byte;
bufloader(b: ref Buffer, q0: int, r: string, nr: int): int
{
b.insert(q0, r, nr);
return nr;
}
loadfile(fd: ref Sys->FD, q0: int, fun: int, b: ref Buffer, f: ref File): int
{
p : array of byte;
r : string;
m, n, nb, nr : int;
q1 : int;
if (pbuf == nil)
pbuf = array[Maxblock+Sys->UTFmax] of byte;
p = pbuf;
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 = sys->read(fd, p[m:], Maxblock);
if(n < 0){
warning(nil, "read error in Buffer.load");
break;
}
m += n;
nb = sys->utfbytes(p, m);
r = string p[0:nb];
p[0:] = p[nb:m];
m -= nb;
nr = len r;
if(fun == Dat->BUFL)
q1 += bufloader(b, q1, r, nr);
else
q1 += ecmd->readloader(f, q1, r, nr);
}
p = nil;
r = nil;
return q1-q0;
}
Buffer.loadx(b : self ref Buffer, q0 : int, fd : ref Sys->FD) : int
{
if (q0>b.nc)
error("bad assert in load");
return loadfile(fd, q0, Dat->BUFL, b, nil);
}
Buffer.read(b : self ref Buffer, q0 : int, s : ref Astring, p : int, n : int)
{
m : int;
if (q0>b.nc || q0+n>b.nc)
error("bad assert in read");
while(n > 0){
b.setcache(q0);
m = min(n, b.cnc-(q0-b.cq));
c := b.c;
cs := c.s;
for (j := 0; j < m; j++)
s.s[p+j] = cs[j+q0-b.cq];
q0 += m;
p += m;
n -= m;
}
}
Buffer.reset(b : self ref Buffer)
{
i : int;
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; )
b.delblock(i);
}
Buffer.close(b : self ref Buffer)
{
b.reset();
if (b.c != nil) {
utils->strfree(b.c);
b.c = nil;
}
b.cnc = 0;
b.bl = nil;
b.nbl = 0;
}