diff options
Diffstat (limited to 'appl/acme/buff.b')
| -rw-r--r-- | appl/acme/buff.b | 380 |
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; +} |
