Browse code

Implement op_move. Make the tests more thorough by having them check several different chunkdatasizes.

Dario Rodriguez authored on 15/03/2019 21:43:40
Showing 3 changed files
... ...
@@ -40,6 +40,9 @@ static char *ptr_getlong(char *ptr,char *endptr,long *data);
40 40
 static char *ptr_getchar(char *ptr,char *endptr,char *data);
41 41
 static char *ptr_searchendchar(char *ptr, char *endptr, char endchar, char **endcharpos);
42 42
 static char sep_select(char *buf, int bufsize, char **pos);
43
+static void *mymemrchr(const void *s, int c, size_t n);
44
+static void meminvert(void *start, void *end);
45
+
43 46
 
44 47
 redata_t *
45 48
 redata_init(void)
... ...
@@ -339,7 +342,7 @@ redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n)
339 342
 {
340 343
         rechunk_t *chunk;
341 344
         if(redata==NULL || n<0
342
-          || chunk<0 || chunkno>=redata->sizechunks
345
+          || chunkno<0 || chunkno>=redata->sizechunks
343 346
           || pos<0 || pos>redata->chunks[chunkno]->useddata
344 347
           || (redata->chunks[chunkno]->useddata-pos)<n)
345 348
                 return(-1); /* sanity check failed */
... ...
@@ -353,6 +356,41 @@ redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n)
353 356
         return(0);
354 357
 }
355 358
 
359
+int
360
+redata_chunk_splithere(redata_t *redata, int chunkno, int pos)
361
+{
362
+        rechunk_t *chunk;
363
+        int size;
364
+        if(redata==NULL
365
+          || chunkno<0 || chunkno>=redata->sizechunks
366
+          || pos<0 || pos>redata->chunks[chunkno]->useddata)
367
+                return(-1); /* sanity check failed */
368
+        chunk=redata->chunks[chunkno];
369
+        if(chunk->useddata==pos)
370
+                return(0); /* all done: already splitted */
371
+        size=redata->chunks[chunkno]->useddata-pos;
372
+        if((chunkno+1)<redata->sizechunks
373
+          && (redata->chunkdatasize-redata->chunks[chunkno+1]->useddata)>=size) {
374
+                redata_chunk_movedata(redata,chunkno,pos,chunkno+1,0,size);
375
+                return(0); /* all done: moved data cleanly to next chunk */
376
+        }
377
+        if(redata_chunk_insertnew(redata,chunkno)!=0)
378
+                return(-1); /* insuf. mem. */
379
+        redata_chunk_movedata(redata,chunkno,pos,chunkno+1,0,size);
380
+        return(0);
381
+}
382
+
383
+int
384
+redata_chunk_fillfromnext(redata_t *redata, int chunkno, int n)
385
+{
386
+        if(redata==NULL || chunkno<0 || (chunkno+1)>=redata->sizechunks
387
+          || (redata->chunkdatasize-redata->chunks[chunkno]->useddata)<n
388
+          || (redata->chunks[chunkno+1]->useddata)<n)
389
+                return(-1); /* sanity check failed */
390
+        return(redata_chunk_movedata(redata,chunkno+1,0,chunkno,redata->chunks[chunkno]->useddata,n));
391
+}
392
+
393
+
356 394
 int
357 395
 redata_whatin_refresh(redata_t *redata, int chunkno)
358 396
 {
... ...
@@ -374,18 +412,6 @@ redata_whatin_refresh(redata_t *redata, int chunkno)
374 412
         return(0);
375 413
 }
376 414
 
377
-void *
378
-mymemrchr(const void *s, int c, size_t n)
379
-{
380
-        long i;
381
-        void *res=NULL;
382
-        unsigned char b;
383
-        b=(*((unsigned int *)(&c)))&0xff;
384
-        for(i=0;i<n;i++) {
385
-                if(((unsigned char *)s)[i]==b)
386
-                        res=(((unsigned char *)s)+i);        }
387
-        return(res);
388
-}
389 415
 
390 416
 int
391 417
 redata_fix_nl(redata_t *redata, int chunkno)
... ...
@@ -896,6 +922,33 @@ redata_undo_wipe(redata_t *redata, undostack_t *stack)
896 922
         return(0);
897 923
 }
898 924
 
925
+int
926
+redata_undo_inactivatelast(redata_t *redata, undostack_t *stack)
927
+{
928
+        if(redata==NULL
929
+          || (stack!=&(redata->undostack) && stack!=&(redata->redostack)))
930
+                return(-1); /* sanity check failed */
931
+        if(stack->usedundo==0)
932
+                return(-1);
933
+        stack->usedundo--;
934
+        stack->usedbuf-=stack->undo[stack->usedundo].len;
935
+        return(0);
936
+}
937
+
938
+int
939
+redata_undo_reactivatelast(redata_t *redata, undostack_t *stack)
940
+{
941
+        if(redata==NULL
942
+          || (stack!=&(redata->undostack) && stack!=&(redata->redostack)))
943
+                return(-1); /* sanity check failed */
944
+        if(stack->usedundo==stack->sizeundo)
945
+                return(-1);
946
+        stack->usedbuf+=stack->undo[stack->usedundo].len;
947
+        stack->usedundo++;
948
+        return(0);
949
+}
950
+
951
+
899 952
 int
900 953
 redata_op_add(redata_t *redata, long insertpos, char *buf, long buflen, undostack_t *fromhere)
901 954
 {
... ...
@@ -982,6 +1035,9 @@ redata_op_add(redata_t *redata, long insertpos, char *buf, long buflen, undostac
982 1035
                 /* from undo stack */
983 1036
                 redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack));
984 1037
         }
1038
+        /* compact if needed */
1039
+        if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2))
1040
+                redata_compact(redata);
985 1041
         return(0);
986 1042
 }
987 1043
 
... ...
@@ -995,7 +1051,7 @@ redata_op_del(redata_t *redata, long delpos, long size, undostack_t *fromhere)
995 1051
         long ndel;
996 1052
         long curpos,curdel;
997 1053
         if(redata==NULL || size<0
998
-          || delpos>redata_getused(redata)
1054
+          || delpos<0
999 1055
           || (delpos+size)>redata_getused(redata)
1000 1056
           || (fromhere!=NULL && (fromhere!=&(redata->undostack) && fromhere!=&(redata->redostack))))
1001 1057
                 return(-1); /* sanity check failed */
... ...
@@ -1037,14 +1093,139 @@ redata_op_del(redata_t *redata, long delpos, long size, undostack_t *fromhere)
1037 1093
                 /* from undo stack */
1038 1094
                 redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack));
1039 1095
         }
1096
+        /* compact if needed */
1097
+        if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2))
1098
+                redata_compact(redata);
1040 1099
         return(0);
1041 1100
 }
1042 1101
 
1043 1102
 int
1044 1103
 redata_op_move(redata_t *redata, long posorig, long size, long posdest, undostack_t *fromhere)
1045 1104
 {
1046
-#warning TODO
1047
-        return(-1);
1105
+        int chunkno,dchunkno,curchunk;
1106
+        long pos,dpos;
1107
+        undo_t *undo;
1108
+        rechunk_t *chunk;
1109
+        if(redata==NULL || size<0
1110
+          || posorig<0
1111
+          || (posorig+size)>redata_getused(redata)
1112
+          || posdest<0
1113
+          || (posdest>=posorig && posdest<(posorig+size))
1114
+          || (fromhere!=NULL && (fromhere!=&(redata->undostack) && fromhere!=&(redata->redostack))))
1115
+                return(-1); /* sanity check failed */
1116
+        if(redata_getposptr(redata,posorig,&chunkno,&pos)==-1)
1117
+                return(-1); /* invalid pos */
1118
+        if(redata_getposptr(redata,posdest,&dchunkno,&dpos)==-1)
1119
+                return(-1); /* invalid pos */
1120
+        if(fromhere!=&(redata->undostack)) {
1121
+                if((undo=redata_undo_newfromchunks(redata,&(redata->undostack),'M',posorig,size))==NULL)
1122
+                        return(-1); /* couldn't create undo struct */
1123
+                /* inactivate the undo so we are able to use return(-1) without removing it */
1124
+                redata_undo_inactivatelast(redata,&(redata->undostack));
1125
+                redata_hash(redata,undo->prehash);
1126
+        } else {
1127
+                undo=NULL;
1128
+        }
1129
+        if((pos+size)<=redata->chunks[chunkno]->useddata
1130
+          && (chunkno==dchunkno || (redata->chunkdatasize-redata->chunks[dchunkno]->useddata)>=size)) {
1131
+                /* trivial case: (all the data is in the same chunk) AND (it is intra-chunk move or destination chunk has enough avail. space) */
1132
+                redata_chunk_movedata(redata, chunkno, pos, dchunkno, dpos, size);
1133
+        } else {
1134
+                /* data spans several chunks, no space on dest, etc: do it the hard way */
1135
+                /* separate the selected data into its own chunk(s) */
1136
+                /* lower positions have to make the boundary first (or risk undoing it with next create boundary) */
1137
+                if(posdest<posorig) {
1138
+                        /* make a chunk boundary in posdest */
1139
+                        if(redata_getposptr(redata,posdest,&chunkno,&pos)==-1
1140
+                          || redata_chunk_splithere(redata,chunkno,pos)!=0)
1141
+                                return(-1); /* invalid pos or insuf. mem */
1142
+                }
1143
+                /* make a chunk boundary in posorig and in posorig+size */
1144
+                if(redata_getposptr(redata,posorig,&chunkno,&pos)==-1
1145
+                  || redata_chunk_splithere(redata,chunkno,pos)!=0)
1146
+                        return(-1); /* invalid pos or insuf. mem */
1147
+                if(redata_getposptr(redata,posorig+size,&chunkno,&pos)==-1
1148
+                  || redata_chunk_splithere(redata,chunkno,pos)!=0)
1149
+                        return(-1); /* invalid pos or insuf. mem */
1150
+                if(posdest>posorig) {
1151
+                        /* make a chunk boundary in posdest */
1152
+                        if(redata_getposptr(redata,posdest,&chunkno,&pos)==-1
1153
+                          || redata_chunk_splithere(redata,chunkno,pos)!=0)
1154
+                                return(-1); /* invalid pos or insuf. mem */
1155
+                }
1156
+                /* reorder the chunks */
1157
+                {
1158
+                        int schunkno;
1159
+                        long spos;
1160
+                        if(redata_getposptr(redata,posorig,&chunkno,&pos)==-1)
1161
+                                return(-1); /* invalid pos or insuf. mem */
1162
+                        if(redata_getposptr(redata,posorig+size,&schunkno,&spos)==-1)
1163
+                                return(-1); /* invalid pos or insuf. mem */
1164
+                        if(redata_getposptr(redata,posdest,&dchunkno,&dpos)==-1)
1165
+                                return(-1); /* invalid pos or insuf. mem */
1166
+                        if(pos!=0)
1167
+                                chunkno++;
1168
+                        if(spos==0)
1169
+                                schunkno--;
1170
+                        if(dpos!=0)
1171
+                                dchunkno++;
1172
+                        if(chunkno<0 || schunkno<0 || dchunkno<0
1173
+                          || chunkno>=redata->sizechunks || schunkno>=redata->sizechunks || dchunkno>=redata->sizechunks)
1174
+                                return(-1); /* ERROR: INTERNAL ERROR */
1175
+                        /* reorder inplace inverting the bytes (as in flipping a image) */
1176
+                        if(chunkno<dchunkno) {
1177
+                                meminvert(redata->chunks+chunkno,redata->chunks+dchunkno);
1178
+                                meminvert(redata->chunks+chunkno,redata->chunks+dchunkno-(schunkno-chunkno));
1179
+                                meminvert(redata->chunks+dchunkno-(schunkno-chunkno),redata->chunks+dchunkno);
1180
+                        } else {
1181
+                                meminvert(redata->chunks+dchunkno,redata->chunks+chunkno);
1182
+                                meminvert(redata->chunks+dchunkno,redata->chunks+dchunkno+(schunkno-chunkno));
1183
+                                meminvert(redata->chunks+dchunkno+(schunkno-chunkno),redata->chunks+chunkno);
1184
+                        }
1185
+                }
1186
+        }
1187
+        /* fix nl and delete unused chunks */
1188
+        if(posorig<posdest) {
1189
+              if(redata_getposptr(redata,posorig,&chunkno,&pos)==-1)
1190
+                      return(-1); /* invalid pos */
1191
+              if(redata_getposptr(redata,posdest,&dchunkno,&dpos)==-1)
1192
+                      return(-1); /* invalid pos */
1193
+        } else { /* posorig>posdest */
1194
+              if(redata_getposptr(redata,posdest,&chunkno,&pos)==-1)
1195
+                      return(-1); /* invalid pos */
1196
+              if(redata_getposptr(redata,posorig+size,&dchunkno,&dpos)==-1)
1197
+                      return(-1); /* invalid pos */
1198
+        }
1199
+        for(curchunk=dchunkno;curchunk>=chunkno;curchunk--) {
1200
+                chunk=redata->chunks[curchunk];
1201
+                if(chunk->useddata==0) {
1202
+                        /* move this chunk to the end */
1203
+                        redata_chunk_deletechunk(redata,curchunk);
1204
+                } else
1205
+                        redata_fix_nl(redata,curchunk);
1206
+        }
1207
+        /* activate undo */
1208
+        if(fromhere!=&(redata->undostack)) {
1209
+                /* reactivate the undo, now it is fine */
1210
+                redata_undo_reactivatelast(redata,&(redata->undostack));
1211
+        }
1212
+        if(undo!=NULL) {
1213
+                /* new or from redo stack */
1214
+                undo->posorig=posorig;
1215
+                undo->posdest=posdest;
1216
+                redata_hash(redata,undo->posthash);
1217
+                if(fromhere==&(redata->redostack))
1218
+                        redata_undo_removelast(redata,&(redata->redostack));
1219
+                else
1220
+                        redata_undo_wipe(redata,&(redata->redostack));
1221
+        } else {
1222
+                /* from undo stack */
1223
+                redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack));
1224
+        }
1225
+        /* compact if needed */
1226
+        if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2))
1227
+                redata_compact(redata);
1228
+        return(0);
1048 1229
 }
1049 1230
 
1050 1231
 int
... ...
@@ -1113,6 +1294,17 @@ redata_data_compare(redata_t *redata, long cmppos, char *buf, long buflen)
1113 1294
         return(0);
1114 1295
 }
1115 1296
 
1297
+int
1298
+redata_compact(redata_t *redata)
1299
+{
1300
+        /* compact and free surplus chunks */
1301
+        /* criterion: */
1302
+        /* 1. if two neighbouring chunks could join with 10% free in result chunk, do it */
1303
+        /* 2. if there are more than 2 unused chunks free at the end, free all unused chunks except two */
1304
+#warning TODO
1305
+        return(-1);
1306
+}
1307
+
1116 1308
 int
1117 1309
 redata_hash(redata_t *redata, char *resbuf129bytes)
1118 1310
 {
... ...
@@ -1315,3 +1507,29 @@ sep_select(char *buf, int bufsize, char **pos)
1315 1507
         return(seps[besti]);
1316 1508
 }
1317 1509
 
1510
+static void *
1511
+mymemrchr(const void *s, int c, size_t n)
1512
+{
1513
+        long i;
1514
+        void *res=NULL;
1515
+        unsigned char b;
1516
+        b=(*((unsigned int *)(&c)))&0xff;
1517
+        for(i=0;i<n;i++) {
1518
+                if(((unsigned char *)s)[i]==b)
1519
+                        res=(((unsigned char *)s)+i);        }
1520
+        return(res);
1521
+}
1522
+
1523
+static void
1524
+meminvert(void *start, void *end)
1525
+{
1526
+        unsigned char *a=(unsigned char *)start;
1527
+        unsigned char *b=(unsigned char *)end;
1528
+        unsigned char t;
1529
+        for(b=b-1;a<b;a++,b--) {
1530
+                t=*a;
1531
+                *a=*b;
1532
+                *b=t;
1533
+        }
1534
+}
1535
+
... ...
@@ -85,6 +85,8 @@ int redata_chunk_deletechunk(redata_t *redata, int chunkno);
85 85
 int redata_chunk_movedata(redata_t *redata, int chunkfrom, long posfrom, int chunkto, long posto, long size);
86 86
 int redata_chunk_insertdata(redata_t *redata, int chunkto, long posto, char *buf, long buflen);
87 87
 int redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n);
88
+int redata_chunk_splithere(redata_t *redata, int chunkno, int pos);
89
+int redata_chunk_fillfromnext(redata_t *redata, int chunkno, int n);
88 90
 int redata_whatin_refresh(redata_t *redata, int chunkno);
89 91
 int redata_fix_nl(redata_t *redata, int chunkno);
90 92
 int redata_undobuf_reserve(redata_t *redata, undostack_t *stack, int minavail);
... ...
@@ -94,6 +96,9 @@ undo_t *redata_undo_newfrombuf(redata_t *redata, undostack_t *stack, char type,
94 96
 int redata_undo_movelast(redata_t *redata, undostack_t *from, undostack_t *to);
95 97
 int redata_undo_removelast(redata_t *redata, undostack_t *stack);
96 98
 int redata_undo_wipe(redata_t *redata, undostack_t *stack);
99
+int redata_undo_inactivatelast(redata_t *redata, undostack_t *stack);
100
+int redata_undo_reactivatelast(redata_t *redata, undostack_t *stack);
101
+
97 102
 
98 103
 /* high level stuff */
99 104
 int redata_unsaved_exists(redata_t *redata, char *filename);
... ...
@@ -116,6 +121,8 @@ int redata_op_redo(redata_t *redata);
116 121
 
117 122
 int redata_data_compare(redata_t *redata, long pos, char *buf, long buflen);
118 123
 
124
+int redata_compact(redata_t *redata);
125
+
119 126
 int redata_hash(redata_t *redata, char *resbuf129bytes);
120 127
 int redata_filehash(redata_t *redata, char *filename, char *resbuf129bytes);
121 128
 int redata_memhash(redata_t *redata, char *buf, long buflen, char *resbuf129bytes);
... ...
@@ -40,36 +40,45 @@ char *test_edit(redata_t *redata, char *filename, char *edits, int filesize, int
40 40
 int
41 41
 main(int argc, char *argv[])
42 42
 {
43
+        static int sizes[]={-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,30,31,32,33,63,64,65,127,128,129,1023,1024,1025,16384};
43 44
         test_t tests[]={
44 45
                 {"newfile_16",test_newfile,PREFIX "FILE", NULL, 16, 0},
45
-                {"newfile_1024",test_newfile,PREFIX "FILE", NULL, 1024, 0},
46
+/*                {"newfile_1024",test_newfile,PREFIX "FILE", NULL, 1024, 0},
46 47
                 {"newfile_32767",test_newfile,PREFIX "FILE", NULL, 32767, 0},
47 48
                 {"newfile_32768",test_newfile,PREFIX "FILE", NULL, 32768, 0},
48 49
                 {"newfile_32769",test_newfile,PREFIX "FILE", NULL, 32769, 0},
49
-                {"newfile_131072",test_newfile,PREFIX "FILE", NULL, 131072, 0},
50
+                {"newfile_131072",test_newfile,PREFIX "FILE", NULL, 131072, 0},*/
50 51
                 {"testedit_add",test_edit,PREFIX "EDIT", "A0$Testing add$",0,0},
51 52
                 {"testedit_del",test_edit,PREFIX "EDIT", "A0$Testing add/del$D8$add/$",0,0},
52 53
         };
53 54
         redata_t *redata;
54
-        int i;
55
-        int nerrors;
55
+        int i,s;
56
+        int nerrors,total;
56 57
         char *res;
57
-        for(i=0,nerrors=0;i<(sizeof(tests)/sizeof(tests[0]));i++) {
58
-                if((redata=redata_init())==NULL) {
59
-                        fprintf(stderr,"ERROR: problem initializing redata module\n");
60
-                        return(1);
61
-                }
62
-                fprintf(stderr,"%s...",tests[i].name);
63
-                res=tests[i].fn(redata,tests[i].param1,tests[i].param2,tests[i].int1,tests[i].int2);
64
-                if(strcmp(res,TEST_OK)==0) {
65
-                        fprintf(stderr," ok.\n");
66
-                } else {
67
-                        fprintf(stderr," ERROR: %s\n",res);
68
-                        nerrors++;
58
+        nerrors=0;
59
+        total=0;
60
+        for(s=0;s<(sizeof(sizes)/sizeof(sizes[0]));s++) {
61
+                if(sizes[s]!=-1)
62
+                        fprintf(stderr,"SIZE: %i\n",sizes[s]);
63
+                for(i=0;i<(sizeof(tests)/sizeof(tests[0]));i++,total++) {
64
+                        if((redata=redata_init())==NULL) {
65
+                                fprintf(stderr,"ERROR: problem initializing redata module\n");
66
+                                return(1);
67
+                        }
68
+                        if(sizes[s]!=-1)
69
+                                redata_config_chunkdatasize(redata,sizes[s]);
70
+                        fprintf(stderr,"%s...",tests[i].name);
71
+                        res=tests[i].fn(redata,tests[i].param1,tests[i].param2,tests[i].int1,tests[i].int2);
72
+                                if(strcmp(res,TEST_OK)==0) {
73
+                                fprintf(stderr," ok.\n");
74
+                        } else {
75
+                                fprintf(stderr," ERROR: %s\n",res);
76
+                                nerrors++;
77
+                                }
78
+                        redata_free(redata),redata=NULL;
69 79
                 }
70
-                redata_free(redata),redata=NULL;
80
+                fprintf(stderr,"\n");
71 81
         }
72
-        fprintf(stderr,"\n");
73 82
         if(nerrors==0)
74 83
                 fprintf(stderr,"All tests passed OK\n");
75 84
         else