piece-chain

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

buf.c (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 }