Blob


1 #include "sam.h"
3 enum
4 {
5 Slop = 100 /* room to grow with reallocation */
6 };
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 }
18 static
19 void
20 addblock(Buffer *b, uint i, uint n)
21 {
22 if(i > b->nbl)
23 panic("internal error: addblock");
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 }
33 static
34 void
35 delblock(Buffer *b, uint i)
36 {
37 if(i >= b->nbl)
38 panic("internal error: delblock");
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 }
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 */
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 }
65 static
66 void
67 setcache(Buffer *b, uint q0)
68 {
69 Block **blp, *bl;
70 uint i, q;
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");
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);
111 void
112 bufinsert(Buffer *b, uint q0, Rune *s, uint n)
114 uint i, m, t, off;
116 if(q0 > b->nc)
117 panic("internal error: bufinsert");
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;
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;
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++;
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;
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;
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;
192 void
193 bufdelete(Buffer *b, uint q0, uint q1)
195 uint m, n, off;
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;
216 uint
217 bufload(Buffer *b, uint q0, int fd, int *nulls)
219 char *p;
220 Rune *r;
221 int l, m, n, nb, nr;
222 uint q1;
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;
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;
254 free(p);
255 free(r);
256 return q1-q0;
259 void
260 bufread(Buffer *b, uint q0, Rune *s, uint n)
262 uint m;
264 if(!(q0<=b->nc && q0+n<=b->nc))
265 panic("bufread: internal error");
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;
277 void
278 bufreset(Buffer *b)
280 int i;
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);
292 void
293 bufclose(Buffer *b)
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;