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 }