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 this block
     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: bufinsert1 cnc!=0");
    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: bufinsert2 cnc!=0");
    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 }