commit ba7b99c16f3b734f1d1cb4facb194b57aececd20
parent a9913cd82a64eed6a26093917b88d3512df337fd
Author: glenda <glenda@kobeni>
Date: Sat, 17 Feb 2024 23:52:43 +0000
fs: add partial fcut support
Diffstat:
M | fs.c | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | pages.c | | | 119 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
M | pages.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);