summaryrefslogtreecommitdiff
path: root/appl/acme/buff.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/acme/buff.b')
-rw-r--r--appl/acme/buff.b380
1 files changed, 380 insertions, 0 deletions
diff --git a/appl/acme/buff.b b/appl/acme/buff.b
new file mode 100644
index 00000000..f5d18146
--- /dev/null
+++ b/appl/acme/buff.b
@@ -0,0 +1,380 @@
+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;
+}