ref: faa5d55642c8bbd26151561eef13b6525f786154
parent: 8d34476e2e8852e577d67f74ada5bf65c5701935
author: qwx <[email protected]>
date: Thu Dec 29 07:16:52 EST 2022
decouple memory buffers from chunks referencing them, no more copying merry fucking christmas - files are still pre-split when reading, but buffers are thenceforth untouched - no more merging chunks or buffers - generalize chunks as chains without a sentinel or dummy pointer - separate chunk handling from editing (perhaps too much) to streamline and enforce some rules - avoid bugprone totalsz usage and other fixed references; recalculating size after every edit isn't expensive in practice, the number of splits wouldn't be in the order of thousands even - fix race condition in rthread needs more testing after recent changes pending: undo/redo, registers, addressable multisnarf buffers
--- /dev/null
+++ b/chunk.c
@@ -1,0 +1,424 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+/* chunk ≡ chain of chunks */
+struct Buf{
+ uchar *buf;
+ usize bufsz;
+ Ref;
+};
+static Chunk *norris;
+
+static void
+printchunks(Chunk *r)
+{
+ Chunk *c;
+
+ fprint(2, "chunklist dot %zux %zux %zux: ",
+ dot.from.pos, dot.pos, dot.to.pos);
+ c = r;
+ do{
+ fprint(2, "[%#p:%zd:←%#p→%#p] ", c, c->len, c->left, c->right);
+ assert(c->right->left == c);
+ c = c->right;
+ }while(c != r);
+ fprint(2, "\n");
+}
+
+static Chunk *
+newchunk(Buf *b)
+{
+ Chunk *c;
+
+ c = emalloc(sizeof *c);
+ c->left = c;
+ c->right = c;
+ c->b = b;
+ c->off = 0;
+ c->len = b->bufsz;
+ incref(&b->Ref);
+ return c;
+}
+static Chunk *
+newbuf(usize n)
+{
+ Buf *b;
+
+ assert((n & 3) == 0);
+ b = emalloc(sizeof *b);
+ b->bufsz = n;
+ b->buf = emalloc(b->bufsz);
+ return newchunk(b);
+}
+
+static void
+linkchunk(Chunk *left, Chunk *c)
+{
+ c->left->right = left->right;
+ left->right->left = c->left;
+ c->left = left;
+ left->right = c;
+}
+static void
+unlink(Chunk *left, Chunk *right)
+{
+ left->left->right = right->right;
+ right->right->left = left->left;
+ left->left = right;
+ right->right = left;
+}
+
+static Chunk *
+clonechunk(Chunk *c)
+{
+ Chunk *nc;
+
+ assert(c != nil && c->b != nil);
+ nc = newchunk(c->b);
+ nc->off = c->off;
+ nc->len = c->len;
+ incref(c->b);
+ return nc;
+}
+Chunk *
+replicate(Chunk *left, Chunk *right)
+{
+ Chunk *cl, *c, *nc;
+
+ cl = clonechunk(left);
+ c = cl;
+ for(; left!=right;){
+ left = left->right;
+ nc = clonechunk(left);
+ linkchunk(c, nc);
+ c = nc;
+ }
+ return cl;
+}
+
+static void
+freebuf(Buf *b)
+{
+ if(b == nil)
+ return;
+ free(b->buf);
+ free(b);
+}
+static void
+freechunk(Chunk *c)
+{
+ if(c == nil)
+ return;
+ if(c->b != nil && decref(c->b) == 0)
+ freebuf(c->b);
+ free(c);
+}
+void
+freechain(Chunk *c)
+{
+ Chunk *cl, *cp;
+
+ if(c == nil)
+ return;
+ for(cl=c->right; cl!=c; cl=cp){
+ cp = cl->right;
+ unlink(cl, cl);
+ freechunk(cl);
+ }
+ freechunk(c);
+}
+
+static void
+shrinkbuf(Chunk *c, usize newsz)
+{
+ Buf *b;
+
+ b = c->b;
+ assert(b != nil);
+ assert(newsz < b->bufsz && newsz > 0);
+ if(c->off + c->len > newsz)
+ c->len = newsz - c->off;
+ b->buf = erealloc(b->buf, newsz, b->bufsz);
+}
+
+#ifdef nope
+static Chunk *
+merge(Chunk *left, Chunk *right)
+{
+ uchar *u;
+ Buf *l, *r;
+ Chunk *nc;
+
+ if(left == right)
+ return left;
+ l = left->b;
+ r = right->b;
+ assert(l != nil && r != nil);
+ nc = newbuf(left->len + right->len);
+ u = nc->b->buf;
+ memcpy(u, l->buf+left->off, left->len);
+ memcpy(u + left->len, r->buf+right->off, right->len);
+ linkchunk(left->left, nc);
+ unlink(left, right);
+ freechain(left);
+ return nc;
+}
+
+Chunk *
+mergedot(usize *off)
+{
+ Chunk *left, *right;
+
+ left = p2c(dot.from.pos, off);
+ right = p2c(dot.to.pos, nil);
+ if(left == right)
+ return left;
+ while(left->right != right)
+ left = merge(left, left->right);
+ return merge(left, right);
+}
+#endif
+
+Chunk *
+p2c(usize p, usize *off)
+{
+ int x;
+ Chunk *c;
+
+ for(c=norris, x=0; p>=c->len; c=c->right){
+ if(c == norris && ++x > 1){
+ c = norris->left;
+ break;
+ }
+ p -= c->len;
+ }
+ if(off != nil)
+ *off = p;
+ return c;
+}
+usize
+c2p(Chunk *tc)
+{
+ Chunk *c;
+ usize p;
+
+ for(p=0, c=norris; c!=tc; c=c->right)
+ p += c->len;
+ return p;
+}
+
+void
+recalcsize(void)
+{
+ int n;
+
+ n = c2p(norris->left) + norris->left->len;
+ if(dot.to.pos == totalsz || dot.to.pos > n)
+ dot.to.pos = n;
+ if(dot.pos < dot.from.pos || dot.pos > dot.to.pos)
+ dot.pos = dot.from.pos;
+ totalsz = n;
+}
+
+#define ASSERT(x) {if(!(x)) printchunks(norris); assert((x)); }
+void
+paranoia(int exact)
+{
+ usize n;
+ Chunk *c, *pc;
+ Buf *b;
+
+ ASSERT(dot.pos >= dot.from.pos && dot.pos < dot.to.pos);
+ for(pc=norris, n=pc->len, c=pc->right; c!=norris; pc=c, c=c->right){
+ b = c->b;
+ ASSERT(b != nil);
+ ASSERT((b->bufsz & 3) == 0 && b->bufsz >= Sampsz);
+ ASSERT(c->off < b->bufsz);
+ ASSERT(c->len > Sampsz);
+ ASSERT(c->off + c->len <= b->bufsz);
+ ASSERT(c->left == pc);
+ n += c->len;
+ }
+ if(exact){
+ ASSERT(n <= totalsz);
+ ASSERT(dot.to.pos <= totalsz);
+ }
+}
+#undef ASSERT
+
+void
+setdot(Dot *dot, Chunk *right)
+{
+ dot->from.pos = 0;
+ if(right == nil)
+ dot->to.pos = c2p(norris->left) + norris->left->len;
+ else
+ dot->to.pos = c2p(right);
+}
+
+Chunk *
+splitchunk(Chunk *c, usize off)
+{
+ Chunk *nc;
+
+ if(off == 0)
+ return c;
+ assert(off <= c->len);
+ nc = clonechunk(c);
+ nc->off = c->off + off;
+ nc->len = c->len - off;
+ c->len = off;
+ linkchunk(c, nc);
+ return nc;
+}
+
+/* c1 [nc … c2] nc */
+int
+splitrange(usize from, usize to, Chunk **left, Chunk **right)
+{
+ usize off;
+ Chunk *c;
+
+ c = p2c(from, &off);
+ if(off > 0){
+ splitchunk(c, off);
+ *left = c->right;
+ }else
+ *left = c; /* dangerous in combination with *right */
+ c = p2c(to, &off);
+ if(off < c->len - 1){
+ splitchunk(c, off);
+ *right = c;
+ }else
+ *right = c;
+ return 0;
+}
+
+Chunk *
+cutrange(usize from, usize to, Chunk **latch)
+{
+ Chunk *c, *left, *right;
+
+ if(splitrange(from, to, &left, &right) < 0)
+ return nil;
+ c = left->left;
+ if(left == norris)
+ norris = c;
+ unlink(left, right);
+ if(latch != nil)
+ *latch = left;
+ return c;
+}
+
+Chunk *
+croprange(usize from, usize to, Chunk **latch)
+{
+ Chunk *left, *right;
+
+ if(splitrange(from, to, &left, &right) < 0)
+ return nil;
+ norris = left;
+ *latch = right->right;
+ unlink(right->right, left->left);
+ return left;
+}
+
+// FIXME: generalized insert(from, to), where from and to not necessarily distinct
+Chunk *
+inserton(usize from, usize to, Chunk *c, Chunk **latch)
+{
+ Chunk *left;
+
+ left = cutrange(from, to, latch);
+ linkchunk(left, c);
+ return left;
+}
+
+Chunk *
+insertat(usize pos, Chunk *c)
+{
+ usize off;
+ Chunk *left;
+
+ if(pos == 0){
+ left = norris->left;
+ norris = c;
+ }else{
+ left = p2c(pos, &off);
+ splitchunk(left, off);
+ }
+ linkchunk(left, c);
+ return left;
+}
+
+uchar *
+getslice(Dot *d, usize n, usize *sz)
+{
+ usize Δbuf, Δloop, off;
+ Chunk *c;
+
+ if(d->pos >= totalsz){
+ werrstr("out of bounds");
+ *sz = 0;
+ return nil;
+ }
+ c = p2c(d->pos, &off);
+ Δloop = d->to.pos - d->pos;
+ Δbuf = c->len - off;
+ if(n < Δloop && n < Δbuf){
+ *sz = n;
+ d->pos += n;
+ }else if(Δloop <= Δbuf){
+ *sz = Δloop;
+ d->pos = d->from.pos;
+ }else{
+ *sz = Δbuf;
+ d->pos += Δbuf;
+ }
+ return c->b->buf + c->off + off;
+}
+
+Chunk *
+readintochunks(int fd)
+{
+ int n;
+ usize m;
+ Chunk *rc, *c, *nc;
+
+ for(m=0, rc=c=nil;; m+=n){
+ nc = newbuf(Iochunksz);
+ if(rc == nil)
+ rc = nc;
+ else
+ linkchunk(c, nc);
+ c = nc;
+ if((n = readn(fd, c->b->buf, Iochunksz)) < Iochunksz)
+ break;
+ yield();
+ }
+ close(fd);
+ if(n < 0)
+ fprint(2, "readintochunks: %r\n");
+ else if(n == 0){
+ if(c != rc)
+ unlink(c, c);
+ freechunk(c);
+ if(c == rc){
+ werrstr("readintochunks: nothing read");
+ return nil;
+ }
+ }else if(n > 0 && n < Iochunksz)
+ shrinkbuf(c, n);
+ return rc;
+}
+
+void
+graphfrom(Chunk *c)
+{
+ norris = c;
+ recalcsize();
+ setdot(&dot, nil);
+}
--- a/cmd.c
+++ b/cmd.c
@@ -6,154 +6,17 @@
Dot dot;
usize totalsz;
-static Chunk norris = {.left = &norris, .right = &norris};
-static Chunk *held;
-static uchar plentyofroom[Iochunksz];
-static int cutheld;
-static int epfd[2];
+int treadsoftly;
-static void
-printchunks(Chunk *r)
-{
- Chunk *c;
+// FIXME: undo/redo as an unbatched series of inserts and deletes
+// FIXME: crazy idea, multisnarf with addressable elements; $n registers; fork pplay to display them → ?
- fprint(2, "chunklist dot %zux %zux %zux: ",
- dot.from.pos, dot.pos, dot.to.pos);
- c = r;
- do{
- fprint(2, "%#p:%zux:←%#p→%#p - ", c, c->bufsz, c->left, c->right);
- assert(c->right->left == c);
- c = c->right;
- }while(c != r);
- fprint(2, "\n");
-}
+enum{
+ Nhold = 64,
+};
+static Chunk *hold[Nhold], *snarf;
+static int epfd[2];
-static void
-recalcsize(void)
-{
- int n;
- Chunk *c;
-
- for(c=norris.right, n=0; c!=&norris; c=c->right)
- n += c->bufsz;
- if(dot.to.pos == totalsz || n < totalsz && dot.to.pos > n)
- dot.to.pos = n;
- totalsz = n;
-}
-
-#define ASSERT(x) {if(!(x)) printchunks(&norris); assert((x)); }
-static void
-paranoia(int exact)
-{
- usize n;
- Chunk *c;
-
- ASSERT(dot.pos >= dot.from.pos && dot.pos < dot.to.pos);
- for(c=norris.right, n=0; c!=&norris; c=c->right){
- ASSERT(c->buf != nil);
- ASSERT((c->bufsz & 3) == 0 && c->bufsz >= Sampsz);
- n += c->bufsz;
- }
- if(exact){
- ASSERT(n <= totalsz);
- ASSERT(dot.to.pos <= totalsz);
- }
-}
-
-static Chunk *
-newchunk(usize n)
-{
- Chunk *c;
-
- assert((n & 3) == 0);
- c = emalloc(sizeof *c);
- c->bufsz = n;
- c->buf = emalloc(c->bufsz);
- c->left = c;
- c->right = c;
- return c;
-}
-
-static Chunk *
-clonechunk(void)
-{
- Chunk *c;
-
- assert(held != nil);
- c = newchunk(held->bufsz);
- memcpy(c->buf, held->buf, c->bufsz);
- return c;
-}
-
-static void
-freechunk(Chunk *c)
-{
- if(c == nil)
- return;
- free(c->buf);
- free(c);
-}
-
-static void
-linkchunk(Chunk *left, Chunk *c)
-{
- c->left->right = left->right;
- left->right->left = c->left;
- c->left = left;
- left->right = c;
-}
-
-static void
-unlinkchunk(Chunk *c)
-{
- c->left->right = c->right;
- c->right->left = c->left;
- c->left = c->right = nil;
-}
-
-static void
-resizechunk(Chunk *c, usize newsz)
-{
- vlong Δ;
-
- Δ = newsz - c->bufsz;
- c->buf = erealloc(c->buf, newsz, c->bufsz);
- c->bufsz = newsz;
- if(c->right == &norris && Δ < 0)
- dot.to.pos += Δ;
-}
-
-Chunk *
-p2c(usize p, usize *off)
-{
- Chunk *c;
-
- assert(p < totalsz);
- c = norris.right;
- while(p >= c->bufsz){
- p -= c->bufsz;
- c = c->right;
- }
- if(off != nil)
- *off = p;
- assert(c != &norris);
- return c;
-}
-static usize
-c2p(Chunk *tc)
-{
- Chunk *c;
- usize p;
-
- p = 0;
- c = norris.right;
- while(c != tc){
- p += c->bufsz;
- c = c->right;
- }
- return p;
-}
-
void
setrange(usize from, usize to)
{
@@ -187,319 +50,113 @@
}
static int
-holdchunk(Chunk *c, int cut)
+replace(char *, Chunk *c)
{
- if(held != nil){
- if(held == c)
- return 0;
- else if(cutheld)
- freechunk(held);
- }
- held = c;
- cutheld = cut;
- if(cut){
- unlinkchunk(c);
- setpos(dot.from.pos);
- }
- return 0;
-}
+ Chunk *left, *latch;
-static Chunk *
-merge(Chunk *left, Chunk *right)
-{
- usize Δ;
-
- assert(right != &norris);
- if(left->buf == nil || right->buf == nil){
- werrstr("can\'t merge self into void");
- return nil;
+ if(c == nil){
+ fprint(2, "replace: nothing to paste\n");
+ return -1;
}
- if(left->buf != right->buf){
- Δ = left->bufsz;
- resizechunk(left, left->bufsz + right->bufsz);
- memmove(left->buf + Δ, right->buf, right->bufsz);
- }else{
- right->buf = nil;
- left->bufsz += right->bufsz;
+ if((left = inserton(dot.from.pos, dot.to.pos, c, &latch)) == nil){
+ fprint(2, "insert: %r\n");
+ return -1;
}
- unlinkchunk(right);
- freechunk(right);
- return 0;
+ setdot(&dot, nil);
+ dot.pos = c2p(left->right);
+ return 1;
}
-static Chunk *
-splitright(Chunk *left, usize off)
+static int
+insert(char *, Chunk *c)
{
- usize Δ;
- Chunk *c;
+ Chunk *left;
- Δ = left->bufsz - off;
- if(off == 0 || Δ == 0)
- return left;
- c = newchunk(Δ);
- memcpy(c->buf, left->buf+off, Δ);
- resizechunk(left, off);
- linkchunk(left, c);
- return c;
-}
-
-static Chunk *
-mergedot(usize *off)
-{
- usize p;
- Chunk *c;
-
- c = p2c(dot.from.pos, &p);
- *off = p;
- p = dot.from.pos - p;
- while(p + c->bufsz < dot.to.pos)
- merge(c, c->right);
- return c;
-}
-
-/* before one may split oneself, one must first merge oneself */
-static Chunk *
-splitdot(void)
-{
- usize p;
- Chunk *c;
-
- c = mergedot(&p);
- splitright(c, p + dot.to.pos - dot.from.pos);
- return splitright(c, p);
-}
-
-uchar *
-getslice(Dot *d, usize n, usize *sz)
-{
- usize Δbuf, Δloop, off;
- Chunk *c;
-
- if(d->pos >= totalsz){
- werrstr("out of bounds");
- *sz = 0;
- return nil;
+ if(c == nil){
+ fprint(2, "insert: nothing to paste\n");
+ return -1;
}
- c = p2c(d->pos, &off);
- Δloop = d->to.pos - d->pos;
- Δbuf = c->bufsz - off;
- if(n < Δloop && n < Δbuf){
- *sz = n;
- d->pos += n;
- }else if(Δloop <= Δbuf){
- *sz = Δloop;
- d->pos = d->from.pos;
- }else{
- *sz = Δbuf;
- d->pos += Δbuf;
+ if((left = insertat(dot.pos, c)) == nil){
+ fprint(2, "insert: %r\n");
+ return -1;
}
- return c->buf + off;
+ setdot(&dot, nil);
+ dot.pos = c2p(left->right);
+ return 1;
}
-vlong
-getbuf(Dot d, usize n, uchar *buf, usize bufsz)
-{
- uchar *p, *b;
- usize sz;
-
- assert(d.pos < totalsz);
- assert(n <= bufsz);
- b = buf;
- while(n > 0){
- if((p = getslice(&d, n, &sz)) == nil || sz < Sampsz)
- return -1;
- memcpy(b, p, sz);
- b += sz;
- n -= sz;
- }
- return b - buf;
-}
-
-int
-advance(Dot *d, usize n)
-{
- usize m, sz;
-
- m = 0;
- while(n > 0){
- if(getslice(d, n, &sz) == nil)
- return -1;
- m += sz;
- n -= sz;
- }
- return m;
-}
-
static int
-insert(char *, Chunk *c)
+paste(char *s, Chunk *c)
{
- usize p;
- Chunk *left;
-
- if(c == nil && (c = clonechunk()) == nil){
- werrstr("insert: no buffer");
+ if(c == nil && (c = snarf) == nil){
+ werrstr("paste: no buffer");
return -1;
}
- left = p2c(dot.pos, &p);
- splitright(left, p);
- linkchunk(left, c);
- setrange(dot.pos, dot.pos + c->bufsz);
- return 1;
+ c = replicate(c, c->left);
+ if(dot.from.pos == 0 && dot.to.pos == totalsz)
+ return insert(s, c);
+ else
+ return replace(s, c);
}
static int
copy(char *)
{
- Chunk *c;
+ Chunk *c, *left, *right;
- c = splitdot();
- holdchunk(c, 0);
+ splitrange(dot.from.pos, dot.to.pos, &left, &right);
+ c = replicate(left, right);
+ freechain(snarf);
+ snarf = c;
return 0;
}
-static int
+static vlong
cut(char *)
{
- Chunk *c;
+ Chunk *latch;
if(dot.from.pos == 0 && dot.to.pos == totalsz){
werrstr("cut: no range selected");
return -1;
}
- c = splitdot();
- holdchunk(c, 1);
+ cutrange(dot.from.pos, dot.to.pos, &latch);
+ setdot(&dot, nil);
return 1;
}
static int
-replace(char *, Chunk *c)
-{
- Chunk *left, *right;
-
- if(c == nil && (c = clonechunk()) == nil){
- werrstr("replace: no buffer");
- return -1;
- }
- right = splitdot();
- left = right->left;
- unlinkchunk(right);
- freechunk(right);
- right = left->right;
- linkchunk(left, c);
- setrange(dot.from.pos, right != &norris ? c2p(right) : totalsz);
- return 1;
-}
-
-static int
-paste(char *s, Chunk *c)
-{
- if(dot.from.pos == 0 && dot.to.pos == totalsz)
- return insert(s, c);
- return replace(s, c);
-}
-
-static int
crop(char *)
{
- usize Δ;
- Chunk *c, *d;
+ Chunk *latch;
- Δ = 0;
- for(c=norris.right; c!=&norris; c=d){
- if(Δ + c->bufsz >= dot.from.pos)
- break;
- d = c->right;
- Δ += c->bufsz;
- unlinkchunk(c);
- freechunk(c);
- }
- dot.from.pos -= Δ;
- dot.to.pos -= Δ;
- if(dot.from.pos > 0){
- Δ = c->bufsz - dot.from.pos;
- memmove(c->buf, c->buf + dot.from.pos, Δ);
- resizechunk(c, Δ);
- dot.to.pos -= dot.from.pos;
- dot.from.pos = 0;
- }
- for(; c!=&norris; dot.to.pos-=c->bufsz, c=c->right)
- if(c->bufsz >= dot.to.pos)
- break;
- if(dot.to.pos > 0)
- resizechunk(c, dot.to.pos);
- for(c=c->right; c!=&norris; c=d){
- d = c->right;
- unlinkchunk(c);
- freechunk(c);
- }
+ if(croprange(dot.from.pos, dot.to.pos, &latch) == nil)
+ return -1;
+ setdot(&dot, nil);
dot.pos = 0;
- dot.to.pos = totalsz;
return 1;
}
-static int
-forcemerge(char *)
+vlong
+getbuf(Dot d, usize n, uchar *buf, usize bufsz)
{
- usize p;
+ uchar *p, *b;
+ usize sz;
- if(dot.from.pos == 0 && dot.to.pos == totalsz){
- werrstr("merge: won\'t implicitely merge entire buffer\n");
- return -1;
+ assert(d.pos < totalsz);
+ assert(n <= bufsz);
+ b = buf;
+ while(n > 0){
+ if((p = getslice(&d, n, &sz)) == nil || sz < Sampsz)
+ return -1;
+ memcpy(b, p, sz);
+ b += sz;
+ n -= sz;
}
- mergedot(&p);
- return 0;
+ return b - buf;
}
-static Chunk *
-readintochunks(int fd)
-{
- int n;
- usize m;
- Chunk *rc, *c, *nc;
-
- for(m=0, rc=c=nil;; m+=n){
- nc = newchunk(Iochunksz);
- if(rc == nil)
- rc = nc;
- else
- linkchunk(c, nc);
- c = nc;
- if((n = readn(fd, c->buf, Iochunksz)) < Iochunksz)
- break;
- yield();
- }
- close(fd);
- if(n < 0)
- fprint(2, "readintochunks: %r\n");
- else if(n == 0){
- if(c != rc)
- unlinkchunk(c);
- freechunk(c);
- if(c == rc){
- werrstr("readintochunks: nothing read");
- return nil;
- }
- }else if(n > 0 && n < Iochunksz)
- resizechunk(c, n);
- return rc;
-}
-
static int
-readfrom(char *s)
-{
- int fd;
- Chunk *c;
-
- if((fd = open(s, OREAD)) < 0)
- return -1;
- c = readintochunks(fd);
- close(fd);
- if(c == nil)
- return -1;
- return paste(nil, c);
-}
-
-static int
writebuf(int fd)
{
static uchar *buf;
@@ -534,6 +191,21 @@
return 0;
}
+int
+advance(Dot *d, usize n)
+{
+ usize m, sz;
+
+ m = 0;
+ while(n > 0){
+ if(getslice(d, n, &sz) == nil)
+ return -1;
+ m += sz;
+ n -= sz;
+ }
+ return m;
+}
+
static void
rc(void *s)
{
@@ -555,19 +227,28 @@
close(fd);
threadexits(nil);
}
+/* using a thread does slow down reads a bit */
+// FIXME: ugly
static void
rthread(void *efd)
{
int fd;
+ Dot d;
Chunk *c;
+ d = dot;
+ treadsoftly = 1;
fd = (intptr)efd;
if((c = readintochunks(fd)) == nil)
threadexits("failed reading from pipe: %r");
close(fd);
+ dot = d;
paste(nil, c);
+ dot.pos = dot.from.pos;
+ setdot(&dot, nil);
recalcsize();
redraw(0);
+ treadsoftly = 0;
threadexits(nil);
}
@@ -609,6 +290,20 @@
return pipeline(arg, 1, 1);
}
+static int
+readfrom(char *s)
+{
+ int fd;
+
+ if((fd = open(s, OREAD)) < 0)
+ return -1;
+ if(threadcreate(rthread, (int*)fd, mainstacksize) < 0){
+ fprint(2, "threadcreate: %r\n");
+ return -1;
+ }
+ return 0;
+}
+
/* the entire string is treated as the filename, ie.
* spaces and any other weird characters will be part
* of it */
@@ -657,7 +352,7 @@
case '|': x = pipeto(s); break;
case 'c': x = copy(s); break;
case 'd': x = cut(s); break;
- case 'm': x = forcemerge(s); break;
+// case 'm': x = forcemerge(s); break;
case 'p': x = paste(s, nil); break;
case 'q': threadexitsall(nil);
case 'r': x = readfrom(s); break;
@@ -678,9 +373,7 @@
if((c = readintochunks(fd)) == nil)
sysfatal("loadin: %r");
- linkchunk(&norris, c);
- recalcsize();
- setrange(0, totalsz);
+ graphfrom(c);
return 0;
}
--- a/dat.h
+++ b/dat.h
@@ -1,6 +1,7 @@
typedef struct Chunk Chunk;
typedef struct Pos Pos;
typedef struct Dot Dot;
+typedef struct Buf Buf;
enum{
Rate = 44100,
@@ -10,9 +11,11 @@
Outsz = WriteDelay * Sampsz,
Iochunksz = 4*1024*1024, /* ≈ 24 sec. at 44.1 kHz */
};
+#pragma incomplete Buf
struct Chunk{
- uchar *buf;
- usize bufsz;
+ Buf *b;
+ usize off;
+ usize len;
Chunk *left;
Chunk *right;
};
@@ -26,9 +29,11 @@
};
extern Dot dot;
extern usize totalsz;
+extern int treadsoftly;
extern int stereo;
extern int debug;
+extern int debugdraw;
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
--- a/draw.c
+++ b/draw.c
@@ -6,6 +6,7 @@
#include "fns.h"
QLock lsync;
+int debugdraw;
enum{
Cbg,
@@ -46,7 +47,7 @@
c = p2c(views, &off);
r = view->r;
- for(p=views-off; p<viewe; p+=c->bufsz, c=c->right){
+ for(p=views-off; p<viewe; p+=c->len, c=c->right){
if(p == 0)
continue;
x = (p - views) / T;
@@ -56,7 +57,7 @@
r.min.x += x;
r.max.x = r.min.x + 1;
draw(view, r, col[Cchunk], nil, ZP);
- if(c->bufsz == 0)
+ if(c->len == 0)
break;
}
}
@@ -195,7 +196,7 @@
r.max.x = r.min.x + 1;
draw(view, r, col[Cloop], nil, ZP);
}
- if(debug)
+ if(debugdraw)
drawchunks();
}
--- a/fns.h
+++ b/fns.h
@@ -1,3 +1,15 @@
+void freechain(Chunk*);
+void recalcsize(void);
+void paranoia(int);
+void setdot(Dot*, Chunk*);
+Chunk* replicate(Chunk*, Chunk*);
+int splitrange(usize, usize, Chunk**, Chunk**);
+void graphfrom(Chunk*);
+Chunk* inserton(usize, usize, Chunk*, Chunk**);
+Chunk* insertat(usize, Chunk*);
+Chunk* croprange(usize, usize, Chunk**);
+Chunk* cutrange(usize, usize, Chunk**);
+Chunk* readintochunks(int);
int cmd(char*);
void initcmd(void);
void update(void);
@@ -12,6 +24,7 @@
int advance(Dot*, usize);
int jump(usize);
Chunk* p2c(usize, usize*);
+usize c2p(Chunk*);
void setrange(usize, usize);
int setpos(usize);
uchar* getslice(Dot*, usize, usize*);
--- a/mkfile
+++ b/mkfile
@@ -3,6 +3,7 @@
MAN=/sys/man/1
TARG=pplay
OFILES=\
+ chunk.$O\
cmd.$O\
draw.$O\
pplay.$O\
--- a/pplay.c
+++ b/pplay.c
@@ -91,7 +91,7 @@
Rune r;
ARGBEGIN{
- case 'D': debug = 1; break;
+ case 'D': debug = 1; debugdraw = 1; break;
case 'c': cat = 1; break;
case 's': stereo = 1; break;
default: usage();
@@ -141,6 +141,7 @@
switch(r){
case Kdel:
case 'q': threadexitsall(nil);
+ case 'D': debugdraw ^= 1; break;
case ' ': toggleplay(); break;
case 'b': setjump(dot.from.pos); break;
case Kesc: setrange(0, totalsz); update(); break;
@@ -151,6 +152,10 @@
case '_': setzoom(-1, 1); break;
case '+': setzoom(1, 1); break;
default:
+ if(treadsoftly){
+ fprint(2, "dropping edit event during ongoing read\n");
+ break;
+ }
if((p = prompt(r)) == nil || strlen(p) == 0)
break;
qlock(&lsync);