amp

pcm player/editor for plan9
git clone git://nsmpr.xyz/amp.git
Log | Files | Refs | README

commit ba7b99c16f3b734f1d1cb4facb194b57aececd20
parent a9913cd82a64eed6a26093917b88d3512df337fd
Author: glenda <glenda@kobeni>
Date:   Sat, 17 Feb 2024 23:52:43 +0000

fs: add partial fcut support

Diffstat:
Mfs.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mpages.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mpages.h | 33+++++++++++++++++++++++++++++----
3 files changed, 188 insertions(+), 33 deletions(-)

diff --git a/fs.c b/fs.c @@ -22,13 +22,14 @@ void fs_open(Req *r); void fs_read(Req *r); void fs_write(Req *r); +void fcut_write(Req *r); + void normalize_sel(void); void update_selstr(void); Srv ampsrv = { .read = fs_read, .write = fs_write, - //.create = fs_create, .open = fs_open, }; @@ -75,11 +76,14 @@ fs_open(Req *r) if ((mode & OTRUNC) == 0) goto end; if (file == fctl) goto end; if (file == fcut) { + // if needed split pages at 'min' and 'max' in two + // remove pages from min to max rstr = "trunc not implemented yet"; goto end; } if (file == fdata) { pg.length = 0; + fdata->length = 0; // TODO: free pages ??? // probably better to do it on fs_close() goto end; @@ -121,9 +125,12 @@ fs_write(Req *r) r->ofcall.count = pbwrite(&pg, r->ifcall.data, r->ifcall.count, r->ifcall.offset); if (pg.length > fdata->length) fdata->length = pg.length; + update_selstr(); respond(r, nil); } else if (r->fid->file == fcut) { - respond(r, "fcut nope"); + fcut_write(r); + update_selstr(); + respond(r, nil); } else if (r->fid->file == fctl) { int newmin, newmax; char *np, *rp; @@ -155,6 +162,64 @@ fs_write(Req *r) } void +fcut_write(Req *r) +{ + Page *maxpage; + vlong min, max, offset; + long count, d; + min = sel.min * FrameSize; + max = sel.max * FrameSize; + + // find page that 'max' lands on + offset = 0; + maxpage = pg.start; + while ((maxpage != nil) && (offset + maxpage->as->len < max)) { + offset += maxpage->as->len; + maxpage = maxpage->next; + } + if (maxpage == nil) { + maxpage = addpage(&pg); + } + d = max - offset; + if (d > 0) { + // split page in two + Page *s = duppage(maxpage); + s->as->len = d; + s->next = maxpage; + if (s->prev != nil) s->prev->next = s; + else pg.start = s; + maxpage->as->len -= d; + maxpage->as->cap -= d; + maxpage->as->p += d; + maxpage->prev = s; + } + // insert more pages as needed + offset = r->ifcall.offset + min; + count = r->ifcall.count; + while (offset + count > max) { + long n = offset + count - max; + if (n > PageSize) n = PageSize; + Page *ins = allocpage(); + ins->as->len = n; + ins->next = maxpage; + ins->prev = maxpage->prev; + if (maxpage->prev != nil) maxpage->prev->next = ins; + else { + pg.start = ins; + } + maxpage->prev = ins; + max += n; + pg.length += n; + } + sel.max = max / FrameSize; + normalize_sel(); + fcut->length = (sel.max - sel.min) * FrameSize; + fdata->length = pg.length; + // write to pagebuffer as usual + r->ofcall.count = pbwrite(&pg, r->ifcall.data, count, offset); +} + +void normalize_sel(void) { vlong bufmax = pg.length / FrameSize; diff --git a/pages.c b/pages.c @@ -3,42 +3,106 @@ #include "pages.h" +Array * +allocarray(long len) +{ + Array *new = malloc(sizeof(ArHeader) + len); + assert(new != nil); + new->len = len; + new->ref = 0; + return new; +} + +ArSlice * +allocarslice(Array *ar, long start, long len) +{ + ArSlice *new = malloc(sizeof(ArSlice)); + new->ar = ar; + new->p = ar->p + start; + new->len = len; + new->cap = ar->len - start; + assert(start + len <= ar->len); + return new; +} + Page * allocpage(void) { - Page *new = mallocz(sizeof(Page), 1); - new->count = PageSize; - new->buf = mallocz(PageSize, 1); + Array *ar = allocarray(PageSize); + memset(ar->p, '?', PageSize); + ArSlice *as = allocarslice(ar, 0, PageSize); + ar->ref++; + Page *new = malloc(sizeof(Page)); + new->as = as; + new->prev = nil; + new->next = nil; + return new; +} + +Page * +duppage(Page *old) +{ + Page *new = malloc(sizeof(Page)); + ArSlice *as = malloc(sizeof(ArSlice)); + new->as = as; + new->prev = old->prev; + new->next = old->next; + as->ar = old->as->ar; + as->p = old->as->p; + as->len = old->as->len; + as->cap = old->as->cap; + as->ar->ref++; return new; } +void +freepage(Page *pg) +{ + pg->as->ar->ref--; + if (pg->as->ar->ref <= 0) { + free(pg->as->ar); + } + free(pg->as); + free(pg); +} + long pbwrite(PBuf *pb, void *buf, long nbytes, vlong offset) { Page *pgpt; - vlong page_offset = 0; long n, nwritten = 0; + + // TODO: move this section to fs.c/fwrite/fdata + // or create separate pbgrow() function + // also, last page's len should be shortened accordingly + // [[[ while (offset + nbytes > pb->size) { addpage(pb); } - pgpt = pb->start; if (pb->length < offset + nbytes) pb->length = offset + nbytes; + // ]]] + + pgpt = pb->start; + if (offset >= pb->length) nbytes = 0; + if (offset + nbytes >= pb->length) { + nbytes = (pb->length - offset); + } while (nbytes > 0) { - assert(page_offset <= offset); - if (page_offset + pgpt->count > offset) { - n = page_offset + pgpt->count - offset; - if (n > nbytes) n = nbytes; - memcpy(pgpt->buf, buf, n); - nwritten += n; - buf = (char *)buf + n; - offset +=n; - nbytes -= n; + if (pgpt == nil) { + break; } - page_offset += pgpt->count; - pgpt = pgpt->next; - } - if (offset > pb->size) { - pb->size = offset; + if (offset >= pgpt->as->len) { + offset -= pgpt->as->len; + pgpt = pgpt->next; + continue; + } + n = nbytes; + if (pgpt->as->len - offset < n) n = pgpt->as->len - offset; + memcpy(pgpt->as->p + offset, buf, n); + nwritten += n; + buf = (char *)buf + n; + offset += n; + nbytes -= n; } return nwritten; } @@ -56,14 +120,14 @@ pbread(PBuf *pb, void *buf, long nbytes, vlong offset) if (pgpt == nil) { break; } - if (offset >= pgpt->count) { - offset -= pgpt->count; + if (offset >= pgpt->as->len) { + offset -= pgpt->as->len; pgpt = pgpt->next; continue; } n = nbytes; - if (pgpt->count - offset < n) n = pgpt->count - offset; - memcpy(buf, pgpt->buf + offset, n); + if (pgpt->as->len - offset < n) n = pgpt->as->len - offset; + memcpy(buf, pgpt->as->p + offset, n); nread += n; buf = (char *)buf + n; offset += n; @@ -76,13 +140,14 @@ Page * addpage(PBuf *pb) { Page *new = allocpage(); - if (pb->start == nil) pb->start = new; - if (pb->end == nil) pb->end = new; - else { + if ((pb->start == nil) || (pb->end == nil)) { + pb->start = new; + pb->end = new; + } else { new->prev = pb->end; pb->end->next = new; pb->end = new; } - pb->size += new->count; + pb->size += new->as->len; return new; } diff --git a/pages.h b/pages.h @@ -5,15 +5,34 @@ enum { }; typedef struct Page Page; +typedef struct Array Array; +typedef struct ArHeader ArHeader; +typedef struct ArSlice ArSlice; + +struct ArHeader { + int ref; + long len; +}; + +struct Array { + ArHeader; + char p[1]; + /* I don't think plan9 C allows + * zero-length arrays */ +}; + +struct ArSlice { + Array *ar; + char *p; + long len, cap; +}; + struct Page { + ArSlice *as; Page *prev; Page *next; - vlong count; - char *buf; }; -Page *allocpage(void); - typedef struct PBuf PBuf; struct PBuf { Page *start; @@ -26,4 +45,10 @@ struct PBuf { long pbwrite(PBuf *pb, void *buf, long nbytes, vlong offset); long pbread(PBuf *pb, void *buf, long nbytes, vlong offset); +Array * allocarray(long); +ArSlice * allocarslice(Array *, long, long); + +Page * allocpage(void); +Page * duppage(Page *); +void freepage(Page *); Page * addpage(PBuf *pb);