piece-chain

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

buf.c (raw) (5252B)


   1 #include "buf.h"
   2 
   3 #include <stdio.h>
   4 #include <stdlib.h>
   5 #include <string.h>
   6 #include <errno.h>
   7 
   8 #define CATCH(condition, msg, action) \
   9 	if (condition) { perror(msg); action; }
  10 
  11 static Piece *pieceblk = NULL;
  12 static Piece **freeblk = NULL;
  13 
  14 static void pclean()
  15 {
  16 	if (freeblk) free(freeblk);
  17 	if (pieceblk) free(pieceblk);
  18 }
  19 
  20 /* adds `p` to `freeblk` and returns the last added `freeblk` item.
  21 	If `p` is NULL, the last added `freeblk` item will be
  22 	returned, or NULL if `freeblk` is empty.
  23 	`freeblk` is dynamically allocated. */
  24 static Piece *pfree(Piece *p)
  25 {
  26 	static size_t nblk = 0, nfree = 0, blksiz = 0;
  27 	static const size_t allocsiz = BUFSIZ/sizeof(Piece *);
  28 
  29 	if (!p) return (nfree == 0) ? NULL : freeblk[--nfree];
  30 
  31 	if (nfree + 1 > allocsiz * nblk) {
  32 		blksiz = (++nblk * allocsiz) * sizeof(Piece *);
  33 		freeblk = realloc(freeblk, blksiz);
  34 	}
  35 	CATCH(!freeblk, "failed to allocate freeblk",
  36 		exit(EXIT_FAILURE));
  37 
  38 	return freeblk[nfree++] = p;
  39 }
  40 
  41 /* returns the next available `Piece` item in `pieceblk`.
  42 	If `freeblk` is not empty, it's last item pointed to by it
  43 	will be returned (recycling `pieceblk` items). The returned
  44 	`Piece` will always have it's values set to 0.
  45 	`pieceblk` is dynamically allocated. */
  46 static Piece *palloc()
  47 {
  48 	Piece *p;
  49 	
  50 	static size_t nblk = 0, nalloc = 0, blksiz = 0;
  51 	static const size_t allocsiz = BUFSIZ/sizeof(Piece);
  52 
  53 	static int hook = 0;
  54 
  55 	if ( !(p = pfree(NULL)) ) {
  56 		if (nalloc + 1 > allocsiz * nblk) {
  57 			blksiz = (++nblk * allocsiz) * sizeof(Piece);
  58 			pieceblk = realloc(pieceblk, blksiz);
  59 		}
  60 		CATCH(!pieceblk, "failed to allocate pieceblk",
  61 			exit(EXIT_FAILURE));
  62 		if (hook == 0) hook = !atexit(pclean);
  63 		
  64 		p = &pieceblk[nalloc++];
  65 	}
  66 
  67 	return memset(p, 0, sizeof(Piece));
  68 }
  69 
  70 /* splits `p` into two `Piece` items. The values of `p` will be
  71 	modified to fit the first `Piece`; a new `Piece` will be
  72 	allocated for the second.
  73 	The `prev` and `next` values of these & associated `Piece`
  74 	items will be updated to reflect the split.
  75 	`offset` should be the offset within `p->len` to split at.
  76 	If offset is not within the boundry of `p->len`, then `p`
  77 	will be returned. The *first* `Piece` in the split is
  78 	returned. */
  79 static Piece *psplit(Piece *p, long int offset)
  80 {
  81 	Piece *q = p;
  82 	if (offset > 0 && offset <= (int)p->len) {
  83 		q = palloc();
  84 		memcpy(q, p, sizeof(Piece));
  85 		q->off += offset;
  86 		q->len -= p->len = offset;
  87 		q->next = p->next;
  88 		q->prev = p;
  89 		p->next = q;
  90 		if (q->next) q->next->prev = q;
  91 	} else if (offset == 0) {
  92 		q = palloc();
  93 		q->next = p;
  94 		p->prev = q;
  95 	}
  96 	return q;
  97 }
  98 
  99 /* pfind will find the `Piece` which contains index `pos`.
 100 	The search starts from `p`, where the index is `idx`.
 101 	`p` will be set to the found `Piece`.
 102 	The new index will be returned. */
 103 static size_t pfind(Piece **ptr, size_t idx, size_t pos)
 104 {
 105 	Piece *p = *ptr;
 106 	if (pos >= idx) {
 107 		while (pos >= idx + p->len && p->next) {
 108 			idx += p->len;
 109 			p = p->next;
 110 		}
 111 	} else {
 112 		do {
 113 			p = p->prev;
 114 			idx -= p->len;
 115 		} while (pos < idx && p->prev);
 116 	}
 117 	*ptr = p;
 118 	return idx;
 119 }
 120 
 121 struct Buf *bufinit(FILE *read, FILE *append)
 122 {
 123 	Buf *b = calloc(1, sizeof(Buf));
 124 
 125 	if (!append) return NULL;
 126 	
 127 	b->read = read;
 128 	b->append = append;
 129 	
 130 	b->tail = b->pos = b->head = palloc();
 131 	b->pos->f = b->read;
 132 	fseek(b->read, 0, SEEK_END);
 133 	b->size = b->pos->len = ftell(b->read);
 134 
 135 	return b;
 136 }
 137 
 138 void buffree(Buf *b)
 139 {
 140 	Piece *p = b->tail->next;
 141 	while (p) {
 142 		pfree(p->prev);
 143 		p = p->next;
 144 	}
 145 	free(b);
 146 }
 147 
 148 Piece *bufidx(Buf *b, size_t pos)
 149 {
 150 	size_t offset, idx = pfind(&b->pos, b->idx, pos);
 151 
 152 	if (idx != pos || idx == 0) {
 153 		offset = (idx >= pos) ? idx - pos : pos - idx;
 154 		b->pos = psplit(b->pos, offset);
 155 		if (!b->pos->prev) b->tail = b->pos;
 156 		if (!b->pos->next) b->head = b->pos;
 157 	}
 158 	
 159 	b->idx = pos;
 160 	return b->pos;
 161 }
 162 
 163 size_t bufins(Buf *b, size_t pos, const char *s)
 164 {
 165 	const size_t slen = strlen(s);
 166 	Piece *p;
 167 
 168 	b->pos = bufidx(b, pos)->prev;
 169 	if (!b->pos) b->pos = b->tail;
 170 	
 171 	p = palloc();
 172 	p->f = b->append;
 173 	p->off = ftell(b->append);
 174 	p->len = slen;
 175 	p->prev = b->pos;
 176 	p->next = b->pos->next;
 177 
 178 	b->pos->next = b->pos->next->prev = p;
 179 
 180 	fprintf(b->append, "%s", s);
 181 	CATCH(ferror(b->append), "bufins: write to append",
 182 		{ pfree(p); p = 0; });
 183 
 184 	if (p != 0) {
 185 		b->idx += slen;
 186 		b->size += slen;
 187 		b->pos = p->next;
 188 	}
 189 
 190 	return b->size;
 191 }
 192 
 193 size_t bufdel(Buf *b, size_t pos, int num)
 194 {
 195 	size_t tmp;
 196 	Piece *pre, *post;
 197 
 198 	if (num < 0) {
 199 		if ((int)pos-num < 0)
 200 			num = pos;
 201 		else {
 202 			tmp = abs(num);
 203 			num = pos;
 204 			pos = (tmp > pos) ? 0 : tmp;
 205 		}
 206 	}
 207 
 208 	if (pos+num > b->size) num = b->size - pos;
 209 	
 210 	pre = bufidx(b, pos)->prev;
 211 	if (!pre) pre = b->tail;
 212 	post = bufidx(b, pos+num);
 213 
 214 	pre->next = post;
 215 	post->prev = pre;
 216 
 217 	b->idx = pos;
 218 	return (b->size -= num);
 219 }
 220 
 221 size_t bufout(Buf *b, FILE *f)
 222 {
 223 	size_t n, fsiz = 0;
 224 	char buf[BUFSIZ];
 225 	Piece *p = b->tail;
 226 
 227 	do {
 228 		if (p->len > 0 && p->f) {
 229 			if ((size_t)ftell(p->f) != p->off)
 230 			fseek(p->f, p->off, SEEK_SET);
 231 		
 232 			n = 0;
 233 			do {
 234 				buf[0] = '\0';
 235 				fread(buf, 1, p->len, p->f);
 236 				CATCH(ferror(p->f), "bufout: fread failed", break);
 237 				fsiz += fwrite(buf, 1, p->len, f);
 238 				CATCH(ferror(f), "bufout: fwrite failed", break);
 239 			} while (p->len - (BUFSIZ*n++) > BUFSIZ);
 240 		}
 241 		p = p->next;
 242 	} while(p);
 243 
 244 	return fsiz;
 245 }