commit 89b16fb6fa73c0d2b5b014258213c46bc6fd7b88
parent 2f3ea34be6b3b10dcf43e89941a6a35394c3c2af
Author: gearsix <gearsix@tuta.io>
Date: Sat, 9 Jul 2022 23:18:19 +0100
cleanup; added some doc, CATCH macro to buf.c; removed undo, redo.
Diffstat:
M | Makefile | | | 5 | +++-- |
D | buf.c | | | 212 | ------------------------------------------------------------------------------- |
D | buf.h | | | 44 | -------------------------------------------- |
A | buffer.c | | | 220 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | buffer.h | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 5 | +++++ |
M | test.c | | | 162 | ++----------------------------------------------------------------------------- |
A | test.h | | | 2 | ++ |
A | test_buffer.c | | | 182 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
9 files changed, 470 insertions(+), 417 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,8 +1,9 @@
-SRC := buf.c
+CC := gcc
+SRC := buffer.c
OUT := ec
OPTS := -g -Wall -Wpedantic -std=c89
all:
${CC} $(OPTS) $(SRC) main.c -o $(OUT)
test:
- ${CC} $(OPTS) $(SRC) test.c -o $(OUT)-test
+ ${CC} $(OPTS) $(SRC) test*c -o $(OUT)-test
diff --git a/buf.c b/buf.c
@@ -1,212 +0,0 @@
-
-#include "buf.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-static Piece *pieceblk = NULL;
-static Piece **freeblk = NULL;
-
-static void pclean()
-{
- free(freeblk);
- free(pieceblk);
-}
-
-/* adds `p` to `freeblk` and returns the last added `freeblk` item.
- If `p` is NULL, the last added `freeblk` item will be returned,
- or NULL if `freeblk` is empty.
- `freeblk` is dynamically allocated. */
-static Piece *pfree(Piece *p)
-{
- static size_t nblk = 0, nfree = 0;
- static const size_t blksiz = BUFSIZ/sizeof(Piece *);
-
- if (!p) return (nfree == 0) ? NULL : freeblk[--nfree];
-
- if (nfree + 1 > blksiz * nblk)
- freeblk = realloc(freeblk, (++nblk * blksiz) * sizeof(Piece *));
- if (!freeblk) { perror("failed to allocate freeblk"); exit(1); }
-
- return freeblk[nfree++] = p;
-}
-
-/* returns the next available `Piece` item in `pieceblk`.
- If `freeblk` is not empty, it's last item pointed to by it will be
- returned (recycling `pieceblk` items). The returned `Piece` will
- always have it's values set to 0. `pieceblk` is dynamically
- allocated.
- `atexit(pclean)` is called the first time the function is called. */
-static Piece *palloc()
-{
- Piece *p;
-
- static size_t nblk = 0, nalloc = 0;
- static const size_t blksiz = BUFSIZ/sizeof(Piece);
-
- static int hook = 0;
- if (!hook) { atexit(pclean); hook = 1; }
-
- if ( !(p = pfree(NULL)) ) {
- if (nalloc + 1 > blksiz * nblk)
- pieceblk = realloc(pieceblk, (++nblk * blksiz) * sizeof(Piece));
- if (!pieceblk) { perror("failed to allocate pieceblk"); exit(1); }
- p = &pieceblk[nalloc++];
- }
-
- return memset(p, 0, sizeof(Piece));
-}
-
-/* splits `p` into two `Piece` items. The values of `p` will be
- modified to fit the first `Piece`; a new `Piece` will be allocated
- for the second.
- The `prev` and `next` items of these & associated `Piece` items will
- be updated to reflect the new `Piece`.
- `offset` should be the offset within `p->len` to split at. If offset
- is not within the boundry of `p->len`, then `p` will be returned.
- The *first* `Piece` in the split is returned. */
-static Piece *psplit(Piece *p, long int offset)
-{
- Piece *q = palloc();
- if (offset > 0 && offset <= (int)p->len) {
- memcpy(q, p, sizeof(Piece));
-
- q->off += offset;
- q->len -= p->len = offset;
- q->next = p->next;
- q->prev = p;
- p->next = q;
- if (q->next) q->next->prev = q;
-
- p = q;
- }
- return p;
-}
-
-struct Buf *bufinit(FILE *read, FILE *append)
-{
- Buf *b = calloc(1, sizeof(Buf));
-
- if (!append) return NULL;
-
- b->read = read;
- b->append = append;
-
- b->tail = b->pos = b->head = palloc();
- b->pos->f = b->read;
- fseek(b->read, 0, SEEK_END);
- b->size = b->pos->len = ftell(b->read);
-
- return b;
-}
-
-void buffree(Buf *b)
-{
- Piece *p = b->tail->next;
- while (p) {
- pfree(p->prev);
- p = p->next;
- }
- free(b);
-}
-
-Piece *bufidx(Buf *b, size_t pos)
-{
- size_t idx = b->idx, offset;
- Piece *p = b->pos;
-
- if (pos >= idx) {
- while (pos >= idx + p->len && p->next) {
- idx += p->len;
- p = p->next;
- }
- } else {
- do {
- p = p->prev;
- idx -= p->len;
- } while(pos > idx && p->prev);
- }
-
- if (idx == pos) {
- b->idx = idx;
- b->pos = p;
- } else {
- offset = (idx >= pos) ? idx - pos : pos - idx;
- b->pos = psplit(p, offset);
- if (!b->pos->next) b->head = b->pos;
- b->idx = pos;
- }
-
- return b->pos;
-}
-
-size_t bufins(Buf *b, size_t pos, const char *s)
-{
- const size_t slen = strlen(s);
- Piece *p = palloc();
-
- b->pos = bufidx(b, pos)->prev;
-
- p->f = b->append;
- p->off = ftell(b->append);
- p->len = slen;
- p->prev = b->pos;
- p->next = b->pos->next;
-
- b->pos->next = b->pos->next->prev = p;
-
- fprintf(b->append, "%s", s);
- if (ferror(b->append)) {
- perror("failed to write to append file");
- pfree(p);
- p = 0;
- } else {
- b->idx += slen;
- b->pos = p->next;
- }
-
- return (p == 0) ? b->size : (b->size += slen);
-}
-
-size_t bufdel(Buf *b, size_t pos, size_t num)
-{
- Piece *pre, *post;
- size_t end = pos+num;
-
- pre = bufidx(b, (pos == 0) ? 0 : pos-1);
- post = bufidx(b, (end > b->size) ? b->size : end)->next;
- if (!post) post = b->head;
-
- pre->next = post;
- post->prev = pre;
-
- b->idx = pos;
- return (b->size -= num);
-}
-
-int bufout(Buf *b, FILE *f)
-{
- size_t n, fsiz = 0;
- char buf[BUFSIZ];
- Piece *p = b->tail;
-
- do {
- if ((size_t)ftell(p->f) != p->off)
- fseek(p->f, p->off, SEEK_SET);
-
- n = 0;
- do {
- buf[0] = '\0';
- fread(buf, 1, p->len, p->f);
- if (ferror(p->f)) { perror("bufout: fread failed"); break; }
- fsiz += fwrite(buf, 1, p->len, f);
- if (ferror(f)) { perror("bufout: fwrite failed"); break; }
- } while (p->len - (BUFSIZ*n++) > BUFSIZ);
-
- p = p->next;
- } while(p);
-
- return fsiz;
-}
diff --git a/buf.h b/buf.h
@@ -1,44 +0,0 @@
-/* TODO */
-#include <stdio.h>
-
-/* Points to a file (`f`), which contains a string which
- starts within `f` at `off` and has a length of `len`.
- `prev` and `next` point to the previous and next
- `Piece` elements in the intended double-linked list. */
-typedef struct Piece {
- FILE *f;
- size_t off, len;
-
- struct Piece *prev, *next;
-} Piece;
-
-typedef struct Buf {
- FILE *read, *append;
- size_t size, idx;
- struct Piece *tail, *pos, *head;
-} Buf;
-
-/* Allocates & initialises a `Buf`. If `append` is NULL,
- nothing is allocated or initialised and NULL is
- returned. */
-Buf *bufinit(FILE *read, FILE *append);
-
-/* Frees `b` and all `Piece` items associated with it. */
-void buffree(Buf *b);
-
-/* Set `b->idx` to `pos` and `b->pos` to the `Piece`
- in the chain, where the start of `b->pos->next`
- is `pos`. */
-Piece *bufidx(Buf *b, size_t pos);
-
-/* Adds a new `Piece` to the chain, at `pos` (found
- using `bufidx`). `s` will be appended to `b->append`
- and the new `Piece` will reflect the appended data. */
-size_t bufins(Buf *b, size_t pos, const char *s);
-
-/* Removed all pieces from index `pos` to `pos+num`.
- `pos` and `pos+num` are found using `bufidx`. */
-size_t bufdel(Buf *b, size_t pos, size_t num);
-
-/* writes all data in `b` to `f`. */
-int bufout(Buf *b, FILE *f);
diff --git a/buffer.c b/buffer.c
@@ -0,0 +1,220 @@
+
+#include "buffer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define CATCH(condition, msg, action) \
+ if (condition) { perror(msg); action; }
+
+static Piece *pieceblk = NULL;
+static Piece **freeblk = NULL;
+
+static void pclean()
+{
+ if (freeblk) free(freeblk);
+ if (pieceblk) free(pieceblk);
+}
+
+/* adds `p` to `freeblk` and returns the last added `freeblk` item.
+ If `p` is NULL, the last added `freeblk` item will be
+ returned, or NULL if `freeblk` is empty.
+ `freeblk` is dynamically allocated. */
+static Piece *pfree(Piece *p)
+{
+ static size_t nblk = 0, nfree = 0, blksiz = 0;
+ static const size_t allocsiz = BUFSIZ/sizeof(Piece *);
+
+ if (!p) return (nfree == 0) ? NULL : freeblk[--nfree];
+
+ if (nfree + 1 > allocsiz * nblk) {
+ blksiz = (++nblk * allocsiz) * sizeof(Piece *);
+ freeblk = realloc(freeblk, blksiz);
+ }
+ CATCH(!freeblk, "failed to allocate freeblk",
+ exit(EXIT_FAILURE));
+
+ return freeblk[nfree++] = p;
+}
+
+/* returns the next available `Piece` item in `pieceblk`.
+ If `freeblk` is not empty, it's last item pointed to by it
+ will be returned (recycling `pieceblk` items). The returned
+ `Piece` will always have it's values set to 0.
+ `pieceblk` is dynamically allocated. */
+static Piece *palloc()
+{
+ Piece *p;
+
+ static size_t nblk = 0, nalloc = 0, blksiz = 0;
+ static const size_t allocsiz = BUFSIZ/sizeof(Piece);
+
+ static int hook = 0;
+
+ if ( !(p = pfree(NULL)) ) {
+ if (nalloc + 1 > allocsiz * nblk) {
+ blksiz = (++nblk * allocsiz) * sizeof(Piece);
+ pieceblk = realloc(pieceblk, blksiz);
+ }
+ CATCH(!pieceblk, "failed to allocate pieceblk",
+ exit(EXIT_FAILURE));
+ if (hook == 0) hook = !atexit(pclean);
+
+ p = &pieceblk[nalloc++];
+ }
+
+ return memset(p, 0, sizeof(Piece));
+}
+
+/* splits `p` into two `Piece` items. The values of `p` will be
+ modified to fit the first `Piece`; a new `Piece` will be
+ allocated for the second.
+ The `prev` and `next` values of these & associated `Piece`
+ items will be updated to reflect the split.
+ `offset` should be the offset within `p->len` to split at.
+ If offset is not within the boundry of `p->len`, then `p`
+ will be returned. The *first* `Piece` in the split is
+ returned. */
+static Piece *psplit(Piece *p, long int offset)
+{
+ Piece *q = palloc();
+ if (offset > 0 && offset <= (int)p->len) {
+ memcpy(q, p, sizeof(Piece));
+
+ q->off += offset;
+ q->len -= p->len = offset;
+ q->next = p->next;
+ q->prev = p;
+ p->next = q;
+ if (q->next) q->next->prev = q;
+
+ p = q;
+ }
+ return p;
+}
+
+struct Buffer *bufinit(FILE *read, FILE *append)
+{
+ Buffer *b = calloc(1, sizeof(Buffer));
+
+ if (!append) return NULL;
+
+ b->read = read;
+ b->append = append;
+
+ b->tail = b->pos = b->head = palloc();
+ b->pos->f = b->read;
+ fseek(b->read, 0, SEEK_END);
+ b->size = b->pos->len = ftell(b->read);
+
+ return b;
+}
+
+void buffree(Buffer *b)
+{
+ Piece *p = b->tail->next;
+ while (p) {
+ pfree(p->prev);
+ p = p->next;
+ }
+ free(b);
+}
+
+Piece *bufidx(Buffer *b, size_t pos)
+{
+ size_t idx = b->idx, offset;
+ Piece *p = b->pos;
+
+ if (pos >= idx) {
+ while (pos >= idx + p->len && p->next) {
+ idx += p->len;
+ p = p->next;
+ }
+ } else {
+ do {
+ p = p->prev;
+ idx -= p->len;
+ } while(pos > idx && p->prev);
+ }
+
+ if (idx == pos) {
+ b->idx = idx;
+ b->pos = p;
+ } else {
+ offset = (idx >= pos) ? idx - pos : pos - idx;
+ b->pos = psplit(p, offset);
+ if (!b->pos->next) b->head = b->pos;
+ b->idx = pos;
+ }
+
+ return b->pos;
+}
+
+size_t bufins(Buffer *b, size_t pos, const char *s)
+{
+ const size_t slen = strlen(s);
+ Piece *p = palloc();
+
+ b->pos = bufidx(b, pos)->prev;
+
+ p->f = b->append;
+ p->off = ftell(b->append);
+ p->len = slen;
+ p->prev = b->pos;
+ p->next = b->pos->next;
+
+ b->pos->next = b->pos->next->prev = p;
+
+ fprintf(b->append, "%s", s);
+ CATCH(ferror(b->append), "bufins: write to append",
+ { pfree(p); p = 0; });
+ if (p != 0) {
+ b->idx += slen;
+ b->pos = p->next;
+ }
+
+ return (p == 0) ? b->size : (b->size += slen);
+}
+
+size_t bufdel(Buffer *b, size_t pos, size_t num)
+{
+ Piece *pre, *post;
+ size_t end = pos+num;
+
+ pre = bufidx(b, (pos == 0) ? 0 : pos-1);
+ post = bufidx(b, (end > b->size) ? b->size : end)->next;
+ if (!post) post = b->head;
+
+ pre->next = post;
+ post->prev = pre;
+
+ b->idx = pos;
+ return (b->size -= num);
+}
+
+int bufout(Buffer *b, FILE *f)
+{
+ size_t n, fsiz = 0;
+ char buf[BUFSIZ];
+ Piece *p = b->tail;
+
+ do {
+ if ((size_t)ftell(p->f) != p->off)
+ fseek(p->f, p->off, SEEK_SET);
+
+ n = 0;
+ do {
+ buf[0] = '\0';
+ fread(buf, 1, p->len, p->f);
+ CATCH(ferror(p->f), "bufout: fread failed", break);
+ fsiz += fwrite(buf, 1, p->len, f);
+ CATCH(ferror(f), "bufout: fwrite failed", break);
+ } while (p->len - (BUFSIZ*n++) > BUFSIZ);
+
+ p = p->next;
+ } while(p);
+
+ return fsiz;
+}
diff --git a/buffer.h b/buffer.h
@@ -0,0 +1,55 @@
+/* A piece chain implementation.
+ gearsix, 2022 */
+#include <stdio.h>
+
+/* Points to a file (`f`), which contains a string which starts
+ within `f` at `off` and has a length of `len`.
+ `prev` and `next` point to the previous and next `Piece`
+ elements in the intended doubly-linked list. */
+typedef struct Piece {
+ FILE *f;
+ size_t off, len;
+ struct Piece *prev, *next;
+} Piece;
+
+/* Holds a doubly-linked `Piece` list, starting at `tail` and ending
+ at `head`. `pos` points to the last addressed `Piece`.
+ `size` is the sum length of all `Piece` `len` values in the
+ chain. `idx` is the last addressed index in the chain.
+ `read` and `append` point to the original file (`read`) and
+ any data to be added `append`. */
+typedef struct Buffer {
+ FILE *read, *append;
+ size_t size, idx;
+ struct Piece *tail, *pos, *head;
+} Buffer;
+
+/* Allocates & initialises a `Buffer`. If `append` is NULL, nothing is
+ allocated or initialised and NULL is returned. */
+Buffer *
+bufinit(FILE *read, FILE *append);
+
+/* Frees `b` and all `Piece` items associated with it. */
+void
+buffree(Buffer *b);
+
+/* Set `b->idx` to `pos` and `b->pos` to the `Piece` in the chain,
+ where the start of `b->pos->next` is `pos`. */
+Piece *
+bufidx(Buffer *b, size_t pos);
+
+/* Adds a new `Piece` to the chain, at `pos` (found using `bufidx`).
+ `s` will be appended to `b->append` and the new `Piece` will
+ reflect the appended data. */
+size_t
+bufins(Buffer *b, size_t pos, const char *s);
+
+/* Removed all pieces from index `pos` to `pos+num`.
+ `pos` and `pos+num` are found using `bufidx`. */
+size_t
+bufdel(Buffer *b, size_t pos, size_t num);
+
+/* writes all data in `b` to `f`. */
+int
+bufout(Buffer *b, FILE *f);
+
diff --git a/main.c b/main.c
@@ -0,0 +1,5 @@
+
+int main()
+{
+ return 0;
+}
diff --git a/test.c b/test.c
@@ -1,167 +1,11 @@
-#include "buf.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#define INFILE "in.txt"
-#define INBUF "hello world"
-#define OUTFILE "out.txt"
-#define OUTBUF "hey buddy!" /* tests will aim to change INBUF to this */
-
-Buf *b;
-FILE *in, *tmp, *out;
-
-void setup()
-{
- in = fopen(INFILE, "w+b");
- if (ferror(in)) perror("fopen in.txt");
- fputs(INBUF, in);
-
- tmp = tmpfile();
-
- out = fopen(OUTFILE, "w+b");
- if (ferror(in)) perror("fopen out.txt");
-
- return;
-}
-
-void setdown()
-{
- buffree(b);
- fclose(out);
- fclose(in);
- fclose(tmp);
-}
-
-void print()
-{
- Piece *p = b->tail;
-
- printf("Buf: %p\n", (void *)b);
- printf("size: %lu, index: %lu\n", b->size, b->idx);
- do {
- printf("%p", (void *)p);
- if (p == b->tail) printf("<-tail");
- if (p == b->pos) printf("<-pos");
- if (p == b->head) printf("<-head");
- puts("");
- printf("\tf: %s, off: %lu, len %lu\n",
- (p->f == b->read) ? "read" : "append", p->off, p->len);
- } while ((p = p->next));
- printf("\n");
- fflush(stdout);
-}
-
-void test_bufinit()
-{
- Piece *p;
-
- b = bufinit(in, tmp);
-
- assert(b->read);
- assert(b->append);
- assert(b->pos == b->tail);
- assert(b->pos == b->head);
-
- assert((p = b->pos));
- assert(p->f == b->read);
- assert(p->off == 0);
- assert(p->len == strlen(INBUF));
- assert(p->prev == NULL);
- assert(p->next == NULL);
-}
-
-void test_bufidx()
-{
- Piece *p; size_t idx = 5;
-
- p = bufidx(b, idx);
+#include "test.h"
- assert(p == b->pos);
- assert(p == b->head);
- assert(p->prev == b->tail);
- assert(p->f == b->read);
- assert(p->off == idx);
- assert(p->len == strlen(INBUF) - idx);
-}
-
-void test_bufins()
-{
- const char *buf = "y";
- const size_t idx = 2, len = strlen(buf);
-
- bufins(b, idx, buf);
-
- assert(b->size == strlen(INBUF) + len);
- assert(b->idx == idx + len);
- assert(b->pos == b->tail->next->next);
-
- assert(b->pos->f == b->read);
- assert(b->pos->off == 2);
- assert(b->pos->len == 3);
- assert(b->pos->prev == b->tail->next);
- assert(b->pos->next == b->head);
-}
-
-void test_bufdel()
-{
- const size_t idx = 3, len = 9;
-
- bufdel(b, idx, len);
-
- assert(b->size == strlen(INBUF)+1-len); /* +1 for bufins */
- assert(b->idx == idx);
- assert(b->pos == b->head);
- assert(b->tail->next->next == b->pos);
-
- assert(b->pos->f == b->read);
- assert(b->pos->off == 11);
- assert(b->pos->len == 0);
- assert(b->pos->prev == b->tail->next);
- assert(b->pos->next == NULL);
-}
-
-void test_bufins1()
-{
- const char *buf = " buddy!";
- const size_t idx = 3, len = strlen(buf), bsiz = b->size;
-
- bufins(b, idx, buf);
-
- assert(b->size == bsiz + len);
- assert(b->idx == idx + len);
- assert(b->pos == b->head);
- assert(b->pos == b->tail->next->next->next);
-
- assert(b->pos->prev->f == b->append);
- assert(b->pos->prev->off == 1);
- assert(b->pos->prev->len == len);
- assert(b->pos->prev == b->tail->next->next);
- assert(b->pos->next == NULL);
-}
-
-void test_bufout()
-{
- char buf[BUFSIZ] = {0};
-
- bufout(b, out);
+#include <stdio.h>
- rewind(out);
- assert(fread(buf, 1, BUFSIZ, out) > 0);
- assert(strcmp(buf, OUTBUF) == 0);
-}
int main()
{
- setup();
- test_bufinit();
- test_bufidx();
- test_bufins();
- test_bufdel();
- test_bufins1();
- test_bufout();
- setdown();
+ test_buffer();
puts("success - no assertions failed");
return 0;
}
diff --git a/test.h b/test.h
@@ -0,0 +1,2 @@
+
+void test_buffer();
diff --git a/test_buffer.c b/test_buffer.c
@@ -0,0 +1,182 @@
+#include "test.h"
+#include "buffer.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+static Buffer *B;
+
+static FILE *in_f;
+static const char *in_p = "in.txt", *in_buf = "hello world";
+static FILE *out_f;
+static const char *out_p = "out.txt", *out_buf = "hey buddy!";
+static FILE *tmp_f;
+static const char *tmp_p = "tmp.txt";
+
+
+static void setup();
+static void setdown();
+static void test_bufinit();
+static void test_bufidx();
+static void test_bufins();
+static void test_bufdel();
+static void test_bufins1();
+static void test_bufout();
+
+
+void test_buffer()
+{
+ printf("test_buffer: ");
+ setup();
+ test_bufinit();
+ test_bufidx();
+ test_bufins();
+ test_bufdel();
+ test_bufins1();
+ test_bufout();
+ setdown();
+ puts("success");
+}
+
+static void debugprint()
+{
+ Piece *p = B->tail;
+
+ printf("Buf: %p\n", (void *)B);
+ printf("size: %d, index: %d\n", (int)B->size, (int)B->idx);
+ do {
+ printf("%p", (void *)p);
+ if (p == B->tail) printf("<-tail");
+ if (p == B->head) printf("<-head");
+ if (p == B->pos) printf("<-pos");
+ printf("\n\toff: %d, len %d, f: %s\n", (int)p->off, (int)p->len,
+ (!p->f) ? "0" : (p->f == B->read) ? "read" : "append");
+ } while ((p = p->next));
+ printf("\n");
+}
+
+static void setup()
+{
+ in_f = fopen(in_p, "w+b");
+ if (ferror(in_f)) perror("fopen in");
+ fputs(in_buf, in_f);
+
+ out_f = fopen(out_p, "w+b");
+ if (ferror(out_f)) perror("fopen out");
+
+ tmp_f = fopen(tmp_p, "w+b");
+ if (ferror(tmp_f)) perror("fopen tmp");
+}
+
+static void setdown()
+{
+ buffree(B);
+ fclose(out_f);
+ fclose(in_f);
+ fclose(tmp_f);
+ remove(in_p);
+ remove(out_p);
+ remove(tmp_p);
+}
+
+static void test_bufinit()
+{
+ Piece *p;
+
+ B = bufinit(in_f, tmp_f);
+
+ assert(B->read);
+ assert(B->append);
+ assert(B->pos == B->tail);
+ assert(B->pos == B->head);
+
+ assert((p = B->pos));
+ assert(p->f == B->read);
+ assert(p->off == 0);
+ assert(p->len == strlen(in_buf));
+ assert(p->prev == NULL);
+ assert(p->next == NULL);
+}
+
+static void test_bufidx()
+{
+ Piece *p; size_t idx = 5;
+
+ p = bufidx(B, idx);
+
+ assert(p == B->pos);
+ assert(p == B->head);
+ assert(p->prev == B->tail);
+ assert(p->f == B->read);
+ assert(p->off == idx);
+ assert(p->len == strlen(in_buf) - idx);
+}
+
+static void test_bufins()
+{
+ const char *buf = "y";
+ const size_t idx = 2, len = strlen(buf);
+
+ bufins(B, idx, buf);
+
+ assert(B->size == strlen(in_buf) + len);
+ assert(B->idx == idx + len);
+ assert(B->pos == B->tail->next->next);
+
+ assert(B->pos->f == B->read);
+ assert(B->pos->off == 2);
+ assert(B->pos->len == 3);
+ assert(B->pos->prev == B->tail->next);
+ assert(B->pos->next == B->head);
+}
+
+static void test_bufdel()
+{
+ const size_t idx = 3, len = 9;
+
+ bufdel(B, idx, len);
+
+ assert(B->size == strlen(in_buf)+1-len); /* +1 for bufins */
+ assert(B->idx == idx);
+ assert(B->pos == B->head);
+ assert(B->tail->next->next == B->pos);
+
+ assert(B->pos->f == B->read);
+ assert(B->pos->off == 11);
+ assert(B->pos->len == 0);
+ assert(B->pos->prev == B->tail->next);
+ assert(B->pos->next == NULL);
+}
+
+static void test_bufins1()
+{
+ const char *buf = " buddy!";
+ const size_t idx = 3, len = strlen(buf), bsiz = B->size;
+
+ bufins(B, idx, buf);
+
+ assert(B->size == bsiz + len);
+ assert(B->idx == idx + len);
+ assert(B->pos == B->head);
+ assert(B->pos == B->tail->next->next->next);
+
+ assert(B->pos->prev->f == B->append);
+ assert(B->pos->prev->off == 1);
+ assert(B->pos->prev->len == len);
+ assert(B->pos->prev == B->tail->next->next);
+ assert(B->pos->next == NULL);
+}
+
+static void test_bufout()
+{
+ size_t n = 0;
+ char buf[BUFSIZ] = {0};
+
+ bufout(B, out_f);
+ rewind(out_f);
+ n = fread(buf, 1, BUFSIZ, out_f);
+
+ assert(n > 0);
+ assert(strcmp(buf, out_buf) == 0);
+}