Browse code

Change chunk data management so that lines are entirely inside one chunk (this is to simplify the implementation of syntax highlighting)

Dario Rodriguez authored on 10/03/2019 00:03:02
Showing 2 changed files
... ...
@@ -78,6 +78,8 @@ redata_free(redata_t *redata)
78 78
         redata->sizechunks=0;
79 79
         if(redata->chunks!=NULL)
80 80
                 free(redata->chunks),redata->chunks=NULL;
81
+        if(redata->tmpchunk!=NULL)
82
+                free(redata->tmpchunk),redata->tmpchunk=NULL;
81 83
         /* undo */
82 84
         if(redata->undo!=NULL)
83 85
                 free(redata->undo),redata->undo=NULL;
... ...
@@ -95,6 +97,16 @@ redata_free(redata_t *redata)
95 97
         return;
96 98
 }
97 99
 
100
+int
101
+redata_config_chunkdatasize(redata_t *redata, int chunkdatasize)
102
+{
103
+        /* NOTE: this can only be configured immediately after doing redata_init() */
104
+        if(redata==NULL || chunkdatasize<=0 || redata->sizechunks!=0)
105
+                return(-1); /* sanity check failed */
106
+        redata->chunkdatasize=chunkdatasize;
107
+        return(0);
108
+}
109
+
98 110
 int
99 111
 redata_getsize(redata_t *redata)
100 112
 {
... ...
@@ -177,10 +189,17 @@ redata_preallocate(redata_t *redata, int newsize)
177 189
         int oldsizechunks;
178 190
         if(redata==NULL || redata->chunkdatasize==0 || newsize<0)
179 191
                 return(-1); /* sanity check failed */
192
+        rechunksize=sizeof(rechunk_t)-1+redata->chunkdatasize;
193
+        /* before doing any chunk alloc, we setup tmpchunk (used for moves) */
194
+        if(redata->tmpchunk==NULL) {
195
+                if((redata->tmpchunk=malloc(rechunksize))==NULL)
196
+                        return(-1);  /* insuf. mem. */
197
+                memset(redata->tmpchunk,0,rechunksize);
198
+        }
199
+        /* new chunk processing */
180 200
         if((cursize=redata_getsize(redata))>=newsize)
181 201
                 return(0); /* all done */
182 202
         nchunks=(newsize-cursize+redata->chunkdatasize-1)/redata->chunkdatasize;
183
-        rechunksize=sizeof(rechunk_t)-1+redata->chunkdatasize;
184 203
         if((newchunks=realloc(redata->chunks,sizeof(rechunk_t *)*(redata->sizechunks+nchunks)))==NULL)
185 204
                 return(-1); /* insuf. mem. */
186 205
         redata->chunks=newchunks;
... ...
@@ -198,6 +217,49 @@ redata_preallocate(redata_t *redata, int newsize)
198 217
         return(0);
199 218
 }
200 219
 
220
+int
221
+redata_chunk_movedata(redata_t *redata, int chunkfrom, int posfrom, int chunkto, int posto, int size)
222
+{
223
+        if(redata==NULL || size<0
224
+          || chunkfrom<0 || chunkfrom>=redata->sizechunks
225
+          || posfrom<0 || (posfrom+size)>redata->chunks[chunkfrom]->useddata
226
+          || chunkto<0 || chunkto>=redata->sizechunks
227
+          || posto<0 || (posto)>redata->chunks[chunkto]->useddata
228
+          || (chunkfrom!=chunkto && (redata->chunkdatasize-redata->chunks[chunkto]->useddata)<size)
229
+          || (chunkfrom==chunkto && posfrom<posto && posfrom+size>posto)
230
+          )
231
+                return(-1); /* sanity check failed */
232
+        if(size==0
233
+          || (chunkfrom==chunkto && posfrom==posto)
234
+          || (chunkfrom==chunkto && (posfrom+size)==posto)
235
+          )
236
+                return(0); /* all done */
237
+        if(chunkfrom!=chunkto) {
238
+                rechunk_t *from,*to;
239
+                from=redata->chunks[chunkfrom];
240
+                to=redata->chunks[chunkto];
241
+                memmove(to->data+posto+size,to->data+posto,to->useddata-posto);
242
+                memcpy(to->data+posto,from->data+posfrom,size);
243
+                memmove(from->data+posfrom,from->data+posfrom+size,from->useddata-posfrom-size);
244
+                from->useddata-=size;
245
+                to->useddata+=size;
246
+        } else {
247
+                /* from==to */
248
+                rechunk_t *chunk=redata->chunks[chunkfrom];
249
+                memcpy(redata->tmpchunk->data,chunk->data+posfrom,size);
250
+                if(posto>posfrom) {
251
+                        int inside=(posto-posfrom)-size;
252
+                        memmove(chunk->data+posfrom,chunk->data+posto-inside,inside);
253
+                        memcpy(chunk->data+posfrom+inside,redata->tmpchunk->data,size);
254
+                } else {
255
+                        int inside=(posto-posfrom);
256
+                        memmove(chunk->data+posfrom+size-inside,chunk->data+posto,inside);
257
+                        memcpy(chunk->data+posto,redata->tmpchunk->data,size);
258
+                }
259
+        }
260
+        return(0);
261
+}
262
+
201 263
 int
202 264
 redata_fill_whatin(redata_t *redata, int chunkno)
203 265
 {
... ...
@@ -207,12 +269,80 @@ redata_fill_whatin(redata_t *redata, int chunkno)
207 269
         if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL)
208 270
                 return(-1); /* sanity check failed */
209 271
         chunk=redata->chunks[chunkno];
272
+        if(chunk->whatin_fresh)
273
+                return(0);
210 274
         memset(&(chunk->whatin),0,sizeof(whatin_t));
211 275
         nlcount=0;
212 276
         for(i=0,lim=chunk->useddata;i<lim;i++) {
213 277
                 nlcount+=(chunk->data[i]=='\n')?1:0;
214 278
         }
215 279
         chunk->whatin.nlcount=nlcount;
280
+        chunk->whatin_fresh=1;
281
+        return(0);
282
+}
283
+
284
+void *
285
+mymemrchr(const void *s, int c, size_t n)
286
+{
287
+        int i;
288
+        void *res=NULL;
289
+        unsigned char b;
290
+        b=(*((unsigned int *)(&c)))&0xff;
291
+        for(i=0;i<n;i++) {
292
+                if(((unsigned char *)s)[i]==b)
293
+                        res=s+i;
294
+        }
295
+        return(res);
296
+}
297
+
298
+int
299
+redata_fix_nl(redata_t *redata, int chunkno)
300
+{
301
+        /* make sure a line of len<chunksize is entirely inside one chunk (to simplify the syntax highlighting code) */
302
+        if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL)
303
+                return(-1); /* sanity check failed */
304
+        if(chunkno==(redata->sizechunks-1) || redata->chunks[chunkno]->useddata==0 || redata->chunks[chunkno]->data[redata->chunks[chunkno]->useddata-1]=='\n')
305
+                return(0); /* Nothing to do (last chunk, chunk empty or already fixed) */
306
+        rechunk_t *chunk,*nextchunk;
307
+        int linesize, nextlinesize, avail, nextavail;
308
+        unsigned char *ptr,*nextptr;
309
+        chunk=redata->chunks[chunkno];
310
+        nextchunk=redata->chunks[chunkno+1];
311
+        ptr=mymemrchr(chunk->data,'\n',chunk->useddata);
312
+        nextptr=memchr(nextchunk->data,'\n',nextchunk->useddata);
313
+        linesize=(ptr==NULL)?chunk->useddata:(chunk->useddata-(ptr-chunk->data)-1);
314
+        nextlinesize=(nextptr==NULL)?nextchunk->useddata:((nextptr-nextchunk->data)+1);
315
+        avail=redata->chunkdatasize-chunk->useddata;
316
+        nextavail=redata->chunkdatasize-nextchunk->useddata;
317
+        if(avail>=nextlinesize) {
318
+                /* move remaining bytes of line to current chunk */
319
+                redata_chunk_movedata(redata, chunkno+1, 0, chunkno, chunk->useddata, nextlinesize);
320
+        } else if(nextavail>=linesize) {
321
+                /* move incomplete line to next chunk */
322
+                redata_chunk_movedata(redata, chunkno, chunk->useddata-linesize, chunkno+1, 0, linesize);
323
+        } else if(ptr!=NULL) {
324
+                /* only if we have more data un chunkno before this long line */
325
+                int i,newavail;
326
+                rechunk_t *newchunk;
327
+                /* create a new empty chunk in-between and put the line there */
328
+                if(redata->chunks[redata->sizechunks-1]->useddata!=0) {
329
+                        if(redata_preallocate(redata,redata->available+redata->chunkdatasize)==-1
330
+                          || redata->chunks[redata->sizechunks-1]->useddata!=0)
331
+                                return(-1);
332
+                }
333
+                newchunk=redata->chunks[redata->sizechunks-1];
334
+                /* move the emty chunk to after chunkno */
335
+                for(i=redata->sizechunks-1;i>(chunkno+1);i--)
336
+                        redata->chunks[i]=redata->chunks[i-1];
337
+                redata->chunks[chunkno+1]=newchunk;
338
+                /* move the data; if it doesn't fit, move from the beginning until it is full */
339
+                redata_chunk_movedata(redata, chunkno, chunk->useddata-linesize, chunkno+1, 0, linesize);
340
+                newavail=redata->sizechunks-linesize;
341
+                redata_chunk_movedata(redata, chunkno+2, 0, chunkno+1, linesize, (newavail<nextlinesize)?newavail:nextlinesize);
342
+                redata->chunks[chunkno+2]->whatin_fresh=0;
343
+        }
344
+        redata->chunks[chunkno]->whatin_fresh=0;
345
+        redata->chunks[chunkno+1]->whatin_fresh=0;
216 346
         return(0);
217 347
 }
218 348
 
... ...
@@ -329,7 +459,6 @@ redata_unsaved_unadd(redata_t *redata, undo_t *undo)
329 459
 int
330 460
 redata_load(redata_t *redata, char *filename, int use_unsaved)
331 461
 {
332
-#warning TODO: make sure a line of len<chunksize is entirely inside one chunk (to simplify the syntax highlighting code)
333 462
         int fd,nread,totalread;
334 463
         int chunkno, avail;
335 464
         struct stat statbuf;
... ...
@@ -374,9 +503,12 @@ redata_load(redata_t *redata, char *filename, int use_unsaved)
374 503
                 }
375 504
                 chunk->useddata+=nread;
376 505
                 redata->available-=nread;
377
-                redata_fill_whatin(redata,chunkno);
378 506
         }
379 507
         close(fd),fd=-1;
508
+        for(chunkno=0;chunkno<redata->sizechunks;chunkno++)
509
+                redata_fix_nl(redata,chunkno);
510
+        for(chunkno=0;chunkno<redata->sizechunks;chunkno++)
511
+                redata_fill_whatin(redata,chunkno);
380 512
         /* unsaved */
381 513
         redata_hash(redata,redata->initialhash);
382 514
         if(use_unsaved) {
... ...
@@ -19,6 +19,7 @@ typedef struct whatin_t {
19 19
 
20 20
 typedef struct rechunk_t {
21 21
         whatin_t whatin;
22
+        int whatin_fresh;
22 23
         int useddata;
23 24
         unsigned char data[1];
24 25
 } rechunk_t;
... ...
@@ -35,6 +36,7 @@ typedef struct redata_t {
35 36
         int sizechunks;
36 37
         rechunk_t **chunks;
37 38
         int available;
39
+        rechunk_t *tmpchunk;
38 40
         /* undo */
39 41
         int sizeundo;
40 42
         int usedundo;
... ...
@@ -55,17 +57,19 @@ typedef struct redata_t {
55 57
 redata_t *redata_init(void);
56 58
 void redata_free(redata_t *redata);
57 59
 
60
+int redata_config_chunkdatasize(redata_t *redata, int chunkdatasize);
61
+
58 62
 int redata_getsize(redata_t *redata);
59 63
 int redata_getused(redata_t *redata);
60 64
 int redata_getavailable(redata_t *redata);
61 65
 int redata_getused(redata_t *redata);
62 66
 int redata_getposptr(redata_t *redata, int pos, int *numchunk, int *offset);
63 67
 
64
-
65
-
66 68
 int redata_wipe(redata_t *redata);
67 69
 int redata_preallocate(redata_t *redata, int size);
70
+int redata_chunk_movedata(redata_t *redata, int chunkfrom, int posfrom, int chunkto, int posto, int size);
68 71
 int redata_fill_whatin(redata_t *redata, int chunkno);
72
+int redata_fix_nl(redata_t *redata, int chunkno);
69 73
 
70 74
 int redata_unsaved_exists(redata_t *redata, char *filename);
71 75
 int redata_unsaved_check(redata_t *redata, char *filename);
... ...
@@ -84,6 +88,8 @@ int redata_op_move(redata_t *redata, int posorig, int size, int posdest);
84 88
 int redata_op_undo(redata_t *redata);
85 89
 int redata_op_redo(redata_t *redata);
86 90
 
91
+int redata_commit_unsaved(redata_t *redata);
92
+
87 93
 int redata_hash(redata_t *redata, char *resbuf129bytes);
88 94
 int redata_filehash(redata_t *redata, char *filename, char *resbuf129bytes);
89 95
 int redata_memhash(redata_t *redata, char *buf, int buflen, char *resbuf129bytes);