piece-chain

Research & implementation of a Piece Chain
git clone git://src.gearsix.net/piece-chain.git
Log | Files | Refs | Atom | README

commit 89596b21383f11e9939ae97fbcfee92b99b03130
parent b13471343e63a9c034bde2a7d7f9476f07e28f42
Author: gearsix <gearsix@tuta.io>
Date:   Wed, 20 Jul 2022 11:50:33 +0100

final cleanup

Diffstat:
MMakefile | 12++++++------
Abuf.c | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuf.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dbuffer.c | 245-------------------------------------------------------------------------------
Dbuffer.h | 67-------------------------------------------------------------------
Mtest.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Dtest.h | 2--
Dtest_buffer.c | 233-------------------------------------------------------------------------------
8 files changed, 544 insertions(+), 557 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,12 +1,12 @@ CC := gcc -SRC := buffer.c -OUT := ec -OPTS := -g -Wall -Wpedantic -std=c89 +SRC := buf.c +OUT := piecetable +OPTS := -g -Wall -Wpedantic + +all: $(static) -all: - ${CC} $(OPTS) $(SRC) main.c -o $(OUT) static: ${CC} $(OPTS) -c $(SRC) -o $(OUT).o ar rcs lib$(OUT).a $(OUT).o 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 @@ -0,0 +1,245 @@ +#include "buf.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 = p; + if (offset > 0 && offset <= (int)p->len) { + q = palloc(); + 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; + } else if (offset == 0) { + q = palloc(); + q->next = p; + p->prev = q; + } + return q; +} + +/* pfind will find the `Piece` which contains index `pos`. + The search starts from `p`, where the index is `idx`. + `p` will be set to the found `Piece`. + The new index will be returned. */ +static size_t pfind(Piece **ptr, size_t idx, size_t pos) +{ + Piece *p = *ptr; + 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); + } + *ptr = p; + return idx; +} + +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 offset, idx = pfind(&b->pos, b->idx, pos); + + if (idx != pos || idx == 0) { + offset = (idx >= pos) ? idx - pos : pos - idx; + b->pos = psplit(b->pos, offset); + if (!b->pos->prev) b->tail = b->pos; + 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; + + b->pos = bufidx(b, pos)->prev; + if (!b->pos) b->pos = b->tail; + + p = palloc(); + 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->size += slen; + b->pos = p->next; + } + + return b->size; +} + +size_t bufdel(Buf *b, size_t pos, int num) +{ + size_t tmp; + Piece *pre, *post; + + if (num < 0) { + if ((int)pos-num < 0) + num = pos; + else { + tmp = abs(num); + num = pos; + pos = (tmp > pos) ? 0 : tmp; + } + } + + if (pos+num > b->size) num = b->size - pos; + + pre = bufidx(b, pos)->prev; + if (!pre) pre = b->tail; + post = bufidx(b, pos+num); + + pre->next = post; + post->prev = pre; + + b->idx = pos; + return (b->size -= num); +} + +size_t bufout(Buf *b, FILE *f) +{ + size_t n, fsiz = 0; + char buf[BUFSIZ]; + Piece *p = b->tail; + + do { + if (p->len > 0 && p->f) { + 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/buf.h b/buf.h @@ -0,0 +1,67 @@ +/* A piece chain implementation. + gearsix, 2022 */ +#ifndef PIECETABLE +#define PIECETABLE + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <stdio.h> + +/* Points to a file (`f`), which contains a string that starts at + `off`, with length of `len`. `prev` and `next` point to the + previous and next items in the table. */ +typedef struct Piece { + FILE *f; + size_t off, len; + struct Piece *prev, *next; +} Piece; + +/* Holds a doubly-linked `Piece` list, starting at `tail`, ending at + `head`. `pos` points to the last addressed `Piece`. `size` is the + sum length of all `len` values of all `Piece` items in the list. + `idx` is the last addressed index in the chain. `read` and `append` + point to the original file and any data to be added. */ +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, int num); + +/* writes all data in `b` to `f`. */ +size_t +bufout(Buf *b, FILE *f); + + +#ifdef __cplusplus +} +#endif + +#endif /* PIECETABLE */ diff --git a/buffer.c b/buffer.c @@ -1,245 +0,0 @@ -#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 = p; - if (offset > 0 && offset <= (int)p->len) { - q = palloc(); - 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; - } else if (offset == 0) { - q = palloc(); - q->next = p; - p->prev = q; - } - return q; -} - -/* pfind will find the `Piece` which contains index `pos`. - The search starts from `p`, where the index is `idx`. - `p` will be set to the found `Piece`. - The new index will be returned. */ -static size_t pfind(Piece **ptr, size_t idx, size_t pos) -{ - Piece *p = *ptr; - 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); - } - *ptr = p; - return idx; -} - -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 offset, idx = pfind(&b->pos, b->idx, pos); - - if (idx != pos || idx == 0) { - offset = (idx >= pos) ? idx - pos : pos - idx; - b->pos = psplit(b->pos, offset); - if (!b->pos->prev) b->tail = b->pos; - 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; - - b->pos = bufidx(b, pos)->prev; - if (!b->pos) b->pos = b->tail; - - p = palloc(); - 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->size += slen; - b->pos = p->next; - } - - return b->size; -} - -size_t bufdel(Buffer *b, size_t pos, int num) -{ - size_t tmp; - Piece *pre, *post; - - if (num < 0) { - if ((int)pos-num < 0) - num = pos; - else { - tmp = abs(num); - num = pos; - pos = (tmp > pos) ? 0 : tmp; - } - } - - if (pos+num > b->size) num = b->size - pos; - - pre = bufidx(b, pos)->prev; - if (!pre) pre = b->tail; - post = bufidx(b, pos+num); - - 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 (p->len > 0 && p->f) { - 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 @@ -1,67 +0,0 @@ -/* A piece chain implementation. - gearsix, 2022 */ -#ifndef ECBUF -#define ECBUF - -#ifdef __cplusplus -extern "C" { -#endif - -#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, int num); - -/* writes all data in `b` to `f`. */ -int -bufout(Buffer *b, FILE *f); - -#ifdef __cplusplus -} -#endif - -#endif /* ECBUF */ diff --git a/test.c b/test.c @@ -1,11 +1,233 @@ -#include "test.h" + +#include "buf.h" #include <stdio.h> +#include <signal.h> +#include <string.h> +#include <assert.h> + +static Buf *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_bufdel1(); +static void test_bufins2(); +static void test_bufout(); int main() { - test_buffer(); - puts("no assertions failed"); - return 0; + printf("test: "); + setup(); + signal(SIGABRT, setdown); + test_bufinit(); + test_bufidx(); + test_bufins(); + test_bufdel(); + test_bufins1(); + test_bufdel1(); + test_bufins2(); + test_bufout(); + setdown(0); + puts("success"); +} + +static void debugprint() +{ + Piece *p = B->tail; + + printf("\nBuf: %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(int n) +{ + buffree(B); + fclose(out_f); + fclose(in_f); + fclose(tmp_f); + if (n != SIGABRT) { + 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"; size_t idx = 2; + const size_t 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() +{ + size_t idx = 3; int len = B->size+1; + const size_t bsiz = B->size; + + bufdel(B, idx, len); + + assert(B->size == bsiz-(bsiz-idx)); + 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() +{ + size_t idx = 3; const char *buf = " buddy!"; + const size_t 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_bufdel1() +{ + size_t idx = 0; int len = 1; + const size_t bsiz = B->size; + + bufdel(B, idx, len); + + assert(B->size == bsiz - len); + assert(B->idx == idx); + + assert(B->pos->prev == B->tail); + assert(B->pos->prev->f == 0); + assert(B->pos->prev->off == 0); + assert(B->pos->prev->len == 0); + assert(B->pos->prev->prev == 0); + assert(B->pos->prev->next == B->pos); + + assert(B->pos->f == B->read); + assert(B->pos->off == 1); + assert(B->pos->len == (size_t)len); + assert(B->pos->prev == B->tail); + assert(B->pos->next->next->next == B->head); +} + +static void test_bufins2() +{ + const char *buf = "H"; const size_t idx = 0; + size_t len = strlen(buf), bsiz = B->size; + + bufins(B, idx, buf); + + assert(B->size == bsiz + len); + assert(B->idx == idx + len); + assert(B->pos == B->tail->next->next); + + assert(B->pos->prev == B->tail->next); + assert(B->pos->prev->f == B->append); + assert(B->pos->prev->off == 8); + assert(B->pos->prev->len == 1); + assert(B->pos->prev->prev == B->tail); + assert(B->pos->prev->next == B->pos); +} + +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); } diff --git a/test.h b/test.h @@ -1,2 +0,0 @@ - -void test_buffer(); diff --git a/test_buffer.c b/test_buffer.c @@ -1,233 +0,0 @@ -#include "test.h" -#include "buffer.h" - -#include <stdio.h> -#include <signal.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_bufdel1(); -static void test_bufins2(); -static void test_bufout(); - - -void test_buffer() -{ - printf("test_buffer: "); - setup(); - signal(SIGABRT, setdown); - test_bufinit(); - test_bufidx(); - test_bufins(); - test_bufdel(); - test_bufins1(); - test_bufdel1(); - test_bufins2(); - test_bufout(); - setdown(0); - puts("success"); -} - -static void debugprint() -{ - Piece *p = B->tail; - - printf("\nBuf: %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(int n) -{ - buffree(B); - fclose(out_f); - fclose(in_f); - fclose(tmp_f); - if (n != SIGABRT) { - 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"; size_t idx = 2; - const size_t 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() -{ - size_t idx = 3; int len = B->size+1; - const size_t bsiz = B->size; - - bufdel(B, idx, len); - - assert(B->size == bsiz-(bsiz-idx)); - 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() -{ - size_t idx = 3; const char *buf = " buddy!"; - const size_t 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_bufdel1() -{ - size_t idx = 0; int len = 1; - const size_t bsiz = B->size; - - bufdel(B, idx, len); - - assert(B->size == bsiz - len); - assert(B->idx == idx); - - assert(B->pos->prev == B->tail); - assert(B->pos->prev->f == 0); - assert(B->pos->prev->off == 0); - assert(B->pos->prev->len == 0); - assert(B->pos->prev->prev == 0); - assert(B->pos->prev->next == B->pos); - - assert(B->pos->f == B->read); - assert(B->pos->off == 1); - assert(B->pos->len == (size_t)len); - assert(B->pos->prev == B->tail); - assert(B->pos->next->next->next == B->head); -} - -static void test_bufins2() -{ - const char *buf = "H"; const size_t idx = 0; - size_t len = strlen(buf), bsiz = B->size; - - bufins(B, idx, buf); - - assert(B->size == bsiz + len); - assert(B->idx == idx + len); - assert(B->pos == B->tail->next->next); - - assert(B->pos->prev == B->tail->next); - assert(B->pos->prev->f == B->append); - assert(B->pos->prev->off == 8); - assert(B->pos->prev->len == 1); - assert(B->pos->prev->prev == B->tail); - assert(B->pos->prev->next == B->pos); -} - -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); -}