1 /* Bluefish HTML Editor
2 * bftextview2_markregion.c
3 *
4 * Copyright (C) 2013 Olivier Sessink
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* markregion design:
21
22 the markregion code is used to mark which characters of the BluefishTextView need
23 to be re-scanned for syntax, or for spell checking. Each document has a
24 separate Tregions structure for syntax scanning and spell checking in BluefishTextView
25
26 The Tregions simply point to a head and tail of a double linked list of Tchange elements.
27
28 After syntax scanning is complete and spell checking is complete these regions are empty.
29
30 So usually the list is very short, and if the list is long (for example after tabs-to-spaces
31 or another function that does many text changes in one go) it is usually only appended to
32 because these functions start in the beginning of the text and work towards the end. That's
33 why a linked list is good enough, and a balanced tree would probably be worse in performance.
34
35 The list has two entries for each sequence of characters that need rescanning/spellchecking,
36 one entry positions the start, the second entry positions the end.
37 */
38
39 #include "bluefish.h"
40 #include "bf_lib.h"
41 #include "bftextview2_private.h"
42 #include "bftextview2_markregion.h"
43
44 #ifdef MARKREGION
45 /*#define PROFILE_MARKREGION*/
46
47 #ifdef PROFILE_MARKREGION
48 typedef struct {
49 gint num_list_walks;
50 gint num_region_changes;
51 gint num_offset_updates;
52 gint num_new;
53 gint num_full_append;
54 gint num_full_prepend;
55 gint num_insert;
56 gint num_allocs;
57 } Tprofile_markregion;
58 Tprofile_markregion prof = {0,0,0,0,0,0,0,0};
59 #endif
60
61 typedef struct {
62 BF_ELIST_HEAD;
63 guint32 pos;
64 guint32 is_start; /* a guint8 would have been good enough, but
65 both on 32bit or 64 bit systems that doesn't affect
66 the size of Tchange, and this is faster on 32bit
67 systems */
68 } Tchange;
69 #define CHANGE(var) ((Tchange *)var)
70
71 #ifdef DEVELOPMENT
72 #ifdef NEEDSCANNING
73 void
compare_markregion_needscanning(BluefishTextView * btv)74 compare_markregion_needscanning(BluefishTextView *btv) {
75 gboolean cont=TRUE;
76 Tchange *cso, *ceo;
77 guint so,eo;
78 GtkTextIter start,end;
79 DBG_MARKREGION("compare_markregion_needscanning, started, markregion has head(%d)|tail(%d)\n",btv->scanning.head?CHANGE(btv->scanning.head)->pos:-1
80 ,btv->scanning.tail?CHANGE(btv->scanning.tail)->pos:-1);
81 gtk_text_buffer_get_start_iter(btv->buffer, &start);
82 cso = CHANGE(btv->scanning.head);
83 while (cont) {
84 if (!gtk_text_iter_begins_tag(&start, btv->needscanning)) {
85 if (!gtk_text_iter_forward_to_tag_toggle(&start, btv->needscanning)) {
86 cont=FALSE;
87 if (cso) {
88 g_print("ABORT: needscanning has no further region, markregion has cso=%d, ceo=%d\n",cso->pos, cso->next?CHANGE(cso->next)->pos:-1);
89 g_assert_not_reached();
90 }
91 break;
92 }
93 }
94 end = start;
95 gtk_text_iter_forward_char(&end);
96 if (!gtk_text_iter_ends_tag(&end, btv->needscanning)) {
97 if (!gtk_text_iter_forward_to_tag_toggle(&end, btv->needscanning)) {
98 g_print("huh2?\n");
99 }
100 }
101 ceo = CHANGE(cso->next);
102 so = gtk_text_iter_get_offset(&start);
103 eo = gtk_text_iter_get_offset(&end);
104 if(so != cso->pos || eo != ceo->pos) {
105 g_print("ABORT: needscanning has %u:%u, markregion has %u:%u\n",so,eo,cso->pos,ceo->pos);
106 g_assert_not_reached();
107 }
108 /* DBG_MARKREGION("region %u:%u\n",so,eo);*/
109 start = end;
110 cso = ceo->next;
111 cont = gtk_text_iter_forward_char(&start);
112 if (!cont && cso) {
113 g_print("ABORT: buffer has no further characters, markregion has cso=%d, ceo=%d\n",cso->pos, cso->next?CHANGE(cso->next)->pos:-1);
114 g_assert_not_reached();
115 }
116 }
117 /* g_print("*****\n");*/
118 }
119 #endif
120
121 static void
markregion_verify_integrity(Tregions * rg)122 markregion_verify_integrity(Tregions *rg)
123 {
124 Tchange *end, *start = rg->head;
125 DBG_MARKREGION("markregion_verify_integrity, started, head(%d)|tail(%d)\n",rg->head?CHANGE(rg->head)->pos:-1
126 ,rg->tail?CHANGE(rg->tail)->pos:-1);
127 if (!rg->head) {
128 if (rg->tail || rg->last) {
129 g_print("ERROR: markregion_verify_integrity, !rg->head but there is a tail (%p) or last (%p) !?!?!\n",rg->tail, rg->last);
130 g_assert_not_reached();
131 }
132 return;
133 }
134
135 /* g_print("* * * * *\n");*/
136 if (CHANGE(rg->head)->prev) {
137 g_print("ERROR: markregion_verify_integrity, rg->head has a previous entry !?!?!\n");
138 g_assert_not_reached();
139 }
140 while (start) {
141 if (start->is_start ==FALSE) {
142 g_print("ERROR: markregion_verify_integrity, entry(%u)->is_start==FALSE but should start a region?!?\n",start->pos);
143 g_assert_not_reached();
144 }
145 end = start->next;
146 if (!end) {
147 g_print("ERROR: markregion_verify_integrity, entry(%u) does not have an end?!?\n",start->pos);
148 g_assert_not_reached();
149 }
150 if (end->prev != start) {
151 g_print("ERROR: markregion_verify_integrity, end(%u)->prev (%p) does not point to start(%u) (%p)?!?\n",end->pos,end->prev,start->pos,start);
152 g_assert_not_reached();
153 }
154 /* g_print("verifying region %u:%u\n",start->pos,end->pos);*/
155
156 if (end->is_start == TRUE) {
157 g_print("ERROR: markregion_verify_integrity, entry(%u)->is_start==TRUE but should end a region?!?\n",end->pos);
158 g_assert_not_reached();
159 }
160 if (start->pos >= end->pos) {
161 g_print("ERROR: markregion_verify_integrity, start at %u but end at %u ?!?\n",start->pos,end->pos);
162 g_assert_not_reached();
163 }
164 start = end->next;
165 if (start) {
166 if (start->prev != end) {
167 g_print("ERROR: markregion_verify_integrity, start->prev does not point to previous end?!?\n");
168 g_assert_not_reached();
169 }
170 if (end->pos >= start->pos) {
171 g_print("ERROR: markregion_verify_integrity, next start at %u but last end at %u ?!?\n",start->pos,end->pos);
172 g_assert_not_reached();
173 }
174 } else {
175 if (end != rg->tail) {
176 g_print("ERROR: markregion_verify_integrity, rg->tail(%u) does not equal the last entry %u ?!?\n",CHANGE(rg->tail)->pos, end->pos);
177 g_assert_not_reached();
178 }
179
180 }
181 }
182 /* g_print("* * * * *\n");*/
183 }
184 #endif
185
186 static Tchange *
new_change(guint pos,gboolean is_start)187 new_change(guint pos, gboolean is_start)
188 {
189 Tchange *change = g_slice_new(Tchange);
190 change->next = change->prev = NULL;
191 change->pos = pos;
192 change->is_start = is_start;
193 #ifdef PROFILE_MARKREGION
194 prof.num_allocs++;
195 #endif
196 return change;
197 }
198
199 /*
200 markregion_region_done is called after a scanning run, it is called with the position up to the point
201 where scanning was finished.
202 */
203 void
markregion_region_done(Tregions * rg,guint end)204 markregion_region_done(Tregions *rg, guint end)
205 {
206 Tchange *tmp;
207 #ifdef PROFILE_MARKREGION
208 prof.num_region_changes++;
209 #endif
210 DBG_MARKREGION("markregion_region_done, end=%u\n",end);
211 if (!rg->head) {
212 return;
213 }
214 tmp = rg->head;
215 while (tmp && tmp->pos <= end) {
216 Tchange *toremove = tmp;
217 DBG_MARKREGION("markregion_region_done, remove change with pos=%u\n",tmp->pos);
218 tmp = CHANGE(bf_elist_remove(BF_ELIST(tmp))); /* returns the previous entry if that exists, but
219 it does not exist in this case because we remove all entries */
220 g_slice_free(Tchange, toremove);
221 #ifdef PROFILE_MARKREGION
222 prof.num_allocs--;
223 prof.num_list_walks++;
224 #endif
225 }
226 if (tmp && tmp->is_start == FALSE) {
227 /* we cannot begin our list of regions with an end-of-region, so remove it, or prepend a start position in front of it */
228 DBG_MARKREGION("next change at %u is a end!\n",tmp->pos);
229 if (tmp->pos == end) {
230 Tchange *toremove = tmp;
231 DBG_MARKREGION("markregion_region_done, remove change with pos=%u\n",toremove->pos);
232 tmp = CHANGE(bf_elist_remove(BF_ELIST(toremove)));
233 #ifdef PROFILE_MARKREGION
234 prof.num_allocs--;
235 #endif
236 g_slice_free(Tchange, toremove);
237 } else {
238 DBG_MARKREGION("markregion_region_done, prepend change with end=%u\n",end);
239 tmp = CHANGE(bf_elist_prepend(BF_ELIST(tmp), BF_ELIST(new_change(end, TRUE))));
240 }
241 } else {
242 #ifdef DEVELOPMENT
243 if (tmp) {
244 DBG_MARKREGION("markregion_region_done, keep %u, is_start=%d\n",tmp->pos,tmp->is_start);
245 }
246 #endif
247 }
248 rg->head = tmp;
249 rg->last = NULL;
250 if (tmp == NULL) {
251 rg->tail = NULL;
252 }
253 DBG_MARKREGION("markregion_region_done, return, head(%d)|tail(%d)\n",rg->head?CHANGE(rg->head)->pos:-1
254 ,rg->tail?CHANGE(rg->tail)->pos:-1);
255 #ifdef PROFILE_MARKREGION
256 g_print("markregion profiling: changes:%d, allocs=%d, offset_updates=%d, list_walks=%d, new/append/prepend/insert=%f/%f/%f/%f\n",
257 prof.num_region_changes, prof.num_allocs, prof.num_offset_updates, prof.num_list_walks,
258 100.0 * prof.num_new / prof.num_region_changes,
259 100.0 * prof.num_full_append / prof.num_region_changes,
260 100.0 * prof.num_full_prepend / prof.num_region_changes,
261 100.0 * prof.num_insert / prof.num_region_changes
262 );
263 #endif
264 }
265
266 static void
update_offset(Tchange * start,gint offset)267 update_offset(Tchange *start, gint offset)
268 {
269 if (offset == 0)
270 return;
271 #ifdef PROFILE_MARKREGION
272 prof.num_offset_updates++;
273 #endif
274 while (start) {
275 start->pos += offset;
276 start = start->next;
277 #ifdef PROFILE_MARKREGION
278 prof.num_list_walks++;
279 #endif
280 }
281 }
282
283 static Tchange *
find_prev_or_equal_position(Tregions * rg,guint position)284 find_prev_or_equal_position(Tregions *rg, guint position)
285 {
286 if (!rg->last) {
287 if (position - CHANGE(rg->head)->pos < CHANGE(rg->tail)->pos - position) {
288 rg->last = rg->head;
289 } else {
290 rg->last = rg->tail;
291 }
292 }
293 #ifdef DEVELOPMENT
294 if (!rg->last) {
295 g_print("ABORT: find_prev_or_equal_position, rg->last==NULL ?!?!?!\n");
296 g_assert_not_reached();
297 }
298 #endif
299 if (CHANGE(rg->last)->pos == position)
300 return CHANGE(rg->last);
301
302 if (CHANGE(rg->last)->pos > position) {
303 /* DBG_MARKREGION("find_prev_or_equal_position, current position %u is beyond the requested position %u\n",CHANGE(rg->last)->pos,position);*/
304 while (rg->last && CHANGE(rg->last)->pos > position) {
305 /* DBG_MARKREGION("backward start %p(%u) to %p\n",CHANGE(rg->last),CHANGE(rg->last)->pos,CHANGE(rg->last)->prev);*/
306 rg->last = CHANGE(rg->last)->prev;
307 #ifdef PROFILE_MARKREGION
308 prof.num_list_walks++;
309 #endif
310 }
311 } else {
312 Tchange *next = CHANGE(rg->last)->next;
313 /* DBG_MARKREGION("find_prev_or_equal_position, current position %u is before the requested position %u\n",CHANGE(rg->last)->pos,position);*/
314 while (next && next->pos <= position) {
315 /* DBG_MARKREGION("forward start %p(%u) to %p(%u), next->next=%p\n",CHANGE(rg->last),CHANGE(rg->last)->pos,next,next->pos,next->next);*/
316 rg->last = next;
317 next = next->next;
318 #ifdef PROFILE_MARKREGION
319 prof.num_list_walks++;
320 #endif
321 }
322 }
323 DBG_MARKREGION("find_prev_or_equal_position, requested position %u, returning change->pos=%d\n",position,rg->last?CHANGE(rg->last)->pos:-1);
324 return CHANGE(rg->last);
325 }
326
327 /* both delete and insert can be handled in a generic way if the new region is either the
328 only region, beyond the end, or before the beginning. */
329 static gboolean
markregion_handle_generic(Tregions * rg,guint markstart,guint markend,guint comparepos,gint offset)330 markregion_handle_generic(Tregions *rg, guint markstart, guint markend, guint comparepos, gint offset)
331 {
332 if (!rg->head) {
333 /* empty region, just append the start and end */
334 DBG_MARKREGION("markregion_handle_generic, empty, just add the first entries\n");
335 rg->head = new_change(markstart, TRUE);
336 rg->last = rg->tail = bf_elist_append(BF_ELIST(rg->head), BF_ELIST(new_change(markend, FALSE)));
337 #ifdef PROFILE_MARKREGION
338 prof.num_new++;
339 #endif
340 return TRUE;
341 }
342 if (CHANGE(rg->tail)->pos < markstart) {
343 DBG_MARKREGION("markregion_handle_generic, markstart (%u) beyond the end (%u), append new entries\n", markstart, CHANGE(rg->tail)->pos);
344 /* the new region is beyond the end */
345 rg->last = rg->tail = bf_elist_append(BF_ELIST(rg->tail), BF_ELIST(new_change(markstart, TRUE)));
346 rg->tail = bf_elist_append(BF_ELIST(rg->tail), BF_ELIST(new_change(markend, FALSE)));
347 #ifdef PROFILE_MARKREGION
348 prof.num_full_append++;
349 #endif
350 return TRUE;
351 }
352 if (CHANGE(rg->head)->pos > comparepos) {
353 Tchange *oldhead = rg->head;
354 DBG_MARKREGION("markregion_handle_generic, comparepos (%u) before the head (%u), prepend new entries and update offsets\n",comparepos,CHANGE(rg->head)->pos);
355 /* the first region is before the current start */
356 rg->last = rg->head = bf_elist_prepend(BF_ELIST(rg->head), BF_ELIST(new_change(markend, FALSE)));
357 rg->head = bf_elist_prepend(BF_ELIST(rg->head), BF_ELIST(new_change(markstart, TRUE)));
358 update_offset(oldhead, offset);
359 #ifdef PROFILE_MARKREGION
360 prof.num_full_prepend++;
361 #endif
362 return TRUE;
363 }
364 return FALSE;
365 }
366 /*
367 markregion_insert is called from the text insert callback
368 */
369 void
markregion_insert(Tregions * rg,guint markstart,guint markend)370 markregion_insert(Tregions *rg, guint markstart, guint markend)
371 {
372 gint offset = markend-markstart;
373 #ifdef PROFILE_MARKREGION
374 prof.num_region_changes++;
375 #endif
376 #ifdef DEVELOPMENT
377 markregion_verify_integrity(rg);
378 if (markstart > markend) {
379 g_print("ABORT: markregion_insert, markstart > markend ?!?!?!\n");
380 g_assert_not_reached();
381 }
382 #endif
383 DBG_MARKREGION("markregion_insert, markstart=%u, markend=%u, offset=%d\n",markstart,markend,offset);
384 if (markstart == markend)
385 return;
386
387 if (markregion_handle_generic(rg, markstart, markend, markstart, offset))
388 return;
389
390 #ifdef PROFILE_MARKREGION
391 prof.num_insert++;
392 #endif
393
394 /* insert somewhere within the existing regions */
395 rg->last = find_prev_or_equal_position(rg, markstart);
396 if (!rg->last && CHANGE(rg->head)->pos > markstart) {
397 rg->head = rg->last = bf_elist_prepend(BF_ELIST(rg->head), BF_ELIST(new_change(markstart, TRUE)));
398 }
399 if (CHANGE(rg->last)->is_start == TRUE) {
400 /* only update the offset */
401 DBG_MARKREGION("update offset, starting at %u\n",CHANGE(CHANGE(rg->last)->next)->pos);
402 update_offset(CHANGE(rg->last)->next, offset);
403 return;
404 }
405 if (CHANGE(rg->last)->pos == markstart) {
406 Tchange *toremove;
407 /* out current start previously ended a region, merge the old and new regions together */
408 toremove = rg->last;
409 rg->last = bf_elist_remove(toremove); /* returns 'change->prev' if there is one */
410 g_slice_free(Tchange,toremove);
411 #ifdef PROFILE_MARKREGION
412 prof.num_allocs--;
413 #endif
414 rg->last = bf_elist_append(BF_ELIST(rg->last), BF_ELIST(new_change(markend, FALSE)));
415 if (CHANGE(rg->last)->next) {
416 update_offset(CHANGE(rg->last)->next, offset);
417 } else {
418 rg->tail = rg->last;
419 }
420 return;
421 }
422 if (CHANGE(rg->last)->pos < markstart) {
423 rg->last = bf_elist_append(BF_ELIST(rg->last), BF_ELIST(new_change(markstart, TRUE)));
424 rg->last = bf_elist_append(BF_ELIST(rg->last), BF_ELIST(new_change(markend, FALSE)));
425 update_offset(CHANGE(rg->last)->next, offset);
426 return;
427 }
428 #ifdef DEVELOPMENT
429 g_print("ABORT: markregion_insert, we should never get to the end of this function\n");
430 g_assert_not_reached();
431 #endif
432 }
433
434
435 /*
436 markregion_delete is called from the text delete callback
437 the markend parameter in markregion_delete should be already corrected for the offset.
438 */
439 void
markregion_delete(Tregions * rg,guint markstart,guint markend,gint offset)440 markregion_delete(Tregions *rg, guint markstart, guint markend, gint offset)
441 {
442 gint comparepos = markend-offset;
443 Tchange *oldlast;
444 #ifdef PROFILE_MARKREGION
445 prof.num_region_changes++;
446 #endif
447
448 #ifdef DEVELOPMENT
449 markregion_verify_integrity(rg);
450 if (markstart > markend) {
451 g_print("ABORT: markregion_insert, markstart > markend ?!?!?!\n");
452 g_assert_not_reached();
453 }
454 #endif
455 DBG_MARKREGION("markregion_delete, %u:%u, update offset with %d, comparepos=%d. head(%d)|tail(%d)\n",markstart,markend,offset,comparepos
456 ,rg->head?CHANGE(rg->head)->pos:-1
457 ,rg->tail?CHANGE(rg->tail)->pos:-1);
458 if (markstart == 0 && markend == 0 && rg->tail) {
459 rg->last = rg->head;
460 while(rg->last && CHANGE(rg->last)->pos <= comparepos) {
461 Tchange *toremove = CHANGE(rg->last);
462 rg->last = CHANGE(rg->last)->next;
463 bf_elist_remove(BF_ELIST(toremove));
464 DBG_MARKREGION("markregion_delete, remove pos=%u, because it is lower than %d\n",toremove->pos,comparepos);
465 g_slice_free(Tchange, toremove);
466 #ifdef PROFILE_MARKREGION
467 prof.num_allocs--;
468 #endif
469 }
470 rg->head = rg->last;
471 if (!rg->last) {
472 rg->tail = NULL;
473 }
474 }
475 if (markstart == markend) {
476 return;
477 }
478
479 if (markregion_handle_generic(rg, markstart, markend, comparepos, offset))
480 return;
481
482 #ifdef PROFILE_MARKREGION
483 prof.num_insert++;
484 #endif
485
486 rg->last = find_prev_or_equal_position(rg, markstart);
487 if (rg->last == NULL) {
488 /* starts before head */
489 DBG_MARKREGION("markregion_delete, prepend start %u to head (%u)\n",markstart,rg->head?CHANGE(rg->head)->pos:-1);
490 rg->last = rg->head = bf_elist_prepend(BF_ELIST(rg->head), BF_ELIST(new_change(markstart, TRUE)));
491 }
492
493 oldlast = rg->last;
494 rg->last = CHANGE(rg->last)->next;
495 /* oldlast has he position _before_ the deleted area */
496 if (CHANGE(oldlast)->is_start == FALSE) {
497 if (oldlast->pos == markstart) {
498 Tchange *toremove = oldlast;
499 /* previous region ends at our start, merge the previous and this region together */
500 oldlast = CHANGE(bf_elist_remove(BF_ELIST(oldlast))); /* returns 'change->prev' if there is one */
501 g_slice_free(Tchange,toremove);
502 #ifdef PROFILE_MARKREGION
503 prof.num_allocs--;
504 #endif
505 } else {
506 /* previous region ended before our start, start a new region */
507 oldlast = CHANGE(bf_elist_append(BF_ELIST(oldlast), BF_ELIST(new_change(markstart, TRUE))));
508 }
509 }
510 /*now remove entries in the deleted area */
511 while(rg->last && CHANGE(rg->last)->pos < comparepos) {
512 Tchange *toremove = CHANGE(rg->last);
513 rg->last = CHANGE(rg->last)->next;
514 bf_elist_remove(BF_ELIST(toremove));
515 /* DBG_MARKREGION("markregion_delete, remove pos=%u, because it is lower than %d\n",toremove->pos,comparepos);*/
516 g_slice_free(Tchange, toremove);
517 #ifdef PROFILE_MARKREGION
518 prof.num_allocs--;
519 #endif
520
521 }
522 /* after the delete loop rg->last points to a position equal or beyond comparepos, or NULL if there was no following position */
523 if (!rg->last) {
524 rg->tail = rg->last = CHANGE(bf_elist_append(BF_ELIST(oldlast), BF_ELIST(new_change(markend, FALSE))));
525 return;
526 }
527
528 if (rg->last && CHANGE(rg->last)->pos == comparepos && CHANGE(rg->last)->is_start == TRUE) {
529 /* if rg->last equals comparepos and is a start, we can remove it to merge the regions. if it is a start,
530 there should also be an end, so removing this entry should not delete the tail! */
531 Tchange *toremove = CHANGE(rg->last);
532 rg->last = CHANGE(rg->last)->next;
533 #ifdef DEVELOPMENT
534 g_assert(rg->last);
535 #endif
536 bf_elist_remove(BF_ELIST(toremove));
537 DBG_MARKREGION("markregion_delete, remove pos=%u, because it equals %d and starts the next region (merge them)\n",toremove->pos,comparepos);
538 #ifdef PROFILE_MARKREGION
539 prof.num_allocs--;
540 #endif
541 g_slice_free(Tchange, toremove);
542 update_offset(rg->last, offset);
543 return;
544 }
545 /*DBG_MARKREGION("rg->last(%u) is_start=%d, oldlast(%u) is_start=%d\n",CHANGE(rg->last)->pos,CHANGE(rg->last)->is_start,oldlast->pos,oldlast->is_start);*/
546 if (CHANGE(rg->last)->is_start == TRUE) {
547 bf_elist_prepend(BF_ELIST(rg->last), BF_ELIST(new_change(markend, FALSE)));
548 }
549 update_offset(rg->last, offset);
550 }
551
552 /*
553 markregion_nochange is called from the scanning engine when the scanning
554 engine detects that the region needs to be enlarged. The code to add these
555 to the Tregions is equal to markregion_delete with an offset of zero.
556 */
557 void
markregion_nochange(Tregions * rg,guint markstart,guint markend)558 markregion_nochange(Tregions *rg, guint markstart, guint markend)
559 {
560 DBG_MARKREGION("markregion_nochange, markstart=%u, markend=%u\n",markstart,markend);
561 #ifdef DEVELOPMENT
562 if (markstart == BF_POSITION_UNDEFINED || markend == BF_POSITION_UNDEFINED) {
563 g_print("ABORT: markregion_nochange is called with a region UNDEFINED, markstart=%d, markend=%d\n",markstart,markend);
564 g_assert_not_reached();
565 }
566 #endif
567 markregion_delete(rg, markstart, markend, 0);
568 }
569
570 /*
571 markregion_get_region returns the first region in Tregions. Some calling funcions
572 (especially find_region_to_scan) call this function multiple times to find the next
573 region, see if it close and merge them. To support this this function returns a pointer
574 to the current TChange, so if you call it the next time, pass this pointer as
575 second parameter, and you get the next one instead of the first region.
576 */
577 gpointer
markregion_get_region(Tregions * rg,gpointer cur,guint * start,guint * end)578 markregion_get_region(Tregions *rg, gpointer cur, guint *start, guint *end)
579 {
580 #ifdef DEVELOPMENT
581 markregion_verify_integrity(rg);
582 #endif
583 DBG_MARKREGION("markregion_get_region, cur->pos(%d), head(%d)|tail(%d)\n",cur?CHANGE(cur)->pos:-1
584 ,rg->head?CHANGE(rg->head)->pos:-1
585 ,rg->tail?CHANGE(rg->tail)->pos:-1);
586 if (cur == NULL) {
587 if (rg->head==NULL) {
588 *start = BF_OFFSET_UNDEFINED;
589 *end = BF_OFFSET_UNDEFINED;
590 return NULL;
591 }
592 cur = rg->head;
593 }
594
595 #ifdef DEVELOPMENT
596 if (!CHANGE(cur)->is_start) {
597 g_print("ABORT: get_region is called, and cur is not a start of region\n");
598 g_assert_not_reached();
599 }
600 #endif
601 *start = CHANGE(cur)->pos;
602 cur = CHANGE(cur)->next;
603 #ifdef DEVELOPMENT
604 if (!cur) {
605 g_print("ABORT: get_region is called, and next cur does not exist\n");
606 g_assert_not_reached();
607 }
608 if (CHANGE(cur)->is_start) {
609 g_print("ABORT: get_region is called, and next cur is a start of region\n");
610 g_assert_not_reached();
611 }
612 #endif
613 *end = CHANGE(cur)->pos;
614 cur = CHANGE(cur)->next;
615 DBG_MARKREGION("markregion_get_region, start=%u,end=%u,cur->pos(%d)\n",*start,*end,cur?CHANGE(cur)->pos:-1);
616 return cur;
617 }
618
619 #endif
620