xref: /plan9/sys/src/cmd/sam/buff.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include "sam.h"
2 
3 enum
4 {
5 	Slop = 100,	/* room to grow with reallocation */
6 };
7 
8 static
9 void
sizecache(Buffer * b,uint n)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
addblock(Buffer * b,uint i,uint n)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
delblock(Buffer * b,uint i)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
flush(Buffer * b)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
setcache(Buffer * b,uint q0)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)->n <= q0 && q+(*blp)->n < b->nc){
95 		q += (*blp)->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->n);
106 	b->cnc = bl->n;
107 	/*read block*/
108 	diskread(disk, bl, b->c, b->cnc);
109 }
110 
111 void
bufinsert(Buffer * b,uint q0,Rune * s,uint n)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
bufdelete(Buffer * b,uint q0,uint q1)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
bufload(Buffer * b,uint q0,int fd,int * nulls)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
bufread(Buffer * b,uint q0,Rune * s,uint n)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
bufreset(Buffer * b)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
bufclose(Buffer * b)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 }
303