| buff.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
| git clone git://git.suckless.org/9base | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| buff.c (5170B) | |
| --- | |
| 1 #include "sam.h" | |
| 2 | |
| 3 enum | |
| 4 { | |
| 5 Slop = 100 /* room to grow with reallocation */ | |
| 6 }; | |
| 7 | |
| 8 static | |
| 9 void | |
| 10 sizecache(Buffer *b, uint n) | |
| 11 { | |
| 12 if(n <= b->cmax) | |
| 13 return; | |
| 14 b->cmax = n+Slop; | |
| 15 b->c = runerealloc(b->c, b->cmax); | |
| 16 } | |
| 17 | |
| 18 static | |
| 19 void | |
| 20 addblock(Buffer *b, uint i, uint n) | |
| 21 { | |
| 22 if(i > b->nbl) | |
| 23 panic("internal error: addblock"); | |
| 24 | |
| 25 b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]); | |
| 26 if(i < b->nbl) | |
| 27 memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*)); | |
| 28 b->bl[i] = disknewblock(disk, n); | |
| 29 b->nbl++; | |
| 30 } | |
| 31 | |
| 32 | |
| 33 static | |
| 34 void | |
| 35 delblock(Buffer *b, uint i) | |
| 36 { | |
| 37 if(i >= b->nbl) | |
| 38 panic("internal error: delblock"); | |
| 39 | |
| 40 diskrelease(disk, b->bl[i]); | |
| 41 b->nbl--; | |
| 42 if(i < b->nbl) | |
| 43 memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*)); | |
| 44 b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]); | |
| 45 } | |
| 46 | |
| 47 /* | |
| 48 * Move cache so b->cq <= q0 < b->cq+b->cnc. | |
| 49 * If at very end, q0 will fall on end of cache block. | |
| 50 */ | |
| 51 | |
| 52 static | |
| 53 void | |
| 54 flush(Buffer *b) | |
| 55 { | |
| 56 if(b->cdirty || b->cnc==0){ | |
| 57 if(b->cnc == 0) | |
| 58 delblock(b, b->cbi); | |
| 59 else | |
| 60 diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); | |
| 61 b->cdirty = FALSE; | |
| 62 } | |
| 63 } | |
| 64 | |
| 65 static | |
| 66 void | |
| 67 setcache(Buffer *b, uint q0) | |
| 68 { | |
| 69 Block **blp, *bl; | |
| 70 uint i, q; | |
| 71 | |
| 72 if(q0 > b->nc) | |
| 73 panic("internal error: setcache"); | |
| 74 /* | |
| 75 * flush and reload if q0 is not in cache. | |
| 76 */ | |
| 77 if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc)) | |
| 78 return; | |
| 79 /* | |
| 80 * if q0 is at end of file and end of cache, continue to grow th… | |
| 81 */ | |
| 82 if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<=Maxblock) | |
| 83 return; | |
| 84 flush(b); | |
| 85 /* find block */ | |
| 86 if(q0 < b->cq){ | |
| 87 q = 0; | |
| 88 i = 0; | |
| 89 }else{ | |
| 90 q = b->cq; | |
| 91 i = b->cbi; | |
| 92 } | |
| 93 blp = &b->bl[i]; | |
| 94 while(q+(*blp)->u.n <= q0 && q+(*blp)->u.n < b->nc){ | |
| 95 q += (*blp)->u.n; | |
| 96 i++; | |
| 97 blp++; | |
| 98 if(i >= b->nbl) | |
| 99 panic("block not found"); | |
| 100 } | |
| 101 bl = *blp; | |
| 102 /* remember position */ | |
| 103 b->cbi = i; | |
| 104 b->cq = q; | |
| 105 sizecache(b, bl->u.n); | |
| 106 b->cnc = bl->u.n; | |
| 107 /*read block*/ | |
| 108 diskread(disk, bl, b->c, b->cnc); | |
| 109 } | |
| 110 | |
| 111 void | |
| 112 bufinsert(Buffer *b, uint q0, Rune *s, uint n) | |
| 113 { | |
| 114 uint i, m, t, off; | |
| 115 | |
| 116 if(q0 > b->nc) | |
| 117 panic("internal error: bufinsert"); | |
| 118 | |
| 119 while(n > 0){ | |
| 120 setcache(b, q0); | |
| 121 off = q0-b->cq; | |
| 122 if(b->cnc+n <= Maxblock){ | |
| 123 /* Everything fits in one block. */ | |
| 124 t = b->cnc+n; | |
| 125 m = n; | |
| 126 if(b->bl == nil){ /* allocate */ | |
| 127 if(b->cnc != 0) | |
| 128 panic("internal error: bufinsert… | |
| 129 addblock(b, 0, t); | |
| 130 b->cbi = 0; | |
| 131 } | |
| 132 sizecache(b, t); | |
| 133 runemove(b->c+off+m, b->c+off, b->cnc-off); | |
| 134 runemove(b->c+off, s, m); | |
| 135 b->cnc = t; | |
| 136 goto Tail; | |
| 137 } | |
| 138 /* | |
| 139 * We must make a new block. If q0 is at | |
| 140 * the very beginning or end of this block, | |
| 141 * just make a new block and fill it. | |
| 142 */ | |
| 143 if(q0==b->cq || q0==b->cq+b->cnc){ | |
| 144 if(b->cdirty) | |
| 145 flush(b); | |
| 146 m = min(n, Maxblock); | |
| 147 if(b->bl == nil){ /* allocate */ | |
| 148 if(b->cnc != 0) | |
| 149 panic("internal error: bufinsert… | |
| 150 i = 0; | |
| 151 }else{ | |
| 152 i = b->cbi; | |
| 153 if(q0 > b->cq) | |
| 154 i++; | |
| 155 } | |
| 156 addblock(b, i, m); | |
| 157 sizecache(b, m); | |
| 158 runemove(b->c, s, m); | |
| 159 b->cq = q0; | |
| 160 b->cbi = i; | |
| 161 b->cnc = m; | |
| 162 goto Tail; | |
| 163 } | |
| 164 /* | |
| 165 * Split the block; cut off the right side and | |
| 166 * let go of it. | |
| 167 */ | |
| 168 m = b->cnc-off; | |
| 169 if(m > 0){ | |
| 170 i = b->cbi+1; | |
| 171 addblock(b, i, m); | |
| 172 diskwrite(disk, &b->bl[i], b->c+off, m); | |
| 173 b->cnc -= m; | |
| 174 } | |
| 175 /* | |
| 176 * Now at end of block. Take as much input | |
| 177 * as possible and tack it on end of block. | |
| 178 */ | |
| 179 m = min(n, Maxblock-b->cnc); | |
| 180 sizecache(b, b->cnc+m); | |
| 181 runemove(b->c+b->cnc, s, m); | |
| 182 b->cnc += m; | |
| 183 Tail: | |
| 184 b->nc += m; | |
| 185 q0 += m; | |
| 186 s += m; | |
| 187 n -= m; | |
| 188 b->cdirty = TRUE; | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 void | |
| 193 bufdelete(Buffer *b, uint q0, uint q1) | |
| 194 { | |
| 195 uint m, n, off; | |
| 196 | |
| 197 if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) | |
| 198 panic("internal error: bufdelete"); | |
| 199 while(q1 > q0){ | |
| 200 setcache(b, q0); | |
| 201 off = q0-b->cq; | |
| 202 if(q1 > b->cq+b->cnc) | |
| 203 n = b->cnc - off; | |
| 204 else | |
| 205 n = q1-q0; | |
| 206 m = b->cnc - (off+n); | |
| 207 if(m > 0) | |
| 208 runemove(b->c+off, b->c+off+n, m); | |
| 209 b->cnc -= n; | |
| 210 b->cdirty = TRUE; | |
| 211 q1 -= n; | |
| 212 b->nc -= n; | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 uint | |
| 217 bufload(Buffer *b, uint q0, int fd, int *nulls) | |
| 218 { | |
| 219 char *p; | |
| 220 Rune *r; | |
| 221 int l, m, n, nb, nr; | |
| 222 uint q1; | |
| 223 | |
| 224 if(q0 > b->nc) | |
| 225 panic("internal error: bufload"); | |
| 226 p = malloc((Maxblock+UTFmax+1)*sizeof p[0]); | |
| 227 if(p == nil) | |
| 228 panic("bufload: malloc failed"); | |
| 229 r = runemalloc(Maxblock); | |
| 230 m = 0; | |
| 231 n = 1; | |
| 232 q1 = q0; | |
| 233 /* | |
| 234 * At top of loop, may have m bytes left over from | |
| 235 * last pass, possibly representing a partial rune. | |
| 236 */ | |
| 237 while(n > 0){ | |
| 238 n = read(fd, p+m, Maxblock); | |
| 239 if(n < 0){ | |
| 240 error(Ebufload); | |
| 241 break; | |
| 242 } | |
| 243 m += n; | |
| 244 p[m] = 0; | |
| 245 l = m; | |
| 246 if(n > 0) | |
| 247 l -= UTFmax; | |
| 248 cvttorunes(p, l, r, &nb, &nr, nulls); | |
| 249 memmove(p, p+nb, m-nb); | |
| 250 m -= nb; | |
| 251 bufinsert(b, q1, r, nr); | |
| 252 q1 += nr; | |
| 253 } | |
| 254 free(p); | |
| 255 free(r); | |
| 256 return q1-q0; | |
| 257 } | |
| 258 | |
| 259 void | |
| 260 bufread(Buffer *b, uint q0, Rune *s, uint n) | |
| 261 { | |
| 262 uint m; | |
| 263 | |
| 264 if(!(q0<=b->nc && q0+n<=b->nc)) | |
| 265 panic("bufread: internal error"); | |
| 266 | |
| 267 while(n > 0){ | |
| 268 setcache(b, q0); | |
| 269 m = min(n, b->cnc-(q0-b->cq)); | |
| 270 runemove(s, b->c+(q0-b->cq), m); | |
| 271 q0 += m; | |
| 272 s += m; | |
| 273 n -= m; | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 void | |
| 278 bufreset(Buffer *b) | |
| 279 { | |
| 280 int i; | |
| 281 | |
| 282 b->nc = 0; | |
| 283 b->cnc = 0; | |
| 284 b->cq = 0; | |
| 285 b->cdirty = 0; | |
| 286 b->cbi = 0; | |
| 287 /* delete backwards to avoid n² behavior */ | |
| 288 for(i=b->nbl-1; --i>=0; ) | |
| 289 delblock(b, i); | |
| 290 } | |
| 291 | |
| 292 void | |
| 293 bufclose(Buffer *b) | |
| 294 { | |
| 295 bufreset(b); | |
| 296 free(b->c); | |
| 297 b->c = nil; | |
| 298 b->cnc = 0; | |
| 299 free(b->bl); | |
| 300 b->bl = nil; | |
| 301 b->nbl = 0; | |
| 302 } |