Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include <libsec.h>
12 #include "dat.h"
13 #include "fns.h"
15 enum
16 {
17 Slop = 100 /* room to grow with reallocation */
18 };
20 static
21 void
22 sizecache(Buffer *b, uint n)
23 {
24 if(n <= b->cmax)
25 return;
26 b->cmax = n+Slop;
27 b->c = runerealloc(b->c, b->cmax);
28 }
30 static
31 void
32 addblock(Buffer *b, uint i, uint n)
33 {
34 if(i > b->nbl)
35 error("internal error: addblock");
37 b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]);
38 if(i < b->nbl)
39 memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*));
40 b->bl[i] = disknewblock(disk, n);
41 b->nbl++;
42 }
44 static
45 void
46 delblock(Buffer *b, uint i)
47 {
48 if(i >= b->nbl)
49 error("internal error: delblock");
51 diskrelease(disk, b->bl[i]);
52 b->nbl--;
53 if(i < b->nbl)
54 memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*));
55 b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]);
56 }
58 /*
59 * Move cache so b->cq <= q0 < b->cq+b->cnc.
60 * If at very end, q0 will fall on end of cache block.
61 */
63 static
64 void
65 flush(Buffer *b)
66 {
67 if(b->cdirty || b->cnc==0){
68 if(b->cnc == 0)
69 delblock(b, b->cbi);
70 else
71 diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc);
72 b->cdirty = FALSE;
73 }
74 }
76 static
77 void
78 setcache(Buffer *b, uint q0)
79 {
80 Block **blp, *bl;
81 uint i, q;
83 if(q0 > b->nc)
84 error("internal error: setcache");
85 /*
86 * flush and reload if q0 is not in cache.
87 */
88 if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc))
89 return;
90 /*
91 * if q0 is at end of file and end of cache, continue to grow this block
92 */
93 if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<Maxblock)
94 return;
95 flush(b);
96 /* find block */
97 if(q0 < b->cq){
98 q = 0;
99 i = 0;
100 }else{
101 q = b->cq;
102 i = b->cbi;
104 blp = &b->bl[i];
105 while(q+(*blp)->u.n <= q0 && q+(*blp)->u.n < b->nc){
106 q += (*blp)->u.n;
107 i++;
108 blp++;
109 if(i >= b->nbl)
110 error("block not found");
112 bl = *blp;
113 /* remember position */
114 b->cbi = i;
115 b->cq = q;
116 sizecache(b, bl->u.n);
117 b->cnc = bl->u.n;
118 /*read block*/
119 diskread(disk, bl, b->c, b->cnc);
122 void
123 bufinsert(Buffer *b, uint q0, Rune *s, uint n)
125 uint i, m, t, off;
127 if(q0 > b->nc)
128 error("internal error: bufinsert");
130 while(n > 0){
131 setcache(b, q0);
132 off = q0-b->cq;
133 if(b->cnc+n <= Maxblock){
134 /* Everything fits in one block. */
135 t = b->cnc+n;
136 m = n;
137 if(b->bl == nil){ /* allocate */
138 if(b->cnc != 0)
139 error("internal error: bufinsert1 cnc!=0");
140 addblock(b, 0, t);
141 b->cbi = 0;
143 sizecache(b, t);
144 runemove(b->c+off+m, b->c+off, b->cnc-off);
145 runemove(b->c+off, s, m);
146 b->cnc = t;
147 goto Tail;
149 /*
150 * We must make a new block. If q0 is at
151 * the very beginning or end of this block,
152 * just make a new block and fill it.
153 */
154 if(q0==b->cq || q0==b->cq+b->cnc){
155 if(b->cdirty)
156 flush(b);
157 m = min(n, Maxblock);
158 if(b->bl == nil){ /* allocate */
159 if(b->cnc != 0)
160 error("internal error: bufinsert2 cnc!=0");
161 i = 0;
162 }else{
163 i = b->cbi;
164 if(q0 > b->cq)
165 i++;
167 addblock(b, i, m);
168 sizecache(b, m);
169 runemove(b->c, s, m);
170 b->cq = q0;
171 b->cbi = i;
172 b->cnc = m;
173 goto Tail;
175 /*
176 * Split the block; cut off the right side and
177 * let go of it.
178 */
179 m = b->cnc-off;
180 if(m > 0){
181 i = b->cbi+1;
182 addblock(b, i, m);
183 diskwrite(disk, &b->bl[i], b->c+off, m);
184 b->cnc -= m;
186 /*
187 * Now at end of block. Take as much input
188 * as possible and tack it on end of block.
189 */
190 m = min(n, Maxblock-b->cnc);
191 sizecache(b, b->cnc+m);
192 runemove(b->c+b->cnc, s, m);
193 b->cnc += m;
194 Tail:
195 b->nc += m;
196 q0 += m;
197 s += m;
198 n -= m;
199 b->cdirty = TRUE;
203 void
204 bufdelete(Buffer *b, uint q0, uint q1)
206 uint m, n, off;
208 if(!(q0<=q1 && q0<=b->nc && q1<=b->nc))
209 error("internal error: bufdelete");
210 while(q1 > q0){
211 setcache(b, q0);
212 off = q0-b->cq;
213 if(q1 > b->cq+b->cnc)
214 n = b->cnc - off;
215 else
216 n = q1-q0;
217 m = b->cnc - (off+n);
218 if(m > 0)
219 runemove(b->c+off, b->c+off+n, m);
220 b->cnc -= n;
221 b->cdirty = TRUE;
222 q1 -= n;
223 b->nc -= n;
227 static int
228 bufloader(void *v, uint q0, Rune *r, int nr)
230 bufinsert(v, q0, r, nr);
231 return nr;
234 uint
235 loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg, DigestState *h)
237 char *p;
238 Rune *r;
239 int l, m, n, nb, nr;
240 uint q1;
242 p = emalloc((Maxblock+UTFmax+1)*sizeof p[0]);
243 r = runemalloc(Maxblock);
244 m = 0;
245 n = 1;
246 q1 = q0;
247 /*
248 * At top of loop, may have m bytes left over from
249 * last pass, possibly representing a partial rune.
250 */
251 while(n > 0){
252 n = read(fd, p+m, Maxblock);
253 if(n < 0){
254 warning(nil, "read error in Buffer.load");
255 break;
257 if(h != nil)
258 sha1((uchar*)p+m, n, nil, h);
259 m += n;
260 p[m] = 0;
261 l = m;
262 if(n > 0)
263 l -= UTFmax;
264 cvttorunes(p, l, r, &nb, &nr, nulls);
265 memmove(p, p+nb, m-nb);
266 m -= nb;
267 q1 += (*f)(arg, q1, r, nr);
269 free(p);
270 free(r);
271 return q1-q0;
274 uint
275 bufload(Buffer *b, uint q0, int fd, int *nulls, DigestState *h)
277 if(q0 > b->nc)
278 error("internal error: bufload");
279 return loadfile(fd, q0, nulls, bufloader, b, h);
282 void
283 bufread(Buffer *b, uint q0, Rune *s, uint n)
285 uint m;
287 if(!(q0<=b->nc && q0+n<=b->nc))
288 error("bufread: internal error");
290 while(n > 0){
291 setcache(b, q0);
292 m = min(n, b->cnc-(q0-b->cq));
293 runemove(s, b->c+(q0-b->cq), m);
294 q0 += m;
295 s += m;
296 n -= m;
300 void
301 bufreset(Buffer *b)
303 int i;
305 b->nc = 0;
306 b->cnc = 0;
307 b->cq = 0;
308 b->cdirty = 0;
309 b->cbi = 0;
310 /* delete backwards to avoid n² behavior */
311 for(i=b->nbl-1; --i>=0; )
312 delblock(b, i);
315 void
316 bufclose(Buffer *b)
318 bufreset(b);
319 free(b->c);
320 b->c = nil;
321 b->cnc = 0;
322 free(b->bl);
323 b->bl = nil;
324 b->nbl = 0;