From 8d7bdfa3fca076b34aaf86548d3243bee11872ad Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Tue, 7 Feb 2017 17:56:50 +0100 Subject: [PATCH 01/70] Provide a new tree data-structure for overlays. * src/itree.c (interval_generator_narrow, interval_generator_next) (interval_node_init, interval_node_begin) (interval_node_end, interval_node_set_region) (interval_tree_create, interval_tree_clear) (interval_tree_init, interval_tree_destroy) (interval_tree_size, interval_tree_insert) (interval_tree_contains, interval_tree_remove) (interval_tree_validate, interval_tree_iter_start) (interval_tree_iter_finish, interval_tree_iter_next) (interval_tree_iter_ensure_space, interval_tree_max_height) (interval_tree_insert_gap, interval_tree_delete_gap) (interval_generator_create, interval_generator_reset) (interval_generator_ensure_space, interval_node_intersects) (interval_generator_next, interval_generator_narrow) (interval_generator_destroy, interval_stack_create) (interval_stack_destroy, interval_stack_clear) (interval_stack_ensure_space, interval_stack_push) (interval_stack_push_flagged, interval_stack_pop) (interval_tree_update_limit, interval_tree_inherit_offset) (interval_tree_propagate_limit, interval_tree_rotate_left) (interval_tree_rotate_right, interval_tree_insert_fix) (interval_tree_remove_fix, interval_tree_transplant) (interval_tree_subtree_min): New file and new functions. * src/itree.h: New file. * configure.ac: Create Makefile for manual overlay tests. * src/Makefile.in: Add itree.o target. * src/alloc.c (build_overlay, mark_overlay, mark_buffer) (sweep_misc, sweep_buffers): Adapt to new tree data-structure. * src/buffer.c (overlays_in, overlays_at): Remove unused arguments prev_ptr and change_req, adapt to new data-structure and reuse code. (copy_overlays, drop_overlays, delete_all_overlays) (reset_buffer, kill-buffer, buffer-swap-text, next_overlay_change) (previous_overlay_change, mouse_face_overlay_overlaps) (disable_line_numbers_overlay_at_eob, overlay_touches_p) (overlay_strings, adjust_overlays_for_insert) (adjust_overlays_for_delete, overlayp, make-overlay, move-overlay) (delete-overlay, overlay-start, overlay-end, overlay-buffer) (overlay-properties, overlays-at, overlays-in) (next-overlay-change, previous-overlay-change, overlay-put) (overlay-get, report_overlay_modification, evaporate_overlays) (init_buffer_once): Adapt to changes and tree data-structure. (overlay-lists, overlay-recenter): Funtions are now obsolete, but kept anyway. (set_buffer_overlays_before, set_buffer_overlays_after) (recenter_overlay_lists,fix_start_end_in_overlays,fix_overlays_before) (unchain_overlay,): Removed functions of the old list data-structure. (swap_buffer_overlays, make_sortvec_item): New functions. (sort_overlays): Adapt to changes and tree data-structure. (sortvec): Moved to buffer.h . (make_lispy_interval_node, overlay_tree, overlay-tree) [ITREE_DEBUG]: New debugging functions. * src/buffer.h (overlays_before, overlays_after): Removed struct member of the list data-structure. (overlays): Added tree struct member. (sortvec): Moved here from buffer.c . (GET_OVERLAYS_AT): Adapt to changes. (set_buffer_intervals, OVERLAY_START, OVERLAY_END, OVERLAY_PLIST): Adapt to tree data-structure. (OVERLAY_POSITION): Removed macro of the list data-structure. (OVERLAY_REAR_ADVANCE_P, OVERLAY_FRONT_ADVANCE_P): New macros. (overlay_start, overlay_end) (set_overlay_region, maybe_alloc_buffer_overlays) (free_buffer_overlays, add_buffer_overlay) (remove_buffer_overlay, buffer_overlay_iter_start) (buffer_overlay_iter_next, buffer_overlay_iter_finish) (buffer_overlay_iter_narrow): New functions. (compare_overlays, make_sortvec_item): Export functions. * src/editfns.c (overlays_around): Reuse overlays_in. (get-pos-property): Adapt to tree data-structure. (transpose-regions): Remove call to deleted function. * src/fileio.c: (insert-file-contents): Remove references to deleted struct member. * src/fns.c (internal_equal): Adapt to tree data-structure. * src/indent.c (check_display_width): Adapt to tree data-structure. (skip_invisible): Remove call to deleted function. * src/insdel.c (adjust_markers_for_insert): Remove calls to deleted functions. * src/intervals.c (adjust_for_invis_intang): Adapt to tree data-structure. * src/keyboard.c (adjust_point_for_property): Adapt to tree data-structure. * src/lisp.h (Lisp_Overlay): Modified struct layout. * src/print.c (temp_output_buffer_setup, print_object): Adapt to tree data-structure. * src/textprop.c (get_char_property_and_overlay): Adapt to tree data-structure. Take advantage of the new data-structure. * src/window.h (overlay_matches_window): New function. * src/xdisp.h (next_overlay_change): Removed function. Use next-overlay-change, which does not use xmalloc anymore. (handle_single_display_spec, load_overlay_strings) (back_to_previous_visible_line_start, note_mouse_highlight): Adapt to tree data-structure. (move_it_to, display_line): Remove calls to deleted functions. * src/xfaces.c (face_at_buffer_position): Adapt to changes and tree data-structure. * test/src/buffer-tests.el: Many tests regarding overlays added. * test/manual/noverlay/itree-tests.c: New file with tests of the tree data-structure on the C level. * test/manual/noverlay/Makefile.in: New file. * test/manual/noverlay/check-sanitize.sh: New file. * test/manual/noverlay/emacs-compat.h: New file. * test/manual/noverlay/.gitignore: New file. * test/manual/noverlay/overlay-perf.el: New file providing performance tests. * test/manual/noverlay/many-errors.h: New file. --- configure.ac | 1 + src/Makefile.in | 1 + src/alloc.c | 49 +- src/buffer.c | 1494 ++--- src/buffer.h | 161 +- src/editfns.c | 56 +- src/fileio.c | 3 +- src/fns.c | 7 +- src/indent.c | 5 +- src/insdel.c | 12 - src/intervals.c | 4 +- src/itree.c | 1138 ++++ src/itree.h | 88 + src/keyboard.c | 4 +- src/lisp.h | 19 +- src/print.c | 12 +- src/textprop.c | 52 +- src/window.h | 10 + src/xdisp.c | 157 +- src/xfaces.c | 17 +- test/manual/noverlay/.gitignore | 1 + test/manual/noverlay/Makefile.in | 32 + test/manual/noverlay/check-sanitize.sh | 11 + test/manual/noverlay/emacs-compat.h | 52 + test/manual/noverlay/itree-tests.c | 1381 +++++ test/manual/noverlay/many-errors.py | 2480 ++++++++ test/manual/noverlay/overlay-perf.el | 764 +++ test/src/buffer-tests.el | 7519 ++++++++++++++++++++++++ 28 files changed, 14170 insertions(+), 1360 deletions(-) create mode 100644 src/itree.c create mode 100644 src/itree.h create mode 100644 test/manual/noverlay/.gitignore create mode 100644 test/manual/noverlay/Makefile.in create mode 100755 test/manual/noverlay/check-sanitize.sh create mode 100644 test/manual/noverlay/emacs-compat.h create mode 100644 test/manual/noverlay/itree-tests.c create mode 100644 test/manual/noverlay/many-errors.py create mode 100644 test/manual/noverlay/overlay-perf.el diff --git a/configure.ac b/configure.ac index eba95e2fb84..76cf5570b52 100644 --- a/configure.ac +++ b/configure.ac @@ -5495,6 +5495,7 @@ if test -f "$srcdir/$opt_makefile.in"; then dnl Again, it's best not to use a variable. Though you can add dnl ", [], [opt_makefile='$opt_makefile']" and it should work. AC_CONFIG_FILES([test/Makefile]) + AC_CONFIG_FILES([test/manual/noverlay/Makefile]) fi diff --git a/src/Makefile.in b/src/Makefile.in index 9a8c9c85f04..8a8df03e49f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -395,6 +395,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ $(XWIDGETS_OBJ) \ profiler.o decompress.o \ thread.o systhread.o \ + itree.o \ $(if $(HYBRID_MALLOC),sheap.o) \ $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) diff --git a/src/alloc.c b/src/alloc.c index 2e6399e7f8d..15a6fc43b72 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -43,6 +43,7 @@ along with GNU Emacs. If not, see . */ #include "frame.h" #include "blockinput.h" #include "termhooks.h" /* For struct terminal. */ +#include "itree.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -3835,16 +3836,19 @@ free_save_value (Lisp_Object save) /* Return a Lisp_Misc_Overlay object with specified START, END and PLIST. */ Lisp_Object -build_overlay (Lisp_Object start, Lisp_Object end, Lisp_Object plist) +build_overlay (ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance, + Lisp_Object plist) { - register Lisp_Object overlay; + Lisp_Object ov = allocate_misc (Lisp_Misc_Overlay); + struct interval_node *node = xmalloc (sizeof (*node)); - overlay = allocate_misc (Lisp_Misc_Overlay); - OVERLAY_START (overlay) = start; - OVERLAY_END (overlay) = end; - set_overlay_plist (overlay, plist); - XOVERLAY (overlay)->next = NULL; - return overlay; + interval_node_init (node, begin, end, front_advance, + rear_advance, ov); + XOVERLAY (ov)->interval = node; + XOVERLAY (ov)->buffer = NULL; + set_overlay_plist (ov, plist); + return ov; } DEFUN ("make-marker", Fmake_marker, Smake_marker, 0, 0, 0, @@ -6280,16 +6284,10 @@ mark_compiled (struct Lisp_Vector *ptr) /* Mark the chain of overlays starting at PTR. */ static void -mark_overlay (struct Lisp_Overlay *ptr) +mark_overlay (struct Lisp_Overlay *ov) { - for (; ptr && !ptr->gcmarkbit; ptr = ptr->next) - { - ptr->gcmarkbit = 1; - /* These two are always markers and can be marked fast. */ - XMARKER (ptr->start)->gcmarkbit = 1; - XMARKER (ptr->end)->gcmarkbit = 1; - mark_object (ptr->plist); - } + ov->gcmarkbit = 1; + mark_object (ov->plist); } /* Mark Lisp_Objects and special pointers in BUFFER. */ @@ -6308,8 +6306,15 @@ mark_buffer (struct buffer *buffer) a special way just before the sweep phase, and after stripping some of its elements that are not needed any more. */ - mark_overlay (buffer->overlays_before); - mark_overlay (buffer->overlays_after); + if (buffer->overlays) + { + struct interval_node *node; + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (buffer))) + mark_overlay (XOVERLAY (node->data)); + buffer_overlay_iter_finish (buffer); + } /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && !VECTOR_MARKED_P (buffer->base_buffer)) @@ -7090,6 +7095,11 @@ sweep_misc (void) unchain_marker (&mblk->markers[i].m.u_marker); else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Finalizer) unchain_finalizer (&mblk->markers[i].m.u_finalizer); + else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Overlay) + { + xfree (mblk->markers[i].m.u_overlay.interval); + mblk->markers[i].m.u_overlay.interval = NULL; + } #ifdef HAVE_MODULES else if (mblk->markers[i].m.u_any.type == Lisp_Misc_User_Ptr) { @@ -7145,6 +7155,7 @@ sweep_buffers (void) if (!VECTOR_MARKED_P (buffer)) { *bprev = buffer->next; + free_buffer_overlays (buffer); lisp_free (buffer); } else diff --git a/src/buffer.c b/src/buffer.c index 76670b89545..21c040ad0e1 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -44,6 +44,7 @@ along with GNU Emacs. If not, see . */ #include "keymap.h" #include "frame.h" #include "xwidget.h" +#include "itree.h" #ifdef WINDOWSNT #include "w32heap.h" /* for mmap_* */ @@ -120,7 +121,7 @@ static Lisp_Object QSFundamental; /* A string "Fundamental". */ static void alloc_buffer_text (struct buffer *, ptrdiff_t); static void free_buffer_text (struct buffer *b); -static struct Lisp_Overlay * copy_overlays (struct buffer *, struct Lisp_Overlay *); +static void copy_overlays (struct buffer *, struct buffer *); static void modify_overlay (struct buffer *, ptrdiff_t, ptrdiff_t); static Lisp_Object buffer_lisp_local_variables (struct buffer *, bool); @@ -600,51 +601,30 @@ even if it is dead. The return value is never nil. */) } -/* Return a list of overlays which is a copy of the overlay list - LIST, but for buffer B. */ +/* Copy overlays of buffer FROM to buffer TO. */ -static struct Lisp_Overlay * -copy_overlays (struct buffer *b, struct Lisp_Overlay *list) +static void +copy_overlays (struct buffer *from, struct buffer *to) { - struct Lisp_Overlay *result = NULL, *tail = NULL; + eassert (to && ! to->overlays); - for (; list; list = list->next) + struct interval_node *node; + + if (! from->overlays) + return; + + buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (from))) { - Lisp_Object overlay, start, end; - struct Lisp_Marker *m; - - eassert (MARKERP (list->start)); - m = XMARKER (list->start); - start = build_marker (b, m->charpos, m->bytepos); - XMARKER (start)->insertion_type = m->insertion_type; - - eassert (MARKERP (list->end)); - m = XMARKER (list->end); - end = build_marker (b, m->charpos, m->bytepos); - XMARKER (end)->insertion_type = m->insertion_type; - - overlay = build_overlay (start, end, Fcopy_sequence (list->plist)); - if (tail) - tail = tail->next = XOVERLAY (overlay); - else - result = tail = XOVERLAY (overlay); + Lisp_Object ov = node->data; + Lisp_Object copy = build_overlay (node->begin, node->end, + node->front_advance, + node->rear_advance, + Fcopy_sequence (OVERLAY_PLIST (ov))); + add_buffer_overlay (to, XOVERLAY (copy)); } - - return result; -} - -/* Set an appropriate overlay of B. */ - -static void -set_buffer_overlays_before (struct buffer *b, struct Lisp_Overlay *o) -{ - b->overlays_before = o; -} - -static void -set_buffer_overlays_after (struct buffer *b, struct Lisp_Overlay *o) -{ - b->overlays_after = o; + buffer_overlay_iter_finish (from); } /* Clone per-buffer values of buffer FROM. @@ -681,8 +661,7 @@ clone_per_buffer_values (struct buffer *from, struct buffer *to) memcpy (to->local_flags, from->local_flags, sizeof to->local_flags); - set_buffer_overlays_before (to, copy_overlays (to, from->overlays_before)); - set_buffer_overlays_after (to, copy_overlays (to, from->overlays_after)); + copy_overlays (from, to); /* Get (a copy of) the alist of Lisp-level local variables of FROM and install that in TO. */ @@ -867,17 +846,16 @@ CLONE nil means the indirect buffer's state is reset to default values. */) return buf; } -/* Mark OV as no longer associated with B. */ +/* Mark OV as no longer associated with its buffer. */ static void -drop_overlay (struct buffer *b, struct Lisp_Overlay *ov) +drop_overlay (struct Lisp_Overlay *ov) { - eassert (b == XBUFFER (Fmarker_buffer (ov->start))); - modify_overlay (b, marker_position (ov->start), - marker_position (ov->end)); - unchain_marker (XMARKER (ov->start)); - unchain_marker (XMARKER (ov->end)); + if (! ov->buffer) + return; + modify_overlay (ov->buffer, overlay_start (ov), overlay_end (ov)); + remove_buffer_overlay (ov->buffer, ov); } /* Delete all overlays of B and reset its overlay lists. */ @@ -885,26 +863,20 @@ drop_overlay (struct buffer *b, struct Lisp_Overlay *ov) void delete_all_overlays (struct buffer *b) { - struct Lisp_Overlay *ov, *next; + struct interval_node *node; - /* FIXME: Since each drop_overlay will scan BUF_MARKERS to unlink its - markers, we have an unneeded O(N^2) behavior here. */ - for (ov = b->overlays_before; ov; ov = next) + if (! b->overlays) + return; + + buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (b))) { - drop_overlay (b, ov); - next = ov->next; - ov->next = NULL; + modify_overlay (b, node->begin, node->end); + /* Where are the nodes freed ? --ap */ + XOVERLAY (node->data)->buffer = NULL; } - - for (ov = b->overlays_after; ov; ov = next) - { - drop_overlay (b, ov); - next = ov->next; - ov->next = NULL; - } - - set_buffer_overlays_before (b, NULL); - set_buffer_overlays_after (b, NULL); + buffer_overlay_iter_finish (b); + interval_tree_clear (b->overlays); } /* Reinitialize everything about a buffer except its name and contents @@ -932,9 +904,7 @@ reset_buffer (register struct buffer *b) b->auto_save_failure_time = 0; bset_auto_save_file_name (b, Qnil); bset_read_only (b, Qnil); - set_buffer_overlays_before (b, NULL); - set_buffer_overlays_after (b, NULL); - b->overlay_center = BEG; + b->overlays = NULL; bset_mark_active (b, Qnil); bset_point_before_scroll (b, Qnil); bset_file_format (b, Qnil); @@ -1843,10 +1813,8 @@ cleaning up all windows currently displaying the buffer to be killed. */) /* Perhaps we should explicitly free the interval tree here... */ } - /* Since we've unlinked the markers, the overlays can't be here any more - either. */ - b->overlays_before = NULL; - b->overlays_after = NULL; + delete_all_overlays (b); + free_buffer_overlays (b); /* Reset the local variables, so that this buffer's local values won't be protected from GC. They would be protected @@ -2254,6 +2222,31 @@ advance_to_char_boundary (ptrdiff_t byte_pos) return byte_pos; } +static void +swap_buffer_overlays (struct buffer *buffer, struct buffer *other) +{ + struct interval_node *node; + + if (buffer->overlays) + { + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + XOVERLAY (node->data)->buffer = other; + buffer_overlay_iter_finish (buffer); + } + if (other->overlays) + { + buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (other))) + XOVERLAY (node->data)->buffer = buffer; + buffer_overlay_iter_finish (other); + } + /* Swap the interval trees. */ + void *tmp = buffer->overlays; + buffer->overlays = other->overlays; + other->overlays = tmp; +} + DEFUN ("buffer-swap-text", Fbuffer_swap_text, Sbuffer_swap_text, 1, 1, 0, doc: /* Swap the text between current buffer and BUFFER. @@ -2324,9 +2317,6 @@ results, see Info node `(elisp)Swapping Text'. */) swapfield (bidi_paragraph_cache, struct region_cache *); current_buffer->prevent_redisplay_optimizations_p = 1; other_buffer->prevent_redisplay_optimizations_p = 1; - swapfield (overlays_before, struct Lisp_Overlay *); - swapfield (overlays_after, struct Lisp_Overlay *); - swapfield (overlay_center, ptrdiff_t); swapfield_ (undo_list, Lisp_Object); swapfield_ (mark, Lisp_Object); swapfield_ (enable_multibyte_characters, Lisp_Object); @@ -2349,6 +2339,7 @@ results, see Info node `(elisp)Swapping Text'. */) current_buffer->text->end_unchanged = current_buffer->text->gpt; other_buffer->text->beg_unchanged = other_buffer->text->gpt; other_buffer->text->end_unchanged = other_buffer->text->gpt; + swap_buffer_overlays (current_buffer, other_buffer); { struct Lisp_Marker *m; for (m = BUF_MARKERS (current_buffer); m; m = m->next) @@ -2763,276 +2754,151 @@ swap_out_buffer_local_variables (struct buffer *b) } } } - -/* Find all the overlays in the current buffer that contain position POS. - Return the number found, and store them in a vector in *VEC_PTR. - Store in *LEN_PTR the size allocated for the vector. - Store in *NEXT_PTR the next position after POS where an overlay starts, - or ZV if there are no more overlays between POS and ZV. - Store in *PREV_PTR the previous position before POS where an overlay ends, - or where an overlay starts which ends at or after POS; - or BEGV if there are no such overlays from BEGV to POS. - NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info. - *VEC_PTR and *LEN_PTR should contain a valid vector and size - when this function is called. - - If EXTEND, make the vector bigger if necessary. - If not, never extend the vector, - and store only as many overlays as will fit. - But still return the total number of overlays. - - If CHANGE_REQ, any position written into *PREV_PTR or - *NEXT_PTR is guaranteed to be not equal to POS, unless it is the - default (BEGV or ZV). */ - -ptrdiff_t -overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr, - ptrdiff_t *len_ptr, - ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr, bool change_req) -{ - Lisp_Object overlay, start, end; - struct Lisp_Overlay *tail; - ptrdiff_t idx = 0; - ptrdiff_t len = *len_ptr; - Lisp_Object *vec = *vec_ptr; - ptrdiff_t next = ZV; - ptrdiff_t prev = BEGV; - bool inhibit_storing = 0; - - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; - - XSETMISC (overlay, tail); - - start = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (endpos < pos) - { - if (prev < endpos) - prev = endpos; - break; - } - startpos = OVERLAY_POSITION (start); - /* This one ends at or after POS - so its start counts for PREV_PTR if it's before POS. */ - if (prev < startpos && startpos < pos) - prev = startpos; - if (endpos == pos) - continue; - if (startpos <= pos) - { - if (idx == len) - { - /* The supplied vector is full. - Either make it bigger, or don't store any more in it. */ - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } - - if (!inhibit_storing) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - else if (startpos < next) - next = startpos; - } - - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; - - XSETMISC (overlay, tail); - - start = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (start); - if (pos < startpos) - { - if (startpos < next) - next = startpos; - break; - } - endpos = OVERLAY_POSITION (end); - if (pos < endpos) - { - if (idx == len) - { - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } - - if (!inhibit_storing) - vec[idx] = overlay; - idx++; - - if (startpos < pos && startpos > prev) - prev = startpos; - } - else if (endpos < pos && endpos > prev) - prev = endpos; - else if (endpos == pos && startpos > prev - && (!change_req || startpos < pos)) - prev = startpos; - } - - if (next_ptr) - *next_ptr = next; - if (prev_ptr) - *prev_ptr = prev; - return idx; -} /* Find all the overlays in the current buffer that overlap the range - BEG-END, or are empty at BEG, or are empty at END provided END - denotes the position at the end of the current buffer. + [BEG, END). + + If EMPTY is true, include empty overlays in that range and also at + END, provided END denotes the position at the end of the buffer. Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. Store in *NEXT_PTR the next position after POS where an overlay starts, or ZV if there are no more overlays. - Store in *PREV_PTR the previous position before POS where an overlay ends, - or BEGV if there are no previous overlays. - NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info. + NEXT_PTR may be 0, meaning don't store that info. *VEC_PTR and *LEN_PTR should contain a valid vector and size when this function is called. - If EXTEND, make the vector bigger if necessary. - If not, never extend the vector, - and store only as many overlays as will fit. - But still return the total number of overlays. */ + If EXTEND, make the vector bigger if necessary. If not, never + extend the vector, and store only as many overlays as will fit. + But still return the total number of overlays. +*/ -static ptrdiff_t -overlays_in (EMACS_INT beg, EMACS_INT end, bool extend, - Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, - ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr) +ptrdiff_t +overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, bool empty, + ptrdiff_t *next_ptr) { - Lisp_Object overlay, ostart, oend; - struct Lisp_Overlay *tail; ptrdiff_t idx = 0; ptrdiff_t len = *len_ptr; - Lisp_Object *vec = *vec_ptr; ptrdiff_t next = ZV; - ptrdiff_t prev = BEGV; - bool inhibit_storing = 0; - bool end_is_Z = end == Z; + Lisp_Object *vec = *vec_ptr; - for (tail = current_buffer->overlays_before; tail; tail = tail->next) + struct interval_node *node; + + if (! current_buffer->overlays) + return idx; + + buffer_overlay_iter_start (current_buffer, beg, + /* Find empty OV at Z ? */ + (end >= Z && empty) ? Z + 1 : ZV, + ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (current_buffer))) { - ptrdiff_t startpos, endpos; + if (node->begin > end) + { + next = min (next, node->begin); + break; + } + else if (node->begin == end) + { + next = node->begin; + if ((! empty || end < Z) && beg < end) + break; + } - XSETMISC (overlay, tail); + if (! empty && node->begin == node->end) + continue; - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (oend); - if (endpos < beg) - { - if (prev < endpos) - prev = endpos; - break; - } - startpos = OVERLAY_POSITION (ostart); - /* Count an interval if it overlaps the range, is empty at the - start of the range, or is empty at END provided END denotes the - end of the buffer. */ - if ((beg < endpos && startpos < end) - || (startpos == endpos - && (beg == endpos || (end_is_Z && endpos == end)))) - { - if (idx == len) - { - /* The supplied vector is full. - Either make it bigger, or don't store any more in it. */ - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } - - if (!inhibit_storing) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - else if (startpos < next) - next = startpos; + if (extend && idx == len) + { + vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, + sizeof *vec); + *vec_ptr = vec; + len = *len_ptr; + } + if (idx < len) + vec[idx] = node->data; + /* Keep counting overlays even if we can't return them all. */ + idx++; } - - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; - - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (ostart); - if (end < startpos) - { - if (startpos < next) - next = startpos; - break; - } - endpos = OVERLAY_POSITION (oend); - /* Count an interval if it overlaps the range, is empty at the - start of the range, or is empty at END provided END denotes the - end of the buffer. */ - if ((beg < endpos && startpos < end) - || (startpos == endpos - && (beg == endpos || (end_is_Z && endpos == end)))) - { - if (idx == len) - { - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } - - if (!inhibit_storing) - vec[idx] = overlay; - idx++; - } - else if (endpos < beg && endpos > prev) - prev = endpos; - } - + buffer_overlay_iter_finish (current_buffer); if (next_ptr) - *next_ptr = next; - if (prev_ptr) - *prev_ptr = prev; + *next_ptr = next ? next : ZV; + return idx; } +/* Find all non-empty overlays in the current buffer that contain + position POS. + + See overlays_in for the meaning of the arguments. + */ + +ptrdiff_t +overlays_at (ptrdiff_t pos, bool extend, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, + ptrdiff_t *next_ptr) +{ + return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, false, next_ptr); +} + +ptrdiff_t +next_overlay_change (ptrdiff_t pos) +{ + ptrdiff_t next = ZV; + struct interval_node *node; + + if (! current_buffer->overlays) + return next; + + buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->begin > pos) + { + /* If we reach this branch, node->begin must be the least upper bound + of pos, because the search is limited to [pos,next) . */ + eassert (node->begin < next); + next = node->begin; + break; + } + else if (node->begin < node->end && node->end < next) + { + next = node->end; + buffer_overlay_iter_narrow (current_buffer, pos, next); + } + } + buffer_overlay_iter_finish (current_buffer); + + return next; +} + +ptrdiff_t +previous_overlay_change (ptrdiff_t pos) +{ + struct interval_node *node; + ptrdiff_t prev = BEGV; + + if (! current_buffer->overlays) + return prev; + + buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->end < pos) + prev = node->end; + else + prev = max (prev, node->begin); + buffer_overlay_iter_narrow (current_buffer, prev, pos); + } + buffer_overlay_iter_finish (current_buffer); + + return prev; +} /* Return true if there exists an overlay with a non-nil `mouse-face' property overlapping OVERLAY. */ @@ -3040,8 +2906,8 @@ overlays_in (EMACS_INT beg, EMACS_INT end, bool extend, bool mouse_face_overlay_overlaps (Lisp_Object overlay) { - ptrdiff_t start = OVERLAY_POSITION (OVERLAY_START (overlay)); - ptrdiff_t end = OVERLAY_POSITION (OVERLAY_END (overlay)); + ptrdiff_t start = OVERLAY_START (overlay); + ptrdiff_t end = OVERLAY_END (overlay); ptrdiff_t n, i, size; Lisp_Object *v, tem; Lisp_Object vbuf[10]; @@ -3049,11 +2915,11 @@ mouse_face_overlay_overlaps (Lisp_Object overlay) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (start, end, 0, &v, &size, NULL, NULL); + n = overlays_in (start, end, 0, &v, &size, true, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (start, end, 0, &v, &n, NULL, NULL); + overlays_in (start, end, 0, &v, &n, true, NULL); } for (i = 0; i < n; ++i) @@ -3095,52 +2961,34 @@ disable_line_numbers_overlay_at_eob (void) } -/* Fast function to just test if we're at an overlay boundary. */ +/* Fast function to just test if we're at an overlay boundary. + + Returns true if some overlay starts or ends (or both) at POS, +*/ bool overlay_touches_p (ptrdiff_t pos) { - Lisp_Object overlay; - struct Lisp_Overlay *tail; + struct interval_node *node; + bool result = false; - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t endpos; + if (! current_buffer->overlays) + return false; - XSETMISC (overlay ,tail); - eassert (OVERLAYP (overlay)); + /* We need to find overlays ending in pos, as well as empty ones at + pos. */ + buffer_overlay_iter_start (current_buffer, + pos - 1, pos + 1, ITREE_DESCENDING); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos == pos || OVERLAY_POSITION (OVERLAY_START (overlay)) == pos) - return 1; - } + while (! result && (node = buffer_overlay_iter_next (current_buffer))) + result = (node->begin == pos || node->end == pos); - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos; + buffer_overlay_iter_finish (current_buffer); - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); - - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - if (pos < startpos) - break; - if (startpos == pos || OVERLAY_POSITION (OVERLAY_END (overlay)) == pos) - return 1; - } - return 0; + return result; } - -struct sortvec -{ - Lisp_Object overlay; - ptrdiff_t beg, end; - EMACS_INT priority; - EMACS_INT spriority; /* Secondary priority. */ -}; -static int + +int compare_overlays (const void *v1, const void *v2) { const struct sortvec *s1 = v1; @@ -3169,6 +3017,33 @@ compare_overlays (const void *v1, const void *v2) return XLI (s1->overlay) < XLI (s2->overlay) ? -1 : 1; } +void +make_sortvec_item (struct sortvec *item, Lisp_Object overlay) +{ + Lisp_Object tem; + /* This overlay is good and counts: put it into sortvec. */ + item->overlay = overlay; + item->beg = OVERLAY_START (overlay); + item->end = OVERLAY_END (overlay); + tem = Foverlay_get (overlay, Qpriority); + if (NILP (tem)) + { + item->priority = 0; + item->spriority = 0; + } + else if (INTEGERP (tem)) + { + item->priority = XINT (tem); + item->spriority = 0; + } + else if (CONSP (tem)) + { + Lisp_Object car = XCAR (tem); + Lisp_Object cdr = XCDR (tem); + item->priority = INTEGERP (car) ? XINT (car) : 0; + item->spriority = INTEGERP (cdr) ? XINT (cdr) : 0; + } +} /* Sort an array of overlays by priority. The array is modified in place. The return value is the new size; this may be smaller than the original size if some of the overlays were invalid or were window-specific. */ @@ -3185,47 +3060,18 @@ sort_overlays (Lisp_Object *overlay_vec, ptrdiff_t noverlays, struct window *w) for (i = 0, j = 0; i < noverlays; i++) { - Lisp_Object tem; Lisp_Object overlay; overlay = overlay_vec[i]; if (OVERLAYP (overlay) - && OVERLAY_POSITION (OVERLAY_START (overlay)) > 0 - && OVERLAY_POSITION (OVERLAY_END (overlay)) > 0) + && OVERLAY_START (overlay) > 0 + && OVERLAY_END (overlay) > 0) { - /* If we're interested in a specific window, then ignore - overlays that are limited to some other window. */ - if (w) - { - Lisp_Object window; - - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != w) - continue; - } - - /* This overlay is good and counts: put it into sortvec. */ - sortvec[j].overlay = overlay; - sortvec[j].beg = OVERLAY_POSITION (OVERLAY_START (overlay)); - sortvec[j].end = OVERLAY_POSITION (OVERLAY_END (overlay)); - tem = Foverlay_get (overlay, Qpriority); - if (NILP (tem)) - { - sortvec[j].priority = 0; - sortvec[j].spriority = 0; - } - else if (INTEGERP (tem)) - { - sortvec[j].priority = XINT (tem); - sortvec[j].spriority = 0; - } - else if (CONSP (tem)) - { - Lisp_Object car = XCAR (tem); - Lisp_Object cdr = XCDR (tem); - sortvec[j].priority = INTEGERP (car) ? XINT (car) : 0; - sortvec[j].spriority = INTEGERP (cdr) ? XINT (cdr) : 0; - } + /* If we're interested in a specific window, then ignore + overlays that are limited to some other window. */ + if (w && ! overlay_matches_window (w, overlay)) + continue; + make_sortvec_item (sortvec + j, overlay); j++; } } @@ -3340,68 +3186,44 @@ ptrdiff_t overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) { Lisp_Object overlay, window, str; - struct Lisp_Overlay *ov; - ptrdiff_t startpos, endpos; + ptrdiff_t obegin, oend; bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters)); + struct interval_node *node; overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; - for (ov = current_buffer->overlays_before; ov; ov = ov->next) + + buffer_overlay_iter_start (current_buffer, + pos - 1, pos + 1, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) { - XSETMISC (overlay, ov); + overlay = node->data; eassert (OVERLAYP (overlay)); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos != pos && startpos != pos) + obegin = node->begin; + oend = node->end; + + if (oend != pos && obegin != pos) continue; window = Foverlay_get (overlay, Qwindow); if (WINDOWP (window) && XWINDOW (window) != w) continue; - if (startpos == pos + if (obegin == pos && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) record_overlay_string (&overlay_heads, str, - (startpos == endpos + (obegin == oend ? Foverlay_get (overlay, Qafter_string) : Qnil), Foverlay_get (overlay, Qpriority), - endpos - startpos); - else if (endpos == pos + oend - obegin); + else if (oend == pos && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))) record_overlay_string (&overlay_tails, str, Qnil, Foverlay_get (overlay, Qpriority), - endpos - startpos); + oend - obegin); } - for (ov = current_buffer->overlays_after; ov; ov = ov->next) - { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); + buffer_overlay_iter_finish (current_buffer); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (startpos > pos) - break; - if (endpos != pos && startpos != pos) - continue; - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != w) - continue; - if (startpos == pos - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) - record_overlay_string (&overlay_heads, str, - (startpos == endpos - ? Foverlay_get (overlay, Qafter_string) - : Qnil), - Foverlay_get (overlay, Qpriority), - endpos - startpos); - else if (endpos == pos - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))) - record_overlay_string (&overlay_tails, str, Qnil, - Foverlay_get (overlay, Qpriority), - endpos - startpos); - } if (overlay_tails.used > 1) qsort (overlay_tails.buf, overlay_tails.used, sizeof (struct sortstr), cmp_for_strings); @@ -3456,385 +3278,26 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) } return 0; } + -/* Shift overlays in BUF's overlay lists, to center the lists at POS. */ - -void -recenter_overlay_lists (struct buffer *buf, ptrdiff_t pos) -{ - Lisp_Object overlay, beg, end; - struct Lisp_Overlay *prev, *tail, *next; - - /* See if anything in overlays_before should move to overlays_after. */ - - /* We don't strictly need prev in this loop; it should always be nil. - But we use it for symmetry and in case that should cease to be true - with some future change. */ - prev = NULL; - for (tail = buf->overlays_before; tail; prev = tail, tail = next) - { - next = tail->next; - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); - - beg = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - - if (OVERLAY_POSITION (end) > pos) - { - /* OVERLAY needs to be moved. */ - ptrdiff_t where = OVERLAY_POSITION (beg); - struct Lisp_Overlay *other, *other_prev; - - /* Splice the cons cell TAIL out of overlays_before. */ - if (prev) - prev->next = next; - else - set_buffer_overlays_before (buf, next); - - /* Search thru overlays_after for where to put it. */ - other_prev = NULL; - for (other = buf->overlays_after; other; - other_prev = other, other = other->next) - { - Lisp_Object otherbeg, otheroverlay; - - XSETMISC (otheroverlay, other); - eassert (OVERLAYP (otheroverlay)); - - otherbeg = OVERLAY_START (otheroverlay); - if (OVERLAY_POSITION (otherbeg) >= where) - break; - } - - /* Add TAIL to overlays_after before OTHER. */ - tail->next = other; - if (other_prev) - other_prev->next = tail; - else - set_buffer_overlays_after (buf, tail); - tail = prev; - } - else - /* We've reached the things that should stay in overlays_before. - All the rest of overlays_before must end even earlier, - so stop now. */ - break; - } - - /* See if anything in overlays_after should be in overlays_before. */ - prev = NULL; - for (tail = buf->overlays_after; tail; prev = tail, tail = next) - { - next = tail->next; - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); - - beg = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - - /* Stop looking, when we know that nothing further - can possibly end before POS. */ - if (OVERLAY_POSITION (beg) > pos) - break; - - if (OVERLAY_POSITION (end) <= pos) - { - /* OVERLAY needs to be moved. */ - ptrdiff_t where = OVERLAY_POSITION (end); - struct Lisp_Overlay *other, *other_prev; - - /* Splice the cons cell TAIL out of overlays_after. */ - if (prev) - prev->next = next; - else - set_buffer_overlays_after (buf, next); - - /* Search thru overlays_before for where to put it. */ - other_prev = NULL; - for (other = buf->overlays_before; other; - other_prev = other, other = other->next) - { - Lisp_Object otherend, otheroverlay; - - XSETMISC (otheroverlay, other); - eassert (OVERLAYP (otheroverlay)); - - otherend = OVERLAY_END (otheroverlay); - if (OVERLAY_POSITION (otherend) <= where) - break; - } - - /* Add TAIL to overlays_before before OTHER. */ - tail->next = other; - if (other_prev) - other_prev->next = tail; - else - set_buffer_overlays_before (buf, tail); - tail = prev; - } - } - - buf->overlay_center = pos; -} - void adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length) { /* After an insertion, the lists are still sorted properly, but we may need to update the value of the overlay center. */ - if (current_buffer->overlay_center >= pos) - current_buffer->overlay_center += length; + if (! current_buffer->overlays) + return; + interval_tree_insert_gap (current_buffer->overlays, pos, length); } void adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length) { - if (current_buffer->overlay_center < pos) - /* The deletion was to our right. No change needed; the before- and - after-lists are still consistent. */ - ; - else if (current_buffer->overlay_center - pos > length) - /* The deletion was to our left. We need to adjust the center value - to account for the change in position, but the lists are consistent - given the new value. */ - current_buffer->overlay_center -= length; - else - /* We're right in the middle. There might be things on the after-list - that now belong on the before-list. Recentering will move them, - and also update the center point. */ - recenter_overlay_lists (current_buffer, pos); -} - -/* Fix up overlays that were garbled as a result of permuting markers - in the range START through END. Any overlay with at least one - endpoint in this range will need to be unlinked from the overlay - list and reinserted in its proper place. - Such an overlay might even have negative size at this point. - If so, we'll make the overlay empty. */ -void -fix_start_end_in_overlays (register ptrdiff_t start, register ptrdiff_t end) -{ - Lisp_Object overlay; - struct Lisp_Overlay *before_list; - struct Lisp_Overlay *after_list; - /* These are either nil, indicating that before_list or after_list - should be assigned, or the cons cell the cdr of which should be - assigned. */ - struct Lisp_Overlay *beforep = NULL, *afterp = NULL; - /* 'Parent', likewise, indicates a cons cell or - current_buffer->overlays_before or overlays_after, depending - which loop we're in. */ - struct Lisp_Overlay *tail, *parent; - ptrdiff_t startpos, endpos; - - /* This algorithm shifts links around instead of consing and GCing. - The loop invariant is that before_list (resp. after_list) is a - well-formed list except that its last element, the CDR of beforep - (resp. afterp) if beforep (afterp) isn't nil or before_list - (after_list) if it is, is still uninitialized. So it's not a bug - that before_list isn't initialized, although it may look - strange. */ - for (parent = NULL, tail = current_buffer->overlays_before; tail;) - { - XSETMISC (overlay, tail); - - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - - /* If the overlay is backwards, make it empty. */ - if (endpos < startpos) - { - startpos = endpos; - Fset_marker (OVERLAY_START (overlay), make_number (startpos), - Qnil); - } - - if (endpos < start) - break; - - if (endpos < end - || (startpos >= start && startpos < end)) - { - /* Add it to the end of the wrong list. Later on, - recenter_overlay_lists will move it to the right place. */ - if (endpos < current_buffer->overlay_center) - { - if (!afterp) - after_list = tail; - else - afterp->next = tail; - afterp = tail; - } - else - { - if (!beforep) - before_list = tail; - else - beforep->next = tail; - beforep = tail; - } - if (!parent) - set_buffer_overlays_before (current_buffer, tail->next); - else - parent->next = tail->next; - tail = tail->next; - } - else - parent = tail, tail = parent->next; - } - for (parent = NULL, tail = current_buffer->overlays_after; tail;) - { - XSETMISC (overlay, tail); - - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - - /* If the overlay is backwards, make it empty. */ - if (endpos < startpos) - { - startpos = endpos; - Fset_marker (OVERLAY_START (overlay), make_number (startpos), - Qnil); - } - - if (startpos >= end) - break; - - if (startpos >= start - || (endpos >= start && endpos < end)) - { - if (endpos < current_buffer->overlay_center) - { - if (!afterp) - after_list = tail; - else - afterp->next = tail; - afterp = tail; - } - else - { - if (!beforep) - before_list = tail; - else - beforep->next = tail; - beforep = tail; - } - if (!parent) - set_buffer_overlays_after (current_buffer, tail->next); - else - parent->next = tail->next; - tail = tail->next; - } - else - parent = tail, tail = parent->next; - } - - /* Splice the constructed (wrong) lists into the buffer's lists, - and let the recenter function make it sane again. */ - if (beforep) - { - beforep->next = current_buffer->overlays_before; - set_buffer_overlays_before (current_buffer, before_list); - } - - if (afterp) - { - afterp->next = current_buffer->overlays_after; - set_buffer_overlays_after (current_buffer, after_list); - } - recenter_overlay_lists (current_buffer, current_buffer->overlay_center); -} - -/* We have two types of overlay: the one whose ending marker is - after-insertion-marker (this is the usual case) and the one whose - ending marker is before-insertion-marker. When `overlays_before' - contains overlays of the latter type and the former type in this - order and both overlays end at inserting position, inserting a text - increases only the ending marker of the latter type, which results - in incorrect ordering of `overlays_before'. - - This function fixes ordering of overlays in the slot - `overlays_before' of the buffer *BP. Before the insertion, `point' - was at PREV, and now is at POS. */ - -void -fix_overlays_before (struct buffer *bp, ptrdiff_t prev, ptrdiff_t pos) -{ - /* If parent is nil, replace overlays_before; otherwise, parent->next. */ - struct Lisp_Overlay *tail = bp->overlays_before, *parent = NULL, *right_pair; - Lisp_Object tem; - ptrdiff_t end UNINIT; - - /* After the insertion, the several overlays may be in incorrect - order. The possibility is that, in the list `overlays_before', - an overlay which ends at POS appears after an overlay which ends - at PREV. Since POS is greater than PREV, we must fix the - ordering of these overlays, by moving overlays ends at POS before - the overlays ends at PREV. */ - - /* At first, find a place where disordered overlays should be linked - in. It is where an overlay which end before POS exists. (i.e. an - overlay whose ending marker is after-insertion-marker if disorder - exists). */ - while (tail - && (XSETMISC (tem, tail), - (end = OVERLAY_POSITION (OVERLAY_END (tem))) >= pos)) - { - parent = tail; - tail = tail->next; - } - - /* If we don't find such an overlay, - or the found one ends before PREV, - or the found one is the last one in the list, - we don't have to fix anything. */ - if (!tail || end < prev || !tail->next) + if (! current_buffer->overlays) return; - - right_pair = parent; - parent = tail; - tail = tail->next; - - /* Now, end position of overlays in the list TAIL should be before - or equal to PREV. In the loop, an overlay which ends at POS is - moved ahead to the place indicated by the CDR of RIGHT_PAIR. If - we found an overlay which ends before PREV, the remaining - overlays are in correct order. */ - while (tail) - { - XSETMISC (tem, tail); - end = OVERLAY_POSITION (OVERLAY_END (tem)); - - if (end == pos) - { /* This overlay is disordered. */ - struct Lisp_Overlay *found = tail; - - /* Unlink the found overlay. */ - tail = found->next; - parent->next = tail; - /* Move an overlay at RIGHT_PLACE to the next of the found one, - and link it into the right place. */ - if (!right_pair) - { - found->next = bp->overlays_before; - set_buffer_overlays_before (bp, found); - } - else - { - found->next = right_pair->next; - right_pair->next = found; - } - } - else if (end == prev) - { - parent = tail; - tail = tail->next; - } - else /* No more disordered overlay. */ - break; - } + interval_tree_delete_gap (current_buffer->overlays, pos, length); } + DEFUN ("overlayp", Foverlayp, Soverlayp, 1, 1, 0, doc: /* Return t if OBJECT is an overlay. */) @@ -3853,10 +3316,11 @@ for the front of the overlay advance when text is inserted there The fifth arg REAR-ADVANCE, if non-nil, makes the marker for the rear of the overlay advance when text is inserted there \(which means the text *is* included in the overlay). */) - (Lisp_Object beg, Lisp_Object end, Lisp_Object buffer, + (Lisp_Object begin, Lisp_Object end, Lisp_Object buffer, Lisp_Object front_advance, Lisp_Object rear_advance) { - Lisp_Object overlay; + Lisp_Object ov; + ptrdiff_t obegin, oend; struct buffer *b; if (NILP (buffer)) @@ -3864,53 +3328,35 @@ for the rear of the overlay advance when text is inserted there else CHECK_BUFFER (buffer); - if (MARKERP (beg) && !EQ (Fmarker_buffer (beg), buffer)) - signal_error ("Marker points into wrong buffer", beg); + b = XBUFFER (buffer); + if (! BUFFER_LIVE_P (b)) + error ("Attempt to create overlay in a dead buffer"); + + if (MARKERP (begin) && !EQ (Fmarker_buffer (begin), buffer)) + signal_error ("Marker points into wrong buffer", begin); if (MARKERP (end) && !EQ (Fmarker_buffer (end), buffer)) signal_error ("Marker points into wrong buffer", end); - CHECK_NUMBER_COERCE_MARKER (beg); + CHECK_NUMBER_COERCE_MARKER (begin); CHECK_NUMBER_COERCE_MARKER (end); - if (XINT (beg) > XINT (end)) + if (XINT (begin) > XINT (end)) { Lisp_Object temp; - temp = beg; beg = end; end = temp; + temp = begin; begin = end; end = temp; } - b = XBUFFER (buffer); - - beg = Fset_marker (Fmake_marker (), beg, buffer); - end = Fset_marker (Fmake_marker (), end, buffer); - - if (!NILP (front_advance)) - XMARKER (beg)->insertion_type = 1; - if (!NILP (rear_advance)) - XMARKER (end)->insertion_type = 1; - - overlay = build_overlay (beg, end, Qnil); - - /* Put the new overlay on the wrong list. */ - end = OVERLAY_END (overlay); - if (OVERLAY_POSITION (end) < b->overlay_center) - { - eassert (b->overlays_after || (XOVERLAY (overlay)->next == NULL)); - XOVERLAY (overlay)->next = b->overlays_after; - set_buffer_overlays_after (b, XOVERLAY (overlay)); - } - else - { - eassert (b->overlays_before || (XOVERLAY (overlay)->next == NULL)); - XOVERLAY (overlay)->next = b->overlays_before; - set_buffer_overlays_before (b, XOVERLAY (overlay)); - } - /* This puts it in the right list, and in the right order. */ - recenter_overlay_lists (b, b->overlay_center); + obegin = clip_to_bounds (BEG, XINT (begin), b->text->z); + oend = clip_to_bounds (obegin, XINT (end), b->text->z); + ov = build_overlay (obegin, oend, + ! NILP (front_advance), + ! NILP (rear_advance), Qnil); + add_buffer_overlay (b, XOVERLAY (ov)); /* We don't need to redisplay the region covered by the overlay, because the overlay has no properties at the moment. */ - return overlay; + return ov; } /* Mark a section of BUF as needing redisplay because of overlays changes. */ @@ -3932,35 +3378,6 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) ++BUF_OVERLAY_MODIFF (buf); } -/* Remove OVERLAY from LIST. */ - -static struct Lisp_Overlay * -unchain_overlay (struct Lisp_Overlay *list, struct Lisp_Overlay *overlay) -{ - register struct Lisp_Overlay *tail, **prev = &list; - - for (tail = list; tail; prev = &tail->next, tail = *prev) - if (tail == overlay) - { - *prev = overlay->next; - overlay->next = NULL; - break; - } - return list; -} - -/* Remove OVERLAY from both overlay lists of B. */ - -static void -unchain_both (struct buffer *b, Lisp_Object overlay) -{ - struct Lisp_Overlay *ov = XOVERLAY (overlay); - - set_buffer_overlays_before (b, unchain_overlay (b->overlays_before, ov)); - set_buffer_overlays_after (b, unchain_overlay (b->overlays_after, ov)); - eassert (XOVERLAY (overlay)->next == NULL); -} - DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. @@ -3976,7 +3393,7 @@ buffer. */) CHECK_OVERLAY (overlay); if (NILP (buffer)) - buffer = Fmarker_buffer (OVERLAY_START (overlay)); + buffer = Foverlay_buffer (overlay); if (NILP (buffer)) XSETBUFFER (buffer, current_buffer); CHECK_BUFFER (buffer); @@ -4000,25 +3417,28 @@ buffer. */) specbind (Qinhibit_quit, Qt); - obuffer = Fmarker_buffer (OVERLAY_START (overlay)); + obuffer = Foverlay_buffer (overlay); b = XBUFFER (buffer); if (!NILP (obuffer)) { ob = XBUFFER (obuffer); - o_beg = OVERLAY_POSITION (OVERLAY_START (overlay)); - o_end = OVERLAY_POSITION (OVERLAY_END (overlay)); - - unchain_both (ob, overlay); + o_beg = OVERLAY_START (overlay); + o_end = OVERLAY_END (overlay); } + if (! EQ (buffer, obuffer)) + { + if (! NILP (obuffer)) + remove_buffer_overlay (XBUFFER (obuffer), XOVERLAY (overlay)); + add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay)); + } /* Set the overlay boundaries, which may clip them. */ - Fset_marker (OVERLAY_START (overlay), beg, buffer); - Fset_marker (OVERLAY_END (overlay), end, buffer); + set_overlay_region (XOVERLAY (overlay), XINT (beg), XINT (end)); - n_beg = marker_position (OVERLAY_START (overlay)); - n_end = marker_position (OVERLAY_END (overlay)); + n_beg = OVERLAY_START (overlay); + n_end = OVERLAY_END (overlay); /* If the overlay has changed buffers, do a thorough redisplay. */ if (!EQ (buffer, obuffer)) @@ -4046,22 +3466,6 @@ buffer. */) if (n_beg == n_end && !NILP (Foverlay_get (overlay, Qevaporate))) return unbind_to (count, Fdelete_overlay (overlay)); - /* Put the overlay into the new buffer's overlay lists, first on the - wrong list. */ - if (n_end < b->overlay_center) - { - XOVERLAY (overlay)->next = b->overlays_after; - set_buffer_overlays_after (b, XOVERLAY (overlay)); - } - else - { - XOVERLAY (overlay)->next = b->overlays_before; - set_buffer_overlays_before (b, XOVERLAY (overlay)); - } - - /* This puts it in the right list, and in the right order. */ - recenter_overlay_lists (b, b->overlay_center); - return unbind_to (count, overlay); } @@ -4069,21 +3473,18 @@ DEFUN ("delete-overlay", Fdelete_overlay, Sdelete_overlay, 1, 1, 0, doc: /* Delete the overlay OVERLAY from its buffer. */) (Lisp_Object overlay) { - Lisp_Object buffer; struct buffer *b; ptrdiff_t count = SPECPDL_INDEX (); CHECK_OVERLAY (overlay); - buffer = Fmarker_buffer (OVERLAY_START (overlay)); - if (NILP (buffer)) + b = OVERLAY_BUFFER (overlay); + if (! b) return Qnil; - b = XBUFFER (buffer); specbind (Qinhibit_quit, Qt); - unchain_both (b, overlay); - drop_overlay (b, XOVERLAY (overlay)); + drop_overlay (XOVERLAY (overlay)); /* When deleting an overlay with before or after strings, turn off display optimizations for the affected buffer, on the basis that @@ -4114,8 +3515,10 @@ DEFUN ("overlay-start", Foverlay_start, Soverlay_start, 1, 1, 0, (Lisp_Object overlay) { CHECK_OVERLAY (overlay); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; - return (Fmarker_position (OVERLAY_START (overlay))); + return make_number (OVERLAY_START (overlay)); } DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 0, @@ -4123,8 +3526,10 @@ DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 0, (Lisp_Object overlay) { CHECK_OVERLAY (overlay); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; - return (Fmarker_position (OVERLAY_END (overlay))); + return make_number (OVERLAY_END (overlay)); } DEFUN ("overlay-buffer", Foverlay_buffer, Soverlay_buffer, 1, 1, 0, @@ -4132,9 +3537,16 @@ DEFUN ("overlay-buffer", Foverlay_buffer, Soverlay_buffer, 1, 1, 0, Return nil if OVERLAY has been deleted. */) (Lisp_Object overlay) { + Lisp_Object buffer; + CHECK_OVERLAY (overlay); - return Fmarker_buffer (OVERLAY_START (overlay)); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; + + XSETBUFFER (buffer, OVERLAY_BUFFER (overlay)); + + return buffer; } DEFUN ("overlay-properties", Foverlay_properties, Soverlay_properties, 1, 1, 0, @@ -4145,7 +3557,7 @@ OVERLAY. */) { CHECK_OVERLAY (overlay); - return Fcopy_sequence (XOVERLAY (overlay)->plist); + return Fcopy_sequence (OVERLAY_PLIST (overlay)); } @@ -4169,8 +3581,7 @@ If SORTED is non-nil, then sort them by decreasing priority. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ - noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, - NULL, NULL, 0); + noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, NULL); if (!NILP (sorted)) noverlays = sort_overlays (overlay_vec, noverlays, @@ -4213,8 +3624,7 @@ end of the buffer. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ - noverlays = overlays_in (XINT (beg), XINT (end), 1, &overlay_vec, &len, - NULL, NULL); + noverlays = overlays_in (XINT (beg), XINT (end), 1, &overlay_vec, &len, true, NULL); /* Make a list of them all. */ result = Flist (noverlays, overlay_vec); @@ -4230,39 +3640,12 @@ If there are no overlay boundaries from POS to (point-max), the value is (point-max). */) (Lisp_Object pos) { - ptrdiff_t i, len, noverlays; - ptrdiff_t endpos; - Lisp_Object *overlay_vec; - CHECK_NUMBER_COERCE_MARKER (pos); if (!buffer_has_overlays ()) return make_number (ZV); - len = 10; - overlay_vec = xmalloc (len * sizeof *overlay_vec); - - /* Put all the overlays we want in a vector in overlay_vec. - Store the length in len. - endpos gets the position where the next overlay starts. */ - noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, - &endpos, 0, 1); - - /* If any of these overlays ends before endpos, - use its ending point instead. */ - for (i = 0; i < noverlays; i++) - { - Lisp_Object oend; - ptrdiff_t oendpos; - - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); - if (oendpos < endpos) - endpos = oendpos; - } - - xfree (overlay_vec); - return make_number (endpos); + return make_number (next_overlay_change (XINT (pos))); } DEFUN ("previous-overlay-change", Fprevious_overlay_change, @@ -4272,32 +3655,15 @@ If there are no overlay boundaries from (point-min) to POS, the value is (point-min). */) (Lisp_Object pos) { - ptrdiff_t prevpos; - Lisp_Object *overlay_vec; - ptrdiff_t len; CHECK_NUMBER_COERCE_MARKER (pos); if (!buffer_has_overlays ()) return make_number (BEGV); - /* At beginning of buffer, we know the answer; - avoid bug subtracting 1 below. */ - if (XINT (pos) == BEGV) - return pos; - - len = 10; - overlay_vec = xmalloc (len * sizeof *overlay_vec); - - /* Put all the overlays we want in a vector in overlay_vec. - Store the length in len. - prevpos gets the position of the previous change. */ - overlays_at (XINT (pos), 1, &overlay_vec, &len, - 0, &prevpos, 1); - - xfree (overlay_vec); - return make_number (prevpos); + return make_number (previous_overlay_change (XINT (pos))); } + /* These functions are for debugging overlays. */ @@ -4307,24 +3673,21 @@ The car has all the overlays before the overlay center; the cdr has all the overlays after the overlay center. Recentering overlays moves overlays between these lists. The lists you get are copies, so that changing them has no effect. -However, the overlays you get are the real objects that the buffer uses. */) +However, the overlays you get are the real objects that the buffer uses. */) (void) { - struct Lisp_Overlay *ol; - Lisp_Object before = Qnil, after = Qnil, tmp; + Lisp_Object overlays = Qnil; - for (ol = current_buffer->overlays_before; ol; ol = ol->next) + if (current_buffer->overlays) { - XSETMISC (tmp, ol); - before = Fcons (tmp, before); - } - for (ol = current_buffer->overlays_after; ol; ol = ol->next) - { - XSETMISC (tmp, ol); - after = Fcons (tmp, after); - } + struct interval_node *node; - return Fcons (Fnreverse (before), Fnreverse (after)); + buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + overlays = Fcons (node->data, overlays); + buffer_overlay_iter_finish (current_buffer); + } + return Fcons (overlays, Qnil); } DEFUN ("overlay-recenter", Foverlay_recenter, Soverlay_recenter, 1, 1, 0, @@ -4333,11 +3696,8 @@ That makes overlay lookup faster for positions near POS (but perhaps slower for positions far away from POS). */) (Lisp_Object pos) { - ptrdiff_t p; CHECK_NUMBER_COERCE_MARKER (pos); - - p = clip_to_bounds (PTRDIFF_MIN, XINT (pos), PTRDIFF_MAX); - recenter_overlay_lists (current_buffer, p); + /* Noop */ return Qnil; } @@ -4354,12 +3714,13 @@ DEFUN ("overlay-put", Foverlay_put, Soverlay_put, 3, 3, 0, VALUE will be returned.*/) (Lisp_Object overlay, Lisp_Object prop, Lisp_Object value) { - Lisp_Object tail, buffer; + Lisp_Object tail; + struct buffer *b; bool changed; CHECK_OVERLAY (overlay); - buffer = Fmarker_buffer (OVERLAY_START (overlay)); + b = OVERLAY_BUFFER (overlay); for (tail = XOVERLAY (overlay)->plist; CONSP (tail) && CONSP (XCDR (tail)); @@ -4375,15 +3736,14 @@ VALUE will be returned.*/) set_overlay_plist (overlay, Fcons (prop, Fcons (value, XOVERLAY (overlay)->plist))); found: - if (! NILP (buffer)) + if (b) { if (changed) - modify_overlay (XBUFFER (buffer), - marker_position (OVERLAY_START (overlay)), - marker_position (OVERLAY_END (overlay))); + modify_overlay (b, OVERLAY_START (overlay), + OVERLAY_END (overlay)); if (EQ (prop, Qevaporate) && ! NILP (value) - && (OVERLAY_POSITION (OVERLAY_START (overlay)) - == OVERLAY_POSITION (OVERLAY_END (overlay)))) + && (OVERLAY_START (overlay) + == OVERLAY_END (overlay))) Fdelete_overlay (overlay); } @@ -4441,14 +3801,9 @@ void report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3) { - Lisp_Object prop, overlay; - struct Lisp_Overlay *tail; /* True if this change is an insertion. */ bool insertion = (after ? XFASTINT (arg3) == 0 : EQ (start, end)); - overlay = Qnil; - tail = NULL; - /* We used to run the functions as soon as we found them and only register them in last_overlay_modification_hooks for the purpose of the `after' case. But running elisp code as we traverse the list of overlays is @@ -4459,31 +3814,35 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (!after) { + struct interval_node *node; + EMACS_INT begin_arg = XFASTINT (start); + EMACS_INT end_arg = XFASTINT (end); /* We are being called before a change. Scan the overlays to find the functions to call. */ last_overlay_modification_hooks_used = 0; - for (tail = current_buffer->overlays_before; tail; tail = tail->next) + + if (! current_buffer->overlays) + return; + buffer_overlay_iter_start (current_buffer, + begin_arg - (insertion ? 1 : 0), + end_arg + (insertion ? 1 : 0), + ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) { - ptrdiff_t startpos, endpos; - Lisp_Object ostart, oend; + Lisp_Object overlay = node->data; + ptrdiff_t obegin = OVERLAY_START (overlay); + ptrdiff_t oend = OVERLAY_END (overlay); + Lisp_Object prop; - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (oend); - if (XFASTINT (start) > endpos) - break; - startpos = OVERLAY_POSITION (ostart); - if (insertion && (XFASTINT (start) == startpos - || XFASTINT (end) == startpos)) + if (insertion && (begin_arg == obegin + || end_arg == obegin)) { prop = Foverlay_get (overlay, Qinsert_in_front_hooks); if (!NILP (prop)) add_overlay_mod_hooklist (prop, overlay); } - if (insertion && (XFASTINT (start) == endpos - || XFASTINT (end) == endpos)) + if (insertion && (begin_arg == oend + || end_arg == oend)) { prop = Foverlay_get (overlay, Qinsert_behind_hooks); if (!NILP (prop)) @@ -4491,52 +3850,15 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, } /* Test for intersecting intervals. This does the right thing for both insertion and deletion. */ - if (XFASTINT (end) > startpos && XFASTINT (start) < endpos) - { - prop = Foverlay_get (overlay, Qmodification_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - } - - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; - Lisp_Object ostart, oend; - - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (ostart); - endpos = OVERLAY_POSITION (oend); - if (XFASTINT (end) < startpos) - break; - if (insertion && (XFASTINT (start) == startpos - || XFASTINT (end) == startpos)) - { - prop = Foverlay_get (overlay, Qinsert_in_front_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - if (insertion && (XFASTINT (start) == endpos - || XFASTINT (end) == endpos)) - { - prop = Foverlay_get (overlay, Qinsert_behind_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - /* Test for intersecting intervals. This does the right thing - for both insertion and deletion. */ - if (XFASTINT (end) > startpos && XFASTINT (start) < endpos) + if (! insertion || (end_arg > obegin && begin_arg < oend)) { prop = Foverlay_get (overlay, Qmodification_hooks); if (!NILP (prop)) add_overlay_mod_hooklist (prop, overlay); } } + buffer_overlay_iter_finish (current_buffer); } - { /* Call the functions recorded in last_overlay_modification_hooks. First copy the vector contents, in case some of these hooks @@ -4558,7 +3880,7 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, function is never called to record the overlay modification hook functions in the last_overlay_modification_hooks array, so anything we find there is not ours. */ - if (XMARKER (OVERLAY_START (ovl))->buffer != current_buffer) + if (OVERLAY_BUFFER (ovl) != current_buffer) return; } @@ -4598,34 +3920,22 @@ call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay, bool after, void evaporate_overlays (ptrdiff_t pos) { - Lisp_Object overlay, hit_list; - struct Lisp_Overlay *tail; + Lisp_Object hit_list; + struct interval_node *node; + + if (! current_buffer->overlays) + return; hit_list = Qnil; - if (pos <= current_buffer->overlay_center) - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t endpos; - XSETMISC (overlay, tail); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos == pos && OVERLAY_POSITION (OVERLAY_START (overlay)) == pos - && ! NILP (Foverlay_get (overlay, Qevaporate))) - hit_list = Fcons (overlay, hit_list); - } - else - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos; - XSETMISC (overlay, tail); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - if (startpos > pos) - break; - if (startpos == pos && OVERLAY_POSITION (OVERLAY_END (overlay)) == pos - && ! NILP (Foverlay_get (overlay, Qevaporate))) - hit_list = Fcons (overlay, hit_list); - } + buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->end == pos + && ! NILP (Foverlay_get (node->data, Qevaporate))) + hit_list = Fcons (node->data, hit_list); + } + buffer_overlay_iter_finish (current_buffer); for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); } @@ -5212,9 +4522,7 @@ init_buffer_once (void) bset_mark_active (&buffer_defaults, Qnil); bset_file_format (&buffer_defaults, Qnil); bset_auto_save_file_format (&buffer_defaults, Qt); - set_buffer_overlays_before (&buffer_defaults, NULL); - set_buffer_overlays_after (&buffer_defaults, NULL); - buffer_defaults.overlay_center = BEG; + buffer_defaults.overlays = NULL; XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8); bset_truncate_lines (&buffer_defaults, Qnil); @@ -5434,6 +4742,48 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, emacs_abort (); } +#ifdef ITREE_DEBUG +static Lisp_Object +make_lispy_interval_node (const struct interval_node *node) +{ + return listn (CONSTYPE_HEAP, 12, + intern (":begin"), + make_number (node->begin), + intern (":end"), + make_number (node->end), + intern (":limit"), + make_number (node->limit), + intern (":offset"), + make_number (node->offset), + intern (":rear-advance"), + node->rear_advance ? Qt : Qnil, + intern (":front-advance"), + node->front_advance ? Qt : Qnil); +} + +static Lisp_Object +overlay_tree (const struct interval_tree *tree, + const struct interval_node *node) +{ + if (node == &tree->nil) + return Qnil; + return list3 (make_lispy_interval_node (node), + overlay_tree (tree, node->left), + overlay_tree (tree, node->right)); +} + +DEFUN ("overlay-tree", Foverlay_tree, Soverlay_tree, 0, 1, 0, + doc: /* Get the overlay tree for BUFFER. */) + (Lisp_Object buffer) +{ + struct buffer *b = decode_buffer (buffer); + if (! b->overlays) + return Qnil; + return overlay_tree (b->overlays, b->overlays->root); +} +#endif + + /* Initialize the buffer routines. */ void @@ -6303,6 +5653,10 @@ Functions running this hook are, `get-buffer-create', defsubr (&Srestore_buffer_modified_p); Fput (intern_c_string ("erase-buffer"), Qdisabled, Qt); + +#ifdef ITREE_DEBUG + defsubr (&Soverlay_tree); +#endif } void diff --git a/src/buffer.h b/src/buffer.h index ac7c5a54679..ef31ad1ed9d 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -26,6 +26,7 @@ along with GNU Emacs. If not, see . */ #include "character.h" #include "lisp.h" +#include "itree.h" INLINE_HEADER_BEGIN @@ -877,16 +878,8 @@ struct buffer /* Non-zero whenever the narrowing is changed in this buffer. */ bool_bf clip_changed : 1; - /* List of overlays that end at or before the current center, - in order of end-position. */ - struct Lisp_Overlay *overlays_before; - - /* List of overlays that end after the current center, - in order of start-position. */ - struct Lisp_Overlay *overlays_after; - - /* Position where the overlay lists are centered. */ - ptrdiff_t overlay_center; + /* The inveral tree containing this buffer's overlays. */ + struct interval_tree *overlays; /* Changes in the buffer are recorded here for undo, and t means don't record anything. This information belongs to the base @@ -896,6 +889,14 @@ struct buffer Lisp_Object undo_list_; }; +struct sortvec +{ + Lisp_Object overlay; + ptrdiff_t beg, end; + EMACS_INT priority; + EMACS_INT spriority; /* Secondary priority. */ +}; + INLINE bool BUFFERP (Lisp_Object a) { @@ -1109,8 +1110,11 @@ extern void delete_all_overlays (struct buffer *); extern void reset_buffer (struct buffer *); extern void compact_buffer (struct buffer *); extern void evaporate_overlays (ptrdiff_t); -extern ptrdiff_t overlays_at (EMACS_INT, bool, Lisp_Object **, - ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool); +extern ptrdiff_t overlays_at (ptrdiff_t, bool, Lisp_Object **, ptrdiff_t *, ptrdiff_t *); +extern ptrdiff_t overlays_in (ptrdiff_t, ptrdiff_t, bool, Lisp_Object **, + ptrdiff_t *, bool, ptrdiff_t *); +extern ptrdiff_t previous_overlay_change (ptrdiff_t); +extern ptrdiff_t next_overlay_change (ptrdiff_t); extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *); extern void recenter_overlay_lists (struct buffer *, ptrdiff_t); extern ptrdiff_t overlay_strings (ptrdiff_t, struct window *, unsigned char **); @@ -1162,18 +1166,16 @@ record_unwind_current_buffer (void) If NEXTP is non-NULL, return next overlay there. See overlay_at arg CHANGE_REQ for meaning of CHRQ arg. */ -#define GET_OVERLAYS_AT(posn, overlays, noverlays, nextp, chrq) \ +#define GET_OVERLAYS_AT(posn, overlays, noverlays, next) \ do { \ ptrdiff_t maxlen = 40; \ SAFE_NALLOCA (overlays, 1, maxlen); \ - (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, \ - nextp, NULL, chrq); \ + (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \ if ((noverlays) > maxlen) \ { \ maxlen = noverlays; \ SAFE_NALLOCA (overlays, 1, maxlen); \ - (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, \ - nextp, NULL, chrq); \ + (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \ } \ } while (false) @@ -1208,7 +1210,8 @@ set_buffer_intervals (struct buffer *b, INTERVAL i) INLINE bool buffer_has_overlays (void) { - return current_buffer->overlays_before || current_buffer->overlays_after; + return current_buffer->overlays + && (interval_tree_size (current_buffer->overlays) > 0); } /* Return character code of multi-byte form at byte position POS. If POS @@ -1248,23 +1251,124 @@ buffer_window_count (struct buffer *b) /* Overlays */ -/* Return the marker that stands for where OV starts in the buffer. */ +INLINE ptrdiff_t +overlay_start (struct Lisp_Overlay *ov) +{ + if (! ov->buffer) + return -1; + return interval_node_begin (ov->buffer->overlays, ov->interval); +} -#define OVERLAY_START(OV) XOVERLAY (OV)->start +INLINE ptrdiff_t +overlay_end (struct Lisp_Overlay *ov) +{ + if (! ov->buffer) + return -1; + return interval_node_end (ov->buffer->overlays, ov->interval); +} -/* Return the marker that stands for where OV ends in the buffer. */ +INLINE void +set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) +{ + eassert (ov->buffer); + begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); + end = clip_to_bounds (begin, end, ov->buffer->text->z); + interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); +} -#define OVERLAY_END(OV) XOVERLAY (OV)->end +INLINE void +maybe_alloc_buffer_overlays (struct buffer *b) +{ + if (! b->overlays) + b->overlays = interval_tree_create (); +} + +/* FIXME: Actually this does not free any overlay, but the tree + only. --ap */ + +INLINE void +free_buffer_overlays (struct buffer *b) +{ + eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); + if (b->overlays) + { + interval_tree_destroy (b->overlays); + b->overlays = NULL; + } +} + +INLINE void +add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (! ov->buffer); + maybe_alloc_buffer_overlays (b); + ov->buffer = b; + interval_tree_insert (b->overlays, ov->interval); +} + +INLINE void +remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (b->overlays); + eassert (ov->buffer == b); + interval_tree_remove (ov->buffer->overlays, ov->interval); + ov->buffer = NULL; +} + +INLINE void +buffer_overlay_iter_start (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (b->overlays) + interval_tree_iter_start (b->overlays, begin, end, order); +} + +INLINE struct interval_node* +buffer_overlay_iter_next (struct buffer *b) +{ + if (! b->overlays) + return NULL; + return interval_tree_iter_next (b->overlays); +} + +INLINE void +buffer_overlay_iter_finish (struct buffer *b) +{ + if (b->overlays) + interval_tree_iter_finish (b->overlays); +} + +INLINE void +buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) +{ + if (b->overlays) + interval_tree_iter_narrow (b->overlays, begin, end); +} + +/* Return the start of OV in its buffer, or -1 if OV is not associated + with any buffer. */ + +#define OVERLAY_START(OV) (overlay_start (XOVERLAY (OV))) + +/* Return the end of OV in its buffer, or -1. */ + +#define OVERLAY_END(OV) (overlay_end (XOVERLAY (OV))) /* Return the plist of overlay OV. */ -#define OVERLAY_PLIST(OV) XOVERLAY (OV)->plist +#define OVERLAY_PLIST(OV) (XOVERLAY (OV)->plist) -/* Return the actual buffer position for the marker P. - We assume you know which buffer it's pointing into. */ +/* Return the buffer of overlay OV. */ -#define OVERLAY_POSITION(P) \ - (MARKERP (P) ? marker_position (P) : (emacs_abort (), 0)) +#define OVERLAY_BUFFER(OV) (XOVERLAY (OV)->buffer) + +/* Return true, if OV's rear-advance is set. */ + +#define OVERLAY_REAR_ADVANCE_P(OV) (XOVERLAY (OV)->interval->rear_advance) + +/* Return true, if OV's front-advance is set. */ + +#define OVERLAY_FRONT_ADVANCE_P(OV) (XOVERLAY (OV)->interval->front_advance) /*********************************************************************** @@ -1405,4 +1509,7 @@ lowercasep (int c) INLINE_HEADER_END +int compare_overlays (const void *v1, const void *v2); +void make_sortvec_item (struct sortvec *item, Lisp_Object overlay); + #endif /* EMACS_BUFFER_H */ diff --git a/src/editfns.c b/src/editfns.c index 4dcf7cbe6ef..8628b1b2d49 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -457,51 +457,9 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) of length LEN. */ static ptrdiff_t -overlays_around (EMACS_INT pos, Lisp_Object *vec, ptrdiff_t len) +overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - Lisp_Object overlay, start, end; - struct Lisp_Overlay *tail; - ptrdiff_t startpos, endpos; - ptrdiff_t idx = 0; - - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - XSETMISC (overlay, tail); - - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (endpos < pos) - break; - start = OVERLAY_START (overlay); - startpos = OVERLAY_POSITION (start); - if (startpos <= pos) - { - if (idx < len) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - } - - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - XSETMISC (overlay, tail); - - start = OVERLAY_START (overlay); - startpos = OVERLAY_POSITION (start); - if (pos < startpos) - break; - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (pos <= endpos) - { - if (idx < len) - vec[idx] = overlay; - idx++; - } - } - - return idx; + return overlays_in (pos - 1, pos, false, &vec, &len, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, @@ -561,11 +519,10 @@ at POSITION. */) if (!NILP (tem)) { /* Check the overlay is indeed active at point. */ - Lisp_Object start = OVERLAY_START (ol), finish = OVERLAY_END (ol); - if ((OVERLAY_POSITION (start) == posn - && XMARKER (start)->insertion_type == 1) - || (OVERLAY_POSITION (finish) == posn - && XMARKER (finish)->insertion_type == 0)) + if ((OVERLAY_START (ol) == posn + && OVERLAY_FRONT_ADVANCE_P (ol)) + || (OVERLAY_END (ol) == posn + && ! OVERLAY_REAR_ADVANCE_P (ol))) ; /* The overlay will not cover a char inserted at point. */ else { @@ -5385,7 +5342,6 @@ Transposing beyond buffer boundaries is an error. */) transpose_markers (start1, end1, start2, end2, start1_byte, start1_byte + len1_byte, start2_byte, start2_byte + len2_byte); - fix_start_end_in_overlays (start1, end2); } else { diff --git a/src/fileio.c b/src/fileio.c index 11370279d1b..6b22b29aa70 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3656,8 +3656,7 @@ by calling `format-decode', which see. */) bset_read_only (buf, Qnil); bset_filename (buf, Qnil); bset_undo_list (buf, Qt); - eassert (buf->overlays_before == NULL); - eassert (buf->overlays_after == NULL); + eassert (buf->overlays == NULL); set_buffer_internal (buf); Ferase_buffer (); diff --git a/src/fns.c b/src/fns.c index 2311a6e041b..9f411036825 100644 --- a/src/fns.c +++ b/src/fns.c @@ -2240,10 +2240,9 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind, return false; if (OVERLAYP (o1)) { - if (!internal_equal (OVERLAY_START (o1), OVERLAY_START (o2), - equal_kind, depth + 1, ht) - || !internal_equal (OVERLAY_END (o1), OVERLAY_END (o2), - equal_kind, depth + 1, ht)) + if (OVERLAY_START (o1) != OVERLAY_START (o2) + || OVERLAY_END (o1) != OVERLAY_END (o2) + || OVERLAY_BUFFER (o1) != OVERLAY_BUFFER (o2)) return false; o1 = XOVERLAY (o1)->plist; o2 = XOVERLAY (o2)->plist; diff --git a/src/indent.c b/src/indent.c index 26507b5eb5b..8ac7c6ef109 100644 --- a/src/indent.c +++ b/src/indent.c @@ -225,9 +225,6 @@ skip_invisible (ptrdiff_t pos, ptrdiff_t *next_boundary_p, ptrdiff_t to, Lisp_Ob XSETFASTINT (position, pos); XSETBUFFER (buffer, current_buffer); - /* Give faster response for overlay lookup near POS. */ - recenter_overlay_lists (current_buffer, pos); - /* We must not advance farther than the next overlay change. The overlay change might change the invisible property; or there might be overlay strings to be displayed there. */ @@ -501,7 +498,7 @@ check_display_width (ptrdiff_t pos, ptrdiff_t col, ptrdiff_t *endpos) { ptrdiff_t start; if (OVERLAYP (overlay)) - *endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); + *endpos = OVERLAY_END (overlay); else get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil); diff --git a/src/insdel.c b/src/insdel.c index 5dfc62843a7..3e9f0c90e34 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -276,7 +276,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, ptrdiff_t to, ptrdiff_t to_byte, bool before_markers) { struct Lisp_Marker *m; - bool adjusted = 0; ptrdiff_t nchars = to - from; ptrdiff_t nbytes = to_byte - from_byte; @@ -292,8 +291,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, { m->bytepos = to_byte; m->charpos = to; - if (m->insertion_type) - adjusted = 1; } } else if (m->bytepos > from_byte) @@ -302,15 +299,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, m->charpos += nchars; } } - - /* Adjusting only markers whose insertion-type is t may result in - - disordered start and end in overlays, and - - disordered overlays in the slot `overlays_before' of current_buffer. */ - if (adjusted) - { - fix_start_end_in_overlays (from, to); - fix_overlays_before (current_buffer, from, to); - } } /* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters. diff --git a/src/intervals.c b/src/intervals.c index e711212d744..3db80ebed4a 100644 --- a/src/intervals.c +++ b/src/intervals.c @@ -1810,8 +1810,8 @@ adjust_for_invis_intang (ptrdiff_t pos, ptrdiff_t test_offs, ptrdiff_t adj, == (test_offs == 0 ? 1 : -1)) /* Invisible property is from an overlay. */ : (test_offs == 0 - ? XMARKER (OVERLAY_START (invis_overlay))->insertion_type == 0 - : XMARKER (OVERLAY_END (invis_overlay))->insertion_type == 1))) + ? ! OVERLAY_FRONT_ADVANCE_P (invis_overlay) + : OVERLAY_REAR_ADVANCE_P (invis_overlay)))) pos += adj; return pos; diff --git a/src/itree.c b/src/itree.c new file mode 100644 index 00000000000..0c10100eef7 --- /dev/null +++ b/src/itree.c @@ -0,0 +1,1138 @@ +/* This file implements an efficient interval data-structure. + +Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) + +This file is not part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#include +#include +#include "lisp.h" +#include "itree.h" + +/* + Intervals of the form [BEGIN, END), are stored as nodes inside a RB + tree, sorted by BEGIN . The core operation of this tree (besides + insert, remove, etc.) is finding all intervals intersecting with + some given interval. In order to perform this operation + efficiently, every node stores a third value called LIMIT. (See + https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree and its + source Introduction to Algorithms (Section 14.3), Cormen et al. .) + + ==== Finding intervals ==== + + If we search for all intervals intersecting with (X, Y], we look at + some node and test whether + + NODE.BEGIN > Y + + Due to the invariant of the search tree, we know, that we may + safely prune NODE's right subtree if this test succeeds, since all + intervals begin strictly after Y. + + But we can not make such an assumptions about the left tree, since + all we know is that the intervals in this subtree must start before + or at NODE.BEGIN. So we can't tell, whether they end before X or + not. To solve this problem we add another attribute to each node, + called LIMIT. + + The LIMIT of a node is the largest END value occurring in the nodes + subtree (including the node itself). Thus, we may look at the left + child of some NODE and test whether + + NODE.left.LIMIT < X + + and this tells us, if all intervals in the left subtree of NODE end + before X and if they can be pruned. + + Conversely, if this inequality is false, the left subtree must + contain at least one intersecting interval, giving a resulting time + complexity of O(K*log(N)) for this operation, where K is the size + of the result set and N the size of the tree. + + ==== Adjusting intervals ==== + + Since this data-structure will be used for overlays in an Emacs + buffer, a second core operation implements the ability to insert or + delete gaps in resp. from the tree. This models the insertion + resp. deletion of text in a buffer and the effects it may have on + the positions of overlays. + + Consider this: Something gets inserted at position P into a buffer + and assume that all overlays occur strictly after P. Ordinarily, + we would have to iterate all overlays and increment their BEGIN and + END values accordingly (the insertion of text pushes them back). + In order to avoid this, we introduce yet another node attribute, + called OFFSET. + + The OFFSET of some some subtree, represented by its root, is the + amount of shift that needs to be applied to its BEGIN, END and + LIMIT values, in order to get to the real values. Coming back to + the example, all we would need to do in this case, is to increment + the OFFSET of the tree's root, without any traversal of the tree + itself. + + As a consequence, the real values of BEGIN, END and LIMIT of some + NODE need to be computed by incrementing them by the sum of NODE's + OFFSET and all of its ancestors offsets. Therefore, we store a + counter (otick) inside every node and also the tree, by which we + remember the fact, that a node's path to the root has no offsets + applied (i.e. its values are up to date). This is the case if some + node's value differs from the tree's one, the later of which is + incremented whenever some node's offset has changed. +*/ + +static struct interval_node *interval_tree_validate(struct interval_tree *, struct interval_node *); +static void interval_generator_ensure_space(struct interval_generator *); +static bool interval_node_intersects(const struct interval_node *, ptrdiff_t, ptrdiff_t); +static int interval_tree_max_height(const struct interval_tree *); +static struct interval_stack *interval_stack_create(intmax_t); +static void interval_stack_destroy(struct interval_stack *); +static void interval_stack_clear(struct interval_stack *); +static void interval_stack_ensure_space(struct interval_stack *, intmax_t); +static void interval_stack_push(struct interval_stack *, struct interval_node *); +static void interval_stack_push_flagged(struct interval_stack *, struct interval_node *, bool); +static struct interval_node *interval_stack_pop(struct interval_stack *); +static void interval_tree_update_limit(const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset(const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit(const struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_left(struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_right(struct interval_tree *, struct interval_node *); +static void interval_tree_insert_fix(struct interval_tree *, struct interval_node *); +static void interval_tree_remove_fix(struct interval_tree *, struct interval_node *); +static void interval_tree_transplant(struct interval_tree *, struct interval_node *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min(const struct interval_tree *, struct interval_node *); +static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_generator_destroy (struct interval_generator *); +static void interval_generator_reset (struct interval_generator *, + ptrdiff_t, ptrdiff_t, + enum interval_tree_order); +static void +interval_generator_narrow (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end); +static inline struct interval_node* +interval_generator_next (struct interval_generator *g); +static inline void interval_tree_iter_ensure_space(struct interval_tree *); + + + +/* +------------------------------------------------------------------------------------+ */ + +/* Simple dynamic array. */ +struct interval_stack +{ + struct interval_node **nodes; + size_t size; + size_t length; +}; + +/* State used when iterating interval. */ +struct interval_generator +{ + struct interval_tree *tree; + struct interval_stack *stack; + ptrdiff_t begin; + ptrdiff_t end; + enum interval_tree_order order; +}; + + + +/* +===================================================================================+ + * | Tree operations + * +===================================================================================+ */ + +/* Initialize an allocated node. */ + +void +interval_node_init (struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance, + Lisp_Object data) +{ + node->begin = begin; + node->end = max (begin, end); + node->front_advance = front_advance; + node->rear_advance = rear_advance; + node->data = data; +} + +/* Return NODE's begin value, computing it if necessary. */ + +ptrdiff_t +interval_node_begin (struct interval_tree *tree, + struct interval_node *node) +{ + interval_tree_validate (tree, node); + return node->begin; +} + +/* Return NODE's end value, computing it if necessary. */ + +ptrdiff_t +interval_node_end (struct interval_tree *tree, + struct interval_node *node) +{ + interval_tree_validate (tree, node); + return node->end; +} + +/* Safely modify a node's interval. */ + +void +interval_node_set_region (struct interval_tree *tree, + struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + interval_tree_validate (tree, node); + if (begin != node->begin) + { + interval_tree_remove (tree, node); + node->begin = min (begin, PTRDIFF_MAX - 1); + node->end = max (node->begin, end); + interval_tree_insert (tree, node); + } + else if (end != node->end) + { + node->end = max (node->begin, end); + interval_tree_propagate_limit (tree, node); + } +} + +/* Allocate an interval_tree. Free with interval_tree_destroy. */ + +struct interval_tree* +interval_tree_create (void) +{ + struct interval_tree *tree = xmalloc (sizeof (*tree)); + interval_tree_clear (tree); + tree->iter = interval_generator_create (tree); + return tree; +} + +/* Reset the tree TREE to its empty state. */ + +void +interval_tree_clear (struct interval_tree *tree) +{ + struct interval_node *nil = &tree->nil; + nil->left = nil->right = nil->parent = nil; + nil->offset = nil->otick = 0; + nil->begin = PTRDIFF_MIN; + nil->end = PTRDIFF_MIN; + nil->limit = PTRDIFF_MIN; /* => max(x, nil.limit) = x */ + nil->color = ITREE_BLACK; + tree->root = nil; + tree->otick = 1; + tree->size = 0; + tree->iter_running = 0; +} + +#ifdef ITREE_TESTING +/* Initialize a pre-allocated tree (presumably on the stack). */ + +static void +interval_tree_init (struct interval_tree *tree) +{ + interval_tree_clear (tree); + tree->iter = interval_generator_create (tree); +} +#endif + +/* Release a tree, freeing its allocated memory. */ +void +interval_tree_destroy (struct interval_tree *tree) +{ + if (! tree) + return; + if (tree->iter) + interval_generator_destroy (tree->iter); + xfree (tree); +} + +/* Return the number of nodes in TREE. */ + +intmax_t +interval_tree_size (struct interval_tree *tree) +{ + return tree->size; +} + +/* Insert a NODE into the TREE. + + Note, that inserting a node twice results in undefined behaviour. +*/ + +void +interval_tree_insert (struct interval_tree *tree, struct interval_node *node) +{ + eassert (node && node->begin <= node->end && node != &tree->nil); + + struct interval_node *parent = &tree->nil; + struct interval_node *child = tree->root; + ptrdiff_t offset = 0; + + /* Find the insertion point, accumulate node's offset and update + ancestors limit values. */ + while (child != &tree->nil) + { + parent = child; + offset += child->offset; + child->limit = max (child->limit, node->end - offset); + /* This suggests that nodes in the right subtree are strictly + greater. But this is not true due to later rotations. */ + child = node->begin <= child->begin ? child->left : child->right; + } + + /* Insert the node */ + if (parent == &tree->nil) + tree->root = node; + else if (node->begin <= parent->begin) + parent->left = node; + else + parent->right = node; + + /* Init the node */ + node->parent = parent; + node->left = &tree->nil; + node->right = &tree->nil; + node->color = ITREE_RED; + node->offset = offset; + node->limit = node->end; + node->otick = tree->otick - 1; + + /* Fix/update the tree */ + ++tree->size; + interval_tree_insert_fix (tree, node); + interval_tree_iter_ensure_space (tree); +} + +/* Return true, if NODE is a member of TREE. */ + +bool +interval_tree_contains (struct interval_tree *tree, struct interval_node *node) +{ + struct interval_node *other; + + interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING); + while ((other = interval_tree_iter_next (tree))) + if (other == node) + break; + + interval_tree_iter_finish (tree); + return other == node; +} + +/* Remove NODE from TREE and return it. NODE must exist in TREE.*/ + +struct interval_node* +interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +{ + eassert (interval_tree_contains (tree, node)); + + struct interval_node *broken = NULL; + + interval_tree_inherit_offset (tree, node); + if (node->left == &tree->nil || node->right == &tree->nil) + { + struct interval_node *subst = + (node->right == &tree->nil) ? node->left : node->right; + if (node->color == ITREE_BLACK) + broken = subst; + interval_tree_transplant (tree, subst, node); + interval_tree_propagate_limit (tree, subst); + } + else + { + struct interval_node *min = interval_tree_subtree_min (tree, node->right); + struct interval_node *min_right = min->right; + + if (min->color == ITREE_BLACK) + broken = min->right; + if (min->parent == node) + min_right->parent = min; /* set parent, if min_right = nil */ + else + { + interval_tree_transplant (tree, min->right, min); + min->right = node->right; + min->right->parent = min; + } + interval_tree_inherit_offset (tree, min); + interval_tree_transplant (tree, min, node); + min->left = node->left; + min->left->parent = min; + min->color = node->color; + interval_tree_propagate_limit (tree, min_right); + interval_tree_propagate_limit (tree, min); + } + + if (broken) + interval_tree_remove_fix (tree, broken); + + node->right = node->left = node->parent = NULL; + --tree->size; + + eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->nil)); + + return node; +} + +static struct interval_node* +interval_tree_validate (struct interval_tree *tree, struct interval_node *node) +{ + + if (tree->otick == node->otick || node == &tree->nil) + return node; + if (node != tree->root) + interval_tree_validate (tree, node->parent); + + interval_tree_inherit_offset (tree, node); + return node; +} + +/* Start a generator iterating all intervals in [BEGIN,END) in the + given ORDER. Only one iterator per tree can be running at any + time. +*/ + +void +interval_tree_iter_start (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (tree->iter_running) + emacs_abort (); + interval_generator_reset (tree->iter, begin, end, order); + tree->iter_running = 1; +} + +/* Limit the search interval of the iterator to the given values. The + interval can only shrink, but never grow.*/ + +inline void +interval_tree_iter_narrow(struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end) +{ + if (! tree->iter_running) + emacs_abort (); + interval_generator_narrow (tree->iter, begin, end); +} + +/* Stop using the iterator. */ + +void +interval_tree_iter_finish (struct interval_tree *tree) +{ + if (! tree->iter_running) + emacs_abort (); + tree->iter_running = 0; +} + +/* Return the next node of the iterator in the order given when it was + started; or NULL if there are no more nodes. */ + +inline struct interval_node* +interval_tree_iter_next (struct interval_tree *tree) +{ + if (! tree->iter_running) + emacs_abort (); + return interval_generator_next (tree->iter); +} + +/* Ensure that the tree's iterator does not need to allocate space + until the tree grows in size. */ + +static inline void +interval_tree_iter_ensure_space (struct interval_tree *tree) +{ + interval_generator_ensure_space (tree->iter); +} + +static int +interval_tree_max_height (const struct interval_tree *tree) +{ + return 2 * log (tree->size + 1) / log (2) + 0.5; +} + + +/* +===================================================================================+ + * | Insert/Delete Gaps + * +===================================================================================+ */ + +/* Insert a gap at POS of length LENGTH expanding all intervals + intersecting it, while respecting their rear_advance and + front_advance setting. */ + +void +interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +{ + if (length <= 0 || tree->size == 0) + return; + + /* FIXME: Don't allocate generator/stack anew every time. */ + + /* Nodes with front_advance starting at pos may mess up the tree + order, so we need to remove them first. */ + struct interval_stack *saved = interval_stack_create (0); + struct interval_node *node = NULL; + interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER); + while ((node = interval_tree_iter_next (tree))) + { + if (node->begin == pos && node->front_advance + && (node->begin != node->end || node->rear_advance)) + interval_stack_push (saved, node); + } + interval_tree_iter_finish (tree); + for (int i = 0; i < saved->length; ++i) + interval_tree_remove (tree, saved->nodes[i]); + + + /* We can't use a generator here, because we can't effectively + narrow AND shift some subtree at the same time. */ + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + interval_stack_push (stack, tree->root); + while ((node = interval_stack_pop (stack))) + { + /* Process in pre-order. */ + interval_tree_inherit_offset (tree, node); + if (node->right != &tree->nil) + { + if (node->begin > pos) + { + /* All nodes in this subtree are shifted by length. */ + node->right->offset += length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } + if (node->left != &tree->nil && pos <= node->left->limit) + interval_stack_push (stack, node->left); + + /* node->begin == pos implies no front-advance. */ + if (node->begin > pos) + node->begin += length; + if (node->end > pos || (node->end == pos && node->rear_advance)) + { + node->end += length; + interval_tree_propagate_limit (tree, node); + } + } + interval_stack_destroy (stack); + + /* Reinsert nodes starting at POS having front-advance. */ + while ((node = interval_stack_pop (saved))) + { + node->begin += length; + if (node->end != pos || node->rear_advance) + node->end += length; + interval_tree_insert (tree, node); + } + + interval_stack_destroy (saved); +} + +/* Delete a gap at POS of length LENGTH, contracting all intervals + intersecting it. */ + +void +interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +{ + if (length <= 0 || tree->size == 0) + return; + + /* FIXME: Don't allocate stack anew every time. */ + + /* Can't use the generator here, because by decrementing begin, we + might unintentionally bring shifted nodes back into our search + space. */ + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + struct interval_node *node; + + interval_stack_push (stack, tree->root); + while ((node = interval_stack_pop (stack))) + { + interval_tree_inherit_offset (tree, node); + if (node->right != &tree->nil) + { + if (node->begin > pos + length) + { + /* Shift right subtree to the left. */ + node->right->offset -= length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } + if (node->left != &tree->nil && pos <= node->left->limit) + interval_stack_push (stack, node->left); + + if (pos < node->begin) + node->begin = max (pos, node->begin - length); + if (node->end > pos) + { + node->end = max (pos , node->end - length); + interval_tree_propagate_limit (tree, node); + } + } + interval_stack_destroy (stack); +} + + + +/* +===================================================================================+ + * | Generator + * +===================================================================================+ */ + +/* Allocate a new generator for TREE. */ + +static struct interval_generator* +interval_generator_create (struct interval_tree *tree) +{ + struct interval_generator *g = xmalloc (sizeof *g); + const int size = interval_tree_max_height (tree) + 1; + + g->stack = interval_stack_create (size); + g->tree = tree; + interval_generator_reset (g, 1, 0, 0); + return g; +} + +/* Reset generator G such that it iterates over intervals intersecting + with [BEGIN, END) in the given ORDER. */ + +void +interval_generator_reset (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (! g) return; + + g->begin = begin; + g->end = end; + g->order = order; + interval_stack_clear (g->stack); + if (begin <= end && g->tree->size > 0) + interval_stack_push_flagged (g->stack, g->tree->root, false); +} + +/* Allocate enough space for the tree of G in its current shape. */ + +static inline void +interval_generator_ensure_space (struct interval_generator *g) +{ + interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); +} + +/* Return true, if NODE's interval intersects with [BEGIN, END). */ + +static inline bool +interval_node_intersects (const struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + return (begin < node->end && node->begin < end) + || (node->begin == node->end && begin == node->begin); +} + +/* Return the next node of G, or NULL if there is none. */ + +inline struct interval_node* +interval_generator_next (struct interval_generator *g) +{ + if (! g) return NULL; + + struct interval_node * const nil = &g->tree->nil; + struct interval_node *node; + + do { + node = interval_stack_pop (g->stack); + + while (node && ! node->visited) + { + struct interval_node * const left = node->left; + struct interval_node * const right = node->right; + + interval_tree_inherit_offset (g->tree, node); + switch (g->order) + { + case ITREE_ASCENDING: + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + /* Node's children may still be off-set and we need to add it. */ + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + break; + case ITREE_DESCENDING: + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + break; + case ITREE_PRE_ORDER: + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + break; + } + node = interval_stack_pop (g->stack); + } + /* Node may have been invalidated by interval_generator_narrow + after it was pushed: Check if it still intersects. */ + } while (node && ! interval_node_intersects (node, g->begin, g->end)); + + return node; +} + +/* Limit G to the new interval [BEGIN, END), which must be a subset of + the current one. I.E. it can't grow on either side. */ + +static inline void +interval_generator_narrow (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end) +{ + g->begin = max (begin, g->begin); + g->end = min (end, g->end); +} + +/* Free the memory allocated for G. */ + +void +interval_generator_destroy (struct interval_generator *g) +{ + if (! g) return; + if (g->stack) + interval_stack_destroy (g->stack); + xfree (g); +} + + +/* +===================================================================================+ + * | Stack + * +===================================================================================+ */ + +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + } +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_ensure_space (stack, stack->length + 1); + stack->nodes[stack->length] = node; + stack->length++; +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + interval_stack_push (stack, node); + node->visited = flag; +} + +static inline struct interval_node* +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return NULL; + return stack->nodes[--stack->length]; +} + + +/* +===================================================================================+ + * | Internal Functions + * +===================================================================================+ */ + +/* Update NODE's limit attribute according to its children. */ + +static void +interval_tree_update_limit (const struct interval_tree *tree, + struct interval_node *node) +{ + if (node == &tree->nil) + return; + + node->limit = max (node->end, max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); +} + +/* Apply NODE's offset to its begin, end and limit values and + propagate it to its children. + + Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . +*/ + +static void +interval_tree_inherit_offset (const struct interval_tree *tree, + struct interval_node *node) +{ + + if (node->otick == tree->otick) + return; + + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != &tree->nil) + node->left->offset += node->offset; + if (node->right != &tree->nil) + node->right->offset += node->offset; + node->offset = 0; + if (node == tree->root || node->parent->otick == tree->otick) + node->otick = tree->otick; +} + +/* Update limit of NODE and its ancestors. Stop when it becomes + stable, i.e. new_limit = old_limit. + + NODE may also be the nil node, in which case its parent is + used. (This feature is due to the RB algorithm.) +*/ + +static void +interval_tree_propagate_limit (const struct interval_tree *tree, + struct interval_node *node) +{ + if (node == &tree->nil) + node = node->parent; + if (node == &tree->nil) + return; + + while (1) { + ptrdiff_t newlimit = max (node->end, max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); + if (newlimit == node->limit) + break; + node->limit = newlimit; + if (node == tree->root) + break; + node = node->parent; + } +} + +/* Perform the familiar left-rotation on node NODE. */ + +static void +interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) +{ + eassert (node->right != &tree->nil); + + struct interval_node *right = node->right; + + interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree, right); + + /* Turn right's left subtree into node's right subtree. */ + node->right = right->left; + if (right->left != &tree->nil) + right->left->parent = node; + + /* right's parent was node's parent. */ + if (right != &tree->nil) + right->parent = node->parent; + + /* Get the parent to point to right instead of node. */ + if (node != tree->root) + { + if (node == node->parent->left) + node->parent->left = right; + else + node->parent->right = right; + } + else + tree->root = right; + + /* Put node on right's left. */ + right->left = node; + if (node != &tree->nil) + node->parent = right; + + /* Order matters here. */ + interval_tree_update_limit (tree, node); + interval_tree_update_limit (tree, right); +} + +/* Perform the familiar right-rotation on node NODE. */ + +static void +interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) +{ + eassert (tree && node && node->left != &tree->nil); + + struct interval_node *left = node->left; + + interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree, left); + + node->left = left->right; + if (left->right != &tree->nil) + left->right->parent = node; + + if (left != &tree->nil) + left->parent = node->parent; + if (node != tree->root) + { + if (node == node->parent->right) + node->parent->right = left; + else + node->parent->left = left; + } + else + tree->root = left; + + left->right = node; + if (node != &tree->nil) + node->parent = left; + + interval_tree_update_limit (tree, left); + interval_tree_update_limit (tree, node); +} + +/* Repair the tree after an insertion. Part of the RB-Tree + algorithm. */ + +static void +interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) +{ + while (node->parent->color == ITREE_RED) + { + /* NODE is red and its parent is red. This is a violation of + red-black tree property #3. */ + + if (node->parent == node->parent->parent->left) + { + /* We're on the left side of our grandparent, and OTHER is + our "uncle". */ + struct interval_node *uncle = node->parent->parent->right; + + if (uncle->color == ITREE_RED) /* case 1.a */ + { + /* Uncle and parent are red but should be black because + NODE is red. Change the colors accordingly and + proceed with the grandparent. */ + node->parent->color = ITREE_BLACK; + uncle->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + node = node->parent->parent; + } + else + { + /* Parent and uncle have different colors; parent is + red, uncle is black. */ + if (node == node->parent->right) /* case 2.a */ + { + node = node->parent; + interval_tree_rotate_left (tree, node); + } + /* case 3.a */ + node->parent->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + interval_tree_rotate_right (tree, node->parent->parent); + } + } + else + { + /* This is the symmetrical case of above. */ + struct interval_node *uncle = node->parent->parent->left; + + if (uncle->color == ITREE_RED) /* case 1.b */ + { + node->parent->color = ITREE_BLACK; + uncle->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + node = node->parent->parent; + } + else + { + if (node == node->parent->left) /* case 2.b */ + { + node = node->parent; + interval_tree_rotate_right (tree, node); + } + /* case 3.b */ + node->parent->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + interval_tree_rotate_left (tree, node->parent->parent); + } + } + } + + /* The root may have been changed to red due to the algorithm. Set + it to black so that property #5 is satisfied. */ + tree->root->color = ITREE_BLACK; +} + +/* Repair the tree after a deletion. Part of the RB-Tree + algorithm. */ + +static void +interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +{ + while (node != tree->root && node->color == ITREE_BLACK) + { + if (node == node->parent->left) + { + struct interval_node *other = node->parent->right; + + if (other->color == ITREE_RED) /* case 1.a */ + { + other->color = ITREE_BLACK; + node->parent->color = ITREE_RED; + interval_tree_rotate_left (tree, node->parent); + other = node->parent->right; + } + + if (other->left->color == ITREE_BLACK /* 2.a */ + && other->right->color == ITREE_BLACK) + { + other->color = ITREE_RED; + node = node->parent; + } + else + { + if (other->right->color == ITREE_BLACK) /* 3.a */ + { + other->left->color = ITREE_BLACK; + other->color = ITREE_RED; + interval_tree_rotate_right (tree, other); + other = node->parent->right; + } + other->color = node->parent->color; /* 4.a */ + node->parent->color = ITREE_BLACK; + other->right->color = ITREE_BLACK; + interval_tree_rotate_left (tree, node->parent); + node = tree->root; + } + } + else + { + struct interval_node *other = node->parent->left; + + if (other->color == ITREE_RED) /* 1.b */ + { + other->color = ITREE_BLACK; + node->parent->color = ITREE_RED; + interval_tree_rotate_right (tree, node->parent); + other = node->parent->left; + } + + if (other->right->color == ITREE_BLACK /* 2.b */ + && other->left->color == ITREE_BLACK) + { + other->color = ITREE_RED; + node = node->parent; + } + else + { + if (other->left->color == ITREE_BLACK) /* 3.b */ + { + other->right->color = ITREE_BLACK; + other->color = ITREE_RED; + interval_tree_rotate_left (tree, other); + other = node->parent->left; + } + + other->color = node->parent->color; /* 4.b */ + node->parent->color = ITREE_BLACK; + other->left->color = ITREE_BLACK; + interval_tree_rotate_right (tree, node->parent); + node = tree->root; + } + } + } + + node->color = ITREE_BLACK; +} + +/* Link node SOURCE in DEST's place. */ + +static void +interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, + struct interval_node *dest) +{ + eassert (tree && source && dest && dest != &tree->nil); + + if (dest == tree->root) + tree->root = source; + else if (dest == dest->parent->left) + dest->parent->left = source; + else + dest->parent->right = source; + + source->parent = dest->parent; +} + + +static struct interval_node* +interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) +{ + if (node == &tree->nil) + return node; + while (node->left != &tree->nil) + node = node->left; + return node; +} + + +/* +===================================================================================+ + * | Debugging + * +===================================================================================+ */ + +/* See Foverlay_tree in buffer.c */ diff --git a/src/itree.h b/src/itree.h new file mode 100644 index 00000000000..d35c5afc24c --- /dev/null +++ b/src/itree.h @@ -0,0 +1,88 @@ +/* This file implements an efficient interval data-structure. + +Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) + +This file is not part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#ifndef ITREE_H +#define ITREE_H +#include +#include +#include + +/* The tree and node structs are mainly here, so they can be allocated. + + NOTE: The only time where it is safe to modify node.begin and + node.end directly, is while the node is not part of any tree. + + NOTE: It is safe to read node.begin and node.end directly, if the + node came from a generator, because it validates the nodes it + returns as a side-effect. +*/ + +struct interval_node; +struct interval_node +{ + enum { ITREE_RED, ITREE_BLACK } color; + struct interval_node *parent; + struct interval_node *left; + struct interval_node *right; + ptrdiff_t begin; /* The beginning of this interval. */ + ptrdiff_t end; /* The end of the interval. */ + ptrdiff_t limit; /* The maximum end in this subtree. */ + ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ + uintmax_t otick; /* offset modified tick */ + Lisp_Object data; /* Exclusively used by the client. */ + bool_bf visited; /* For traversal via generator. */ + bool_bf rear_advance : 1; /* Same as for marker and overlays. */ + bool_bf front_advance : 1; /* Same as for marker and overlays. */ +}; + +struct interval_tree +{ + struct interval_node *root; + struct interval_node nil; /* The tree's version of NULL. */ + uintmax_t otick; /* offset tick, compared with node's otick. */ + intmax_t size; /* Number of nodes in the tree. */ + struct interval_generator *iter; + bool_bf iter_running; +}; + +enum interval_tree_order { + ITREE_ASCENDING = 0, + ITREE_DEFLT_ORDER = 0, + ITREE_DESCENDING, + ITREE_PRE_ORDER, +}; + +void interval_node_init(struct interval_node *, ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); +ptrdiff_t interval_node_begin(struct interval_tree *, struct interval_node *); +ptrdiff_t interval_node_end(struct interval_tree *, struct interval_node *); +void interval_node_set_region(struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); +struct interval_tree *interval_tree_create(void); +void interval_tree_destroy(struct interval_tree *); +intmax_t interval_tree_size(struct interval_tree *); +void interval_tree_clear(struct interval_tree *); +void interval_tree_insert(struct interval_tree *, struct interval_node *); +bool interval_tree_contains(struct interval_tree *, struct interval_node *); +struct interval_node *interval_tree_remove(struct interval_tree *, struct interval_node *); +void interval_tree_iter_start(struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order); +void interval_tree_iter_narrow(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_iter_finish(struct interval_tree *); +struct interval_node *interval_tree_iter_next(struct interval_tree *); +void interval_tree_insert_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_delete_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +#endif diff --git a/src/keyboard.c b/src/keyboard.c index e8701b88708..60cdaba9f0b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1668,8 +1668,8 @@ adjust_point_for_property (ptrdiff_t last_pt, bool modified) && display_prop_intangible_p (val, overlay, PT, PT_BYTE) && (!OVERLAYP (overlay) ? get_property_and_range (PT, Qdisplay, &val, &beg, &end, Qnil) - : (beg = OVERLAY_POSITION (OVERLAY_START (overlay)), - end = OVERLAY_POSITION (OVERLAY_END (overlay)))) + : (beg = OVERLAY_START (overlay), + end = OVERLAY_END (overlay))) && (beg < PT /* && end > PT <- It's always the case. */ || (beg <= PT && STRINGP (val) && SCHARS (val) == 0))) { diff --git a/src/lisp.h b/src/lisp.h index 680c25d4c49..222a99950a8 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2217,15 +2217,14 @@ struct Lisp_Overlay - next fields of start and end markers (singly linked list of markers). I.e. 9words plus 2 bits, 3words of which are for external linked lists. */ - { - ENUM_BF (Lisp_Misc_Type) type : 16; /* = Lisp_Misc_Overlay */ - bool_bf gcmarkbit : 1; - unsigned spacer : 15; - struct Lisp_Overlay *next; - Lisp_Object start; - Lisp_Object end; - Lisp_Object plist; - }; +{ + ENUM_BF (Lisp_Misc_Type) type : 16; /* = Lisp_Misc_Overlay */ + bool_bf gcmarkbit : 1; + unsigned spacer : 15; + Lisp_Object plist; + struct buffer *buffer; /* eassert (live buffer || NULL). */ + struct interval_node *interval; +}; /* Number of bits needed to store one of the values SAVE_UNUSED..SAVE_OBJECT. */ @@ -3704,7 +3703,7 @@ extern Lisp_Object make_save_funcptr_ptr_obj (void (*) (void), void *, Lisp_Object); extern Lisp_Object make_save_memory (Lisp_Object *, ptrdiff_t); extern void free_save_value (Lisp_Object); -extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); +extern Lisp_Object build_overlay (ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); extern void free_marker (Lisp_Object); extern void free_cons (struct Lisp_Cons *); extern void init_alloc_once (void); diff --git a/src/print.c b/src/print.c index f280616af8a..a07baa3067a 100644 --- a/src/print.c +++ b/src/print.c @@ -548,8 +548,7 @@ temp_output_buffer_setup (const char *bufname) bset_read_only (current_buffer, Qnil); bset_filename (current_buffer, Qnil); bset_undo_list (current_buffer, Qt); - eassert (current_buffer->overlays_before == NULL); - eassert (current_buffer->overlays_after == NULL); + eassert (current_buffer->overlays == NULL); bset_enable_multibyte_characters (current_buffer, BVAR (&buffer_defaults, enable_multibyte_characters)); specbind (Qinhibit_read_only, Qt); @@ -2074,7 +2073,7 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) obj = XCDR (obj); if (!(i & 1)) halftail = XCDR (halftail); - } + } /* OBJ non-nil here means it's the end of a dotted list. */ if (!NILP (obj)) @@ -2114,15 +2113,14 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) case Lisp_Misc_Overlay: print_c_string ("#buffer) + if (! OVERLAY_BUFFER (obj)) print_c_string ("in no buffer", printcharfun); else { int len = sprintf (buf, "from %"pD"d to %"pD"d in ", - marker_position (OVERLAY_START (obj)), - marker_position (OVERLAY_END (obj))); + OVERLAY_START (obj), OVERLAY_END (obj)); strout (buf, len, len, printcharfun); - print_string (BVAR (XMARKER (OVERLAY_START (obj))->buffer, name), + print_string (BVAR (OVERLAY_BUFFER (obj), name), printcharfun); } printchar ('>', printcharfun); diff --git a/src/textprop.c b/src/textprop.c index 513780c3009..aebb6524e68 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -617,36 +617,42 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, } if (BUFFERP (object)) { - ptrdiff_t noverlays; - Lisp_Object *overlay_vec; - struct buffer *obuf = current_buffer; + struct buffer *b = XBUFFER (object); + struct interval_node *node; + struct sortvec items[2]; + struct sortvec *result = NULL; + Lisp_Object result_tem = Qnil; - if (XINT (position) < BUF_BEGV (XBUFFER (object)) - || XINT (position) > BUF_ZV (XBUFFER (object))) + if (XINT (position) < BUF_BEGV (b) || XINT (position) > BUF_ZV (b)) xsignal1 (Qargs_out_of_range, position); - set_buffer_temp (XBUFFER (object)); - - USE_SAFE_ALLOCA; - GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, false); - noverlays = sort_overlays (overlay_vec, noverlays, w); - - set_buffer_temp (obuf); + buffer_overlay_iter_start(b, XINT (position), XINT (position) + 1, + ITREE_ASCENDING); /* Now check the overlays in order of decreasing priority. */ - while (--noverlays >= 0) + while ((node = buffer_overlay_iter_next (b))) { - Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop); - if (!NILP (tem)) - { - if (overlay) - /* Return the overlay we got the property from. */ - *overlay = overlay_vec[noverlays]; - SAFE_FREE (); - return tem; - } + Lisp_Object tem = Foverlay_get (node->data, prop); + struct sortvec *this; + + if (NILP (tem) || (w && ! overlay_matches_window (w, node->data))) + continue; + + this = (result == items ? items + 1 : items); + make_sortvec_item (this, node->data); + if (! result || (compare_overlays (result, this) < 0)) + { + result = this; + result_tem = tem; + } } - SAFE_FREE (); + buffer_overlay_iter_finish (b); + if (result) + { + if (overlay) + *overlay = result->overlay; + return result_tem; + } } if (overlay) diff --git a/src/window.h b/src/window.h index df7c23f824b..324d30b57f9 100644 --- a/src/window.h +++ b/src/window.h @@ -1128,6 +1128,16 @@ output_cursor_to (struct window *w, int vpos, int hpos, int y, int x) w->output_cursor.y = y; } +/* Return true, if overlay OV's properties should have an effect in + window W. */ +INLINE bool +overlay_matches_window (const struct window *w, Lisp_Object ov) +{ + eassert (OVERLAYP (ov)); + Lisp_Object window = Foverlay_get (ov, Qwindow); + return (! WINDOWP (window) || XWINDOW (window) == w); +} + INLINE_HEADER_END #endif /* not WINDOW_H_INCLUDED */ diff --git a/src/xdisp.c b/src/xdisp.c index 86164eb9f6f..b3b9ecae377 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -873,7 +873,6 @@ static enum move_it_result static void get_visually_first_element (struct it *); static void compute_stop_pos (struct it *); static int face_before_or_after_it_pos (struct it *, bool); -static ptrdiff_t next_overlay_change (ptrdiff_t); static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object, Lisp_Object, struct text_pos *, ptrdiff_t, bool); static int handle_single_display_spec (struct it *, Lisp_Object, Lisp_Object, @@ -3606,39 +3605,6 @@ compute_stop_pos (struct it *it) && it->stop_charpos >= IT_CHARPOS (*it))); } - -/* Return the position of the next overlay change after POS in - current_buffer. Value is point-max if no overlay change - follows. This is like `next-overlay-change' but doesn't use - xmalloc. */ - -static ptrdiff_t -next_overlay_change (ptrdiff_t pos) -{ - ptrdiff_t i, noverlays; - ptrdiff_t endpos; - Lisp_Object *overlays; - USE_SAFE_ALLOCA; - - /* Get all overlays at the given position. */ - GET_OVERLAYS_AT (pos, overlays, noverlays, &endpos, true); - - /* If any of these overlays ends before endpos, - use its ending point instead. */ - for (i = 0; i < noverlays; ++i) - { - Lisp_Object oend; - ptrdiff_t oendpos; - - oend = OVERLAY_END (overlays[i]); - oendpos = OVERLAY_POSITION (oend); - endpos = min (endpos, oendpos); - } - - SAFE_FREE (); - return endpos; -} - /* How many characters forward to search for a display property or display string. Searching too far forward makes the bidi display sluggish, especially in small windows. */ @@ -5071,7 +5037,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, overlay's display string/image twice. */ if (!NILP (overlay)) { - ptrdiff_t ovendpos = OVERLAY_POSITION (OVERLAY_END (overlay)); + ptrdiff_t ovendpos = OVERLAY_END (overlay); /* Some borderline-sane Lisp might call us with the current buffer narrowed so that overlay-end is outside the @@ -5785,13 +5751,14 @@ static void load_overlay_strings (struct it *it, ptrdiff_t charpos) { Lisp_Object overlay, window, str, invisible; - struct Lisp_Overlay *ov; ptrdiff_t start, end; ptrdiff_t n = 0, i, j; int invis; struct overlay_entry entriesbuf[20]; ptrdiff_t size = ARRAYELTS (entriesbuf); struct overlay_entry *entries = entriesbuf; + struct interval_node *node; + USE_SAFE_ALLOCA; if (charpos <= 0) @@ -5823,83 +5790,47 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) } \ while (false) - /* Process overlay before the overlay center. */ - for (ov = current_buffer->overlays_before; ov; ov = ov->next) + if (current_buffer->overlays) { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); - start = OVERLAY_POSITION (OVERLAY_START (overlay)); - end = OVERLAY_POSITION (OVERLAY_END (overlay)); + buffer_overlay_iter_start (current_buffer, + charpos - 1, charpos + 1, ITREE_DESCENDING); + /* Process overlays. */ + while ((node = buffer_overlay_iter_next (current_buffer))) + { + overlay = node->data; + eassert (OVERLAYP (overlay)); + start = node->begin; + end = node->end; - if (end < charpos) - break; + /* Skip this overlay if it doesn't start or end at IT's current + position. */ + if (end != charpos && start != charpos) + continue; - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; + /* Skip this overlay if it doesn't apply to IT->w. */ + window = Foverlay_get (overlay, Qwindow); + if (WINDOWP (window) && XWINDOW (window) != it->w) + continue; - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; + /* If the text ``under'' the overlay is invisible, both before- + and after-strings from this overlay are visible; start and + end position are indistinguishable. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - /* If the text ``under'' the overlay is invisible, both before- - and after-strings from this overlay are visible; start and - end position are indistinguishable. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); + /* If overlay has a non-empty before-string, record it. */ + if ((start == charpos || (end == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, false); - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); - - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); - } - - /* Process overlays after the overlay center. */ - for (ov = current_buffer->overlays_after; ov; ov = ov->next) - { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); - start = OVERLAY_POSITION (OVERLAY_START (overlay)); - end = OVERLAY_POSITION (OVERLAY_END (overlay)); - - if (start > charpos) - break; - - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; - - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; - - /* If the text ``under'' the overlay is invisible, it has a zero - dimension, and both before- and after-strings apply. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); - - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); + /* If overlay has a non-empty after-string, record it. */ + if ((end == charpos || (start == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, true); + } + buffer_overlay_iter_finish (current_buffer); } #undef RECORD_OVERLAY_STRING @@ -6463,7 +6394,7 @@ back_to_previous_visible_line_start (struct it *it) && !NILP (val = get_char_property_and_overlay (make_number (pos), Qdisplay, Qnil, &overlay)) && (OVERLAYP (overlay) - ? (beg = OVERLAY_POSITION (OVERLAY_START (overlay))) + ? (beg = OVERLAY_START (overlay)) : get_property_and_range (pos, Qdisplay, &val, &beg, &end, Qnil))) { RESTORE_IT (it, it, it2data); @@ -9568,7 +9499,6 @@ move_it_to (struct it *it, ptrdiff_t to_charpos, int to_x, int to_y, int to_vpos } /* Reset/increment for the next run. */ - recenter_overlay_lists (current_buffer, IT_CHARPOS (*it)); it->current_x = line_start_x; line_start_x = 0; it->hpos = 0; @@ -21212,13 +21142,6 @@ display_line (struct it *it, int cursor_vpos) row->starts_in_middle_of_char_p = it->starts_in_middle_of_char_p; it->starts_in_middle_of_char_p = false; - /* Arrange the overlays nicely for our purposes. Usually, we call - display_line on only one line at a time, in which case this - can't really hurt too much, or we call it on lines which appear - one after another in the buffer, in which case all calls to - recenter_overlay_lists but the first will be pretty cheap. */ - recenter_overlay_lists (current_buffer, IT_CHARPOS (*it)); - /* If we are going to display the cursor's line, account for the hscroll of that line. We subtract the window's min_hscroll, because that was already accounted for in init_iterator. */ @@ -31212,7 +31135,7 @@ note_mouse_highlight (struct frame *f, int x, int y) if (BUFFERP (object)) { /* Put all the overlays we want in a vector in overlay_vec. */ - GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL, false); + GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL); /* Sort overlays into increasing priority order. */ noverlays = sort_overlays (overlay_vec, noverlays, w); } diff --git a/src/xfaces.c b/src/xfaces.c index b309c161278..b1788725eb7 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -5931,8 +5931,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, USE_SAFE_ALLOCA; { ptrdiff_t next_overlay; - - GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay, false); + GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay); if (next_overlay < endpos) endpos = next_overlay; } @@ -5975,7 +5974,6 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, { for (prop = Qnil, i = noverlays - 1; i >= 0 && NILP (prop); --i) { - Lisp_Object oend; ptrdiff_t oendpos; prop = Foverlay_get (overlay_vec[i], propname); @@ -5988,8 +5986,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, merge_face_ref (f, prop, attrs, true, 0); } - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); + oendpos = OVERLAY_END (overlay_vec[i]); if (oendpos < endpos) endpos = oendpos; } @@ -5998,18 +5995,16 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, { for (i = 0; i < noverlays; i++) { - Lisp_Object oend; ptrdiff_t oendpos; prop = Foverlay_get (overlay_vec[i], propname); if (!NILP (prop)) merge_face_ref (f, prop, attrs, true, 0); - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); - if (oendpos < endpos) - endpos = oendpos; - } + oendpos = OVERLAY_END (overlay_vec[i]); + if (oendpos < endpos) + endpos = oendpos; + } } *endptr = endpos; diff --git a/test/manual/noverlay/.gitignore b/test/manual/noverlay/.gitignore new file mode 100644 index 00000000000..ca7fc452b84 --- /dev/null +++ b/test/manual/noverlay/.gitignore @@ -0,0 +1 @@ +itree-tests diff --git a/test/manual/noverlay/Makefile.in b/test/manual/noverlay/Makefile.in new file mode 100644 index 00000000000..beef1dbc097 --- /dev/null +++ b/test/manual/noverlay/Makefile.in @@ -0,0 +1,32 @@ +PROGRAM = itree-tests +LIBS = check +top_srcdir = @top_srcdir@ +CFLAGS += -O0 -g3 $(shell pkg-config --cflags $(LIBS)) -I $(top_srcdir)/src +LDFLAGS += $(shell pkg-config --libs $(LIBS)) -lm +OBJECTS = itree-tests.o +CC = gcc +EMACS ?= ../../../src/emacs + +.PHONY: all check have-libcheck + +all: check + +have-libcheck: + pkg-config --cflags $(LIBS) + +check: have-libcheck $(PROGRAM) + ./check-sanitize.sh ./$(PROGRAM) + +itree-tests.o: emacs-compat.h itree-tests.c $(top_srcdir)/src/itree.c $(top_srcdir)/src/itree.h + +$(PROGRAM): $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $(PROGRAM) + +perf: + -$(EMACS) -Q -l ./overlay-perf.el -f perf-run-batch + +clean: + rm -f -- $(OBJECTS) $(PROGRAM) + +distclean: clean + rm -f -- Makefile diff --git a/test/manual/noverlay/check-sanitize.sh b/test/manual/noverlay/check-sanitize.sh new file mode 100755 index 00000000000..03eedce8a67 --- /dev/null +++ b/test/manual/noverlay/check-sanitize.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +prog=$1 +shift + +[ -z "$prog" ] && { + echo "usage:$(basename $0) CHECK_PRGOGRAM"; + exit 1; +} + +"$prog" "$@" | sed -e 's/^\([^:]\+\):\([0-9]\+\):[PFE]:[^:]*:\([^:]*\):[^:]*: *\(.*\)/\1:\2:\3:\4/' diff --git a/test/manual/noverlay/emacs-compat.h b/test/manual/noverlay/emacs-compat.h new file mode 100644 index 00000000000..812f8e48a36 --- /dev/null +++ b/test/manual/noverlay/emacs-compat.h @@ -0,0 +1,52 @@ +#ifndef TEST_COMPAT_H +#define TEST_COMPAT_H + +#include +#include + +typedef int Lisp_Object; + +void * +xmalloc (size_t size) +{ + return malloc (size); +} + +void +xfree (void *ptr) +{ + free (ptr); +} + +void * +xrealloc (void *block, size_t size) +{ + return realloc (block, size); +} + +void +emacs_abort () +{ + fprintf (stderr, "Aborting...\n"); + exit (1); +} + +#ifndef eassert +#define eassert(cond) \ + do { \ + if (! (cond)) { \ + fprintf (stderr, "\n%s:%d:eassert condition failed: %s\n", \ + __FILE__, __LINE__ ,#cond); \ + exit (1); \ + } \ + } while (0) +#endif + +#ifndef max +#define max(x,y) ((x) >= (y) ? (x) : (y)) +#endif +#ifndef min +#define min(x,y) ((x) <= (y) ? (x) : (y)) +#endif + +#endif diff --git a/test/manual/noverlay/itree-tests.c b/test/manual/noverlay/itree-tests.c new file mode 100644 index 00000000000..a3183892132 --- /dev/null +++ b/test/manual/noverlay/itree-tests.c @@ -0,0 +1,1381 @@ +#include +#include +#include +#include +#include "emacs-compat.h" + +#define EMACS_LISP_H /* lisp.h inclusion guard */ +#define ITREE_DEBUG 1 +#define ITREE_TESTING +#include "itree.c" + +/* Basic tests of the interval_tree data-structure. */ + +/* +===================================================================================+ + * | Insert + * +===================================================================================+ */ + +/* The graphs below display the trees after each insertion (as they + should be). See the source code for the different cases + applied. */ + +#define N_50 (n[0]) +#define N_30 (n[1]) +#define N_20 (n[2]) +#define N_10 (n[3]) +#define N_15 (n[4]) +#define N_05 (n[5]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node n[6]; \ + interval_tree_init (&tree); \ + const int values[] = {50, 30, 20, 10, 15, 5}; \ + for (int i = 0; i < 6; ++i) \ + { \ + n[i].begin = values[i]; \ + n[i].end = values[i]; \ + } + +START_TEST (test_insert_1) +{ + /* + * [50] + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (&N_50 == tree.root); +} +END_TEST + +START_TEST (test_insert_2) +{ + /* + * [50] + * / + * (30) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_RED); + ck_assert (&N_50 == tree.root); + ck_assert (N_30.parent == &N_50); + ck_assert (N_50.left == &N_30); + ck_assert (N_50.right == &tree.nil); + ck_assert (N_30.left == &tree.nil); + ck_assert (N_30.right == &tree.nil); +} +END_TEST + +START_TEST (test_insert_3) +{ + /* case 3.a + * [30] + * / \ + * (20) (50) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + ck_assert (N_50.color == ITREE_RED); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_20); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_30); +} +END_TEST + +START_TEST (test_insert_4) +{ + /* 1.a + * [30] + * / \ + * [20] [50] + * / + * (10) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_30); + ck_assert (N_10.parent == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_10.right == &tree.nil); +} +END_TEST + +START_TEST (test_insert_5) +{ + /* 2.a + * [30] + * / \ + * [15] [50] + * / \ + * (10) (20) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + interval_tree_insert (&tree, &N_15); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_RED); + ck_assert (N_10.color == ITREE_RED); + ck_assert (N_15.color == ITREE_BLACK); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_15); + ck_assert (N_10.parent == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_10.right == &tree.nil); + ck_assert (N_15.right == &N_20); + ck_assert (N_15.left == &N_10); + ck_assert (N_15.parent == &N_30); + +} +END_TEST + +START_TEST (test_insert_6) +{ + /* 1.a + * [30] + * / \ + * (15) [50] + * / \ + * [10] [20] + * / + * (5) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + interval_tree_insert (&tree, &N_15); + interval_tree_insert (&tree, &N_05); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_15.color == ITREE_RED); + ck_assert (N_05.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_15); + ck_assert (N_10.parent == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_10.right == &tree.nil); + ck_assert (N_15.right == &N_20); + ck_assert (N_15.left == &N_10); + ck_assert (N_15.parent == &N_30); + ck_assert (N_05.parent == &N_10); + ck_assert (N_10.left == &N_05); + ck_assert (N_05.right == &tree.nil); +} +END_TEST + +#undef N_50 +#undef N_30 +#undef N_20 +#undef N_10 +#undef N_15 +#undef N_05 +#undef DEF_TEST_SETUP + + + +/* These are the mirror cases to the above ones. */ + +#define N_50 (n[0]) +#define N_70 (n[1]) +#define N_80 (n[2]) +#define N_90 (n[3]) +#define N_85 (n[4]) +#define N_95 (n[5]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node n[6]; \ + interval_tree_init (&tree); \ + const int values[] = {50, 70, 80, 90, 85, 95}; \ + for (int i = 0; i < 6; ++i) \ + { \ + n[i].begin = values[i]; \ + n[i].end = values[i]; \ + } + +START_TEST (test_insert_7) +{ + /* + * [50] + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (&N_50 == tree.root); +} +END_TEST + +START_TEST (test_insert_8) +{ + /* + * [50] + * \ + * (70) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_RED); + ck_assert (&N_50 == tree.root); + ck_assert (N_70.parent == &N_50); + ck_assert (N_50.right == &N_70); + ck_assert (N_50.left == &tree.nil); + ck_assert (N_70.right == &tree.nil); + ck_assert (N_70.left == &tree.nil); +} +END_TEST + +START_TEST (test_insert_9) +{ + /* 3.a + * [70] + * / \ + * (50) (80) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + ck_assert (N_50.color == ITREE_RED); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_80); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_70); +} +END_TEST + +START_TEST (test_insert_10) +{ + /* 1.b + * [70] + * / \ + * [50] [80] + * \ + * (90) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_BLACK); + ck_assert (N_90.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_80); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &N_90); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_70); + ck_assert (N_90.parent == &N_80); + ck_assert (N_80.right == &N_90); + ck_assert (N_90.left == &tree.nil); +} +END_TEST + +START_TEST (test_insert_11) +{ + /* 2.b + * [70] + * / \ + * [50] [85] + * / \ + * (80) (90) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + interval_tree_insert (&tree, &N_85); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_RED); + ck_assert (N_90.color == ITREE_RED); + ck_assert (N_85.color == ITREE_BLACK); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_85); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_85); + ck_assert (N_90.parent == &N_85); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_90.left == &tree.nil); + ck_assert (N_85.right == &N_90); + ck_assert (N_85.left == &N_80); + ck_assert (N_85.parent == &N_70); + +} +END_TEST + +START_TEST (test_insert_12) +{ + /* 1.b + * [70] + * / \ + * [50] (85) + * / \ + * [80] [90] + * \ + * (95) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + interval_tree_insert (&tree, &N_85); + interval_tree_insert (&tree, &N_95); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_BLACK); + ck_assert (N_90.color == ITREE_BLACK); + ck_assert (N_85.color == ITREE_RED); + ck_assert (N_95.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_85); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_85); + ck_assert (N_90.parent == &N_85); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_90.left == &tree.nil); + ck_assert (N_85.right == &N_90); + ck_assert (N_85.left == &N_80); + ck_assert (N_85.parent == &N_70); + ck_assert (N_95.parent == &N_90); + ck_assert (N_90.right == &N_95); + ck_assert (N_95.left == &tree.nil); +} +END_TEST + +#undef N_50 +#undef N_70 +#undef N_80 +#undef N_90 +#undef N_85 +#undef N_95 +#undef DEF_TEST_SETUP + +struct interval_tree* +test_get_tree4 (struct interval_node **n) +{ + static struct interval_tree tree; + static struct interval_node nodes[4]; + memset (&tree, 0, sizeof (struct interval_tree)); + memset (&nodes, 0, 4 * sizeof (struct interval_node)); + interval_tree_init (&tree); + for (int i = 0; i < 4; ++i) + { + nodes[i].begin = 10 * (i + 1); + nodes[i].end = nodes[i].begin; + interval_tree_insert (&tree, &nodes[i]); + } + *n = nodes; + return &tree; +} + +static void +shuffle (int *index, int n) +{ + for (int i = n - 1; i >= 0; --i) + { + int j = random () % (i + 1); + int h = index[j]; + index[j] = index[i]; + index[i] = h; + } +} + +#define N_10 (nodes[0]) +#define N_20 (nodes[1]) +#define N_30 (nodes[2]) +#define N_40 (nodes[3]) + +START_TEST (test_insert_13) +{ + struct interval_node *nodes = NULL; + struct interval_tree *tree = test_get_tree4 (&nodes); + + + ck_assert (tree->root == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &N_30); + ck_assert (N_30.right == &N_40); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_RED); +} +END_TEST + +START_TEST (test_insert_14) +{ + struct interval_tree tree; + struct interval_node nodes[3]; + + nodes[0].begin = nodes[1].begin = nodes[2].begin = 10; + nodes[0].end = nodes[1].end = nodes[2].end = 10; + + for (int i = 0; i < 3; ++i) + interval_tree_insert (&tree, &nodes[i]); + for (int i = 0; i < 3; ++i) + ck_assert (interval_tree_contains (&tree, &nodes[i])); +} +END_TEST + + + + +/* +===================================================================================+ + * | Remove + * +===================================================================================+ */ + +#define A (nodes[0]) +#define B (nodes[1]) +#define C (nodes[2]) +#define D (nodes[3]) +#define E (nodes[4]) + +/* Creating proper test trees for the formal tests via insertions is + way to tedious, so we just fake it and only test the + fix-routine. */ +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node nodes[5]; \ + interval_tree_init (&tree); \ + tree.root = &B; \ + A.parent = &B; B.parent = &tree.nil; C.parent = &D; \ + D.parent = &B; E.parent = &D; \ + A.left = A.right = C.left = C.right = &tree.nil; \ + E.left = E.right = &tree.nil; \ + B.left = &A; B.right = &D; D.left = &C; D.right = &E \ + +/* 1.a -> 2.a + * [B] + * / \ + * [A] (D) + * / \ + * [C] [E] + */ + + +START_TEST (test_remove_1) +{ + DEF_TEST_SETUP (); + B.color = A.color = C.color = E.color = ITREE_BLACK; + D.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (D.right == &E); + ck_assert (D.left == &B); + ck_assert (tree.root == &D); +} +END_TEST + +/* 2.a */ +START_TEST (test_remove_2) +{ + DEF_TEST_SETUP (); + B.color = D.color = A.color = C.color = E.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_RED); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &D); + ck_assert (C.parent == &D); + ck_assert (E.parent == &D); + ck_assert (tree.root == &B); +} +END_TEST + +/* 3.a -> 4.a*/ +START_TEST (test_remove_3) +{ + DEF_TEST_SETUP (); + D.color = A.color = E.color = ITREE_BLACK; + B.color = C.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &tree.nil); + ck_assert (&C == tree.root); + ck_assert (C.left == &B); + ck_assert (C.right == &D); + ck_assert (E.parent == &D); + ck_assert (D.left == &tree.nil); + +} +END_TEST + +/* 4.a */ +START_TEST (test_remove_4) +{ + DEF_TEST_SETUP (); + B.color = C.color = E.color = ITREE_RED; + A.color = D.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (tree.root == &D); +} +END_TEST + + +#undef A +#undef B +#undef C +#undef D +#undef E +#undef DEF_TEST_SETUP + + + +/* These are the mirrored cases. */ + +#define A (nodes[0]) +#define B (nodes[1]) +#define C (nodes[2]) +#define D (nodes[3]) +#define E (nodes[4]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node nodes[5]; \ + interval_tree_init (&tree); \ + tree.root = &B; \ + A.parent = &B; B.parent = &tree.nil; C.parent = &D; \ + D.parent = &B; E.parent = &D; \ + A.right = A.left = C.right = C.left = &tree.nil; \ + E.right = E.left = &tree.nil; \ + B.right = &A; B.left = &D; D.right = &C; D.left = &E \ + +/* 1.b -> 2.b + * [B] + * / \ + * [A] (D) + * / \ + * [C] [E] + */ + + +START_TEST (test_remove_5) +{ + DEF_TEST_SETUP (); + B.color = A.color = C.color = E.color = ITREE_BLACK; + D.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (D.left == &E); + ck_assert (D.right == &B); + ck_assert (tree.root == &D); +} +END_TEST + +/* 2.b */ +START_TEST (test_remove_6) +{ + DEF_TEST_SETUP (); + B.color = D.color = A.color = C.color = E.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_RED); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &D); + ck_assert (C.parent == &D); + ck_assert (E.parent == &D); + ck_assert (tree.root == &B); +} +END_TEST + +/* 3.b -> 4.b*/ +START_TEST (test_remove_7) +{ + DEF_TEST_SETUP (); + D.color = A.color = E.color = ITREE_BLACK; + B.color = C.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &tree.nil); + ck_assert (&C == tree.root); + ck_assert (C.right == &B); + ck_assert (C.left == &D); + ck_assert (E.parent == &D); + ck_assert (D.right == &tree.nil); + +} +END_TEST + +/* 4.b */ +START_TEST (test_remove_8) +{ + DEF_TEST_SETUP (); + B.color = C.color = E.color = ITREE_RED; + A.color = D.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (tree.root == &D); +} +END_TEST + + +#undef A +#undef B +#undef C +#undef D +#undef E +#undef DEF_TEST_SETUP + + +START_TEST (test_remove_9) +{ + struct interval_node *nodes = NULL; + struct interval_tree *tree = test_get_tree4 (&nodes); + + ck_assert (tree->root == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &N_30); + ck_assert (N_30.right == &N_40); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_RED); + + interval_tree_remove (tree, &N_10); + + ck_assert (tree->root == &N_30); + ck_assert (N_30.parent == &tree->nil); + ck_assert (N_30.left == &N_20); + ck_assert (N_30.right == &N_40); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_BLACK); +} +END_TEST + +#define N 3 + +START_TEST (test_remove_10) +{ + struct interval_tree tree; + struct interval_node nodes[N]; + int index[N]; + + srand (42); + interval_tree_init (&tree); + for (int i = 0; i < N; ++i) + { + nodes[i].begin = (i + 1) * 10; + nodes[i].end = nodes[i].begin + 1; + index[i] = i; + } + shuffle (index, N); + for (int i = 0; i < N; ++i) + interval_tree_insert (&tree, &nodes[index[i]]); + + shuffle (index, N); + for (int i = 0; i < N; ++i) + { + ck_assert (interval_tree_contains (&tree, &nodes[index[i]])); + interval_tree_remove (&tree, &nodes[index[i]]); + } + ck_assert (tree.root == &tree.nil); + ck_assert (tree.size == 0); +} +END_TEST + + +/* +===================================================================================+ + * | Generator + * +===================================================================================+ */ + +START_TEST (test_generator_1) +{ + struct interval_tree tree; + struct interval_node node, *n; + struct interval_generator *g; + interval_tree_init (&tree); + node.begin = 10; + node.end = 20; + interval_tree_insert (&tree, &node); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 30, ITREE_ASCENDING); + n = interval_generator_next (g); + ck_assert (n == &node); + ck_assert (n->begin == 10 && n->end == 20); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + interval_generator_destroy (g); + + g = interval_generator_create (&tree); + interval_generator_reset (g, 30, 50, ITREE_ASCENDING); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + interval_generator_destroy (g); +} +END_TEST + +void +test_check_generator (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end, + int n, ...) +{ + va_list ap; + struct interval_generator *g = interval_generator_create (tree); + interval_generator_reset (g, begin, end, ITREE_ASCENDING); + + va_start (ap, n); + for (int i = 0; i < n; ++i) + { + ptrdiff_t begin = va_arg (ap, ptrdiff_t); + struct interval_node *node = interval_generator_next (g); + ck_assert (node); + ck_assert_int_eq (node->begin, begin); + } + va_end (ap); + ck_assert (! interval_generator_next (g)); + ck_assert (! interval_generator_next (g)); + interval_generator_destroy (g); +} + +#define DEF_TEST_SETUP() \ + + +START_TEST (test_generator_2) +{ + struct interval_tree tree; + struct interval_node nodes[3]; + + interval_tree_init (&tree); + + for (int i = 0; i < 3; ++i) { + nodes[i].begin = 10 * (i + 1); + nodes[i].end = 10 * (i + 2); + interval_tree_insert (&tree, &nodes[i]); + } + + test_check_generator (&tree, 0, 50, 3, + 10, 20, 30); + test_check_generator (&tree, 0, 10, 0); + test_check_generator (&tree, 40, 50, 0); + test_check_generator (&tree, 15, 35, 3, + 10, 20, 30); + test_check_generator (&tree, -100, -50, 0); + test_check_generator (&tree, -100, -50, 0); + test_check_generator (&tree, 100, 50, 0); + test_check_generator (&tree, 100, 150, 0); + test_check_generator (&tree, 0, 0, 0); + test_check_generator (&tree, 40, 40, 0); + test_check_generator (&tree, 30, 30, 0); + test_check_generator (&tree, 35, 35, 1, + 30); +} +END_TEST + + +struct interval_node* +test_create_tree (struct interval_tree *tree, int n, + bool doshuffle, ...) +{ + va_list ap; + struct interval_node *nodes = calloc (n, sizeof (struct interval_node)); + int *index = calloc (n, sizeof (int)); + + interval_tree_init (tree); + va_start (ap, doshuffle); + for (int i = 0; i < n; ++i) + { + ptrdiff_t begin = va_arg (ap, ptrdiff_t); + ptrdiff_t end = va_arg (ap, ptrdiff_t); + nodes[i].begin = begin; + nodes[i].end = end; + index[i] = i; + } + va_end (ap); + srand (42); + if (doshuffle) + shuffle (index, n); + for (int i = 0; i < n; ++i) + interval_tree_insert (tree, &nodes[index[i]]); + free (index); + + return nodes; +} + +START_TEST (test_generator_3) +{ + struct interval_tree tree; + struct interval_node *nodes = NULL; + + nodes = test_create_tree (&tree, 3, true, + 10, 10, + 10, 10, + 10, 10); + test_check_generator (&tree, 0, 10, 0); + test_check_generator (&tree, 10, 10, 3, 10, 10, 10); + test_check_generator (&tree, 10, 20, 3, 10, 10, 10); + free (nodes); +} +END_TEST + +#define FOREACH(n, g) \ + for ((n) = interval_generator_next (g); (n) != NULL; \ + (n) = interval_generator_next (g)) + +START_TEST (test_generator_5) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, false, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_PRE_ORDER); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (20, n->begin); break; + case 1: ck_assert_int_eq (10, n->begin); break; + case 2: ck_assert_int_eq (30, n->begin); break; + case 3: ck_assert_int_eq (40, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_6) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, true, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_ASCENDING); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (10, n->begin); break; + case 1: ck_assert_int_eq (20, n->begin); break; + case 2: ck_assert_int_eq (30, n->begin); break; + case 3: ck_assert_int_eq (40, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_7) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, true, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_DESCENDING); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (40, n->begin); break; + case 1: ck_assert_int_eq (30, n->begin); break; + case 2: ck_assert_int_eq (20, n->begin); break; + case 3: ck_assert_int_eq (10, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_8) +{ + struct interval_tree tree; + struct interval_node *nodes, *n; + struct interval_generator *g; + nodes = test_create_tree (&tree, 2, false, + 20, 30, + 40, 50); + g = interval_generator_create (&tree); + interval_generator_reset (g, 1, 60, ITREE_DESCENDING); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 40); + interval_generator_narrow (g, 50, 60); + n = interval_generator_next (g); + ck_assert (n == NULL); + free (nodes); +} +END_TEST + + +START_TEST (test_generator_9) +{ + struct interval_tree tree; + struct interval_node *nodes, *n; + struct interval_generator *g; + nodes = test_create_tree (&tree, 2, false, + 25, 25, + 20, 30); + g = interval_generator_create (&tree); + interval_generator_reset (g, 1, 30, ITREE_DESCENDING); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 25); + interval_generator_narrow (g, 25, 35); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 20); + free (nodes); +} +END_TEST + + +/* +===================================================================================+ + * | Insert Gap + * +===================================================================================+ */ + +static struct interval_tree gap_tree; +static struct interval_node gap_node; + +#define N_BEG (interval_tree_validate (&gap_tree, &gap_node)->begin) +#define N_END (interval_tree_validate (&gap_tree, &gap_node)->end) + +static void +test_setup_gap_node (ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance) +{ + interval_tree_init (&gap_tree); + gap_node.begin = begin; + gap_node.end = end; + gap_node.front_advance = front_advance; + gap_node.rear_advance = rear_advance; + interval_tree_insert (&gap_tree, &gap_node); +} + +static void +test_setup_gap_node_noadvance (ptrdiff_t begin, ptrdiff_t end) +{ + test_setup_gap_node (begin, end, false, false); +} + +START_TEST (test_gap_insert_1) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 100 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); +} +END_TEST + +START_TEST (test_gap_insert_2) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 300, 10); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); +} +END_TEST + +START_TEST (test_gap_insert_3) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 0, 15); + ck_assert_int_eq (N_BEG, 100 + 15); + ck_assert_int_eq (N_END, 200 + 15); +} +END_TEST + +START_TEST (test_gap_insert_4) +{ + test_setup_gap_node (100, 200, true, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100 + 20); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_5) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_6) +{ + test_setup_gap_node (100, 200, false, true); + interval_tree_insert_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_7) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_insert_8) +{ + test_setup_gap_node (100, 100, true, true); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100 + 20); + ck_assert_int_eq (N_END, 100 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_9) +{ + test_setup_gap_node (100, 100, false, true); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_10) +{ + test_setup_gap_node (100, 100, true, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100); + +} +END_TEST + +START_TEST (test_gap_insert_11) +{ + test_setup_gap_node (100, 100, false, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100); + +} +END_TEST + + +/* +===================================================================================+ + * | Delete Gap + * +===================================================================================+ */ + +START_TEST (test_gap_delete_1) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_2) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 200 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_delete_3) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_delete_4) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 - 20, 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_5) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 70, 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_6) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 80, 100); + ck_assert_int_eq (N_BEG, 80); + ck_assert_int_eq (N_END, 100); +} +END_TEST + +START_TEST (test_gap_delete_7) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 120, 100); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 120); +} +END_TEST + +START_TEST (test_gap_delete_8) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 - 20, 200 + 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 100 - 20); + +} +END_TEST + + + +Suite * basic_suite () +{ + Suite *s = suite_create ("basic_suite"); + TCase *tc = tcase_create ("basic_test"); + + tcase_add_test (tc, test_insert_1); + tcase_add_test (tc, test_insert_2); + tcase_add_test (tc, test_insert_3); + tcase_add_test (tc, test_insert_4); + tcase_add_test (tc, test_insert_5); + tcase_add_test (tc, test_insert_6); + tcase_add_test (tc, test_insert_7); + tcase_add_test (tc, test_insert_8); + tcase_add_test (tc, test_insert_9); + tcase_add_test (tc, test_insert_10); + tcase_add_test (tc, test_insert_11); + tcase_add_test (tc, test_insert_12); + tcase_add_test (tc, test_insert_13); + + tcase_add_test (tc, test_remove_1); + tcase_add_test (tc, test_remove_2); + tcase_add_test (tc, test_remove_3); + tcase_add_test (tc, test_remove_4); + tcase_add_test (tc, test_remove_5); + tcase_add_test (tc, test_remove_6); + tcase_add_test (tc, test_remove_7); + tcase_add_test (tc, test_remove_8); + tcase_add_test (tc, test_remove_9); + tcase_add_test (tc, test_remove_10); + + tcase_add_test (tc, test_generator_1); + tcase_add_test (tc, test_generator_2); + tcase_add_test (tc, test_generator_3); + tcase_add_test (tc, test_generator_5); + tcase_add_test (tc, test_generator_6); + tcase_add_test (tc, test_generator_7); + tcase_add_test (tc, test_generator_8); + tcase_add_test (tc, test_generator_9); + + tcase_add_test (tc, test_gap_insert_1); + tcase_add_test (tc, test_gap_insert_2); + tcase_add_test (tc, test_gap_insert_3); + tcase_add_test (tc, test_gap_insert_4); + tcase_add_test (tc, test_gap_insert_5); + tcase_add_test (tc, test_gap_insert_6); + tcase_add_test (tc, test_gap_insert_7); + tcase_add_test (tc, test_gap_insert_8); + tcase_add_test (tc, test_gap_insert_9); + tcase_add_test (tc, test_gap_insert_10); + tcase_add_test (tc, test_gap_insert_11); + + tcase_add_test (tc, test_gap_delete_1); + tcase_add_test (tc, test_gap_delete_2); + tcase_add_test (tc, test_gap_delete_3); + tcase_add_test (tc, test_gap_delete_4); + tcase_add_test (tc, test_gap_delete_5); + tcase_add_test (tc, test_gap_delete_6); + tcase_add_test (tc, test_gap_delete_7); + tcase_add_test (tc, test_gap_delete_8); + + /* tcase_set_timeout (tc, 120); */ + suite_add_tcase (s, tc); + return s; +} + +int +main (void) +{ + int nfailed; + Suite *s = basic_suite (); + SRunner *sr = srunner_create (s); + + srunner_run_all (sr, CK_NORMAL); + nfailed = srunner_ntests_failed (sr); + srunner_free (sr); + return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/manual/noverlay/many-errors.py b/test/manual/noverlay/many-errors.py new file mode 100644 index 00000000000..fa4ef5f98d1 --- /dev/null +++ b/test/manual/noverlay/many-errors.py @@ -0,0 +1,2480 @@ +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass diff --git a/test/manual/noverlay/overlay-perf.el b/test/manual/noverlay/overlay-perf.el new file mode 100644 index 00000000000..e84941c08f9 --- /dev/null +++ b/test/manual/noverlay/overlay-perf.el @@ -0,0 +1,764 @@ +;; -*- lexical-binding:t -*- +(require 'cl-lib) +(require 'subr-x) +(require 'seq) +(require 'hi-lock) + + +;; +===================================================================================+ +;; | Framework +;; +===================================================================================+ + +(defmacro perf-define-constant-test (name &optional doc &rest body) + (declare (indent 1) (debug (symbol &optional string &rest form))) + `(progn + (put ',name 'perf-constant-test t) + (defun ,name nil ,doc ,@body))) + +(defmacro perf-define-variable-test (name args &optional doc &rest body) + (declare (indent 2) (debug defun)) + (unless (and (consp args) + (= (length args) 1)) + (error "Function %s should accept exactly one argument." name)) + `(progn + (put ',name 'perf-variable-test t) + (defun ,name ,args ,doc ,@body))) + +(defmacro perf-define-test-suite (name &rest tests) + (declare (indent 1)) + `(put ',name 'perf-test-suite + ,(cons 'list tests))) + +(defun perf-constant-test-p (test) + (get test 'perf-constant-test)) + +(defun perf-variable-test-p (test) + (get test 'perf-variable-test)) + +(defun perf-test-suite-p (suite) + (not (null (perf-test-suite-elements suite)))) + +(defun perf-test-suite-elements (suite) + (get suite 'perf-test-suite)) + +(defun perf-expand-suites (test-and-suites) + (apply #' append (mapcar (lambda (elt) + (if (perf-test-suite-p elt) + (perf-test-suite-elements elt) + (list elt))) + test-and-suites))) +(defun perf-test-p (symbol) + (or (perf-variable-test-p symbol) + (perf-constant-test-p symbol))) + +(defun perf-all-tests () + (let (result) + (mapatoms (lambda (symbol) + (when (and (fboundp symbol) + (perf-test-p symbol)) + (push symbol result)))) + (sort result #'string-lessp))) + +(defvar perf-default-test-argument 4096) + +(defun perf-run-1 (&optional k n &rest tests) + "Run TESTS K times using N as argument for non-constant ones. + +Return test-total elapsed time." + (random "") + (when (and n (not (numberp n))) + (push k tests) + (push n tests) + (setq n nil k nil)) + (when (and k (not (numberp k))) + (push k tests) + (setq k nil)) + (let* ((k (or k 1)) + (n (or n perf-default-test-argument)) + (tests (perf-expand-suites (or tests + (perf-all-tests)))) + (variable-tests (seq-filter #'perf-variable-test-p tests)) + (constant-tests (seq-filter #'perf-constant-test-p tests)) + (max-test-string-width (perf-max-symbol-length tests))) + (unless (seq-every-p #'perf-test-p tests) + (error "Some of these are not tests: %s" tests)) + (cl-labels ((format-result (result) + (cond + ((numberp result) (format "%.2f" result)) + ((stringp result) result) + ((null result) "N/A"))) + (format-test (fn) + (concat (symbol-name fn) + (make-string + (+ (- max-test-string-width + (length (symbol-name fn))) + 1) + ?\s))) + (format-summary (results _total) + (let ((min (apply #'min results)) + (max (apply #'max results)) + (avg (/ (apply #'+ results) (float (length results))))) + (format "n=%d min=%.2f avg=%.2f max=%.2f" (length results) min avg max))) + (run-test (fn) + (let ((total 0) results) + (dotimes (_ (max 0 k)) + (garbage-collect) + (princ (concat " " (format-test fn))) + (let ((result (condition-case-unless-debug err + (cond + ((perf-variable-test-p fn) + (random "") (car (funcall fn n))) + ((perf-constant-test-p fn) + (random "") (car (funcall fn))) + (t "skip")) + (error (error-message-string err))))) + (when (numberp result) + (cl-incf total result) + (push result results)) + (princ (format-result result)) + (terpri))) + (when (> (length results) 1) + (princ (concat "#" (format-test fn) + (format-summary results total))) + (terpri))))) + (when variable-tests + (terpri) + (dolist (fn variable-tests) + (run-test fn) + (terpri))) + (when constant-tests + (dolist (fn constant-tests) + (run-test fn) + (terpri)))))) + +(defun perf-run (&optional k n &rest tests) + (interactive + (let* ((n (if current-prefix-arg + (prefix-numeric-value current-prefix-arg) + perf-default-test-argument)) + (tests (mapcar #'intern + (completing-read-multiple + (format "Run tests (n=%d): " n) + (perf-all-tests) nil t nil 'perf-test-history)))) + (cons 1 (cons n tests)))) + (with-current-buffer (get-buffer-create "*perf-results*") + (let ((inhibit-read-only t) + (standard-output (current-buffer))) + (erase-buffer) + (apply #'perf-run-1 k n tests) + (display-buffer (current-buffer))))) + + +(defun perf-batch-parse-command-line (args) + (let ((k 1) + (n perf-default-test-argument) + tests) + (while args + (cond ((string-match-p "\\`-[cn]\\'" (car args)) + (unless (and (cdr args) + (string-match-p "\\`[0-9]+\\'" (cadr args))) + (error "%s expectes a natnum argument" (car args))) + (if (equal (car args) "-c") + (setq k (string-to-number (cadr args))) + (setq n (string-to-number (cadr args)))) + (setq args (cddr args))) + (t (push (intern (pop args)) tests)))) + (list k n tests))) + + +(defun perf-run-batch () + "Runs tests from `command-line-args-left' and kill emacs." + (let ((standard-output #'external-debugging-output)) + (condition-case err + (cl-destructuring-bind (k n tests) + (perf-batch-parse-command-line command-line-args-left) + (apply #'perf-run-1 k n tests) + (save-buffers-kill-emacs)) + (error + (princ (error-message-string err)) + (save-buffers-kill-emacs))))) + +(defconst perf-number-of-columns 70) + +(defun perf-insert-lines (n) + "Insert N lines into the current buffer." + (dotimes (i n) + (insert (make-string 70 (if (= (% i 2) 0) + ?. + ?O)) + ?\n))) + +(defun perf-switch-to-buffer-scroll-random (n &optional buffer) + (interactive) + (set-window-buffer nil (or buffer (current-buffer))) + (goto-char (point-min)) + (redisplay t) + (dotimes (_ n) + (goto-char (random (point-max))) + (recenter) + (redisplay t))) + +(defun perf-insert-overlays (n &optional create-callback random-p) + (if random-p + (perf-insert-overlays-random n create-callback) + (perf-insert-overlays-sequential n create-callback))) + +(defun perf-insert-overlays-sequential (n &optional create-callback) + "Insert an overlay every Nth line." + (declare (indent 1)) + (let ((i 0) + (create-callback (or create-callback #'ignore))) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (when (= 0 (% i n)) + (let ((ov (make-overlay (point-at-bol) (point-at-eol)))) + (funcall create-callback ov) + (overlay-put ov 'priority (random (buffer-size))))) + (cl-incf i) + (forward-line))))) + +(defun perf-insert-overlays-random (n &optional create-callback) + "Insert an overlay every Nth line." + (declare (indent 1)) + (let ((create-callback (or create-callback #'ignore))) + (save-excursion + (while (>= (cl-decf n) 0) + (let* ((beg (1+ (random (point-max)))) + (ov (make-overlay beg (+ beg (random 70))))) + (funcall create-callback ov) + (overlay-put ov 'priority (random (buffer-size)))))))) + +(defun perf-insert-overlays-hierarchical (n &optional create-callback) + (let ((create-callback (or create-callback #'ignore))) + (save-excursion + (goto-char (point-min)) + (let ((spacing (floor (/ (/ (count-lines (point-min) (point-max)) + (float 3)) + n)))) + (when (< spacing 1) + (error "Hierarchical overlay overflow !!")) + (dotimes (i n) + (funcall create-callback + (make-overlay (point) + (save-excursion + (goto-char (point-max)) + (forward-line (- (* spacing i))) + (point)))) + + (when (eobp) + (error "End of buffer in hierarchical overlays")) + (forward-line spacing)))))) + +(defun perf-overlay-ascii-chart (&optional buffer width) + (interactive) + (save-current-buffer + (when buffer (set-buffer buffer)) + (unless width (setq width 100)) + (let* ((ovl (sort (overlays-in (point-min) (point-max)) + (lambda (ov1 ov2) + (or (<= (overlay-start ov1) + (overlay-start ov2)) + (and + (= (overlay-start ov1) + (overlay-start ov2)) + (< (overlay-end ov1) + (overlay-end ov2))))))) + (ov-width (apply #'max (mapcar (lambda (ov) + (- (overlay-end ov) + (overlay-start ov))) + ovl))) + (ov-min (apply #'min (mapcar #'overlay-start ovl))) + (ov-max (apply #'max (mapcar #'overlay-end ovl))) + (scale (/ (float width) (+ ov-min ov-width)))) + (with-current-buffer (get-buffer-create "*overlay-ascii-chart*") + (let ((inhibit-read-only t)) + (erase-buffer) + (buffer-disable-undo) + (insert (format "%06d%s%06d\n" ov-min (make-string (- width 12) ?\s) ov-max)) + (dolist (ov ovl) + (let ((length (round (* scale (- (overlay-end ov) + (overlay-start ov)))))) + (insert (make-string (round (* scale (overlay-start ov))) ?\s)) + (cl-case length + (0 (insert "O")) + (1 (insert "|")) + (t (insert (format "|%s|" (make-string (- length 2) ?-))))) + (insert "\n"))) + (goto-char (point-min))) + (read-only-mode 1) + (pop-to-buffer (current-buffer)))))) + +(defconst perf-overlay-faces (mapcar #'intern (seq-take hi-lock-face-defaults 3))) + +(defun perf-overlay-face-callback (ov) + (overlay-put ov 'face (nth (random (length perf-overlay-faces)) + perf-overlay-faces))) + +(defun perf-overlay-invisible-callback (ov) + (overlay-put ov 'invisble (= 1 (random 2)))) + +(defun perf-overlay-display-callback (ov) + (overlay-put ov 'display (make-string 70 ?*))) + +(defmacro perf-define-display-test (overlay-type property-type scroll-type) + (let ((name (intern (format "perf-display-%s/%s/%s" + overlay-type property-type scroll-type))) + (arg (make-symbol "n"))) + + `(perf-define-variable-test ,name (,arg) + (with-temp-buffer + (perf-insert-lines ,arg) + (overlay-recenter (point-max)) + ,@(perf-define-display-test-1 arg overlay-type property-type scroll-type))))) + +(defun perf-define-display-test-1 (arg overlay-type property-type scroll-type) + (list (append (cl-case overlay-type + (sequential + (list 'perf-insert-overlays-sequential 2)) + (hierarchical + `(perf-insert-overlays-hierarchical (/ ,arg 10))) + (random + `(perf-insert-overlays-random (/ ,arg 2))) + (t (error "Invalid insert type: %s" overlay-type))) + (list + (cl-case property-type + (display '#'perf-overlay-display-callback) + (face '#'perf-overlay-face-callback) + (invisible '#'perf-overlay-invisible-callback) + (t (error "Invalid overlay type: %s" overlay-type))))) + (list 'benchmark-run 1 + (cl-case scroll-type + (scroll '(perf-switch-to-buffer-scroll-up-and-down)) + (random `(perf-switch-to-buffer-scroll-random (/ ,arg 50))) + (t (error "Invalid scroll type: %s" overlay-type)))))) + +(defun perf-max-symbol-length (symbols) + "Return the longest symbol in SYMBOLS, or -1 if symbols is nil." + (if (null symbols) + -1 + (apply #'max (mapcar + (lambda (elt) + (length (symbol-name elt))) + symbols)))) + +(defun perf-insert-text (n) + "Insert N character into the current buffer." + (let ((ncols 68) + (char ?.)) + (dotimes (_ (/ n ncols)) + (insert (make-string (1- ncols) char) ?\n)) + (when (> (% n ncols) 0) + (insert (make-string (1- (% n ncols)) char) ?\n)))) + +(defconst perf-insert-overlays-default-length 24) + +(defun perf-insert-overlays-scattered (n &optional length) + "Insert N overlays of max length 24 randomly." + (dotimes (_ n) + (let ((begin (random (1+ (point-max))))) + (make-overlay + begin (+ begin (random (1+ (or length perf-insert-overlays-default-length 0)))))))) + +(defvar perf-marker-gc-protection nil) + +(defun perf-insert-marker-scattered (n) + "Insert N marker randomly." + (setq perf-marker-gc-protection nil) + (dotimes (_ n) + (push (copy-marker (random (1+ (point-max)))) + perf-marker-gc-protection))) + +(defun perf-switch-to-buffer-scroll-up-and-down (&optional buffer) + (interactive) + (set-window-buffer nil (or buffer (current-buffer))) + (goto-char (point-min)) + (redisplay t) + (while (condition-case nil + (progn (scroll-up) t) + (end-of-buffer nil)) + (redisplay t)) + (while (condition-case nil + (progn (scroll-down) t) + (beginning-of-buffer nil)) + (redisplay t))) + +(defun perf-emacs-lisp-setup () + (add-to-list 'imenu-generic-expression + '(nil "^\\s-*(perf-define\\(?:\\w\\|\\s_\\)*\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)" 1))) + +(add-hook 'emacs-lisp-mode 'perf-emacs-lisp-setup) + + +;; +===================================================================================+ +;; | Basic performance tests +;; +===================================================================================+ + +(perf-define-variable-test perf-make-overlay (n) + (with-temp-buffer + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ n) + (make-overlay 1 1))))) + +(perf-define-variable-test perf-make-overlay-continuous (n) + (with-temp-buffer + (perf-insert-text n) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (i n) + (make-overlay i (1+ i)))))) + +(perf-define-variable-test perf-make-overlay-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (benchmark-run 1 + (perf-insert-overlays-scattered n)))) + +(perf-define-variable-test perf-delete-overlay (n) + (with-temp-buffer + (let ((ovls (cl-loop for i from 1 to n + collect (make-overlay 1 1)))) + (overlay-recenter (point-min)) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-delete-overlay-continuous (n) + (with-temp-buffer + (perf-insert-text n) + (let ((ovls (cl-loop for i from 1 to n + collect (make-overlay i (1+ i))))) + (overlay-recenter (point-min)) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-delete-overlay-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (let ((ovls (progn (perf-insert-overlays-scattered n) + (overlays-in (point-min) (point-max))))) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-overlays-at (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (benchmark-run 1 + (dotimes (i (point-max)) + (overlays-at i))))) + +(perf-define-variable-test perf-overlays-in (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (let ((len perf-insert-overlays-default-length)) + (benchmark-run 1 + (dotimes (i (- (point-max) len)) + (overlays-in i (+ i len))))))) + +(perf-define-variable-test perf-insert-before (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char 1) + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (insert ?X))))) + +(perf-define-variable-test perf-insert-before-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-before n))) +(perf-define-variable-test perf-insert-after-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-after n))) +(perf-define-variable-test perf-insert-scatter-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-scatter n))) +(perf-define-variable-test perf-delete-before-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-before n))) +(perf-define-variable-test perf-delete-after-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-after n))) +(perf-define-variable-test perf-delete-scatter-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-scatter n))) + +(defmacro perf-define-marker-test (type where) + (let ((name (intern (format "perf-%s-%s-marker" type where)))) + `(perf-define-variable-test ,name (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-marker-scattered n) + (goto-char ,(cl-case where + (after (list 'point-max)) + (t (list 'point-min)))) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + ,@(when (eq where 'scatter) + (list '(goto-char (max 1 (random (point-max)))))) + ,(cl-case type + (insert (list 'insert ?X)) + (delete (list 'delete-char (if (eq where 'after) -1 1)))))))))) + +(perf-define-test-suite perf-marker-suite + (perf-define-marker-test insert before) + (perf-define-marker-test insert after) + (perf-define-marker-test insert scatter) + (perf-define-marker-test delete before) + (perf-define-marker-test delete after) + (perf-define-marker-test delete scatter)) + +(perf-define-variable-test perf-insert-after (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (insert ?X))))) + +(perf-define-variable-test perf-insert-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (goto-char (1+ (random (point-max)))) + (insert ?X))))) + +(perf-define-variable-test perf-delete-before (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char 1) + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (delete-char 1))))) + +(perf-define-variable-test perf-delete-after (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (delete-char -1))))) + +(perf-define-variable-test perf-delete-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (goto-char (max 1 (random (point-max)))) + (delete-char 1))))) + +(perf-define-test-suite perf-insert-delete-suite + 'perf-insert-before + 'perf-insert-after + 'perf-insert-scatter + 'perf-delete-before + 'perf-delete-after + 'perf-delete-scatter + ) + + +;; +===================================================================================+ +;; | Redisplay (new) +;; +===================================================================================+ + +;; 5000 +;; 25000 +;; 75000 + +;; Number of Overlays = N / 2 +;; +;; (except for the hierarchical case, where it is divided by 10.) + + ;; . scrolling through a buffer with lots of overlays that affect faces + ;; of characters in the buffer text + ;; . scrolling through a buffer with lots of overlays that define + ;; 'display' properties which are strings + ;; . scrolling through a buffer with lots of overlays that define + ;; 'invisible' properties + +(perf-define-test-suite perf-display-suite + (perf-define-display-test sequential display scroll) + (perf-define-display-test sequential display random) + (perf-define-display-test sequential face scroll) + (perf-define-display-test sequential face random) + (perf-define-display-test sequential invisible scroll) + (perf-define-display-test sequential invisible random) + (perf-define-display-test random display scroll) + (perf-define-display-test random display random) + (perf-define-display-test random face scroll) + (perf-define-display-test random face random) + (perf-define-display-test random invisible scroll) + (perf-define-display-test random invisible random)) + +;; |------------| +;; |--------| +;; |----| +(perf-define-display-test hierarchical face scroll) + + + + +;; +===================================================================================+ +;; | Real World +;; +===================================================================================+ + +(require 'python) + +(defconst perf-many-errors-file + (expand-file-name "many-errors.py" + (and load-file-name (file-name-directory load-file-name)))) + +(perf-define-constant-test perf-realworld-flycheck + (interactive) + (package-initialize) + (when (and (require 'flycheck nil t) + (file-exists-p perf-many-errors-file) + (or (executable-find "pylint") + (executable-find "flake8"))) + (setq flycheck-python-pylint-executable + (executable-find "pylint")) + (setq flycheck-python-flake8-executable + (executable-find "flake8")) + (setq python-indent-guess-indent-offset-verbose nil) + (setq flycheck-check-syntax-automatically nil) + (setq flycheck-checker-error-threshold nil) + (setq flycheck-display-errors-function nil) + (with-current-buffer (find-file-noselect perf-many-errors-file) + (let* ((done) + (flycheck-after-syntax-check-hook + (list (lambda () (setq done t))))) + (flycheck-mode 1) + (flycheck-buffer) + (benchmark-run 1 + (while (not done) + (accept-process-output)) + (perf-switch-to-buffer-scroll-up-and-down) + (flycheck-mode -1)))))) + +;; https://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00242.html +(defun make-lines-invisible (regexp &optional arg) + "Make all lines matching a regexp invisible and intangible. +With a prefix arg, make it visible again. It is not necessary +that REGEXP matches the whole line; if a hit is found, the +affected line gets automatically selected. + +This command affects the whole buffer." + (interactive "MRegexp: \nP") + (let (ov + ovs + count) + (cond + ((equal arg '(4)) + (setq ovs (overlays-in (point-min) (point-max))) + (mapc (lambda (o) + (if (overlay-get o 'make-lines-invisible) + (delete-overlay o))) + ovs)) + (t + (save-excursion + (goto-char (point-min)) + (setq count 0) + (while (re-search-forward regexp nil t) + (setq count (1+ count)) + (if (= (% count 100) 0) + (message "%d" count)) + (setq ov (make-overlay (line-beginning-position) + (1+ (line-end-position)))) + (overlay-put ov 'make-lines-invisible t) + (overlay-put ov 'invisible t) + (overlay-put ov 'intangible t) + (goto-char (line-end-position)))))))) + +(perf-define-constant-test perf-realworld-make-lines-invisible + (with-temp-buffer + (insert-file-contents "/usr/share/dict/words") + (set-window-buffer nil (current-buffer)) + (redisplay t) + (overlay-recenter (point-max)) + (benchmark-run 1 + (make-lines-invisible "a")))) + +(perf-define-constant-test perf-realworld-line-numbering + (interactive) + (with-temp-buffer + (insert-file-contents "/usr/share/dict/words") + (overlay-recenter (point-max)) + (goto-char (point-min)) + (let* ((nlines (count-lines (point-min) (point-max))) + (line 1) + (width 0)) + (dotimes (i nlines) ;;-with-progress-reporter "Creating overlays" + (let ((ov (make-overlay (point) (point))) + (str (propertize (format "%04d" line) 'face 'shadow))) + (overlay-put ov 'before-string + (propertize " " 'display `((margin left-margin) ,str))) + (setq width (max width (length str))) + (cl-incf line) + (forward-line))) + (benchmark-run 1 + (let ((left-margin-width width)) + (perf-switch-to-buffer-scroll-up-and-down)))))) + +(perf-define-test-suite perf-realworld-suite + 'perf-realworld-flycheck + 'perf-realworld-make-lines-invisible + 'perf-realworld-line-numbering) + + +;; +===================================================================================+ +;; | next-overlay-change +;; +===================================================================================+ + +(perf-define-variable-test perf-noc-hierarchical/forward/linear (n) + "Search linear for the next change on every line." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (perf-insert-overlays-hierarchical n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (forward-line))))) + +(perf-define-variable-test perf-noc-sequential/forward/linear (n) + "Search linear for the next change on every line." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (perf-insert-overlays-sequential n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (forward-line))))) + +(perf-define-variable-test perf-noc-hierarchical/forward/backnforth (n) + "Search back and forth for the next change from `point-min' to `point-max'." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (overlay-recenter (point-max)) + (perf-insert-overlays-hierarchical n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (next-overlay-change (+ (point) 2)) + (forward-char))))) + +(perf-define-test-suite perf-noc-suite + 'perf-noc-hierarchical/forward/linear + 'perf-noc-hierarchical/forward/backnforth + 'perf-noc-hierarchical/forward/backnforth) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 834acaf66f5..71f8705c6fd 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -20,6 +20,7 @@ ;;; Code: (require 'ert) +(require 'seq) (ert-deftest overlay-modification-hooks-message-other-buf () "Test for bug#21824. @@ -50,4 +51,7522 @@ with parameters from the *Messages* buffer modification." (progn (get-buffer-create "nil") (generate-new-buffer-name "nil"))))) + +;; +===================================================================================+ +;; | Overlay test setup +;; +===================================================================================+ + +(eval-when-compile + (defun make-overlay-test-name (fn x y) + (intern (format "test-%s-%s-%s" fn x y)))) + +(defun unmake-ov-test-name (symbol) + (let ((name (if (stringp symbol) symbol (symbol-name symbol)))) + (when (string-match "\\`test-\\(.*\\)-\\(.*\\)-\\(.*\\)\\'" name) + (list (match-string 1 name) (match-string 2 name) (match-string 3 name))))) + +(defmacro deftest-make-overlay-1 (id args) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'make-overlay 1 id) () + (with-temp-buffer + (should ,(cons 'make-overlay args))))) + +(defmacro deftest-make-overlay-2 (id args condition) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'make-overlay 2 id) () + (with-temp-buffer + (should-error + ,(cons 'make-overlay args) + :type ',condition + :exclude-subtypes t)))) + +(defmacro deftest-overlay-start/end-1 (id start-end-args start-end-should) + (declare (indent 1)) + (cl-destructuring-bind (start end sstart send) + (append start-end-args start-end-should) + `(ert-deftest ,(make-overlay-test-name 'overlay-start/end 1 id) () + (with-temp-buffer + (insert (make-string 9 ?\n)) + (let ((ov (make-overlay ,start ,end))) + (should (equal ,sstart (overlay-start ov))) + (should (equal ,send (overlay-end ov)))))))) + +(defmacro deftest-overlay-buffer-1 (id arg-expr should-expr) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'overlay-buffer 1 id) () + (with-temp-buffer + (should (equal (overlay-buffer (make-overlay 1 1 ,arg-expr)) + ,should-expr))))) + +(defmacro deftest-overlayp-1 (id arg-expr should-expr) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'overlay-buffer 1 id) () + (with-temp-buffer + (should (equal ,should-expr (overlayp ,arg-expr)))))) + +(defmacro deftest-next-overlay-change-1 (id pos result &rest ov-tuple) + `(ert-deftest ,(make-overlay-test-name 'next-overlay-change 1 id) () + (let ((tuple (copy-sequence ',ov-tuple))) + (with-temp-buffer + (insert (make-string (max 100 (if tuple + (apply #'max + (mapcar + (lambda (m) (apply #'max m)) tuple)) + 0)) + ?\n)) + (dolist (tup tuple) + (make-overlay (car tup) (cadr tup))) + (should (equal (next-overlay-change ,pos) + ,result)))))) + +(defmacro deftest-previous-overlay-change-1 (id pos result &rest ov-tuple) + `(ert-deftest ,(make-overlay-test-name 'previous-overlay-change 1 id) () + (let ((tuple ',ov-tuple)) + (with-temp-buffer + (insert (make-string (max 100 (if tuple + (apply #'max + (mapcar + (lambda (m) (apply #'max m)) tuple)) + 0)) + ?\n)) + (dolist (tup tuple) + (make-overlay (car tup) (cadr tup))) + (should (equal (previous-overlay-change ,pos) + ,result)))))) + +(defmacro deftest-overlays-at-1 (id pos result &rest ov-triple) + `(ert-deftest ,(make-overlay-test-name 'overlays-at 1 id) () + (let ((pos* ,pos)) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (should-not (memq nil ',result)) + (dolist (v ',ov-triple) + (cl-destructuring-bind (tag start end) + v + (overlay-put (make-overlay start end) 'tag tag))) + (let ((ovl (overlays-at pos*))) + (should (equal (length ovl) (length ',result))) + (dolist (ov ovl) + (should (memq (overlay-get ov 'tag) ',result)))))))) + +(defmacro deftest-overlays-in-1 (id beg end result &rest ov-triple) + `(ert-deftest ,(make-overlay-test-name 'overlays-in 1 id) () + (let ((beg* ,beg) + (end* ,end)) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (should-not (memq nil ',result)) + (dolist (v ',ov-triple) + (cl-destructuring-bind (tag start end) + v + (overlay-put (make-overlay start end) 'tag tag))) + (let ((ovl (overlays-in beg* end*))) + (should (equal (length ovl) (length ',result))) + (dolist (ov ovl) + (should (memq (overlay-get ov 'tag) ',result)))))))) + +(defmacro test-with-overlay-in-buffer (symbol-beg-end-fa-ra &rest body) + (declare (indent 1)) + (cl-destructuring-bind (symbol beg end &optional fa ra) + symbol-beg-end-fa-ra + `(with-temp-buffer + (insert (make-string (max 1000 (1- ,end)) ?\s)) + (goto-char 1) + (let ((,symbol (make-overlay ,beg ,end nil ,fa ,ra))) + ,@body)))) + +(defmacro deftest-overlays-equal-1 (id result ov1-args ov2-args) + `(ert-deftest ,(make-overlay-test-name 'overlays-equal 1 id) () + (cl-labels ((create-overlay (args) + (cl-destructuring-bind (start end &optional fa ra &rest properties) + args + (let ((ov (make-overlay start end nil fa ra))) + (while properties + (overlay-put ov (pop properties) (pop properties))) + ov)))) + (with-temp-buffer + (insert (make-string 1024 ?\s)) + (should (,(if result 'identity 'not) + (equal (create-overlay ',ov1-args) + (create-overlay ',ov2-args)))))))) + + +(defun find-ert-overlay-test (name) + (let ((test (unmake-ov-test-name name))) + (or (and test + (cl-destructuring-bind (fn x y) + test + (let ((regexp (format "deftest-%s-%s +%s" fn x y))) + (re-search-forward regexp nil t)))) + (let ((find-function-regexp-alist + (cl-remove 'find-ert-overlay-test find-function-regexp-alist :key #'cdr))) + (find-function-do-it name 'ert-deftest 'switch-to-buffer-other-window))))) + +(add-to-list 'find-function-regexp-alist + '(ert-deftest . find-ert-overlay-test)) + + +;; +===================================================================================+ +;; | make-overlay +;; +===================================================================================+ + +;; Test if making an overlay succeeds. +(deftest-make-overlay-1 A (1 1)) +(deftest-make-overlay-1 B (7 26)) +(deftest-make-overlay-1 C (29 7)) +(deftest-make-overlay-1 D (most-positive-fixnum 1)) +(deftest-make-overlay-1 E (most-negative-fixnum 1)) +(deftest-make-overlay-1 F (1 most-positive-fixnum)) +(deftest-make-overlay-1 G (1 most-negative-fixnum)) +(deftest-make-overlay-1 H (1 1 nil t)) +(deftest-make-overlay-1 I (1 1 nil nil)) +(deftest-make-overlay-1 J (1 1 nil nil nil)) +(deftest-make-overlay-1 K (1 1 nil nil t)) +(deftest-make-overlay-1 L (1 1 nil t t)) +(deftest-make-overlay-1 M (1 1 nil "yes" "yes")) + +;; Test if trying to make an overlay signals conditions. +(deftest-make-overlay-2 A () wrong-number-of-arguments) +(deftest-make-overlay-2 B (1) wrong-number-of-arguments) +(deftest-make-overlay-2 C (1 2 3 4 5 6) wrong-number-of-arguments) +(deftest-make-overlay-2 D ("1") wrong-number-of-arguments) +(deftest-make-overlay-2 E ("1" "2") wrong-type-argument) +(deftest-make-overlay-2 F (1 2 "b") wrong-type-argument) +(deftest-make-overlay-2 G (1 2 3.14) wrong-type-argument) +(deftest-make-overlay-2 H (3.14 3) wrong-type-argument) +(deftest-make-overlay-2 I (1 [1]) wrong-type-argument) +(deftest-make-overlay-2 J (1 1 (with-temp-buffer + (current-buffer))) + error) + + +;; +===================================================================================+ +;; | overlay-start/end +;; +===================================================================================+ + +;; Test if the overlays return proper positions. point-max of the +;; buffer will equal 10. ARG RESULT +(deftest-overlay-start/end-1 A (1 1) (1 1)) +(deftest-overlay-start/end-1 B (2 7) (2 7)) +(deftest-overlay-start/end-1 C (7 2) (2 7)) +(deftest-overlay-start/end-1 D (1 10) (1 10)) +(deftest-overlay-start/end-1 E (1 11) (1 10)) +(deftest-overlay-start/end-1 F (1 most-positive-fixnum) (1 10)) +(deftest-overlay-start/end-1 G (most-positive-fixnum 1) (1 10)) +(deftest-overlay-start/end-1 H (most-positive-fixnum most-positive-fixnum) (10 10)) +(deftest-overlay-start/end-1 I (100 11) (10 10)) +(deftest-overlay-start/end-1 J (11 100) (10 10)) +(deftest-overlay-start/end-1 K (0 1) (1 1)) +(deftest-overlay-start/end-1 L (1 0) (1 1)) +(deftest-overlay-start/end-1 M (0 0) (1 1)) + +(ert-deftest test-overlay-start/end-2 () + (should-not (overlay-start (with-temp-buffer (make-overlay 1 1)))) + (should-not (overlay-end (with-temp-buffer (make-overlay 1 1))))) + + +;; +===================================================================================+ +;; | overlay-buffer +;; +===================================================================================+ + +;; Test if overlay-buffer returns appropriate values. +(deftest-overlay-buffer-1 A (current-buffer) (current-buffer)) +(deftest-overlay-buffer-1 B nil (current-buffer)) +(ert-deftest test-overlay-buffer-1-C () + (should-error (make-overlay + 1 1 (with-temp-buffer (current-buffer))))) + + +;; +===================================================================================+ +;; | overlayp +;; +===================================================================================+ + +;; Check the overlay predicate. +(deftest-overlayp-1 A (make-overlay 1 1) t) +(deftest-overlayp-1 B (with-temp-buffer (make-overlay 1 1)) t) +(deftest-overlayp-1 C nil nil) +(deftest-overlayp-1 D 'symbol nil) +(deftest-overlayp-1 E "string" nil) +(deftest-overlayp-1 F 42 nil) +(deftest-overlayp-1 G [1 2] nil) +(deftest-overlayp-1 H (symbol-function 'car) nil) +(deftest-overlayp-1 I float-pi nil) +(deftest-overlayp-1 J (cons 1 2) nil) +(deftest-overlayp-1 K (make-hash-table) nil) +(deftest-overlayp-1 L (symbol-function 'ert-deftest) nil) +(deftest-overlayp-1 M (current-buffer) nil) +(deftest-overlayp-1 N (selected-window) nil) +(deftest-overlayp-1 O (selected-frame) nil) + + +;; +===================================================================================+ +;; | overlay equality +;; +===================================================================================+ + +(deftest-overlays-equal-1 A t (1 1) (1 1)) +(deftest-overlays-equal-1 B t (5 10) (5 10)) +(deftest-overlays-equal-1 C nil (5 11) (5 10)) +(deftest-overlays-equal-1 D t (10 20 t) (10 20)) +(deftest-overlays-equal-1 E t (10 20 nil t) (10 20)) +(deftest-overlays-equal-1 F t (10 20 t t) (10 20 nil t)) +(deftest-overlays-equal-1 G t (10 20 t t) (10 20 t nil)) +(deftest-overlays-equal-1 H t (10 20 nil nil foo 42) (10 20 nil nil foo 42)) +(deftest-overlays-equal-1 I nil (10 20 nil nil foo 42) (10 20 nil nil foo 43)) + + +;; +===================================================================================+ +;; | overlay-lists +;; +===================================================================================+ + +;; Check whether overlay-lists returns something sensible. +(ert-deftest test-overlay-lists-1 () + (with-temp-buffer + (should (equal (cons nil nil) (overlay-lists))) + (dotimes (i 10) (make-overlay 1 i)) + (should (listp (car (overlay-lists)))) + (should (listp (cdr (overlay-lists)))) + (let ((list (append (car (overlay-lists)) + (cdr (overlay-lists))))) + (should (= 10 (length list))) + (should (seq-every-p #'overlayp list))))) + + +;; +===================================================================================+ +;; | overlay-put/get/properties +;; +===================================================================================+ + +;; Test if overlay-put properties can be retrieved by overlay-get and +;; overlay-properties. +(ert-deftest test-overlay-props-1 () + (with-temp-buffer + (let* ((keys '(:k1 :k2 :k3)) + (values '(1 "v2" v3)) + (ov (make-overlay 1 1)) + (n (length keys))) + (should (equal (length keys) (length values))) + (should (null (overlay-properties ov))) + ;; Insert keys and values. + (dotimes (i n) + (should (equal (overlay-put ov (nth i keys) (nth i values)) + (nth i values)))) + ;; Compare with what overlay-get says. + (dotimes (i n) + (should (equal (overlay-get ov (nth i keys)) + (nth i values)))) + ;; Test if overlay-properties is a superset. + (dotimes (i n) + (should (equal (plist-get (overlay-properties ov) + (nth i keys)) + (nth i values)))) + ;; Check if overlay-properties is a subset. + (should (= (length (overlay-properties ov)) (* n 2)))))) + + +;; +===================================================================================+ +;; | next-overlay-change +;; +===================================================================================+ + +;; Test if next-overlay-change returns RESULT if called with POS in a +;; buffer with overlays corresponding to OVS and point-max >= 100. +;; (POS RESULT &rest OVS) +;; 0 overlays +(deftest-next-overlay-change-1 A (point-min) (point-max)) +(deftest-next-overlay-change-1 B (point-max) (point-max)) +;; 1 non-empty overlay +(deftest-next-overlay-change-1 C 1 10 (10 20)) +(deftest-next-overlay-change-1 D 10 20 (10 20)) +(deftest-next-overlay-change-1 E 15 20 (10 20)) +(deftest-next-overlay-change-1 F 20 (point-max) (10 20)) +(deftest-next-overlay-change-1 G 30 (point-max) (10 20)) +;; 1 empty overlay +(deftest-next-overlay-change-1 H 1 10 (10 10)) +(deftest-next-overlay-change-1 I 10 (point-max) (10 10)) +(deftest-next-overlay-change-1 J 20 (point-max) (10 10)) +;; 2 non-empty, non-intersecting +(deftest-next-overlay-change-1 D 10 20 (20 30) (40 50)) +(deftest-next-overlay-change-1 E 35 40 (20 30) (40 50)) +(deftest-next-overlay-change-1 F 60 (point-max) (20 30) (40 50)) +(deftest-next-overlay-change-1 G 30 40 (20 30) (40 50)) +(deftest-next-overlay-change-1 H 50 (point-max) (20 30) (40 50)) +;; 2 non-empty, intersecting +(deftest-next-overlay-change-1 I 10 20 (20 30) (25 35)) +(deftest-next-overlay-change-1 J 20 25 (20 30) (25 35)) +(deftest-next-overlay-change-1 K 23 25 (20 30) (25 35)) +(deftest-next-overlay-change-1 L 25 30 (20 30) (25 35)) +(deftest-next-overlay-change-1 M 28 30 (20 30) (25 35)) +(deftest-next-overlay-change-1 N 30 35 (20 30) (25 35)) +(deftest-next-overlay-change-1 O 35 (point-max) (20 30) (25 35)) +(deftest-next-overlay-change-1 P 50 (point-max) (20 30) (25 35)) +;; 2 non-empty, continuous +(deftest-next-overlay-change-1 Q 10 20 (20 30) (30 40)) +(deftest-next-overlay-change-1 R 20 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 S 25 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 T 30 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 U 35 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 V 40 (point-max) (20 30) (30 40)) +(deftest-next-overlay-change-1 W 50 (point-max) (20 30) (30 40)) +;; 1 empty, 1 non-empty, non-in +(deftest-next-overlay-change-1 a 10 20 (20 20) (30 40)) +(deftest-next-overlay-change-1 b 20 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 c 25 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 d 30 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 e 35 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 f 40 (point-max) (20 30) (30 40)) +(deftest-next-overlay-change-1 g 50 (point-max) (20 30) (30 40)) +;; 1 empty, 1 non-empty, intersecting at begin +(deftest-next-overlay-change-1 h 10 20 (20 20) (20 30)) +(deftest-next-overlay-change-1 i 20 30 (20 20) (20 30)) +(deftest-next-overlay-change-1 j 25 30 (20 20) (20 30)) +(deftest-next-overlay-change-1 k 30 (point-max) (20 20) (20 30)) +(deftest-next-overlay-change-1 l 40 (point-max) (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting at end +(deftest-next-overlay-change-1 h 10 20 (30 30) (20 30)) +(deftest-next-overlay-change-1 i 20 30 (30 30) (20 30)) +(deftest-next-overlay-change-1 j 25 30 (30 30) (20 30)) +(deftest-next-overlay-change-1 k 30 (point-max) (20 20) (20 30)) +(deftest-next-overlay-change-1 l 40 (point-max) (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting in the middle +(deftest-next-overlay-change-1 m 10 20 (25 25) (20 30)) +(deftest-next-overlay-change-1 n 20 25 (25 25) (20 30)) +(deftest-next-overlay-change-1 o 25 30 (25 25) (20 30)) +(deftest-next-overlay-change-1 p 30 (point-max) (25 25) (20 30)) +(deftest-next-overlay-change-1 q 40 (point-max) (25 25) (20 30)) +;; 2 empty, intersecting +(deftest-next-overlay-change-1 r 10 20 (20 20) (20 20)) +(deftest-next-overlay-change-1 s 20 (point-max) (20 20) (20 20)) +(deftest-next-overlay-change-1 t 30 (point-max) (20 20) (20 20)) +;; 2 empty, non-intersecting +(deftest-next-overlay-change-1 u 10 20 (20 20) (30 30)) +(deftest-next-overlay-change-1 v 20 30 (20 20) (30 30)) +(deftest-next-overlay-change-1 w 25 30 (20 20) (30 30)) +(deftest-next-overlay-change-1 x 30 (point-max) (20 20) (30 30)) +(deftest-next-overlay-change-1 y 50 (point-max) (20 20) (30 30)) +;; 10 random +(deftest-next-overlay-change-1 aa 1 5 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-next-overlay-change-1 ab (point-max) (point-max) + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-next-overlay-change-1 ac 67 88 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) + + +;; +===================================================================================+ +;; | previous-overlay-change. +;; +===================================================================================+ + +;; Same for previous-overlay-change. +;; 1 non-empty overlay +(deftest-previous-overlay-change-1 A (point-max) 1) +(deftest-previous-overlay-change-1 B 1 1) +(deftest-previous-overlay-change-1 C 1 1 (10 20)) +(deftest-previous-overlay-change-1 D 10 1 (10 20)) +(deftest-previous-overlay-change-1 E 15 10 (10 20)) +(deftest-previous-overlay-change-1 F 20 10 (10 20)) +(deftest-previous-overlay-change-1 G 30 20 (10 20)) +;; 1 empty overlay +(deftest-previous-overlay-change-1 H 1 1 (10 10)) +(deftest-previous-overlay-change-1 I 10 1 (10 10)) +(deftest-previous-overlay-change-1 J 20 10 (10 10)) +;; 2 non-empty, non-intersecting +(deftest-previous-overlay-change-1 D 10 1 (20 30) (40 50)) +(deftest-previous-overlay-change-1 E 35 30 (20 30) (40 50)) +(deftest-previous-overlay-change-1 F 60 50 (20 30) (40 50)) +(deftest-previous-overlay-change-1 G 30 20 (20 30) (40 50)) +(deftest-previous-overlay-change-1 H 50 40 (20 30) (40 50)) +;; 2 non-empty, intersecting +(deftest-previous-overlay-change-1 I 10 1 (20 30) (25 35)) +(deftest-previous-overlay-change-1 J 20 1 (20 30) (25 35)) +(deftest-previous-overlay-change-1 K 23 20 (20 30) (25 35)) +(deftest-previous-overlay-change-1 L 25 20 (20 30) (25 35)) +(deftest-previous-overlay-change-1 M 28 25 (20 30) (25 35)) +(deftest-previous-overlay-change-1 N 30 25 (20 30) (25 35)) +(deftest-previous-overlay-change-1 O 35 30 (20 30) (25 35)) +(deftest-previous-overlay-change-1 P 50 35 (20 30) (25 35)) +;; 2 non-empty, continuous +(deftest-previous-overlay-change-1 Q 10 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 R 20 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 S 25 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 T 30 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 U 35 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 V 40 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 W 50 40 (20 30) (30 40)) +;; 1 empty, 1 non-empty, non-intersecting +(deftest-previous-overlay-change-1 a 10 1 (20 20) (30 40)) +(deftest-previous-overlay-change-1 b 20 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 c 25 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 d 30 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 e 35 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 f 40 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 g 50 40 (20 30) (30 40)) +;; 1 empty, 1 non-empty, intersecting at begin +(deftest-previous-overlay-change-1 h 10 1 (20 20) (20 30)) +(deftest-previous-overlay-change-1 i 20 1 (20 20) (20 30)) +(deftest-previous-overlay-change-1 j 25 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 k 30 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 l 40 30 (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting at end +(deftest-previous-overlay-change-1 m 10 1 (30 30) (20 30)) +(deftest-previous-overlay-change-1 n 20 1 (30 30) (20 30)) +(deftest-previous-overlay-change-1 o 25 20 (30 30) (20 30)) +(deftest-previous-overlay-change-1 p 30 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 q 40 30 (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersectig in the middle +(deftest-previous-overlay-change-1 r 10 1 (25 25) (20 30)) +(deftest-previous-overlay-change-1 s 20 1 (25 25) (20 30)) +(deftest-previous-overlay-change-1 t 25 20 (25 25) (20 30)) +(deftest-previous-overlay-change-1 u 30 25 (25 25) (20 30)) +(deftest-previous-overlay-change-1 v 40 30 (25 25) (20 30)) +;; 2 empty, intersecting +(deftest-previous-overlay-change-1 w 10 1 (20 20) (20 20)) +(deftest-previous-overlay-change-1 x 20 1 (20 20) (20 20)) +(deftest-previous-overlay-change-1 y 30 20 (20 20) (20 20)) +;; 2 empty, non-intersecting +(deftest-previous-overlay-change-1 z 10 1 (20 20) (30 30)) +(deftest-previous-overlay-change-1 aa 20 1 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ab 25 20 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ac 30 20 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ad 50 30 (20 20) (30 30)) +;; 10 random +(deftest-previous-overlay-change-1 ae 100 90 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-previous-overlay-change-1 af (point-min) (point-min) + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-previous-overlay-change-1 ag 29 28 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) + + +;; +===================================================================================+ +;; | overlays-at +;; +===================================================================================+ + + +;; Test whether overlay-at returns RESULT at POS after overlays OVL were +;; created in a buffer. POS RES OVL +(deftest-overlays-at-1 A 1 ()) +;; 1 overlay +(deftest-overlays-at-1 B 10 (a) (a 10 20)) +(deftest-overlays-at-1 C 15 (a) (a 10 20)) +(deftest-overlays-at-1 D 19 (a) (a 10 20)) +(deftest-overlays-at-1 E 20 () (a 10 20)) +(deftest-overlays-at-1 F 1 () (a 10 20)) + +;; 2 non-empty overlays non-intersecting +(deftest-overlays-at-1 G 1 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 H 10 (a) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 I 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 K 20 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 L 25 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 M 30 (b) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 N 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 O 40 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 P 50 () (a 10 20) (b 30 40)) + +;; 2 non-empty overlays intersecting +(deftest-overlays-at-1 G 1 () (a 10 30) (b 20 40)) +(deftest-overlays-at-1 H 10 (a) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 I 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 K 20 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 L 25 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 M 30 (b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 N 35 (b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 O 40 () (a 10 30) (b 20 40)) +(deftest-overlays-at-1 P 50 () (a 10 30) (b 20 40)) + +;; 2 non-empty overlays continuous +(deftest-overlays-at-1 G 1 () (a 10 20) (b 20 30)) +(deftest-overlays-at-1 H 10 (a) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 I 15 (a) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 K 20 (b) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 L 25 (b) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 M 30 () (a 10 20) (b 20 30)) + +;; overlays-at never returns empty overlays. +(deftest-overlays-at-1 N 1 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 O 20 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 P 30 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 Q 40 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 R 50 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 S 60 () (a 1 60) (c 1 1) (b 30 30) (d 50 50)) + +;; behaviour at point-min and point-max +(ert-deftest test-overlays-at-2 () + (cl-macrolet ((should-length (n list) + `(should (= ,n (length ,list))))) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay 1 (point-max)) + (make-overlay 10 10) + (make-overlay 20 20) + (make-overlay (point-max) (point-max)) + (should-length 1 (overlays-at 1)) + (should-length 1 (overlays-at 10)) + (should-length 1 (overlays-at 20)) + (should-length 0 (overlays-at (point-max))) + (narrow-to-region 10 20) + (should-length 1 (overlays-at (point-min))) + (should-length 1 (overlays-at 15)) + (should-length 1 (overlays-at (point-max)))))) + + +;; +===================================================================================+ +;; | overlay-in +;; +===================================================================================+ + + +;; Test whether overlays-in returns RES in BEG,END after overlays OVL were +;; created in a buffer. + +(deftest-overlays-in-1 A 1 (point-max) ());;POS RES OVL +;; 1 overlay +(deftest-overlays-in-1 B 1 10 () (a 10 20)) +(deftest-overlays-in-1 C 5 10 () (a 10 20)) +(deftest-overlays-in-1 D 5 15 (a) (a 10 20)) +(deftest-overlays-in-1 E 10 15 (a) (a 10 20)) +(deftest-overlays-in-1 F 10 20 (a) (a 10 20)) +(deftest-overlays-in-1 G 15 20 (a) (a 10 20)) +(deftest-overlays-in-1 H 15 25 (a) (a 10 20)) +(deftest-overlays-in-1 I 20 25 () (a 10 20)) +(deftest-overlays-in-1 J 30 50 () (a 10 20)) + +;; 2 non-empty overlays non-intersecting +(deftest-overlays-in-1 K 1 5 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 L 5 10 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 M 5 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 N 10 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 O 15 20 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 P 15 25 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Q 20 25 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 R 20 30 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 S 25 30 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 T 25 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 U 30 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 V 40 50 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 W 50 60 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 X 1 50 (a b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Y 10 40 (a b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Z 10 41 (a b) (a 10 20) (b 30 40)) + +;; 2 non-empty overlays intersecting +(deftest-overlays-in-1 a 1 5 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 b 5 10 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 c 5 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 d 10 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 e 10 20 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 f 15 20 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 g 20 30 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 h 20 40 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 i 25 30 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 j 30 30 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 k 30 35 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 l 35 40 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 m 40 45 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 n 41 45 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 o 50 60 () (a 10 30) (b 20 40)) + +;; 2 non-empty overlays continuous +(deftest-overlays-in-1 p 1 5 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 q 5 10 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 r 15 20 (a) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 s 15 25 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 t 20 25 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 u 25 30 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 v 29 35 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 w 30 35 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 x 35 50 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 y 1 50 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 z 15 50 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 aa 1 25 (a b) (a 10 20) (b 20 30)) + +;; 1 empty overlay +(deftest-overlays-in-1 ab 1 10 () (a 10 10)) +(deftest-overlays-in-1 ac 10 10 (a) (a 10 10)) +(deftest-overlays-in-1 ad 9 10 () (a 10 10)) +(deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) +(deftest-overlays-in-1 af 10 11 (a) (a 10 10)) + + +;; behaviour at point-max +(ert-deftest test-overlays-in-2 () + (cl-macrolet ((should-length (n list) + `(should (= ,n (length ,list))))) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay (point-max) (point-max)) + (make-overlay 50 50) + (should-length 1 (overlays-in 50 50)) + (should-length 2 (overlays-in 1 (point-max))) + (should-length 1 (overlays-in (point-max) (point-max))) + (narrow-to-region 1 50) + (should-length 0 (overlays-in 1 (point-max))) + (should-length 1 (overlays-in (point-max) (point-max)))))) + + +;; +===================================================================================+ +;; | overlay-recenter +;; +===================================================================================+ + +;; This function is a noop in the overlay tree branch. +(ert-deftest test-overlay-recenter () + (with-temp-buffer + (should-not (overlay-recenter 1)) + (insert (make-string 100 ?\s)) + (dotimes (i 10) + (make-overlay i (1+ i)) + (should-not (overlay-recenter i))))) + + +;; +===================================================================================+ +;; | move-overlay +;; +===================================================================================+ + +;; buffer nil with live overlay +(ert-deftest test-move-overlay-1 () + (test-with-overlay-in-buffer (ov 1 100) + (move-overlay ov 50 60) + (should (= 50 (overlay-start ov))) + (should (= 60 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov))))) + +;; buffer nil, dead overlay +(ert-deftest test-move-overlay-2 () + (with-temp-buffer + (let ((ov (test-with-overlay-in-buffer (ov 1 100) ov))) + (insert (make-string 100 ?\s)) + (move-overlay ov 50 60) + (should (= 50 (overlay-start ov))) + (should (= 60 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))))) + +;; buffer non-nil, live overlay +(ert-deftest test-move-overlay-3 () + (test-with-overlay-in-buffer (ov 10 100) + (with-temp-buffer + (move-overlay ov 1 1 (current-buffer)) + (should (= 1 (overlay-start ov))) + (should (= 1 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; buffer non-nil, dead overlay +(ert-deftest test-move-overlay-4 () + (let ((ov (test-with-overlay-in-buffer (ov 1 1) ov))) + (with-temp-buffer + (move-overlay ov 1 1 (current-buffer)) + (should (= 1 (overlay-start ov))) + (should (= 1 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; This used to fail. +(ert-deftest test-move-overlay-5 () + (skip-unless (fboundp 'overlay-tree)) + (with-temp-buffer + (insert (make-string 1 ?.)) + (let ((other (make-overlay 1 1))) + (make-overlay 1 1) + (insert "()") + (move-overlay other (point-max) (1+ (point-max)) (current-buffer)) + (delete-overlay other)) + (should (= (plist-get (car (with-no-warnings (overlay-tree))) :limit) + 1)))) + + +;; +===================================================================================+ +;; | delete-(all-)overlay +;; +===================================================================================+ + +;; delete live overlay +(ert-deftest test-delete-overlay-1 () + (test-with-overlay-in-buffer (ov 10 100) + (should (buffer-live-p (overlay-buffer ov))) + (delete-overlay ov) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; delete dead overlay +(ert-deftest test-delete-overlay-2 () + (let ((ov (test-with-overlay-in-buffer (ov 10 100) ov))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)) + (should-not (delete-overlay ov)) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +(ert-deftest test-delete-all-overlay-1 () + (with-temp-buffer + (should-not (delete-all-overlays)) + (should-not (delete-all-overlays (current-buffer))) + (insert (make-string 100 ?\s)) + (dotimes (i 10) (make-overlay i (1+ i))) + (should-not (delete-all-overlays (current-buffer))) + (should-not (delete-all-overlays)))) + + +;; +===================================================================================+ +;; | get-char-property(-and-overlay) +;; +===================================================================================+ + +;; FIXME: TBD + + +;; +===================================================================================+ +;; | Moving by insertions +;; +===================================================================================+ + +(defmacro deftest-moving-insert-1 (id beg-end insert sbeg-send fa ra) + (cl-destructuring-bind (beg end ipos ilen sbeg send fa ra) + (append beg-end insert sbeg-send (list fa ra) nil) + `(ert-deftest ,(make-overlay-test-name 'moving-insert 1 id) () + (test-with-overlay-in-buffer (ov ,beg ,end ,fa ,ra) + (should (= ,beg (overlay-start ov))) + (should (= ,end (overlay-end ov))) + (goto-char ,ipos) + (insert (make-string ,ilen ?x)) + (should (= ,sbeg (overlay-start ov))) + (should (= ,send (overlay-end ov))))))) + +;; non-empty, no fa, no ra +;; -------------------- OV INS RESULT +(deftest-moving-insert-1 A (10 20) (15 3) (10 23) nil nil) +(deftest-moving-insert-1 B (10 20) (20 4) (10 20) nil nil) +(deftest-moving-insert-1 C (10 20) (5 5) (15 25) nil nil) +(deftest-moving-insert-1 D (10 20) (10 3) (10 23) nil nil) +(deftest-moving-insert-1 E (10 20) (20 4) (10 20) nil nil) + +;; non-empty no fa, ra +(deftest-moving-insert-1 F (10 20) (15 3) (10 23) nil t) +(deftest-moving-insert-1 G (10 20) (20 4) (10 24) nil t) +(deftest-moving-insert-1 H (10 20) (5 5) (15 25) nil t) +(deftest-moving-insert-1 I (10 20) (10 3) (10 23) nil t) +(deftest-moving-insert-1 J (10 20) (20 4) (10 24) nil t) + +;; non-empty, fa, no r +(deftest-moving-insert-1 K (10 20) (15 3) (10 23) t nil) +(deftest-moving-insert-1 L (10 20) (20 4) (10 20) t nil) +(deftest-moving-insert-1 M (10 20) (5 5) (15 25) t nil) +(deftest-moving-insert-1 N (10 20) (10 3) (13 23) t nil) +(deftest-moving-insert-1 O (10 20) (20 4) (10 20) t nil) + +;; This used to fail. +(ert-deftest test-moving-insert-2-a () + (with-temp-buffer + (insert (make-string 1 ?.)) + (let ((ov (make-overlay 1 1 nil t nil))) + (insert "()") + (should (= 1 (overlay-end ov)))))) + +;; non-empty, fa, ra +(deftest-moving-insert-1 P (10 20) (15 3) (10 23) t t) +(deftest-moving-insert-1 Q (10 20) (20 4) (10 24) t t) +(deftest-moving-insert-1 R (10 20) (5 5) (15 25) t t) +(deftest-moving-insert-1 S (10 20) (10 3) (13 23) t t) +(deftest-moving-insert-1 T (10 20) (20 4) (10 24) t t) + +;; empty, no fa, no ra +(deftest-moving-insert-1 U (15 15) (20 4) (15 15) nil nil) +(deftest-moving-insert-1 V (15 15) (5 5) (20 20) nil nil) +(deftest-moving-insert-1 W (15 15) (15 3) (15 15) nil nil) + +;; empty no fa, ra +(deftest-moving-insert-1 X (15 15) (20 4) (15 15) nil t) +(deftest-moving-insert-1 Y (15 15) (5 5) (20 20) nil t) +(deftest-moving-insert-1 Z (15 15) (15 3) (15 18) nil t) + +;; empty, fa, no ra +(deftest-moving-insert-1 a (15 15) (20 4) (15 15) t nil) +(deftest-moving-insert-1 b (15 15) (5 5) (20 20) t nil) +(deftest-moving-insert-1 c (15 15) (15 3) (15 15) t nil) + +;; empty, fa, ra +(deftest-moving-insert-1 d (15 15) (20 4) (15 15) t t) +(deftest-moving-insert-1 e (15 15) (5 5) (20 20) t t) +(deftest-moving-insert-1 f (15 15) (15 3) (18 18) t t) + +;; Try to trigger a pathological case where the tree could become +;; unordered due to an insert operation. + +(ert-deftest test-moving-insert-2 () + (with-temp-buffer + (insert (make-string 1000 ?x)) + (let ((root (make-overlay 50 75 nil nil 'rear-advance)) + (left (make-overlay 25 50 nil 'front-advance 'rear-advance)) + (right (make-overlay 75 100 nil nil nil))) + ;; [50] <--- start + ;; / \ + ;; (25) (75) + (delete-region 25 75) + ;; [25] + ;; / \ + ;; (25) (25) + (should (= 25 (overlay-start root))) + (should (= 25 (overlay-end root))) + (should (= 25 (overlay-start left))) + (should (= 25 (overlay-end left))) + (should (= 25 (overlay-start right))) + (should (= 50 (overlay-end right))) + ;; Inserting at start should make left advance while right and + ;; root stay, thus we would have left > right . + (goto-char 25) + (insert (make-string 25 ?x)) + ;; [25] + ;; / \ + ;; (50) (25) + (should (= 25 (overlay-start root))) + (should (= 50 (overlay-end root))) + (should (= 50 (overlay-start left))) + (should (= 50 (overlay-end left))) + (should (= 25 (overlay-start right))) + (should (= 75 (overlay-end right))) + ;; Try to detect the error, by removing left. The should fail + ;; an eassert, since it won't be found by a reular tree + ;; traversal - in theory. + (delete-overlay left) + (should (= 2 (length (overlays-in 1 (point-max)))))))) + + + +;; +===================================================================================+ +;; | Moving by deletions +;; +===================================================================================+ + +(defmacro deftest-moving-delete-1 (id beg-end delete sbeg-send fa ra) + (cl-destructuring-bind (beg end dpos dlen sbeg send fa ra) + (append beg-end delete sbeg-send (list fa ra) nil) + `(ert-deftest ,(make-overlay-test-name 'moving-delete 1 id) () + (test-with-overlay-in-buffer (ov ,beg ,end ,fa ,ra) + (should (= ,beg (overlay-start ov))) + (should (= ,end (overlay-end ov))) + (delete-region ,dpos (+ ,dpos ,dlen)) + (should (= ,sbeg (overlay-start ov))) + (should (= ,send (overlay-end ov))))))) + +;; non-empty, no fa, no ra +;; -------------------- OV DEL RESULT +(deftest-moving-delete-1 A (10 20) (15 3) (10 17) nil nil) +(deftest-moving-delete-1 B (10 20) (20 4) (10 20) nil nil) +(deftest-moving-delete-1 C (10 20) (5 5) (5 15) nil nil) +(deftest-moving-delete-1 D (10 20) (10 3) (10 17) nil nil) +(deftest-moving-delete-1 E (10 20) (20 4) (10 20) nil nil) + +;; non-empty no fa, ra +(deftest-moving-delete-1 F (10 20) (15 3) (10 17) nil t) +(deftest-moving-delete-1 G (10 20) (20 4) (10 20) nil t) +(deftest-moving-delete-1 H (10 20) (5 5) (5 15) nil t) +(deftest-moving-delete-1 I (10 20) (10 3) (10 17) nil t) +(deftest-moving-delete-1 J (10 20) (20 4) (10 20) nil t) + +;; non-empty, fa, no ra +(deftest-moving-delete-1 K (10 20) (15 3) (10 17) t nil) +(deftest-moving-delete-1 L (10 20) (20 4) (10 20) t nil) +(deftest-moving-delete-1 M (10 20) (5 5) (5 15) t nil) +(deftest-moving-delete-1 N (10 20) (10 3) (10 17) t nil) +(deftest-moving-delete-1 O (10 20) (20 4) (10 20) t nil) + +;; non-empty, fa, ra +(deftest-moving-delete-1 P (10 20) (15 3) (10 17) t t) +(deftest-moving-delete-1 Q (10 20) (20 4) (10 20) t t) +(deftest-moving-delete-1 R (10 20) (5 5) (5 15) t t) +(deftest-moving-delete-1 S (10 20) (10 3) (10 17) t t) +(deftest-moving-delete-1 T (10 20) (20 4) (10 20) t t) + +;; empty, no fa, no ra +(deftest-moving-delete-1 U (15 15) (20 4) (15 15) nil nil) +(deftest-moving-delete-1 V (15 15) (5 5) (10 10) nil nil) +(deftest-moving-delete-1 W (15 15) (15 3) (15 15) nil nil) + +;; empty no fa, ra +(deftest-moving-delete-1 X (15 15) (20 4) (15 15) nil t) +(deftest-moving-delete-1 Y (15 15) (5 5) (10 10) nil t) +(deftest-moving-delete-1 Z (15 15) (15 3) (15 15) nil t) + +;; empty, fa, no ra +(deftest-moving-delete-1 a (15 15) (20 4) (15 15) t nil) +(deftest-moving-delete-1 b (15 15) (5 5) (10 10) t nil) +(deftest-moving-delete-1 c (15 15) (15 3) (15 15) t nil) + +;; empty, fa, ra +(deftest-moving-delete-1 d (15 15) (20 4) (15 15) t t) +(deftest-moving-delete-1 e (15 15) (5 5) (10 10) t t) +(deftest-moving-delete-1 f (15 15) (15 3) (15 15) t t) + + +;; +===================================================================================+ +;; | make-indirect-buffer +;; +===================================================================================+ + +;; Check if overlays are cloned/seperate from indirect buffer. +(ert-deftest test-make-indirect-buffer-1 () + (with-temp-buffer + (dotimes (_ 10) (make-overlay 1 1)) + (let (indirect clone) + (unwind-protect + (progn + (setq indirect (make-indirect-buffer + (current-buffer) "indirect")) + (with-current-buffer indirect + (should-not (overlays-in (point-min) (point-max))) + (dotimes (_ 20) (make-overlay 1 1)) + (should (= 20 (length (overlays-in (point-min) (point-max))))) + (delete-all-overlays) + (should-not (overlays-in (point-min) (point-max)))) + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (setq clone (make-indirect-buffer + (current-buffer) "clone" 'clone)) + (with-current-buffer clone + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (dotimes (_ 30) (make-overlay 1 1)) + (should (= 40 (length (overlays-in (point-min) (point-max)))))) + ;; back in temp buffer + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (with-current-buffer clone + (mapc #'delete-overlay + (seq-take (overlays-in (point-min) (point-max)) 10)) + (should (= 30 (length (overlays-in (point-min) (point-max)))))) + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (delete-all-overlays) + (with-current-buffer clone + (should (= 30 (length (overlays-in (point-min) (point-max))))))) + (when (buffer-live-p clone) + (kill-buffer clone)) + (when (buffer-live-p indirect) + (kill-buffer indirect)))))) + + + +;; +===================================================================================+ +;; | buffer-swap-text +;; +===================================================================================+ + +(defmacro test-with-temp-buffers (vars &rest body) + (declare (indent 1) (debug (sexp &rest form))) + (if (null vars) + `(progn ,@body) + `(with-temp-buffer + (let ((,(car vars) (current-buffer))) + (test-with-temp-buffers ,(cdr vars) ,@body))))) + +;; basic +(ert-deftest test-buffer-swap-text-1 () + (test-with-temp-buffers (buffer other) + (with-current-buffer buffer + (let ((ov (make-overlay 1 1))) + (buffer-swap-text other) + (should-not (overlays-in 1 1)) + (with-current-buffer other + (should (overlays-in 1 1)) + (should (eq ov (car (overlays-in 1 1))))))))) + +;; properties +(ert-deftest test-buffer-swap-text-1 () + (test-with-temp-buffers (buffer other) + (with-current-buffer other + (overlay-put (make-overlay 1 1) 'buffer 'other)) + (with-current-buffer buffer + (overlay-put (make-overlay 1 1) 'buffer 'buffer) + (buffer-swap-text other) + (should (= 1 (length (overlays-in 1 1)))) + (should (eq (overlay-get (car (overlays-in 1 1)) 'buffer) 'other))) + (with-current-buffer other + (should (= 1 (length (overlays-in 1 1)))) + (should (eq (overlay-get (car (overlays-in 1 1)) 'buffer) 'buffer))))) + + +;; +===================================================================================+ +;; | priorities +;; +===================================================================================+ + +(ert-deftest test-overlay-priorities-1 () + (with-temp-buffer + (insert " ") + (dotimes (i 10) + (let ((ov (make-overlay 1 2))) + (overlay-put ov 'priority i) + (overlay-put ov 'value i))) + (should (eq 9 (get-char-property 1 'value))))) + +(ert-deftest test-overlay-priorities-2 () + (with-temp-buffer + (insert " ") + (dotimes (j 10) + (let* ((i (- 9 j)) + (ov (make-overlay 1 2))) + (overlay-put ov 'priority i) + (overlay-put ov 'value i))) + (should (eq 9 (get-char-property 1 'value))))) + + +;; +===================================================================================+ +;; | Other +;; +===================================================================================+ + +(defun test-overlay-regions () + (sort (mapcar (lambda (ov) + (cons (overlay-start ov) + (overlay-end ov))) + (overlays-in (point-min) + (point-max))) + (lambda (o1 o2) + (or (< (car o1) (car o2)) + (and (= (car o1) (car o2)) + (< (cdr o1) (cdr o2))))))) + +;; This test used to fail. +(ert-deftest overlay-complex-delete-with-offset () + (with-temp-buffer + (let (todelete) + (insert (make-string 1000 ?\s)) + (make-overlay 1 2 nil t nil) + (make-overlay 2 3 nil t nil) + (make-overlay 3 4 nil t nil) + (make-overlay 4 5 nil t nil) + (setq todelete (make-overlay 280 287 nil t nil)) + (make-overlay 265 275 nil t nil) + (make-overlay 329 386 nil t nil) + (make-overlay 386 390 nil t nil) + (goto-char 50) + (delete-char 50) + (goto-char 1) + (delete-char 2) + (delete-overlay todelete) + (should (equal (test-overlay-regions) + '((1 . 1) (1 . 1) (1 . 2) (2 . 3) (213 . 223) (277 . 334) (334 . 338))))))) + +;; This test used to fail. +(ert-deftest overlay-complex-insert-1 () + (with-temp-buffer + (insert " ") + (make-overlay 8 11 nil nil t) + (make-overlay 2 7 nil nil nil) + (make-overlay 2 4 nil t nil) + (goto-char 1) + (insert " ") + (should (equal (test-overlay-regions) + '((7 . 9) + (7 . 12) + (13 . 16)))))) + +;; This test used to fail. +(ert-deftest overlay-complex-insert-2 () + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay 77 7 nil nil t) + (make-overlay 21 53 nil t t) + (make-overlay 84 14 nil nil nil) + (make-overlay 38 69 nil t nil) + (make-overlay 93 15 nil nil t) + (make-overlay 73 48 nil t t) + (make-overlay 96 51 nil t t) + (make-overlay 6 43 nil t t) + (make-overlay 15 100 nil t t) + (make-overlay 22 17 nil nil nil) + (make-overlay 72 45 nil t nil) + (make-overlay 2 74 nil nil t) + (make-overlay 15 29 nil t t) + (make-overlay 17 34 nil t t) + (make-overlay 101 66 nil t nil) + (make-overlay 94 24 nil nil nil) + (goto-char 78) + (insert " ") + (narrow-to-region 47 19) + (goto-char 46) + (widen) + (narrow-to-region 13 3) + (goto-char 9) + (delete-char 0) + (goto-char 11) + (insert " ") + (goto-char 3) + (insert " ") + (goto-char 8) + (insert " ") + (goto-char 26) + (insert " ") + (goto-char 14) + (widen) + (narrow-to-region 71 35) + (should + (equal (test-overlay-regions) + '((2 . 104) + (23 . 73) + (24 . 107) + (44 . 125) + (45 . 59) + (45 . 134) + (45 . 141) + (47 . 52) + (47 . 64) + (51 . 83) + (54 . 135) + (68 . 99)))))) + + +;; +===================================================================================+ +;; | Autogenerated insert/delete/narrow tests +;; +===================================================================================+ + + +;; (defun test-overlay-generate-test (name) +;; (interactive) +;; (with-temp-buffer +;; (let ((forms nil) +;; (buffer-size 64) +;; (noverlays 16) +;; (nforms 32) +;; (dist '(0.5 0.4 0.1))) +;; (cl-labels ((brand () +;; (+ (point-min) +;; (random (1+ (- (point-max) (point-min))))))) +;; (cl-macrolet ((push-eval (form) +;; `(cl-destructuring-bind (&rest args) +;; (list ,@(cdr form)) +;; (push (cons ',(car form) args) forms) +;; (apply #',(car form) args)))) +;; (push-eval (insert (make-string buffer-size ?.))) +;; (dotimes (_ noverlays) +;; (push-eval (make-overlay (brand) (brand) +;; nil +;; (= 0 (random 2)) +;; (= 0 (random 2))))) +;; (dotimes (_ nforms) +;; (push-eval (goto-char (brand))) +;; (pcase (/ (random 100) 100.0) +;; ((and x (guard (< x (nth 0 dist)))) +;; (push-eval (insert (make-string (random 16) ?.)))) +;; ((and x (guard (< x (+ (nth 0 dist) (nth 1 dist))))) +;; (push-eval (delete-char (random (1+ (- (point-max) (point))))))) +;; (_ +;; (push-eval (widen)) +;; (push-eval (narrow-to-region (brand) (brand)))))) +;; `(ert-deftest ,name () +;; (with-temp-buffer +;; ,@(nreverse forms) +;; (should (equal (test-overlay-regions) +;; ',(test-overlay-regions)))))))))) + +;; (defun test-overlay-generate-tests (n) +;; (let ((namefmt "overlay-autogenerated-test-%d") +;; (standard-output (current-buffer)) +;; (print-length nil) +;; (print-level nil) +;; (print-quoted t)) +;; (dotimes (i n) +;; (pp (test-overlay-generate-test (intern (format namefmt i)))) +;; (terpri)))) + +;; (progn (random "4711") (test-overlay-generate-tests 64)) + +(ert-deftest overlay-autogenerated-test-0 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 63 7 nil t t) + (make-overlay 47 9 nil nil nil) + (make-overlay 50 43 nil nil nil) + (make-overlay 20 53 nil nil t) + (make-overlay 62 4 nil nil t) + (make-overlay 40 27 nil t t) + (make-overlay 58 44 nil t t) + (make-overlay 46 38 nil nil nil) + (make-overlay 51 28 nil t nil) + (make-overlay 12 53 nil t t) + (make-overlay 52 60 nil nil nil) + (make-overlay 13 47 nil nil nil) + (make-overlay 16 31 nil nil nil) + (make-overlay 9 48 nil t t) + (make-overlay 43 29 nil nil t) + (make-overlay 48 13 nil t nil) + (goto-char 44) + (delete-char 15) + (goto-char 19) + (widen) + (narrow-to-region 20 8) + (goto-char 9) + (delete-char 3) + (goto-char 16) + (insert "..............") + (goto-char 12) + (delete-char 15) + (goto-char 12) + (delete-char 4) + (goto-char 12) + (delete-char 0) + (goto-char 12) + (insert "......") + (goto-char 13) + (delete-char 5) + (goto-char 8) + (insert "...") + (goto-char 10) + (insert ".............") + (goto-char 14) + (insert ".......") + (goto-char 25) + (delete-char 4) + (goto-char 26) + (insert "...............") + (goto-char 27) + (insert "...") + (goto-char 29) + (delete-char 7) + (goto-char 24) + (insert "...") + (goto-char 30) + (insert "..........") + (goto-char 29) + (widen) + (narrow-to-region 34 41) + (goto-char 40) + (delete-char 0) + (goto-char 35) + (delete-char 4) + (goto-char 36) + (widen) + (narrow-to-region 80 66) + (goto-char 74) + (delete-char 5) + (goto-char 69) + (delete-char 5) + (goto-char 70) + (widen) + (narrow-to-region 50 71) + (goto-char 66) + (insert "...............") + (goto-char 54) + (insert "...............") + (goto-char 84) + (insert "....") + (goto-char 72) + (insert "...........") + (goto-char 84) + (insert "..........") + (goto-char 102) + (insert "") + (goto-char 80) + (delete-char 25) + (should + (equal + (test-overlay-regions) + '((4 . 99) + (7 . 100) + (48 . 99) + (48 . 99) + (48 . 99) + (49 . 99) + (49 . 99) + (51 . 80) + (51 . 99) + (80 . 99) + (80 . 99) + (80 . 99) + (99 . 99) + (99 . 99) + (99 . 99) + (99 . 99)))))) + +(ert-deftest overlay-autogenerated-test-1 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 17 27 nil nil nil) + (make-overlay 13 28 nil nil t) + (make-overlay 8 56 nil nil nil) + (make-overlay 34 64 nil nil nil) + (make-overlay 51 4 nil t t) + (make-overlay 1 19 nil nil nil) + (make-overlay 53 59 nil nil t) + (make-overlay 25 13 nil nil nil) + (make-overlay 19 28 nil t nil) + (make-overlay 33 23 nil t nil) + (make-overlay 10 46 nil t t) + (make-overlay 18 39 nil nil nil) + (make-overlay 1 49 nil t nil) + (make-overlay 57 21 nil t t) + (make-overlay 10 58 nil t t) + (make-overlay 39 49 nil nil t) + (goto-char 37) + (delete-char 9) + (goto-char 3) + (insert "......") + (goto-char 38) + (delete-char 14) + (goto-char 18) + (insert "..........") + (goto-char 53) + (insert "....") + (goto-char 49) + (delete-char 10) + (goto-char 11) + (delete-char 12) + (goto-char 17) + (delete-char 22) + (goto-char 8) + (insert ".") + (goto-char 16) + (insert "........") + (goto-char 16) + (delete-char 5) + (goto-char 11) + (delete-char 0) + (goto-char 22) + (insert ".......") + (goto-char 18) + (delete-char 11) + (goto-char 16) + (delete-char 0) + (goto-char 9) + (insert "...........") + (goto-char 7) + (insert "...............") + (goto-char 2) + (insert ".......") + (goto-char 21) + (delete-char 11) + (goto-char 13) + (insert "..............") + (goto-char 17) + (delete-char 3) + (goto-char 21) + (insert "......") + (goto-char 15) + (delete-char 32) + (goto-char 10) + (insert "........") + (goto-char 25) + (widen) + (narrow-to-region 15 20) + (goto-char 17) + (insert ".............") + (goto-char 22) + (insert "............") + (goto-char 21) + (delete-char 8) + (goto-char 36) + (delete-char 1) + (goto-char 32) + (delete-char 2) + (goto-char 21) + (insert ".....") + (goto-char 31) + (insert "......") + (should + (equal + (test-overlay-regions) + '((1 . 58) + (1 . 58)))))) + +(ert-deftest overlay-autogenerated-test-2 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 59 nil t t) + (make-overlay 56 16 nil nil nil) + (make-overlay 65 51 nil t nil) + (make-overlay 14 24 nil t nil) + (make-overlay 28 9 nil t nil) + (make-overlay 58 50 nil t t) + (make-overlay 13 32 nil t t) + (make-overlay 12 21 nil t nil) + (make-overlay 60 23 nil t nil) + (make-overlay 39 38 nil nil t) + (make-overlay 15 64 nil t nil) + (make-overlay 17 21 nil nil t) + (make-overlay 46 23 nil t t) + (make-overlay 19 40 nil t nil) + (make-overlay 13 48 nil nil t) + (make-overlay 35 11 nil t nil) + (goto-char 41) + (delete-char 19) + (goto-char 45) + (insert "......") + (goto-char 3) + (delete-char 32) + (goto-char 19) + (insert "") + (goto-char 16) + (insert "...............") + (goto-char 2) + (insert "") + (goto-char 30) + (delete-char 0) + (goto-char 18) + (delete-char 17) + (goto-char 2) + (insert "...............") + (goto-char 12) + (insert "...") + (goto-char 2) + (insert ".............") + (goto-char 16) + (insert ".......") + (goto-char 15) + (insert ".......") + (goto-char 43) + (insert "......") + (goto-char 22) + (insert ".........") + (goto-char 25) + (delete-char 1) + (goto-char 38) + (insert "...............") + (goto-char 76) + (delete-char 3) + (goto-char 12) + (delete-char 5) + (goto-char 70) + (delete-char 9) + (goto-char 36) + (delete-char 4) + (goto-char 18) + (insert "...............") + (goto-char 52) + (delete-char 14) + (goto-char 23) + (insert "..........") + (goto-char 64) + (insert "...........") + (goto-char 68) + (delete-char 21) + (goto-char 71) + (insert "........") + (goto-char 28) + (delete-char 43) + (goto-char 25) + (insert "....") + (goto-char 2) + (insert "...............") + (goto-char 40) + (insert "....") + (goto-char 56) + (delete-char 2) + (should + (equal + (test-overlay-regions) + '((51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 58)))))) + +(ert-deftest overlay-autogenerated-test-3 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 53 38 nil t nil) + (make-overlay 17 40 nil t t) + (make-overlay 64 26 nil t t) + (make-overlay 48 24 nil t nil) + (make-overlay 21 18 nil nil nil) + (make-overlay 2 20 nil nil t) + (make-overlay 43 26 nil t t) + (make-overlay 56 28 nil t nil) + (make-overlay 19 51 nil nil nil) + (make-overlay 39 61 nil t nil) + (make-overlay 59 12 nil t nil) + (make-overlay 65 7 nil t nil) + (make-overlay 41 7 nil t nil) + (make-overlay 62 50 nil t nil) + (make-overlay 7 10 nil t t) + (make-overlay 45 28 nil t nil) + (goto-char 13) + (insert "...") + (goto-char 37) + (widen) + (narrow-to-region 2 10) + (goto-char 8) + (delete-char 1) + (goto-char 3) + (delete-char 6) + (goto-char 2) + (insert "...........") + (goto-char 5) + (widen) + (narrow-to-region 55 70) + (goto-char 55) + (insert "......") + (goto-char 64) + (delete-char 12) + (goto-char 61) + (insert ".....") + (goto-char 64) + (insert "..............") + (goto-char 72) + (delete-char 6) + (goto-char 63) + (delete-char 12) + (goto-char 63) + (delete-char 2) + (goto-char 57) + (insert "..............") + (goto-char 68) + (insert "........") + (goto-char 77) + (delete-char 6) + (goto-char 77) + (insert ".............") + (goto-char 67) + (delete-char 0) + (goto-char 84) + (insert "........") + (goto-char 74) + (delete-char 12) + (goto-char 78) + (insert "...") + (goto-char 80) + (insert "............") + (goto-char 69) + (insert "......") + (goto-char 89) + (insert ".") + (goto-char 56) + (insert "....") + (goto-char 100) + (insert ".............") + (goto-char 114) + (delete-char 0) + (goto-char 61) + (widen) + (narrow-to-region 94 50) + (goto-char 55) + (insert "............") + (goto-char 53) + (insert ".............") + (goto-char 116) + (delete-char 3) + (goto-char 81) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((14 . 166) + (16 . 164) + (26 . 164) + (31 . 68) + (33 . 165) + (35 . 52) + (35 . 164) + (45 . 164) + (46 . 164)))))) + +(ert-deftest overlay-autogenerated-test-4 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 25 15 nil nil t) + (make-overlay 8 13 nil nil nil) + (make-overlay 45 49 nil t t) + (make-overlay 22 13 nil t t) + (make-overlay 34 17 nil nil t) + (make-overlay 42 15 nil nil t) + (make-overlay 43 28 nil t t) + (make-overlay 3 28 nil t nil) + (make-overlay 32 61 nil nil t) + (make-overlay 30 64 nil t t) + (make-overlay 21 39 nil nil t) + (make-overlay 32 62 nil t nil) + (make-overlay 25 29 nil t nil) + (make-overlay 34 43 nil t nil) + (make-overlay 9 11 nil t nil) + (make-overlay 21 65 nil nil t) + (goto-char 21) + (delete-char 4) + (goto-char 25) + (insert "..") + (goto-char 53) + (insert "..") + (goto-char 2) + (insert "...............") + (goto-char 42) + (delete-char 36) + (goto-char 23) + (delete-char 12) + (goto-char 22) + (widen) + (narrow-to-region 30 32) + (goto-char 30) + (delete-char 0) + (goto-char 31) + (delete-char 1) + (goto-char 31) + (widen) + (narrow-to-region 28 27) + (goto-char 27) + (delete-char 1) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (insert ".") + (goto-char 28) + (insert "......") + (goto-char 34) + (delete-char 0) + (goto-char 27) + (delete-char 5) + (goto-char 27) + (delete-char 1) + (goto-char 27) + (insert ".............") + (goto-char 30) + (insert "..............") + (goto-char 37) + (delete-char 15) + (goto-char 32) + (delete-char 2) + (goto-char 36) + (delete-char 1) + (goto-char 34) + (delete-char 0) + (goto-char 34) + (delete-char 1) + (goto-char 32) + (widen) + (narrow-to-region 24 19) + (goto-char 21) + (delete-char 1) + (goto-char 21) + (widen) + (narrow-to-region 11 38) + (goto-char 27) + (widen) + (narrow-to-region 20 22) + (goto-char 20) + (delete-char 1) + (goto-char 20) + (widen) + (narrow-to-region 36 4) + (goto-char 26) + (delete-char 9) + (should + (equal + (test-overlay-regions) + '((18 . 25) + (21 . 21) + (21 . 21) + (21 . 22) + (21 . 22) + (21 . 27) + (21 . 27) + (22 . 25) + (22 . 27) + (22 . 28) + (26 . 27)))))) + +(ert-deftest overlay-autogenerated-test-5 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 64 1 nil nil nil) + (make-overlay 38 43 nil nil nil) + (make-overlay 42 19 nil t nil) + (make-overlay 22 12 nil nil nil) + (make-overlay 12 30 nil t t) + (make-overlay 38 46 nil nil nil) + (make-overlay 18 23 nil nil nil) + (make-overlay 58 65 nil nil t) + (make-overlay 52 41 nil nil nil) + (make-overlay 12 26 nil nil nil) + (make-overlay 39 4 nil nil nil) + (make-overlay 20 1 nil nil t) + (make-overlay 36 60 nil nil nil) + (make-overlay 24 18 nil t nil) + (make-overlay 9 50 nil nil nil) + (make-overlay 19 17 nil t nil) + (goto-char 40) + (insert "") + (goto-char 64) + (insert ".............") + (goto-char 32) + (delete-char 40) + (goto-char 25) + (insert "...") + (goto-char 31) + (delete-char 1) + (goto-char 8) + (delete-char 14) + (goto-char 20) + (delete-char 5) + (goto-char 20) + (insert "...........") + (goto-char 20) + (insert ".........") + (goto-char 17) + (widen) + (narrow-to-region 11 21) + (goto-char 14) + (widen) + (narrow-to-region 9 24) + (goto-char 24) + (insert ".............") + (goto-char 30) + (widen) + (narrow-to-region 47 45) + (goto-char 47) + (insert ".") + (goto-char 46) + (widen) + (narrow-to-region 30 42) + (goto-char 32) + (delete-char 0) + (goto-char 34) + (insert ".......") + (goto-char 42) + (delete-char 4) + (goto-char 39) + (delete-char 6) + (goto-char 31) + (delete-char 6) + (goto-char 31) + (insert "............") + (goto-char 30) + (insert "......") + (goto-char 50) + (delete-char 0) + (goto-char 30) + (insert "....") + (goto-char 53) + (insert "............") + (goto-char 41) + (delete-char 12) + (goto-char 52) + (insert ".......") + (goto-char 56) + (insert "...........") + (goto-char 68) + (insert ".......") + (goto-char 52) + (insert "......") + (goto-char 71) + (delete-char 10) + (goto-char 47) + (insert "") + (should + (equal + (test-overlay-regions) + '((20 . 89)))))) + +(ert-deftest overlay-autogenerated-test-6 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 28 59 nil nil nil) + (make-overlay 36 21 nil t t) + (make-overlay 60 19 nil t nil) + (make-overlay 26 30 nil t nil) + (make-overlay 47 27 nil nil t) + (make-overlay 8 25 nil t t) + (make-overlay 57 43 nil t t) + (make-overlay 28 61 nil nil t) + (make-overlay 42 31 nil nil t) + (make-overlay 15 44 nil t nil) + (make-overlay 56 38 nil nil nil) + (make-overlay 39 44 nil nil t) + (make-overlay 50 6 nil t nil) + (make-overlay 6 19 nil t nil) + (make-overlay 50 44 nil t t) + (make-overlay 34 60 nil nil t) + (goto-char 27) + (insert "...............") + (goto-char 23) + (insert "..............") + (goto-char 50) + (widen) + (narrow-to-region 53 67) + (goto-char 60) + (delete-char 0) + (goto-char 54) + (insert "......") + (goto-char 64) + (delete-char 1) + (goto-char 66) + (delete-char 3) + (goto-char 58) + (insert ".............") + (goto-char 58) + (insert ".........") + (goto-char 76) + (insert "...........") + (goto-char 57) + (insert "....") + (goto-char 106) + (widen) + (narrow-to-region 5 45) + (goto-char 31) + (delete-char 8) + (goto-char 36) + (insert "...") + (goto-char 6) + (insert "........") + (goto-char 33) + (insert ".............") + (goto-char 38) + (delete-char 3) + (goto-char 28) + (delete-char 6) + (goto-char 42) + (widen) + (narrow-to-region 17 25) + (goto-char 19) + (insert "..............") + (goto-char 37) + (delete-char 1) + (goto-char 22) + (delete-char 9) + (goto-char 28) + (insert "..............") + (goto-char 37) + (delete-char 3) + (goto-char 18) + (insert "...............") + (goto-char 30) + (widen) + (narrow-to-region 68 25) + (goto-char 38) + (delete-char 22) + (goto-char 43) + (widen) + (narrow-to-region 47 96) + (goto-char 86) + (insert ".") + (goto-char 63) + (insert "......") + (goto-char 78) + (widen) + (narrow-to-region 61 27) + (goto-char 43) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((14 . 38) + (14 . 132) + (16 . 43) + (38 . 118) + (38 . 126) + (38 . 142) + (44 . 115) + (45 . 129)))))) + +(ert-deftest overlay-autogenerated-test-7 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 13 50 nil t nil) + (make-overlay 28 44 nil nil t) + (make-overlay 56 27 nil t nil) + (make-overlay 8 34 nil nil nil) + (make-overlay 22 8 nil nil t) + (make-overlay 8 28 nil t nil) + (make-overlay 65 31 nil nil t) + (make-overlay 44 8 nil nil nil) + (make-overlay 52 64 nil nil t) + (make-overlay 52 27 nil t t) + (make-overlay 47 32 nil nil nil) + (make-overlay 18 62 nil nil nil) + (make-overlay 18 24 nil t t) + (make-overlay 33 46 nil nil t) + (make-overlay 20 8 nil t nil) + (make-overlay 51 51 nil t nil) + (goto-char 2) + (delete-char 46) + (goto-char 12) + (delete-char 5) + (goto-char 2) + (delete-char 12) + (goto-char 2) + (insert "..") + (goto-char 2) + (widen) + (narrow-to-region 2 4) + (goto-char 4) + (insert "......") + (goto-char 4) + (widen) + (narrow-to-region 4 6) + (goto-char 5) + (insert "") + (goto-char 6) + (insert "...............") + (goto-char 9) + (insert "...") + (goto-char 7) + (delete-char 13) + (goto-char 8) + (delete-char 1) + (goto-char 9) + (insert "...............") + (goto-char 24) + (delete-char 1) + (goto-char 15) + (insert "...............") + (goto-char 16) + (insert "............") + (goto-char 17) + (delete-char 8) + (goto-char 36) + (widen) + (narrow-to-region 47 38) + (goto-char 43) + (delete-char 0) + (goto-char 46) + (delete-char 0) + (goto-char 40) + (delete-char 4) + (goto-char 39) + (insert ".......") + (goto-char 50) + (delete-char 0) + (goto-char 47) + (insert "...........") + (goto-char 45) + (insert ".....") + (goto-char 38) + (delete-char 3) + (goto-char 59) + (delete-char 1) + (goto-char 42) + (insert "...............") + (goto-char 65) + (insert "...........") + (goto-char 73) + (delete-char 13) + (goto-char 72) + (insert "....") + (goto-char 47) + (insert "..") + (should + (equal + (test-overlay-regions) + '((2 . 81) + (2 . 81) + (2 . 81) + (2 . 81) + (2 . 81) + (81 . 81) + (81 . 81)))))) + +(ert-deftest overlay-autogenerated-test-8 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 6 nil t nil) + (make-overlay 48 13 nil t nil) + (make-overlay 58 65 nil nil t) + (make-overlay 63 65 nil nil nil) + (make-overlay 42 40 nil t t) + (make-overlay 40 6 nil nil t) + (make-overlay 37 46 nil t nil) + (make-overlay 4 14 nil nil nil) + (make-overlay 58 44 nil t t) + (make-overlay 14 16 nil nil t) + (make-overlay 31 61 nil t nil) + (make-overlay 34 3 nil nil nil) + (make-overlay 11 16 nil t nil) + (make-overlay 19 42 nil nil t) + (make-overlay 30 9 nil nil t) + (make-overlay 63 52 nil t t) + (goto-char 57) + (delete-char 2) + (goto-char 8) + (insert "........") + (goto-char 30) + (insert "...........") + (goto-char 35) + (insert "...........") + (goto-char 66) + (insert "...............") + (goto-char 53) + (delete-char 15) + (goto-char 75) + (delete-char 10) + (goto-char 62) + (delete-char 21) + (goto-char 52) + (delete-char 10) + (goto-char 10) + (insert "............") + (goto-char 42) + (insert "...........") + (goto-char 68) + (insert ".............") + (goto-char 12) + (insert "........") + (goto-char 1) + (insert "...............") + (goto-char 89) + (insert "") + (goto-char 94) + (insert ".............") + (goto-char 57) + (insert "...........") + (goto-char 130) + (insert "...") + (goto-char 69) + (insert "..") + (goto-char 101) + (insert "......") + (goto-char 128) + (delete-char 13) + (goto-char 19) + (delete-char 100) + (goto-char 22) + (insert "..") + (goto-char 13) + (widen) + (narrow-to-region 30 16) + (goto-char 19) + (insert "..........") + (goto-char 22) + (delete-char 3) + (goto-char 19) + (insert ".........") + (goto-char 17) + (insert "..") + (goto-char 16) + (insert "............") + (goto-char 47) + (insert ".") + (goto-char 50) + (insert "..........") + (goto-char 70) + (delete-char 1) + (should + (equal + (test-overlay-regions) + '((32 . 75) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 60) + (33 . 75) + (33 . 75) + (33 . 75) + (60 . 75)))))) + +(ert-deftest overlay-autogenerated-test-9 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 13 nil nil nil) + (make-overlay 29 4 nil nil t) + (make-overlay 3 53 nil nil nil) + (make-overlay 31 9 nil t t) + (make-overlay 48 30 nil nil nil) + (make-overlay 43 50 nil nil nil) + (make-overlay 7 27 nil nil t) + (make-overlay 30 59 nil nil nil) + (make-overlay 42 25 nil nil t) + (make-overlay 15 13 nil t t) + (make-overlay 39 11 nil t t) + (make-overlay 21 62 nil t t) + (make-overlay 35 2 nil t nil) + (make-overlay 60 53 nil nil t) + (make-overlay 64 8 nil nil t) + (make-overlay 58 59 nil t t) + (goto-char 28) + (insert ".............") + (goto-char 28) + (insert "...............") + (goto-char 71) + (insert ".......") + (goto-char 65) + (insert "......") + (goto-char 3) + (delete-char 12) + (goto-char 79) + (delete-char 11) + (goto-char 65) + (widen) + (narrow-to-region 12 53) + (goto-char 38) + (insert ".......") + (goto-char 20) + (insert ".........") + (goto-char 27) + (insert "...........") + (goto-char 75) + (insert "........") + (goto-char 85) + (insert "............") + (goto-char 52) + (insert "..........") + (goto-char 16) + (delete-char 8) + (goto-char 15) + (insert "...............") + (goto-char 112) + (insert "") + (goto-char 61) + (insert "..") + (goto-char 29) + (delete-char 34) + (goto-char 52) + (delete-char 32) + (goto-char 43) + (insert "........") + (goto-char 45) + (insert "..") + (goto-char 35) + (insert "...........") + (goto-char 29) + (insert ".......") + (goto-char 75) + (widen) + (narrow-to-region 69 55) + (goto-char 67) + (delete-char 2) + (goto-char 66) + (delete-char 0) + (goto-char 62) + (delete-char 1) + (goto-char 61) + (delete-char 3) + (goto-char 63) + (insert ".") + (goto-char 56) + (insert ".....") + (goto-char 67) + (insert ".............") + (goto-char 76) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((2 . 90) + (3 . 90) + (3 . 90) + (3 . 99) + (3 . 117) + (3 . 117) + (3 . 120) + (9 . 118) + (13 . 102)))))) + +(ert-deftest overlay-autogenerated-test-10 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 16 60 nil nil nil) + (make-overlay 36 53 nil nil nil) + (make-overlay 44 39 nil t t) + (make-overlay 61 47 nil t t) + (make-overlay 58 39 nil nil t) + (make-overlay 23 54 nil nil t) + (make-overlay 65 59 nil t t) + (make-overlay 13 57 nil nil t) + (make-overlay 22 64 nil nil t) + (make-overlay 16 19 nil nil nil) + (make-overlay 16 1 nil nil t) + (make-overlay 28 21 nil t t) + (make-overlay 10 62 nil nil nil) + (make-overlay 12 18 nil nil t) + (make-overlay 15 5 nil nil t) + (make-overlay 36 31 nil nil t) + (goto-char 42) + (insert "...") + (goto-char 25) + (delete-char 28) + (goto-char 30) + (delete-char 10) + (goto-char 8) + (delete-char 9) + (goto-char 5) + (insert "........") + (goto-char 6) + (delete-char 2) + (goto-char 4) + (insert "") + (goto-char 21) + (insert ".............") + (goto-char 6) + (delete-char 33) + (goto-char 1) + (delete-char 1) + (goto-char 6) + (insert "..........") + (goto-char 8) + (insert "...........") + (goto-char 21) + (insert "........") + (goto-char 16) + (delete-char 18) + (goto-char 5) + (insert "...") + (goto-char 5) + (delete-char 8) + (goto-char 11) + (insert ".") + (goto-char 1) + (insert ".......") + (goto-char 9) + (delete-char 9) + (goto-char 5) + (insert "") + (goto-char 8) + (delete-char 0) + (goto-char 11) + (insert "..............") + (goto-char 12) + (insert "") + (goto-char 11) + (delete-char 8) + (goto-char 7) + (delete-char 3) + (goto-char 5) + (delete-char 3) + (goto-char 1) + (delete-char 8) + (goto-char 1) + (insert "....") + (goto-char 1) + (insert "..") + (goto-char 7) + (insert "...") + (goto-char 8) + (widen) + (narrow-to-region 9 11) + (goto-char 11) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((1 . 10) + (1 . 10) + (1 . 10) + (1 . 10) + (1 . 10) + (1 . 12) + (1 . 12) + (1 . 12) + (10 . 10) + (10 . 10) + (10 . 12)))))) + +(ert-deftest overlay-autogenerated-test-11 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 33 18 nil nil nil) + (make-overlay 56 38 nil t nil) + (make-overlay 2 45 nil nil t) + (make-overlay 19 55 nil nil t) + (make-overlay 28 42 nil t t) + (make-overlay 50 29 nil t nil) + (make-overlay 40 63 nil nil nil) + (make-overlay 13 2 nil nil t) + (make-overlay 26 7 nil t t) + (make-overlay 22 25 nil nil nil) + (make-overlay 14 14 nil t nil) + (make-overlay 15 39 nil t t) + (make-overlay 51 22 nil t t) + (make-overlay 58 5 nil t nil) + (make-overlay 16 10 nil nil nil) + (make-overlay 32 33 nil t nil) + (goto-char 40) + (delete-char 20) + (goto-char 45) + (delete-char 0) + (goto-char 6) + (insert "..") + (goto-char 45) + (insert "...") + (goto-char 26) + (insert "...............") + (goto-char 27) + (insert "...........") + (goto-char 38) + (insert "......") + (goto-char 62) + (insert "...............") + (goto-char 18) + (insert "...........") + (goto-char 99) + (widen) + (narrow-to-region 37 17) + (goto-char 29) + (delete-char 2) + (goto-char 28) + (delete-char 2) + (goto-char 17) + (insert ".....") + (goto-char 21) + (widen) + (narrow-to-region 34 96) + (goto-char 44) + (delete-char 22) + (goto-char 39) + (insert "..") + (goto-char 53) + (insert "...............") + (goto-char 58) + (insert ".............") + (goto-char 93) + (insert ".........") + (goto-char 78) + (widen) + (narrow-to-region 27 104) + (goto-char 93) + (delete-char 11) + (goto-char 59) + (insert "....") + (goto-char 59) + (insert "..............") + (goto-char 74) + (delete-char 5) + (goto-char 70) + (insert ".") + (goto-char 37) + (insert "...........") + (goto-char 34) + (delete-char 46) + (goto-char 49) + (insert "......") + (goto-char 55) + (insert "...") + (goto-char 42) + (insert "...") + (goto-char 70) + (delete-char 8) + (goto-char 48) + (delete-char 28) + (should + (equal + (test-overlay-regions) + '((2 . 62) + (5 . 62) + (9 . 34) + (22 . 61) + (33 . 55) + (33 . 62) + (34 . 34) + (34 . 62)))))) + +(ert-deftest overlay-autogenerated-test-12 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 18 50 nil nil nil) + (make-overlay 63 3 nil nil t) + (make-overlay 44 20 nil t t) + (make-overlay 58 38 nil nil t) + (make-overlay 3 17 nil t nil) + (make-overlay 31 62 nil t nil) + (make-overlay 12 17 nil t nil) + (make-overlay 17 52 nil nil nil) + (make-overlay 9 35 nil nil nil) + (make-overlay 17 38 nil nil nil) + (make-overlay 53 54 nil nil t) + (make-overlay 65 34 nil t nil) + (make-overlay 12 33 nil t nil) + (make-overlay 54 58 nil nil nil) + (make-overlay 42 26 nil t nil) + (make-overlay 2 4 nil t nil) + (goto-char 4) + (delete-char 26) + (goto-char 39) + (insert ".") + (goto-char 2) + (delete-char 14) + (goto-char 16) + (widen) + (narrow-to-region 19 1) + (goto-char 7) + (delete-char 9) + (goto-char 6) + (insert ".........") + (goto-char 6) + (insert "..........") + (goto-char 16) + (insert ".............") + (goto-char 36) + (delete-char 1) + (goto-char 4) + (insert "..........") + (goto-char 49) + (delete-char 2) + (goto-char 16) + (insert "............") + (goto-char 52) + (widen) + (narrow-to-region 36 38) + (goto-char 37) + (delete-char 1) + (goto-char 37) + (insert ".............") + (goto-char 46) + (insert ".") + (goto-char 40) + (delete-char 5) + (goto-char 45) + (delete-char 0) + (goto-char 46) + (delete-char 0) + (goto-char 40) + (insert "..........") + (goto-char 39) + (delete-char 4) + (goto-char 39) + (delete-char 3) + (goto-char 40) + (widen) + (narrow-to-region 8 9) + (goto-char 8) + (delete-char 1) + (goto-char 8) + (delete-char 0) + (goto-char 8) + (widen) + (narrow-to-region 45 15) + (goto-char 40) + (insert "...............") + (goto-char 29) + (delete-char 7) + (goto-char 30) + (delete-char 6) + (goto-char 21) + (delete-char 9) + (goto-char 22) + (insert "...............") + (goto-char 51) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((2 . 92) + (2 . 92) + (2 . 93) + (2 . 96) + (2 . 97) + (2 . 99)))))) + +(ert-deftest overlay-autogenerated-test-13 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 18 30 nil t t) + (make-overlay 54 37 nil nil t) + (make-overlay 16 61 nil nil t) + (make-overlay 58 7 nil nil t) + (make-overlay 27 39 nil nil t) + (make-overlay 39 31 nil nil t) + (make-overlay 11 47 nil nil nil) + (make-overlay 47 40 nil t t) + (make-overlay 27 18 nil nil nil) + (make-overlay 33 26 nil nil t) + (make-overlay 55 4 nil t t) + (make-overlay 62 50 nil t t) + (make-overlay 47 65 nil t t) + (make-overlay 17 23 nil nil t) + (make-overlay 30 31 nil t nil) + (make-overlay 10 37 nil t nil) + (goto-char 8) + (delete-char 6) + (goto-char 56) + (delete-char 0) + (goto-char 28) + (insert ".........") + (goto-char 19) + (insert "..............") + (goto-char 4) + (delete-char 28) + (goto-char 49) + (delete-char 4) + (goto-char 2) + (insert "............") + (goto-char 10) + (delete-char 37) + (goto-char 19) + (delete-char 2) + (goto-char 20) + (delete-char 0) + (goto-char 16) + (insert "..") + (goto-char 8) + (widen) + (narrow-to-region 12 3) + (goto-char 10) + (delete-char 2) + (goto-char 9) + (insert "..") + (goto-char 12) + (insert "...............") + (goto-char 25) + (insert ".....") + (goto-char 10) + (widen) + (narrow-to-region 42 18) + (goto-char 20) + (insert ".......") + (goto-char 18) + (insert ".........") + (goto-char 55) + (delete-char 3) + (goto-char 48) + (insert ".......") + (goto-char 52) + (delete-char 6) + (goto-char 45) + (delete-char 11) + (goto-char 27) + (delete-char 13) + (goto-char 22) + (insert "...........") + (goto-char 19) + (delete-char 15) + (goto-char 20) + (delete-char 0) + (goto-char 23) + (widen) + (narrow-to-region 12 25) + (goto-char 16) + (insert "..........") + (goto-char 25) + (widen) + (narrow-to-region 2 38) + (goto-char 34) + (delete-char 0) + (goto-char 31) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((12 . 12) + (12 . 12) + (12 . 12) + (12 . 12) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 55)))))) + +(ert-deftest overlay-autogenerated-test-14 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 37 nil t nil) + (make-overlay 15 44 nil nil nil) + (make-overlay 31 34 nil nil t) + (make-overlay 35 33 nil t t) + (make-overlay 4 27 nil t t) + (make-overlay 37 5 nil nil t) + (make-overlay 58 19 nil nil t) + (make-overlay 57 47 nil nil t) + (make-overlay 49 5 nil t t) + (make-overlay 21 59 nil t t) + (make-overlay 42 33 nil t nil) + (make-overlay 22 16 nil t t) + (make-overlay 9 51 nil t nil) + (make-overlay 20 24 nil nil t) + (make-overlay 21 7 nil t t) + (make-overlay 58 52 nil t t) + (goto-char 39) + (widen) + (narrow-to-region 55 54) + (goto-char 54) + (insert ".............") + (goto-char 55) + (insert "............") + (goto-char 66) + (delete-char 10) + (goto-char 62) + (insert "...............") + (goto-char 82) + (delete-char 2) + (goto-char 82) + (delete-char 0) + (goto-char 76) + (insert "..............") + (goto-char 60) + (insert ".............") + (goto-char 71) + (insert "...............") + (goto-char 122) + (delete-char 0) + (goto-char 93) + (delete-char 3) + (goto-char 108) + (delete-char 1) + (goto-char 121) + (insert "........") + (goto-char 92) + (insert "") + (goto-char 103) + (insert "..........") + (goto-char 85) + (delete-char 13) + (goto-char 116) + (delete-char 7) + (goto-char 103) + (widen) + (narrow-to-region 60 27) + (goto-char 28) + (delete-char 16) + (goto-char 35) + (insert ".......") + (goto-char 47) + (insert "........") + (goto-char 38) + (delete-char 1) + (goto-char 43) + (insert "..........") + (goto-char 59) + (insert "........") + (goto-char 57) + (insert "........") + (goto-char 36) + (insert "...........") + (goto-char 82) + (delete-char 11) + (goto-char 67) + (insert "..........") + (goto-char 46) + (delete-char 1) + (goto-char 47) + (insert "......") + (goto-char 69) + (delete-char 7) + (should + (equal + (test-overlay-regions) + '((5 . 28) + (5 . 33) + (9 . 35) + (15 . 28) + (19 . 154) + (21 . 155) + (28 . 28) + (28 . 28) + (28 . 28) + (28 . 28) + (31 . 153) + (58 . 154)))))) + +(ert-deftest overlay-autogenerated-test-15 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 3 19 nil t t) + (make-overlay 11 18 nil t nil) + (make-overlay 28 51 nil nil t) + (make-overlay 29 15 nil t t) + (make-overlay 46 57 nil t t) + (make-overlay 26 24 nil nil nil) + (make-overlay 29 43 nil nil nil) + (make-overlay 54 29 nil nil nil) + (make-overlay 34 52 nil t nil) + (make-overlay 10 32 nil nil nil) + (make-overlay 28 34 nil nil t) + (make-overlay 11 43 nil nil nil) + (make-overlay 18 50 nil t t) + (make-overlay 28 39 nil nil nil) + (make-overlay 62 62 nil t t) + (make-overlay 30 62 nil t nil) + (goto-char 30) + (widen) + (narrow-to-region 6 22) + (goto-char 9) + (insert "..") + (goto-char 12) + (insert ".............") + (goto-char 29) + (insert "..............") + (goto-char 47) + (insert "........") + (goto-char 46) + (insert ".............") + (goto-char 55) + (insert "..........") + (goto-char 62) + (insert "...............") + (goto-char 47) + (delete-char 49) + (goto-char 11) + (insert "...........") + (goto-char 40) + (delete-char 1) + (goto-char 27) + (insert "..............") + (goto-char 51) + (insert "......") + (goto-char 60) + (delete-char 10) + (goto-char 37) + (insert ".........") + (goto-char 69) + (insert ".") + (goto-char 36) + (insert "............") + (goto-char 75) + (insert ".............") + (goto-char 21) + (widen) + (narrow-to-region 44 21) + (goto-char 37) + (insert ".............") + (goto-char 55) + (widen) + (narrow-to-region 84 28) + (goto-char 58) + (widen) + (narrow-to-region 96 49) + (goto-char 62) + (delete-char 0) + (goto-char 72) + (delete-char 24) + (goto-char 61) + (widen) + (narrow-to-region 105 83) + (goto-char 96) + (widen) + (narrow-to-region 109 46) + (goto-char 95) + (delete-char 4) + (goto-char 81) + (insert ".") + (goto-char 51) + (delete-char 8) + (goto-char 52) + (insert ".") + (goto-char 60) + (delete-char 10) + (goto-char 50) + (insert "......") + (should + (equal + (test-overlay-regions) + '((3 . 81) + (23 . 88) + (66 . 99) + (69 . 81) + (78 . 85) + (81 . 106) + (84 . 85) + (85 . 90) + (85 . 95) + (85 . 99) + (85 . 107) + (85 . 110) + (86 . 118) + (90 . 108)))))) + +(ert-deftest overlay-autogenerated-test-16 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 3 55 nil t nil) + (make-overlay 45 47 nil nil nil) + (make-overlay 23 57 nil t t) + (make-overlay 64 55 nil nil nil) + (make-overlay 37 26 nil t t) + (make-overlay 29 38 nil nil t) + (make-overlay 33 3 nil t t) + (make-overlay 49 16 nil t nil) + (make-overlay 35 56 nil t t) + (make-overlay 9 39 nil nil nil) + (make-overlay 2 61 nil nil nil) + (make-overlay 59 26 nil nil t) + (make-overlay 5 50 nil t t) + (make-overlay 19 19 nil nil t) + (make-overlay 64 21 nil t nil) + (make-overlay 21 8 nil nil t) + (goto-char 17) + (insert ".....") + (goto-char 29) + (insert "............") + (goto-char 42) + (delete-char 38) + (goto-char 24) + (insert "") + (goto-char 9) + (delete-char 2) + (goto-char 20) + (insert "..") + (goto-char 27) + (delete-char 8) + (goto-char 25) + (delete-char 6) + (goto-char 8) + (delete-char 21) + (goto-char 9) + (insert "..............") + (goto-char 3) + (insert "....") + (goto-char 8) + (delete-char 18) + (goto-char 6) + (widen) + (narrow-to-region 5 8) + (goto-char 5) + (delete-char 3) + (goto-char 5) + (insert "...") + (goto-char 8) + (insert "..........") + (goto-char 5) + (insert "") + (goto-char 7) + (delete-char 8) + (goto-char 8) + (widen) + (narrow-to-region 2 2) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (widen) + (narrow-to-region 10 3) + (goto-char 8) + (delete-char 2) + (goto-char 7) + (insert ".......") + (goto-char 8) + (delete-char 3) + (goto-char 12) + (insert "..") + (goto-char 9) + (delete-char 2) + (goto-char 7) + (insert "......") + (goto-char 15) + (insert "..........") + (goto-char 4) + (insert "........") + (should + (equal + (test-overlay-regions) + '((2 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 36) + (13 . 36) + (13 . 36) + (13 . 36)))))) + +(ert-deftest overlay-autogenerated-test-17 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 37 nil t nil) + (make-overlay 40 3 nil t t) + (make-overlay 61 19 nil t t) + (make-overlay 46 9 nil nil t) + (make-overlay 64 39 nil nil t) + (make-overlay 50 58 nil nil t) + (make-overlay 21 30 nil t nil) + (make-overlay 44 54 nil t nil) + (make-overlay 32 2 nil t nil) + (make-overlay 14 9 nil t t) + (make-overlay 41 40 nil t nil) + (make-overlay 17 26 nil t nil) + (make-overlay 57 50 nil t t) + (make-overlay 16 65 nil nil t) + (make-overlay 13 61 nil t t) + (make-overlay 39 64 nil nil t) + (goto-char 37) + (widen) + (narrow-to-region 12 1) + (goto-char 12) + (insert "......") + (goto-char 8) + (delete-char 4) + (goto-char 11) + (delete-char 3) + (goto-char 6) + (insert ".....") + (goto-char 6) + (widen) + (narrow-to-region 53 48) + (goto-char 48) + (delete-char 5) + (goto-char 48) + (widen) + (narrow-to-region 59 58) + (goto-char 59) + (delete-char 0) + (goto-char 58) + (insert "...") + (goto-char 60) + (insert "...............") + (goto-char 58) + (insert ".............") + (goto-char 67) + (insert ".....") + (goto-char 73) + (insert "") + (goto-char 68) + (insert ".....") + (goto-char 64) + (insert "....") + (goto-char 62) + (insert "..") + (goto-char 91) + (insert "..........") + (goto-char 80) + (insert "............") + (goto-char 100) + (delete-char 21) + (goto-char 74) + (insert "...") + (goto-char 60) + (delete-char 30) + (goto-char 64) + (widen) + (narrow-to-region 71 23) + (goto-char 53) + (delete-char 11) + (goto-char 23) + (delete-char 21) + (goto-char 39) + (delete-char 0) + (goto-char 35) + (insert "") + (goto-char 35) + (insert ".........") + (goto-char 30) + (insert "...........") + (goto-char 35) + (insert "..") + (goto-char 37) + (delete-char 1) + (goto-char 28) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((13 . 27) + (17 . 67) + (20 . 71) + (23 . 23) + (23 . 24) + (23 . 67) + (23 . 70) + (23 . 70) + (27 . 41) + (28 . 41) + (28 . 41)))))) + +(ert-deftest overlay-autogenerated-test-18 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 43 52 nil nil t) + (make-overlay 27 29 nil nil t) + (make-overlay 24 18 nil nil nil) + (make-overlay 39 52 nil nil nil) + (make-overlay 33 62 nil t t) + (make-overlay 16 7 nil t nil) + (make-overlay 47 39 nil nil t) + (make-overlay 59 41 nil nil nil) + (make-overlay 22 55 nil nil t) + (make-overlay 60 16 nil t t) + (make-overlay 55 20 nil nil t) + (make-overlay 25 12 nil nil t) + (make-overlay 26 2 nil nil t) + (make-overlay 17 35 nil nil t) + (make-overlay 46 41 nil t nil) + (make-overlay 57 53 nil t t) + (goto-char 52) + (insert "") + (goto-char 4) + (delete-char 21) + (goto-char 17) + (insert "") + (goto-char 35) + (insert "...............") + (goto-char 8) + (insert "...............") + (goto-char 9) + (insert "........") + (goto-char 73) + (delete-char 9) + (goto-char 62) + (insert "...............") + (goto-char 27) + (widen) + (narrow-to-region 34 84) + (goto-char 81) + (insert "...........") + (goto-char 48) + (insert "...") + (goto-char 74) + (insert ".......") + (goto-char 41) + (widen) + (narrow-to-region 37 105) + (goto-char 75) + (insert "...............") + (goto-char 47) + (insert "..........") + (goto-char 99) + (delete-char 13) + (goto-char 105) + (delete-char 4) + (goto-char 94) + (delete-char 5) + (goto-char 96) + (insert "..............") + (goto-char 74) + (insert "") + (goto-char 121) + (insert "...") + (goto-char 102) + (insert "...") + (goto-char 64) + (insert "......") + (goto-char 67) + (insert "...") + (goto-char 95) + (delete-char 19) + (goto-char 37) + (insert "..........") + (goto-char 50) + (widen) + (narrow-to-region 67 96) + (goto-char 88) + (insert "..........") + (goto-char 91) + (insert ".............") + (goto-char 70) + (delete-char 8) + (goto-char 111) + (widen) + (narrow-to-region 72 103) + (goto-char 101) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((4 . 119) + (4 . 119) + (4 . 162) + (35 . 162) + (51 . 78) + (53 . 162) + (55 . 78) + (79 . 162)))))) + +(ert-deftest overlay-autogenerated-test-19 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 19 31 nil t t) + (make-overlay 40 5 nil nil nil) + (make-overlay 13 41 nil t t) + (make-overlay 41 43 nil nil t) + (make-overlay 7 60 nil t nil) + (make-overlay 40 23 nil t nil) + (make-overlay 32 15 nil t t) + (make-overlay 12 45 nil nil nil) + (make-overlay 18 1 nil nil nil) + (make-overlay 58 32 nil t t) + (make-overlay 30 3 nil t t) + (make-overlay 43 61 nil t nil) + (make-overlay 54 57 nil nil t) + (make-overlay 34 14 nil t t) + (make-overlay 26 49 nil nil t) + (make-overlay 54 49 nil nil t) + (goto-char 28) + (insert "........") + (goto-char 32) + (insert "...........") + (goto-char 78) + (delete-char 6) + (goto-char 37) + (delete-char 0) + (goto-char 49) + (insert ".........") + (goto-char 40) + (widen) + (narrow-to-region 8 30) + (goto-char 20) + (delete-char 4) + (goto-char 23) + (delete-char 1) + (goto-char 10) + (insert ".") + (goto-char 22) + (delete-char 2) + (goto-char 22) + (insert "......") + (goto-char 17) + (insert "..........") + (goto-char 34) + (delete-char 0) + (goto-char 21) + (insert "............") + (goto-char 45) + (delete-char 7) + (goto-char 39) + (insert "...............") + (goto-char 29) + (insert "........") + (goto-char 9) + (delete-char 3) + (goto-char 63) + (delete-char 1) + (goto-char 33) + (insert "........") + (goto-char 16) + (delete-char 36) + (goto-char 20) + (delete-char 2) + (goto-char 28) + (delete-char 0) + (goto-char 24) + (insert "...........") + (goto-char 43) + (insert "..........") + (goto-char 30) + (delete-char 1) + (goto-char 40) + (delete-char 13) + (goto-char 22) + (delete-char 19) + (goto-char 10) + (delete-char 8) + (goto-char 14) + (delete-char 0) + (goto-char 12) + (delete-char 2) + (goto-char 11) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((1 . 12) + (3 . 40) + (5 . 50) + (7 . 69) + (10 . 42) + (10 . 44) + (10 . 51) + (10 . 55)))))) + +(ert-deftest overlay-autogenerated-test-20 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 44 42 nil t t) + (make-overlay 47 1 nil nil nil) + (make-overlay 24 48 nil nil nil) + (make-overlay 62 50 nil nil t) + (make-overlay 54 38 nil nil nil) + (make-overlay 3 9 nil nil nil) + (make-overlay 61 28 nil t nil) + (make-overlay 33 33 nil nil t) + (make-overlay 37 37 nil t nil) + (make-overlay 20 13 nil nil t) + (make-overlay 54 36 nil t nil) + (make-overlay 18 58 nil nil t) + (make-overlay 55 3 nil nil t) + (make-overlay 23 21 nil t t) + (make-overlay 47 55 nil t t) + (make-overlay 50 12 nil nil nil) + (goto-char 11) + (delete-char 46) + (goto-char 7) + (delete-char 3) + (goto-char 14) + (delete-char 1) + (goto-char 14) + (insert "......") + (goto-char 14) + (delete-char 4) + (goto-char 12) + (widen) + (narrow-to-region 11 12) + (goto-char 11) + (insert "...") + (goto-char 13) + (delete-char 1) + (goto-char 14) + (insert ".") + (goto-char 13) + (delete-char 2) + (goto-char 11) + (delete-char 2) + (goto-char 11) + (insert "") + (goto-char 11) + (delete-char 0) + (goto-char 11) + (delete-char 0) + (goto-char 11) + (delete-char 0) + (goto-char 11) + (insert ".") + (goto-char 11) + (insert ".") + (goto-char 12) + (insert "......") + (goto-char 14) + (delete-char 2) + (goto-char 11) + (delete-char 2) + (goto-char 14) + (insert "............") + (goto-char 19) + (insert "..............") + (goto-char 29) + (insert ".....") + (goto-char 42) + (delete-char 1) + (goto-char 22) + (insert ".....") + (goto-char 19) + (insert "..............") + (goto-char 42) + (insert ".....") + (goto-char 63) + (widen) + (narrow-to-region 26 42) + (goto-char 36) + (insert "..........") + (goto-char 40) + (delete-char 11) + (goto-char 26) + (delete-char 13) + (goto-char 28) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((8 . 56)))))) + +(ert-deftest overlay-autogenerated-test-21 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 65 15 nil nil nil) + (make-overlay 52 31 nil nil nil) + (make-overlay 12 51 nil t t) + (make-overlay 42 20 nil nil t) + (make-overlay 51 48 nil nil nil) + (make-overlay 59 28 nil t t) + (make-overlay 51 53 nil t nil) + (make-overlay 50 59 nil nil t) + (make-overlay 24 40 nil t nil) + (make-overlay 51 61 nil nil nil) + (make-overlay 12 58 nil nil t) + (make-overlay 64 17 nil t t) + (make-overlay 26 38 nil t t) + (make-overlay 23 36 nil nil nil) + (make-overlay 57 50 nil nil nil) + (make-overlay 42 15 nil nil t) + (goto-char 14) + (insert "............") + (goto-char 37) + (insert ".") + (goto-char 73) + (insert "..........") + (goto-char 17) + (delete-char 31) + (goto-char 21) + (delete-char 35) + (goto-char 9) + (delete-char 0) + (goto-char 7) + (delete-char 2) + (goto-char 1) + (insert "") + (goto-char 5) + (insert ".......") + (goto-char 8) + (insert "....") + (goto-char 27) + (delete-char 0) + (goto-char 10) + (insert ".............") + (goto-char 24) + (delete-char 16) + (goto-char 14) + (insert ".............") + (goto-char 25) + (delete-char 11) + (goto-char 3) + (insert "........") + (goto-char 38) + (insert "............") + (goto-char 41) + (insert "..............") + (goto-char 56) + (delete-char 3) + (goto-char 15) + (widen) + (narrow-to-region 16 53) + (goto-char 19) + (widen) + (narrow-to-region 18 33) + (goto-char 32) + (insert "......") + (goto-char 38) + (delete-char 1) + (goto-char 19) + (widen) + (narrow-to-region 11 11) + (goto-char 11) + (insert ".........") + (goto-char 11) + (insert ".........") + (goto-char 20) + (widen) + (narrow-to-region 22 69) + (goto-char 49) + (insert ".........") + (goto-char 54) + (delete-char 22) + (goto-char 44) + (insert "........") + (goto-char 40) + (delete-char 7) + (goto-char 29) + (delete-char 22) + (should + (equal + (test-overlay-regions) + '((33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33)))))) + +(ert-deftest overlay-autogenerated-test-22 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 12 14 nil nil t) + (make-overlay 54 7 nil nil t) + (make-overlay 8 3 nil nil nil) + (make-overlay 42 32 nil nil nil) + (make-overlay 10 27 nil t t) + (make-overlay 50 28 nil t t) + (make-overlay 39 35 nil nil nil) + (make-overlay 12 4 nil t t) + (make-overlay 29 54 nil nil nil) + (make-overlay 14 52 nil t t) + (make-overlay 9 15 nil t nil) + (make-overlay 44 11 nil nil nil) + (make-overlay 46 29 nil t t) + (make-overlay 40 58 nil t t) + (make-overlay 40 61 nil t nil) + (make-overlay 13 59 nil nil t) + (goto-char 32) + (insert ".............") + (goto-char 25) + (delete-char 10) + (goto-char 3) + (insert ".............") + (goto-char 33) + (delete-char 32) + (goto-char 39) + (widen) + (narrow-to-region 41 46) + (goto-char 43) + (delete-char 2) + (goto-char 42) + (delete-char 2) + (goto-char 42) + (insert "...") + (goto-char 43) + (delete-char 1) + (goto-char 42) + (widen) + (narrow-to-region 8 46) + (goto-char 25) + (delete-char 7) + (goto-char 12) + (delete-char 10) + (goto-char 23) + (insert "...............") + (goto-char 41) + (delete-char 3) + (goto-char 17) + (insert ".........") + (goto-char 37) + (insert "...............") + (goto-char 53) + (delete-char 7) + (goto-char 53) + (delete-char 0) + (goto-char 42) + (widen) + (narrow-to-region 20 54) + (goto-char 20) + (delete-char 28) + (goto-char 23) + (insert "..........") + (goto-char 30) + (insert "......") + (goto-char 26) + (delete-char 1) + (goto-char 27) + (widen) + (narrow-to-region 40 37) + (goto-char 37) + (insert ".....") + (goto-char 41) + (widen) + (narrow-to-region 13 37) + (goto-char 29) + (insert "...........") + (goto-char 33) + (delete-char 7) + (goto-char 33) + (delete-char 8) + (goto-char 20) + (insert "") + (goto-char 23) + (delete-char 7) + (goto-char 14) + (widen) + (narrow-to-region 33 33) + (should + (equal + (test-overlay-regions) + '((15 . 39) + (16 . 38) + (16 . 39)))))) + +(ert-deftest overlay-autogenerated-test-23 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 51 32 nil t t) + (make-overlay 13 61 nil t nil) + (make-overlay 47 19 nil nil t) + (make-overlay 11 30 nil nil nil) + (make-overlay 50 26 nil t t) + (make-overlay 64 13 nil t t) + (make-overlay 29 8 nil t t) + (make-overlay 25 42 nil t t) + (make-overlay 33 28 nil t t) + (make-overlay 54 7 nil nil nil) + (make-overlay 30 59 nil nil nil) + (make-overlay 65 50 nil t t) + (make-overlay 64 15 nil t nil) + (make-overlay 16 35 nil nil nil) + (make-overlay 40 36 nil nil t) + (make-overlay 31 35 nil t nil) + (goto-char 61) + (insert "......") + (goto-char 55) + (delete-char 2) + (goto-char 20) + (insert "..............") + (goto-char 56) + (insert "............") + (goto-char 48) + (delete-char 6) + (goto-char 9) + (delete-char 54) + (goto-char 20) + (delete-char 2) + (goto-char 16) + (delete-char 12) + (goto-char 18) + (insert ".............") + (goto-char 24) + (delete-char 7) + (goto-char 5) + (delete-char 2) + (goto-char 1) + (insert ".......") + (goto-char 1) + (insert ".......") + (goto-char 33) + (insert "") + (goto-char 4) + (insert "..") + (goto-char 5) + (widen) + (narrow-to-region 17 4) + (goto-char 13) + (insert ".") + (goto-char 8) + (insert "............") + (goto-char 9) + (delete-char 3) + (goto-char 4) + (widen) + (narrow-to-region 32 32) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 43) + (delete-char 4) + (goto-char 32) + (delete-char 1) + (goto-char 40) + (widen) + (narrow-to-region 33 19) + (goto-char 27) + (insert "........") + (goto-char 38) + (delete-char 2) + (goto-char 26) + (insert "") + (goto-char 33) + (delete-char 1) + (goto-char 27) + (insert ".") + (should + (equal + (test-overlay-regions) + '((38 . 56)))))) + +(ert-deftest overlay-autogenerated-test-24 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 63 8 nil t t) + (make-overlay 10 13 nil nil t) + (make-overlay 40 38 nil nil nil) + (make-overlay 21 34 nil t t) + (make-overlay 55 29 nil nil nil) + (make-overlay 36 65 nil t t) + (make-overlay 29 12 nil t nil) + (make-overlay 41 3 nil nil t) + (make-overlay 20 9 nil t t) + (make-overlay 52 42 nil t t) + (make-overlay 21 56 nil nil t) + (make-overlay 25 65 nil nil nil) + (make-overlay 38 4 nil t t) + (make-overlay 48 23 nil t t) + (make-overlay 52 9 nil nil t) + (make-overlay 48 19 nil nil nil) + (goto-char 43) + (delete-char 8) + (goto-char 30) + (delete-char 16) + (goto-char 7) + (insert "...") + (goto-char 14) + (delete-char 5) + (goto-char 36) + (delete-char 0) + (goto-char 9) + (insert "...............") + (goto-char 13) + (delete-char 17) + (goto-char 16) + (delete-char 2) + (goto-char 9) + (insert "") + (goto-char 11) + (delete-char 5) + (goto-char 18) + (insert "........") + (goto-char 15) + (insert "....") + (goto-char 16) + (delete-char 14) + (goto-char 20) + (insert ".") + (goto-char 25) + (delete-char 1) + (goto-char 14) + (delete-char 14) + (goto-char 3) + (delete-char 7) + (goto-char 3) + (delete-char 4) + (goto-char 1) + (insert "...........") + (goto-char 9) + (insert ".......") + (goto-char 5) + (delete-char 7) + (goto-char 12) + (insert ".........") + (goto-char 2) + (delete-char 4) + (goto-char 3) + (widen) + (narrow-to-region 14 6) + (goto-char 9) + (insert "..........") + (goto-char 13) + (delete-char 8) + (goto-char 7) + (delete-char 7) + (goto-char 7) + (insert "..") + (goto-char 9) + (insert ".............") + (goto-char 9) + (insert "..........") + (goto-char 21) + (insert "...............") + (goto-char 42) + (insert ".........") + (should + (equal + (test-overlay-regions) + 'nil)))) + +(ert-deftest overlay-autogenerated-test-25 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 24 8 nil nil t) + (make-overlay 41 16 nil t nil) + (make-overlay 3 16 nil nil nil) + (make-overlay 26 42 nil nil nil) + (make-overlay 32 45 nil nil t) + (make-overlay 34 19 nil nil nil) + (make-overlay 37 54 nil nil t) + (make-overlay 44 34 nil t nil) + (make-overlay 49 40 nil t t) + (make-overlay 29 34 nil t nil) + (make-overlay 54 16 nil t t) + (make-overlay 29 4 nil t nil) + (make-overlay 44 57 nil nil nil) + (make-overlay 5 32 nil nil nil) + (make-overlay 12 33 nil nil t) + (make-overlay 38 29 nil t nil) + (goto-char 12) + (delete-char 53) + (goto-char 1) + (delete-char 6) + (goto-char 5) + (widen) + (narrow-to-region 6 1) + (goto-char 6) + (insert "......") + (goto-char 10) + (insert "...............") + (goto-char 17) + (delete-char 5) + (goto-char 7) + (insert ".....") + (goto-char 8) + (insert "...............") + (goto-char 4) + (insert ".....") + (goto-char 44) + (widen) + (narrow-to-region 18 11) + (goto-char 15) + (delete-char 1) + (goto-char 17) + (delete-char 0) + (goto-char 13) + (delete-char 3) + (goto-char 14) + (insert "..") + (goto-char 16) + (insert "..") + (goto-char 15) + (delete-char 3) + (goto-char 13) + (delete-char 0) + (goto-char 14) + (insert "..........") + (goto-char 19) + (insert ".") + (goto-char 23) + (delete-char 1) + (goto-char 12) + (widen) + (narrow-to-region 23 40) + (goto-char 35) + (insert "....") + (goto-char 33) + (insert "..........") + (goto-char 37) + (delete-char 16) + (goto-char 37) + (delete-char 0) + (goto-char 23) + (widen) + (narrow-to-region 30 8) + (goto-char 29) + (delete-char 0) + (goto-char 15) + (delete-char 15) + (goto-char 9) + (insert "...........") + (goto-char 9) + (delete-char 1) + (goto-char 22) + (delete-char 3) + (goto-char 10) + (insert ".........") + (should + (equal + (test-overlay-regions) + '((1 . 30) + (1 . 30) + (1 . 30) + (2 . 53) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 53) + (30 . 53) + (30 . 53)))))) + +(ert-deftest overlay-autogenerated-test-26 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 60 59 nil t nil) + (make-overlay 18 11 nil nil t) + (make-overlay 4 44 nil nil nil) + (make-overlay 7 22 nil nil nil) + (make-overlay 54 50 nil t nil) + (make-overlay 59 28 nil nil nil) + (make-overlay 49 23 nil nil t) + (make-overlay 21 5 nil t nil) + (make-overlay 17 39 nil t nil) + (make-overlay 16 14 nil nil nil) + (make-overlay 50 26 nil nil nil) + (make-overlay 37 14 nil nil nil) + (make-overlay 6 59 nil nil t) + (make-overlay 30 17 nil nil t) + (make-overlay 17 34 nil nil t) + (make-overlay 7 22 nil t nil) + (goto-char 35) + (delete-char 25) + (goto-char 30) + (delete-char 7) + (goto-char 25) + (widen) + (narrow-to-region 3 19) + (goto-char 6) + (insert ".........") + (goto-char 21) + (insert "...............") + (goto-char 12) + (insert ".............") + (goto-char 34) + (widen) + (narrow-to-region 64 37) + (goto-char 62) + (insert ".............") + (goto-char 50) + (widen) + (narrow-to-region 72 38) + (goto-char 66) + (insert "") + (goto-char 54) + (insert "...") + (goto-char 70) + (delete-char 4) + (goto-char 49) + (delete-char 13) + (goto-char 38) + (insert "....") + (goto-char 46) + (insert ".") + (goto-char 43) + (widen) + (narrow-to-region 74 53) + (goto-char 60) + (delete-char 10) + (goto-char 53) + (insert "..............") + (goto-char 72) + (insert "............") + (goto-char 87) + (delete-char 2) + (goto-char 73) + (insert "............") + (goto-char 81) + (insert "........") + (goto-char 106) + (insert "...") + (goto-char 95) + (widen) + (narrow-to-region 77 39) + (goto-char 43) + (insert "..........") + (goto-char 40) + (insert "...............") + (goto-char 101) + (insert "") + (goto-char 53) + (insert "....") + (goto-char 79) + (delete-char 21) + (goto-char 85) + (insert "........") + (goto-char 52) + (delete-char 41) + (goto-char 43) + (insert ".....") + (should + (equal + (test-overlay-regions) + '((4 . 90) + (5 . 57) + (6 . 90) + (29 . 57) + (29 . 57) + (33 . 57)))))) + +(ert-deftest overlay-autogenerated-test-27 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 12 nil t nil) + (make-overlay 3 10 nil t t) + (make-overlay 11 53 nil t nil) + (make-overlay 59 3 nil t nil) + (make-overlay 28 19 nil t t) + (make-overlay 16 30 nil t t) + (make-overlay 39 19 nil t t) + (make-overlay 33 50 nil t nil) + (make-overlay 36 54 nil nil nil) + (make-overlay 42 59 nil nil nil) + (make-overlay 30 48 nil t nil) + (make-overlay 20 13 nil nil t) + (make-overlay 63 48 nil t nil) + (make-overlay 48 12 nil t t) + (make-overlay 64 50 nil nil nil) + (make-overlay 7 7 nil nil nil) + (goto-char 20) + (widen) + (narrow-to-region 21 54) + (goto-char 40) + (insert "..........") + (goto-char 21) + (delete-char 2) + (goto-char 35) + (widen) + (narrow-to-region 70 11) + (goto-char 45) + (insert "...............") + (goto-char 74) + (insert ".") + (goto-char 28) + (widen) + (narrow-to-region 77 67) + (goto-char 72) + (insert "..........") + (goto-char 85) + (delete-char 1) + (goto-char 82) + (widen) + (narrow-to-region 83 86) + (goto-char 83) + (delete-char 0) + (goto-char 86) + (delete-char 0) + (goto-char 86) + (insert "...........") + (goto-char 97) + (insert ".......") + (goto-char 103) + (widen) + (narrow-to-region 44 68) + (goto-char 49) + (insert "..") + (goto-char 65) + (insert ".............") + (goto-char 59) + (delete-char 0) + (goto-char 57) + (insert "........") + (goto-char 55) + (delete-char 30) + (goto-char 45) + (insert "...............") + (goto-char 44) + (insert "") + (goto-char 62) + (insert "............") + (goto-char 63) + (widen) + (narrow-to-region 12 5) + (goto-char 8) + (delete-char 4) + (goto-char 6) + (delete-char 0) + (goto-char 7) + (insert "..........") + (goto-char 15) + (delete-char 0) + (goto-char 16) + (insert "............") + (goto-char 20) + (insert ".........") + (goto-char 13) + (insert "..") + (goto-char 32) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((3 . 55) + (3 . 173) + (7 . 7)))))) + +(ert-deftest overlay-autogenerated-test-28 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 59 48 nil t nil) + (make-overlay 59 4 nil nil t) + (make-overlay 45 35 nil t nil) + (make-overlay 13 18 nil t t) + (make-overlay 10 7 nil t t) + (make-overlay 9 8 nil nil nil) + (make-overlay 33 47 nil nil t) + (make-overlay 1 57 nil t nil) + (make-overlay 16 59 nil nil t) + (make-overlay 43 58 nil nil t) + (make-overlay 6 11 nil nil nil) + (make-overlay 59 7 nil t nil) + (make-overlay 3 57 nil t t) + (make-overlay 61 35 nil nil nil) + (make-overlay 57 8 nil nil nil) + (make-overlay 5 32 nil t nil) + (goto-char 18) + (insert "............") + (goto-char 43) + (delete-char 2) + (goto-char 38) + (delete-char 26) + (goto-char 42) + (insert ".....") + (goto-char 52) + (insert "..........") + (goto-char 45) + (delete-char 11) + (goto-char 33) + (insert "....") + (goto-char 23) + (delete-char 14) + (goto-char 33) + (widen) + (narrow-to-region 30 33) + (goto-char 30) + (delete-char 0) + (goto-char 30) + (insert "...........") + (goto-char 30) + (delete-char 7) + (goto-char 30) + (insert ".") + (goto-char 32) + (delete-char 4) + (goto-char 34) + (delete-char 0) + (goto-char 34) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 46) + (insert ".........") + (goto-char 45) + (delete-char 3) + (goto-char 49) + (delete-char 2) + (goto-char 42) + (delete-char 2) + (goto-char 32) + (insert "..........") + (goto-char 47) + (insert "....") + (goto-char 59) + (insert ".......") + (goto-char 35) + (insert ".") + (goto-char 45) + (insert "..............") + (goto-char 37) + (insert "..") + (goto-char 80) + (insert ".....") + (goto-char 30) + (insert ".............") + (goto-char 102) + (insert "............") + (goto-char 113) + (insert "") + (goto-char 66) + (widen) + (narrow-to-region 47 38) + (should + (equal + (test-overlay-regions) + '((1 . 45) + (3 . 117) + (4 . 121) + (7 . 121) + (8 . 45) + (16 . 121) + (28 . 121) + (28 . 121) + (28 . 121)))))) + +(ert-deftest overlay-autogenerated-test-29 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 5 63 nil nil t) + (make-overlay 20 28 nil t t) + (make-overlay 58 53 nil t nil) + (make-overlay 4 57 nil t t) + (make-overlay 4 16 nil nil nil) + (make-overlay 33 26 nil t nil) + (make-overlay 9 32 nil t t) + (make-overlay 11 8 nil nil nil) + (make-overlay 59 35 nil nil t) + (make-overlay 15 25 nil t t) + (make-overlay 36 16 nil nil nil) + (make-overlay 8 37 nil nil nil) + (make-overlay 65 63 nil nil t) + (make-overlay 3 20 nil nil t) + (make-overlay 44 55 nil t t) + (make-overlay 45 25 nil t nil) + (goto-char 39) + (insert "...") + (goto-char 22) + (insert "........") + (goto-char 60) + (insert ".........") + (goto-char 17) + (insert "............") + (goto-char 13) + (widen) + (narrow-to-region 79 16) + (goto-char 19) + (delete-char 11) + (goto-char 25) + (insert "........") + (goto-char 61) + (insert "....") + (goto-char 45) + (widen) + (narrow-to-region 73 66) + (goto-char 71) + (insert "............") + (goto-char 81) + (delete-char 2) + (goto-char 73) + (insert "..........") + (goto-char 74) + (insert "............") + (goto-char 82) + (delete-char 7) + (goto-char 78) + (delete-char 18) + (goto-char 75) + (insert ".........") + (goto-char 66) + (insert ".........") + (goto-char 86) + (delete-char 12) + (goto-char 77) + (widen) + (narrow-to-region 23 55) + (goto-char 43) + (insert ".") + (goto-char 50) + (insert "..") + (goto-char 25) + (delete-char 18) + (goto-char 33) + (delete-char 7) + (goto-char 26) + (insert "........") + (goto-char 29) + (insert "...........") + (goto-char 33) + (insert "...") + (goto-char 40) + (insert "..........") + (goto-char 26) + (insert "") + (goto-char 35) + (insert ".") + (goto-char 59) + (insert ".") + (goto-char 51) + (insert "..") + (goto-char 59) + (insert ".............") + (should + (equal + (test-overlay-regions) + '((4 . 130) + (5 . 136) + (8 . 82) + (9 . 82) + (15 . 25) + (16 . 82) + (21 . 77) + (25 . 105) + (75 . 82)))))) + +(ert-deftest overlay-autogenerated-test-30 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 27 65 nil t t) + (make-overlay 39 51 nil t t) + (make-overlay 53 2 nil nil nil) + (make-overlay 3 17 nil nil t) + (make-overlay 35 4 nil nil t) + (make-overlay 65 53 nil t nil) + (make-overlay 8 21 nil t t) + (make-overlay 18 62 nil t t) + (make-overlay 42 59 nil nil t) + (make-overlay 12 37 nil t t) + (make-overlay 64 31 nil t nil) + (make-overlay 39 54 nil nil t) + (make-overlay 41 24 nil t nil) + (make-overlay 10 21 nil nil t) + (make-overlay 49 15 nil t nil) + (make-overlay 49 63 nil nil t) + (goto-char 43) + (insert "..........") + (goto-char 44) + (delete-char 29) + (goto-char 32) + (insert "..") + (goto-char 13) + (insert ".") + (goto-char 42) + (insert ".........") + (goto-char 39) + (insert "..........") + (goto-char 15) + (insert "............") + (goto-char 58) + (delete-char 9) + (goto-char 63) + (insert ".........") + (goto-char 49) + (insert ".") + (goto-char 28) + (delete-char 51) + (goto-char 12) + (delete-char 6) + (goto-char 20) + (delete-char 2) + (goto-char 7) + (widen) + (narrow-to-region 2 9) + (goto-char 5) + (insert "...............") + (goto-char 18) + (delete-char 1) + (goto-char 4) + (insert ".............") + (goto-char 13) + (delete-char 22) + (goto-char 12) + (insert "") + (goto-char 3) + (insert ".............") + (goto-char 22) + (insert "...............") + (goto-char 9) + (insert "....") + (goto-char 8) + (insert "...........") + (goto-char 6) + (delete-char 34) + (goto-char 21) + (insert "....") + (goto-char 14) + (insert ".....") + (goto-char 20) + (insert ".......") + (goto-char 34) + (widen) + (narrow-to-region 3 2) + (goto-char 3) + (delete-char 0) + (goto-char 2) + (insert "..............") + (goto-char 15) + (delete-char 2) + (goto-char 11) + (insert "......") + (should + (equal + (test-overlay-regions) + '((2 . 68)))))) + +(ert-deftest overlay-autogenerated-test-31 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 54 64 nil nil nil) + (make-overlay 49 12 nil nil t) + (make-overlay 40 12 nil t nil) + (make-overlay 17 38 nil nil nil) + (make-overlay 21 36 nil t t) + (make-overlay 8 38 nil t nil) + (make-overlay 50 22 nil t nil) + (make-overlay 65 15 nil nil t) + (make-overlay 57 60 nil t t) + (make-overlay 35 11 nil nil t) + (make-overlay 49 44 nil nil t) + (make-overlay 45 31 nil nil t) + (make-overlay 51 24 nil t t) + (make-overlay 20 14 nil nil nil) + (make-overlay 6 18 nil t t) + (make-overlay 25 3 nil nil nil) + (goto-char 18) + (delete-char 10) + (goto-char 36) + (delete-char 13) + (goto-char 8) + (delete-char 4) + (goto-char 2) + (delete-char 8) + (goto-char 12) + (delete-char 10) + (goto-char 15) + (delete-char 4) + (goto-char 16) + (insert ".........") + (goto-char 17) + (insert "...............") + (goto-char 33) + (delete-char 0) + (goto-char 38) + (delete-char 0) + (goto-char 11) + (insert "...........") + (goto-char 8) + (delete-char 14) + (goto-char 32) + (insert "........") + (goto-char 40) + (widen) + (narrow-to-region 14 6) + (goto-char 10) + (delete-char 1) + (goto-char 7) + (widen) + (narrow-to-region 18 39) + (goto-char 36) + (delete-char 1) + (goto-char 34) + (widen) + (narrow-to-region 39 14) + (goto-char 22) + (widen) + (narrow-to-region 25 21) + (goto-char 23) + (delete-char 2) + (goto-char 23) + (delete-char 0) + (goto-char 23) + (insert ".........") + (goto-char 32) + (delete-char 0) + (goto-char 31) + (insert ".........") + (goto-char 32) + (insert "...") + (goto-char 30) + (widen) + (narrow-to-region 10 56) + (goto-char 10) + (insert ".........") + (goto-char 38) + (insert ".........") + (goto-char 19) + (insert "..") + (goto-char 11) + (insert "..............") + (goto-char 66) + (insert "...............") + (goto-char 13) + (insert "......") + (should + (equal + (test-overlay-regions) + '((2 . 41) + (3 . 117) + (6 . 41) + (8 . 41) + (9 . 41) + (10 . 42) + (41 . 42)))))) + +(ert-deftest overlay-autogenerated-test-32 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 35 60 nil nil t) + (make-overlay 45 46 nil nil nil) + (make-overlay 47 11 nil nil t) + (make-overlay 12 51 nil t nil) + (make-overlay 61 17 nil t nil) + (make-overlay 7 24 nil t nil) + (make-overlay 36 37 nil nil t) + (make-overlay 5 39 nil t t) + (make-overlay 5 40 nil nil t) + (make-overlay 38 40 nil t t) + (make-overlay 47 45 nil t nil) + (make-overlay 61 48 nil nil nil) + (make-overlay 23 39 nil t t) + (make-overlay 11 52 nil nil nil) + (make-overlay 37 35 nil nil nil) + (make-overlay 19 20 nil t nil) + (goto-char 43) + (insert "........") + (goto-char 7) + (insert "") + (goto-char 28) + (delete-char 41) + (goto-char 3) + (delete-char 17) + (goto-char 2) + (insert ".") + (goto-char 7) + (insert ".........") + (goto-char 21) + (delete-char 4) + (goto-char 13) + (delete-char 1) + (goto-char 2) + (insert "...............") + (goto-char 7) + (insert "") + (goto-char 14) + (insert ".....") + (goto-char 16) + (insert ".") + (goto-char 10) + (insert "..............") + (goto-char 16) + (delete-char 18) + (goto-char 1) + (delete-char 36) + (goto-char 1) + (delete-char 0) + (goto-char 1) + (delete-char 0) + (goto-char 1) + (insert ".............") + (goto-char 9) + (insert ".") + (goto-char 14) + (insert ".....") + (goto-char 9) + (delete-char 0) + (goto-char 15) + (delete-char 0) + (goto-char 6) + (delete-char 4) + (goto-char 11) + (delete-char 5) + (goto-char 5) + (insert "....") + (goto-char 5) + (insert ".....") + (goto-char 12) + (insert "") + (goto-char 13) + (insert ".......") + (goto-char 14) + (insert "......") + (goto-char 9) + (delete-char 3) + (goto-char 17) + (delete-char 0) + (goto-char 7) + (delete-char 12) + (should + (equal + (test-overlay-regions) + '((1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 18) + (1 . 18) + (1 . 18) + (1 . 18) + (18 . 18) + (18 . 18) + (18 . 18)))))) + +(ert-deftest overlay-autogenerated-test-33 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 65 33 nil t nil) + (make-overlay 45 54 nil t t) + (make-overlay 17 38 nil t nil) + (make-overlay 58 46 nil nil t) + (make-overlay 21 36 nil t t) + (make-overlay 31 63 nil nil t) + (make-overlay 37 64 nil t t) + (make-overlay 42 19 nil nil nil) + (make-overlay 51 60 nil t nil) + (make-overlay 47 15 nil t t) + (make-overlay 57 47 nil nil nil) + (make-overlay 40 45 nil nil nil) + (make-overlay 44 47 nil t nil) + (make-overlay 42 35 nil t nil) + (make-overlay 1 65 nil nil t) + (make-overlay 29 63 nil t nil) + (goto-char 33) + (insert "...........") + (goto-char 56) + (insert ".........") + (goto-char 67) + (insert "....") + (goto-char 28) + (delete-char 35) + (goto-char 9) + (insert "......") + (goto-char 43) + (delete-char 17) + (goto-char 29) + (insert ".......") + (goto-char 20) + (insert "....") + (goto-char 53) + (insert ".......") + (goto-char 14) + (widen) + (narrow-to-region 38 57) + (goto-char 51) + (insert "") + (goto-char 57) + (insert ".......") + (goto-char 64) + (insert ".....") + (goto-char 59) + (delete-char 3) + (goto-char 45) + (delete-char 12) + (goto-char 43) + (insert "......") + (goto-char 48) + (insert "......") + (goto-char 52) + (insert "........") + (goto-char 57) + (delete-char 16) + (goto-char 43) + (delete-char 9) + (goto-char 40) + (insert "") + (goto-char 39) + (insert "..........") + (goto-char 50) + (widen) + (narrow-to-region 31 27) + (goto-char 27) + (insert "..........") + (goto-char 33) + (delete-char 0) + (goto-char 37) + (insert "..") + (goto-char 38) + (delete-char 4) + (goto-char 38) + (insert "..........") + (goto-char 45) + (insert ".....") + (goto-char 53) + (insert "...") + (goto-char 51) + (insert ".") + (goto-char 28) + (insert "...") + (should + (equal + (test-overlay-regions) + '((1 . 93) + (25 . 92) + (41 . 88) + (60 . 88)))))) + +(ert-deftest overlay-autogenerated-test-34 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 2 63 nil nil t) + (make-overlay 54 30 nil t nil) + (make-overlay 21 57 nil t nil) + (make-overlay 61 19 nil nil nil) + (make-overlay 55 8 nil nil t) + (make-overlay 14 51 nil nil nil) + (make-overlay 33 13 nil t t) + (make-overlay 36 25 nil t t) + (make-overlay 22 21 nil nil t) + (make-overlay 21 48 nil nil t) + (make-overlay 36 7 nil nil t) + (make-overlay 2 40 nil nil nil) + (make-overlay 21 27 nil nil t) + (make-overlay 26 2 nil nil nil) + (make-overlay 60 43 nil nil nil) + (make-overlay 12 50 nil t t) + (goto-char 44) + (delete-char 6) + (goto-char 5) + (insert "..") + (goto-char 17) + (insert "........") + (goto-char 48) + (insert "..") + (goto-char 27) + (delete-char 29) + (goto-char 10) + (delete-char 2) + (goto-char 35) + (insert ".............") + (goto-char 20) + (delete-char 0) + (goto-char 6) + (insert ".") + (goto-char 9) + (delete-char 6) + (goto-char 38) + (insert ".........") + (goto-char 5) + (insert ".........") + (goto-char 10) + (delete-char 20) + (goto-char 6) + (delete-char 6) + (goto-char 14) + (insert ".............") + (goto-char 31) + (delete-char 10) + (goto-char 20) + (widen) + (narrow-to-region 27 39) + (goto-char 34) + (delete-char 5) + (goto-char 32) + (delete-char 1) + (goto-char 27) + (insert "..") + (goto-char 28) + (insert "........") + (goto-char 39) + (insert "........") + (goto-char 38) + (delete-char 7) + (goto-char 44) + (delete-char 0) + (goto-char 30) + (insert "...............") + (goto-char 43) + (insert "............") + (goto-char 56) + (delete-char 1) + (goto-char 65) + (delete-char 3) + (goto-char 36) + (insert ".........") + (goto-char 74) + (insert ".....") + (goto-char 67) + (delete-char 5) + (goto-char 38) + (insert "..") + (should + (equal + (test-overlay-regions) + '((2 . 80) + (6 . 78)))))) + +(ert-deftest overlay-autogenerated-test-35 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 38 16 nil nil nil) + (make-overlay 19 22 nil t nil) + (make-overlay 16 43 nil nil t) + (make-overlay 27 5 nil nil nil) + (make-overlay 43 34 nil t nil) + (make-overlay 47 4 nil nil t) + (make-overlay 1 47 nil nil t) + (make-overlay 27 35 nil t nil) + (make-overlay 41 41 nil nil t) + (make-overlay 21 19 nil nil nil) + (make-overlay 16 38 nil nil t) + (make-overlay 33 39 nil t nil) + (make-overlay 34 51 nil nil t) + (make-overlay 45 36 nil t nil) + (make-overlay 42 18 nil t t) + (make-overlay 12 30 nil nil nil) + (goto-char 18) + (insert "") + (goto-char 58) + (delete-char 3) + (goto-char 58) + (delete-char 0) + (goto-char 1) + (insert ".......") + (goto-char 48) + (delete-char 17) + (goto-char 39) + (delete-char 6) + (goto-char 33) + (widen) + (narrow-to-region 45 46) + (goto-char 46) + (insert "") + (goto-char 46) + (delete-char 0) + (goto-char 46) + (insert ".....") + (goto-char 51) + (widen) + (narrow-to-region 17 26) + (goto-char 25) + (widen) + (narrow-to-region 50 41) + (goto-char 45) + (insert "..............") + (goto-char 59) + (insert "...........") + (goto-char 47) + (delete-char 9) + (goto-char 59) + (insert "") + (goto-char 46) + (insert "") + (goto-char 54) + (delete-char 5) + (goto-char 57) + (widen) + (narrow-to-region 57 31) + (goto-char 42) + (delete-char 2) + (goto-char 52) + (insert "....") + (goto-char 44) + (insert "..") + (goto-char 44) + (insert "...............") + (goto-char 72) + (delete-char 1) + (goto-char 66) + (delete-char 6) + (goto-char 64) + (delete-char 5) + (goto-char 49) + (delete-char 12) + (goto-char 32) + (insert "......") + (goto-char 44) + (delete-char 2) + (goto-char 39) + (delete-char 12) + (goto-char 42) + (insert "......") + (goto-char 36) + (widen) + (narrow-to-region 14 47) + (should + (equal + (test-overlay-regions) + '((1 . 39) + (11 . 39) + (12 . 39) + (19 . 39) + (23 . 39) + (23 . 39) + (23 . 39) + (25 . 39) + (26 . 28) + (26 . 29) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39)))))) + +(ert-deftest overlay-autogenerated-test-36 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 1 38 nil t t) + (make-overlay 58 34 nil t nil) + (make-overlay 6 33 nil nil t) + (make-overlay 63 54 nil nil t) + (make-overlay 54 54 nil t t) + (make-overlay 21 61 nil nil nil) + (make-overlay 64 55 nil nil t) + (make-overlay 28 65 nil nil t) + (make-overlay 32 51 nil t nil) + (make-overlay 36 38 nil nil nil) + (make-overlay 35 21 nil nil nil) + (make-overlay 65 48 nil nil nil) + (make-overlay 32 27 nil nil t) + (make-overlay 27 55 nil t t) + (make-overlay 30 22 nil t nil) + (make-overlay 14 58 nil t nil) + (goto-char 40) + (delete-char 7) + (goto-char 42) + (insert "......") + (goto-char 11) + (widen) + (narrow-to-region 64 9) + (goto-char 21) + (delete-char 23) + (goto-char 24) + (insert "...") + (goto-char 13) + (insert "..........") + (goto-char 12) + (delete-char 5) + (goto-char 10) + (delete-char 0) + (goto-char 21) + (widen) + (narrow-to-region 9 5) + (goto-char 6) + (delete-char 0) + (goto-char 9) + (delete-char 0) + (goto-char 9) + (delete-char 0) + (goto-char 7) + (insert "............") + (goto-char 9) + (insert "...") + (goto-char 18) + (insert ".") + (goto-char 23) + (delete-char 1) + (goto-char 9) + (insert "....") + (goto-char 6) + (insert ".....") + (goto-char 23) + (widen) + (narrow-to-region 28 1) + (goto-char 6) + (insert "...........") + (goto-char 30) + (delete-char 8) + (goto-char 2) + (insert ".") + (goto-char 18) + (insert "......") + (goto-char 5) + (delete-char 9) + (goto-char 5) + (delete-char 20) + (goto-char 4) + (delete-char 3) + (goto-char 3) + (delete-char 2) + (goto-char 3) + (delete-char 0) + (goto-char 1) + (insert "......") + (goto-char 8) + (widen) + (narrow-to-region 39 2) + (goto-char 13) + (delete-char 12) + (goto-char 24) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((7 . 20) + (9 . 20) + (13 . 36) + (20 . 20) + (20 . 20) + (20 . 20) + (20 . 20) + (20 . 29) + (20 . 33) + (20 . 36) + (20 . 39) + (20 . 43) + (20 . 43)))))) + +(ert-deftest overlay-autogenerated-test-37 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 26 30 nil nil nil) + (make-overlay 55 50 nil nil t) + (make-overlay 43 54 nil nil t) + (make-overlay 53 48 nil nil nil) + (make-overlay 37 51 nil nil t) + (make-overlay 15 30 nil nil nil) + (make-overlay 2 24 nil t t) + (make-overlay 56 61 nil t nil) + (make-overlay 65 46 nil t nil) + (make-overlay 28 47 nil t nil) + (make-overlay 21 24 nil t t) + (make-overlay 17 13 nil t t) + (make-overlay 7 44 nil t nil) + (make-overlay 28 63 nil nil nil) + (make-overlay 22 16 nil t t) + (make-overlay 26 44 nil t t) + (goto-char 57) + (delete-char 6) + (goto-char 42) + (insert ".....") + (goto-char 63) + (insert ".............") + (goto-char 17) + (insert "") + (goto-char 57) + (insert "...........") + (goto-char 3) + (delete-char 47) + (goto-char 15) + (insert ".............") + (goto-char 28) + (insert "") + (goto-char 17) + (delete-char 31) + (goto-char 7) + (delete-char 16) + (goto-char 2) + (insert "...........") + (goto-char 2) + (insert "..") + (goto-char 18) + (widen) + (narrow-to-region 20 8) + (goto-char 13) + (widen) + (narrow-to-region 12 10) + (goto-char 10) + (delete-char 1) + (goto-char 11) + (delete-char 0) + (goto-char 10) + (insert "...") + (goto-char 11) + (delete-char 0) + (goto-char 13) + (insert "..") + (goto-char 16) + (delete-char 0) + (goto-char 10) + (delete-char 2) + (goto-char 11) + (insert ".....") + (goto-char 16) + (widen) + (narrow-to-region 6 13) + (goto-char 10) + (insert "..") + (goto-char 6) + (delete-char 6) + (goto-char 8) + (insert "...............") + (goto-char 21) + (delete-char 0) + (goto-char 21) + (widen) + (narrow-to-region 36 11) + (goto-char 12) + (insert "...............") + (goto-char 19) + (insert ".......") + (goto-char 56) + (delete-char 2) + (goto-char 42) + (delete-char 11) + (should + (equal + (test-overlay-regions) + '((44 . 45)))))) + +(ert-deftest overlay-autogenerated-test-38 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 13 nil t t) + (make-overlay 19 28 nil nil t) + (make-overlay 47 33 nil nil nil) + (make-overlay 8 44 nil t nil) + (make-overlay 48 4 nil t nil) + (make-overlay 8 20 nil t t) + (make-overlay 38 31 nil nil t) + (make-overlay 17 65 nil nil t) + (make-overlay 49 31 nil nil nil) + (make-overlay 39 19 nil nil t) + (make-overlay 40 49 nil t t) + (make-overlay 24 16 nil t t) + (make-overlay 4 41 nil t nil) + (make-overlay 61 42 nil t nil) + (make-overlay 46 11 nil nil nil) + (make-overlay 1 43 nil nil t) + (goto-char 62) + (delete-char 2) + (goto-char 25) + (widen) + (narrow-to-region 30 38) + (goto-char 37) + (delete-char 1) + (goto-char 37) + (insert "...........") + (goto-char 41) + (delete-char 3) + (goto-char 39) + (delete-char 5) + (goto-char 39) + (widen) + (narrow-to-region 31 9) + (goto-char 11) + (insert "..............") + (goto-char 9) + (widen) + (narrow-to-region 62 30) + (goto-char 32) + (widen) + (narrow-to-region 17 48) + (goto-char 39) + (delete-char 7) + (goto-char 24) + (delete-char 8) + (goto-char 19) + (insert "") + (goto-char 25) + (delete-char 5) + (goto-char 28) + (delete-char 0) + (goto-char 22) + (widen) + (narrow-to-region 52 35) + (goto-char 49) + (delete-char 0) + (goto-char 49) + (delete-char 3) + (goto-char 48) + (insert "...........") + (goto-char 37) + (delete-char 23) + (goto-char 36) + (delete-char 0) + (goto-char 35) + (insert "....") + (goto-char 35) + (insert "..") + (goto-char 39) + (delete-char 4) + (goto-char 39) + (delete-char 0) + (goto-char 36) + (delete-char 3) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (insert ".....") + (goto-char 38) + (delete-char 1) + (goto-char 35) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((1 . 37) + (24 . 44) + (25 . 37)))))) + +(ert-deftest overlay-autogenerated-test-39 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 49 nil t t) + (make-overlay 27 20 nil t nil) + (make-overlay 55 50 nil t nil) + (make-overlay 17 5 nil t t) + (make-overlay 26 56 nil nil t) + (make-overlay 42 11 nil t t) + (make-overlay 24 35 nil nil t) + (make-overlay 47 45 nil t t) + (make-overlay 37 12 nil nil t) + (make-overlay 17 25 nil t nil) + (make-overlay 32 53 nil nil nil) + (make-overlay 20 34 nil nil t) + (make-overlay 56 58 nil nil t) + (make-overlay 42 31 nil nil t) + (make-overlay 22 55 nil t t) + (make-overlay 55 11 nil t nil) + (goto-char 16) + (insert ".............") + (goto-char 30) + (insert ".") + (goto-char 12) + (delete-char 56) + (goto-char 9) + (insert ".............") + (goto-char 6) + (insert "....") + (goto-char 19) + (delete-char 19) + (goto-char 19) + (insert "...............") + (goto-char 13) + (delete-char 21) + (goto-char 7) + (delete-char 0) + (goto-char 14) + (widen) + (narrow-to-region 5 6) + (goto-char 5) + (delete-char 0) + (goto-char 6) + (insert "......") + (goto-char 10) + (delete-char 0) + (goto-char 7) + (widen) + (narrow-to-region 2 6) + (goto-char 2) + (insert "..........") + (goto-char 2) + (delete-char 9) + (goto-char 7) + (insert "...") + (goto-char 9) + (insert "...") + (goto-char 10) + (insert "......") + (goto-char 4) + (delete-char 14) + (goto-char 4) + (insert ".") + (goto-char 5) + (insert "..............") + (goto-char 13) + (insert "......") + (goto-char 10) + (insert "......") + (goto-char 20) + (insert "............") + (goto-char 16) + (widen) + (narrow-to-region 3 32) + (goto-char 18) + (insert "..") + (goto-char 6) + (insert "......") + (goto-char 38) + (delete-char 0) + (goto-char 31) + (insert "............") + (goto-char 28) + (insert "") + (goto-char 9) + (delete-char 23) + (should + (equal + (test-overlay-regions) + 'nil)))) + +(ert-deftest overlay-autogenerated-test-40 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 52 3 nil t nil) + (make-overlay 35 41 nil t t) + (make-overlay 4 2 nil t nil) + (make-overlay 51 48 nil nil t) + (make-overlay 44 57 nil t t) + (make-overlay 13 32 nil nil nil) + (make-overlay 46 29 nil t nil) + (make-overlay 28 13 nil t nil) + (make-overlay 10 65 nil t t) + (make-overlay 41 48 nil nil t) + (make-overlay 36 44 nil nil t) + (make-overlay 29 61 nil t nil) + (make-overlay 25 24 nil nil t) + (make-overlay 22 45 nil nil t) + (make-overlay 37 55 nil nil t) + (make-overlay 36 39 nil nil nil) + (goto-char 16) + (delete-char 48) + (goto-char 17) + (delete-char 0) + (goto-char 7) + (insert "..............") + (goto-char 30) + (insert "........") + (goto-char 11) + (insert "..........") + (goto-char 5) + (delete-char 14) + (goto-char 19) + (insert ".") + (goto-char 27) + (insert "..") + (goto-char 35) + (delete-char 1) + (goto-char 29) + (delete-char 0) + (goto-char 33) + (delete-char 2) + (goto-char 33) + (insert "..") + (goto-char 28) + (insert ".........") + (goto-char 30) + (delete-char 4) + (goto-char 40) + (delete-char 1) + (goto-char 15) + (widen) + (narrow-to-region 40 8) + (goto-char 10) + (delete-char 13) + (goto-char 11) + (delete-char 5) + (goto-char 15) + (insert "........") + (goto-char 26) + (delete-char 4) + (goto-char 11) + (delete-char 1) + (goto-char 14) + (insert "............") + (goto-char 33) + (insert ".") + (goto-char 10) + (insert "...") + (goto-char 30) + (widen) + (narrow-to-region 28 9) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (delete-char 1) + (goto-char 26) + (insert "..") + (goto-char 27) + (insert "..") + (goto-char 20) + (delete-char 5) + (goto-char 12) + (widen) + (narrow-to-region 40 30) + (goto-char 37) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((13 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (37 . 37) + (37 . 37)))))) + +(ert-deftest overlay-autogenerated-test-41 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 28 48 nil nil t) + (make-overlay 30 11 nil nil t) + (make-overlay 7 12 nil t nil) + (make-overlay 65 35 nil t nil) + (make-overlay 22 61 nil t nil) + (make-overlay 37 42 nil nil nil) + (make-overlay 33 38 nil nil t) + (make-overlay 48 45 nil t t) + (make-overlay 45 62 nil t nil) + (make-overlay 63 7 nil nil t) + (make-overlay 23 42 nil t nil) + (make-overlay 21 4 nil t nil) + (make-overlay 64 41 nil t nil) + (make-overlay 20 33 nil t t) + (make-overlay 41 26 nil t nil) + (make-overlay 43 31 nil t t) + (goto-char 55) + (delete-char 3) + (goto-char 12) + (insert "..") + (goto-char 62) + (insert "") + (goto-char 24) + (delete-char 2) + (goto-char 41) + (insert "............") + (goto-char 2) + (insert ".") + (goto-char 55) + (insert "........") + (goto-char 67) + (delete-char 6) + (goto-char 58) + (delete-char 10) + (goto-char 29) + (insert "") + (goto-char 6) + (widen) + (narrow-to-region 44 45) + (goto-char 44) + (delete-char 1) + (goto-char 44) + (widen) + (narrow-to-region 24 37) + (goto-char 30) + (delete-char 7) + (goto-char 27) + (insert "......") + (goto-char 35) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 37) + (delete-char 9) + (goto-char 40) + (insert "..........") + (goto-char 35) + (insert "......") + (goto-char 25) + (delete-char 7) + (goto-char 40) + (delete-char 4) + (goto-char 25) + (delete-char 14) + (goto-char 28) + (insert "") + (goto-char 28) + (widen) + (narrow-to-region 17 43) + (goto-char 20) + (insert "..........") + (goto-char 22) + (delete-char 2) + (goto-char 48) + (insert "............") + (goto-char 47) + (insert ".........") + (goto-char 69) + (widen) + (narrow-to-region 52 25) + (goto-char 26) + (insert "......") + (goto-char 53) + (insert "..") + (should + (equal + (test-overlay-regions) + '((5 . 38) + (8 . 97) + (12 . 47) + (37 . 47) + (39 . 52) + (39 . 87) + (39 . 95) + (46 . 90) + (47 . 49) + (47 . 90) + (47 . 99) + (48 . 87)))))) + +(ert-deftest overlay-autogenerated-test-42 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 23 nil nil nil) + (make-overlay 45 51 nil t nil) + (make-overlay 34 58 nil t nil) + (make-overlay 27 11 nil nil nil) + (make-overlay 14 8 nil t t) + (make-overlay 64 43 nil t nil) + (make-overlay 61 56 nil nil t) + (make-overlay 28 14 nil t nil) + (make-overlay 21 46 nil t t) + (make-overlay 30 34 nil t t) + (make-overlay 47 40 nil nil nil) + (make-overlay 5 44 nil t t) + (make-overlay 11 45 nil nil nil) + (make-overlay 65 8 nil nil t) + (make-overlay 47 54 nil t t) + (make-overlay 37 57 nil t nil) + (goto-char 11) + (insert "....") + (goto-char 65) + (delete-char 0) + (goto-char 56) + (delete-char 4) + (goto-char 11) + (delete-char 2) + (goto-char 23) + (insert ".............") + (goto-char 2) + (insert "............") + (goto-char 84) + (delete-char 1) + (goto-char 10) + (insert "..............") + (goto-char 19) + (insert "............") + (goto-char 69) + (delete-char 6) + (goto-char 15) + (insert "........") + (goto-char 104) + (insert "") + (goto-char 94) + (delete-char 11) + (goto-char 66) + (insert ".....") + (goto-char 67) + (insert "") + (goto-char 53) + (delete-char 22) + (goto-char 42) + (insert ".") + (goto-char 38) + (delete-char 13) + (goto-char 27) + (insert "......") + (goto-char 16) + (insert "............") + (goto-char 71) + (widen) + (narrow-to-region 59 15) + (goto-char 46) + (insert "..") + (goto-char 20) + (widen) + (narrow-to-region 95 93) + (goto-char 94) + (insert ".............") + (goto-char 103) + (widen) + (narrow-to-region 97 7) + (goto-char 93) + (insert "....") + (goto-char 85) + (insert "...........") + (goto-char 69) + (delete-char 24) + (goto-char 87) + (insert ".............") + (goto-char 7) + (delete-char 28) + (goto-char 65) + (delete-char 8) + (goto-char 48) + (insert "......") + (should + (equal + (test-overlay-regions) + '((31 . 44) + (33 . 33) + (33 . 41) + (33 . 41) + (33 . 41) + (33 . 41) + (33 . 82) + (40 . 44) + (41 . 41) + (41 . 41) + (41 . 47) + (41 . 48) + (44 . 45) + (44 . 46) + (44 . 63) + (46 . 57)))))) + +(ert-deftest overlay-autogenerated-test-43 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 53 nil t nil) + (make-overlay 11 50 nil t nil) + (make-overlay 1 30 nil nil nil) + (make-overlay 54 15 nil t t) + (make-overlay 22 30 nil nil nil) + (make-overlay 1 33 nil nil nil) + (make-overlay 18 15 nil t nil) + (make-overlay 43 39 nil nil t) + (make-overlay 43 17 nil t nil) + (make-overlay 2 29 nil t nil) + (make-overlay 57 42 nil t nil) + (make-overlay 40 1 nil nil nil) + (make-overlay 8 64 nil nil nil) + (make-overlay 64 15 nil nil nil) + (make-overlay 9 11 nil nil t) + (make-overlay 40 21 nil t nil) + (goto-char 5) + (delete-char 37) + (goto-char 25) + (delete-char 2) + (goto-char 17) + (insert "...........") + (goto-char 19) + (widen) + (narrow-to-region 20 20) + (goto-char 20) + (delete-char 0) + (goto-char 20) + (insert "..........") + (goto-char 24) + (delete-char 5) + (goto-char 24) + (insert "...") + (goto-char 28) + (widen) + (narrow-to-region 20 36) + (goto-char 26) + (delete-char 2) + (goto-char 31) + (insert ".............") + (goto-char 22) + (insert ".....") + (goto-char 38) + (delete-char 0) + (goto-char 31) + (delete-char 4) + (goto-char 27) + (insert "...") + (goto-char 23) + (widen) + (narrow-to-region 37 20) + (goto-char 22) + (insert ".............") + (goto-char 33) + (insert "......") + (goto-char 43) + (insert "............") + (goto-char 59) + (insert ".......") + (goto-char 25) + (delete-char 26) + (goto-char 49) + (insert ".........") + (goto-char 50) + (insert ".......") + (goto-char 39) + (widen) + (narrow-to-region 54 86) + (goto-char 64) + (insert "...............") + (goto-char 83) + (insert "............") + (goto-char 70) + (insert "........") + (goto-char 58) + (insert "..............") + (goto-char 83) + (insert "............") + (goto-char 83) + (insert "..........") + (goto-char 69) + (delete-char 75) + (goto-char 75) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((5 . 75) + (5 . 75) + (5 . 80) + (5 . 80)))))) + +(ert-deftest overlay-autogenerated-test-44 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 48 nil nil t) + (make-overlay 52 38 nil nil nil) + (make-overlay 3 63 nil nil nil) + (make-overlay 44 15 nil nil t) + (make-overlay 27 44 nil nil t) + (make-overlay 43 9 nil nil t) + (make-overlay 11 27 nil t nil) + (make-overlay 36 41 nil nil t) + (make-overlay 23 25 nil t t) + (make-overlay 19 60 nil t t) + (make-overlay 11 55 nil t nil) + (make-overlay 59 2 nil t nil) + (make-overlay 32 64 nil t nil) + (make-overlay 15 8 nil nil nil) + (make-overlay 61 15 nil nil nil) + (make-overlay 64 30 nil t t) + (goto-char 42) + (delete-char 20) + (goto-char 44) + (delete-char 1) + (goto-char 43) + (insert "...........") + (goto-char 43) + (delete-char 1) + (goto-char 28) + (delete-char 8) + (goto-char 37) + (delete-char 9) + (goto-char 4) + (delete-char 30) + (goto-char 6) + (delete-char 0) + (goto-char 7) + (delete-char 0) + (goto-char 2) + (delete-char 2) + (goto-char 5) + (delete-char 0) + (goto-char 5) + (delete-char 0) + (goto-char 2) + (insert ".....") + (goto-char 10) + (insert "...........") + (goto-char 21) + (insert "...") + (goto-char 10) + (delete-char 13) + (goto-char 9) + (insert "..........") + (goto-char 16) + (delete-char 1) + (goto-char 16) + (delete-char 4) + (goto-char 16) + (delete-char 0) + (goto-char 14) + (delete-char 1) + (goto-char 3) + (widen) + (narrow-to-region 2 9) + (goto-char 2) + (insert "") + (goto-char 2) + (insert ".............") + (goto-char 17) + (insert "....") + (goto-char 12) + (insert "........") + (goto-char 8) + (widen) + (narrow-to-region 32 23) + (goto-char 29) + (insert ".....") + (goto-char 35) + (delete-char 2) + (goto-char 27) + (delete-char 7) + (goto-char 23) + (widen) + (narrow-to-region 4 14) + (goto-char 8) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((2 . 43) + (2 . 43) + (2 . 43) + (2 . 43) + (2 . 43) + (2 . 44)))))) + +(ert-deftest overlay-autogenerated-test-45 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 48 nil nil nil) + (make-overlay 1 47 nil t nil) + (make-overlay 43 4 nil t t) + (make-overlay 9 45 nil t t) + (make-overlay 1 25 nil t t) + (make-overlay 5 46 nil t t) + (make-overlay 7 14 nil t nil) + (make-overlay 1 53 nil nil t) + (make-overlay 13 41 nil t nil) + (make-overlay 5 31 nil t t) + (make-overlay 26 10 nil nil nil) + (make-overlay 56 37 nil nil nil) + (make-overlay 23 15 nil t nil) + (make-overlay 62 30 nil t t) + (make-overlay 2 35 nil t t) + (make-overlay 46 41 nil nil nil) + (goto-char 65) + (delete-char 0) + (goto-char 55) + (insert "...........") + (goto-char 22) + (insert "") + (goto-char 73) + (delete-char 3) + (goto-char 43) + (widen) + (narrow-to-region 54 63) + (goto-char 56) + (insert "......") + (goto-char 61) + (delete-char 3) + (goto-char 65) + (insert "......") + (goto-char 66) + (insert ".....") + (goto-char 62) + (insert ".") + (goto-char 74) + (insert ".........") + (goto-char 76) + (delete-char 4) + (goto-char 56) + (widen) + (narrow-to-region 2 46) + (goto-char 43) + (insert "...........") + (goto-char 20) + (delete-char 4) + (goto-char 38) + (delete-char 7) + (goto-char 25) + (delete-char 21) + (goto-char 12) + (insert ".........") + (goto-char 19) + (widen) + (narrow-to-region 72 61) + (goto-char 63) + (insert "") + (goto-char 65) + (delete-char 4) + (goto-char 61) + (delete-char 5) + (goto-char 63) + (delete-char 0) + (goto-char 63) + (delete-char 0) + (goto-char 62) + (delete-char 0) + (goto-char 61) + (insert "............") + (goto-char 72) + (insert "..............") + (goto-char 62) + (delete-char 7) + (goto-char 71) + (delete-char 5) + (goto-char 75) + (widen) + (narrow-to-region 29 8) + (goto-char 17) + (delete-char 2) + (goto-char 27) + (insert "........") + (should + (equal + (test-overlay-regions) + '((1 . 36) + (1 . 41) + (1 . 47) + (2 . 40) + (4 . 40) + (5 . 40) + (5 . 40) + (7 . 21) + (9 . 40) + (10 . 37) + (20 . 40) + (22 . 27) + (22 . 42)))))) + +(ert-deftest overlay-autogenerated-test-46 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 2 43 nil nil t) + (make-overlay 44 40 nil nil t) + (make-overlay 49 14 nil nil t) + (make-overlay 6 55 nil nil nil) + (make-overlay 13 52 nil t t) + (make-overlay 40 54 nil t nil) + (make-overlay 51 41 nil nil t) + (make-overlay 7 28 nil nil t) + (make-overlay 10 47 nil nil t) + (make-overlay 63 21 nil t nil) + (make-overlay 4 55 nil nil nil) + (make-overlay 52 58 nil t nil) + (make-overlay 62 11 nil t t) + (make-overlay 22 49 nil t nil) + (make-overlay 23 65 nil nil nil) + (make-overlay 50 33 nil nil t) + (goto-char 22) + (insert "..............") + (goto-char 12) + (insert "....") + (goto-char 25) + (delete-char 16) + (goto-char 14) + (delete-char 53) + (goto-char 2) + (insert "............") + (goto-char 20) + (delete-char 5) + (goto-char 11) + (delete-char 7) + (goto-char 9) + (widen) + (narrow-to-region 11 7) + (goto-char 8) + (insert "...............") + (goto-char 12) + (delete-char 4) + (goto-char 21) + (insert "...") + (goto-char 20) + (delete-char 5) + (goto-char 7) + (delete-char 3) + (goto-char 16) + (delete-char 0) + (goto-char 12) + (delete-char 1) + (goto-char 15) + (delete-char 0) + (goto-char 7) + (insert "..............") + (goto-char 17) + (insert "...........") + (goto-char 15) + (insert "............") + (goto-char 20) + (delete-char 5) + (goto-char 7) + (insert "....") + (goto-char 37) + (delete-char 7) + (goto-char 8) + (insert "..........") + (goto-char 47) + (insert ".............") + (goto-char 65) + (insert ".......") + (goto-char 39) + (delete-char 26) + (goto-char 14) + (delete-char 2) + (goto-char 27) + (insert ".............") + (goto-char 17) + (widen) + (narrow-to-region 54 32) + (goto-char 40) + (widen) + (narrow-to-region 10 3) + (goto-char 7) + (insert "........") + (goto-char 13) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((2 . 85)))))) + +(ert-deftest overlay-autogenerated-test-47 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 62 nil t nil) + (make-overlay 14 38 nil nil nil) + (make-overlay 63 44 nil t t) + (make-overlay 41 41 nil nil t) + (make-overlay 19 39 nil nil nil) + (make-overlay 10 49 nil t t) + (make-overlay 56 38 nil t t) + (make-overlay 23 38 nil nil t) + (make-overlay 1 64 nil nil t) + (make-overlay 21 3 nil t nil) + (make-overlay 1 1 nil nil t) + (make-overlay 27 61 nil nil nil) + (make-overlay 29 59 nil nil nil) + (make-overlay 37 30 nil t nil) + (make-overlay 47 21 nil nil t) + (make-overlay 34 26 nil t nil) + (goto-char 6) + (delete-char 44) + (goto-char 8) + (delete-char 0) + (goto-char 8) + (insert "....") + (goto-char 17) + (delete-char 2) + (goto-char 12) + (insert "...") + (goto-char 20) + (insert "") + (goto-char 2) + (delete-char 20) + (goto-char 1) + (insert ".........") + (goto-char 7) + (insert ".............") + (goto-char 27) + (delete-char 0) + (goto-char 15) + (insert "..........") + (goto-char 36) + (insert "..............") + (goto-char 26) + (insert "..............") + (goto-char 63) + (insert "...........") + (goto-char 9) + (insert "............") + (goto-char 71) + (delete-char 17) + (goto-char 36) + (insert "....") + (goto-char 45) + (delete-char 31) + (goto-char 28) + (delete-char 8) + (goto-char 10) + (delete-char 16) + (goto-char 14) + (delete-char 4) + (goto-char 16) + (delete-char 0) + (goto-char 15) + (insert "") + (goto-char 14) + (delete-char 1) + (goto-char 10) + (delete-char 2) + (goto-char 6) + (delete-char 0) + (goto-char 1) + (insert ".........") + (goto-char 23) + (insert "......") + (goto-char 25) + (insert "..........") + (goto-char 25) + (widen) + (narrow-to-region 10 30) + (goto-char 21) + (delete-char 1) + (goto-char 17) + (insert "..........") + (should + (equal + (test-overlay-regions) + '((1 . 48) + (1 . 48) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 48) + (32 . 48) + (32 . 48)))))) + +(ert-deftest overlay-autogenerated-test-48 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 1 11 nil nil nil) + (make-overlay 35 29 nil t t) + (make-overlay 24 46 nil nil t) + (make-overlay 15 43 nil nil t) + (make-overlay 51 49 nil t t) + (make-overlay 25 43 nil t nil) + (make-overlay 23 59 nil nil nil) + (make-overlay 10 4 nil t nil) + (make-overlay 40 45 nil nil nil) + (make-overlay 42 43 nil nil t) + (make-overlay 20 38 nil t nil) + (make-overlay 17 49 nil nil nil) + (make-overlay 9 25 nil nil t) + (make-overlay 13 19 nil nil nil) + (make-overlay 44 31 nil t nil) + (make-overlay 12 65 nil nil t) + (goto-char 59) + (widen) + (narrow-to-region 28 14) + (goto-char 26) + (insert "...") + (goto-char 30) + (delete-char 1) + (goto-char 23) + (insert "...") + (goto-char 27) + (widen) + (narrow-to-region 45 67) + (goto-char 50) + (insert "...............") + (goto-char 59) + (insert "..............") + (goto-char 55) + (insert ".............") + (goto-char 106) + (delete-char 0) + (goto-char 97) + (delete-char 10) + (goto-char 67) + (delete-char 16) + (goto-char 76) + (insert "..............") + (goto-char 71) + (insert ".............") + (goto-char 110) + (delete-char 0) + (goto-char 56) + (delete-char 38) + (goto-char 61) + (delete-char 10) + (goto-char 56) + (delete-char 5) + (goto-char 49) + (insert ".......") + (goto-char 62) + (insert "...") + (goto-char 54) + (insert "..........") + (goto-char 47) + (delete-char 10) + (goto-char 47) + (delete-char 20) + (goto-char 46) + (insert ".............") + (goto-char 56) + (insert "...........") + (goto-char 70) + (delete-char 1) + (goto-char 62) + (widen) + (narrow-to-region 50 64) + (goto-char 60) + (insert "..") + (goto-char 55) + (delete-char 6) + (goto-char 60) + (insert ".............") + (goto-char 61) + (delete-char 9) + (goto-char 64) + (delete-char 0) + (goto-char 53) + (widen) + (narrow-to-region 15 62) + (should + (equal + (test-overlay-regions) + '((9 . 28) + (12 . 73) + (13 . 19) + (15 . 70) + (17 . 70) + (20 . 43) + (23 . 70) + (27 . 70) + (28 . 70) + (34 . 40) + (36 . 70) + (45 . 70)))))) + +(ert-deftest overlay-autogenerated-test-49 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 24 10 nil nil t) + (make-overlay 53 23 nil t nil) + (make-overlay 53 9 nil nil t) + (make-overlay 65 64 nil t t) + (make-overlay 48 2 nil nil t) + (make-overlay 12 58 nil nil t) + (make-overlay 64 64 nil nil nil) + (make-overlay 26 13 nil t t) + (make-overlay 46 26 nil nil t) + (make-overlay 28 59 nil t t) + (make-overlay 33 52 nil nil nil) + (make-overlay 39 8 nil t t) + (make-overlay 9 59 nil t t) + (make-overlay 50 45 nil nil t) + (make-overlay 41 53 nil nil t) + (make-overlay 51 51 nil t nil) + (goto-char 61) + (insert "..............") + (goto-char 19) + (widen) + (narrow-to-region 10 65) + (goto-char 65) + (delete-char 0) + (goto-char 11) + (insert "...............") + (goto-char 77) + (delete-char 0) + (goto-char 51) + (insert "...") + (goto-char 75) + (insert ".....") + (goto-char 77) + (delete-char 11) + (goto-char 45) + (delete-char 0) + (goto-char 24) + (widen) + (narrow-to-region 33 52) + (goto-char 46) + (insert "..............") + (goto-char 46) + (insert "..........") + (goto-char 39) + (widen) + (narrow-to-region 46 77) + (goto-char 77) + (insert "..............") + (goto-char 54) + (insert ".......") + (goto-char 87) + (insert ".") + (goto-char 70) + (delete-char 16) + (goto-char 79) + (delete-char 0) + (goto-char 73) + (widen) + (narrow-to-region 74 100) + (goto-char 91) + (insert ".............") + (goto-char 80) + (delete-char 11) + (goto-char 82) + (insert "......") + (goto-char 108) + (delete-char 0) + (goto-char 104) + (insert ".....") + (goto-char 100) + (delete-char 1) + (goto-char 90) + (insert ".............") + (goto-char 99) + (insert ".............") + (goto-char 124) + (insert "..............") + (goto-char 114) + (insert "....") + (goto-char 134) + (delete-char 0) + (goto-char 89) + (delete-char 65) + (goto-char 75) + (delete-char 16) + (should + (equal + (test-overlay-regions) + '((2 . 75) + (8 . 75) + (9 . 76) + (9 . 82) + (27 . 82) + (38 . 76) + (41 . 75) + (43 . 82) + (70 . 75)))))) + +(ert-deftest overlay-autogenerated-test-50 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 53 nil t t) + (make-overlay 65 64 nil nil nil) + (make-overlay 3 31 nil nil t) + (make-overlay 45 59 nil t nil) + (make-overlay 60 37 nil t t) + (make-overlay 7 5 nil t t) + (make-overlay 37 24 nil nil nil) + (make-overlay 45 20 nil nil nil) + (make-overlay 33 42 nil nil t) + (make-overlay 47 57 nil t nil) + (make-overlay 14 49 nil t t) + (make-overlay 14 30 nil t nil) + (make-overlay 21 40 nil t t) + (make-overlay 5 45 nil t t) + (make-overlay 59 40 nil t t) + (make-overlay 37 52 nil nil nil) + (goto-char 48) + (insert "") + (goto-char 7) + (insert ".........") + (goto-char 31) + (insert "...........") + (goto-char 41) + (delete-char 7) + (goto-char 21) + (delete-char 11) + (goto-char 41) + (widen) + (narrow-to-region 51 53) + (goto-char 52) + (insert ".....") + (goto-char 55) + (widen) + (narrow-to-region 18 24) + (goto-char 23) + (widen) + (narrow-to-region 39 38) + (goto-char 38) + (insert ".............") + (goto-char 41) + (insert "......") + (goto-char 38) + (insert "..............") + (goto-char 52) + (insert "...............") + (goto-char 78) + (delete-char 5) + (goto-char 50) + (insert "..........") + (goto-char 50) + (delete-char 3) + (goto-char 85) + (widen) + (narrow-to-region 86 1) + (goto-char 5) + (insert "....") + (goto-char 69) + (insert "...........") + (goto-char 94) + (insert "......") + (goto-char 98) + (delete-char 7) + (goto-char 46) + (insert "...............") + (goto-char 79) + (insert "............") + (goto-char 89) + (insert "") + (goto-char 14) + (delete-char 63) + (goto-char 20) + (insert ".........") + (goto-char 34) + (insert "...") + (goto-char 53) + (delete-char 14) + (goto-char 6) + (widen) + (narrow-to-region 6 52) + (goto-char 42) + (insert "...........") + (goto-char 40) + (insert ".......") + (goto-char 46) + (widen) + (narrow-to-region 1 68) + (should + (equal + (test-overlay-regions) + '((3 . 14) + (9 . 14) + (9 . 91) + (14 . 14) + (14 . 83) + (14 . 86) + (14 . 88) + (14 . 91) + (14 . 95) + (14 . 104)))))) + +(ert-deftest overlay-autogenerated-test-51 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 14 5 nil t nil) + (make-overlay 62 34 nil nil t) + (make-overlay 7 62 nil nil t) + (make-overlay 23 12 nil t t) + (make-overlay 16 4 nil nil nil) + (make-overlay 24 15 nil nil nil) + (make-overlay 6 6 nil t t) + (make-overlay 25 64 nil t t) + (make-overlay 23 6 nil t t) + (make-overlay 55 64 nil nil nil) + (make-overlay 8 62 nil nil t) + (make-overlay 65 65 nil nil nil) + (make-overlay 57 51 nil t t) + (make-overlay 35 8 nil t nil) + (make-overlay 55 13 nil nil t) + (make-overlay 60 62 nil nil t) + (goto-char 12) + (insert "..") + (goto-char 66) + (insert "............") + (goto-char 32) + (insert "..") + (goto-char 27) + (insert ".........") + (goto-char 8) + (insert ".............") + (goto-char 79) + (insert ".") + (goto-char 47) + (insert "....") + (goto-char 49) + (insert "...") + (goto-char 81) + (insert "....") + (goto-char 112) + (delete-char 0) + (goto-char 97) + (insert ".....") + (goto-char 109) + (delete-char 5) + (goto-char 20) + (insert ".....") + (goto-char 59) + (delete-char 33) + (goto-char 87) + (insert ".............") + (goto-char 98) + (insert "....") + (goto-char 22) + (delete-char 36) + (goto-char 45) + (insert "..............") + (goto-char 42) + (delete-char 29) + (goto-char 51) + (widen) + (narrow-to-region 39 41) + (goto-char 39) + (delete-char 2) + (goto-char 39) + (insert ".............") + (goto-char 51) + (insert "......") + (goto-char 52) + (insert "...............") + (goto-char 56) + (widen) + (narrow-to-region 59 20) + (goto-char 56) + (insert "............") + (goto-char 57) + (insert ".") + (goto-char 37) + (delete-char 12) + (goto-char 39) + (delete-char 11) + (goto-char 38) + (delete-char 8) + (goto-char 36) + (widen) + (narrow-to-region 65 26) + (goto-char 40) + (widen) + (narrow-to-region 27 55) + (should + (equal + (test-overlay-regions) + '((7 . 55) + (8 . 55) + (22 . 29) + (23 . 55) + (23 . 56) + (24 . 31) + (29 . 56) + (37 . 55)))))) + +(ert-deftest overlay-autogenerated-test-52 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 32 nil nil nil) + (make-overlay 44 54 nil nil t) + (make-overlay 27 50 nil nil nil) + (make-overlay 55 35 nil nil t) + (make-overlay 40 46 nil nil t) + (make-overlay 56 63 nil t nil) + (make-overlay 29 48 nil nil nil) + (make-overlay 45 24 nil t nil) + (make-overlay 60 25 nil t nil) + (make-overlay 55 41 nil t nil) + (make-overlay 55 1 nil nil t) + (make-overlay 30 45 nil t t) + (make-overlay 26 19 nil nil t) + (make-overlay 61 5 nil nil nil) + (make-overlay 33 5 nil nil nil) + (make-overlay 42 18 nil t nil) + (goto-char 55) + (insert ".") + (goto-char 49) + (delete-char 12) + (goto-char 41) + (insert "..........") + (goto-char 27) + (insert ".....") + (goto-char 58) + (insert "...........") + (goto-char 24) + (delete-char 23) + (goto-char 47) + (delete-char 9) + (goto-char 4) + (insert "...") + (goto-char 10) + (delete-char 32) + (goto-char 4) + (insert "..............") + (goto-char 29) + (insert "....") + (goto-char 28) + (delete-char 2) + (goto-char 34) + (insert "...........") + (goto-char 9) + (insert "......") + (goto-char 5) + (insert "") + (goto-char 45) + (delete-char 1) + (goto-char 18) + (insert ".........") + (goto-char 36) + (delete-char 5) + (goto-char 15) + (delete-char 27) + (goto-char 15) + (delete-char 10) + (goto-char 16) + (delete-char 2) + (goto-char 16) + (widen) + (narrow-to-region 10 2) + (goto-char 9) + (delete-char 1) + (goto-char 3) + (delete-char 2) + (goto-char 2) + (widen) + (narrow-to-region 9 10) + (goto-char 9) + (insert "...........") + (goto-char 19) + (delete-char 0) + (goto-char 14) + (delete-char 3) + (goto-char 11) + (delete-char 2) + (goto-char 9) + (delete-char 6) + (goto-char 9) + (delete-char 0) + (goto-char 10) + (insert "....") + (should + (equal + (test-overlay-regions) + '((1 . 17)))))) + +(ert-deftest overlay-autogenerated-test-53 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 10 30 nil nil nil) + (make-overlay 11 57 nil t nil) + (make-overlay 59 56 nil nil t) + (make-overlay 20 37 nil nil t) + (make-overlay 41 29 nil nil nil) + (make-overlay 31 10 nil nil t) + (make-overlay 6 36 nil nil nil) + (make-overlay 12 54 nil nil nil) + (make-overlay 25 26 nil t t) + (make-overlay 21 19 nil nil t) + (make-overlay 1 21 nil nil t) + (make-overlay 48 51 nil nil nil) + (make-overlay 54 55 nil t nil) + (make-overlay 64 48 nil t t) + (make-overlay 56 25 nil nil t) + (make-overlay 12 60 nil t nil) + (goto-char 41) + (delete-char 1) + (goto-char 63) + (insert "") + (goto-char 14) + (delete-char 5) + (goto-char 11) + (insert "..............") + (goto-char 41) + (widen) + (narrow-to-region 12 1) + (goto-char 1) + (delete-char 3) + (goto-char 9) + (delete-char 0) + (goto-char 5) + (insert "..............") + (goto-char 1) + (insert "..........") + (goto-char 29) + (insert "...............") + (goto-char 4) + (insert "..") + (goto-char 31) + (delete-char 15) + (goto-char 31) + (insert "") + (goto-char 27) + (insert "......") + (goto-char 6) + (insert "...") + (goto-char 23) + (widen) + (narrow-to-region 23 47) + (goto-char 37) + (delete-char 2) + (goto-char 35) + (delete-char 5) + (goto-char 38) + (delete-char 2) + (goto-char 30) + (insert ".......") + (goto-char 45) + (widen) + (narrow-to-region 13 2) + (goto-char 9) + (delete-char 1) + (goto-char 3) + (insert ".....") + (goto-char 2) + (insert "...............") + (goto-char 16) + (delete-char 5) + (goto-char 20) + (insert ".....") + (goto-char 26) + (delete-char 0) + (goto-char 26) + (widen) + (narrow-to-region 76 98) + (goto-char 88) + (insert ".........") + (goto-char 92) + (insert ".") + (goto-char 108) + (delete-char 0) + (goto-char 103) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((1 . 79) + (37 . 103) + (61 . 88) + (61 . 99) + (74 . 121) + (75 . 118) + (75 . 124) + (77 . 79) + (78 . 103) + (83 . 84) + (83 . 120) + (87 . 106)))))) + +(ert-deftest overlay-autogenerated-test-54 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 36 nil t t) + (make-overlay 55 49 nil nil t) + (make-overlay 12 25 nil nil t) + (make-overlay 16 37 nil t t) + (make-overlay 42 25 nil t t) + (make-overlay 8 41 nil t t) + (make-overlay 13 27 nil nil t) + (make-overlay 52 22 nil t nil) + (make-overlay 36 17 nil t nil) + (make-overlay 1 52 nil t nil) + (make-overlay 55 5 nil nil t) + (make-overlay 50 50 nil t nil) + (make-overlay 32 15 nil t nil) + (make-overlay 39 26 nil t nil) + (make-overlay 26 4 nil nil nil) + (make-overlay 38 47 nil t t) + (goto-char 23) + (insert ".") + (goto-char 57) + (delete-char 6) + (goto-char 54) + (insert "..............") + (goto-char 46) + (insert "...............") + (goto-char 29) + (insert ".......") + (goto-char 58) + (delete-char 21) + (goto-char 45) + (delete-char 4) + (goto-char 50) + (delete-char 4) + (goto-char 20) + (insert ".........") + (goto-char 16) + (insert "......") + (goto-char 17) + (insert ".....") + (goto-char 63) + (insert "........") + (goto-char 83) + (insert "....") + (goto-char 73) + (delete-char 8) + (goto-char 69) + (insert "...........") + (goto-char 48) + (widen) + (narrow-to-region 19 31) + (goto-char 22) + (delete-char 3) + (goto-char 23) + (delete-char 5) + (goto-char 20) + (insert "............") + (goto-char 23) + (delete-char 11) + (goto-char 19) + (insert "..........") + (goto-char 23) + (insert "........") + (goto-char 38) + (delete-char 1) + (goto-char 33) + (delete-char 5) + (goto-char 27) + (insert "..........") + (goto-char 35) + (delete-char 8) + (goto-char 35) + (insert ".") + (goto-char 20) + (insert "......") + (goto-char 22) + (delete-char 22) + (goto-char 23) + (delete-char 0) + (goto-char 22) + (widen) + (narrow-to-region 1 41) + (goto-char 13) + (insert ".......") + (should + (equal + (test-overlay-regions) + '((1 . 83) + (4 . 46) + (5 . 97) + (8 . 83) + (12 . 45) + (13 . 47) + (22 . 59) + (30 . 82) + (30 . 83) + (41 . 83) + (45 . 83) + (46 . 83)))))) + +(ert-deftest overlay-autogenerated-test-55 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 20 nil nil nil) + (make-overlay 60 33 nil t nil) + (make-overlay 6 27 nil nil nil) + (make-overlay 53 31 nil nil t) + (make-overlay 30 55 nil t t) + (make-overlay 4 64 nil t t) + (make-overlay 51 31 nil nil t) + (make-overlay 4 65 nil t t) + (make-overlay 57 62 nil t t) + (make-overlay 28 7 nil nil t) + (make-overlay 61 48 nil t nil) + (make-overlay 23 54 nil nil t) + (make-overlay 47 49 nil nil nil) + (make-overlay 12 52 nil t nil) + (make-overlay 39 57 nil t t) + (make-overlay 28 61 nil nil t) + (goto-char 8) + (insert "..............") + (goto-char 63) + (delete-char 3) + (goto-char 67) + (delete-char 6) + (goto-char 3) + (widen) + (narrow-to-region 10 67) + (goto-char 43) + (insert ".............") + (goto-char 20) + (insert "...............") + (goto-char 18) + (insert "..") + (goto-char 37) + (delete-char 47) + (goto-char 34) + (insert "..............") + (goto-char 31) + (delete-char 2) + (goto-char 16) + (widen) + (narrow-to-region 29 36) + (goto-char 31) + (delete-char 2) + (goto-char 31) + (insert ".......") + (goto-char 40) + (delete-char 0) + (goto-char 32) + (widen) + (narrow-to-region 40 19) + (goto-char 40) + (insert "..") + (goto-char 37) + (delete-char 0) + (goto-char 40) + (delete-char 1) + (goto-char 34) + (delete-char 4) + (goto-char 33) + (insert "..............") + (goto-char 19) + (widen) + (narrow-to-region 78 70) + (goto-char 77) + (insert ".........") + (goto-char 80) + (delete-char 1) + (goto-char 73) + (delete-char 3) + (goto-char 70) + (insert ".........") + (goto-char 75) + (delete-char 10) + (goto-char 74) + (delete-char 3) + (goto-char 73) + (insert "...............") + (goto-char 90) + (insert "......") + (goto-char 94) + (insert "..............") + (goto-char 101) + (insert "........") + (goto-char 111) + (insert "........") + (should + (equal + (test-overlay-regions) + '((4 . 132) + (4 . 133) + (65 . 89) + (65 . 89) + (65 . 89) + (65 . 89) + (65 . 129) + (65 . 130) + (65 . 130) + (65 . 130) + (65 . 130) + (89 . 89) + (89 . 130)))))) + +(ert-deftest overlay-autogenerated-test-56 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 7 14 nil nil t) + (make-overlay 10 10 nil nil t) + (make-overlay 21 23 nil nil t) + (make-overlay 4 44 nil t nil) + (make-overlay 42 16 nil t t) + (make-overlay 1 57 nil t nil) + (make-overlay 15 27 nil nil nil) + (make-overlay 31 1 nil t nil) + (make-overlay 56 45 nil t t) + (make-overlay 46 19 nil t nil) + (make-overlay 15 6 nil nil nil) + (make-overlay 31 26 nil nil t) + (make-overlay 39 41 nil t t) + (make-overlay 52 48 nil nil t) + (make-overlay 44 2 nil t nil) + (make-overlay 60 7 nil nil t) + (goto-char 49) + (delete-char 11) + (goto-char 43) + (delete-char 9) + (goto-char 42) + (delete-char 2) + (goto-char 12) + (insert "...........") + (goto-char 36) + (insert ".........") + (goto-char 1) + (insert "......") + (goto-char 67) + (delete-char 0) + (goto-char 47) + (insert ".............") + (goto-char 57) + (insert "........") + (goto-char 22) + (widen) + (narrow-to-region 75 33) + (goto-char 41) + (delete-char 28) + (goto-char 43) + (delete-char 0) + (goto-char 33) + (delete-char 5) + (goto-char 38) + (insert "..") + (goto-char 42) + (delete-char 0) + (goto-char 38) + (delete-char 0) + (goto-char 38) + (insert "............") + (goto-char 51) + (insert ".......") + (goto-char 48) + (insert "..") + (goto-char 55) + (insert ".") + (goto-char 33) + (delete-char 8) + (goto-char 42) + (insert "..") + (goto-char 45) + (insert "..") + (goto-char 59) + (insert ".............") + (goto-char 53) + (insert ".......") + (goto-char 81) + (delete-char 0) + (goto-char 44) + (delete-char 36) + (goto-char 38) + (delete-char 8) + (goto-char 33) + (insert ".............") + (goto-char 41) + (insert "..............") + (goto-char 65) + (insert "...............") + (goto-char 61) + (insert "...") + (should + (equal + (test-overlay-regions) + '((7 . 86) + (7 . 97) + (8 . 97) + (10 . 97) + (13 . 97) + (32 . 68) + (33 . 60) + (60 . 97) + (60 . 97) + (68 . 86)))))) + +(ert-deftest overlay-autogenerated-test-57 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 52 31 nil t nil) + (make-overlay 39 17 nil t nil) + (make-overlay 19 20 nil t t) + (make-overlay 18 3 nil nil t) + (make-overlay 19 47 nil nil t) + (make-overlay 38 54 nil nil nil) + (make-overlay 30 51 nil nil t) + (make-overlay 29 60 nil t t) + (make-overlay 57 38 nil nil nil) + (make-overlay 13 41 nil t nil) + (make-overlay 9 44 nil t nil) + (make-overlay 30 55 nil t nil) + (make-overlay 33 10 nil nil nil) + (make-overlay 14 35 nil nil t) + (make-overlay 53 50 nil t nil) + (make-overlay 25 28 nil nil t) + (goto-char 40) + (insert "..") + (goto-char 64) + (insert "........") + (goto-char 47) + (insert "............") + (goto-char 65) + (delete-char 0) + (goto-char 86) + (delete-char 1) + (goto-char 59) + (delete-char 11) + (goto-char 64) + (delete-char 8) + (goto-char 53) + (delete-char 0) + (goto-char 28) + (delete-char 8) + (goto-char 6) + (delete-char 33) + (goto-char 14) + (delete-char 2) + (goto-char 2) + (delete-char 10) + (goto-char 3) + (insert "..") + (goto-char 5) + (insert ".........") + (goto-char 1) + (insert "........") + (goto-char 10) + (delete-char 4) + (goto-char 26) + (insert "........") + (goto-char 23) + (insert "....") + (goto-char 1) + (widen) + (narrow-to-region 15 23) + (goto-char 19) + (insert "...") + (goto-char 24) + (delete-char 0) + (goto-char 19) + (insert ".......") + (goto-char 18) + (insert "..") + (goto-char 33) + (insert "...") + (goto-char 32) + (insert "...............") + (goto-char 29) + (delete-char 10) + (goto-char 29) + (insert "..........") + (goto-char 50) + (insert "") + (goto-char 16) + (insert ".........") + (goto-char 52) + (widen) + (narrow-to-region 59 15) + (goto-char 35) + (delete-char 4) + (goto-char 18) + (insert "....") + (should + (equal + (test-overlay-regions) + '((10 . 57) + (10 . 57) + (10 . 57) + (10 . 60) + (10 . 60) + (10 . 61) + (10 . 68) + (57 . 57)))))) + +(ert-deftest overlay-autogenerated-test-58 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 16 nil t nil) + (make-overlay 57 27 nil nil nil) + (make-overlay 15 62 nil nil nil) + (make-overlay 32 33 nil nil t) + (make-overlay 47 27 nil nil t) + (make-overlay 41 4 nil nil t) + (make-overlay 57 61 nil t nil) + (make-overlay 18 43 nil nil t) + (make-overlay 64 51 nil t t) + (make-overlay 44 26 nil nil nil) + (make-overlay 9 13 nil nil t) + (make-overlay 41 65 nil nil t) + (make-overlay 23 13 nil t t) + (make-overlay 26 59 nil t t) + (make-overlay 65 65 nil t t) + (make-overlay 15 7 nil nil nil) + (goto-char 41) + (insert "........") + (goto-char 35) + (delete-char 14) + (goto-char 32) + (widen) + (narrow-to-region 23 46) + (goto-char 41) + (delete-char 5) + (goto-char 29) + (delete-char 10) + (goto-char 31) + (insert ".") + (goto-char 29) + (insert "........") + (goto-char 27) + (delete-char 7) + (goto-char 29) + (insert "") + (goto-char 24) + (insert "............") + (goto-char 43) + (delete-char 1) + (goto-char 31) + (delete-char 9) + (goto-char 34) + (widen) + (narrow-to-region 20 14) + (goto-char 20) + (delete-char 0) + (goto-char 17) + (insert "...........") + (goto-char 31) + (delete-char 0) + (goto-char 16) + (insert "...........") + (goto-char 17) + (delete-char 8) + (goto-char 23) + (delete-char 5) + (goto-char 20) + (insert "..........") + (goto-char 33) + (widen) + (narrow-to-region 16 29) + (goto-char 24) + (insert "...............") + (goto-char 44) + (delete-char 0) + (goto-char 30) + (insert "....") + (goto-char 27) + (widen) + (narrow-to-region 4 22) + (goto-char 10) + (insert "..............") + (goto-char 36) + (insert "..") + (goto-char 10) + (delete-char 21) + (goto-char 14) + (delete-char 1) + (goto-char 14) + (insert "...........") + (goto-char 12) + (insert "........") + (goto-char 32) + (insert "........") + (should + (equal + (test-overlay-regions) + '((4 . 92) + (7 . 10) + (8 . 10) + (9 . 10) + (10 . 82) + (10 . 104)))))) + +(ert-deftest overlay-autogenerated-test-59 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 46 30 nil t t) + (make-overlay 3 26 nil nil nil) + (make-overlay 36 28 nil t t) + (make-overlay 49 49 nil t t) + (make-overlay 27 61 nil t nil) + (make-overlay 14 16 nil nil nil) + (make-overlay 50 61 nil t nil) + (make-overlay 59 63 nil nil nil) + (make-overlay 36 34 nil t nil) + (make-overlay 35 29 nil nil nil) + (make-overlay 5 65 nil nil nil) + (make-overlay 20 61 nil nil t) + (make-overlay 10 42 nil nil nil) + (make-overlay 47 49 nil nil t) + (make-overlay 12 4 nil nil nil) + (make-overlay 32 24 nil t t) + (goto-char 11) + (insert ".") + (goto-char 32) + (delete-char 2) + (goto-char 61) + (insert ".........") + (goto-char 36) + (insert "........") + (goto-char 55) + (widen) + (narrow-to-region 8 55) + (goto-char 21) + (insert "....") + (goto-char 32) + (delete-char 15) + (goto-char 30) + (delete-char 5) + (goto-char 31) + (insert "......") + (goto-char 18) + (insert "..") + (goto-char 14) + (insert ".............") + (goto-char 34) + (insert "............") + (goto-char 51) + (widen) + (narrow-to-region 58 31) + (goto-char 50) + (delete-char 5) + (goto-char 53) + (insert ".........") + (goto-char 56) + (insert "...............") + (goto-char 45) + (delete-char 1) + (goto-char 67) + (insert "............") + (goto-char 84) + (insert "") + (goto-char 39) + (delete-char 27) + (goto-char 39) + (delete-char 21) + (goto-char 32) + (insert "............") + (goto-char 36) + (widen) + (narrow-to-region 7 37) + (goto-char 11) + (insert ".......") + (goto-char 21) + (delete-char 13) + (goto-char 15) + (insert "....") + (goto-char 9) + (insert ".............") + (goto-char 13) + (delete-char 21) + (goto-char 21) + (delete-char 6) + (goto-char 16) + (insert ".......") + (goto-char 22) + (insert "") + (goto-char 27) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((3 . 42) + (4 . 16) + (5 . 83) + (13 . 51) + (25 . 27)))))) + +(ert-deftest overlay-autogenerated-test-60 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 38 32 nil t nil) + (make-overlay 32 42 nil t nil) + (make-overlay 29 11 nil nil t) + (make-overlay 52 22 nil t t) + (make-overlay 39 59 nil t nil) + (make-overlay 41 30 nil t t) + (make-overlay 29 61 nil nil t) + (make-overlay 11 45 nil nil nil) + (make-overlay 46 17 nil nil t) + (make-overlay 35 51 nil t t) + (make-overlay 22 13 nil nil t) + (make-overlay 52 34 nil nil t) + (make-overlay 59 4 nil nil t) + (make-overlay 8 22 nil nil nil) + (make-overlay 4 49 nil nil nil) + (make-overlay 52 45 nil t t) + (goto-char 48) + (delete-char 16) + (goto-char 37) + (delete-char 8) + (goto-char 14) + (insert "...............") + (goto-char 40) + (delete-char 16) + (goto-char 19) + (insert ".........") + (goto-char 16) + (insert "......") + (goto-char 10) + (insert "........") + (goto-char 11) + (insert "...............") + (goto-char 22) + (insert ".") + (goto-char 62) + (delete-char 16) + (goto-char 14) + (delete-char 11) + (goto-char 47) + (insert "....") + (goto-char 33) + (insert ".............") + (goto-char 49) + (delete-char 13) + (goto-char 28) + (insert "..") + (goto-char 35) + (delete-char 13) + (goto-char 44) + (insert "....") + (goto-char 34) + (delete-char 14) + (goto-char 23) + (insert ".....") + (goto-char 25) + (delete-char 4) + (goto-char 33) + (insert ".....") + (goto-char 27) + (delete-char 3) + (goto-char 16) + (widen) + (narrow-to-region 36 37) + (goto-char 36) + (delete-char 1) + (goto-char 36) + (insert ".......") + (goto-char 37) + (widen) + (narrow-to-region 35 31) + (goto-char 34) + (delete-char 0) + (goto-char 31) + (delete-char 2) + (goto-char 31) + (widen) + (narrow-to-region 24 3) + (goto-char 22) + (delete-char 2) + (goto-char 22) + (insert ".............") + (goto-char 4) + (insert ".") + (should + (equal + (test-overlay-regions) + '((4 . 54) + (4 . 54) + (9 . 46)))))) + +(ert-deftest overlay-autogenerated-test-61 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 45 56 nil t nil) + (make-overlay 60 45 nil nil nil) + (make-overlay 26 8 nil t t) + (make-overlay 63 39 nil nil nil) + (make-overlay 18 11 nil t nil) + (make-overlay 22 64 nil nil t) + (make-overlay 8 41 nil nil t) + (make-overlay 6 51 nil t t) + (make-overlay 38 26 nil t t) + (make-overlay 7 46 nil t nil) + (make-overlay 2 42 nil nil t) + (make-overlay 44 64 nil nil nil) + (make-overlay 7 62 nil t nil) + (make-overlay 8 40 nil nil t) + (make-overlay 62 36 nil t t) + (make-overlay 61 27 nil nil nil) + (goto-char 21) + (delete-char 0) + (goto-char 8) + (insert "") + (goto-char 55) + (insert "......") + (goto-char 38) + (delete-char 25) + (goto-char 37) + (delete-char 4) + (goto-char 12) + (delete-char 4) + (goto-char 3) + (delete-char 26) + (goto-char 10) + (insert ".......") + (goto-char 18) + (delete-char 0) + (goto-char 16) + (insert ".............") + (goto-char 18) + (delete-char 3) + (goto-char 7) + (insert "...") + (goto-char 20) + (insert "........") + (goto-char 38) + (delete-char 0) + (goto-char 1) + (delete-char 36) + (goto-char 3) + (delete-char 1) + (goto-char 2) + (insert "......") + (goto-char 4) + (insert ".......") + (goto-char 2) + (insert "...........") + (goto-char 27) + (insert ".....") + (goto-char 15) + (insert "...............") + (goto-char 2) + (insert "......") + (goto-char 17) + (delete-char 8) + (goto-char 15) + (delete-char 7) + (goto-char 33) + (delete-char 5) + (goto-char 13) + (insert "...........") + (goto-char 34) + (insert "...............") + (goto-char 33) + (insert "") + (goto-char 51) + (insert "....") + (goto-char 14) + (delete-char 36) + (goto-char 16) + (delete-char 1) + (goto-char 14) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 18) + (1 . 18)))))) + +(ert-deftest overlay-autogenerated-test-62 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 25 36 nil t nil) + (make-overlay 38 6 nil t nil) + (make-overlay 40 63 nil nil t) + (make-overlay 34 23 nil nil nil) + (make-overlay 48 46 nil nil nil) + (make-overlay 43 57 nil t t) + (make-overlay 6 53 nil t t) + (make-overlay 37 27 nil t t) + (make-overlay 8 39 nil t nil) + (make-overlay 62 6 nil nil nil) + (make-overlay 51 6 nil t t) + (make-overlay 58 11 nil nil t) + (make-overlay 19 25 nil t nil) + (make-overlay 13 8 nil nil nil) + (make-overlay 19 8 nil nil t) + (make-overlay 39 5 nil t t) + (goto-char 51) + (delete-char 5) + (goto-char 16) + (delete-char 9) + (goto-char 18) + (insert "") + (goto-char 47) + (delete-char 4) + (goto-char 24) + (insert ".........") + (goto-char 24) + (insert ".....") + (goto-char 18) + (insert "...........") + (goto-char 5) + (delete-char 6) + (goto-char 30) + (insert "...........") + (goto-char 8) + (insert ".............") + (goto-char 78) + (insert "............") + (goto-char 67) + (insert "") + (goto-char 58) + (insert "") + (goto-char 5) + (insert ".") + (goto-char 79) + (widen) + (narrow-to-region 51 55) + (goto-char 51) + (insert "....") + (goto-char 58) + (widen) + (narrow-to-region 36 37) + (goto-char 37) + (insert "....") + (goto-char 40) + (insert ".......") + (goto-char 47) + (delete-char 1) + (goto-char 43) + (delete-char 4) + (goto-char 37) + (insert "........") + (goto-char 49) + (insert "............") + (goto-char 42) + (widen) + (narrow-to-region 75 111) + (goto-char 104) + (widen) + (narrow-to-region 21 95) + (goto-char 22) + (widen) + (narrow-to-region 64 79) + (goto-char 64) + (delete-char 0) + (goto-char 68) + (insert "........") + (goto-char 82) + (insert "") + (goto-char 81) + (insert "........") + (goto-char 92) + (delete-char 2) + (goto-char 87) + (insert ".") + (should + (equal + (test-overlay-regions) + '((5 . 145) + (5 . 148) + (6 . 118) + (6 . 119) + (6 . 119) + (6 . 143) + (6 . 143) + (24 . 114) + (24 . 116) + (63 . 117)))))) + +(ert-deftest overlay-autogenerated-test-63 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 9 49 nil t nil) + (make-overlay 9 16 nil nil nil) + (make-overlay 64 2 nil t t) + (make-overlay 17 31 nil nil t) + (make-overlay 24 51 nil nil nil) + (make-overlay 27 56 nil t t) + (make-overlay 21 4 nil nil nil) + (make-overlay 24 29 nil t t) + (make-overlay 4 63 nil nil t) + (make-overlay 34 49 nil t nil) + (make-overlay 19 47 nil nil t) + (make-overlay 8 50 nil t nil) + (make-overlay 49 61 nil t nil) + (make-overlay 52 10 nil t t) + (make-overlay 64 30 nil t nil) + (make-overlay 5 13 nil t nil) + (goto-char 27) + (insert "........") + (goto-char 42) + (insert "......") + (goto-char 48) + (insert "....") + (goto-char 55) + (widen) + (narrow-to-region 10 5) + (goto-char 8) + (insert ".............") + (goto-char 19) + (insert "......") + (goto-char 19) + (delete-char 3) + (goto-char 8) + (delete-char 3) + (goto-char 9) + (insert ".......") + (goto-char 29) + (insert "...............") + (goto-char 38) + (insert ".......") + (goto-char 34) + (insert "......") + (goto-char 28) + (delete-char 20) + (goto-char 22) + (insert "............") + (goto-char 21) + (delete-char 23) + (goto-char 25) + (delete-char 2) + (goto-char 19) + (delete-char 2) + (goto-char 12) + (delete-char 6) + (goto-char 12) + (delete-char 0) + (goto-char 13) + (delete-char 0) + (goto-char 12) + (insert "........") + (goto-char 23) + (delete-char 2) + (goto-char 5) + (insert "...............") + (goto-char 28) + (delete-char 0) + (goto-char 16) + (insert "..........") + (goto-char 8) + (delete-char 17) + (goto-char 27) + (delete-char 0) + (goto-char 12) + (insert ".") + (goto-char 14) + (delete-char 12) + (goto-char 11) + (insert "..............") + (goto-char 34) + (insert "") + (goto-char 25) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((2 . 98) + (4 . 37) + (4 . 97) + (25 . 29) + (25 . 32) + (25 . 84)))))) + ;;; buffer-tests.el ends here From 7c0824a1d04d660c86c78143d5485af174ee0c6b Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Thu, 5 Oct 2017 19:24:55 +0200 Subject: [PATCH 02/70] Add test regarding overlay and buffer encoding * test/src/buffer-tests.el (test-overlay-multibyte-transition-1): New test exposing a bug regarding overlays when changing the multibyteness of a buffer. --- test/src/buffer-tests.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 71f8705c6fd..e3bc2c5dfcb 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1212,6 +1212,14 @@ with parameters from the *Messages* buffer modification." (54 . 135) (68 . 99)))))) +(ert-deftest test-overlay-multibyte-transition-1 () + (with-temp-buffer + (set-buffer-multibyte t) + (insert "Ä") + (let ((ov (make-overlay (point-min) (point-max)))) + (set-buffer-multibyte nil) + (should (eq (overlay-end ov) (point-max)))))) + ;; +===================================================================================+ ;; | Autogenerated insert/delete/narrow tests From 342fc823a6622f25c79baa8535cb9ea939ef666f Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Thu, 5 Oct 2017 20:40:31 +0200 Subject: [PATCH 03/70] Add test exposing overlay bug * test/src/buffer-tests.el (overlay-autogenerated-test-64): New test. --- test/src/buffer-tests.el | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index e3bc2c5dfcb..c2c9d060336 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -7577,4 +7577,105 @@ with parameters from the *Messages* buffer modification." (25 . 32) (25 . 84)))))) +(ert-deftest overlay-autogenerated-test-64 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 31 10 nil nil nil) + (make-overlay 17 58 nil nil t) + (make-overlay 20 21 nil t nil) + (make-overlay 3 47 nil t t) + (make-overlay 47 43 nil t t) + (make-overlay 54 8 nil nil t) + (make-overlay 51 26 nil t nil) + (make-overlay 60 14 nil t nil) + (make-overlay 38 6 nil nil t) + (make-overlay 41 9 nil nil nil) + (make-overlay 44 38 nil nil t) + (make-overlay 55 48 nil nil t) + (make-overlay 10 41 nil nil t) + (make-overlay 35 49 nil t nil) + (make-overlay 50 46 nil nil nil) + (make-overlay 28 28 nil t nil) + (goto-char 59) + (delete-char 3) + (goto-char 28) + (widen) + (narrow-to-region 13 7) + (goto-char 11) + (insert ".") + (goto-char 9) + (delete-char 3) + (goto-char 8) + (delete-char 0) + (goto-char 7) + (insert ".............") + (goto-char 9) + (insert "..........") + (goto-char 22) + (delete-char 1) + (goto-char 31) + (delete-char 2) + (goto-char 22) + (insert ".........") + (goto-char 33) + (delete-char 1) + (goto-char 29) + (widen) + (narrow-to-region 59 51) + (goto-char 52) + (insert ".........") + (goto-char 53) + (insert "........") + (goto-char 53) + (delete-char 4) + (goto-char 54) + (insert "........") + (goto-char 53) + (insert "....") + (goto-char 75) + (widen) + (goto-char 70) + (delete-char 2) + (goto-char 108) + (delete-char 1) + (goto-char 80) + (widen) + (goto-char 70) + (widen) + (narrow-to-region 49 63) + (goto-char 49) + (insert "...") + (goto-char 66) + (delete-char 0) + (goto-char 63) + (delete-char 3) + (goto-char 59) + (insert "..........") + (goto-char 56) + (delete-char 6) + (goto-char 60) + (insert ".........") + (goto-char 62) + (widen) + (goto-char 58) + (insert ".............") + (goto-char 105) + (widen) + (narrow-to-region 94 109) + (goto-char 103) + (insert "............") + (should + (equal + (test-overlay-regions) + '((3 . 134) + (6 . 125) + (38 . 141) + (39 . 118) + (39 . 128) + (39 . 128) + (40 . 146) + (43 . 145) + (101 . 138) + (103 . 103)))))) + ;;; buffer-tests.el ends here From 828552916eb4e14a8f3dcf83b78687f9f34b1e4b Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Thu, 5 Oct 2017 21:55:43 +0200 Subject: [PATCH 04/70] Add offsets when inspecting a node's children's values *src/itree.c (interval_tree_insert_gap): Add offset. --- src/itree.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/itree.c b/src/itree.c index 0c10100eef7..5df2d8d1cc5 100644 --- a/src/itree.c +++ b/src/itree.c @@ -519,7 +519,8 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil && pos <= node->left->limit) + if (node->left != &tree->nil + && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); /* node->begin == pos implies no front-advance. */ @@ -578,7 +579,8 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil && pos <= node->left->limit) + if (node->left != &tree->nil + && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); if (pos < node->begin) From bb9ab6f5c06e8589ac6635e9700dd20e0c925625 Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Thu, 5 Oct 2017 21:57:17 +0200 Subject: [PATCH 05/70] Fix assignment of offset to newly inserted nodes. * src/itree.c (interval_tree_insert): Fix offset. --- src/itree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index 5df2d8d1cc5..785c83ab798 100644 --- a/src/itree.c +++ b/src/itree.c @@ -309,7 +309,9 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->left = &tree->nil; node->right = &tree->nil; node->color = ITREE_RED; - node->offset = offset; + node->offset = 0; + node->begin -= offset; + node->end -= offset; node->limit = node->end; node->otick = tree->otick - 1; From 3945c019ecc8bea75bf868bb388fa56881cb82b6 Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Fri, 6 Oct 2017 11:59:54 +0200 Subject: [PATCH 06/70] Add a function collecting all interval nodes * src/itree.c (interval_tree_nodes): New function --- src/itree.c | 22 ++++++++++++++++++++++ src/itree.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/itree.c b/src/itree.c index 785c83ab798..f43189cabe7 100644 --- a/src/itree.c +++ b/src/itree.c @@ -404,6 +404,28 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } +/* Fill memory pointed at via NODES with all nodes of TREE in the + given ORDER. + + The size of NODES must be sufficiently large. + */ + +void +interval_tree_nodes (struct interval_tree *tree, + struct interval_node **nodes, + enum interval_tree_order order) +{ + struct interval_node *node; + + interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order); + while ((node = interval_tree_iter_next (tree))) + { + *nodes = node; + ++nodes; + } + interval_tree_iter_finish (tree); +} + /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. diff --git a/src/itree.h b/src/itree.h index d35c5afc24c..d685a69eb78 100644 --- a/src/itree.h +++ b/src/itree.h @@ -85,4 +85,5 @@ void interval_tree_iter_finish(struct interval_tree *); struct interval_node *interval_tree_iter_next(struct interval_tree *); void interval_tree_insert_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); #endif From c2826d9a0312981e2553f699d3afe9b39880f6ce Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Fri, 6 Oct 2017 12:02:54 +0200 Subject: [PATCH 07/70] Adapt overlays when multibyteness changes * src/buffer.c (set_overlays_multibyte): New function. (set-buffer-multibyte): Use function. --- src/buffer.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/buffer.c b/src/buffer.c index 21c040ad0e1..122c60fab39 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -879,6 +879,56 @@ delete_all_overlays (struct buffer *b) interval_tree_clear (b->overlays); } +/* Adjust the position of overlays in the current buffer according to + MULTIBYTE. + + Assume that positions currently correspond to byte positions, if + MULTIBYTE is true and to character positions if not. +*/ + +static void +set_overlays_multibyte (bool multibyte) +{ + if (! current_buffer->overlays || Z == Z_BYTE) + return; + + struct interval_node **nodes = NULL; + struct interval_tree *tree = current_buffer->overlays; + const intmax_t size = interval_tree_size (tree); + + USE_SAFE_ALLOCA; + SAFE_NALLOCA (nodes, 1, size); + interval_tree_nodes (tree, nodes, ITREE_ASCENDING); + + for (int i = 0; i < size; ++i, ++nodes) + { + struct interval_node * const node = *nodes; + + if (multibyte) + { + ptrdiff_t begin = interval_node_begin (tree, node); + ptrdiff_t end = interval_node_end (tree, node); + + /* This models the behavior of markers. (The behavior of + text-intervals differs slightly.) */ + while (begin < Z_BYTE + && !CHAR_HEAD_P (FETCH_BYTE (begin))) + begin++; + while (end < Z_BYTE + && !CHAR_HEAD_P (FETCH_BYTE (end))) + end++; + interval_node_set_region (tree, node, BYTE_TO_CHAR (begin), + BYTE_TO_CHAR (end)); + } + else + { + interval_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), + CHAR_TO_BYTE (node->end)); + } + } + SAFE_FREE (); +} + /* Reinitialize everything about a buffer except its name and contents and local variables. If called on an already-initialized buffer, the list of overlays @@ -2456,6 +2506,7 @@ current buffer is cleared. */) /* Do this first, so it can use CHAR_TO_BYTE to calculate the old correspondences. */ set_intervals_multibyte (0); + set_overlays_multibyte (0); bset_enable_multibyte_characters (current_buffer, Qnil); @@ -2648,6 +2699,7 @@ current buffer is cleared. */) /* Do this last, so it can calculate the new correspondences between chars and bytes. */ set_intervals_multibyte (1); + set_overlays_multibyte (1); } if (!EQ (old_undo, Qt)) From 1ebc62c41e0cb622b04cbbfab692c617bb59d263 Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Sat, 7 Oct 2017 16:19:52 +0200 Subject: [PATCH 08/70] Expand overlay multibyte tests * test/src/buffer-tests.el (test-overlay-multibyte-transition-1): Expand test. (test-overlay-multibyte-transition-2): New test. --- test/src/buffer-tests.el | 61 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index c2c9d060336..153aea3a20b 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1215,10 +1215,65 @@ with parameters from the *Messages* buffer modification." (ert-deftest test-overlay-multibyte-transition-1 () (with-temp-buffer (set-buffer-multibyte t) - (insert "Ä") - (let ((ov (make-overlay (point-min) (point-max)))) + (insert "ääää") + ;; aeaeaeae + ;; 1 2 3 4 5 + ;; 123456789 + (let ((nonempty-bob (make-overlay 1 2)) + (empty-bob (make-overlay 1 1)) + (empty (make-overlay 2 2)) + (nonempty (make-overlay 2 4)) + (nonempty-eob (make-overlay 4 5)) + (empty-eob (make-overlay 5 5))) (set-buffer-multibyte nil) - (should (eq (overlay-end ov) (point-max)))))) + (cl-macrolet ((ovshould (ov begin end) + `(should (equal (list (overlay-start ,ov) (overlay-end ,ov)) + (list ,begin ,end))))) + (ovshould nonempty-bob 1 3) + (ovshould empty-bob 1 1) + (ovshould empty 3 3) + (ovshould nonempty 3 7) + (ovshould nonempty-eob 7 9) + (ovshould empty-eob 9 9))))) + +(ert-deftest test-overlay-multibyte-transition-2 () + (with-temp-buffer + (set-buffer-multibyte t) + (insert "ääää") + (set-buffer-multibyte nil) + ;; aeaeaeae + ;; 1 2 3 4 5 + ;; 123456789 + (let ((nonempty-bob-end (make-overlay 1 2)) + (nonempty-bob-beg (make-overlay 1 3)) + (empty-bob (make-overlay 1 1)) + (empty-beg (make-overlay 3 3)) + (empty-end (make-overlay 2 2)) + (nonempty-beg-beg (make-overlay 3 7)) + (nonempty-beg-end (make-overlay 3 8)) + (nonempty-end-beg (make-overlay 4 7)) + (nonempty-end-end (make-overlay 4 8)) + (nonempty-eob-beg (make-overlay 5 9)) + (nonempty-eob-end (make-overlay 6 9)) + (empty-eob (make-overlay 9 9))) + (set-buffer-multibyte t) + (cl-macrolet ((ovshould (ov begin end) + `(should (equal (list (overlay-start ,ov) (overlay-end ,ov)) + (list ,begin ,end))))) + (ovshould nonempty-bob-end 1 2) + (ovshould nonempty-bob-beg 1 2) + (ovshould empty-bob 1 1) + (ovshould empty-beg 2 2) + (ovshould empty-end 2 2) + (ovshould nonempty-beg-beg 2 4) + (ovshould nonempty-beg-end 2 5) + (ovshould nonempty-end-beg 3 4) + (ovshould nonempty-end-end 3 5) + (ovshould nonempty-eob-beg 3 5) + (ovshould nonempty-eob-end 4 5) + (ovshould empty-eob 5 5))))) + + ;; +===================================================================================+ From 46a3a8f6ed31ff4d0411ecc4cb276345a02e23dc Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Sat, 7 Oct 2017 17:11:05 +0200 Subject: [PATCH 09/70] Make boolean struct member use one bit * src/itree.h (struct interval_node): Add bit descriptor. --- src/itree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.h b/src/itree.h index d685a69eb78..80115aa467c 100644 --- a/src/itree.h +++ b/src/itree.h @@ -46,7 +46,7 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ - bool_bf visited; /* For traversal via generator. */ + bool_bf visited : 1; /* For traversal via generator. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; From 55ac6f21053935fb8369a33a827f5df2f99e872a Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Sat, 7 Oct 2017 17:18:37 +0200 Subject: [PATCH 10/70] Optimize struct layout for space * src/itree.h (struct interval_node): Move color member near the end. --- src/itree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.h b/src/itree.h index 80115aa467c..7a5c4ae02a8 100644 --- a/src/itree.h +++ b/src/itree.h @@ -36,7 +36,6 @@ along with GNU Emacs. If not, see . */ struct interval_node; struct interval_node { - enum { ITREE_RED, ITREE_BLACK } color; struct interval_node *parent; struct interval_node *left; struct interval_node *right; @@ -46,6 +45,7 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ + enum { ITREE_RED, ITREE_BLACK } color; bool_bf visited : 1; /* For traversal via generator. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ From a38c38c0526894ff05e2317994703c1157d5f909 Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Sat, 7 Oct 2017 17:19:45 +0200 Subject: [PATCH 11/70] Make boolean struct member use one bit * src/itree.h (struct interval_tree): Add bit descriptor. --- src/itree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.h b/src/itree.h index 7a5c4ae02a8..2f418f0cf85 100644 --- a/src/itree.h +++ b/src/itree.h @@ -58,7 +58,7 @@ struct interval_tree uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; - bool_bf iter_running; + bool_bf iter_running : 1; }; enum interval_tree_order { From 8869332684c2302b5ba1ead4568bbc7ba1c0183e Mon Sep 17 00:00:00 2001 From: Andreas Politz Date: Mon, 9 Oct 2017 08:22:43 +0200 Subject: [PATCH 12/70] Remove redundant checks for the empty overlay tree * src/alloc.c (mark_buffer): Remove b->overlays check. * src/buffer.c (copy_overlays): Also. * src/buffer.c (swap_buffer_overlays, overlays_in): Also. (next_overlay_change, previous_overlay_change): Also. (overlay_touches_p, Foverlay_lists): Also. (evaporate_overlays): Also. * src/xdisp.c (load_overlay_strings): Also. --- src/alloc.c | 14 ++++------- src/buffer.c | 63 +++++++++++++----------------------------------- src/xdisp.c | 68 +++++++++++++++++++++++++--------------------------- 3 files changed, 55 insertions(+), 90 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 15a6fc43b72..9f72f914e00 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6306,15 +6306,11 @@ mark_buffer (struct buffer *buffer) a special way just before the sweep phase, and after stripping some of its elements that are not needed any more. */ - if (buffer->overlays) - { - struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - - while ((node = buffer_overlay_iter_next (buffer))) - mark_overlay (XOVERLAY (node->data)); - buffer_overlay_iter_finish (buffer); - } + struct interval_node *node; + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + mark_overlay (XOVERLAY (node->data)); + buffer_overlay_iter_finish (buffer); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && !VECTOR_MARKED_P (buffer->base_buffer)) diff --git a/src/buffer.c b/src/buffer.c index 122c60fab39..9ddc9c7e056 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -607,14 +607,9 @@ static void copy_overlays (struct buffer *from, struct buffer *to) { eassert (to && ! to->overlays); - struct interval_node *node; - if (! from->overlays) - return; - buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (from))) { Lisp_Object ov = node->data; @@ -2277,20 +2272,16 @@ swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { struct interval_node *node; - if (buffer->overlays) - { - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) - XOVERLAY (node->data)->buffer = other; - buffer_overlay_iter_finish (buffer); - } - if (other->overlays) - { - buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (other))) - XOVERLAY (node->data)->buffer = buffer; - buffer_overlay_iter_finish (other); - } + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + XOVERLAY (node->data)->buffer = other; + buffer_overlay_iter_finish (buffer); + + buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (other))) + XOVERLAY (node->data)->buffer = buffer; + buffer_overlay_iter_finish (other); + /* Swap the interval trees. */ void *tmp = buffer->overlays; buffer->overlays = other->overlays; @@ -2837,12 +2828,8 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ptrdiff_t len = *len_ptr; ptrdiff_t next = ZV; Lisp_Object *vec = *vec_ptr; - struct interval_node *node; - if (! current_buffer->overlays) - return idx; - buffer_overlay_iter_start (current_buffer, beg, /* Find empty OV at Z ? */ (end >= Z && empty) ? Z + 1 : ZV, @@ -2904,9 +2891,6 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - if (! current_buffer->overlays) - return next; - buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { @@ -2935,9 +2919,6 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - if (! current_buffer->overlays) - return prev; - buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { @@ -3023,9 +3004,6 @@ overlay_touches_p (ptrdiff_t pos) struct interval_node *node; bool result = false; - if (! current_buffer->overlays) - return false; - /* We need to find overlays ending in pos, as well as empty ones at pos. */ buffer_overlay_iter_start (current_buffer, @@ -3729,16 +3707,13 @@ However, the overlays you get are the real objects that the buffer uses. */) (void) { Lisp_Object overlays = Qnil; + struct interval_node *node; - if (current_buffer->overlays) - { - struct interval_node *node; + buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + overlays = Fcons (node->data, overlays); + buffer_overlay_iter_finish (current_buffer); - buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) - overlays = Fcons (node->data, overlays); - buffer_overlay_iter_finish (current_buffer); - } return Fcons (overlays, Qnil); } @@ -3972,15 +3947,10 @@ call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay, bool after, void evaporate_overlays (ptrdiff_t pos) { - Lisp_Object hit_list; + Lisp_Object hit_list = Qnil; struct interval_node *node; - if (! current_buffer->overlays) - return; - - hit_list = Qnil; buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->end == pos @@ -3988,6 +3958,7 @@ evaporate_overlays (ptrdiff_t pos) hit_list = Fcons (node->data, hit_list); } buffer_overlay_iter_finish (current_buffer); + for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); } diff --git a/src/xdisp.c b/src/xdisp.c index b3b9ecae377..f94643b1f7b 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -5790,48 +5790,46 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) } \ while (false) - if (current_buffer->overlays) + + buffer_overlay_iter_start (current_buffer, + charpos - 1, charpos + 1, ITREE_DESCENDING); + /* Process overlays. */ + while ((node = buffer_overlay_iter_next (current_buffer))) { - buffer_overlay_iter_start (current_buffer, - charpos - 1, charpos + 1, ITREE_DESCENDING); - /* Process overlays. */ - while ((node = buffer_overlay_iter_next (current_buffer))) - { - overlay = node->data; - eassert (OVERLAYP (overlay)); - start = node->begin; - end = node->end; + overlay = node->data; + eassert (OVERLAYP (overlay)); + start = node->begin; + end = node->end; - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; + /* Skip this overlay if it doesn't start or end at IT's current + position. */ + if (end != charpos && start != charpos) + continue; - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; + /* Skip this overlay if it doesn't apply to IT->w. */ + window = Foverlay_get (overlay, Qwindow); + if (WINDOWP (window) && XWINDOW (window) != it->w) + continue; - /* If the text ``under'' the overlay is invisible, both before- - and after-strings from this overlay are visible; start and - end position are indistinguishable. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); + /* If the text ``under'' the overlay is invisible, both before- + and after-strings from this overlay are visible; start and + end position are indistinguishable. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); + /* If overlay has a non-empty before-string, record it. */ + if ((start == charpos || (end == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, false); - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); - } - buffer_overlay_iter_finish (current_buffer); + /* If overlay has a non-empty after-string, record it. */ + if ((end == charpos || (start == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, true); } + buffer_overlay_iter_finish (current_buffer); #undef RECORD_OVERLAY_STRING From 1a77f09f3ceaafa81cd400fc825f16b194b627b1 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 25 Sep 2022 22:24:35 -0400 Subject: [PATCH 13/70] * src/buffer.c (overlays_in): Fix confusion Z-vs-ZV This fixes test failures in `test-overlays-in-2` and `test-remove-overlays`. --- src/buffer.c | 4 ++-- src/pdumper.c | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 1bb2af98e75..2b1997fc8be 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2933,7 +2933,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, buffer_overlay_iter_start (current_buffer, beg, /* Find empty OV at Z ? */ - (end >= Z && empty) ? Z + 1 : ZV, + (end >= ZV && empty) ? ZV + 1 : ZV, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) @@ -2946,7 +2946,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, else if (node->begin == end) { next = node->begin; - if ((! empty || end < Z) && beg < end) + if ((! empty || end < ZV) && beg < end) break; } diff --git a/src/pdumper.c b/src/pdumper.c index 7618f5d1e87..7053c2d74f2 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2871,9 +2871,6 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) else out->overlays = NULL; - /* dump_field_lv_rawptr (ctx, out, buffer, &buffer->overlays, - ?Lisp_Vectorlike?, WEIGHT_NORMAL); */ - dump_field_lv (ctx, out, buffer, &buffer->undo_list_, WEIGHT_STRONG); dump_off offset = finish_dump_pvec (ctx, &out->header); From 409327ff68f9ccdc8099f6a2ba2fee76abaaab70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Tue, 27 Sep 2022 12:45:16 +0200 Subject: [PATCH 14/70] Fix macOS build (bug#58108) * src/itree.h (struct interval_tree): Rename member nil to null. * src/itree.c: Use null instead of nil * src/pdumper.c (dump_buffer): Use null instead of nil. * src/itree.c: Fix copyright. * src/itree.h: Fix copyright. --- src/itree.c | 100 +++++++++++++++++++++++++------------------------- src/itree.h | 6 +-- src/pdumper.c | 2 +- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/itree.c b/src/itree.c index adb55fe950b..ab734c3c18c 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1,8 +1,8 @@ /* This file implements an efficient interval data-structure. -Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) +Copyright (C) 2017-2022 Free Software Foundation, Inc. -This file is not part of GNU Emacs. +This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -227,14 +227,14 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - struct interval_node *nil = &tree->nil; - nil->left = nil->right = nil->parent = nil; - nil->offset = nil->otick = 0; - nil->begin = PTRDIFF_MIN; - nil->end = PTRDIFF_MIN; - nil->limit = PTRDIFF_MIN; /* => max(x, nil.limit) = x */ - nil->color = ITREE_BLACK; - tree->root = nil; + struct interval_node *null = &tree->null; + null->left = null->right = null->parent = null; + null->offset = null->otick = 0; + null->begin = PTRDIFF_MIN; + null->end = PTRDIFF_MIN; + null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ + null->color = ITREE_BLACK; + tree->root = null; tree->otick = 1; tree->size = 0; tree->iter_running = 0; @@ -278,15 +278,15 @@ interval_tree_size (struct interval_tree *tree) void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != &tree->nil); + eassert (node && node->begin <= node->end && node != &tree->null); - struct interval_node *parent = &tree->nil; - struct interval_node *child = tree->root; + struct interval_node *parent = &tree->null; + struct interval_node *child = &tree->null; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != &tree->nil) + while (child != &tree->null) { parent = child; offset += child->offset; @@ -297,7 +297,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == &tree->nil) + if (parent == &tree->null) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -306,8 +306,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = &tree->nil; - node->right = &tree->nil; + node->left = &tree->null; + node->right = &tree->null; node->color = ITREE_RED; node->offset = 0; node->begin -= offset; @@ -347,10 +347,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; interval_tree_inherit_offset (tree, node); - if (node->left == &tree->nil || node->right == &tree->nil) + if (node->left == &tree->null || node->right == &tree->null) { struct interval_node *subst = - (node->right == &tree->nil) ? node->left : node->right; + (node->right == &tree->null) ? node->left : node->right; if (node->color == ITREE_BLACK) broken = subst; interval_tree_transplant (tree, subst, node); @@ -364,7 +364,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (min->color == ITREE_BLACK) broken = min->right; if (min->parent == node) - min_right->parent = min; /* set parent, if min_right = nil */ + min_right->parent = min; /* set parent, if min_right = null */ else { interval_tree_transplant (tree, min->right, min); @@ -386,7 +386,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->nil)); + eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->null)); return node; } @@ -395,7 +395,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == &tree->nil) + if (tree->otick == node->otick || node == &tree->null) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -532,7 +532,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); - if (node->right != &tree->nil) + if (node->right != &tree->null) { if (node->begin > pos) { @@ -543,7 +543,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil + if (node->left != &tree->null && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -592,7 +592,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l while ((node = interval_stack_pop (stack))) { interval_tree_inherit_offset (tree, node); - if (node->right != &tree->nil) + if (node->right != &tree->null) { if (node->begin > pos + length) { @@ -603,7 +603,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil + if (node->left != &tree->null && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -681,7 +681,7 @@ interval_generator_next (struct interval_generator *g) { if (! g) return NULL; - struct interval_node * const nil = &g->tree->nil; + struct interval_node * const null = &g->tree->null; struct interval_node *node; do { @@ -696,26 +696,26 @@ interval_generator_next (struct interval_generator *g) switch (g->order) { case ITREE_ASCENDING: - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); /* Node's children may still be off-set and we need to add it. */ - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); break; case ITREE_DESCENDING: - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); break; case ITREE_PRE_ORDER: - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); @@ -832,7 +832,7 @@ static void interval_tree_update_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) return; node->limit = max (node->end, max (node->left->limit + node->left->offset, @@ -856,9 +856,9 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != &tree->nil) + if (node->left != &tree->null) node->left->offset += node->offset; - if (node->right != &tree->nil) + if (node->right != &tree->null) node->right->offset += node->offset; node->offset = 0; if (node == tree->root || node->parent->otick == tree->otick) @@ -868,7 +868,7 @@ interval_tree_inherit_offset (const struct interval_tree *tree, /* Update limit of NODE and its ancestors. Stop when it becomes stable, i.e. new_limit = old_limit. - NODE may also be the nil node, in which case its parent is + NODE may also be the null node, in which case its parent is used. (This feature is due to the RB algorithm.) */ @@ -876,9 +876,9 @@ static void interval_tree_propagate_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) node = node->parent; - if (node == &tree->nil) + if (node == &tree->null) return; while (1) { @@ -898,7 +898,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != &tree->nil); + eassert (node->right != &tree->null); struct interval_node *right = node->right; @@ -907,11 +907,11 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != &tree->nil) + if (right->left != &tree->null) right->left->parent = node; /* right's parent was node's parent. */ - if (right != &tree->nil) + if (right != &tree->null) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -927,7 +927,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Put node on right's left. */ right->left = node; - if (node != &tree->nil) + if (node != &tree->null) node->parent = right; /* Order matters here. */ @@ -940,7 +940,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != &tree->nil); + eassert (tree && node && node->left != &tree->null); struct interval_node *left = node->left; @@ -948,10 +948,10 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no interval_tree_inherit_offset (tree, left); node->left = left->right; - if (left->right != &tree->nil) + if (left->right != &tree->null) left->right->parent = node; - if (left != &tree->nil) + if (left != &tree->null) left->parent = node->parent; if (node != tree->root) { @@ -964,7 +964,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no tree->root = left; left->right = node; - if (node != &tree->nil) + if (node != &tree->null) node->parent = left; interval_tree_update_limit (tree, left); @@ -1133,7 +1133,7 @@ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && source && dest && dest != &tree->nil); + eassert (tree && source && dest && dest != &tree->null); if (dest == tree->root) tree->root = source; @@ -1149,9 +1149,9 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) return node; - while (node->left != &tree->nil) + while (node->left != &tree->null) node = node->left; return node; } diff --git a/src/itree.h b/src/itree.h index 08b152f92d2..21d8b21a02f 100644 --- a/src/itree.h +++ b/src/itree.h @@ -1,8 +1,8 @@ /* This file implements an efficient interval data-structure. -Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) +Copyright (C) 2017-2022 Free Software Foundation, Inc. -This file is not part of GNU Emacs. +This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -54,7 +54,7 @@ struct interval_node struct interval_tree { struct interval_node *root; - struct interval_node nil; /* The tree's version of NULL. */ + struct interval_node null; /* The tree's version of NULL. */ uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; diff --git a/src/pdumper.c b/src/pdumper.c index 7618f5d1e87..6c8c2131794 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2865,7 +2865,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); if (buffer->overlays - && (buffer->overlays->root != &buffer->overlays->nil)) + && (buffer->overlays->root != &buffer->overlays->null)) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else From 8173a292e7366beff0279480cbd2f73bf2357568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Wed, 28 Sep 2022 12:10:30 +0200 Subject: [PATCH 15/70] Fix last change --- src/itree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index ab734c3c18c..a6439970626 100644 --- a/src/itree.c +++ b/src/itree.c @@ -281,7 +281,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) eassert (node && node->begin <= node->end && node != &tree->null); struct interval_node *parent = &tree->null; - struct interval_node *child = &tree->null; + struct interval_node *child = tree->root; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update From 498d331b07117408c5c5d8a3889b49b275b9a654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Wed, 28 Sep 2022 16:30:34 +0200 Subject: [PATCH 16/70] Add debugging help for nested iterators (nug#58144) When starting an iteration, store __FILE__ and __LINE__ where this happens in the interval_tree structure. * src/buffer.h (buffer_overlay_iter_start): New macro adding __FILE and __LINE__. (buffer_overlay_iter_start1): Renamed from ..._start. * src/itree.h (struct interval_tree): Add file and line info. * src/itree.c: (interval_tree_contains, interval_tree_nodes, interval_tree_insert_gap): Pass __FILE__ and __LINE__ to iter_start. (interval_tree_iter_start): Record file and line info in tree. --- src/buffer.h | 9 ++++++--- src/itree.c | 11 +++++++---- src/itree.h | 5 ++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/buffer.h b/src/buffer.h index 7d6c693b0f2..097283be671 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1457,13 +1457,16 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) } INLINE void -buffer_overlay_iter_start (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) +buffer_overlay_iter_start1 (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order, const char* file, int line) { if (b->overlays) - interval_tree_iter_start (b->overlays, begin, end, order); + interval_tree_iter_start (b->overlays, begin, end, order, file, line); } +#define buffer_overlay_iter_start(b, begin, end, order) \ + buffer_overlay_iter_start1 ((b), (begin), (end), (order), __FILE__, __LINE__) + INLINE struct interval_node* buffer_overlay_iter_next (struct buffer *b) { diff --git a/src/itree.c b/src/itree.c index a6439970626..e31ce39ba11 100644 --- a/src/itree.c +++ b/src/itree.c @@ -328,7 +328,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { struct interval_node *other; - interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING); + interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); while ((other = interval_tree_iter_next (tree))) if (other == node) break; @@ -417,7 +417,7 @@ interval_tree_nodes (struct interval_tree *tree, { struct interval_node *node; - interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order); + interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); while ((node = interval_tree_iter_next (tree))) { *nodes = node; @@ -434,12 +434,15 @@ interval_tree_nodes (struct interval_tree *tree, void interval_tree_iter_start (struct interval_tree *tree, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) + enum interval_tree_order order, + const char* file, int line) { if (tree->iter_running) emacs_abort (); interval_generator_reset (tree->iter, begin, end, order); tree->iter_running = 1; + tree->file = file; + tree->line = line; } /* Limit the search interval of the iterator to the given values. The @@ -511,7 +514,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER); + interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER, __FILE__, __LINE__); while ((node = interval_tree_iter_next (tree))) { if (node->begin == pos && node->front_advance diff --git a/src/itree.h b/src/itree.h index 21d8b21a02f..e5d68fbfabb 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,6 +59,8 @@ struct interval_tree intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; bool_bf iter_running : 1; + const char* file; + int line; }; enum interval_tree_order { @@ -79,7 +81,8 @@ void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order); +void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, + const char* file, int line); void interval_tree_iter_narrow (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_iter_finish (struct interval_tree *); struct interval_node *interval_tree_iter_next (struct interval_tree *); From 17ba7ac5d9295d39ec3b97712d061b3c7866a093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Wed, 28 Sep 2022 16:38:04 +0200 Subject: [PATCH 17/70] Avoid nested iteration over intervals (bug#58144) * src/xdisp.c (strings_with_newlines): Call buffer_overlay_iter_finish. --- src/xdisp.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/xdisp.c b/src/xdisp.c index cee75def804..6ecd3bdf24f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -7031,13 +7031,21 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) str = Foverlay_get (overlay, Qbefore_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) - return true; + { + buffer_overlay_iter_finish (current_buffer); + return true; + } str = Foverlay_get (overlay, Qafter_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) - return true; + { + buffer_overlay_iter_finish (current_buffer); + return true; + } } + buffer_overlay_iter_finish (current_buffer); + /* Check for 'display' properties whose values include strings. */ Lisp_Object cpos = make_fixnum (startpos); Lisp_Object limpos = make_fixnum (endpos); From ea8daec9bb8ebf3cbca35edec4e4ef7b6edac3de Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 28 Sep 2022 19:05:16 -0400 Subject: [PATCH 18/70] itree.[ch]: Add sanity checks, comments, and minor tweaks * src/alloc.c (mark_overlay): Add sanity check. * src/buffer.c (next_overlay_change, previous_overlay_change): Tweak code to keep the same vars for the bounds. * src/itree.c (interval_tree_clear, interval_tree_insert) (interval_tree_remove, interval_tree_insert_fix, interval_tree_remove_fix): Adjust to the `color` -> `red` change. (interval_tree_clear): Prefer `true/false` for booleans. (interval_generator_create): Use an actual `interval_tree_order` value rather than 0. (interval_generator_next): Simplify a tiny bit. Add comment. (interval_generator_narrow): Add sanity check. * src/itree.h (struct interval_node): Replace `color` field with boolean `red` field. (enum interval_tree_order): Remove unused `ITREE_DEFLT_ORDER` value. * src/pdumper.c (dump_interval_node): Adjust to the `color` -> `red` change. --- src/alloc.c | 3 ++ src/buffer.c | 4 +- src/itree.c | 134 ++++++++++++++++++++++++++++++-------------------- src/itree.h | 7 ++- src/pdumper.c | 2 +- 5 files changed, 89 insertions(+), 61 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 20b8981bd66..be55dcf8dfd 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6505,6 +6505,9 @@ mark_char_table (struct Lisp_Vector *ptr, enum pvec_type pvectype) static void mark_overlay (struct Lisp_Overlay *ov) { + /* We don't mark the `interval_node` object, because it is managed manually + rather than by the GC. */ + eassert (BASE_EQ (ov->interval->data, make_lisp_ptr (ov, Lisp_Vectorlike))); set_vectorlike_marked (&ov->header); mark_object (ov->plist); } diff --git a/src/buffer.c b/src/buffer.c index 2b1997fc8be..879e14be960 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2992,7 +2992,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); + buffer_overlay_iter_start (current_buffer, pos, next, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->begin > pos) @@ -3020,7 +3020,7 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); + buffer_overlay_iter_start (current_buffer, prev, pos, ITREE_DESCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->end < pos) diff --git a/src/itree.c b/src/itree.c index e31ce39ba11..aa6fcc1bea0 100644 --- a/src/itree.c +++ b/src/itree.c @@ -233,11 +233,11 @@ interval_tree_clear (struct interval_tree *tree) null->begin = PTRDIFF_MIN; null->end = PTRDIFF_MIN; null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->color = ITREE_BLACK; + null->red = false; tree->root = null; tree->otick = 1; tree->size = 0; - tree->iter_running = 0; + tree->iter_running = false; } #ifdef ITREE_TESTING @@ -308,7 +308,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = &tree->null; node->right = &tree->null; - node->color = ITREE_RED; + node->red = true; node->offset = 0; node->begin -= offset; node->end -= offset; @@ -351,7 +351,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { struct interval_node *subst = (node->right == &tree->null) ? node->left : node->right; - if (node->color == ITREE_BLACK) + if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); interval_tree_propagate_limit (tree, subst); @@ -361,7 +361,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *min = interval_tree_subtree_min (tree, node->right); struct interval_node *min_right = min->right; - if (min->color == ITREE_BLACK) + if (!min->red) broken = min->right; if (min->parent == node) min_right->parent = min; /* set parent, if min_right = null */ @@ -375,7 +375,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; - min->color = node->color; + min->red = node->red; interval_tree_propagate_limit (tree, min_right); interval_tree_propagate_limit (tree, min); } @@ -440,7 +440,7 @@ interval_tree_iter_start (struct interval_tree *tree, if (tree->iter_running) emacs_abort (); interval_generator_reset (tree->iter, begin, end, order); - tree->iter_running = 1; + tree->iter_running = true; tree->file = file; tree->line = line; } @@ -464,7 +464,7 @@ interval_tree_iter_finish (struct interval_tree *tree) { if (! tree->iter_running) emacs_abort (); - tree->iter_running = 0; + tree->iter_running = false; } /* Return the next node of the iterator in the order given when it was @@ -637,7 +637,7 @@ interval_generator_create (struct interval_tree *tree) g->stack = interval_stack_create (size); g->tree = tree; - interval_generator_reset (g, 1, 0, 0); + interval_generator_reset (g, 1, 0, ITREE_ASCENDING); return g; } @@ -667,7 +667,13 @@ interval_generator_ensure_space (struct interval_generator *g) interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); } -/* Return true, if NODE's interval intersects with [BEGIN, END). */ +/* Return true, if NODE's interval intersects with [BEGIN, END). + Note: We always include empty nodes at BEGIN (and not at END), + but if BEGIN==END, then we don't include non-empty nodes starting + at BEGIN or ending at END. This seems to match the behavior of the + old overlays code but it's not clear if it's The Right Thing + (e.g. it breaks the expectation that if NODE1 is included, then + a NODE2 strictly bigger than NODE1 should also be included). */ static inline bool interval_node_intersects (const struct interval_node *node, @@ -687,10 +693,29 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const null = &g->tree->null; struct interval_node *node; - do { - node = interval_stack_pop (g->stack); + /* The `visited` flag stored in each node is used here (and only here): + We keep a "workstack" of nodes we need to consider. This stack + consist of nodes of two types: nodes that we have decided + should be returned by the generator, and nodes which we may + need to consider (including checking their children). + We start an iteration with a stack containing just the root + node marked as "not visited" which means that it (and its children) + needs to be considered but we haven't yet decided whether it's included + in the generator's output. */ - while (node && ! node->visited) + /* FIXME: We should move the `visited` flag to the stack: each entry + there should simply consist of a node and a bool (the `visited` status) + so this internal implementation detail doesn't leak into the + `interval_node` structure. + [ In theory it would also allow multiple iterations to be active + at the same time, tho that does not seem particularly useful at + this time and would require further changes anyway. ] + To save space on the stack, we could hijack the LSB bit of the `node*` + word to hold the `visited` bit. */ + + do { + while ((node = interval_stack_pop (g->stack)) + && ! node->visited) { struct interval_node * const left = node->left; struct interval_node * const right = node->right; @@ -724,7 +749,6 @@ interval_generator_next (struct interval_generator *g) interval_stack_push_flagged (g->stack, node, true); break; } - node = interval_stack_pop (g->stack); } /* Node may have been invalidated by interval_generator_narrow after it was pushed: Check if it still intersects. */ @@ -740,6 +764,8 @@ static inline void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { + eassert (begin >= g->begin); + eassert (end <= g->end); g->begin = max (begin, g->begin); g->end = min (end, g->end); } @@ -980,7 +1006,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) { - while (node->parent->color == ITREE_RED) + while (node->parent->red) { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ @@ -991,14 +1017,14 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node our "uncle". */ struct interval_node *uncle = node->parent->parent->right; - if (uncle->color == ITREE_RED) /* case 1.a */ + if (uncle->red) /* case 1.a */ { /* Uncle and parent are red but should be black because NODE is red. Change the colors accordingly and proceed with the grandparent. */ - node->parent->color = ITREE_BLACK; - uncle->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; node = node->parent->parent; } else @@ -1011,8 +1037,8 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node interval_tree_rotate_left (tree, node); } /* case 3.a */ - node->parent->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + node->parent->parent->red = true; interval_tree_rotate_right (tree, node->parent->parent); } } @@ -1021,11 +1047,11 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* This is the symmetrical case of above. */ struct interval_node *uncle = node->parent->parent->left; - if (uncle->color == ITREE_RED) /* case 1.b */ + if (uncle->red) /* case 1.b */ { - node->parent->color = ITREE_BLACK; - uncle->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; node = node->parent->parent; } else @@ -1036,8 +1062,8 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node interval_tree_rotate_right (tree, node); } /* case 3.b */ - node->parent->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + node->parent->parent->red = true; interval_tree_rotate_left (tree, node->parent->parent); } } @@ -1045,7 +1071,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* The root may have been changed to red due to the algorithm. Set it to black so that property #5 is satisfied. */ - tree->root->color = ITREE_BLACK; + tree->root->red = false; } /* Repair the tree after a deletion. Part of the RB-Tree @@ -1054,38 +1080,38 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node static void interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) { - while (node != tree->root && node->color == ITREE_BLACK) + while (node != tree->root && !node->red) { if (node == node->parent->left) { struct interval_node *other = node->parent->right; - if (other->color == ITREE_RED) /* case 1.a */ + if (other->red) /* case 1.a */ { - other->color = ITREE_BLACK; - node->parent->color = ITREE_RED; + other->red = false; + node->parent->red = true; interval_tree_rotate_left (tree, node->parent); other = node->parent->right; } - if (other->left->color == ITREE_BLACK /* 2.a */ - && other->right->color == ITREE_BLACK) + if (!other->left->red /* 2.a */ + && !other->right->red) { - other->color = ITREE_RED; + other->red = true; node = node->parent; } else { - if (other->right->color == ITREE_BLACK) /* 3.a */ + if (!other->right->red) /* 3.a */ { - other->left->color = ITREE_BLACK; - other->color = ITREE_RED; + other->left->red = false; + other->red = true; interval_tree_rotate_right (tree, other); other = node->parent->right; } - other->color = node->parent->color; /* 4.a */ - node->parent->color = ITREE_BLACK; - other->right->color = ITREE_BLACK; + other->red = node->parent->red; /* 4.a */ + node->parent->red = false; + other->right->red = false; interval_tree_rotate_left (tree, node->parent); node = tree->root; } @@ -1094,40 +1120,40 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node { struct interval_node *other = node->parent->left; - if (other->color == ITREE_RED) /* 1.b */ + if (other->red) /* 1.b */ { - other->color = ITREE_BLACK; - node->parent->color = ITREE_RED; + other->red = false; + node->parent->red = true; interval_tree_rotate_right (tree, node->parent); other = node->parent->left; } - if (other->right->color == ITREE_BLACK /* 2.b */ - && other->left->color == ITREE_BLACK) + if (!other->right->red /* 2.b */ + && !other->left->red) { - other->color = ITREE_RED; + other->red = true; node = node->parent; } else { - if (other->left->color == ITREE_BLACK) /* 3.b */ + if (!other->left->red) /* 3.b */ { - other->right->color = ITREE_BLACK; - other->color = ITREE_RED; + other->right->red = false; + other->red = true; interval_tree_rotate_left (tree, other); other = node->parent->left; } - other->color = node->parent->color; /* 4.b */ - node->parent->color = ITREE_BLACK; - other->left->color = ITREE_BLACK; + other->red = node->parent->red; /* 4.b */ + node->parent->red = false; + other->left->red = false; interval_tree_rotate_right (tree, node->parent); node = tree->root; } } } - node->color = ITREE_BLACK; + node->red = false; } /* Link node SOURCE in DEST's place. */ diff --git a/src/itree.h b/src/itree.h index e5d68fbfabb..8f0454dc7a4 100644 --- a/src/itree.h +++ b/src/itree.h @@ -45,8 +45,8 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ - enum { ITREE_RED, ITREE_BLACK } color; - bool_bf visited : 1; /* For traversal via generator. */ + bool_bf red : 1; + bool_bf visited : 1; /* Internal to `interval_generator_next`. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; @@ -64,8 +64,7 @@ struct interval_tree }; enum interval_tree_order { - ITREE_ASCENDING = 0, - ITREE_DEFLT_ORDER = 0, + ITREE_ASCENDING, ITREE_DESCENDING, ITREE_PRE_ORDER, }; diff --git a/src/pdumper.c b/src/pdumper.c index 79644c01517..44486015f05 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2154,7 +2154,7 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, DUMP_FIELD_COPY (&out, node, offset); DUMP_FIELD_COPY (&out, node, otick); dump_field_lv (ctx, &out, node, &node->data, WEIGHT_STRONG); - DUMP_FIELD_COPY (&out, node, color); + DUMP_FIELD_COPY (&out, node, red); DUMP_FIELD_COPY (&out, node, visited); DUMP_FIELD_COPY (&out, node, rear_advance); DUMP_FIELD_COPY (&out, node, front_advance); From 5b954f8f9d9b4b015e5aa57a2307f92801669932 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 29 Sep 2022 09:08:37 -0400 Subject: [PATCH 19/70] alloc.c: Avoid nested overlay iterations (bug#58158) * src/alloc.c (mark_overlays): New function. (mark_buffer): Use it instead of using the overlay iterator. --- src/alloc.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index be55dcf8dfd..8dc45659b50 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6512,6 +6512,21 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } +static void +mark_overlays (struct interval_tree *it, struct interval_node *in) +{ + /* `left/right` are set to NULL when the overlay is deleted, but + they use the `null` node instead when the overlay is not deleted + (i.e. is within an overlay tree). */ + eassert (in); + if (in == &it->null) + return; + + mark_object (in->data); + mark_overlays (it, in->left); + mark_overlays (it, in->right); +} + /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6533,11 +6548,8 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) - mark_overlay (XOVERLAY (node->data)); - buffer_overlay_iter_finish (buffer); + if (buffer->overlays) + mark_overlays (buffer->overlays, buffer->overlays->root); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && From 8e61648693c779a411aad5ce8ad3a493e4032155 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 29 Sep 2022 14:10:04 -0400 Subject: [PATCH 20/70] * src/itree.c (interval_tree_iter_start): Improve error message --- src/itree.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index aa6fcc1bea0..bdaa3fe09bb 100644 --- a/src/itree.c +++ b/src/itree.c @@ -438,7 +438,12 @@ interval_tree_iter_start (struct interval_tree *tree, const char* file, int line) { if (tree->iter_running) - emacs_abort (); + { + fprintf (stderr, + "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", + tree->file, tree->line, file, line); + emacs_abort (); + } interval_generator_reset (tree->iter, begin, end, order); tree->iter_running = true; tree->file = file; From 757c116f6b0bc2d8e81aef18f8eada27ca8745a1 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 29 Sep 2022 16:15:01 -0400 Subject: [PATCH 21/70] free_buffer_overlays: Move nearer to its sole caller * src/buffer.c (free_buffer_overlays): Move from `buffer.h`. * src/buffer.h (free_buffer_overlays): Move to `buffer.c`. * src/itree.c (interval_tree_iter_narrow, interval_tree_iter_finish) (interval_tree_iter_next): Prefer `eassert`. --- src/buffer.c | 12 ++++++++++++ src/buffer.h | 14 ------------- src/itree.c | 55 +++++++++++++++++++++++++--------------------------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 879e14be960..2f026584bbf 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -932,6 +932,18 @@ delete_all_overlays (struct buffer *b) interval_tree_clear (b->overlays); } +static void +free_buffer_overlays (struct buffer *b) +{ + /* Actually this does not free any overlay, but the tree only. --ap */ + eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); + if (b->overlays) + { + interval_tree_destroy (b->overlays); + b->overlays = NULL; + } +} + /* Adjust the position of overlays in the current buffer according to MULTIBYTE. diff --git a/src/buffer.h b/src/buffer.h index 097283be671..447be06594c 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1424,20 +1424,6 @@ maybe_alloc_buffer_overlays (struct buffer *b) b->overlays = interval_tree_create (); } -/* FIXME: Actually this does not free any overlay, but the tree - only. --ap */ - -INLINE void -free_buffer_overlays (struct buffer *b) -{ - eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); - if (b->overlays) - { - interval_tree_destroy (b->overlays); - b->overlays = NULL; - } -} - INLINE void add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) { diff --git a/src/itree.c b/src/itree.c index bdaa3fe09bb..7c2602683c4 100644 --- a/src/itree.c +++ b/src/itree.c @@ -94,26 +94,26 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ -static struct interval_node *interval_tree_validate(struct interval_tree *, struct interval_node *); -static void interval_generator_ensure_space(struct interval_generator *); -static bool interval_node_intersects(const struct interval_node *, ptrdiff_t, ptrdiff_t); -static int interval_tree_max_height(const struct interval_tree *); -static struct interval_stack *interval_stack_create(intmax_t); -static void interval_stack_destroy(struct interval_stack *); -static void interval_stack_clear(struct interval_stack *); -static void interval_stack_ensure_space(struct interval_stack *, intmax_t); -static void interval_stack_push(struct interval_stack *, struct interval_node *); -static void interval_stack_push_flagged(struct interval_stack *, struct interval_node *, bool); -static struct interval_node *interval_stack_pop(struct interval_stack *); -static void interval_tree_update_limit(const struct interval_tree *, struct interval_node *); -static void interval_tree_inherit_offset(const struct interval_tree *, struct interval_node *); -static void interval_tree_propagate_limit(const struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_left(struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_right(struct interval_tree *, struct interval_node *); -static void interval_tree_insert_fix(struct interval_tree *, struct interval_node *); -static void interval_tree_remove_fix(struct interval_tree *, struct interval_node *); -static void interval_tree_transplant(struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min(const struct interval_tree *, struct interval_node *); +static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); +static void interval_generator_ensure_space (struct interval_generator *); +static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); +static int interval_tree_max_height (const struct interval_tree *); +static struct interval_stack *interval_stack_create (intmax_t); +static void interval_stack_destroy (struct interval_stack *); +static void interval_stack_clear (struct interval_stack *); +static void interval_stack_ensure_space (struct interval_stack *, intmax_t); +static void interval_stack_push (struct interval_stack *, struct interval_node *); +static void interval_stack_push_flagged (struct interval_stack *, struct interval_node *, bool); +static struct interval_node *interval_stack_pop (struct interval_stack *); +static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); +static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); +static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); +static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); static void interval_generator_reset (struct interval_generator *, @@ -124,7 +124,7 @@ interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end); static inline struct interval_node* interval_generator_next (struct interval_generator *g); -static inline void interval_tree_iter_ensure_space(struct interval_tree *); +static inline void interval_tree_iter_ensure_space (struct interval_tree *); @@ -454,11 +454,10 @@ interval_tree_iter_start (struct interval_tree *tree, interval can only shrink, but never grow.*/ inline void -interval_tree_iter_narrow(struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end) +interval_tree_iter_narrow (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); interval_generator_narrow (tree->iter, begin, end); } @@ -467,8 +466,7 @@ interval_tree_iter_narrow(struct interval_tree *tree, void interval_tree_iter_finish (struct interval_tree *tree) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); tree->iter_running = false; } @@ -478,8 +476,7 @@ interval_tree_iter_finish (struct interval_tree *tree) inline struct interval_node* interval_tree_iter_next (struct interval_tree *tree) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); return interval_generator_next (tree->iter); } From a7ad0f806c1ed82f4d0710111aa92417e04a1110 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 29 Sep 2022 17:12:21 -0400 Subject: [PATCH 22/70] itree: Remove the `visited` flag from the tree nodes These bits really belong in the "workstack" used within `interval_generator_next`, so move them there. * src/itree.c (nodeptr_and_flag): New type; (struct interval_stack): Use it. (make_nav, nav_nodeptr, nav_flag): New functions. (interval_tree_insert_gap, interval_tree_delete_gap): Adjust accordingly. (interval_generator_next): Stash the `visited` bit in the work stack rather than inside the tree nodes. (interval_stack_create, interval_stack_destroy, interval_stack_clear) (interval_stack_ensure_space, interval_stack_push_flagged) (interval_stack_push, interval_stack_pop): Move before first use. * src/itree.h (struct interval_node): Remove `visited` field. * src/pdumper.c (dump_interval_node): Adjust accordingly. --- src/itree.c | 202 +++++++++++++++++++++++++++----------------------- src/itree.h | 1 - src/pdumper.c | 1 - 3 files changed, 109 insertions(+), 95 deletions(-) diff --git a/src/itree.c b/src/itree.c index 7c2602683c4..3b354b56403 100644 --- a/src/itree.c +++ b/src/itree.c @@ -98,13 +98,6 @@ static struct interval_node *interval_tree_validate (struct interval_tree *, str static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); -static struct interval_stack *interval_stack_create (intmax_t); -static void interval_stack_destroy (struct interval_stack *); -static void interval_stack_clear (struct interval_stack *); -static void interval_stack_ensure_space (struct interval_stack *, intmax_t); -static void interval_stack_push (struct interval_stack *, struct interval_node *); -static void interval_stack_push_flagged (struct interval_stack *, struct interval_node *, bool); -static struct interval_node *interval_stack_pop (struct interval_stack *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); @@ -130,10 +123,12 @@ static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* +------------------------------------------------------------------------------------+ */ +typedef uintptr_t nodeptr_and_flag; + /* Simple dynamic array. */ struct interval_stack { - struct interval_node **nodes; + nodeptr_and_flag *nodes; size_t size; size_t length; }; @@ -148,6 +143,97 @@ struct interval_generator enum interval_tree_order order; }; + +/* +===================================================================================+ + * | Stack + * +===================================================================================+ */ + +static inline nodeptr_and_flag +make_nav (struct interval_node *ptr, bool flag) +{ + uintptr_t v = (uintptr_t) ptr; + /* We assume alignment imposes the LSB is clear for us to use it. */ + eassert (!(v & 1)); + return v | !!flag; +} + +static inline struct interval_node * +nav_nodeptr (nodeptr_and_flag nav) +{ + return (struct interval_node *) (nav & (~(uintptr_t)1)); +} + +static inline bool +nav_flag (nodeptr_and_flag nav) +{ + return (bool) (nav & 1); +} + +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + } +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + /* FIXME: Isn't this redundant with the calls that are passed + `interval_tree_max_height` before the iteration? */ + interval_stack_ensure_space (stack, stack->length + 1); + + stack->nodes[stack->length] = make_nav (node, flag); + stack->length++; +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_push_flagged (stack, node, false); +} + +static inline nodeptr_and_flag +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return make_nav (NULL, false); + return stack->nodes[--stack->length]; +} /* +===================================================================================+ @@ -525,7 +611,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l } interval_tree_iter_finish (tree); for (int i = 0; i < saved->length; ++i) - interval_tree_remove (tree, saved->nodes[i]); + interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); /* We can't use a generator here, because we can't effectively @@ -533,7 +619,9 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); interval_stack_push (stack, tree->root); - while ((node = interval_stack_pop (stack))) + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack), + node = nav_nodeptr (nav))) { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); @@ -564,7 +652,8 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l interval_stack_destroy (stack); /* Reinsert nodes starting at POS having front-advance. */ - while ((node = interval_stack_pop (saved))) + while ((nav = interval_stack_pop (saved), + node = nav_nodeptr (nav))) { node->begin += length; if (node->end != pos || node->rear_advance) @@ -594,8 +683,10 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l struct interval_node *node; interval_stack_push (stack, tree->root); - while ((node = interval_stack_pop (stack))) + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack))) { + node = nav_nodeptr (nav); interval_tree_inherit_offset (tree, node); if (node->right != &tree->null) { @@ -705,19 +796,13 @@ interval_generator_next (struct interval_generator *g) needs to be considered but we haven't yet decided whether it's included in the generator's output. */ - /* FIXME: We should move the `visited` flag to the stack: each entry - there should simply consist of a node and a bool (the `visited` status) - so this internal implementation detail doesn't leak into the - `interval_node` structure. - [ In theory it would also allow multiple iterations to be active - at the same time, tho that does not seem particularly useful at - this time and would require further changes anyway. ] - To save space on the stack, we could hijack the LSB bit of the `node*` - word to hold the `visited` bit. */ - do { - while ((node = interval_stack_pop (g->stack)) - && ! node->visited) + nodeptr_and_flag nav; + bool visited; + while ((nav = interval_stack_pop (g->stack), + node = nav_nodeptr (nav), + visited = nav_flag (nav), + node && !visited)) { struct interval_node * const left = node->left; struct interval_node * const right = node->right; @@ -783,75 +868,6 @@ interval_generator_destroy (struct interval_generator *g) xfree (g); } - -/* +===================================================================================+ - * | Stack - * +===================================================================================+ */ - -/* This is just a simple dynamic array with stack semantics. */ - -static struct interval_stack* -interval_stack_create (intmax_t initial_size) -{ - struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); - stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); - stack->length = 0; - return stack; -} - -static void -interval_stack_destroy (struct interval_stack *stack) -{ - if (! stack) - return; - if (stack->nodes) - xfree (stack->nodes); - xfree (stack); -} - -static void -interval_stack_clear (struct interval_stack *stack) -{ - stack->length = 0; -} - -static inline void -interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) -{ - if (nelements > stack->size) - { - stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); - } -} - -static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) -{ - interval_stack_ensure_space (stack, stack->length + 1); - stack->nodes[stack->length] = node; - stack->length++; -} - -/* Push NODE on the STACK, while settings its visited flag to FLAG. */ - -static inline void -interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) -{ - interval_stack_push (stack, node); - node->visited = flag; -} - -static inline struct interval_node* -interval_stack_pop (struct interval_stack *stack) -{ - if (stack->length == 0) - return NULL; - return stack->nodes[--stack->length]; -} - /* +===================================================================================+ * | Internal Functions diff --git a/src/itree.h b/src/itree.h index 8f0454dc7a4..f24b12fcf61 100644 --- a/src/itree.h +++ b/src/itree.h @@ -46,7 +46,6 @@ struct interval_node uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ bool_bf red : 1; - bool_bf visited : 1; /* Internal to `interval_generator_next`. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; diff --git a/src/pdumper.c b/src/pdumper.c index 44486015f05..4c057117b4a 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2155,7 +2155,6 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, DUMP_FIELD_COPY (&out, node, otick); dump_field_lv (ctx, &out, node, &node->data, WEIGHT_STRONG); DUMP_FIELD_COPY (&out, node, red); - DUMP_FIELD_COPY (&out, node, visited); DUMP_FIELD_COPY (&out, node, rear_advance); DUMP_FIELD_COPY (&out, node, front_advance); dump_off offset = dump_object_finish (ctx, &out, sizeof (out)); From 009249e0c6d3bb6c4a3714a279ae91807d133c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Fri, 30 Sep 2022 13:25:15 +0200 Subject: [PATCH 23/70] Remove the per-tree null node "make check" shows 0 unexpcted. * src/itree.h (itree_null): Declare extern. (ITREE_NULL): New macro (struct interval_tree): Remove null member. * src/alloc.c (mark_overlays): Use ITREE_NULL. * src/itree.c: Use ITREE_NULL insteads of a tree's null. * src/pdumper.c (dump_buffer): Use ITREE_NULL. --- src/alloc.c | 2 +- src/itree.c | 81 ++++++++++++++++++++++++++++++--------------------- src/itree.h | 5 +++- src/pdumper.c | 3 +- 4 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 8dc45659b50..db8f39a60e0 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6519,7 +6519,7 @@ mark_overlays (struct interval_tree *it, struct interval_node *in) they use the `null` node instead when the overlay is not deleted (i.e. is within an overlay tree). */ eassert (in); - if (in == &it->null) + if (in == ITREE_NULL) return; mark_object (in->data); diff --git a/src/itree.c b/src/itree.c index 3b354b56403..6d97dd2a715 100644 --- a/src/itree.c +++ b/src/itree.c @@ -119,6 +119,14 @@ static inline struct interval_node* interval_generator_next (struct interval_generator *g); static inline void interval_tree_iter_ensure_space (struct interval_tree *); +/* The sentinel node, the null node. */ +struct interval_node itree_null; + +static void +init_itree_null (void) +{ + itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; +} /* +------------------------------------------------------------------------------------+ */ @@ -302,6 +310,11 @@ interval_node_set_region (struct interval_tree *tree, struct interval_tree* interval_tree_create (void) { + /* FIXME? Maybe avoid the initialization of itree_null in the same + way that is used to call mem_init in alloc.c? It's not really + important though. */ + init_itree_null (); + struct interval_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); tree->iter = interval_generator_create (tree); @@ -313,13 +326,15 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - struct interval_node *null = &tree->null; + /* FIXME: Why is this done? */ + struct interval_node *null = ITREE_NULL; null->left = null->right = null->parent = null; null->offset = null->otick = 0; null->begin = PTRDIFF_MIN; null->end = PTRDIFF_MIN; null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ null->red = false; + tree->root = null; tree->otick = 1; tree->size = 0; @@ -364,15 +379,15 @@ interval_tree_size (struct interval_tree *tree) void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != &tree->null); + eassert (node && node->begin <= node->end && node != ITREE_NULL); - struct interval_node *parent = &tree->null; + struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != &tree->null) + while (child != ITREE_NULL) { parent = child; offset += child->offset; @@ -383,7 +398,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == &tree->null) + if (parent == ITREE_NULL) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -392,8 +407,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = &tree->null; - node->right = &tree->null; + node->left = ITREE_NULL; + node->right = ITREE_NULL; node->red = true; node->offset = 0; node->begin -= offset; @@ -433,10 +448,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; interval_tree_inherit_offset (tree, node); - if (node->left == &tree->null || node->right == &tree->null) + if (node->left == ITREE_NULL || node->right == ITREE_NULL) { - struct interval_node *subst = - (node->right == &tree->null) ? node->left : node->right; + struct interval_node *subst + = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); @@ -472,7 +487,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->null)); + eassert (tree->size == 0 || (tree->size > 0 && tree->root != ITREE_NULL)); return node; } @@ -481,7 +496,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == &tree->null) + if (tree->otick == node->otick || node == ITREE_NULL) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -625,7 +640,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); - if (node->right != &tree->null) + if (node->right != ITREE_NULL) { if (node->begin > pos) { @@ -636,7 +651,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->null + if (node->left != ITREE_NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -688,7 +703,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { node = nav_nodeptr (nav); interval_tree_inherit_offset (tree, node); - if (node->right != &tree->null) + if (node->right != ITREE_NULL) { if (node->begin > pos + length) { @@ -699,7 +714,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->null + if (node->left != ITREE_NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -783,7 +798,7 @@ interval_generator_next (struct interval_generator *g) { if (! g) return NULL; - struct interval_node * const null = &g->tree->null; + struct interval_node * const null = ITREE_NULL; struct interval_node *node; /* The `visited` flag stored in each node is used here (and only here): @@ -879,7 +894,7 @@ static void interval_tree_update_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) return; node->limit = max (node->end, max (node->left->limit + node->left->offset, @@ -903,9 +918,9 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != &tree->null) + if (node->left != ITREE_NULL) node->left->offset += node->offset; - if (node->right != &tree->null) + if (node->right != ITREE_NULL) node->right->offset += node->offset; node->offset = 0; if (node == tree->root || node->parent->otick == tree->otick) @@ -923,9 +938,9 @@ static void interval_tree_propagate_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) node = node->parent; - if (node == &tree->null) + if (node == ITREE_NULL) return; while (1) { @@ -945,7 +960,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != &tree->null); + eassert (node->right != ITREE_NULL); struct interval_node *right = node->right; @@ -954,11 +969,11 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != &tree->null) + if (right->left != ITREE_NULL) right->left->parent = node; /* right's parent was node's parent. */ - if (right != &tree->null) + if (right != ITREE_NULL) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -974,7 +989,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Put node on right's left. */ right->left = node; - if (node != &tree->null) + if (node != ITREE_NULL) node->parent = right; /* Order matters here. */ @@ -987,7 +1002,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != &tree->null); + eassert (tree && node && node->left != ITREE_NULL); struct interval_node *left = node->left; @@ -995,10 +1010,10 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no interval_tree_inherit_offset (tree, left); node->left = left->right; - if (left->right != &tree->null) + if (left->right != ITREE_NULL) left->right->parent = node; - if (left != &tree->null) + if (left != ITREE_NULL) left->parent = node->parent; if (node != tree->root) { @@ -1011,7 +1026,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no tree->root = left; left->right = node; - if (node != &tree->null) + if (node != ITREE_NULL) node->parent = left; interval_tree_update_limit (tree, left); @@ -1180,7 +1195,7 @@ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && source && dest && dest != &tree->null); + eassert (tree && source && dest && dest != ITREE_NULL); if (dest == tree->root) tree->root = source; @@ -1196,9 +1211,9 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) return node; - while (node->left != &tree->null) + while (node->left != ITREE_NULL) node = node->left; return node; } diff --git a/src/itree.h b/src/itree.h index f24b12fcf61..f1ef7f99463 100644 --- a/src/itree.h +++ b/src/itree.h @@ -50,10 +50,13 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; +/* The sentinel node, the null node. */ +extern struct interval_node itree_null; +#define ITREE_NULL (&itree_null) + struct interval_tree { struct interval_node *root; - struct interval_node null; /* The tree's version of NULL. */ uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; diff --git a/src/pdumper.c b/src/pdumper.c index 4c057117b4a..e39f5f1109f 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2863,8 +2863,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); - if (buffer->overlays - && (buffer->overlays->root != &buffer->overlays->null)) + if (buffer->overlays && buffer->overlays->root != ITREE_NULL) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else From ab2926aad3e15c6cfa0e4b31ae9274c47a58baf2 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 30 Sep 2022 20:37:15 -0400 Subject: [PATCH 24/70] itree.c: Improve division between tree and iterator * src/buffer.c (delete_all_overlays): Add comment. * src/itree.c (struct interval_generator): New fields `running`, `file`, and `line` moved from `interval_tree`. (interval_stack_push_flagged): Adjust comment to resolve a FIXME. (interval_tree_clear): Replace assignment with an a (interval_tree_iter_next): Delete function. (interval_tree_clear): Don't set `iter_running` here any more. (interval_generator_create): Set it here instead. (interval_tree_iter_start): Fetch `iter` once and for all. (interval_generator_narrow): Mark it as non-static. (interval_tree_iter_next, interval_tree_iter_narrow): Delete functions. Inline their old bodies in the callers. (interval_tree_iter_finish): Take the iter rather than the whole tree. Adjust all callers. (interval_generator_next): Move `running `assertion here from `interval_tree_iter_next`. * src/buffer.h: Adjust accordingly. * src/itree.h (struct interval_tree): Remove fields `iter_running`, `file`, and `line`, moved to `interval_generator`. (interval_generator_narrow): Replace `interval_tree_iter_narrow`. --- src/buffer.c | 8 +++++ src/buffer.h | 6 ++-- src/itree.c | 93 ++++++++++++++++++++++++---------------------------- src/itree.h | 9 ++--- 4 files changed, 56 insertions(+), 60 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 2f026584bbf..19937216ed5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -921,6 +921,14 @@ delete_all_overlays (struct buffer *b) if (! b->overlays) return; + /* FIXME: This loop sets the overlays' `buffer` field to NULL but + doesn't set the interval_nodes' `parent`, `left` and `right` + fields accordingly. I believe it's harmless, but a bit untidy since + other parts of the code are careful to set those fields to NULL when + the overlay is deleted. + Of course, we can't set them to NULL from within the iteration + because the iterator may need them (tho we could if we added + an ITREE_POST_ORDER iteration order). */ buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (b))) { diff --git a/src/buffer.h b/src/buffer.h index 447be06594c..ad3b2ad6df5 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1458,21 +1458,21 @@ buffer_overlay_iter_next (struct buffer *b) { if (! b->overlays) return NULL; - return interval_tree_iter_next (b->overlays); + return interval_generator_next (b->overlays->iter); } INLINE void buffer_overlay_iter_finish (struct buffer *b) { if (b->overlays) - interval_tree_iter_finish (b->overlays); + interval_tree_iter_finish (b->overlays->iter); } INLINE void buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) { if (b->overlays) - interval_tree_iter_narrow (b->overlays, begin, end); + interval_generator_narrow (b->overlays->iter, begin, end); } /* Return the start of OV in its buffer, or -1 if OV is not associated diff --git a/src/itree.c b/src/itree.c index 6d97dd2a715..4f8aea924ac 100644 --- a/src/itree.c +++ b/src/itree.c @@ -94,6 +94,9 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ +/* FIXME: The code seems to use "generator" and "iterator" + inconsistently/interchangeably. We should fix this naming. */ + static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); @@ -110,13 +113,8 @@ static struct interval_node *interval_tree_subtree_min (const struct interval_tr static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); static void interval_generator_reset (struct interval_generator *, - ptrdiff_t, ptrdiff_t, - enum interval_tree_order); -static void -interval_generator_narrow (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end); -static inline struct interval_node* -interval_generator_next (struct interval_generator *g); + ptrdiff_t, ptrdiff_t, + enum interval_tree_order); static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ @@ -149,6 +147,9 @@ struct interval_generator ptrdiff_t begin; ptrdiff_t end; enum interval_tree_order order; + bool_bf running : 1; + const char* file; + int line; }; @@ -221,8 +222,11 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { - /* FIXME: Isn't this redundant with the calls that are passed - `interval_tree_max_height` before the iteration? */ + /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant + with the work of `interval_generator_ensure_space` but it's still needed + here because `interval_generator_next` can push up to 3 elements per + node it visits, so for a tree of depth N it can use up a stack + space up to 3 times larger than what we computed. :-( */ interval_stack_ensure_space (stack, stack->length + 1); stack->nodes[stack->length] = make_nav (node, flag); @@ -338,7 +342,6 @@ interval_tree_clear (struct interval_tree *tree) tree->root = null; tree->otick = 1; tree->size = 0; - tree->iter_running = false; } #ifdef ITREE_TESTING @@ -430,11 +433,11 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) struct interval_node *other; interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); - while ((other = interval_tree_iter_next (tree))) + while ((other = interval_generator_next (tree->iter))) if (other == node) break; - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); return other == node; } @@ -519,12 +522,12 @@ interval_tree_nodes (struct interval_tree *tree, struct interval_node *node; interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); - while ((node = interval_tree_iter_next (tree))) + while ((node = interval_generator_next (tree->iter))) { *nodes = node; ++nodes; } - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); } /* Start a generator iterating all intervals in [BEGIN,END) in the @@ -538,47 +541,27 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - if (tree->iter_running) + struct interval_generator *iter = tree->iter; + if (iter->running) { fprintf (stderr, "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", - tree->file, tree->line, file, line); + iter->file, iter->line, file, line); emacs_abort (); } - interval_generator_reset (tree->iter, begin, end, order); - tree->iter_running = true; - tree->file = file; - tree->line = line; -} - -/* Limit the search interval of the iterator to the given values. The - interval can only shrink, but never grow.*/ - -inline void -interval_tree_iter_narrow (struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end) -{ - eassert (tree->iter_running); - interval_generator_narrow (tree->iter, begin, end); + interval_generator_reset (iter, begin, end, order); + iter->running = true; + iter->file = file; + iter->line = line; } /* Stop using the iterator. */ void -interval_tree_iter_finish (struct interval_tree *tree) +interval_tree_iter_finish (struct interval_generator *iter) { - eassert (tree->iter_running); - tree->iter_running = false; -} - -/* Return the next node of the iterator in the order given when it was - started; or NULL if there are no more nodes. */ - -inline struct interval_node* -interval_tree_iter_next (struct interval_tree *tree) -{ - eassert (tree->iter_running); - return interval_generator_next (tree->iter); + eassert (iter->running); + iter->running = false; } /* Ensure that the tree's iterator does not need to allocate space @@ -617,14 +600,15 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER, __FILE__, __LINE__); - while ((node = interval_tree_iter_next (tree))) + interval_tree_iter_start (tree, pos, pos + 1, + ITREE_PRE_ORDER, __FILE__, __LINE__); + while ((node = interval_generator_next (tree->iter))) { if (node->begin == pos && node->front_advance && (node->begin != node->end || node->rear_advance)) interval_stack_push (saved, node); } - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); @@ -737,14 +721,16 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l /* Allocate a new generator for TREE. */ -static struct interval_generator* -interval_generator_create (struct interval_tree *tree) +static struct interval_generator * +interval_generator_create (struct interval_tree *tree) { struct interval_generator *g = xmalloc (sizeof *g); + /* FIXME: Is tree ever non-empty here? */ const int size = interval_tree_max_height (tree) + 1; g->stack = interval_stack_create (size); g->tree = tree; + g->running = false; interval_generator_reset (g, 1, 0, ITREE_ASCENDING); return g; } @@ -791,11 +777,13 @@ interval_node_intersects (const struct interval_node *node, || (node->begin == node->end && begin == node->begin); } -/* Return the next node of G, or NULL if there is none. */ +/* Return the next node of the iterator in the order given when it was + started; or NULL if there are no more nodes. */ inline struct interval_node* interval_generator_next (struct interval_generator *g) { + eassert (g->running); if (! g) return NULL; struct interval_node * const null = ITREE_NULL; @@ -862,10 +850,11 @@ interval_generator_next (struct interval_generator *g) /* Limit G to the new interval [BEGIN, END), which must be a subset of the current one. I.E. it can't grow on either side. */ -static inline void +inline void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { + eassert (g->running); eassert (begin >= g->begin); eassert (end <= g->end); g->begin = max (begin, g->begin); @@ -923,6 +912,8 @@ interval_tree_inherit_offset (const struct interval_tree *tree, if (node->right != ITREE_NULL) node->right->offset += node->offset; node->offset = 0; + /* FIXME: I wonder when/why this condition can be false, and more generally + why we'd want to propagate offsets that may not be fully up-to-date. */ if (node == tree->root || node->parent->otick == tree->otick) node->otick = tree->otick; } diff --git a/src/itree.h b/src/itree.h index f1ef7f99463..b9294c5662c 100644 --- a/src/itree.h +++ b/src/itree.h @@ -60,9 +60,6 @@ struct interval_tree uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; - bool_bf iter_running : 1; - const char* file; - int line; }; enum interval_tree_order { @@ -84,9 +81,9 @@ bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); -void interval_tree_iter_narrow (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_iter_finish (struct interval_tree *); -struct interval_node *interval_tree_iter_next (struct interval_tree *); +void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); +void interval_tree_iter_finish (struct interval_generator *); +struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); From c55254dcccf53df3e619737bb6670f0a573f27a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= Date: Sat, 1 Oct 2022 08:28:14 +0200 Subject: [PATCH 25/70] Make it compile with ITREE_DEBUG defined * src/buffer.c (make_lispy_interval_node): Use make_fixnum. (overlay_tree): Use ITREE_NULL. --- src/buffer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 19937216ed5..1e01750e16b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4913,15 +4913,15 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, static Lisp_Object make_lispy_interval_node (const struct interval_node *node) { - return listn (CONSTYPE_HEAP, 12, + return listn (12, intern (":begin"), - make_number (node->begin), + make_fixnum (node->begin), intern (":end"), - make_number (node->end), + make_fixnum (node->end), intern (":limit"), - make_number (node->limit), + make_fixnum (node->limit), intern (":offset"), - make_number (node->offset), + make_fixnum (node->offset), intern (":rear-advance"), node->rear_advance ? Qt : Qnil, intern (":front-advance"), @@ -4932,7 +4932,7 @@ static Lisp_Object overlay_tree (const struct interval_tree *tree, const struct interval_node *node) { - if (node == &tree->nil) + if (node == ITREE_NULL) return Qnil; return list3 (make_lispy_interval_node (node), overlay_tree (tree, node->left), From 1303f55161ae40cc98ccddc37755b58b68840798 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 2 Oct 2022 01:30:44 -0400 Subject: [PATCH 26/70] New ITREE_FOREACH macro * src/itree.h (interval_tree_iter_start): Adjust type. (interval_tree_nodes): Delete declaration. (ITREE_FOREACH, ITREE_FOREACH_ABORT, ITREE_FOREACH_NARROW): New macros. * src/itree.c (interval_tree_contains, interval_tree_insert_gap): Use the new ITREE_FOREACH macro. (interval_tree_nodes): Delete function. (interval_tree_iter_start): Return the iterator. (interval_generator_next, interval_tree_destroy): Don't accept a NULL arg any more. * src/xdisp.c (load_overlay_strings, strings_with_newlines): * src/textprop.c (get_char_property_and_overlay): * src/buffer.c (copy_overlays, delete_all_overlays) (set_overlays_multibyte, swap_buffer_overlays, overlays_in) (next_overlay_change, previous_overlay_change, overlay_touches_p) (overlay_strings, Foverlay_lists, report_overlay_modification) (evaporate_overlays): Use the new ITREE_FOREACH macro. * src/buffer.h (buffer_overlay_iter_start1) (buffer_overlay_iter_start, buffer_overlay_iter_next) (buffer_overlay_iter_finish, buffer_overlay_iter_narrow): Delete declarations. --- src/buffer.c | 97 +++++++++++++++++++++----------------------------- src/buffer.h | 33 ----------------- src/itree.c | 57 +++++++++-------------------- src/itree.h | 52 +++++++++++++++++++++++++-- src/textprop.c | 5 +-- src/xdisp.c | 17 +++------ 6 files changed, 113 insertions(+), 148 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 19937216ed5..8d02d705b55 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -647,8 +647,7 @@ copy_overlays (struct buffer *from, struct buffer *to) eassert (to && ! to->overlays); struct interval_node *node; - buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (from))) + ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { Lisp_Object ov = node->data; Lisp_Object copy = build_overlay (node->begin, node->end, @@ -657,7 +656,6 @@ copy_overlays (struct buffer *from, struct buffer *to) Fcopy_sequence (OVERLAY_PLIST (ov))); add_buffer_overlay (to, XOVERLAY (copy)); } - buffer_overlay_iter_finish (from); } bool @@ -929,14 +927,12 @@ delete_all_overlays (struct buffer *b) Of course, we can't set them to NULL from within the iteration because the iterator may need them (tho we could if we added an ITREE_POST_ORDER iteration order). */ - buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (b))) + ITREE_FOREACH (node, b->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { modify_overlay (b, node->begin, node->end); /* Where are the nodes freed ? --ap */ XOVERLAY (node->data)->buffer = NULL; } - buffer_overlay_iter_finish (b); interval_tree_clear (b->overlays); } @@ -944,7 +940,6 @@ static void free_buffer_overlays (struct buffer *b) { /* Actually this does not free any overlay, but the tree only. --ap */ - eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); if (b->overlays) { interval_tree_destroy (b->overlays); @@ -969,9 +964,16 @@ set_overlays_multibyte (bool multibyte) struct interval_tree *tree = current_buffer->overlays; const intmax_t size = interval_tree_size (tree); + /* We can't use `interval_node_set_region` at the same time + as we iterate over the itree, so we need an auxiliary storage + to keep the list of nodes. */ USE_SAFE_ALLOCA; SAFE_NALLOCA (nodes, 1, size); - interval_tree_nodes (tree, nodes, ITREE_ASCENDING); + { + struct interval_node *node, **cursor = nodes; + ITREE_FOREACH (node, tree, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) + *(cursor++) = node; + } for (int i = 0; i < size; ++i, ++nodes) { @@ -2418,15 +2420,11 @@ swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) + ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = other; - buffer_overlay_iter_finish (buffer); - buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (other))) + ITREE_FOREACH (node, other->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = buffer; - buffer_overlay_iter_finish (other); /* Swap the interval trees. */ void *tmp = buffer->overlays; @@ -2951,23 +2949,24 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, Lisp_Object *vec = *vec_ptr; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, beg, - /* Find empty OV at Z ? */ - (end >= ZV && empty) ? ZV + 1 : ZV, - ITREE_ASCENDING); - - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, beg, + /* Find empty OV at Z ? */ + (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) { if (node->begin > end) { next = min (next, node->begin); + ITREE_FOREACH_ABORT (); break; } else if (node->begin == end) { next = node->begin; if ((! empty || end < ZV) && beg < end) - break; + { + ITREE_FOREACH_ABORT (); + break; + } } if (! empty && node->begin == node->end) @@ -2985,7 +2984,6 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, /* Keep counting overlays even if we can't return them all. */ idx++; } - buffer_overlay_iter_finish (current_buffer); if (next_ptr) *next_ptr = next ? next : ZV; @@ -3012,8 +3010,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, next, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos, next, ASCENDING) { if (node->begin > pos) { @@ -3021,15 +3018,15 @@ next_overlay_change (ptrdiff_t pos) of pos, because the search is limited to [pos,next) . */ eassert (node->begin < next); next = node->begin; + ITREE_FOREACH_ABORT (); break; } else if (node->begin < node->end && node->end < next) { next = node->end; - buffer_overlay_iter_narrow (current_buffer, pos, next); + ITREE_FOREACH_NARROW (pos, next); } } - buffer_overlay_iter_finish (current_buffer); return next; } @@ -3040,16 +3037,14 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - buffer_overlay_iter_start (current_buffer, prev, pos, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, prev, pos, DESCENDING) { if (node->end < pos) prev = node->end; else prev = max (prev, node->begin); - buffer_overlay_iter_narrow (current_buffer, prev, pos); + ITREE_FOREACH_NARROW (prev, pos); } - buffer_overlay_iter_finish (current_buffer); return prev; } @@ -3123,19 +3118,16 @@ bool overlay_touches_p (ptrdiff_t pos) { struct interval_node *node; - bool result = false; /* We need to find overlays ending in pos, as well as empty ones at pos. */ - buffer_overlay_iter_start (current_buffer, - pos - 1, pos + 1, ITREE_DESCENDING); - - while (! result && (node = buffer_overlay_iter_next (current_buffer))) - result = (node->begin == pos || node->end == pos); - - buffer_overlay_iter_finish (current_buffer); - - return result; + ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING) + if (node->begin == pos || node->end == pos) + { + ITREE_FOREACH_ABORT (); + return true; + } + return false; } @@ -3342,9 +3334,7 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; - buffer_overlay_iter_start (current_buffer, - pos - 1, pos + 1, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -3358,6 +3348,8 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) if (WINDOWP (window) && XWINDOW (window) != w) continue; Lisp_Object str; + /* FIXME: Are we really sure that `record_overlay_string` can + never cause a non-local exit? */ if (startpos == pos && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) record_overlay_string (&overlay_heads, str, @@ -3372,7 +3364,6 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) Foverlay_get (overlay, Qpriority), endpos - startpos); } - buffer_overlay_iter_finish (current_buffer); if (overlay_tails.used > 1) qsort (overlay_tails.buf, overlay_tails.used, sizeof (struct sortstr), @@ -3842,10 +3833,8 @@ However, the overlays you get are the real objects that the buffer uses. */) Lisp_Object overlays = Qnil; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, BEG, Z, DESCENDING) overlays = Fcons (node->data, overlays); - buffer_overlay_iter_finish (current_buffer); return Fcons (overlays, Qnil); } @@ -3983,11 +3972,10 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (! current_buffer->overlays) return; - buffer_overlay_iter_start (current_buffer, - begin_arg - (insertion ? 1 : 0), - end_arg + (insertion ? 1 : 0), - ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, + begin_arg - (insertion ? 1 : 0), + end_arg + (insertion ? 1 : 0), + ASCENDING) { Lisp_Object overlay = node->data; ptrdiff_t obegin = OVERLAY_START (overlay); @@ -4016,7 +4004,6 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, add_overlay_mod_hooklist (prop, overlay); } } - buffer_overlay_iter_finish (current_buffer); } { /* Call the functions recorded in last_overlay_modification_hooks. @@ -4070,14 +4057,12 @@ evaporate_overlays (ptrdiff_t pos) Lisp_Object hit_list = Qnil; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos, pos, ASCENDING) { if (node->end == pos && ! NILP (Foverlay_get (node->data, Qevaporate))) hit_list = Fcons (node->data, hit_list); } - buffer_overlay_iter_finish (current_buffer); for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); diff --git a/src/buffer.h b/src/buffer.h index ad3b2ad6df5..a4e3934cad2 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1442,39 +1442,6 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) ov->buffer = NULL; } -INLINE void -buffer_overlay_iter_start1 (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order, const char* file, int line) -{ - if (b->overlays) - interval_tree_iter_start (b->overlays, begin, end, order, file, line); -} - -#define buffer_overlay_iter_start(b, begin, end, order) \ - buffer_overlay_iter_start1 ((b), (begin), (end), (order), __FILE__, __LINE__) - -INLINE struct interval_node* -buffer_overlay_iter_next (struct buffer *b) -{ - if (! b->overlays) - return NULL; - return interval_generator_next (b->overlays->iter); -} - -INLINE void -buffer_overlay_iter_finish (struct buffer *b) -{ - if (b->overlays) - interval_tree_iter_finish (b->overlays->iter); -} - -INLINE void -buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) -{ - if (b->overlays) - interval_generator_narrow (b->overlays->iter, begin, end); -} - /* Return the start of OV in its buffer, or -1 if OV is not associated with any buffer. */ diff --git a/src/itree.c b/src/itree.c index 4f8aea924ac..eeecaf1839a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -325,7 +325,7 @@ interval_tree_create (void) return tree; } -/* Reset the tree TREE to its empty state. */ +/* Reset the tree TREE to its empty state. */ void interval_tree_clear (struct interval_tree *tree) @@ -345,7 +345,7 @@ interval_tree_clear (struct interval_tree *tree) } #ifdef ITREE_TESTING -/* Initialize a pre-allocated tree (presumably on the stack). */ +/* Initialize a pre-allocated tree (presumably on the stack). */ static void interval_tree_init (struct interval_tree *tree) @@ -355,12 +355,11 @@ interval_tree_init (struct interval_tree *tree) } #endif -/* Release a tree, freeing its allocated memory. */ +/* Release a tree, freeing its allocated memory. */ void interval_tree_destroy (struct interval_tree *tree) { - if (! tree) - return; + eassert (tree->root == ITREE_NULL); if (tree->iter) interval_generator_destroy (tree->iter); xfree (tree); @@ -389,14 +388,14 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update - ancestors limit values. */ + ancestors limit values. */ while (child != ITREE_NULL) { parent = child; offset += child->offset; child->limit = max (child->limit, node->end - offset); /* This suggests that nodes in the right subtree are strictly - greater. But this is not true due to later rotations. */ + greater. But this is not true due to later rotations. */ child = node->begin <= child->begin ? child->left : child->right; } @@ -430,15 +429,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) bool interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { + eassert (node); struct interval_node *other; - - interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); - while ((other = interval_generator_next (tree->iter))) + ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) if (other == node) - break; + { + ITREE_FOREACH_ABORT (); + return true; + } - interval_tree_iter_finish (tree->iter); - return other == node; + return false; } /* Remove NODE from TREE and return it. NODE must exist in TREE. */ @@ -508,34 +508,12 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } -/* Fill memory pointed at via NODES with all nodes of TREE in the - given ORDER. - - The size of NODES must be sufficiently large. - */ - -void -interval_tree_nodes (struct interval_tree *tree, - struct interval_node **nodes, - enum interval_tree_order order) -{ - struct interval_node *node; - - interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); - while ((node = interval_generator_next (tree->iter))) - { - *nodes = node; - ++nodes; - } - interval_tree_iter_finish (tree->iter); -} - /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. */ -void +struct interval_generator * interval_tree_iter_start (struct interval_tree *tree, ptrdiff_t begin, ptrdiff_t end, enum interval_tree_order order, @@ -553,6 +531,7 @@ interval_tree_iter_start (struct interval_tree *tree, iter->running = true; iter->file = file; iter->line = line; + return iter; } /* Stop using the iterator. */ @@ -600,15 +579,12 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, - ITREE_PRE_ORDER, __FILE__, __LINE__); - while ((node = interval_generator_next (tree->iter))) + ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER) { if (node->begin == pos && node->front_advance && (node->begin != node->end || node->rear_advance)) interval_stack_push (saved, node); } - interval_tree_iter_finish (tree->iter); for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); @@ -784,7 +760,6 @@ inline struct interval_node* interval_generator_next (struct interval_generator *g) { eassert (g->running); - if (! g) return NULL; struct interval_node * const null = ITREE_NULL; struct interval_node *node; diff --git a/src/itree.h b/src/itree.h index b9294c5662c..1f019a2607e 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,6 +59,10 @@ struct interval_tree struct interval_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ + /* FIXME: We can only have one iteration active per tree, which is very + restrictive. Actually, in practice this is no better than limiting + to a single active iteration *globally*, so we could move this `iter` + to a global variable! */ struct interval_generator *iter; }; @@ -79,12 +83,56 @@ void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, +struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); void interval_tree_iter_finish (struct interval_generator *); struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); + +/* Iterate over the intervals between BEG and END in the tree T. + N will hold successive nodes. ORDER can be one of : `ASCENDING`, + `DESCENDING`, or `PRE_ORDER`. + It should be used as: + + ITREE_FOREACH (n, t, beg, end, order) + { + .. do the thing with n .. + } + + BEWARE: + - The expression T may be evaluated more than once, so make sure + it is cheap a pure. + - Only a single iteration can happen at a time, so make sure none of the + code within the loop can start another tree_itertion. + - If you need to exit the loop early, you *have* to call `ITREE_ABORT` + just before exiting (e.g. with `break` or `return`). + - Non-local exits are not supported within the body of the loop, + unless the caller makes sure `ITREE_ABORT` is called via some + kind of unwind_protect. + - Don't modify the tree during the iteration. + */ +#define ITREE_FOREACH(n, t, beg, end, order) \ + /* FIXME: We'd want to declare `x` right here, but I can't figure out + how to make that work here: the `for` syntax only allows a single + clause for the var declarations where we need 2 different types. + We could use the `struct {foo x; bar y; } p;` trick to declare two + vars `p.x` and `p.y` of unrelated types, but then none of the names + of the vars matches the `n` we receive :-(. */ \ + if (!t) \ + { } \ + else \ + for (struct interval_generator *itree_iter_ \ + = interval_tree_iter_start (t, beg, end, ITREE_##order, \ + __FILE__, __LINE__); \ + ((n = interval_generator_next (itree_iter_)) \ + || (interval_tree_iter_finish (itree_iter_), false));) + +#define ITREE_FOREACH_ABORT() \ + interval_tree_iter_finish (itree_iter_) + +#define ITREE_FOREACH_NARROW(beg, end) \ + interval_generator_narrow (itree_iter_, beg, end) + #endif diff --git a/src/textprop.c b/src/textprop.c index c2c3622d05f..04fae528097 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -643,10 +643,8 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, && pos <= BUF_ZV (b))) xsignal1 (Qargs_out_of_range, position); - buffer_overlay_iter_start (b, pos, pos + 1, ITREE_ASCENDING); - /* Now check the overlays in order of decreasing priority. */ - while ((node = buffer_overlay_iter_next (b))) + ITREE_FOREACH (node, b->overlays, pos, pos + 1, ASCENDING) { Lisp_Object tem = Foverlay_get (node->data, prop); struct sortvec *this; @@ -662,7 +660,6 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, result_tem = tem; } } - buffer_overlay_iter_finish (b); if (result) { if (overlay) diff --git a/src/xdisp.c b/src/xdisp.c index 6ecd3bdf24f..d585d57fd05 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -6568,10 +6568,8 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) while (false) - buffer_overlay_iter_start (current_buffer, - charpos - 1, charpos + 1, ITREE_DESCENDING); /* Process overlays. */ - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, charpos - 1, charpos + 1, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -6607,7 +6605,6 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) && SCHARS (str)) RECORD_OVERLAY_STRING (overlay, str, true); } - buffer_overlay_iter_finish (current_buffer); #undef RECORD_OVERLAY_STRING @@ -7005,10 +7002,8 @@ static bool strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) { struct interval_node *node; - /* Process overlays before the overlay center. */ - buffer_overlay_iter_start (current_buffer, - startpos, endpos, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + /* Process overlays. */ + ITREE_FOREACH (node, current_buffer->overlays, startpos, endpos, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -7032,20 +7027,18 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) { - buffer_overlay_iter_finish (current_buffer); + ITREE_FOREACH_ABORT (); return true; } str = Foverlay_get (overlay, Qafter_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) { - buffer_overlay_iter_finish (current_buffer); + ITREE_FOREACH_ABORT (); return true; } } - buffer_overlay_iter_finish (current_buffer); - /* Check for 'display' properties whose values include strings. */ Lisp_Object cpos = make_fixnum (startpos); Lisp_Object limpos = make_fixnum (endpos); From ba5fe8e7895a2cbfd2d666ca88c0ed96a73fbe29 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 2 Oct 2022 11:11:57 -0400 Subject: [PATCH 27/70] itree.c: Remove `tree` field from iterator * src/itree.c (interval_generator_ensure_space, interval_generator_reset): Inline and then delete functions. (interval_tree_inherit_offset): Only take the tree's `otick` as arg. Update all callers. (struct interval_generator): Remove `tree` field, replace with a copy of the tree's `otick`. (interval_stack_push_flagged): The arg should be a real node. (interval_tree_insert_gap): Prefer checking root==NULL rather than size==0. Skip loop when tree is empty to avoid pushing&processing the NULL node. (interval_tree_inherit_offset): Prefer parent==NULL rather than node==root to avoid accessing the tree object. --- src/itree.c | 151 +++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 84 deletions(-) diff --git a/src/itree.c b/src/itree.c index eeecaf1839a..1ce45a981eb 100644 --- a/src/itree.c +++ b/src/itree.c @@ -98,11 +98,10 @@ along with GNU Emacs. If not, see . */ inconsistently/interchangeably. We should fix this naming. */ static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); -static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); -static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); @@ -112,9 +111,6 @@ static void interval_tree_transplant (struct interval_tree *, struct interval_no static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); -static void interval_generator_reset (struct interval_generator *, - ptrdiff_t, ptrdiff_t, - enum interval_tree_order); static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ @@ -142,10 +138,10 @@ struct interval_stack /* State used when iterating interval. */ struct interval_generator { - struct interval_tree *tree; struct interval_stack *stack; ptrdiff_t begin; ptrdiff_t end; + uintmax_t otick; /* A copy of the tree's `otick`. */ enum interval_tree_order order; bool_bf running : 1; const char* file; @@ -222,6 +218,8 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { + eassert (node && node != ITREE_NULL); + /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant with the work of `interval_generator_ensure_space` but it's still needed here because `interval_generator_next` can push up to 3 elements per @@ -450,7 +448,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); if (node->left == ITREE_NULL || node->right == ITREE_NULL) { struct interval_node *subst @@ -475,7 +473,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->right = node->right; min->right->parent = min; } - interval_tree_inherit_offset (tree, min); + interval_tree_inherit_offset (tree->otick, min); interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; @@ -504,7 +502,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) if (node != tree->root) interval_tree_validate (tree, node->parent); - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); return node; } @@ -527,10 +525,16 @@ interval_tree_iter_start (struct interval_tree *tree, iter->file, iter->line, file, line); emacs_abort (); } - interval_generator_reset (iter, begin, end, order); - iter->running = true; + iter->begin = begin; + iter->end = end; + iter->otick = tree->otick; + iter->order = order; + interval_stack_clear (iter->stack); + if (begin <= end && tree->root != ITREE_NULL) + interval_stack_push_flagged (iter->stack, tree->root, false); iter->file = file; iter->line = line; + iter->running = true; return iter; } @@ -549,7 +553,8 @@ interval_tree_iter_finish (struct interval_generator *iter) static inline void interval_tree_iter_ensure_space (struct interval_tree *tree) { - interval_generator_ensure_space (tree->iter); + interval_stack_ensure_space (tree->iter->stack, + interval_tree_max_height (tree) + 1); } static int @@ -570,7 +575,7 @@ interval_tree_max_height (const struct interval_tree *tree) void interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->size == 0) + if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate generator/stack anew every time. */ @@ -588,45 +593,48 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); - /* We can't use a generator here, because we can't effectively narrow AND shift some subtree at the same time. */ - const int size = interval_tree_max_height (tree) + 1; - struct interval_stack *stack = interval_stack_create (size); - interval_stack_push (stack, tree->root); - nodeptr_and_flag nav; - while ((nav = interval_stack_pop (stack), - node = nav_nodeptr (nav))) + if (tree->root != ITREE_NULL) { - /* Process in pre-order. */ - interval_tree_inherit_offset (tree, node); - if (node->right != ITREE_NULL) + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + interval_stack_push (stack, tree->root); + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack), + node = nav_nodeptr (nav))) { - if (node->begin > pos) + /* Process in pre-order. */ + interval_tree_inherit_offset (tree->otick, node); + if (node->right != ITREE_NULL) { - /* All nodes in this subtree are shifted by length. */ - node->right->offset += length; - ++tree->otick; + if (node->begin > pos) + { + /* All nodes in this subtree are shifted by length. */ + node->right->offset += length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); } - else - interval_stack_push (stack, node->right); - } - if (node->left != ITREE_NULL - && pos <= node->left->limit + node->left->offset) - interval_stack_push (stack, node->left); + if (node->left != ITREE_NULL + && pos <= node->left->limit + node->left->offset) + interval_stack_push (stack, node->left); - /* node->begin == pos implies no front-advance. */ - if (node->begin > pos) - node->begin += length; - if (node->end > pos || (node->end == pos && node->rear_advance)) - { - node->end += length; - interval_tree_propagate_limit (tree, node); + /* node->begin == pos implies no front-advance. */ + if (node->begin > pos) + node->begin += length; + if (node->end > pos || (node->end == pos && node->rear_advance)) + { + node->end += length; + interval_tree_propagate_limit (tree, node); + } } + interval_stack_destroy (stack); } - interval_stack_destroy (stack); /* Reinsert nodes starting at POS having front-advance. */ + nodeptr_and_flag nav; while ((nav = interval_stack_pop (saved), node = nav_nodeptr (nav))) { @@ -645,7 +653,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l void interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->size == 0) + if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate stack anew every time. */ @@ -662,7 +670,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l while ((nav = interval_stack_pop (stack))) { node = nav_nodeptr (nav); - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); if (node->right != ITREE_NULL) { if (node->begin > pos + length) @@ -705,38 +713,14 @@ interval_generator_create (struct interval_tree *tree) const int size = interval_tree_max_height (tree) + 1; g->stack = interval_stack_create (size); - g->tree = tree; g->running = false; - interval_generator_reset (g, 1, 0, ITREE_ASCENDING); + g->begin = 0; + g->end = 0; + g->file = NULL; + g->line = 0; return g; } -/* Reset generator G such that it iterates over intervals intersecting - with [BEGIN, END) in the given ORDER. */ - -void -interval_generator_reset (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) -{ - if (! g) return; - - g->begin = begin; - g->end = end; - g->order = order; - interval_stack_clear (g->stack); - if (begin <= end && g->tree->size > 0) - interval_stack_push_flagged (g->stack, g->tree->root, false); -} - -/* Allocate enough space for the tree of G in its current shape. */ - -static inline void -interval_generator_ensure_space (struct interval_generator *g) -{ - interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); -} - /* Return true, if NODE's interval intersects with [BEGIN, END). Note: We always include empty nodes at BEGIN (and not at END), but if BEGIN==END, then we don't include non-empty nodes starting @@ -785,7 +769,7 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const left = node->left; struct interval_node * const right = node->right; - interval_tree_inherit_offset (g->tree, node); + interval_tree_inherit_offset (g->otick, node); switch (g->order) { case ITREE_ASCENDING: @@ -872,11 +856,9 @@ interval_tree_update_limit (const struct interval_tree *tree, */ static void -interval_tree_inherit_offset (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - - if (node->otick == tree->otick) + if (node->otick == otick) return; node->begin += node->offset; @@ -889,8 +871,8 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->offset = 0; /* FIXME: I wonder when/why this condition can be false, and more generally why we'd want to propagate offsets that may not be fully up-to-date. */ - if (node == tree->root || node->parent->otick == tree->otick) - node->otick = tree->otick; + if (node->parent == ITREE_NULL || node->parent->otick == otick) + node->otick = otick; } /* Update limit of NODE and its ancestors. Stop when it becomes @@ -910,8 +892,9 @@ interval_tree_propagate_limit (const struct interval_tree *tree, return; while (1) { - ptrdiff_t newlimit = max (node->end, max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); if (newlimit == node->limit) break; node->limit = newlimit; @@ -930,8 +913,8 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod struct interval_node *right = node->right; - interval_tree_inherit_offset (tree, node); - interval_tree_inherit_offset (tree, right); + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, right); /* Turn right's left subtree into node's right subtree. */ node->right = right->left; @@ -972,8 +955,8 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no struct interval_node *left = node->left; - interval_tree_inherit_offset (tree, node); - interval_tree_inherit_offset (tree, left); + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, left); node->left = left->right; if (left->right != ITREE_NULL) From b8fbd42f0a7caa4cd9e2d50dd4e4b2101ac78acd Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 2 Oct 2022 12:21:13 -0400 Subject: [PATCH 28/70] mark_overlays: Use the normal ITREE_FOREACH This commit basically reverts commit 5b954f8f9. The problem of nested iterations hasn't been fixed in the mean time, but since the GC can run arbitrary ELisp code (via `post-gc-hook`), running the GC from within an itree iteration is already unsafe anyway :-( * src/alloc.c (mark_overlays): Delete function. (mark_buffer): Use ITREE_FOREACH. --- src/alloc.c | 20 +++----------------- src/itree.h | 3 ++- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index db8f39a60e0..50968b7e121 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6512,21 +6512,6 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } -static void -mark_overlays (struct interval_tree *it, struct interval_node *in) -{ - /* `left/right` are set to NULL when the overlay is deleted, but - they use the `null` node instead when the overlay is not deleted - (i.e. is within an overlay tree). */ - eassert (in); - if (in == ITREE_NULL) - return; - - mark_object (in->data); - mark_overlays (it, in->left); - mark_overlays (it, in->right); -} - /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6548,8 +6533,9 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - if (buffer->overlays) - mark_overlays (buffer->overlays, buffer->overlays->root); + struct interval_node *node; + ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) + mark_object (node->data); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && diff --git a/src/itree.h b/src/itree.h index 1f019a2607e..29bc8dd1b25 100644 --- a/src/itree.h +++ b/src/itree.h @@ -105,7 +105,8 @@ void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); - The expression T may be evaluated more than once, so make sure it is cheap a pure. - Only a single iteration can happen at a time, so make sure none of the - code within the loop can start another tree_itertion. + code within the loop can start another tree iteration, i.e. it shouldn't + be able to run ELisp code (or GC for that matter). - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - Non-local exits are not supported within the body of the loop, From 2c4a3910b384a1f5b14d282818b04e25785e25b0 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 2 Oct 2022 12:27:37 -0400 Subject: [PATCH 29/70] itree: Use a single iterator object Instead of having one iterator object per buffer, use just a single global one. There is virtually no benefit to having per-buffer iterators anyway: if two iterations can be active at the same time, then there can be cases where those two iterations happen to operate on the same buffer :-( * src/itree.h (struct interval_tree): Remove `iter` field. * src/itree.c (interval_generator_destroy) (interval_tree_iter_ensure_space): Delete functions. (iter): New global variable. (init_itree_null): Rename to `itree_init` and adjust all callers. Initialize `iter` as well. (interval_tree_create, interval_tree_init): Don't initialize `iter` field any more. (interval_tree_destroy): Don't destroy `iter` field any more. (interval_tree_insert): Don't bother growing the iterator (it's grown in `interval_stack_push_flagged` if needed anyway, and in any case there's no `iter` here to grow any more). (interval_tree_remove): Tweak assertion to be more precise and self-evident. (interval_tree_iter_start): Use the global `iter`. (interval_generator_create): Make it work with a NULL argument. --- src/itree.c | 81 +++++++++++++++++++++++------------------------------ src/itree.h | 5 ---- 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/src/itree.c b/src/itree.c index 1ce45a981eb..046ad2fa8f0 100644 --- a/src/itree.c +++ b/src/itree.c @@ -110,19 +110,10 @@ static void interval_tree_remove_fix (struct interval_tree *, struct interval_no static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); -static void interval_generator_destroy (struct interval_generator *); -static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ struct interval_node itree_null; -static void -init_itree_null (void) -{ - itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; -} - - /* +------------------------------------------------------------------------------------+ */ typedef uintptr_t nodeptr_and_flag; @@ -148,6 +139,20 @@ struct interval_generator int line; }; +/* Ideally, every iteration would use its own `iter` object, so we could + have several iterations active at the same time. In practice, iterations + are limited by the fact we don't allow modifying the tree at the same + time, making the use of nested iterations quite rare anyway. + So we just use a single global iterator instead for now. */ +static struct interval_generator *iter; + +static void +itree_init (void) +{ + itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; + iter = interval_generator_create (NULL); +} + /* +===================================================================================+ * | Stack @@ -208,7 +213,8 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) if (nelements > stack->size) { stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + stack->nodes = xrealloc (stack->nodes, + stack->size * sizeof (*stack->nodes)); } } @@ -220,11 +226,12 @@ interval_stack_push_flagged (struct interval_stack *stack, { eassert (node && node != ITREE_NULL); - /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant - with the work of `interval_generator_ensure_space` but it's still needed - here because `interval_generator_next` can push up to 3 elements per - node it visits, so for a tree of depth N it can use up a stack - space up to 3 times larger than what we computed. :-( */ + /* FIXME: While the stack used in the iterator is bounded by the tree + depth and could be easily pre-allocated to a large enough size to avoid + this "ensure" check, `interval_stack_push` is also used elsewhere to + simply collect some subset of the overlays, where it's only bounded by + the total number of overlays in the buffer (which can be large and thus + preferably not pre-allocated needlessly). */ interval_stack_ensure_space (stack, stack->length + 1); stack->nodes[stack->length] = make_nav (node, flag); @@ -315,11 +322,10 @@ interval_tree_create (void) /* FIXME? Maybe avoid the initialization of itree_null in the same way that is used to call mem_init in alloc.c? It's not really important though. */ - init_itree_null (); + itree_init (); struct interval_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); - tree->iter = interval_generator_create (tree); return tree; } @@ -349,7 +355,7 @@ static void interval_tree_init (struct interval_tree *tree) { interval_tree_clear (tree); - tree->iter = interval_generator_create (tree); + /* tree->iter = interval_generator_create (tree); */ } #endif @@ -358,8 +364,8 @@ void interval_tree_destroy (struct interval_tree *tree) { eassert (tree->root == ITREE_NULL); - if (tree->iter) - interval_generator_destroy (tree->iter); + /* if (tree->iter) + * interval_generator_destroy (tree->iter); */ xfree (tree); } @@ -419,7 +425,6 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; interval_tree_insert_fix (tree, node); - interval_tree_iter_ensure_space (tree); } /* Return true, if NODE is a member of TREE. */ @@ -488,7 +493,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != ITREE_NULL)); + eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); return node; } @@ -517,7 +522,7 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - struct interval_generator *iter = tree->iter; + /* struct interval_generator *iter = tree->iter; */ if (iter->running) { fprintf (stderr, @@ -535,6 +540,8 @@ interval_tree_iter_start (struct interval_tree *tree, iter->file = file; iter->line = line; iter->running = true; + /* interval_stack_ensure_space (iter->stack, + 2 * interval_tree_max_height (tree)); */ return iter; } @@ -547,16 +554,6 @@ interval_tree_iter_finish (struct interval_generator *iter) iter->running = false; } -/* Ensure that the tree's iterator does not need to allocate space - until the tree grows in size. */ - -static inline void -interval_tree_iter_ensure_space (struct interval_tree *tree) -{ - interval_stack_ensure_space (tree->iter->stack, - interval_tree_max_height (tree) + 1); -} - static int interval_tree_max_height (const struct interval_tree *tree) { @@ -709,8 +706,11 @@ static struct interval_generator * interval_generator_create (struct interval_tree *tree) { struct interval_generator *g = xmalloc (sizeof *g); - /* FIXME: Is tree ever non-empty here? */ - const int size = interval_tree_max_height (tree) + 1; + /* 19 here just avoids starting with a silly-small stack. + FIXME: Since this stack only needs to be about 2*max_depth + in the worst case, we could completely pre-allocate it to something + like word-bit-size * 2 and then never worry about growing it. */ + const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; g->stack = interval_stack_create (size); g->running = false; @@ -820,17 +820,6 @@ interval_generator_narrow (struct interval_generator *g, g->end = min (end, g->end); } -/* Free the memory allocated for G. */ - -void -interval_generator_destroy (struct interval_generator *g) -{ - if (! g) return; - if (g->stack) - interval_stack_destroy (g->stack); - xfree (g); -} - /* +===================================================================================+ * | Internal Functions diff --git a/src/itree.h b/src/itree.h index 29bc8dd1b25..a04ff6827cf 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,11 +59,6 @@ struct interval_tree struct interval_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ - /* FIXME: We can only have one iteration active per tree, which is very - restrictive. Actually, in practice this is no better than limiting - to a single active iteration *globally*, so we could move this `iter` - to a global variable! */ - struct interval_generator *iter; }; enum interval_tree_order { From 4f4327c0b0db7f908ae7153e5ddc802b9808dc2e Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Tue, 4 Oct 2022 22:29:27 -0400 Subject: [PATCH 30/70] itree.c: Remove some unnecessary `tree` arguments * src/itree.c (interval_tree_propagate_limit) (interval_tree_subtree_min): Remove `tree` argument. (interval_node_set_region, interval_tree_remove) (interval_tree_insert_gap, interval_tree_delete_gap): Adjust callers. --- src/itree.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/itree.c b/src/itree.c index 046ad2fa8f0..58456fbc6ee 100644 --- a/src/itree.c +++ b/src/itree.c @@ -102,13 +102,13 @@ static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, p static int interval_tree_max_height (const struct interval_tree *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); -static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min (struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); /* The sentinel node, the null node. */ @@ -310,7 +310,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } @@ -461,15 +461,16 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit (tree, subst); + interval_tree_propagate_limit (subst); } else { - struct interval_node *min = interval_tree_subtree_min (tree, node->right); + struct interval_node *min = interval_tree_subtree_min (node->right); struct interval_node *min_right = min->right; if (!min->red) broken = min->right; + eassert (min != ITREE_NULL); if (min->parent == node) min_right->parent = min; /* set parent, if min_right = null */ else @@ -483,8 +484,8 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit (tree, min_right); - interval_tree_propagate_limit (tree, min); + interval_tree_propagate_limit (min_right); + interval_tree_propagate_limit (min); } if (broken) @@ -624,7 +625,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } interval_stack_destroy (stack); @@ -688,7 +689,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos) { node->end = max (pos , node->end - length); - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } interval_stack_destroy (stack); @@ -872,8 +873,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) */ static void -interval_tree_propagate_limit (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_propagate_limit (struct interval_node *node) { if (node == ITREE_NULL) node = node->parent; @@ -887,7 +887,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, if (newlimit == node->limit) break; node->limit = newlimit; - if (node == tree->root) + if (node->parent == ITREE_NULL) break; node = node->parent; } @@ -1147,7 +1147,7 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* -interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) +interval_tree_subtree_min (struct interval_node *node) { if (node == ITREE_NULL) return node; From aa5a32ca2c07691f3f10b7ec0c423ade11479220 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 5 Oct 2022 12:12:01 -0400 Subject: [PATCH 31/70] itree.c: Clarify how the sentinel is used * src/itree.c (null_is_sane): New function. (interval_tree_iter_start): Use it to make sure we haven't garbled null. (itree_init): Fully initialize `itree_null` here... (interval_tree_clear): ...instead of here. (interval_tree_propagate_limit): Remove special code that read NULL->parent. (interval_tree_remove): Do it explicitly before the call in those two places where it can happen. --- src/itree.c | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/itree.c b/src/itree.c index 58456fbc6ee..dcad848c216 100644 --- a/src/itree.c +++ b/src/itree.c @@ -114,6 +114,19 @@ static struct interval_generator* interval_generator_create (struct interval_tre /* The sentinel node, the null node. */ struct interval_node itree_null; +static inline bool +null_is_sane (void) +{ + /* The sentinel node has most of its fields read-only, except for `parent`, + `left`, `right` which are write only. */ + return itree_null.red == false + && itree_null.otick == 0 + && itree_null.offset == 0 + && itree_null.begin == PTRDIFF_MIN + && itree_null.end == PTRDIFF_MIN + && itree_null.limit == PTRDIFF_MIN; +} + /* +------------------------------------------------------------------------------------+ */ typedef uintptr_t nodeptr_and_flag; @@ -149,7 +162,13 @@ static struct interval_generator *iter; static void itree_init (void) { - itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; + struct interval_node *null = ITREE_NULL; + null->left = null->right = null->parent = null; + null->offset = null->otick = 0; + null->begin = PTRDIFF_MIN; + null->end = PTRDIFF_MIN; + null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ + null->red = false; iter = interval_generator_create (NULL); } @@ -310,6 +329,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -334,16 +354,7 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - /* FIXME: Why is this done? */ - struct interval_node *null = ITREE_NULL; - null->left = null->right = null->parent = null; - null->offset = null->otick = 0; - null->begin = PTRDIFF_MIN; - null->end = PTRDIFF_MIN; - null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->red = false; - - tree->root = null; + tree->root = ITREE_NULL; tree->otick = 1; tree->size = 0; } @@ -461,7 +472,9 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit (subst); + interval_tree_propagate_limit + /* FIXME: null->parent is supposed to be write only! */ + (subst == ITREE_NULL ? ITREE_NULL->parent : subst); } else { @@ -472,7 +485,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) broken = min->right; eassert (min != ITREE_NULL); if (min->parent == node) - min_right->parent = min; /* set parent, if min_right = null */ + { + if (min_right == ITREE_NULL) + ITREE_NULL->parent = min; /* set parent, if min_right = null */ + else + eassert (min_right->parent == min); + } else { interval_tree_transplant (tree, min->right, min); @@ -484,7 +502,9 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit (min_right); + interval_tree_propagate_limit + /* FIXME: null->parent is supposed to be write only! */ + (min_right == ITREE_NULL ? ITREE_NULL->parent : min_right); interval_tree_propagate_limit (min); } @@ -523,6 +543,7 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { + eassert (null_is_sane ()); /* struct interval_generator *iter = tree->iter; */ if (iter->running) { @@ -625,6 +646,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -689,6 +711,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos) { node->end = max (pos , node->end - length); + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -875,8 +898,6 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) static void interval_tree_propagate_limit (struct interval_node *node) { - if (node == ITREE_NULL) - node = node->parent; if (node == ITREE_NULL) return; From 5642b4a255171f5593ae56ae76c98daf7f4cd6ad Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 5 Oct 2022 16:35:31 -0400 Subject: [PATCH 32/70] itree.c: Fix incomplete update of `limit`s in corner cases `interval_tree_remove` called `interval_tree_propagate_limit (subst)` and `interval_tree_propagate_limit (min_right)` but both of those nodes are moved without touching their subtrees, so their `limit`s are "stable" causing `interval_tree_propagate_limit` to do nothing. Indeed we don't need to update those nodes's `limit`s but we *do* need to update their parents since those nodes have been moved. Incidentally, this removes some uses of `null->parent` :-) There are more uses of `null->parent`, tho, so I added more comments explaining them (with the help of the matching section of the book from which the algorithm was taken). * src/itree.c (interval_tree_update_limit): Remove unused arg `tree`. (interval_tree_rotate_left, interval_tree_rotate_right): Adjust callers. (interval_tree_contains): Mark as static. (itree_limit_is_stable, itree_limits_are_stable): New functions. (interval_tree_remove): Fix incomplete update of `limit`s in corner cases. (interval_generator_next): Add sanity check to make sure the `limit`s were properly updated. * src/itree.h (interval_tree_contains): Remove declaration. --- src/itree.c | 94 ++++++++++++++++++++++++++++++++++++++++------------- src/itree.h | 1 - 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/itree.c b/src/itree.c index dcad848c216..d6c2dd8e30d 100644 --- a/src/itree.c +++ b/src/itree.c @@ -100,7 +100,7 @@ along with GNU Emacs. If not, see . */ static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); -static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_update_limit (struct interval_node *); static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); @@ -440,7 +440,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Return true, if NODE is a member of TREE. */ -bool +static bool interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { eassert (node); @@ -455,13 +455,45 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } +static inline bool +itree_limit_is_stable (struct interval_node *node) +{ + if (node == ITREE_NULL) + return true; + ptrdiff_t newlimit = max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); + return (newlimit == node->limit); +} + +static inline bool +itree_limits_are_stable (struct interval_node *node) +{ + if (node == ITREE_NULL) + return true; + return itree_limit_is_stable (node) + && itree_limits_are_stable (node->right) + && itree_limits_are_stable (node->left); +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { + /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); + /* `broken`, if non-NULL, holds a node that's being moved up to where a black + node used to be, which may thus require further fixups in its parents + (done in `interval_tree_remove_fix`). + BEWARE: `null->parent` is usually write-only, *BUT* in this function, + we use `null->parent` to simplify the code for the case where the + "broken" node is null: `broken->parent` is set typically in + `interval_tree_transplant` and then used in + `interval_tree_remove_fix`. + This trick is described in Cormen et al.'s Introduction to Algorithms. */ + struct interval_node *broken = NULL; interval_tree_inherit_offset (tree->otick, node); @@ -471,29 +503,30 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) broken = subst; + /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit - /* FIXME: null->parent is supposed to be write only! */ - (subst == ITREE_NULL ? ITREE_NULL->parent : subst); } else { struct interval_node *min = interval_tree_subtree_min (node->right); struct interval_node *min_right = min->right; + struct interval_node *min_parent = min->parent; if (!min->red) - broken = min->right; + broken = min_right; eassert (min != ITREE_NULL); if (min->parent == node) { if (min_right == ITREE_NULL) - ITREE_NULL->parent = min; /* set parent, if min_right = null */ + /* BEWARE: Here is one place we set `null->parent`. */ + min_right->parent = min; else eassert (min_right->parent == min); } else { - interval_tree_transplant (tree, min->right, min); + /* BEWARE: Here is one place we may set `null->parent`. */ + interval_tree_transplant (tree, min_right, min); min->right = node->right; min->right->parent = min; } @@ -502,19 +535,27 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit - /* FIXME: null->parent is supposed to be write only! */ - (min_right == ITREE_NULL ? ITREE_NULL->parent : min_right); - interval_tree_propagate_limit (min); + interval_tree_update_limit (min); + /* This call "belongs" with the first `interval_tree_transplant` + (of `min_right`, done earlier in the `if`) but we prefer to do it + here ("late") because otherwise it would sometimes update part of + the tree with values that would be invalidated by the second + `interval_tree_transplant`. */ + interval_tree_propagate_limit (min_parent); } + interval_tree_propagate_limit (node->parent); + /* eassert (itree_limits_are_stable (tree->root)); */ if (broken) + /* BEWARE: Here is where we may end up relying on the `null->parent` + set earlier. */ interval_tree_remove_fix (tree, broken); node->right = node->left = node->parent = NULL; --tree->size; eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + /* eassert (itree_limits_are_stable (tree->root)); */ return node; } @@ -794,6 +835,7 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const right = node->right; interval_tree_inherit_offset (g->otick, node); + eassert (itree_limit_is_stable (node)); switch (g->order) { case ITREE_ASCENDING: @@ -852,8 +894,7 @@ interval_generator_narrow (struct interval_generator *g, /* Update NODE's limit attribute according to its children. */ static void -interval_tree_update_limit (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_update_limit (struct interval_node *node) { if (node == ITREE_NULL) return; @@ -952,8 +993,8 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod node->parent = right; /* Order matters here. */ - interval_tree_update_limit (tree, node); - interval_tree_update_limit (tree, right); + interval_tree_update_limit (node); + interval_tree_update_limit (right); } /* Perform the familiar right-rotation on node NODE. */ @@ -988,12 +1029,13 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no if (node != ITREE_NULL) node->parent = left; - interval_tree_update_limit (tree, left); - interval_tree_update_limit (tree, node); + interval_tree_update_limit (left); + interval_tree_update_limit (node); } -/* Repair the tree after an insertion. Part of the RB-Tree - algorithm. */ +/* Repair the tree after an insertion. + The new NODE was added as red, so we may have 2 reds in a row. + Rebalance the parents as needed to re-establish the RB invariants. */ static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) @@ -1066,12 +1108,16 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node tree->root->red = false; } -/* Repair the tree after a deletion. Part of the RB-Tree - algorithm. */ +/* Repair the tree after a deletion. + The black-depth of NODE is one less than that of its sibling, + so re-balance the parents to re-establish the RB invariants. */ static void interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) { + /* BEWARE: null->parent is usually write-only, *BUT* + NODE here can be the NULL node, in which case its `parent` field + has to be properly set to point to the intended "parent" node. */ while (node != tree->root && !node->red) { if (node == node->parent->left) @@ -1148,7 +1194,9 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node node->red = false; } -/* Link node SOURCE in DEST's place. */ +/* Link node SOURCE in DEST's place. + It's the caller's responsability to refresh the `limit`s + of DEST->parents afterwards. */ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, diff --git a/src/itree.h b/src/itree.h index a04ff6827cf..8f6bb667d64 100644 --- a/src/itree.h +++ b/src/itree.h @@ -76,7 +76,6 @@ void interval_tree_destroy (struct interval_tree *); intmax_t interval_tree_size (struct interval_tree *); void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); -bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); From 1f31534f510fdd9ed3166f761d736c0dda322db5 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 5 Oct 2022 22:55:54 -0400 Subject: [PATCH 33/70] itree.c: Fix corner case errors in offsets In some cases, `interval_tree_remove` could cause some nodes to inherit fewer (or additional) offsets than the should because nodes were transplanted between two parts of the tree where offsets had not been propagated "equally". So we remove/apply all offsets along the path between the two points of a transplant before doing the transplant. * src/itree.c (interval_tree_subtree_min): Move before first use; delete the declaration; add an `otick` argument, and use it to update offsets along the way. (interval_tree_remove): Update all offsets on the way from `node` to `min`. Reorder some of the operations so that when we transplant `min` to `node` those nodes are in the proper state where `interval_tree_transplant` can do its sanity checks. (itree_total_offset): New function. (interval_tree_transplant): Use it to sanity check that improper offsets aren't accidentally inherited/lost because of the transplant. (itree_newlimit): New function. (itree_limit_is_stable, interval_tree_update_limit) (interval_tree_propagate_limit): Use it. (null_is_sane): Remove `inline` annotation; it's not needed. (interval_tree_inherit_offset): Sanity check that `offset` is 0 when `otick` is uptodate. Skip the unneeded increments when the offset is 0. (interval_tree_insert_fix): Add sanity check that we indeed have 2 reds. --- src/itree.c | 121 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 47 deletions(-) diff --git a/src/itree.c b/src/itree.c index d6c2dd8e30d..545eb55b0d3 100644 --- a/src/itree.c +++ b/src/itree.c @@ -29,7 +29,7 @@ along with GNU Emacs. If not, see . */ some given interval. In order to perform this operation efficiently, every node stores a third value called LIMIT. (See https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree and its - source Introduction to Algorithms (Section 14.3), Cormen et al. .) + source Introduction to Algorithms, Cormen et al. .) ==== Finding intervals ==== @@ -108,17 +108,18 @@ static void interval_tree_rotate_right (struct interval_tree *, struct interval_ static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min (struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); /* The sentinel node, the null node. */ struct interval_node itree_null; -static inline bool +static bool null_is_sane (void) { /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. */ + `left`, `right` which are write only. + BEWARE: The `parent` field is actually used both for read&write + in the code of `interval_tree_remove`. */ return itree_null.red == false && itree_null.otick == 0 && itree_null.offset == 0 @@ -455,14 +456,21 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } -static inline bool +static inline ptrdiff_t +itree_newlimit (struct interval_node *node) +{ + eassert (node != ITREE_NULL); + return max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); +} + +static bool itree_limit_is_stable (struct interval_node *node) { if (node == ITREE_NULL) return true; - ptrdiff_t newlimit = max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = itree_newlimit (node); return (newlimit == node->limit); } @@ -476,6 +484,17 @@ itree_limits_are_stable (struct interval_node *node) && itree_limits_are_stable (node->left); } +static struct interval_node* +interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) +{ + if (node == ITREE_NULL) + return node; + while ((interval_tree_inherit_offset (otick, node), + node->left != ITREE_NULL)) + node = node->left; + return node; +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* @@ -508,33 +527,33 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) } else { - struct interval_node *min = interval_tree_subtree_min (node->right); + struct interval_node *min + = interval_tree_subtree_min (tree->otick, node->right); struct interval_node *min_right = min->right; struct interval_node *min_parent = min->parent; if (!min->red) broken = min_right; eassert (min != ITREE_NULL); - if (min->parent == node) - { - if (min_right == ITREE_NULL) - /* BEWARE: Here is one place we set `null->parent`. */ - min_right->parent = min; - else - eassert (min_right->parent == min); - } - else + /* `min` should not have any offsets any more so we can move nodes + underneath it without risking changing their begin/end. */ + eassert (min->offset == 0); + if (min->parent != node) { /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, min_right, min); min->right = node->right; - min->right->parent = min; } - interval_tree_inherit_offset (tree->otick, min); - interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; min->red = node->red; + /* FIXME: At this point node->right->parent = min but node->right + is a parent of `min` so total_offsets gets stuck in an inf-loop! */ + interval_tree_transplant (tree, min, node); + /* We set min->right->parent after `interval_tree_transplant` so + that calls to `itree_total_offset` don't get stuck in an inf-loop. */ + /* BEWARE: Here is one place we may set `null->parent`. */ + min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` (of `min_right`, done earlier in the `if`) but we prefer to do it @@ -544,7 +563,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (min_parent); } interval_tree_propagate_limit (node->parent); - /* eassert (itree_limits_are_stable (tree->root)); */ if (broken) /* BEWARE: Here is where we may end up relying on the `null->parent` @@ -899,8 +917,7 @@ interval_tree_update_limit (struct interval_node *node) if (node == ITREE_NULL) return; - node->limit = max (node->end, max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + node->limit = itree_newlimit (node); } /* Apply NODE's offset to its begin, end and limit values and @@ -913,16 +930,22 @@ static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { if (node->otick == otick) - return; + { + eassert (node->offset == 0); + return; + } - node->begin += node->offset; - node->end += node->offset; - node->limit += node->offset; - if (node->left != ITREE_NULL) - node->left->offset += node->offset; - if (node->right != ITREE_NULL) - node->right->offset += node->offset; - node->offset = 0; + if (node->offset) + { + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != ITREE_NULL) + node->left->offset += node->offset; + if (node->right != ITREE_NULL) + node->right->offset += node->offset; + node->offset = 0; + } /* FIXME: I wonder when/why this condition can be false, and more generally why we'd want to propagate offsets that may not be fully up-to-date. */ if (node->parent == ITREE_NULL || node->parent->otick == otick) @@ -943,9 +966,7 @@ interval_tree_propagate_limit (struct interval_node *node) return; while (1) { - ptrdiff_t newlimit = max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = itree_newlimit (node); if (newlimit == node->limit) break; node->limit = newlimit; @@ -1044,6 +1065,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ + eassert (node->red); if (node->parent == node->parent->parent->left) { @@ -1194,6 +1216,20 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node node->red = false; } +/* Return accumulated offsets of NODE's parents. */ +static ptrdiff_t +itree_total_offset (struct interval_node *node) +{ + eassert (node != ITREE_NULL); + ptrdiff_t offset = 0; + while (node->parent != ITREE_NULL) + { + node = node->parent; + offset += node->offset; + } + return offset; +} + /* Link node SOURCE in DEST's place. It's the caller's responsability to refresh the `limit`s of DEST->parents afterwards. */ @@ -1203,6 +1239,8 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour struct interval_node *dest) { eassert (tree && source && dest && dest != ITREE_NULL); + eassert (source == ITREE_NULL + || itree_total_offset (source) == itree_total_offset (dest)); if (dest == tree->root) tree->root = source; @@ -1214,17 +1252,6 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour source->parent = dest->parent; } - -static struct interval_node* -interval_tree_subtree_min (struct interval_node *node) -{ - if (node == ITREE_NULL) - return node; - while (node->left != ITREE_NULL) - node = node->left; - return node; -} - /* +===================================================================================+ * | Debugging From a1f1fdd291a708684262c2494eae5bba32857cd7 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 5 Oct 2022 23:48:47 -0400 Subject: [PATCH 34/70] * src/itree.c (interval_tree_remove_fix): Move before first use --- src/itree.c | 173 ++++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 87 deletions(-) diff --git a/src/itree.c b/src/itree.c index 545eb55b0d3..a0c3d6ab5e0 100644 --- a/src/itree.c +++ b/src/itree.c @@ -106,7 +106,6 @@ static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); @@ -495,6 +494,92 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) return node; } +/* Repair the tree after a deletion. + The black-depth of NODE is one less than that of its sibling, + so re-balance the parents to re-establish the RB invariants. */ + +static void +interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +{ + /* BEWARE: null->parent is usually write-only, *BUT* + NODE here can be the NULL node, in which case its `parent` field + has to be properly set to point to the intended "parent" node. */ + while (node != tree->root && !node->red) + { + if (node == node->parent->left) + { + struct interval_node *other = node->parent->right; + + if (other->red) /* case 1.a */ + { + other->red = false; + node->parent->red = true; + interval_tree_rotate_left (tree, node->parent); + other = node->parent->right; + } + + if (!other->left->red /* 2.a */ + && !other->right->red) + { + other->red = true; + node = node->parent; + } + else + { + if (!other->right->red) /* 3.a */ + { + other->left->red = false; + other->red = true; + interval_tree_rotate_right (tree, other); + other = node->parent->right; + } + other->red = node->parent->red; /* 4.a */ + node->parent->red = false; + other->right->red = false; + interval_tree_rotate_left (tree, node->parent); + node = tree->root; + } + } + else + { + struct interval_node *other = node->parent->left; + + if (other->red) /* 1.b */ + { + other->red = false; + node->parent->red = true; + interval_tree_rotate_right (tree, node->parent); + other = node->parent->left; + } + + if (!other->right->red /* 2.b */ + && !other->left->red) + { + other->red = true; + node = node->parent; + } + else + { + if (!other->left->red) /* 3.b */ + { + other->right->red = false; + other->red = true; + interval_tree_rotate_left (tree, other); + other = node->parent->left; + } + + other->red = node->parent->red; /* 4.b */ + node->parent->red = false; + other->left->red = false; + interval_tree_rotate_right (tree, node->parent); + node = tree->root; + } + } + } + + node->red = false; +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* @@ -1130,92 +1215,6 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node tree->root->red = false; } -/* Repair the tree after a deletion. - The black-depth of NODE is one less than that of its sibling, - so re-balance the parents to re-establish the RB invariants. */ - -static void -interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) -{ - /* BEWARE: null->parent is usually write-only, *BUT* - NODE here can be the NULL node, in which case its `parent` field - has to be properly set to point to the intended "parent" node. */ - while (node != tree->root && !node->red) - { - if (node == node->parent->left) - { - struct interval_node *other = node->parent->right; - - if (other->red) /* case 1.a */ - { - other->red = false; - node->parent->red = true; - interval_tree_rotate_left (tree, node->parent); - other = node->parent->right; - } - - if (!other->left->red /* 2.a */ - && !other->right->red) - { - other->red = true; - node = node->parent; - } - else - { - if (!other->right->red) /* 3.a */ - { - other->left->red = false; - other->red = true; - interval_tree_rotate_right (tree, other); - other = node->parent->right; - } - other->red = node->parent->red; /* 4.a */ - node->parent->red = false; - other->right->red = false; - interval_tree_rotate_left (tree, node->parent); - node = tree->root; - } - } - else - { - struct interval_node *other = node->parent->left; - - if (other->red) /* 1.b */ - { - other->red = false; - node->parent->red = true; - interval_tree_rotate_right (tree, node->parent); - other = node->parent->left; - } - - if (!other->right->red /* 2.b */ - && !other->left->red) - { - other->red = true; - node = node->parent; - } - else - { - if (!other->left->red) /* 3.b */ - { - other->right->red = false; - other->red = true; - interval_tree_rotate_left (tree, other); - other = node->parent->left; - } - - other->red = node->parent->red; /* 4.b */ - node->parent->red = false; - other->left->red = false; - interval_tree_rotate_right (tree, node->parent); - node = tree->root; - } - } - } - - node->red = false; -} - /* Return accumulated offsets of NODE's parents. */ static ptrdiff_t itree_total_offset (struct interval_node *node) From 8bd114b98a31a31c1091e937891b369a165add3a Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 5 Oct 2022 23:52:01 -0400 Subject: [PATCH 35/70] itree.c: Get rid of the trick using null->parent * src/itree.c (interval_tree_remove_fix): Add a `parent` argument. Change the loop so it always keeps both `node` and `parent` in sync, thus avoiding the use of `node->parent` on the initial node (since that one can be null). (interval_tree_remove): Manually keep track of the `broken` node's parent to pass it to `interval_tree_remove_fix`. --- src/itree.c | 101 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/itree.c b/src/itree.c index a0c3d6ab5e0..ed31ef11562 100644 --- a/src/itree.c +++ b/src/itree.c @@ -116,9 +116,7 @@ static bool null_is_sane (void) { /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. - BEWARE: The `parent` field is actually used both for read&write - in the code of `interval_tree_remove`. */ + `left`, `right` which are write only. */ return itree_null.red == false && itree_null.otick == 0 && itree_null.offset == 0 @@ -499,30 +497,36 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) so re-balance the parents to re-establish the RB invariants. */ static void -interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +interval_tree_remove_fix (struct interval_tree *tree, + struct interval_node *node, + struct interval_node *parent) { - /* BEWARE: null->parent is usually write-only, *BUT* - NODE here can be the NULL node, in which case its `parent` field - has to be properly set to point to the intended "parent" node. */ - while (node != tree->root && !node->red) + eassert (node == ITREE_NULL || node->parent == parent); + eassert (parent == ITREE_NULL + || node == parent->left || node == parent->right); + + while (parent != ITREE_NULL && !node->red) { - if (node == node->parent->left) + if (node == parent->left) { - struct interval_node *other = node->parent->right; + struct interval_node *other = parent->right; if (other->red) /* case 1.a */ { other->red = false; - node->parent->red = true; - interval_tree_rotate_left (tree, node->parent); - other = node->parent->right; + parent->red = true; + interval_tree_rotate_left (tree, parent); + parent = node->parent; + other = parent->right; } if (!other->left->red /* 2.a */ && !other->right->red) { other->red = true; - node = node->parent; + node = parent; + eassert (node != ITREE_NULL); + parent = node->parent; } else { @@ -531,32 +535,37 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node other->left->red = false; other->red = true; interval_tree_rotate_right (tree, other); - other = node->parent->right; + parent = node->parent; + other = parent->right; } - other->red = node->parent->red; /* 4.a */ - node->parent->red = false; + other->red = parent->red; /* 4.a */ + parent->red = false; other->right->red = false; - interval_tree_rotate_left (tree, node->parent); + interval_tree_rotate_left (tree, parent); node = tree->root; + parent = ITREE_NULL; } } else { - struct interval_node *other = node->parent->left; + struct interval_node *other = parent->left; if (other->red) /* 1.b */ { other->red = false; - node->parent->red = true; - interval_tree_rotate_right (tree, node->parent); - other = node->parent->left; + parent->red = true; + interval_tree_rotate_right (tree, parent); + parent = node->parent; + other = parent->left; } if (!other->right->red /* 2.b */ && !other->left->red) { other->red = true; - node = node->parent; + node = parent; + eassert (node != ITREE_NULL); + parent = node->parent; } else { @@ -565,14 +574,16 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node other->right->red = false; other->red = true; interval_tree_rotate_left (tree, other); - other = node->parent->left; + parent = node->parent; + other = parent->left; } - other->red = node->parent->red; /* 4.b */ - node->parent->red = false; + other->red = parent->red; /* 4.b */ + parent->red = false; other->left->red = false; - interval_tree_rotate_right (tree, node->parent); + interval_tree_rotate_right (tree, parent); node = tree->root; + parent = ITREE_NULL; } } } @@ -590,15 +601,18 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents - (done in `interval_tree_remove_fix`). - BEWARE: `null->parent` is usually write-only, *BUT* in this function, - we use `null->parent` to simplify the code for the case where the - "broken" node is null: `broken->parent` is set typically in - `interval_tree_transplant` and then used in - `interval_tree_remove_fix`. - This trick is described in Cormen et al.'s Introduction to Algorithms. */ - + (done in `interval_tree_remove_fix`). */ struct interval_node *broken = NULL; + /* `broken` may be null but `interval_tree_remove_fix` still + needs to know its "parent". + Cormen et al.'s Introduction to Algorithms uses a trick where + they rely on the null sentinel node's `parent` field to hold + the right value. While this works, it breaks the rule that + the `parent` field is write-only making correctness much more tricky + and introducing a dependency on a global state (which is incompatible + with concurrency among other things), so instead we keep track of + `broken`'s parent manually. */ + struct interval_node *broken_parent = NULL; interval_tree_inherit_offset (tree->otick, node); if (node->left == ITREE_NULL || node->right == ITREE_NULL) @@ -606,8 +620,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *subst = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) - broken = subst; - /* BEWARE: Here is one place we may set `null->parent`. */ + { + broken = subst; + broken_parent = node->parent; /* The future parent. */ + } interval_tree_transplant (tree, subst, node); } else @@ -623,10 +639,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) /* `min` should not have any offsets any more so we can move nodes underneath it without risking changing their begin/end. */ eassert (min->offset == 0); - if (min->parent != node) + if (min->parent == node) + broken_parent = min; /* The future parent. */ + else { - /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, min_right, min); + broken_parent = min->parent; /* The parent. */ min->right = node->right; } min->left = node->left; @@ -637,7 +655,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); /* We set min->right->parent after `interval_tree_transplant` so that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - /* BEWARE: Here is one place we may set `null->parent`. */ min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` @@ -650,9 +667,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (node->parent); if (broken) - /* BEWARE: Here is where we may end up relying on the `null->parent` - set earlier. */ - interval_tree_remove_fix (tree, broken); + interval_tree_remove_fix (tree, broken, broken_parent); node->right = node->left = node->parent = NULL; --tree->size; From 6dff825a9943434cfccd64916c506ab10977acf8 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 6 Oct 2022 09:36:24 -0700 Subject: [PATCH 36/70] ; * src/itree.h: include "lisp.h" for Lisp_Object --- src/itree.c | 2 +- src/itree.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index ed31ef11562..a782410860e 100644 --- a/src/itree.c +++ b/src/itree.c @@ -19,7 +19,7 @@ along with GNU Emacs. If not, see . */ #include #include -#include "lisp.h" + #include "itree.h" /* diff --git a/src/itree.h b/src/itree.h index 8f6bb667d64..9b79551f77c 100644 --- a/src/itree.h +++ b/src/itree.h @@ -23,6 +23,8 @@ along with GNU Emacs. If not, see . */ #include #include +#include "lisp.h" + /* The tree and node structs are mainly here, so they can be allocated. NOTE: The only time where it is safe to modify node.begin and From 0fcd6de93b998a03f7e7c086522e803602974150 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 6 Oct 2022 13:05:19 -0700 Subject: [PATCH 37/70] ; * src/itree.h (struct interval_node): document field invariants. --- src/itree.h | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/itree.h b/src/itree.h index 9b79551f77c..9e40b87cc4f 100644 --- a/src/itree.h +++ b/src/itree.h @@ -25,7 +25,8 @@ along with GNU Emacs. If not, see . */ #include "lisp.h" -/* The tree and node structs are mainly here, so they can be allocated. +/* The tree and node structs are mainly here, so they can be + allocated. NOTE: The only time where it is safe to modify node.begin and node.end directly, is while the node is not part of any tree. @@ -33,14 +34,56 @@ along with GNU Emacs. If not, see . */ NOTE: It is safe to read node.begin and node.end directly, if the node came from a generator, because it validates the nodes it returns as a side-effect. -*/ + */ struct interval_node; struct interval_node { + /* The normal parent, left and right links found in binary trees. + See also `red`, below, which completes the Red-Black tree + representation. */ struct interval_node *parent; struct interval_node *left; struct interval_node *right; + + /* The following five fields comprise the interval abstraction. + + BEGIN, END are buffer positions describing the range. When a + node is in a tree these fields are read only, written only by + itree functions. + + The LIMIT, OFFSET and OTICK fields should be considered internal + to itree.c and used only by itree functions. + + LIMIT is a buffer position, the maximum of END of this node and + its children. See itree.c for its use. + + OFFSET is in buffer position units, and will be non-zero only + when the node is dirty. + + OTICK determines whether BEGIN, END, LIMIT and OFFSET are + considered dirty. A node is clean when its OTICK is equal to the + OTICK of its tree (see struct interval_tree). Otherwise, it is + dirty. + + In a clean node, BEGIN, END and LIMIT are correct buffer + positions, and OFFSET is zero. The parent of a clean node is + also clean, recursively. + + In a dirty node, the node's OTICK won't equal its tree's OTICK, + and its OFFSET may be non-zero. At all times the descendents of + a dirty node are also dirty. BEGIN, END and LIMIT require + adjustment before use as buffer positions. + + NOTE: BEGIN and END must not be modified while the node is part + of a tree. Use interval_tree_insert_gap and + interval_tree_delete_gap instead. + + NOTE: The interval generators ensure nodes are clean before + yielding them, so BEGIN and END may be safely used as buffer + positions then. + */ + ptrdiff_t begin; /* The beginning of this interval. */ ptrdiff_t end; /* The end of the interval. */ ptrdiff_t limit; /* The maximum end in this subtree. */ From cab3b67652da1d82a854e7d2007d080e2a3c6605 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 6 Oct 2022 13:12:54 -0700 Subject: [PATCH 38/70] ; * src/itree.c: change comments for clarity. --- src/itree.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/itree.c b/src/itree.c index a782410860e..3098fe1cf45 100644 --- a/src/itree.c +++ b/src/itree.c @@ -24,7 +24,7 @@ along with GNU Emacs. If not, see . */ /* Intervals of the form [BEGIN, END), are stored as nodes inside a RB - tree, sorted by BEGIN . The core operation of this tree (besides + tree, ordered by BEGIN. The core operation of this tree (besides insert, remove, etc.) is finding all intervals intersecting with some given interval. In order to perform this operation efficiently, every node stores a third value called LIMIT. (See @@ -65,10 +65,10 @@ along with GNU Emacs. If not, see . */ ==== Adjusting intervals ==== Since this data-structure will be used for overlays in an Emacs - buffer, a second core operation implements the ability to insert or - delete gaps in resp. from the tree. This models the insertion - resp. deletion of text in a buffer and the effects it may have on - the positions of overlays. + buffer, a second core operation is the ability to insert and delete + gaps in the tree. This models the insertion and deletion of text + in a buffer and the effects it may have on the positions of + overlays. Consider this: Something gets inserted at position P into a buffer and assume that all overlays occur strictly after P. Ordinarily, @@ -79,10 +79,10 @@ along with GNU Emacs. If not, see . */ The OFFSET of some some subtree, represented by its root, is the amount of shift that needs to be applied to its BEGIN, END and - LIMIT values, in order to get to the real values. Coming back to - the example, all we would need to do in this case, is to increment - the OFFSET of the tree's root, without any traversal of the tree - itself. + LIMIT values, in order to get to the actual buffer positions. + Coming back to the example, all we would need to do in this case, + is to increment the OFFSET of the tree's root, without any + traversal of the tree itself. As a consequence, the real values of BEGIN, END and LIMIT of some NODE need to be computed by incrementing them by the sum of NODE's From c0d5026321e5f5c5cfbf012f06d91fdead01eae4 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 6 Oct 2022 13:18:46 -0700 Subject: [PATCH 39/70] Use a bool instead of a bitfield * src/itree.c (struct interval_generator): use a bool instead of a bitfield, since space is not an issue. --- src/itree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index 3098fe1cf45..79e39d6e2ab 100644 --- a/src/itree.c +++ b/src/itree.c @@ -145,7 +145,7 @@ struct interval_generator ptrdiff_t end; uintmax_t otick; /* A copy of the tree's `otick`. */ enum interval_tree_order order; - bool_bf running : 1; + bool running; const char* file; int line; }; From 780d3d8df2e9222a4643f0d0e9caf7628085d7bf Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 6 Oct 2022 15:47:20 -0700 Subject: [PATCH 40/70] ; * src/itree.c: Add comment describing when noverlay is O(N) --- src/itree.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/itree.c b/src/itree.c index 79e39d6e2ab..d955c575390 100644 --- a/src/itree.c +++ b/src/itree.c @@ -62,6 +62,40 @@ along with GNU Emacs. If not, see . */ complexity of O(K*log(N)) for this operation, where K is the size of the result set and N the size of the tree. + ==== FIXME: bug#58342 some important operations remain slow === + + The amortized costs of Emacs' previous-overlay-change and + next-overlay-change functions are O(N) with this data structure. + The root problem is that we only have an order for the BEG field, + but not the END. The previous/next overlay change operations need + to find the nearest point where there is *either* an interval BEG + or END point, but there is no efficient way to narrow the search + space over END postions. + + Consider the case where next-overlay-change is called at POS, all + interval BEG positions are less than pos POS and all interval END + posistions are after. These END positions have no order, and so + *every* interval must be examined. This is at least O(N). The + previous-overlay-change case is similar. The root issue is that + the iterative "narrowing" approach is not guaranteed to reduce the + search space in logarithmic time, since END is not ordered in the + tree. + + One might argue that the LIMIT value will do this narrowing, but + this narrowing is O(K*log(N)) where K is the size of the result + set. If we are interested in finding the node in a range with the + smallest END, we might have to examine all K nodes in that range. + In the case of the *-overlay-channge functions, K may well be equal + to N. + + Ideally, a tree based data structure for overlays would have + O(log(N)) performance for previous-overlay-change and + next-overlay-change, as these are called in performance sensitive + situations such as redisplay. The only way I can think of + achieving this is by keeping one ordering by BEG and a separate + ordering by END, and then performing logic quite similar to the + current Emacs overlays-before and overlays-after lists. + ==== Adjusting intervals ==== Since this data-structure will be used for overlays in an Emacs From 208de5de59ae85887d637ec93690e17c418a7ab6 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 7 Oct 2022 19:15:07 -0400 Subject: [PATCH 41/70] itree: Try and detect non-local exits during itree iterations * src/itree.c (itree_busy_p): New function. * src/eval.c (signal_or_quit): Use it. * src/itree.h (itree_busy_p): Declare it. --- src/eval.c | 1 + src/itree.c | 6 ++++++ src/itree.h | 5 ++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/eval.c b/src/eval.c index 7da1d8fb989..37c25c79148 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1711,6 +1711,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) Lisp_Object clause = Qnil; struct handler *h; + eassert (!itree_busy_p ()); if (gc_in_progress || waiting_for_input) emacs_abort (); diff --git a/src/itree.c b/src/itree.c index d955c575390..de16af5b0c2 100644 --- a/src/itree.c +++ b/src/itree.c @@ -725,6 +725,12 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } +bool +itree_busy_p (void) +{ + return (iter && iter->running); +} + /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. diff --git a/src/itree.h b/src/itree.h index 9e40b87cc4f..7fdc9c07c93 100644 --- a/src/itree.h +++ b/src/itree.h @@ -129,6 +129,7 @@ void interval_tree_iter_finish (struct interval_generator *); struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); +bool itree_busy_p (void); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, @@ -148,9 +149,7 @@ void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); be able to run ELisp code (or GC for that matter). - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - - Non-local exits are not supported within the body of the loop, - unless the caller makes sure `ITREE_ABORT` is called via some - kind of unwind_protect. + - Non-local exits are not supported within the body of the loop. - Don't modify the tree during the iteration. */ #define ITREE_FOREACH(n, t, beg, end, order) \ From 30f52202775155c1d301af3634d0122c3d7851f8 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 8 Oct 2022 09:15:26 -0700 Subject: [PATCH 42/70] Comment change: explain inheriting "dirty" offsets ; * src/itree.c (interval_generator_next): explain why the code handles inheriting offsets from dirty nodes. --- src/itree.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/itree.c b/src/itree.c index de16af5b0c2..05851007f5a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1086,8 +1086,17 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->right->offset += node->offset; node->offset = 0; } - /* FIXME: I wonder when/why this condition can be false, and more generally - why we'd want to propagate offsets that may not be fully up-to-date. */ + /* FIXME: I wonder when/why this condition can be false, and more + generally why we'd want to propagate offsets that may not be + fully up-to-date. --stef + + Offsets can be inherited from dirty nodes (with out of date + otick) during insert and remove. Offsets aren't inherited + downward from the root for these operations so rotations are + performed on potentially "dirty" nodes. We could fix this by + always inheriting offsets downward from the root for every insert + and remove. --matt + */ if (node->parent == ITREE_NULL || node->parent->otick == otick) node->otick = otick; } From 92a0bf6ce2d2afe909d8a075ad9760eb003a8e73 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 8 Oct 2022 09:28:29 -0700 Subject: [PATCH 43/70] ; * test/src/buffer-tests.el (test-overlay-randomly): new test. --- test/src/buffer-tests.el | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index a12d15bc798..01780a15cc1 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1508,6 +1508,98 @@ with parameters from the *Messages* buffer modification." (ovshould nonempty-eob-end 4 5) (ovshould empty-eob 5 5))))) +(ert-deftest test-overlay-randomly () + "Exercise overlay code, but perform few assertions. + +This test works best when Emacs is configured with +--enable-checking=yes. This is a little bit like fuzz testing, +except this test has no way to reduce to a minimal failng test +case. Regardless, by exercising many corner cases bugs can be +found using Emacs' internal consistency assertions." + (let* ( + ;; The size and slack for the test buffer size. + (buffer-size-target 1000) + (buffer-size-slack 200) + + ;; Use up to 100 overlays. We need not use more to observe + ;; reasonable variation in the overlay data structures. + (overlay-count-limit 100) + + ;; This test maintains a vector of overlays. Each iteration + ;; may append or erase one overlay. + (overlays (make-vector overlay-count-limit nil)) + (overlay-count 0) + + ;; The test is either slowly growing or shrinking the overlay + ;; count. Deletions still occur while growing, and additions + ;; still occur while shrinking. The GROWING variable only + ;; controls the relative probability of doing one or the + ;; other. + (growing t) + + ;; Loop up to 1M times. + (iteration-count 0) + (iteration-target 100000)) + (with-temp-buffer + (while (< (buffer-size) buffer-size-target) + (insert "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, +quae ab illo inventore veritatis et quasi architecto beatae vitae +dicta sunt, explicabo. ")) + + (while (< iteration-count iteration-target) + (cl-incf iteration-count) + + ;; Toggle GROWING if we've reached a size boundary. The idea + ;; is to initially steadily increase the overlay count, then + ;; steadily decrease it, then repeat. + (when (and growing (= overlay-count overlay-count-limit)) + (message "now shrinking") + (setq growing nil)) + (when (and (not growing) (= overlay-count 0)) + (message "now growing") + (setq growing t)) + + ;; Create or delete a random overlay according to a + ;; probability chosen by GROWING. + (let ((create-overlay (>= (random 100) (if growing 40 60)))) + (cond + ;; Possibly create a new overlay in a random place in the + ;; buffer. We have two easy choices. We can choose the + ;; overlay BEGIN randomly, then choose its END among the + ;; valid remaining buffer posiitions. Or we could choose + ;; the overlay width randomly, then choose a valid BEGIN. + ;; We take the former approach, because the overlay data + ;; structure is ordered primarily by BEGIN. + ((and create-overlay (< overlay-count overlay-count-limit)) + (let* ((begin (random (buffer-size))) + (end (+ begin (random (- (buffer-size) begin)))) + (ov (make-overlay begin end nil + (= 0 (random 2)) (= 0 (random 2))))) + (aset overlays overlay-count ov) + (cl-incf overlay-count))) + ((and (not create-overlay) (> overlay-count 0)) + + ;; Possibly delete a random overlay. + (let* ((last-index (1- overlay-count)) + (index (random overlay-count)) + (ov (aref overlays index))) + (when (< index last-index) + (aset overlays index (aref overlays last-index))) + (aset overlays last-index nil) + (cl-decf overlay-count) + (delete-overlay ov))))) + + ;; Modify the buffer on occasion, which exercises the + ;; insert/remove gap logic in the overlay implementation. + (when (and (< (buffer-size) (+ buffer-size-target buffer-size-slack)) + (zerop (random 10))) + (goto-char (1+ (random (buffer-size)))) + (insert (+ ?a (random 26)))) + (when (and (> (buffer-size) (- buffer-size-target buffer-size-slack)) + (zerop (random 10))) + (goto-char (1+ (random (buffer-size)))) + (delete-char 1)))))) From fe14454101cfd9951b76549773645b2ffeed66bd Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 8 Oct 2022 19:53:36 -0700 Subject: [PATCH 44/70] Debug check overlay tree invariants * src/itree.c (check_tree): (recurse_check_tree): new functions. (interval_tree_insert): call them. (interval_tree_remove): ditto. (interval_tree_insert_fix): ditto. --- src/itree.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index 05851007f5a..4ad47b2e3fa 100644 --- a/src/itree.c +++ b/src/itree.c @@ -204,7 +204,67 @@ itree_init (void) iter = interval_generator_create (NULL); } - +static ptrdiff_t +recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, + ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t max_begin, intmax_t *size) +{ + if (node == ITREE_NULL) + return PTRDIFF_MIN; + ++*size; + + /* Validate structure. */ + eassert ( + node->parent == ITREE_NULL + || (node->parent->left == node || node->parent->right == node)); + eassert (node->left == ITREE_NULL || node->left->parent == node); + eassert (node->right == ITREE_NULL || node->right->parent == node); + + /* Red nodes cannot have red parents. */ + eassert (node->parent == ITREE_NULL + || !(node->red && node->parent->red)); + + eassert (node->offset == 0 || node->otick < tree_otick); + + offset += node->offset; + ptrdiff_t begin = node->begin + offset; + ptrdiff_t end = node->end + offset; + ptrdiff_t limit = node->limit + offset; + + eassert (min_begin <= max_begin); + eassert (min_begin <= begin); + eassert (begin <= max_begin); + eassert (end <= limit); + + ptrdiff_t left_limit + = recurse_check_tree (node->left, tree_otick, offset, min_begin, + begin, size); + ptrdiff_t right_limit + = recurse_check_tree (node->right, tree_otick, offset, begin, + max_begin, size); + eassert (left_limit <= limit); + eassert (right_limit <= limit); + eassert (limit == max (end, max (left_limit, right_limit))); + return limit; +} + +static bool +check_tree (struct interval_tree *tree) +{ + eassert (tree != NULL); + eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + if (tree->root == ITREE_NULL) + return true; + + intmax_t size = 0; + ptrdiff_t offset = tree->root->offset; + ptrdiff_t limit + = recurse_check_tree (tree->root, tree->otick, offset, + PTRDIFF_MIN, PTRDIFF_MAX, &size); + eassert (limit == tree->root->limit + offset); + return true; +} + /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -429,6 +489,7 @@ void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); + eassert (check_tree (tree)); struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; @@ -468,6 +529,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; interval_tree_insert_fix (tree, node); + eassert (check_tree (tree)); } /* Return true, if NODE is a member of TREE. */ @@ -632,6 +694,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); + eassert (check_tree (tree)); /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents @@ -708,6 +771,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); /* eassert (itree_limits_are_stable (tree->root)); */ + eassert (check_tree (tree)); return node; } @@ -1277,6 +1341,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* The root may have been changed to red due to the algorithm. Set it to black so that property #5 is satisfied. */ tree->root->red = false; + eassert (check_tree (tree)); } /* Return accumulated offsets of NODE's parents. */ From 4f3f7aebc957732f4fbe5c799da5367f46607680 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 9 Oct 2022 00:56:24 -0400 Subject: [PATCH 45/70] itree.c: Use `interval_tree_inherit_offset` The insertion code tried to manipulate the offset in its own way, and apparently there was a bug in it. Replace that with a call to `interval_tree_inherit_offset`, making the whole logic a bit simpler, and fixing a bug along the way (not sure where the bug was, to be honest). * src/itree.c (interval_tree_insert): Use `interval_tree_inherit_offset`. Check the tree before insert_fix. (recurse_check_tree): Don't check RB invariants. (itree_limits_are_stable): Delete function (subsumed by `check_tree`). --- src/itree.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/itree.c b/src/itree.c index 4ad47b2e3fa..b57c3cc656f 100644 --- a/src/itree.c +++ b/src/itree.c @@ -220,9 +220,12 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); + /* We don't check the RB invariants here (neither the absence of + red+red nor the equal-black-depth), so that we can use this check + even while the tree is temporarily breaking some of those invarints. */ /* Red nodes cannot have red parents. */ - eassert (node->parent == ITREE_NULL - || !(node->red && node->parent->red)); + /* eassert (node->parent == ITREE_NULL + || !(node->red && node->parent->red)); */ eassert (node->offset == 0 || node->otick < tree_otick); @@ -493,15 +496,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; - ptrdiff_t offset = 0; + uintmax_t otick = tree->otick; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ while (child != ITREE_NULL) { + interval_tree_inherit_offset (otick, child); parent = child; - offset += child->offset; - child->limit = max (child->limit, node->end - offset); + eassert (child->offset == 0); + child->limit = max (child->limit, node->end); /* This suggests that nodes in the right subtree are strictly greater. But this is not true due to later rotations. */ child = node->begin <= child->begin ? child->left : child->right; @@ -521,15 +525,13 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->right = ITREE_NULL; node->red = true; node->offset = 0; - node->begin -= offset; - node->end -= offset; node->limit = node->end; - node->otick = tree->otick - 1; + node->otick = otick; /* Fix/update the tree */ ++tree->size; - interval_tree_insert_fix (tree, node); eassert (check_tree (tree)); + interval_tree_insert_fix (tree, node); } /* Return true, if NODE is a member of TREE. */ @@ -567,16 +569,6 @@ itree_limit_is_stable (struct interval_node *node) return (newlimit == node->limit); } -static inline bool -itree_limits_are_stable (struct interval_node *node) -{ - if (node == ITREE_NULL) - return true; - return itree_limit_is_stable (node) - && itree_limits_are_stable (node->right) - && itree_limits_are_stable (node->left); -} - static struct interval_node* interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) { @@ -692,7 +684,6 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { - /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree)); @@ -770,7 +761,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) --tree->size; eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - /* eassert (itree_limits_are_stable (tree->root)); */ eassert (check_tree (tree)); return node; From 7cbeeabc7e6271948e7bb93020c2e4e50c65f384 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 9 Oct 2022 19:45:26 -0400 Subject: [PATCH 46/70] Tighten up handling of `otick` Move args between `build_overlay` and `add_buffer_overlay`, to try and keep buffer positions together with their buffer. Be more strict in the `otick` values passed to `interval_tree_insert`. Move a few things around to try and reduce dependencies through `.h` files. Fix a thinko bug in `check_tree`. * src/alloc.c (build_overlay): Remove `begin` and `end` args. * src/buffer.c (add_buffer_overlay): Move from `buffer.h`. Add `begin` and `end` args. (copy_overlays): Adjust accordingly. (Fmake_overlay): Use BUF_BEG and BUF_Z; adjust call to `build_overlay` and `add_buffer_overlay`. (Fmove_overlay): Use BUF_BEG and BUF_Z; Use the new `begin` and `end` args of `add_buffer_overlay` so we don't need to use `interval_node_set_region` when moving to a new buffer. (remove_buffer_overlay, set_overlay_region): Move from `buffer.h`. * src/buffer.h (set_overlay_region, add_buffer_overlay) (remove_buffer_overlay): Move to `buffer.c`. (build_overlay): Move from `lisp.h`. (maybe_alloc_buffer_overlays): Delete function (inline into its only caller). * src/itree.c (interval_tree_insert): Move declaration `from buffer.h`. (check_tree): Fix initial offset in call to `recurse_check_tree`. Remove redundant check of the `limit` value. (interval_node_init): Remove `begin` and `end` args. (interval_tree_insert): Mark it as static. Assert that the new node's `otick` should already be uptodate and its new parent as well. (itree_insert_node): New function. (interval_tree_insert_gap): Assert the otick of the removed+added nodes were uptodate and mark them as uptodate again after adjusting their positions. (interval_tree_inherit_offset): Check that the parent is at least as uptodate as the child. * src/lisp.h (build_overlay): Move to `buffer.h`. * src/itree.h (interval_node_init): Adjust accordingly. (interval_tree_insert): Remove declaration. (itree_insert_node): New declaration. --- src/alloc.c | 8 +++----- src/buffer.c | 58 +++++++++++++++++++++++++++++++++++++--------------- src/buffer.h | 35 +------------------------------ src/itree.c | 55 ++++++++++++++++++++++++++++++------------------- src/itree.h | 5 +++-- src/lisp.h | 1 - 6 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 50968b7e121..00f2991f250 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3702,19 +3702,17 @@ build_symbol_with_pos (Lisp_Object symbol, Lisp_Object position) return val; } -/* Return a new overlay with specified START, END and PLIST. */ +/* Return a new (deleted) overlay with PLIST. */ Lisp_Object -build_overlay (ptrdiff_t begin, ptrdiff_t end, - bool front_advance, bool rear_advance, +build_overlay (bool front_advance, bool rear_advance, Lisp_Object plist) { struct Lisp_Overlay *p = ALLOCATE_PSEUDOVECTOR (struct Lisp_Overlay, plist, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); struct interval_node *node = xmalloc (sizeof (*node)); - interval_node_init (node, begin, end, front_advance, - rear_advance, overlay); + interval_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; set_overlay_plist (overlay, plist); diff --git a/src/buffer.c b/src/buffer.c index f59fddcbdeb..e1303f2a880 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -638,6 +638,16 @@ even if it is dead. The return value is never nil. */) return buffer; } +static void +add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov, + ptrdiff_t begin, ptrdiff_t end) +{ + eassert (! ov->buffer); + if (! b->overlays) + b->overlays = interval_tree_create (); + ov->buffer = b; + itree_insert_node (b->overlays, ov->interval, begin, end); +} /* Copy overlays of buffer FROM to buffer TO. */ @@ -650,11 +660,10 @@ copy_overlays (struct buffer *from, struct buffer *to) ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { Lisp_Object ov = node->data; - Lisp_Object copy = build_overlay (node->begin, node->end, - node->front_advance, + Lisp_Object copy = build_overlay (node->front_advance, node->rear_advance, Fcopy_sequence (OVERLAY_PLIST (ov))); - add_buffer_overlay (to, XOVERLAY (copy)); + add_buffer_overlay (to, XOVERLAY (copy), node->begin, node->end); } } @@ -897,6 +906,15 @@ does not run the hooks `kill-buffer-hook', return buf; } +static void +remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (b->overlays); + eassert (ov->buffer == b); + interval_tree_remove (ov->buffer->overlays, ov->interval); + ov->buffer = NULL; +} + /* Mark OV as no longer associated with its buffer. */ static void @@ -3486,12 +3504,11 @@ for the rear of the overlay advance when text is inserted there temp = beg; beg = end; end = temp; } - ptrdiff_t obeg = clip_to_bounds (BEG, XFIXNUM (beg), b->text->z); - ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), b->text->z); - ov = build_overlay (obeg, oend, - ! NILP (front_advance), + ptrdiff_t obeg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); + ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), BUF_Z (b)); + ov = build_overlay (! NILP (front_advance), ! NILP (rear_advance), Qnil); - add_buffer_overlay (b, XOVERLAY (ov)); + add_buffer_overlay (b, XOVERLAY (ov), obeg, oend); /* We don't need to redisplay the region covered by the overlay, because the overlay has no properties at the moment. */ @@ -3518,6 +3535,15 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1); } +INLINE void +set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) +{ + eassert (ov->buffer); + begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); + end = clip_to_bounds (begin, end, ov->buffer->text->z); + interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); +} + DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. @@ -3528,7 +3554,6 @@ buffer. */) struct buffer *b, *ob = 0; Lisp_Object obuffer; specpdl_ref count = SPECPDL_INDEX (); - ptrdiff_t n_beg, n_end; ptrdiff_t o_beg UNINIT, o_end UNINIT; CHECK_OVERLAY (overlay); @@ -3555,11 +3580,14 @@ buffer. */) temp = beg; beg = end; end = temp; } - specbind (Qinhibit_quit, Qt); + specbind (Qinhibit_quit, Qt); /* FIXME: Why? */ obuffer = Foverlay_buffer (overlay); b = XBUFFER (buffer); + ptrdiff_t n_beg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); + ptrdiff_t n_end = clip_to_bounds (n_beg, XFIXNUM (end), BUF_Z (b)); + if (!NILP (obuffer)) { ob = XBUFFER (obuffer); @@ -3572,13 +3600,11 @@ buffer. */) { if (! NILP (obuffer)) remove_buffer_overlay (XBUFFER (obuffer), XOVERLAY (overlay)); - add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay)); + add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay), n_beg, n_end); } - /* Set the overlay boundaries, which may clip them. */ - set_overlay_region (XOVERLAY (overlay), XFIXNUM (beg), XFIXNUM (end)); - - n_beg = OVERLAY_START (overlay); - n_end = OVERLAY_END (overlay); + else + interval_node_set_region (b->overlays, XOVERLAY (overlay)->interval, + n_beg, n_end); /* If the overlay has changed buffers, do a thorough redisplay. */ if (!BASE_EQ (buffer, obuffer)) diff --git a/src/buffer.h b/src/buffer.h index a4e3934cad2..288acd4f5ee 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1189,6 +1189,7 @@ extern void fix_overlays_before (struct buffer *, ptrdiff_t, ptrdiff_t); extern void mmap_set_vars (bool); extern void restore_buffer (Lisp_Object); extern void set_buffer_if_live (Lisp_Object); +extern Lisp_Object build_overlay (bool, bool, Lisp_Object); /* Return B as a struct buffer pointer, defaulting to the current buffer. */ @@ -1408,40 +1409,6 @@ overlay_end (struct Lisp_Overlay *ov) return interval_node_end (ov->buffer->overlays, ov->interval); } -INLINE void -set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) -{ - eassert (ov->buffer); - begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); - end = clip_to_bounds (begin, end, ov->buffer->text->z); - interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); -} - -INLINE void -maybe_alloc_buffer_overlays (struct buffer *b) -{ - if (! b->overlays) - b->overlays = interval_tree_create (); -} - -INLINE void -add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) -{ - eassert (! ov->buffer); - maybe_alloc_buffer_overlays (b); - ov->buffer = b; - interval_tree_insert (b->overlays, ov->interval); -} - -INLINE void -remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) -{ - eassert (b->overlays); - eassert (ov->buffer == b); - interval_tree_remove (ov->buffer->overlays, ov->interval); - ov->buffer = NULL; -} - /* Return the start of OV in its buffer, or -1 if OV is not associated with any buffer. */ diff --git a/src/itree.c b/src/itree.c index b57c3cc656f..bbab70dac7c 100644 --- a/src/itree.c +++ b/src/itree.c @@ -142,6 +142,7 @@ static void interval_tree_rotate_right (struct interval_tree *, struct interval_ static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_tree_insert (struct interval_tree *, struct interval_node *); /* The sentinel node, the null node. */ struct interval_node itree_null; @@ -260,11 +261,8 @@ check_tree (struct interval_tree *tree) return true; intmax_t size = 0; - ptrdiff_t offset = tree->root->offset; - ptrdiff_t limit - = recurse_check_tree (tree->root, tree->otick, offset, - PTRDIFF_MIN, PTRDIFF_MAX, &size); - eassert (limit == tree->root->limit + offset); + recurse_check_tree (tree->root, tree->otick, 0, + PTRDIFF_MIN, PTRDIFF_MAX, &size); return true; } @@ -375,12 +373,11 @@ interval_stack_pop (struct interval_stack *stack) void interval_node_init (struct interval_node *node, - ptrdiff_t begin, ptrdiff_t end, bool front_advance, bool rear_advance, Lisp_Object data) { - node->begin = begin; - node->end = max (begin, end); + node->begin = -1; + node->end = -1; node->front_advance = front_advance; node->rear_advance = rear_advance; node->data = data; @@ -488,7 +485,7 @@ interval_tree_size (struct interval_tree *tree) Note, that inserting a node twice results in undefined behaviour. */ -void +static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); @@ -497,6 +494,9 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; uintmax_t otick = tree->otick; + /* It's the responsability of the caller to set `otick` on the node, + to "confirm" that the begin/end fields are uptodate. */ + eassert (node->otick == otick); /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ @@ -526,7 +526,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->red = true; node->offset = 0; node->limit = node->end; - node->otick = otick; + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; @@ -534,6 +534,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) interval_tree_insert_fix (tree, node); } +void +itree_insert_node (struct interval_tree *tree, struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + node->begin = begin; + node->end = end; + node->otick = tree->otick; + interval_tree_insert (tree, node); +} + /* Return true, if NODE is a member of TREE. */ static bool @@ -849,6 +859,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { if (length <= 0 || tree->root == ITREE_NULL) return; + uintmax_t ootick = tree->otick; /* FIXME: Don't allocate generator/stack anew every time. */ @@ -907,13 +918,16 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l } /* Reinsert nodes starting at POS having front-advance. */ + uintmax_t notick = tree->otick; nodeptr_and_flag nav; while ((nav = interval_stack_pop (saved), node = nav_nodeptr (nav))) { + eassert (node->otick == ootick); node->begin += length; if (node->end != pos || node->rear_advance) node->end += length; + node->otick = notick; interval_tree_insert (tree, node); } @@ -1123,12 +1137,19 @@ interval_tree_update_limit (struct interval_node *node) static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); if (node->otick == otick) { eassert (node->offset == 0); return; } + /* Offsets can be inherited from dirty nodes (with out of date + otick) during insert and remove. Offsets aren't inherited + downward from the root for these operations so rotations are + performed on potentially "dirty" nodes, where we only make sure + the *local* offsets are all zero. */ + if (node->offset) { node->begin += node->offset; @@ -1140,17 +1161,9 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->right->offset += node->offset; node->offset = 0; } - /* FIXME: I wonder when/why this condition can be false, and more - generally why we'd want to propagate offsets that may not be - fully up-to-date. --stef - - Offsets can be inherited from dirty nodes (with out of date - otick) during insert and remove. Offsets aren't inherited - downward from the root for these operations so rotations are - performed on potentially "dirty" nodes. We could fix this by - always inheriting offsets downward from the root for every insert - and remove. --matt - */ + /* The only thing that matters about `otick` is whether it's equal to + that of the tree. We could also "blindly" inherit from parent->otick, + but we need to tree's `otick` anyway for when there's no parent. */ if (node->parent == ITREE_NULL || node->parent->otick == otick) node->otick = otick; } diff --git a/src/itree.h b/src/itree.h index 7fdc9c07c93..5733ec15305 100644 --- a/src/itree.h +++ b/src/itree.h @@ -112,7 +112,7 @@ enum interval_tree_order { ITREE_PRE_ORDER, }; -void interval_node_init (struct interval_node *, ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); +void interval_node_init (struct interval_node *, bool, bool, Lisp_Object); ptrdiff_t interval_node_begin (struct interval_tree *, struct interval_node *); ptrdiff_t interval_node_end (struct interval_tree *, struct interval_node *); void interval_node_set_region (struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); @@ -120,7 +120,8 @@ struct interval_tree *interval_tree_create (void); void interval_tree_destroy (struct interval_tree *); intmax_t interval_tree_size (struct interval_tree *); void interval_tree_clear (struct interval_tree *); -void interval_tree_insert (struct interval_tree *, struct interval_node *); +void itree_insert_node (struct interval_tree *tree, struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); diff --git a/src/lisp.h b/src/lisp.h index 7d838afb5c9..7cd78712814 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4415,7 +4415,6 @@ extern Lisp_Object make_float (double); extern void display_malloc_warning (void); extern specpdl_ref inhibit_garbage_collection (void); extern Lisp_Object build_symbol_with_pos (Lisp_Object, Lisp_Object); -extern Lisp_Object build_overlay (ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); extern void free_cons (struct Lisp_Cons *); extern void init_alloc_once (void); extern void init_alloc (void); From 246acbddbeb3e9a390fe78242259182af0c2cc18 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 9 Oct 2022 10:12:32 -0700 Subject: [PATCH 47/70] ; * test/src/buffer-tests.el: Remove unecessary `message' calls. --- test/src/buffer-tests.el | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 01780a15cc1..9bccbdf2e89 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1554,10 +1554,8 @@ dicta sunt, explicabo. ")) ;; is to initially steadily increase the overlay count, then ;; steadily decrease it, then repeat. (when (and growing (= overlay-count overlay-count-limit)) - (message "now shrinking") (setq growing nil)) (when (and (not growing) (= overlay-count 0)) - (message "now growing") (setq growing t)) ;; Create or delete a random overlay according to a From 67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Mon, 10 Oct 2022 08:48:41 -0700 Subject: [PATCH 48/70] Improve check_subtree * src/itree.c (struct check_subtree_result): new struct returned by check_subtree. (check_subtree): new function, renamed from recurse_check_tree. Add new black height assertions. (check_tree): assert that the tree has non-negative size, assert that limiting to interval_tree_max_height(tree) levels is enough to traverses the complete tree. --- src/itree.c | 156 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 30 deletions(-) diff --git a/src/itree.c b/src/itree.c index bbab70dac7c..aa8a5f7f3b9 100644 --- a/src/itree.c +++ b/src/itree.c @@ -205,14 +205,44 @@ itree_init (void) iter = interval_generator_create (NULL); } -static ptrdiff_t -recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, - ptrdiff_t offset, ptrdiff_t min_begin, - ptrdiff_t max_begin, intmax_t *size) +struct check_subtree_result { + /* Were all nodes visited? */ + bool complete; + + /* Computed node count of the tree. */ + int size; + + /* Computed limit of the tree (max END). */ + ptrdiff_t limit; + + /* Computed black height of the tree (count of black nodes from the + bottom up to the root). */ + int black_height; +}; + +static struct check_subtree_result +check_subtree (struct interval_node *node, uintmax_t tree_otick, + int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t max_begin) +{ + struct check_subtree_result result = { .complete = false, + .size = 0, + .limit = PTRDIFF_MIN, + .black_height = 0 }; if (node == ITREE_NULL) - return PTRDIFF_MIN; - ++*size; + { + /* Every nil node of a Red-Black tree is black */ + result.black_height = 1; + result.complete = true; + return result; + }; + + if (max_depth == 0) + { + result.complete = false; + return result; + } /* Validate structure. */ eassert ( @@ -221,14 +251,35 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* We don't check the RB invariants here (neither the absence of - red+red nor the equal-black-depth), so that we can use this check - even while the tree is temporarily breaking some of those invarints. */ - /* Red nodes cannot have red parents. */ - /* eassert (node->parent == ITREE_NULL - || !(node->red && node->parent->red)); */ + /* We don't normally check the RB invariants here (neither the + absence of red+red nor the equal-black-depth), so that we can use + this check even while the tree is temporarily breaking some of + those invarints. You can enable them if you want. */ + if (false) + { + /* If a node is red then both of its children are black. Red + nodes cannot have red parents. */ + if (node->red) + { + eassert (node->left == ITREE_NULL + || node->left->red == false); + eassert (node->right == ITREE_NULL + || node->right->red == false); + eassert (node->parent == ITREE_NULL || !node->parent->red); + } + } - eassert (node->offset == 0 || node->otick < tree_otick); + /* Validate otick. A node's otick must be <= to the tree's otick + and <= to its parent's otick. + + Note: we cannot assert that (NODE.otick == NODE.parent.otick) + implies (NODE.offset == 0) because interval_tree_inherit_offset() + doesn't always update otick. It could, but it is not clear there + is a need. */ + eassert (node->otick <= tree_otick); + eassert (node->parent == ITREE_NULL + || node->otick <= node->parent->otick); + eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; ptrdiff_t begin = node->begin + offset; @@ -240,29 +291,74 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (begin <= max_begin); eassert (end <= limit); - ptrdiff_t left_limit - = recurse_check_tree (node->left, tree_otick, offset, min_begin, - begin, size); - ptrdiff_t right_limit - = recurse_check_tree (node->right, tree_otick, offset, begin, - max_begin, size); - eassert (left_limit <= limit); - eassert (right_limit <= limit); - eassert (limit == max (end, max (left_limit, right_limit))); - return limit; + struct check_subtree_result left_result + = check_subtree (node->left, tree_otick, max_depth - 1, offset, + min_begin, begin); + struct check_subtree_result right_result + = check_subtree (node->right, tree_otick, max_depth - 1, offset, + begin, max_begin); + + eassert (left_result.limit <= limit); + eassert (right_result.limit <= limit); + + result.complete = left_result.complete && right_result.complete; + if (result.complete) + { + /* Every path from a node to a descendent leaf contains the same + number of black nodes. Often said this way: all nodes have + the same "black height". */ + eassert (left_result.black_height == right_result.black_height); + result.black_height + = (node->red ? 0 : 1) + left_result.black_height; + + result.size = 1 + left_result.size + right_result.size; + result.limit + = max (end, max (left_result.limit, right_result.limit)); + + eassert (limit == result.limit); + } + + return result; } +/* Validate invariants for TREE. + + This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 + (i.e. Emacs is not configured with + "--enable_checking=yes,overlays"). In this mode it can't check all + the invariants. When ENABLE_OVERLAY_CHECKING is 1 it checks the + entire tree and validates all invariants. +*/ static bool check_tree (struct interval_tree *tree) { eassert (tree != NULL); + eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); if (tree->root == ITREE_NULL) return true; - intmax_t size = 0; - recurse_check_tree (tree->root, tree->otick, 0, - PTRDIFF_MIN, PTRDIFF_MAX, &size); + /* Limit the traversal depth to what 'interval_tree_max_height' + returns. Later, verify that this is enough height to traverse + the complete tree. */ + const int max_height = interval_tree_max_height (tree); + eassert (max_height >= 0); + eassert (max_height <= 120); + + /* NOTE: if this check is too expensive an easy fix is to reduce + max_height to for large trees, then relax the assertion on + result.complete. Assertions in check_subtree will still be made + at the bottom of the tree (where they are probably most + interesting), but some will be skipped closer to the root. */ + + struct interval_node *node = tree->root; + struct check_subtree_result result + = check_subtree (node, tree->otick, max_height, node->offset, + PTRDIFF_MIN, PTRDIFF_MAX); + eassert (result.complete); + eassert (result.size == tree->size); + + /* The only way this function fails is eassert(). */ return true; } @@ -1145,10 +1241,10 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) } /* Offsets can be inherited from dirty nodes (with out of date - otick) during insert and remove. Offsets aren't inherited - downward from the root for these operations so rotations are - performed on potentially "dirty" nodes, where we only make sure - the *local* offsets are all zero. */ + otick) during removal, since we do not travel down from the root + in that case. In this case rotations are performed on + potentially "dirty" nodes, where we only need to make sure the + *local* offsets are zero. */ if (node->offset) { From 51a8e375ebea2e6e05eed623bddfb323b8e408f0 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Mon, 10 Oct 2022 09:07:42 -0700 Subject: [PATCH 49/70] Check red-black invariants in most places Stefan recently disabled this but I happened to want it back soon after. * src/itree.c (check_subtree): new arg: allow_red_red (check_tree_common): renamed from check_tree, pass allow_red_red through. (check_tree): new function, pass allow_red_red=false (interval_tree_insert): check_tree -> check_tree_common with allow_red_red=true. --- src/itree.c | 80 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/src/itree.c b/src/itree.c index aa8a5f7f3b9..7ac400398bd 100644 --- a/src/itree.c +++ b/src/itree.c @@ -222,7 +222,8 @@ struct check_subtree_result }; static struct check_subtree_result -check_subtree (struct interval_node *node, uintmax_t tree_otick, +check_subtree (struct interval_node *node, + bool check_red_black_invariants, uintmax_t tree_otick, int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) { @@ -251,23 +252,9 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* We don't normally check the RB invariants here (neither the - absence of red+red nor the equal-black-depth), so that we can use - this check even while the tree is temporarily breaking some of - those invarints. You can enable them if you want. */ - if (false) - { - /* If a node is red then both of its children are black. Red - nodes cannot have red parents. */ - if (node->red) - { - eassert (node->left == ITREE_NULL - || node->left->red == false); - eassert (node->right == ITREE_NULL - || node->right->red == false); - eassert (node->parent == ITREE_NULL || !node->parent->red); - } - } + /* No red nodes have red parents. */ + if (check_red_black_invariants && node->parent != ITREE_NULL) + eassert (!node->red || !node->parent->red); /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -292,11 +279,13 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, eassert (end <= limit); struct check_subtree_result left_result - = check_subtree (node->left, tree_otick, max_depth - 1, offset, - min_begin, begin); + = check_subtree (node->left, check_red_black_invariants, + tree_otick, max_depth - 1, offset, min_begin, + begin); struct check_subtree_result right_result - = check_subtree (node->right, tree_otick, max_depth - 1, offset, - begin, max_begin); + = check_subtree (node->right, check_red_black_invariants, + tree_otick, max_depth - 1, offset, begin, + max_begin); eassert (left_result.limit <= limit); eassert (right_result.limit <= limit); @@ -304,24 +293,29 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, result.complete = left_result.complete && right_result.complete; if (result.complete) { - /* Every path from a node to a descendent leaf contains the same - number of black nodes. Often said this way: all nodes have - the same "black height". */ - eassert (left_result.black_height == right_result.black_height); - result.black_height - = (node->red ? 0 : 1) + left_result.black_height; - result.size = 1 + left_result.size + right_result.size; result.limit = max (end, max (left_result.limit, right_result.limit)); eassert (limit == result.limit); + + if (check_red_black_invariants) + { + /* Every path from a node to a descendent leaf contains the + same number of black nodes. Often said this way: all + nodes have the same "black height". */ + eassert (left_result.black_height + == right_result.black_height); + result.black_height + = (node->red ? 0 : 1) + left_result.black_height; + } } return result; } -/* Validate invariants for TREE. +/* Validate invariants for TREE. If CHECK_RED_BLACK_INVARIANTS, red + nodes with red children are considered invalid. This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 (i.e. Emacs is not configured with @@ -330,7 +324,8 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, entire tree and validates all invariants. */ static bool -check_tree (struct interval_tree *tree) +check_tree_common (struct interval_tree *tree, + bool check_red_black_invariants) { eassert (tree != NULL); eassert (tree->size >= 0); @@ -353,8 +348,9 @@ check_tree (struct interval_tree *tree) struct interval_node *node = tree->root; struct check_subtree_result result - = check_subtree (node, tree->otick, max_height, node->offset, - PTRDIFF_MIN, PTRDIFF_MAX); + = check_subtree (node, check_red_black_invariants, tree->otick, + max_height, node->offset, PTRDIFF_MIN, + PTRDIFF_MAX); eassert (result.complete); eassert (result.size == tree->size); @@ -362,6 +358,22 @@ check_tree (struct interval_tree *tree) return true; } +/* Check the tree with all invariant checks enabled. */ +static bool +check_tree (struct interval_tree *tree) +{ + return check_tree_common (tree, true); +} + +/* Check the tree with all invariant checks enabled, except for the + red-black tree invariants. Useful for asserting the other + invariants while inserting or removing. */ +static bool +check_tree_no_rb (struct interval_tree *tree) +{ + return check_tree_common (tree, false); +} + /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -626,7 +638,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; - eassert (check_tree (tree)); + eassert (check_tree_no_rb (tree)); interval_tree_insert_fix (tree, node); } From a154259bfacf7f1406794a952e80a8197b9a83fb Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Mon, 10 Oct 2022 08:32:56 -0700 Subject: [PATCH 50/70] Simplify itree_null initialization * src/itree.c (null_is_sane): call eassert directly, check REAR_ADVANCE, FRONT_ADVANCE. Add FIXME that PARENT is still read/write. (itree_null): initialize statically (itree_init): remove initialization code, call eassert(null_is_sane()) (check_tree_common): call eassert (null_is_sane()) --- src/itree.c | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/itree.c b/src/itree.c index 7ac400398bd..d9f9ec8cd67 100644 --- a/src/itree.c +++ b/src/itree.c @@ -145,19 +145,42 @@ static struct interval_generator* interval_generator_create (struct interval_tre static void interval_tree_insert (struct interval_tree *, struct interval_node *); /* The sentinel node, the null node. */ -struct interval_node itree_null; +struct interval_node itree_null = { + .parent = &itree_null, + .left = &itree_null, + .right = &itree_null, + .begin = PTRDIFF_MIN, + .end = PTRDIFF_MIN, + .limit = PTRDIFF_MIN, /* => max(x, null.limit) = x */ + .offset = 0, + .otick = 0, + .red = false, + .rear_advance = false, + .front_advance = false, +}; static bool null_is_sane (void) { - /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. */ - return itree_null.red == false - && itree_null.otick == 0 - && itree_null.offset == 0 - && itree_null.begin == PTRDIFF_MIN - && itree_null.end == PTRDIFF_MIN - && itree_null.limit == PTRDIFF_MIN; + /* The sentinel node has most of its fields read-only. + + FIXME: PARENT is still read/write. It is written to + ininterval_tree_transplant, and later read. --matt + */ + /* eassert (itree_null.parent == &itree_null); */ + eassert (itree_null.left == &itree_null); + eassert (itree_null.right == &itree_null); + eassert (itree_null.begin == PTRDIFF_MIN); + eassert (itree_null.end == PTRDIFF_MIN); + eassert (itree_null.limit == PTRDIFF_MIN); + eassert (itree_null.offset == 0); + eassert (itree_null.otick == 0); + eassert (itree_null.red == false); + eassert (itree_null.rear_advance == false); + eassert (itree_null.front_advance == false); + + /* if we get this far things must be good */ + return true; } /* +------------------------------------------------------------------------------------+ */ @@ -195,13 +218,8 @@ static struct interval_generator *iter; static void itree_init (void) { - struct interval_node *null = ITREE_NULL; - null->left = null->right = null->parent = null; - null->offset = null->otick = 0; - null->begin = PTRDIFF_MIN; - null->end = PTRDIFF_MIN; - null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->red = false; + eassert (null_is_sane ()); + iter = interval_generator_create (NULL); } @@ -327,6 +345,8 @@ static bool check_tree_common (struct interval_tree *tree, bool check_red_black_invariants) { + eassert (null_is_sane ()); + eassert (tree != NULL); eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); From da0387f0fe79f577fae6d5453c758f600e1ae495 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Mon, 10 Oct 2022 10:45:05 -0700 Subject: [PATCH 51/70] Stop reading and writing the itree_null.parent field entirely. With this change all fields in the itree_null sentinel are read only. This makes accessing itree_null thread safe in an obvious way. Because it took two commits from two peole to get this right, I think we can call this design fragile and difficult to reason about. Another benefit of this commit is as preparation for removing sentinel node completely, and just using NULL. * src/itree.c (itree_null): Statically initialize itree_null.parent to NULL. It is never accessed. (null_is_sane): Assert parent == NULL. (interval_tree_remove_fix): Remove unecessary assignments to parent from node->parent. These were the last places itree_null.parent were read. (interval_tree_remove): Avoid an assignment to itree_null.parent through min->right->parent. (interval_tree_transplant): Avoid an assignment to itree_null.parent through source->parent. --- src/itree.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/itree.c b/src/itree.c index d9f9ec8cd67..9c5d8ce1426 100644 --- a/src/itree.c +++ b/src/itree.c @@ -146,7 +146,7 @@ static void interval_tree_insert (struct interval_tree *, struct interval_node * /* The sentinel node, the null node. */ struct interval_node itree_null = { - .parent = &itree_null, + .parent = NULL, /* never accessed */ .left = &itree_null, .right = &itree_null, .begin = PTRDIFF_MIN, @@ -162,12 +162,8 @@ struct interval_node itree_null = { static bool null_is_sane (void) { - /* The sentinel node has most of its fields read-only. - - FIXME: PARENT is still read/write. It is written to - ininterval_tree_transplant, and later read. --matt - */ - /* eassert (itree_null.parent == &itree_null); */ + /* All sentinel node fields are read-only. */ + eassert (itree_null.parent == NULL); eassert (itree_null.left == &itree_null); eassert (itree_null.right == &itree_null); eassert (itree_null.begin == PTRDIFF_MIN); @@ -742,7 +738,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->red = false; parent->red = true; interval_tree_rotate_left (tree, parent); - parent = node->parent; other = parent->right; } @@ -761,7 +756,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->left->red = false; other->red = true; interval_tree_rotate_right (tree, other); - parent = node->parent; other = parent->right; } other->red = parent->red; /* 4.a */ @@ -781,7 +775,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->red = false; parent->red = true; interval_tree_rotate_right (tree, parent); - parent = node->parent; other = parent->left; } @@ -800,7 +793,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->right->red = false; other->red = true; interval_tree_rotate_left (tree, other); - parent = node->parent; other = parent->left; } @@ -881,7 +873,8 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); /* We set min->right->parent after `interval_tree_transplant` so that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - min->right->parent = min; + if (min->right != ITREE_NULL) + min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` (of `min_right`, done earlier in the `if`) but we prefer to do it @@ -1508,7 +1501,8 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour else dest->parent->right = source; - source->parent = dest->parent; + if (source != ITREE_NULL) + source->parent = dest->parent; } From 12836db6e4e09378d41301b3d4e1fcff58132d3a Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Tue, 11 Oct 2022 11:17:44 -0400 Subject: [PATCH 52/70] itree.c (check_tree): Simplify * src/itree.c (struct check_subtree_result): Remove `complete`. (check_subtree): Remove `max_depth` arg (and adjust callers). Use 0 as black-depth of empty tree. Remove redundant `node->parent` check (already performed by the caller). (check_tree): Replace with `check_tree_common` (update all callers). Check the root's `parent` field. (check_tree_no_rb): Delete function, inlined in its sole caller. (interval_tree_remove): Add call to `check_tree` (without RB checks) before `interval_tree_remove_fix`. Move update of `size` field accordingly. --- src/itree.c | 138 +++++++++++++--------------------------------------- 1 file changed, 35 insertions(+), 103 deletions(-) diff --git a/src/itree.c b/src/itree.c index 9c5d8ce1426..ef623d0850a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -221,55 +221,27 @@ itree_init (void) struct check_subtree_result { - /* Were all nodes visited? */ - bool complete; - - /* Computed node count of the tree. */ - int size; - - /* Computed limit of the tree (max END). */ - ptrdiff_t limit; - - /* Computed black height of the tree (count of black nodes from the - bottom up to the root). */ - int black_height; + int size; /* Node count of the tree. */ + ptrdiff_t limit; /* Limit of the tree (max END). */ + int black_height; /* Black height of the tree. */ }; static struct check_subtree_result check_subtree (struct interval_node *node, bool check_red_black_invariants, uintmax_t tree_otick, - int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) { - struct check_subtree_result result = { .complete = false, - .size = 0, + struct check_subtree_result result = { .size = 0, .limit = PTRDIFF_MIN, .black_height = 0 }; if (node == ITREE_NULL) - { - /* Every nil node of a Red-Black tree is black */ - result.black_height = 1; - result.complete = true; - return result; - }; - - if (max_depth == 0) - { - result.complete = false; - return result; - } + return result; /* Validate structure. */ - eassert ( - node->parent == ITREE_NULL - || (node->parent->left == node || node->parent->right == node)); eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* No red nodes have red parents. */ - if (check_red_black_invariants && node->parent != ITREE_NULL) - eassert (!node->red || !node->parent->red); - /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -278,8 +250,7 @@ check_subtree (struct interval_node *node, doesn't always update otick. It could, but it is not clear there is a need. */ eassert (node->otick <= tree_otick); - eassert (node->parent == ITREE_NULL - || node->otick <= node->parent->otick); + eassert (node->parent == ITREE_NULL || node->otick <= node->parent->otick); eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; @@ -294,37 +265,24 @@ check_subtree (struct interval_node *node, struct check_subtree_result left_result = check_subtree (node->left, check_red_black_invariants, - tree_otick, max_depth - 1, offset, min_begin, - begin); + tree_otick, offset, min_begin, begin); struct check_subtree_result right_result = check_subtree (node->right, check_red_black_invariants, - tree_otick, max_depth - 1, offset, begin, - max_begin); + tree_otick, offset, begin, max_begin); eassert (left_result.limit <= limit); eassert (right_result.limit <= limit); + eassert (limit == max (end, max (left_result.limit, right_result.limit))); - result.complete = left_result.complete && right_result.complete; - if (result.complete) + if (check_red_black_invariants) { - result.size = 1 + left_result.size + right_result.size; - result.limit - = max (end, max (left_result.limit, right_result.limit)); - - eassert (limit == result.limit); - - if (check_red_black_invariants) - { - /* Every path from a node to a descendent leaf contains the - same number of black nodes. Often said this way: all - nodes have the same "black height". */ - eassert (left_result.black_height - == right_result.black_height); - result.black_height - = (node->red ? 0 : 1) + left_result.black_height; - } + eassert (left_result.black_height == right_result.black_height); + eassert (node->parent != ITREE_NULL || !node->red || !node->parent->red); } + result.size = 1 + left_result.size + right_result.size; + result.limit = limit; + result.black_height = (node->red ? 0 : 1) + left_result.black_height; return result; } @@ -338,8 +296,8 @@ check_subtree (struct interval_node *node, entire tree and validates all invariants. */ static bool -check_tree_common (struct interval_tree *tree, - bool check_red_black_invariants) +check_tree (struct interval_tree *tree, + bool check_red_black_invariants) { eassert (null_is_sane ()); @@ -348,48 +306,19 @@ check_tree_common (struct interval_tree *tree, eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); if (tree->root == ITREE_NULL) return true; - - /* Limit the traversal depth to what 'interval_tree_max_height' - returns. Later, verify that this is enough height to traverse - the complete tree. */ - const int max_height = interval_tree_max_height (tree); - eassert (max_height >= 0); - eassert (max_height <= 120); - - /* NOTE: if this check is too expensive an easy fix is to reduce - max_height to for large trees, then relax the assertion on - result.complete. Assertions in check_subtree will still be made - at the bottom of the tree (where they are probably most - interesting), but some will be skipped closer to the root. */ + eassert (tree->root->parent == ITREE_NULL); struct interval_node *node = tree->root; struct check_subtree_result result = check_subtree (node, check_red_black_invariants, tree->otick, - max_height, node->offset, PTRDIFF_MIN, + node->offset, PTRDIFF_MIN, PTRDIFF_MAX); - eassert (result.complete); eassert (result.size == tree->size); /* The only way this function fails is eassert(). */ return true; } -/* Check the tree with all invariant checks enabled. */ -static bool -check_tree (struct interval_tree *tree) -{ - return check_tree_common (tree, true); -} - -/* Check the tree with all invariant checks enabled, except for the - red-black tree invariants. Useful for asserting the other - invariants while inserting or removing. */ -static bool -check_tree_no_rb (struct interval_tree *tree) -{ - return check_tree_common (tree, false); -} - /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -613,7 +542,7 @@ static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; @@ -654,7 +583,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; - eassert (check_tree_no_rb (tree)); + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ interval_tree_insert_fix (tree, node); } @@ -815,7 +744,7 @@ struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { eassert (interval_tree_contains (tree, node)); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents @@ -884,15 +813,18 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (min_parent); } interval_tree_propagate_limit (node->parent); - - if (broken) - interval_tree_remove_fix (tree, broken, broken_parent); - - node->right = node->left = node->parent = NULL; --tree->size; + if (broken) + { + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ + interval_tree_remove_fix (tree, broken, broken_parent); + } + + node->right = node->left = node->parent = NULL; + eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ return node; } @@ -1462,10 +1394,10 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node } } - /* The root may have been changed to red due to the algorithm. Set - it to black so that property #5 is satisfied. */ + /* The root may have been changed to red due to the algorithm. + Set it to black so that property #5 is satisfied. */ tree->root->red = false; - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ } /* Return accumulated offsets of NODE's parents. */ From 034d50415858b18b032b116804bfefc1be421bb3 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Tue, 11 Oct 2022 11:41:47 -0700 Subject: [PATCH 53/70] ; * .clang-format: Add ITREE_FOREACH. --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 44200a39952..ac9f95c88a8 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ BreakBeforeBinaryOperators: All BreakBeforeBraces: GNU ColumnLimit: 70 ContinuationIndentWidth: 2 -ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE] +ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE, ITREE_FOREACH] IncludeCategories: - Regex: '^$' Priority: -1 From fda8723be640593a662d7ff9d4900b7f9e56423e Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Tue, 11 Oct 2022 20:32:08 -0700 Subject: [PATCH 54/70] ; * src/itree.c (check_tree): assert that the tree root is black --- src/itree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/itree.c b/src/itree.c index ef623d0850a..deef0335cfd 100644 --- a/src/itree.c +++ b/src/itree.c @@ -307,6 +307,7 @@ check_tree (struct interval_tree *tree, if (tree->root == ITREE_NULL) return true; eassert (tree->root->parent == ITREE_NULL); + eassert (!check_red_black_invariants || !tree->root->red); struct interval_node *node = tree->root; struct check_subtree_result result From 65a7b5a802a15daa6274403fef822ec3c9b95469 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Tue, 11 Oct 2022 20:19:16 -0700 Subject: [PATCH 55/70] ; * src/itree.c (check_subtree): fix logical error in eassert --- src/itree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/itree.c b/src/itree.c index deef0335cfd..1728a8ab3a1 100644 --- a/src/itree.c +++ b/src/itree.c @@ -277,7 +277,7 @@ check_subtree (struct interval_node *node, if (check_red_black_invariants) { eassert (left_result.black_height == right_result.black_height); - eassert (node->parent != ITREE_NULL || !node->red || !node->parent->red); + eassert (node->parent == ITREE_NULL || !node->red || !node->parent->red); } result.size = 1 + left_result.size + right_result.size; From b86505387480fed81629cbc81cef6b70098bd607 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 14 Oct 2022 16:28:33 -0400 Subject: [PATCH 56/70] src/textprop.c (get_char_property_and_overlay): Fix bug#58479 Correct `get-char-property` which failed to ignore empty overlays. * src/textprop.c (get_char_property_and_overlay): Make sure the overlay covers the character. * test/src/buffer-tests.el (buffer-tests--overlay-bug58479): New test. * src/buffer.h (buffer_has_overlays): Avoid `interval_tree_size`. --- src/buffer.h | 2 +- src/textprop.c | 3 ++- test/src/buffer-tests.el | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/buffer.h b/src/buffer.h index 288acd4f5ee..deb0367d990 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1275,7 +1275,7 @@ INLINE bool buffer_has_overlays (void) { return current_buffer->overlays - && (interval_tree_size (current_buffer->overlays) > 0); + && (current_buffer->overlays->root != ITREE_NULL); } /* Functions for accessing a character or byte, diff --git a/src/textprop.c b/src/textprop.c index 04fae528097..b34246f5bc7 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -649,7 +649,8 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object tem = Foverlay_get (node->data, prop); struct sortvec *this; - if (NILP (tem) || (w && ! overlay_matches_window (w, node->data))) + if (NILP (tem) || node->end < pos + 1 + || (w && ! overlay_matches_window (w, node->data))) continue; this = (result == items ? items + 1 : items); diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 9bccbdf2e89..3833f88c5c8 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1340,6 +1340,14 @@ with parameters from the *Messages* buffer modification." (overlay-put ov 'value i))) (should (eq 9 (get-char-property 1 'value))))) +(ert-deftest buffer-tests--overlay-bug58479 () + (with-temp-buffer + (insert "ab") + (let* ((pos (+ (point-min) 1)) + (ol (make-overlay pos pos))) + (overlay-put ol 'my-prop 'set) + (should (null (get-char-property pos 'my-prop)))))) + ;; +==========================================================================+ ;; | Other From 258e618364a878658a5bb5f3997034d5ad17fead Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Wed, 12 Oct 2022 10:06:03 -0700 Subject: [PATCH 57/70] Delete the itree_null sentinel node, use NULL everywhere. This effort caught a few (already commited) places that were dereferencing through ITREE_NULL in a confusing way. It makes some functions have to check for NULL in more places, but in my experinece this is worth it from a code clarity point of view. In doing this I rewrote `interval_tree_remove` completely. There there was one final bug in that function that I simply could not find when I #define'd ITREE_NULL to NULL. I couldn't easily understand that function, so instead I rewrote it completely with a focus on code clarity. Bug went away. I have left the ITREE_NULL #define in code, to reduce code review noise. It is easily removed later, mechanically. * src/itree.h: #define ITREE_NULL to NULL and leave a FIXME. * src/itree.c (itree_null): Delete the itree_null static variable. (null_is_sane): Delete (and all callers). (interval_tree_insert): Insert the first node as black straight away. (itree_newlimit): Handle NULL children. (interval_tree_remove_fix): ditto. (interval_tree_insert_fix): ditto. (interval_tree_remove): Rewrite for clarity. (null_safe_is_red): New function. (null_safe_is_black): New function. (interval_tree_replace_child): Renamed from interval_tree_transplant. (interval_tree_transplant): New function that something I think is more like a full transplantation. (names are hard) --- src/itree.c | 278 ++++++++++++++++++++++++++-------------------------- src/itree.h | 5 +- 2 files changed, 140 insertions(+), 143 deletions(-) diff --git a/src/itree.c b/src/itree.c index 1728a8ab3a1..6e2dfc65673 100644 --- a/src/itree.c +++ b/src/itree.c @@ -140,44 +140,17 @@ static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_tree_replace_child (struct interval_tree *, + struct interval_node *, + struct interval_node *); +static void interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest); +static struct interval_generator * +interval_generator_create (struct interval_tree *); static void interval_tree_insert (struct interval_tree *, struct interval_node *); - -/* The sentinel node, the null node. */ -struct interval_node itree_null = { - .parent = NULL, /* never accessed */ - .left = &itree_null, - .right = &itree_null, - .begin = PTRDIFF_MIN, - .end = PTRDIFF_MIN, - .limit = PTRDIFF_MIN, /* => max(x, null.limit) = x */ - .offset = 0, - .otick = 0, - .red = false, - .rear_advance = false, - .front_advance = false, -}; - -static bool -null_is_sane (void) -{ - /* All sentinel node fields are read-only. */ - eassert (itree_null.parent == NULL); - eassert (itree_null.left == &itree_null); - eassert (itree_null.right == &itree_null); - eassert (itree_null.begin == PTRDIFF_MIN); - eassert (itree_null.end == PTRDIFF_MIN); - eassert (itree_null.limit == PTRDIFF_MIN); - eassert (itree_null.offset == 0); - eassert (itree_null.otick == 0); - eassert (itree_null.red == false); - eassert (itree_null.rear_advance == false); - eassert (itree_null.front_advance == false); - - /* if we get this far things must be good */ - return true; -} +static bool null_safe_is_red (struct interval_node *node); +static bool null_safe_is_black (struct interval_node *node); /* +------------------------------------------------------------------------------------+ */ @@ -214,8 +187,6 @@ static struct interval_generator *iter; static void itree_init (void) { - eassert (null_is_sane ()); - iter = interval_generator_create (NULL); } @@ -299,8 +270,6 @@ static bool check_tree (struct interval_tree *tree, bool check_red_black_invariants) { - eassert (null_is_sane ()); - eassert (tree != NULL); eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); @@ -549,7 +518,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, - to "confirm" that the begin/end fields are uptodate. */ + to "confirm" that the begin/end fields are up to date. */ eassert (node->otick == otick); /* Find the insertion point, accumulate node's offset and update @@ -577,7 +546,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = ITREE_NULL; node->right = ITREE_NULL; - node->red = true; + node->red = node != tree->root; node->offset = 0; node->limit = node->end; eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); @@ -620,8 +589,12 @@ itree_newlimit (struct interval_node *node) { eassert (node != ITREE_NULL); return max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + max (node->left == ITREE_NULL + ? PTRDIFF_MIN + : node->left->limit + node->left->offset, + node->right == ITREE_NULL + ? PTRDIFF_MIN + : node->right->limit + node->right->offset)); } static bool @@ -653,17 +626,20 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node, struct interval_node *parent) { + if (parent == ITREE_NULL) + eassert (node == tree->root); + else eassert (node == ITREE_NULL || node->parent == parent); - eassert (parent == ITREE_NULL - || node == parent->left || node == parent->right); - while (parent != ITREE_NULL && !node->red) + while (parent != ITREE_NULL && null_safe_is_black (node)) { + eassert (node == parent->left || node == parent->right); + if (node == parent->left) { struct interval_node *other = parent->right; - if (other->red) /* case 1.a */ + if (null_safe_is_red (other)) /* case 1.a */ { other->red = false; parent->red = true; @@ -671,8 +647,8 @@ interval_tree_remove_fix (struct interval_tree *tree, other = parent->right; } - if (!other->left->red /* 2.a */ - && !other->right->red) + if (null_safe_is_black (other->left) /* 2.a */ + && null_safe_is_black (other->right)) { other->red = true; node = parent; @@ -681,7 +657,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - if (!other->right->red) /* 3.a */ + if (null_safe_is_black (other->right)) /* 3.a */ { other->left->red = false; other->red = true; @@ -700,7 +676,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { struct interval_node *other = parent->left; - if (other->red) /* 1.b */ + if (null_safe_is_red (other)) /* 1.b */ { other->red = false; parent->red = true; @@ -708,8 +684,8 @@ interval_tree_remove_fix (struct interval_tree *tree, other = parent->left; } - if (!other->right->red /* 2.b */ - && !other->left->red) + if (null_safe_is_black (other->right) /* 2.b */ + && null_safe_is_black (other->left)) { other->red = true; node = parent; @@ -718,7 +694,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - if (!other->left->red) /* 3.b */ + if (null_safe_is_black (other->left)) /* 3.b */ { other->right->red = false; other->red = true; @@ -736,6 +712,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } } + if (node != ITREE_NULL) node->red = false; } @@ -747,86 +724,70 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - /* `broken`, if non-NULL, holds a node that's being moved up to where a black - node used to be, which may thus require further fixups in its parents - (done in `interval_tree_remove_fix`). */ - struct interval_node *broken = NULL; - /* `broken` may be null but `interval_tree_remove_fix` still - needs to know its "parent". - Cormen et al.'s Introduction to Algorithms uses a trick where - they rely on the null sentinel node's `parent` field to hold - the right value. While this works, it breaks the rule that - the `parent` field is write-only making correctness much more tricky - and introducing a dependency on a global state (which is incompatible - with concurrency among other things), so instead we keep track of - `broken`'s parent manually. */ - struct interval_node *broken_parent = NULL; - + /* Find `splice`, the leaf node to splice out of the tree. When + `node` has at most one child this is `node` itself. Otherwise, + it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); - if (node->left == ITREE_NULL || node->right == ITREE_NULL) - { - struct interval_node *subst - = node->right == ITREE_NULL ? node->left : node->right; - if (!node->red) - { - broken = subst; - broken_parent = node->parent; /* The future parent. */ - } - interval_tree_transplant (tree, subst, node); - } - else - { - struct interval_node *min - = interval_tree_subtree_min (tree->otick, node->right); - struct interval_node *min_right = min->right; - struct interval_node *min_parent = min->parent; + struct interval_node *splice + = (node->left == ITREE_NULL || node->right == ITREE_NULL) + ? node + : interval_tree_subtree_min (tree->otick, node->right); - if (!min->red) - broken = min_right; - eassert (min != ITREE_NULL); - /* `min` should not have any offsets any more so we can move nodes - underneath it without risking changing their begin/end. */ - eassert (min->offset == 0); - if (min->parent == node) - broken_parent = min; /* The future parent. */ - else - { - interval_tree_transplant (tree, min_right, min); - broken_parent = min->parent; /* The parent. */ - min->right = node->right; - } - min->left = node->left; - min->left->parent = min; - min->red = node->red; - /* FIXME: At this point node->right->parent = min but node->right - is a parent of `min` so total_offsets gets stuck in an inf-loop! */ - interval_tree_transplant (tree, min, node); - /* We set min->right->parent after `interval_tree_transplant` so - that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - if (min->right != ITREE_NULL) - min->right->parent = min; - interval_tree_update_limit (min); - /* This call "belongs" with the first `interval_tree_transplant` - (of `min_right`, done earlier in the `if`) but we prefer to do it - here ("late") because otherwise it would sometimes update part of - the tree with values that would be invalidated by the second - `interval_tree_transplant`. */ - interval_tree_propagate_limit (min_parent); + /* Find `subtree`, the only child of `splice` (may be NULL). Note: + `subtree` will not be modified other than changing its parent to + `splice`. */ + eassert (splice->left == ITREE_NULL || splice->right == ITREE_NULL); + struct interval_node *subtree + = (splice->left != ITREE_NULL) ? splice->left : splice->right; + + /* Save a pointer to the parent of where `subtree` will eventually + be in `subtree_parent`. */ + struct interval_node *subtree_parent + = (splice->parent != node) ? splice->parent : splice; + + /* If `splice` is black removing it may violate Red-Black + invariants, so note this for later. */ + + /* Replace `splice` with `subtree` under subtree's parent. If + `splice` is black, this creates a red-red violation, so remember + this now as the field can be overwritten when splice is + transplanted below. */ + interval_tree_replace_child (tree, subtree, splice); + bool removed_black = !splice->red; + + /* Replace `node` with `splice` in the tree and propagate limit + upwards, if necessary. Note: Limit propagation can stabilize at + any point, so we must call from bottom to top for every node that + has a new child. */ + if (splice != node) + { + interval_tree_transplant (tree, splice, node); + interval_tree_propagate_limit (subtree_parent); + if (splice != subtree_parent) + interval_tree_propagate_limit (splice); } - interval_tree_propagate_limit (node->parent); + interval_tree_propagate_limit (splice->parent); + --tree->size; - if (broken) - { - eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ - interval_tree_remove_fix (tree, broken, broken_parent); - } - - node->right = node->left = node->parent = NULL; + /* Fix any black height violation caused by removing a black + node. */ + if (removed_black) + interval_tree_remove_fix (tree, subtree, subtree_parent); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ + /* Clear fields related to the tree for sanity while debugging. */ + node->red = false; + node->right = node->left = node->parent = NULL; + node->limit = 0; + + /* Must be clean (all offsets applied). Also, some callers rely on + node's otick being the tree's otick. */ + eassert (node->otick == tree->otick); + eassert (node->offset == 0); + return node; } @@ -860,7 +821,6 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - eassert (null_is_sane ()); /* struct interval_generator *iter = tree->iter; */ if (iter->running) { @@ -1171,6 +1131,18 @@ interval_generator_narrow (struct interval_generator *g, * | Internal Functions * +===================================================================================+ */ +static bool +null_safe_is_red (struct interval_node *node) +{ + return node != ITREE_NULL && node->red; +} + +static bool +null_safe_is_black (struct interval_node *node) +{ + return node == ITREE_NULL || !node->red; /* NULL nodes are black */ +} + /* Update NODE's limit attribute according to its children. */ static void @@ -1224,9 +1196,6 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) /* Update limit of NODE and its ancestors. Stop when it becomes stable, i.e. new_limit = old_limit. - - NODE may also be the null node, in which case its parent is - used. (This feature is due to the RB algorithm.) */ static void @@ -1331,7 +1300,9 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) { - while (node->parent->red) + eassert (tree->root->red == false); + + while (null_safe_is_red (node->parent)) { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ @@ -1343,7 +1314,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node our "uncle". */ struct interval_node *uncle = node->parent->parent->right; - if (uncle->red) /* case 1.a */ + if (null_safe_is_red (uncle)) /* case 1.a */ { /* Uncle and parent are red but should be black because NODE is red. Change the colors accordingly and @@ -1373,7 +1344,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* This is the symmetrical case of above. */ struct interval_node *uncle = node->parent->parent->left; - if (uncle->red) /* case 1.b */ + if (null_safe_is_red (uncle)) /* case 1.b */ { node->parent->red = false; uncle->red = false; @@ -1415,15 +1386,20 @@ itree_total_offset (struct interval_node *node) return offset; } -/* Link node SOURCE in DEST's place. - It's the caller's responsability to refresh the `limit`s - of DEST->parents afterwards. */ +/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts + *only* the parent linkage of SOURCE and either the parent's child + link the tree root. + + Warning: DEST is left unmodified. SOURCE's child links are + unchanged. Caller is responsible for recalculation of `limit`. + Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, - struct interval_node *dest) +interval_tree_replace_child (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) { - eassert (tree && source && dest && dest != ITREE_NULL); + eassert (tree && dest != ITREE_NULL); eassert (source == ITREE_NULL || itree_total_offset (source) == itree_total_offset (dest)); @@ -1438,6 +1414,28 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour source->parent = dest->parent; } +/* Replace DEST with SOURCE in the tree. Copies the following fields + from DEST to SOURCE: red, parent, left, right. Also updates + parent, left and right in surrounding nodes to point to SOURCE. + + Warning: DEST is left unmodified. Caller is responsible for + recalculation of `limit`. Requires both nodes to be using the same + effective `offset`. */ +static void +interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + interval_tree_replace_child (tree, source, dest); + source->left = dest->left; + if (source->left != ITREE_NULL) + source->left->parent = source; + source->right = dest->right; + if (source->right != ITREE_NULL) + source->right->parent = source; + source->red = dest->red; +} + /* +===================================================================================+ * | Debugging diff --git a/src/itree.h b/src/itree.h index 5733ec15305..0e2e7d1f81f 100644 --- a/src/itree.h +++ b/src/itree.h @@ -95,9 +95,8 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -/* The sentinel node, the null node. */ -extern struct interval_node itree_null; -#define ITREE_NULL (&itree_null) +/* FIXME: replace ITREE_NULL -> NULL everywhere */ +#define ITREE_NULL NULL struct interval_tree { From 4020dde296b9a1e5b313c39291fdc61a4fc42d96 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 16 Oct 2022 15:10:17 -0700 Subject: [PATCH 58/70] ; * src/itree.c: remove 'inline' from functions with extern linkage --- src/itree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/itree.c b/src/itree.c index 6e2dfc65673..15af56e806a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1045,7 +1045,7 @@ interval_node_intersects (const struct interval_node *node, /* Return the next node of the iterator in the order given when it was started; or NULL if there are no more nodes. */ -inline struct interval_node* +struct interval_node * interval_generator_next (struct interval_generator *g) { eassert (g->running); @@ -1115,7 +1115,7 @@ interval_generator_next (struct interval_generator *g) /* Limit G to the new interval [BEGIN, END), which must be a subset of the current one. I.E. it can't grow on either side. */ -inline void +void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { From 75bfb2614d310f0708619f66f9088784f5733c49 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 16 Oct 2022 16:03:30 -0700 Subject: [PATCH 59/70] Rename itree iterators with itree_iterator prefix * src/itree.h: Rename struct interval_generator -> itree_iterator. Rename functions: itree_busy_p -> itree_iterator_busy_p, interval_tree_iter_start -> itree_iterator_start, interval_generator_narrow -> itree_iterator_narrow, interval_tree_iter_finish -> itree_iterator_finish, interval_generator_next -> itree_iterator_next. * src/itree.c: Use new names everywhere. * src/eval.c: ditto. --- src/eval.c | 2 +- src/itree.c | 43 +++++++++++++++++++++---------------------- src/itree.h | 34 ++++++++++++++++++++-------------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/eval.c b/src/eval.c index 37c25c79148..197eb6afcb3 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1711,7 +1711,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) Lisp_Object clause = Qnil; struct handler *h; - eassert (!itree_busy_p ()); + eassert (!itree_iterator_busy_p ()); if (gc_in_progress || waiting_for_input) emacs_abort (); diff --git a/src/itree.c b/src/itree.c index 15af56e806a..0ba9e662bc2 100644 --- a/src/itree.c +++ b/src/itree.c @@ -146,8 +146,8 @@ static void interval_tree_replace_child (struct interval_tree *, static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest); -static struct interval_generator * -interval_generator_create (struct interval_tree *); +static struct itree_iterator * +itree_iterator_create (struct interval_tree *); static void interval_tree_insert (struct interval_tree *, struct interval_node *); static bool null_safe_is_red (struct interval_node *node); static bool null_safe_is_black (struct interval_node *node); @@ -165,7 +165,7 @@ struct interval_stack }; /* State used when iterating interval. */ -struct interval_generator +struct itree_iterator { struct interval_stack *stack; ptrdiff_t begin; @@ -182,12 +182,12 @@ struct interval_generator are limited by the fact we don't allow modifying the tree at the same time, making the use of nested iterations quite rare anyway. So we just use a single global iterator instead for now. */ -static struct interval_generator *iter; +static struct itree_iterator *iter; static void itree_init (void) { - iter = interval_generator_create (NULL); + iter = itree_iterator_create (NULL); } struct check_subtree_result @@ -481,7 +481,7 @@ static void interval_tree_init (struct interval_tree *tree) { interval_tree_clear (tree); - /* tree->iter = interval_generator_create (tree); */ + /* tree->iter = itree_iterator_create (tree); */ } #endif @@ -491,7 +491,7 @@ interval_tree_destroy (struct interval_tree *tree) { eassert (tree->root == ITREE_NULL); /* if (tree->iter) - * interval_generator_destroy (tree->iter); */ + * itree_iterator_destroy (tree->iter); */ xfree (tree); } @@ -805,7 +805,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) } bool -itree_busy_p (void) +itree_iterator_busy_p (void) { return (iter && iter->running); } @@ -815,13 +815,12 @@ itree_busy_p (void) time. */ -struct interval_generator * -interval_tree_iter_start (struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order, - const char* file, int line) +struct itree_iterator * +itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum interval_tree_order order, + const char *file, int line) { - /* struct interval_generator *iter = tree->iter; */ + /* struct itree_iterator *iter = tree->iter; */ if (iter->running) { fprintf (stderr, @@ -847,7 +846,7 @@ interval_tree_iter_start (struct interval_tree *tree, /* Stop using the iterator. */ void -interval_tree_iter_finish (struct interval_generator *iter) +itree_iterator_finish (struct itree_iterator *iter) { eassert (iter->running); iter->running = false; @@ -1007,10 +1006,10 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l /* Allocate a new generator for TREE. */ -static struct interval_generator * -interval_generator_create (struct interval_tree *tree) +static struct itree_iterator * +itree_iterator_create (struct interval_tree *tree) { - struct interval_generator *g = xmalloc (sizeof *g); + struct itree_iterator *g = xmalloc (sizeof *g); /* 19 here just avoids starting with a silly-small stack. FIXME: Since this stack only needs to be about 2*max_depth in the worst case, we could completely pre-allocate it to something @@ -1046,7 +1045,7 @@ interval_node_intersects (const struct interval_node *node, started; or NULL if there are no more nodes. */ struct interval_node * -interval_generator_next (struct interval_generator *g) +itree_iterator_next (struct itree_iterator *g) { eassert (g->running); @@ -1105,7 +1104,7 @@ interval_generator_next (struct interval_generator *g) break; } } - /* Node may have been invalidated by interval_generator_narrow + /* Node may have been invalidated by itree_iterator_narrow after it was pushed: Check if it still intersects. */ } while (node && ! interval_node_intersects (node, g->begin, g->end)); @@ -1116,8 +1115,8 @@ interval_generator_next (struct interval_generator *g) the current one. I.E. it can't grow on either side. */ void -interval_generator_narrow (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end) +itree_iterator_narrow (struct itree_iterator *g, + ptrdiff_t begin, ptrdiff_t end) { eassert (g->running); eassert (begin >= g->begin); diff --git a/src/itree.h b/src/itree.h index 0e2e7d1f81f..f98f028ea52 100644 --- a/src/itree.h +++ b/src/itree.h @@ -32,8 +32,8 @@ along with GNU Emacs. If not, see . */ node.end directly, is while the node is not part of any tree. NOTE: It is safe to read node.begin and node.end directly, if the - node came from a generator, because it validates the nodes it - returns as a side-effect. + node came from an iterator, because it validates the nodes it + returns as a side-effect. See ITREE_FOREACH. */ struct interval_node; @@ -122,14 +122,20 @@ void interval_tree_clear (struct interval_tree *); void itree_insert_node (struct interval_tree *tree, struct interval_node *node, ptrdiff_t begin, ptrdiff_t end); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, - const char* file, int line); -void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); -void interval_tree_iter_finish (struct interval_generator *); -struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -bool itree_busy_p (void); + +/* Iteration functions. Almost all code should use ITREE_FOREACH + instead. */ +bool itree_iterator_busy_p (void); +struct itree_iterator * +itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum interval_tree_order order, + const char *file, int line); +void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, + ptrdiff_t); +void itree_iterator_finish (struct itree_iterator *); +struct interval_node *itree_iterator_next (struct itree_iterator *); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, @@ -162,16 +168,16 @@ bool itree_busy_p (void); if (!t) \ { } \ else \ - for (struct interval_generator *itree_iter_ \ - = interval_tree_iter_start (t, beg, end, ITREE_##order, \ + for (struct itree_iterator *itree_iter_ \ + = itree_iterator_start (t, beg, end, ITREE_##order, \ __FILE__, __LINE__); \ - ((n = interval_generator_next (itree_iter_)) \ - || (interval_tree_iter_finish (itree_iter_), false));) + ((n = itree_iterator_next (itree_iter_)) \ + || (itree_iterator_finish (itree_iter_), false));) #define ITREE_FOREACH_ABORT() \ - interval_tree_iter_finish (itree_iter_) + itree_iterator_finish (itree_iter_) #define ITREE_FOREACH_NARROW(beg, end) \ - interval_generator_narrow (itree_iter_, beg, end) + itree_iterator_narrow (itree_iter_, beg, end) #endif From 19f5431cf644517776e2945912dd5d5e8933b3dc Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 17 Oct 2022 14:10:06 -0400 Subject: [PATCH 60/70] * src/itree.c: Eliminate all prototypes for static functions Massive code reorganization to move definitions of static functions before their first use, so as to remove the need for redundant prototypes. While at it, fix a few places where the used more than 80 column. --- src/itree.c | 1012 +++++++++++++++++++++++++-------------------------- 1 file changed, 494 insertions(+), 518 deletions(-) diff --git a/src/itree.c b/src/itree.c index 0ba9e662bc2..aabf33fcb38 100644 --- a/src/itree.c +++ b/src/itree.c @@ -128,34 +128,33 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ -/* FIXME: The code seems to use "generator" and "iterator" - inconsistently/interchangeably. We should fix this naming. */ - -static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); -static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); -static int interval_tree_max_height (const struct interval_tree *); -static void interval_tree_update_limit (struct interval_node *); -static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); -static void interval_tree_propagate_limit (struct interval_node *); -static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); -static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_replace_child (struct interval_tree *, - struct interval_node *, - struct interval_node *); -static void interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest); -static struct itree_iterator * -itree_iterator_create (struct interval_tree *); -static void interval_tree_insert (struct interval_tree *, struct interval_node *); -static bool null_safe_is_red (struct interval_node *node); -static bool null_safe_is_black (struct interval_node *node); - -/* +------------------------------------------------------------------------------------+ */ +/* +=======================================================================+ + * | Stack + * +=======================================================================+ */ typedef uintptr_t nodeptr_and_flag; +static inline nodeptr_and_flag +make_nav (struct interval_node *ptr, bool flag) +{ + uintptr_t v = (uintptr_t) ptr; + /* We assume alignment imposes the LSB is clear for us to use it. */ + eassert (!(v & 1)); + return v | !!flag; +} + +static inline struct interval_node * +nav_nodeptr (nodeptr_and_flag nav) +{ + return (struct interval_node *) (nav & (~(uintptr_t)1)); +} + +static inline bool +nav_flag (nodeptr_and_flag nav) +{ + return (bool) (nav & 1); +} + /* Simple dynamic array. */ struct interval_stack { @@ -164,6 +163,82 @@ struct interval_stack size_t length; }; +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, + stack->size * sizeof (*stack->nodes)); + } +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + eassert (node && node != ITREE_NULL); + + /* FIXME: While the stack used in the iterator is bounded by the tree + depth and could be easily pre-allocated to a large enough size to avoid + this "ensure" check, `interval_stack_push` is also used elsewhere to + simply collect some subset of the overlays, where it's only bounded by + the total number of overlays in the buffer (which can be large and thus + preferably not pre-allocated needlessly). */ + interval_stack_ensure_space (stack, stack->length + 1); + + stack->nodes[stack->length] = make_nav (node, flag); + stack->length++; +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_push_flagged (stack, node, false); +} + +static inline nodeptr_and_flag +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return make_nav (NULL, false); + return stack->nodes[--stack->length]; +} + + +/* +-----------------------------------------------------------------------+ */ + /* State used when iterating interval. */ struct itree_iterator { @@ -184,6 +259,33 @@ struct itree_iterator So we just use a single global iterator instead for now. */ static struct itree_iterator *iter; +static int +interval_tree_max_height (const struct interval_tree *tree) +{ + return 2 * log (tree->size + 1) / log (2) + 0.5; +} + +/* Allocate a new iterator for TREE. */ + +static struct itree_iterator * +itree_iterator_create (struct interval_tree *tree) +{ + struct itree_iterator *g = xmalloc (sizeof *g); + /* 19 here just avoids starting with a silly-small stack. + FIXME: Since this stack only needs to be about 2*max_depth + in the worst case, we could completely pre-allocate it to something + like word-bit-size * 2 and then never worry about growing it. */ + const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; + + g->stack = interval_stack_create (size); + g->running = false; + g->begin = 0; + g->end = 0; + g->file = NULL; + g->line = 0; + return g; +} + static void itree_init (void) { @@ -289,108 +391,122 @@ check_tree (struct interval_tree *tree, return true; } -/* +===================================================================================+ - * | Stack - * +===================================================================================+ */ +/* +=======================================================================+ + * | Internal Functions + * +=======================================================================+ */ -static inline nodeptr_and_flag -make_nav (struct interval_node *ptr, bool flag) +static bool +null_safe_is_red (struct interval_node *node) { - uintptr_t v = (uintptr_t) ptr; - /* We assume alignment imposes the LSB is clear for us to use it. */ - eassert (!(v & 1)); - return v | !!flag; + return node != ITREE_NULL && node->red; } -static inline struct interval_node * -nav_nodeptr (nodeptr_and_flag nav) +static bool +null_safe_is_black (struct interval_node *node) { - return (struct interval_node *) (nav & (~(uintptr_t)1)); + return node == ITREE_NULL || !node->red; /* NULL nodes are black */ } -static inline bool -nav_flag (nodeptr_and_flag nav) +static inline ptrdiff_t +itree_newlimit (struct interval_node *node) { - return (bool) (nav & 1); + eassert (node != ITREE_NULL); + return max (node->end, + max (node->left == ITREE_NULL + ? PTRDIFF_MIN + : node->left->limit + node->left->offset, + node->right == ITREE_NULL + ? PTRDIFF_MIN + : node->right->limit + node->right->offset)); } -/* This is just a simple dynamic array with stack semantics. */ - -static struct interval_stack* -interval_stack_create (intmax_t initial_size) -{ - struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); - stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); - stack->length = 0; - return stack; -} +/* Update NODE's limit attribute according to its children. */ static void -interval_stack_destroy (struct interval_stack *stack) +interval_tree_update_limit (struct interval_node *node) { - if (! stack) + if (node == ITREE_NULL) return; - if (stack->nodes) - xfree (stack->nodes); - xfree (stack); + + node->limit = itree_newlimit (node); } +/* Apply NODE's offset to its begin, end and limit values and + propagate it to its children. + + Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . +*/ + static void -interval_stack_clear (struct interval_stack *stack) +interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - stack->length = 0; -} - -static inline void -interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) -{ - if (nelements > stack->size) + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + if (node->otick == otick) { - stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, - stack->size * sizeof (*stack->nodes)); + eassert (node->offset == 0); + return; } + + /* Offsets can be inherited from dirty nodes (with out of date + otick) during removal, since we do not travel down from the root + in that case. In this case rotations are performed on + potentially "dirty" nodes, where we only need to make sure the + *local* offsets are zero. */ + + if (node->offset) + { + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != ITREE_NULL) + node->left->offset += node->offset; + if (node->right != ITREE_NULL) + node->right->offset += node->offset; + node->offset = 0; + } + /* The only thing that matters about `otick` is whether it's equal to + that of the tree. We could also "blindly" inherit from parent->otick, + but we need to tree's `otick` anyway for when there's no parent. */ + if (node->parent == ITREE_NULL || node->parent->otick == otick) + node->otick = otick; } -/* Push NODE on the STACK, while settings its visited flag to FLAG. */ +/* Update limit of NODE and its ancestors. Stop when it becomes + stable, i.e. new_limit = old_limit. */ -static inline void -interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) +static void +interval_tree_propagate_limit (struct interval_node *node) { - eassert (node && node != ITREE_NULL); + if (node == ITREE_NULL) + return; - /* FIXME: While the stack used in the iterator is bounded by the tree - depth and could be easily pre-allocated to a large enough size to avoid - this "ensure" check, `interval_stack_push` is also used elsewhere to - simply collect some subset of the overlays, where it's only bounded by - the total number of overlays in the buffer (which can be large and thus - preferably not pre-allocated needlessly). */ - interval_stack_ensure_space (stack, stack->length + 1); - - stack->nodes[stack->length] = make_nav (node, flag); - stack->length++; + while (1) { + ptrdiff_t newlimit = itree_newlimit (node); + if (newlimit == node->limit) + break; + node->limit = newlimit; + if (node->parent == ITREE_NULL) + break; + node = node->parent; + } } -static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) +static struct interval_node* +interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - interval_stack_push_flagged (stack, node, false); + + if (tree->otick == node->otick || node == ITREE_NULL) + return node; + if (node != tree->root) + interval_tree_validate (tree, node->parent); + + interval_tree_inherit_offset (tree->otick, node); + return node; } -static inline nodeptr_and_flag -interval_stack_pop (struct interval_stack *stack) -{ - if (stack->length == 0) - return make_nav (NULL, false); - return stack->nodes[--stack->length]; -} - - -/* +===================================================================================+ +/* +=======================================================================+ * | Tree operations - * +===================================================================================+ */ + * +=======================================================================+ */ /* Initialize an allocated node. */ @@ -399,6 +515,9 @@ interval_node_init (struct interval_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { + node->parent = ITREE_NULL; + node->left = ITREE_NULL; + node->right = ITREE_NULL; node->begin = -1; node->end = -1; node->front_advance = front_advance; @@ -426,29 +545,6 @@ interval_node_end (struct interval_tree *tree, return node->end; } -/* Safely modify a node's interval. */ - -void -interval_node_set_region (struct interval_tree *tree, - struct interval_node *node, - ptrdiff_t begin, ptrdiff_t end) -{ - interval_tree_validate (tree, node); - if (begin != node->begin) - { - interval_tree_remove (tree, node); - node->begin = min (begin, PTRDIFF_MAX - 1); - node->end = max (node->begin, end); - interval_tree_insert (tree, node); - } - else if (end != node->end) - { - node->end = max (node->begin, end); - eassert (node != ITREE_NULL); - interval_tree_propagate_limit (node); - } -} - /* Allocate an interval_tree. Free with interval_tree_destroy. */ struct interval_tree* @@ -503,15 +599,177 @@ interval_tree_size (struct interval_tree *tree) return tree->size; } -/* Insert a NODE into the TREE. +/* Perform the familiar left-rotation on node NODE. */ - Note, that inserting a node twice results in undefined behaviour. -*/ +static void +interval_tree_rotate_left (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (node->right != ITREE_NULL); + + struct interval_node *right = node->right; + + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, right); + + /* Turn right's left subtree into node's right subtree. */ + node->right = right->left; + if (right->left != ITREE_NULL) + right->left->parent = node; + + /* right's parent was node's parent. */ + if (right != ITREE_NULL) + right->parent = node->parent; + + /* Get the parent to point to right instead of node. */ + if (node != tree->root) + { + if (node == node->parent->left) + node->parent->left = right; + else + node->parent->right = right; + } + else + tree->root = right; + + /* Put node on right's left. */ + right->left = node; + if (node != ITREE_NULL) + node->parent = right; + + /* Order matters here. */ + interval_tree_update_limit (node); + interval_tree_update_limit (right); +} + +/* Perform the familiar right-rotation on node NODE. */ + +static void +interval_tree_rotate_right (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (tree && node && node->left != ITREE_NULL); + + struct interval_node *left = node->left; + + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, left); + + node->left = left->right; + if (left->right != ITREE_NULL) + left->right->parent = node; + + if (left != ITREE_NULL) + left->parent = node->parent; + if (node != tree->root) + { + if (node == node->parent->right) + node->parent->right = left; + else + node->parent->left = left; + } + else + tree->root = left; + + left->right = node; + if (node != ITREE_NULL) + node->parent = left; + + interval_tree_update_limit (left); + interval_tree_update_limit (node); +} + +/* Repair the tree after an insertion. + The new NODE was added as red, so we may have 2 reds in a row. + Rebalance the parents as needed to re-establish the RB invariants. */ + +static void +interval_tree_insert_fix (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (tree->root->red == false); + + while (null_safe_is_red (node->parent)) + { + /* NODE is red and its parent is red. This is a violation of + red-black tree property #3. */ + eassert (node->red); + + if (node->parent == node->parent->parent->left) + { + /* We're on the left side of our grandparent, and OTHER is + our "uncle". */ + struct interval_node *uncle = node->parent->parent->right; + + if (null_safe_is_red (uncle)) /* case 1.a */ + { + /* Uncle and parent are red but should be black because + NODE is red. Change the colors accordingly and + proceed with the grandparent. */ + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; + node = node->parent->parent; + } + else + { + /* Parent and uncle have different colors; parent is + red, uncle is black. */ + if (node == node->parent->right) /* case 2.a */ + { + node = node->parent; + interval_tree_rotate_left (tree, node); + } + /* case 3.a */ + node->parent->red = false; + node->parent->parent->red = true; + interval_tree_rotate_right (tree, node->parent->parent); + } + } + else + { + /* This is the symmetrical case of above. */ + struct interval_node *uncle = node->parent->parent->left; + + if (null_safe_is_red (uncle)) /* case 1.b */ + { + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; + node = node->parent->parent; + } + else + { + if (node == node->parent->left) /* case 2.b */ + { + node = node->parent; + interval_tree_rotate_right (tree, node); + } + /* case 3.b */ + node->parent->red = false; + node->parent->parent->red = true; + interval_tree_rotate_left (tree, node->parent->parent); + } + } + } + + /* The root may have been changed to red due to the algorithm. + Set it to black so that property #5 is satisfied. */ + tree->root->red = false; + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ +} + +/* Insert a NODE into the TREE. + Note, that inserting a node twice results in undefined behaviour. */ static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != ITREE_NULL); + eassert (node->begin <= node->end && node != ITREE_NULL); + /* FIXME: The assertion below fails because `delete_all_overlays` + doesn't set left/right/parent to ITREE_NULL. */ + /* eassert (node->left == ITREE_NULL && node->right == ITREE_NULL + && node->parent == ITREE_NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ struct interval_node *parent = ITREE_NULL; @@ -546,15 +804,20 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = ITREE_NULL; node->right = ITREE_NULL; - node->red = node != tree->root; node->offset = 0; node->limit = node->end; eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; - eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ - interval_tree_insert_fix (tree, node); + if (node == tree->root) + node->red = false; + else + { + node->red = true; + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ + interval_tree_insert_fix (tree, node); + } } void @@ -567,6 +830,29 @@ itree_insert_node (struct interval_tree *tree, struct interval_node *node, interval_tree_insert (tree, node); } +/* Safely modify a node's interval. */ + +void +interval_node_set_region (struct interval_tree *tree, + struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + interval_tree_validate (tree, node); + if (begin != node->begin) + { + interval_tree_remove (tree, node); + node->begin = min (begin, PTRDIFF_MAX - 1); + node->end = max (node->begin, end); + interval_tree_insert (tree, node); + } + else if (end != node->end) + { + node->end = max (node->begin, end); + eassert (node != ITREE_NULL); + interval_tree_propagate_limit (node); + } +} + /* Return true, if NODE is a member of TREE. */ static bool @@ -584,19 +870,6 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } -static inline ptrdiff_t -itree_newlimit (struct interval_node *node) -{ - eassert (node != ITREE_NULL); - return max (node->end, - max (node->left == ITREE_NULL - ? PTRDIFF_MIN - : node->left->limit + node->left->offset, - node->right == ITREE_NULL - ? PTRDIFF_MIN - : node->right->limit + node->right->offset)); -} - static bool itree_limit_is_stable (struct interval_node *node) { @@ -716,6 +989,68 @@ interval_tree_remove_fix (struct interval_tree *tree, node->red = false; } +/* Return accumulated offsets of NODE's parents. */ +static ptrdiff_t +itree_total_offset (struct interval_node *node) +{ + eassert (node != ITREE_NULL); + ptrdiff_t offset = 0; + while (node->parent != ITREE_NULL) + { + node = node->parent; + offset += node->offset; + } + return offset; +} + +/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts + *only* the parent linkage of SOURCE and either the parent's child + link the tree root. + + Warning: DEST is left unmodified. SOURCE's child links are + unchanged. Caller is responsible for recalculation of `limit`. + Requires both nodes to be using the same effective `offset`. */ +static void +interval_tree_replace_child (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + eassert (tree && dest != ITREE_NULL); + eassert (source == ITREE_NULL + || itree_total_offset (source) == itree_total_offset (dest)); + + if (dest == tree->root) + tree->root = source; + else if (dest == dest->parent->left) + dest->parent->left = source; + else + dest->parent->right = source; + + if (source != ITREE_NULL) + source->parent = dest->parent; +} +/* Replace DEST with SOURCE in the tree. Copies the following fields + from DEST to SOURCE: red, parent, left, right. Also updates + parent, left and right in surrounding nodes to point to SOURCE. + + Warning: DEST is left unmodified. Caller is responsible for + recalculation of `limit`. Requires both nodes to be using the same + effective `offset`. */ +static void +interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + interval_tree_replace_child (tree, source, dest); + source->left = dest->left; + if (source->left != ITREE_NULL) + source->left->parent = source; + source->right = dest->right; + if (source->right != ITREE_NULL) + source->right->parent = source; + source->red = dest->red; +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* @@ -764,14 +1099,13 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, splice, node); interval_tree_propagate_limit (subtree_parent); if (splice != subtree_parent) - interval_tree_propagate_limit (splice); + interval_tree_update_limit (splice); } interval_tree_propagate_limit (splice->parent); --tree->size; - /* Fix any black height violation caused by removing a black - node. */ + /* Fix any black height violation caused by removing a black node. */ if (removed_black) interval_tree_remove_fix (tree, subtree, subtree_parent); @@ -791,29 +1125,14 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) return node; } -static struct interval_node* -interval_tree_validate (struct interval_tree *tree, struct interval_node *node) -{ - - if (tree->otick == node->otick || node == ITREE_NULL) - return node; - if (node != tree->root) - interval_tree_validate (tree, node->parent); - - interval_tree_inherit_offset (tree->otick, node); - return node; -} - bool itree_iterator_busy_p (void) { return (iter && iter->running); } -/* Start a generator iterating all intervals in [BEGIN,END) in the - given ORDER. Only one iterator per tree can be running at any - time. -*/ +/* Start a iterator enumerating all intervals in [BEGIN,END) in the + given ORDER. Only one iterator per tree can be running at any time. */ struct itree_iterator * itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, @@ -852,29 +1171,24 @@ itree_iterator_finish (struct itree_iterator *iter) iter->running = false; } -static int -interval_tree_max_height (const struct interval_tree *tree) -{ - return 2 * log (tree->size + 1) / log (2) + 0.5; -} - -/* +===================================================================================+ +/* +=======================================================================+ * | Insert/Delete Gaps - * +===================================================================================+ */ + * +=======================================================================+ */ /* Insert a gap at POS of length LENGTH expanding all intervals intersecting it, while respecting their rear_advance and front_advance setting. */ void -interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +interval_tree_insert_gap (struct interval_tree *tree, + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == ITREE_NULL) return; uintmax_t ootick = tree->otick; - /* FIXME: Don't allocate generator/stack anew every time. */ + /* FIXME: Don't allocate iterator/stack anew every time. */ /* Nodes with front_advance starting at pos may mess up the tree order, so we need to remove them first. */ @@ -889,7 +1203,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); - /* We can't use a generator here, because we can't effectively + /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ if (tree->root != ITREE_NULL) { @@ -951,16 +1265,16 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l intersecting it. */ void -interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +interval_tree_delete_gap (struct interval_tree *tree, + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate stack anew every time. */ - /* Can't use the generator here, because by decrementing begin, we - might unintentionally bring shifted nodes back into our search - space. */ + /* Can't use the iterator here, because by decrementing begin, we + might unintentionally bring shifted nodes back into our search space. */ const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); struct interval_node *node; @@ -1000,30 +1314,9 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l -/* +===================================================================================+ - * | Generator - * +===================================================================================+ */ - -/* Allocate a new generator for TREE. */ - -static struct itree_iterator * -itree_iterator_create (struct interval_tree *tree) -{ - struct itree_iterator *g = xmalloc (sizeof *g); - /* 19 here just avoids starting with a silly-small stack. - FIXME: Since this stack only needs to be about 2*max_depth - in the worst case, we could completely pre-allocate it to something - like word-bit-size * 2 and then never worry about growing it. */ - const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; - - g->stack = interval_stack_create (size); - g->running = false; - g->begin = 0; - g->end = 0; - g->file = NULL; - g->line = 0; - return g; -} +/* +=======================================================================+ + * | Iterator + * +=======================================================================+ */ /* Return true, if NODE's interval intersects with [BEGIN, END). Note: We always include empty nodes at BEGIN (and not at END), @@ -1055,12 +1348,12 @@ itree_iterator_next (struct itree_iterator *g) /* The `visited` flag stored in each node is used here (and only here): We keep a "workstack" of nodes we need to consider. This stack consist of nodes of two types: nodes that we have decided - should be returned by the generator, and nodes which we may + should be returned by the iterator, and nodes which we may need to consider (including checking their children). We start an iteration with a stack containing just the root node marked as "not visited" which means that it (and its children) needs to be considered but we haven't yet decided whether it's included - in the generator's output. */ + in the iterator's output. */ do { nodeptr_and_flag nav; @@ -1082,7 +1375,7 @@ itree_iterator_next (struct itree_iterator *g) interval_stack_push_flagged (g->stack, right, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); - /* Node's children may still be off-set and we need to add it. */ + /* Node's children may still be off-set and we need to add it. */ if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); break; @@ -1124,320 +1417,3 @@ itree_iterator_narrow (struct itree_iterator *g, g->begin = max (begin, g->begin); g->end = min (end, g->end); } - - -/* +===================================================================================+ - * | Internal Functions - * +===================================================================================+ */ - -static bool -null_safe_is_red (struct interval_node *node) -{ - return node != ITREE_NULL && node->red; -} - -static bool -null_safe_is_black (struct interval_node *node) -{ - return node == ITREE_NULL || !node->red; /* NULL nodes are black */ -} - -/* Update NODE's limit attribute according to its children. */ - -static void -interval_tree_update_limit (struct interval_node *node) -{ - if (node == ITREE_NULL) - return; - - node->limit = itree_newlimit (node); -} - -/* Apply NODE's offset to its begin, end and limit values and - propagate it to its children. - - Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . -*/ - -static void -interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) -{ - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); - if (node->otick == otick) - { - eassert (node->offset == 0); - return; - } - - /* Offsets can be inherited from dirty nodes (with out of date - otick) during removal, since we do not travel down from the root - in that case. In this case rotations are performed on - potentially "dirty" nodes, where we only need to make sure the - *local* offsets are zero. */ - - if (node->offset) - { - node->begin += node->offset; - node->end += node->offset; - node->limit += node->offset; - if (node->left != ITREE_NULL) - node->left->offset += node->offset; - if (node->right != ITREE_NULL) - node->right->offset += node->offset; - node->offset = 0; - } - /* The only thing that matters about `otick` is whether it's equal to - that of the tree. We could also "blindly" inherit from parent->otick, - but we need to tree's `otick` anyway for when there's no parent. */ - if (node->parent == ITREE_NULL || node->parent->otick == otick) - node->otick = otick; -} - -/* Update limit of NODE and its ancestors. Stop when it becomes - stable, i.e. new_limit = old_limit. -*/ - -static void -interval_tree_propagate_limit (struct interval_node *node) -{ - if (node == ITREE_NULL) - return; - - while (1) { - ptrdiff_t newlimit = itree_newlimit (node); - if (newlimit == node->limit) - break; - node->limit = newlimit; - if (node->parent == ITREE_NULL) - break; - node = node->parent; - } -} - -/* Perform the familiar left-rotation on node NODE. */ - -static void -interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) -{ - eassert (node->right != ITREE_NULL); - - struct interval_node *right = node->right; - - interval_tree_inherit_offset (tree->otick, node); - interval_tree_inherit_offset (tree->otick, right); - - /* Turn right's left subtree into node's right subtree. */ - node->right = right->left; - if (right->left != ITREE_NULL) - right->left->parent = node; - - /* right's parent was node's parent. */ - if (right != ITREE_NULL) - right->parent = node->parent; - - /* Get the parent to point to right instead of node. */ - if (node != tree->root) - { - if (node == node->parent->left) - node->parent->left = right; - else - node->parent->right = right; - } - else - tree->root = right; - - /* Put node on right's left. */ - right->left = node; - if (node != ITREE_NULL) - node->parent = right; - - /* Order matters here. */ - interval_tree_update_limit (node); - interval_tree_update_limit (right); -} - -/* Perform the familiar right-rotation on node NODE. */ - -static void -interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) -{ - eassert (tree && node && node->left != ITREE_NULL); - - struct interval_node *left = node->left; - - interval_tree_inherit_offset (tree->otick, node); - interval_tree_inherit_offset (tree->otick, left); - - node->left = left->right; - if (left->right != ITREE_NULL) - left->right->parent = node; - - if (left != ITREE_NULL) - left->parent = node->parent; - if (node != tree->root) - { - if (node == node->parent->right) - node->parent->right = left; - else - node->parent->left = left; - } - else - tree->root = left; - - left->right = node; - if (node != ITREE_NULL) - node->parent = left; - - interval_tree_update_limit (left); - interval_tree_update_limit (node); -} - -/* Repair the tree after an insertion. - The new NODE was added as red, so we may have 2 reds in a row. - Rebalance the parents as needed to re-establish the RB invariants. */ - -static void -interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) -{ - eassert (tree->root->red == false); - - while (null_safe_is_red (node->parent)) - { - /* NODE is red and its parent is red. This is a violation of - red-black tree property #3. */ - eassert (node->red); - - if (node->parent == node->parent->parent->left) - { - /* We're on the left side of our grandparent, and OTHER is - our "uncle". */ - struct interval_node *uncle = node->parent->parent->right; - - if (null_safe_is_red (uncle)) /* case 1.a */ - { - /* Uncle and parent are red but should be black because - NODE is red. Change the colors accordingly and - proceed with the grandparent. */ - node->parent->red = false; - uncle->red = false; - node->parent->parent->red = true; - node = node->parent->parent; - } - else - { - /* Parent and uncle have different colors; parent is - red, uncle is black. */ - if (node == node->parent->right) /* case 2.a */ - { - node = node->parent; - interval_tree_rotate_left (tree, node); - } - /* case 3.a */ - node->parent->red = false; - node->parent->parent->red = true; - interval_tree_rotate_right (tree, node->parent->parent); - } - } - else - { - /* This is the symmetrical case of above. */ - struct interval_node *uncle = node->parent->parent->left; - - if (null_safe_is_red (uncle)) /* case 1.b */ - { - node->parent->red = false; - uncle->red = false; - node->parent->parent->red = true; - node = node->parent->parent; - } - else - { - if (node == node->parent->left) /* case 2.b */ - { - node = node->parent; - interval_tree_rotate_right (tree, node); - } - /* case 3.b */ - node->parent->red = false; - node->parent->parent->red = true; - interval_tree_rotate_left (tree, node->parent->parent); - } - } - } - - /* The root may have been changed to red due to the algorithm. - Set it to black so that property #5 is satisfied. */ - tree->root->red = false; - eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ -} - -/* Return accumulated offsets of NODE's parents. */ -static ptrdiff_t -itree_total_offset (struct interval_node *node) -{ - eassert (node != ITREE_NULL); - ptrdiff_t offset = 0; - while (node->parent != ITREE_NULL) - { - node = node->parent; - offset += node->offset; - } - return offset; -} - -/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts - *only* the parent linkage of SOURCE and either the parent's child - link the tree root. - - Warning: DEST is left unmodified. SOURCE's child links are - unchanged. Caller is responsible for recalculation of `limit`. - Requires both nodes to be using the same effective `offset`. */ - -static void -interval_tree_replace_child (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) -{ - eassert (tree && dest != ITREE_NULL); - eassert (source == ITREE_NULL - || itree_total_offset (source) == itree_total_offset (dest)); - - if (dest == tree->root) - tree->root = source; - else if (dest == dest->parent->left) - dest->parent->left = source; - else - dest->parent->right = source; - - if (source != ITREE_NULL) - source->parent = dest->parent; -} - -/* Replace DEST with SOURCE in the tree. Copies the following fields - from DEST to SOURCE: red, parent, left, right. Also updates - parent, left and right in surrounding nodes to point to SOURCE. - - Warning: DEST is left unmodified. Caller is responsible for - recalculation of `limit`. Requires both nodes to be using the same - effective `offset`. */ -static void -interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) -{ - interval_tree_replace_child (tree, source, dest); - source->left = dest->left; - if (source->left != ITREE_NULL) - source->left->parent = source; - source->right = dest->right; - if (source->right != ITREE_NULL) - source->right->parent = source; - source->red = dest->red; -} - - -/* +===================================================================================+ - * | Debugging - * +===================================================================================+ */ - -/* See Foverlay_tree in buffer.c */ From 1c44f6c83d88914cfee159ae021be33851e0112f Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 15 Oct 2022 20:04:12 -0700 Subject: [PATCH 61/70] ; * src/buffer.c (set_overlay_region): remove unused function. --- src/buffer.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index e1303f2a880..74c6705cbd3 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3535,15 +3535,6 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1); } -INLINE void -set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) -{ - eassert (ov->buffer); - begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); - end = clip_to_bounds (begin, end, ov->buffer->text->z); - interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); -} - DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. From 8159d8b1a71dd59e31060f00b2abe20ad9d1f924 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Wed, 19 Oct 2022 08:41:31 -0700 Subject: [PATCH 62/70] Remove the ITREE_NULL macro and use NULL everywhere. * src/itree.h: Delete the ITREE_NULL macro. * src/itree.c (check_subtree): Use NULL everywhere. * src/pdumper.c (dump_buffer): ditto. --- src/buffer.h | 2 +- src/itree.c | 158 +++++++++++++++++++++++++------------------------- src/itree.h | 3 - src/pdumper.c | 2 +- 4 files changed, 81 insertions(+), 84 deletions(-) diff --git a/src/buffer.h b/src/buffer.h index deb0367d990..afcdfcd9a02 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1275,7 +1275,7 @@ INLINE bool buffer_has_overlays (void) { return current_buffer->overlays - && (current_buffer->overlays->root != ITREE_NULL); + && (current_buffer->overlays->root != NULL); } /* Functions for accessing a character or byte, diff --git a/src/itree.c b/src/itree.c index aabf33fcb38..f7597ef86ad 100644 --- a/src/itree.c +++ b/src/itree.c @@ -208,7 +208,7 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { - eassert (node && node != ITREE_NULL); + eassert (node && node != NULL); /* FIXME: While the stack used in the iterator is bounded by the tree depth and could be easily pre-allocated to a large enough size to avoid @@ -308,12 +308,12 @@ check_subtree (struct interval_node *node, struct check_subtree_result result = { .size = 0, .limit = PTRDIFF_MIN, .black_height = 0 }; - if (node == ITREE_NULL) + if (node == NULL) return result; /* Validate structure. */ - eassert (node->left == ITREE_NULL || node->left->parent == node); - eassert (node->right == ITREE_NULL || node->right->parent == node); + eassert (node->left == NULL || node->left->parent == node); + eassert (node->right == NULL || node->right->parent == node); /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -323,7 +323,7 @@ check_subtree (struct interval_node *node, doesn't always update otick. It could, but it is not clear there is a need. */ eassert (node->otick <= tree_otick); - eassert (node->parent == ITREE_NULL || node->otick <= node->parent->otick); + eassert (node->parent == NULL || node->otick <= node->parent->otick); eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; @@ -350,7 +350,7 @@ check_subtree (struct interval_node *node, if (check_red_black_invariants) { eassert (left_result.black_height == right_result.black_height); - eassert (node->parent == ITREE_NULL || !node->red || !node->parent->red); + eassert (node->parent == NULL || !node->red || !node->parent->red); } result.size = 1 + left_result.size + right_result.size; @@ -374,10 +374,10 @@ check_tree (struct interval_tree *tree, { eassert (tree != NULL); eassert (tree->size >= 0); - eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - if (tree->root == ITREE_NULL) + eassert ((tree->size == 0) == (tree->root == NULL)); + if (tree->root == NULL) return true; - eassert (tree->root->parent == ITREE_NULL); + eassert (tree->root->parent == NULL); eassert (!check_red_black_invariants || !tree->root->red); struct interval_node *node = tree->root; @@ -398,24 +398,24 @@ check_tree (struct interval_tree *tree, static bool null_safe_is_red (struct interval_node *node) { - return node != ITREE_NULL && node->red; + return node != NULL && node->red; } static bool null_safe_is_black (struct interval_node *node) { - return node == ITREE_NULL || !node->red; /* NULL nodes are black */ + return node == NULL || !node->red; /* NULL nodes are black */ } static inline ptrdiff_t itree_newlimit (struct interval_node *node) { - eassert (node != ITREE_NULL); + eassert (node != NULL); return max (node->end, - max (node->left == ITREE_NULL + max (node->left == NULL ? PTRDIFF_MIN : node->left->limit + node->left->offset, - node->right == ITREE_NULL + node->right == NULL ? PTRDIFF_MIN : node->right->limit + node->right->offset)); } @@ -425,7 +425,7 @@ itree_newlimit (struct interval_node *node) static void interval_tree_update_limit (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return; node->limit = itree_newlimit (node); @@ -440,7 +440,7 @@ interval_tree_update_limit (struct interval_node *node) static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + eassert (node->parent == NULL || node->parent->otick >= node->otick); if (node->otick == otick) { eassert (node->offset == 0); @@ -458,16 +458,16 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != ITREE_NULL) + if (node->left != NULL) node->left->offset += node->offset; - if (node->right != ITREE_NULL) + if (node->right != NULL) node->right->offset += node->offset; node->offset = 0; } /* The only thing that matters about `otick` is whether it's equal to that of the tree. We could also "blindly" inherit from parent->otick, but we need to tree's `otick` anyway for when there's no parent. */ - if (node->parent == ITREE_NULL || node->parent->otick == otick) + if (node->parent == NULL || node->parent->otick == otick) node->otick = otick; } @@ -477,7 +477,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) static void interval_tree_propagate_limit (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return; while (1) { @@ -485,7 +485,7 @@ interval_tree_propagate_limit (struct interval_node *node) if (newlimit == node->limit) break; node->limit = newlimit; - if (node->parent == ITREE_NULL) + if (node->parent == NULL) break; node = node->parent; } @@ -495,7 +495,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == ITREE_NULL) + if (tree->otick == node->otick || node == NULL) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -515,9 +515,9 @@ interval_node_init (struct interval_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { - node->parent = ITREE_NULL; - node->left = ITREE_NULL; - node->right = ITREE_NULL; + node->parent = NULL; + node->left = NULL; + node->right = NULL; node->begin = -1; node->end = -1; node->front_advance = front_advance; @@ -565,7 +565,7 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - tree->root = ITREE_NULL; + tree->root = NULL; tree->otick = 1; tree->size = 0; } @@ -585,7 +585,7 @@ interval_tree_init (struct interval_tree *tree) void interval_tree_destroy (struct interval_tree *tree) { - eassert (tree->root == ITREE_NULL); + eassert (tree->root == NULL); /* if (tree->iter) * itree_iterator_destroy (tree->iter); */ xfree (tree); @@ -605,7 +605,7 @@ static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != ITREE_NULL); + eassert (node->right != NULL); struct interval_node *right = node->right; @@ -614,11 +614,11 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != ITREE_NULL) + if (right->left != NULL) right->left->parent = node; /* right's parent was node's parent. */ - if (right != ITREE_NULL) + if (right != NULL) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -634,7 +634,7 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Put node on right's left. */ right->left = node; - if (node != ITREE_NULL) + if (node != NULL) node->parent = right; /* Order matters here. */ @@ -648,7 +648,7 @@ static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != ITREE_NULL); + eassert (tree && node && node->left != NULL); struct interval_node *left = node->left; @@ -656,10 +656,10 @@ interval_tree_rotate_right (struct interval_tree *tree, interval_tree_inherit_offset (tree->otick, left); node->left = left->right; - if (left->right != ITREE_NULL) + if (left->right != NULL) left->right->parent = node; - if (left != ITREE_NULL) + if (left != NULL) left->parent = node->parent; if (node != tree->root) { @@ -672,7 +672,7 @@ interval_tree_rotate_right (struct interval_tree *tree, tree->root = left; left->right = node; - if (node != ITREE_NULL) + if (node != NULL) node->parent = left; interval_tree_update_limit (left); @@ -765,14 +765,14 @@ interval_tree_insert_fix (struct interval_tree *tree, static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node->begin <= node->end && node != ITREE_NULL); + eassert (node->begin <= node->end && node != NULL); /* FIXME: The assertion below fails because `delete_all_overlays` - doesn't set left/right/parent to ITREE_NULL. */ - /* eassert (node->left == ITREE_NULL && node->right == ITREE_NULL - && node->parent == ITREE_NULL) */; + doesn't set left/right/parent to NULL. */ + /* eassert (node->left == NULL && node->right == NULL + && node->parent == NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - struct interval_node *parent = ITREE_NULL; + struct interval_node *parent = NULL; struct interval_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, @@ -781,7 +781,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != ITREE_NULL) + while (child != NULL) { interval_tree_inherit_offset (otick, child); parent = child; @@ -793,7 +793,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == ITREE_NULL) + if (parent == NULL) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -802,11 +802,11 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = ITREE_NULL; - node->right = ITREE_NULL; + node->left = NULL; + node->right = NULL; node->offset = 0; node->limit = node->end; - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + eassert (node->parent == NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; @@ -848,7 +848,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -873,7 +873,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) static bool itree_limit_is_stable (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return true; ptrdiff_t newlimit = itree_newlimit (node); return (newlimit == node->limit); @@ -882,10 +882,10 @@ itree_limit_is_stable (struct interval_node *node) static struct interval_node* interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return node; while ((interval_tree_inherit_offset (otick, node), - node->left != ITREE_NULL)) + node->left != NULL)) node = node->left; return node; } @@ -899,12 +899,12 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node, struct interval_node *parent) { - if (parent == ITREE_NULL) + if (parent == NULL) eassert (node == tree->root); else - eassert (node == ITREE_NULL || node->parent == parent); + eassert (node == NULL || node->parent == parent); - while (parent != ITREE_NULL && null_safe_is_black (node)) + while (parent != NULL && null_safe_is_black (node)) { eassert (node == parent->left || node == parent->right); @@ -925,7 +925,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { other->red = true; node = parent; - eassert (node != ITREE_NULL); + eassert (node != NULL); parent = node->parent; } else @@ -942,7 +942,7 @@ interval_tree_remove_fix (struct interval_tree *tree, other->right->red = false; interval_tree_rotate_left (tree, parent); node = tree->root; - parent = ITREE_NULL; + parent = NULL; } } else @@ -962,7 +962,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { other->red = true; node = parent; - eassert (node != ITREE_NULL); + eassert (node != NULL); parent = node->parent; } else @@ -980,12 +980,12 @@ interval_tree_remove_fix (struct interval_tree *tree, other->left->red = false; interval_tree_rotate_right (tree, parent); node = tree->root; - parent = ITREE_NULL; + parent = NULL; } } } - if (node != ITREE_NULL) + if (node != NULL) node->red = false; } @@ -993,9 +993,9 @@ interval_tree_remove_fix (struct interval_tree *tree, static ptrdiff_t itree_total_offset (struct interval_node *node) { - eassert (node != ITREE_NULL); + eassert (node != NULL); ptrdiff_t offset = 0; - while (node->parent != ITREE_NULL) + while (node->parent != NULL) { node = node->parent; offset += node->offset; @@ -1015,8 +1015,8 @@ interval_tree_replace_child (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && dest != ITREE_NULL); - eassert (source == ITREE_NULL + eassert (tree && dest != NULL); + eassert (source == NULL || itree_total_offset (source) == itree_total_offset (dest)); if (dest == tree->root) @@ -1026,7 +1026,7 @@ interval_tree_replace_child (struct interval_tree *tree, else dest->parent->right = source; - if (source != ITREE_NULL) + if (source != NULL) source->parent = dest->parent; } /* Replace DEST with SOURCE in the tree. Copies the following fields @@ -1043,10 +1043,10 @@ interval_tree_transplant (struct interval_tree *tree, { interval_tree_replace_child (tree, source, dest); source->left = dest->left; - if (source->left != ITREE_NULL) + if (source->left != NULL) source->left->parent = source; source->right = dest->right; - if (source->right != ITREE_NULL) + if (source->right != NULL) source->right->parent = source; source->red = dest->red; } @@ -1064,16 +1064,16 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); struct interval_node *splice - = (node->left == ITREE_NULL || node->right == ITREE_NULL) + = (node->left == NULL || node->right == NULL) ? node : interval_tree_subtree_min (tree->otick, node->right); /* Find `subtree`, the only child of `splice` (may be NULL). Note: `subtree` will not be modified other than changing its parent to `splice`. */ - eassert (splice->left == ITREE_NULL || splice->right == ITREE_NULL); + eassert (splice->left == NULL || splice->right == NULL); struct interval_node *subtree - = (splice->left != ITREE_NULL) ? splice->left : splice->right; + = (splice->left != NULL) ? splice->left : splice->right; /* Save a pointer to the parent of where `subtree` will eventually be in `subtree_parent`. */ @@ -1109,7 +1109,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (removed_black) interval_tree_remove_fix (tree, subtree, subtree_parent); - eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + eassert ((tree->size == 0) == (tree->root == NULL)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ /* Clear fields related to the tree for sanity while debugging. */ @@ -1152,7 +1152,7 @@ itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, iter->otick = tree->otick; iter->order = order; interval_stack_clear (iter->stack); - if (begin <= end && tree->root != ITREE_NULL) + if (begin <= end && tree->root != NULL) interval_stack_push_flagged (iter->stack, tree->root, false); iter->file = file; iter->line = line; @@ -1184,7 +1184,7 @@ void interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->root == ITREE_NULL) + if (length <= 0 || tree->root == NULL) return; uintmax_t ootick = tree->otick; @@ -1205,7 +1205,7 @@ interval_tree_insert_gap (struct interval_tree *tree, /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ - if (tree->root != ITREE_NULL) + if (tree->root != NULL) { const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); @@ -1216,7 +1216,7 @@ interval_tree_insert_gap (struct interval_tree *tree, { /* Process in pre-order. */ interval_tree_inherit_offset (tree->otick, node); - if (node->right != ITREE_NULL) + if (node->right != NULL) { if (node->begin > pos) { @@ -1227,7 +1227,7 @@ interval_tree_insert_gap (struct interval_tree *tree, else interval_stack_push (stack, node->right); } - if (node->left != ITREE_NULL + if (node->left != NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -1237,7 +1237,7 @@ interval_tree_insert_gap (struct interval_tree *tree, if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -1268,7 +1268,7 @@ void interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->root == ITREE_NULL) + if (length <= 0 || tree->root == NULL) return; /* FIXME: Don't allocate stack anew every time. */ @@ -1285,7 +1285,7 @@ interval_tree_delete_gap (struct interval_tree *tree, { node = nav_nodeptr (nav); interval_tree_inherit_offset (tree->otick, node); - if (node->right != ITREE_NULL) + if (node->right != NULL) { if (node->begin > pos + length) { @@ -1296,7 +1296,7 @@ interval_tree_delete_gap (struct interval_tree *tree, else interval_stack_push (stack, node->right); } - if (node->left != ITREE_NULL + if (node->left != NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -1305,7 +1305,7 @@ interval_tree_delete_gap (struct interval_tree *tree, if (node->end > pos) { node->end = max (pos , node->end - length); - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -1342,7 +1342,7 @@ itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - struct interval_node * const null = ITREE_NULL; + struct interval_node * const null = NULL; struct interval_node *node; /* The `visited` flag stored in each node is used here (and only here): diff --git a/src/itree.h b/src/itree.h index f98f028ea52..8d33ef223b5 100644 --- a/src/itree.h +++ b/src/itree.h @@ -95,9 +95,6 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -/* FIXME: replace ITREE_NULL -> NULL everywhere */ -#define ITREE_NULL NULL - struct interval_tree { struct interval_node *root; diff --git a/src/pdumper.c b/src/pdumper.c index e39f5f1109f..1a2ecea71e6 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2863,7 +2863,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); - if (buffer->overlays && buffer->overlays->root != ITREE_NULL) + if (buffer->overlays && buffer->overlays->root != NULL) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else From 06252617b2c4cc9bcdd9407f1e709a7e0908cf27 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Wed, 19 Oct 2022 13:42:35 -0700 Subject: [PATCH 63/70] Revert "mark_overlays: Use the normal ITREE_FOREACH" This reverts commit b8fbd42f0a7caa4cd9e2d50dd4e4b2101ac78acd, with edits. * src/alloc.c (mark_overlays): restore function. (mark_buffer): Call it, not ITREE_FOREACH. (garbage_collect): eassert (!itree_busy_p ()). * src/itree.h: Comment tweak: explain why GC is considered risky. It isn't that GC itself is risky, it is that GC can call ELisp by way of a hook, and running ELisp during iteration is risks nested iteration. --- src/alloc.c | 22 +++++++++++++++++++--- src/itree.h | 3 ++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 00f2991f250..d7e0a99ffe7 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6279,6 +6279,11 @@ garbage_collect (void) image_prune_animation_caches (false); #endif + /* ELisp code run by `gc-post-hook' could result in itree iteration, + which must not happen while the itree is already busy. See + bug#58639. */ + eassert (!itree_iterator_busy_p ()); + if (!NILP (Vpost_gc_hook)) { specpdl_ref gc_count = inhibit_garbage_collection (); @@ -6510,6 +6515,18 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } +/* Mark the overlay subtree rooted at NODE. */ + +static void +mark_overlays (struct interval_node *node) +{ + if (node == NULL) + return; + mark_object (node->data); + mark_overlays (node->left); + mark_overlays (node->right); +} + /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6531,9 +6548,8 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - struct interval_node *node; - ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) - mark_object (node->data); + if (buffer->overlays) + mark_overlays (buffer->overlays->root); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && diff --git a/src/itree.h b/src/itree.h index 8d33ef223b5..b0f7a1d193b 100644 --- a/src/itree.h +++ b/src/itree.h @@ -149,7 +149,8 @@ struct interval_node *itree_iterator_next (struct itree_iterator *); it is cheap a pure. - Only a single iteration can happen at a time, so make sure none of the code within the loop can start another tree iteration, i.e. it shouldn't - be able to run ELisp code (or GC for that matter). + be able to run ELisp code, nor GC since GC can run ELisp by way + of `post-gc-hook`. - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - Non-local exits are not supported within the body of the loop. From f421b58db5d34f45773a73c699b4b1a5a5b9da03 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Wed, 19 Oct 2022 15:56:07 -0700 Subject: [PATCH 64/70] Prefix all itree.h type names with itree_ Rename interval_node -> itree_node, interval_tree -> itree_tree, interval_tree_order -> itree_order. * src/alloc.c: Renames. * src/buffer.c: ditto. * src/itree.c: ditto. * src/itree.h: ditto. * src/lisp.h: ditto. * src/pdumper.h: ditto. * src/textprop.h: ditto. * src/xdisp.h: ditto. --- src/alloc.c | 4 +- src/buffer.c | 40 ++++++------- src/buffer.h | 2 +- src/itree.c | 160 ++++++++++++++++++++++++------------------------- src/itree.h | 47 +++++++-------- src/lisp.h | 2 +- src/pdumper.c | 10 ++-- src/textprop.c | 2 +- src/xdisp.c | 4 +- 9 files changed, 135 insertions(+), 136 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index d7e0a99ffe7..a08249b1b11 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3711,7 +3711,7 @@ build_overlay (bool front_advance, bool rear_advance, struct Lisp_Overlay *p = ALLOCATE_PSEUDOVECTOR (struct Lisp_Overlay, plist, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); - struct interval_node *node = xmalloc (sizeof (*node)); + struct itree_node *node = xmalloc (sizeof (*node)); interval_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; @@ -6518,7 +6518,7 @@ mark_overlay (struct Lisp_Overlay *ov) /* Mark the overlay subtree rooted at NODE. */ static void -mark_overlays (struct interval_node *node) +mark_overlays (struct itree_node *node) { if (node == NULL) return; diff --git a/src/buffer.c b/src/buffer.c index 74c6705cbd3..228c6e60560 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -655,7 +655,7 @@ static void copy_overlays (struct buffer *from, struct buffer *to) { eassert (to && ! to->overlays); - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { @@ -932,13 +932,13 @@ drop_overlay (struct Lisp_Overlay *ov) void delete_all_overlays (struct buffer *b) { - struct interval_node *node; + struct itree_node *node; if (! b->overlays) return; /* FIXME: This loop sets the overlays' `buffer` field to NULL but - doesn't set the interval_nodes' `parent`, `left` and `right` + doesn't set the itree_nodes' `parent`, `left` and `right` fields accordingly. I believe it's harmless, but a bit untidy since other parts of the code are careful to set those fields to NULL when the overlay is deleted. @@ -978,8 +978,8 @@ set_overlays_multibyte (bool multibyte) if (! current_buffer->overlays || Z == Z_BYTE) return; - struct interval_node **nodes = NULL; - struct interval_tree *tree = current_buffer->overlays; + struct itree_node **nodes = NULL; + struct itree_tree *tree = current_buffer->overlays; const intmax_t size = interval_tree_size (tree); /* We can't use `interval_node_set_region` at the same time @@ -988,14 +988,14 @@ set_overlays_multibyte (bool multibyte) USE_SAFE_ALLOCA; SAFE_NALLOCA (nodes, 1, size); { - struct interval_node *node, **cursor = nodes; + struct itree_node *node, **cursor = nodes; ITREE_FOREACH (node, tree, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) *(cursor++) = node; } for (int i = 0; i < size; ++i, ++nodes) { - struct interval_node * const node = *nodes; + struct itree_node * const node = *nodes; if (multibyte) { @@ -2436,7 +2436,7 @@ advance_to_char_boundary (ptrdiff_t byte_pos) static void swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = other; @@ -2965,7 +2965,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ptrdiff_t len = *len_ptr; ptrdiff_t next = ZV; Lisp_Object *vec = *vec_ptr; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, beg, /* Find empty OV at Z ? */ @@ -3026,7 +3026,7 @@ ptrdiff_t next_overlay_change (ptrdiff_t pos) { ptrdiff_t next = ZV; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, pos, next, ASCENDING) { @@ -3052,7 +3052,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t previous_overlay_change (ptrdiff_t pos) { - struct interval_node *node; + struct itree_node *node; ptrdiff_t prev = BEGV; ITREE_FOREACH (node, current_buffer->overlays, prev, pos, DESCENDING) @@ -3135,7 +3135,7 @@ disable_line_numbers_overlay_at_eob (void) bool overlay_touches_p (ptrdiff_t pos) { - struct interval_node *node; + struct itree_node *node; /* We need to find overlays ending in pos, as well as empty ones at pos. */ @@ -3347,7 +3347,7 @@ ptrdiff_t overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) { bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters)); - struct interval_node *node; + struct itree_node *node; overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; @@ -3848,7 +3848,7 @@ However, the overlays you get are the real objects that the buffer uses. */) (void) { Lisp_Object overlays = Qnil; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, BEG, Z, DESCENDING) overlays = Fcons (node->data, overlays); @@ -3980,7 +3980,7 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (!after) { - struct interval_node *node; + struct itree_node *node; EMACS_INT begin_arg = XFIXNUM (start); EMACS_INT end_arg = XFIXNUM (end); /* We are being called before a change. @@ -4072,7 +4072,7 @@ void evaporate_overlays (ptrdiff_t pos) { Lisp_Object hit_list = Qnil; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, pos, pos, ASCENDING) { @@ -4913,7 +4913,7 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, #ifdef ITREE_DEBUG static Lisp_Object -make_lispy_interval_node (const struct interval_node *node) +make_lispy_itree_node (const struct itree_node *node) { return listn (12, intern (":begin"), @@ -4931,12 +4931,12 @@ make_lispy_interval_node (const struct interval_node *node) } static Lisp_Object -overlay_tree (const struct interval_tree *tree, - const struct interval_node *node) +overlay_tree (const struct itree_tree *tree, + const struct itree_node *node) { if (node == ITREE_NULL) return Qnil; - return list3 (make_lispy_interval_node (node), + return list3 (make_lispy_itree_node (node), overlay_tree (tree, node->left), overlay_tree (tree, node->right)); } diff --git a/src/buffer.h b/src/buffer.h index afcdfcd9a02..ee0c8dd8361 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -698,7 +698,7 @@ struct buffer bool_bf long_line_optimizations_p : 1; /* The inveral tree containing this buffer's overlays. */ - struct interval_tree *overlays; + struct itree_tree *overlays; /* Changes in the buffer are recorded here for undo, and t means don't record anything. This information belongs to the base diff --git a/src/itree.c b/src/itree.c index f7597ef86ad..6d54a36c3bb 100644 --- a/src/itree.c +++ b/src/itree.c @@ -135,7 +135,7 @@ along with GNU Emacs. If not, see . */ typedef uintptr_t nodeptr_and_flag; static inline nodeptr_and_flag -make_nav (struct interval_node *ptr, bool flag) +make_nav (struct itree_node *ptr, bool flag) { uintptr_t v = (uintptr_t) ptr; /* We assume alignment imposes the LSB is clear for us to use it. */ @@ -143,10 +143,10 @@ make_nav (struct interval_node *ptr, bool flag) return v | !!flag; } -static inline struct interval_node * +static inline struct itree_node * nav_nodeptr (nodeptr_and_flag nav) { - return (struct interval_node *) (nav & (~(uintptr_t)1)); + return (struct itree_node *) (nav & (~(uintptr_t)1)); } static inline bool @@ -170,7 +170,7 @@ interval_stack_create (intmax_t initial_size) { struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->nodes = xmalloc (stack->size * sizeof (struct itree_node*)); stack->length = 0; return stack; } @@ -206,7 +206,7 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) static inline void interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) + struct itree_node *node, bool flag) { eassert (node && node != NULL); @@ -223,7 +223,7 @@ interval_stack_push_flagged (struct interval_stack *stack, } static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) +interval_stack_push (struct interval_stack *stack, struct itree_node *node) { interval_stack_push_flagged (stack, node, false); } @@ -246,7 +246,7 @@ struct itree_iterator ptrdiff_t begin; ptrdiff_t end; uintmax_t otick; /* A copy of the tree's `otick`. */ - enum interval_tree_order order; + enum itree_order order; bool running; const char* file; int line; @@ -260,7 +260,7 @@ struct itree_iterator static struct itree_iterator *iter; static int -interval_tree_max_height (const struct interval_tree *tree) +interval_tree_max_height (const struct itree_tree *tree) { return 2 * log (tree->size + 1) / log (2) + 0.5; } @@ -268,7 +268,7 @@ interval_tree_max_height (const struct interval_tree *tree) /* Allocate a new iterator for TREE. */ static struct itree_iterator * -itree_iterator_create (struct interval_tree *tree) +itree_iterator_create (struct itree_tree *tree) { struct itree_iterator *g = xmalloc (sizeof *g); /* 19 here just avoids starting with a silly-small stack. @@ -300,7 +300,7 @@ struct check_subtree_result }; static struct check_subtree_result -check_subtree (struct interval_node *node, +check_subtree (struct itree_node *node, bool check_red_black_invariants, uintmax_t tree_otick, ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) @@ -369,7 +369,7 @@ check_subtree (struct interval_node *node, entire tree and validates all invariants. */ static bool -check_tree (struct interval_tree *tree, +check_tree (struct itree_tree *tree, bool check_red_black_invariants) { eassert (tree != NULL); @@ -380,7 +380,7 @@ check_tree (struct interval_tree *tree, eassert (tree->root->parent == NULL); eassert (!check_red_black_invariants || !tree->root->red); - struct interval_node *node = tree->root; + struct itree_node *node = tree->root; struct check_subtree_result result = check_subtree (node, check_red_black_invariants, tree->otick, node->offset, PTRDIFF_MIN, @@ -396,19 +396,19 @@ check_tree (struct interval_tree *tree, * +=======================================================================+ */ static bool -null_safe_is_red (struct interval_node *node) +null_safe_is_red (struct itree_node *node) { return node != NULL && node->red; } static bool -null_safe_is_black (struct interval_node *node) +null_safe_is_black (struct itree_node *node) { return node == NULL || !node->red; /* NULL nodes are black */ } static inline ptrdiff_t -itree_newlimit (struct interval_node *node) +itree_newlimit (struct itree_node *node) { eassert (node != NULL); return max (node->end, @@ -423,7 +423,7 @@ itree_newlimit (struct interval_node *node) /* Update NODE's limit attribute according to its children. */ static void -interval_tree_update_limit (struct interval_node *node) +interval_tree_update_limit (struct itree_node *node) { if (node == NULL) return; @@ -438,7 +438,7 @@ interval_tree_update_limit (struct interval_node *node) */ static void -interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) +interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node) { eassert (node->parent == NULL || node->parent->otick >= node->otick); if (node->otick == otick) @@ -475,7 +475,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) stable, i.e. new_limit = old_limit. */ static void -interval_tree_propagate_limit (struct interval_node *node) +interval_tree_propagate_limit (struct itree_node *node) { if (node == NULL) return; @@ -491,8 +491,8 @@ interval_tree_propagate_limit (struct interval_node *node) } } -static struct interval_node* -interval_tree_validate (struct interval_tree *tree, struct interval_node *node) +static struct itree_node* +interval_tree_validate (struct itree_tree *tree, struct itree_node *node) { if (tree->otick == node->otick || node == NULL) @@ -511,7 +511,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) /* Initialize an allocated node. */ void -interval_node_init (struct interval_node *node, +interval_node_init (struct itree_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { @@ -528,8 +528,8 @@ interval_node_init (struct interval_node *node, /* Return NODE's begin value, computing it if necessary. */ ptrdiff_t -interval_node_begin (struct interval_tree *tree, - struct interval_node *node) +interval_node_begin (struct itree_tree *tree, + struct itree_node *node) { interval_tree_validate (tree, node); return node->begin; @@ -538,8 +538,8 @@ interval_node_begin (struct interval_tree *tree, /* Return NODE's end value, computing it if necessary. */ ptrdiff_t -interval_node_end (struct interval_tree *tree, - struct interval_node *node) +interval_node_end (struct itree_tree *tree, + struct itree_node *node) { interval_tree_validate (tree, node); return node->end; @@ -547,7 +547,7 @@ interval_node_end (struct interval_tree *tree, /* Allocate an interval_tree. Free with interval_tree_destroy. */ -struct interval_tree* +struct itree_tree* interval_tree_create (void) { /* FIXME? Maybe avoid the initialization of itree_null in the same @@ -555,7 +555,7 @@ interval_tree_create (void) important though. */ itree_init (); - struct interval_tree *tree = xmalloc (sizeof (*tree)); + struct itree_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); return tree; } @@ -563,7 +563,7 @@ interval_tree_create (void) /* Reset the tree TREE to its empty state. */ void -interval_tree_clear (struct interval_tree *tree) +interval_tree_clear (struct itree_tree *tree) { tree->root = NULL; tree->otick = 1; @@ -583,7 +583,7 @@ interval_tree_init (struct interval_tree *tree) /* Release a tree, freeing its allocated memory. */ void -interval_tree_destroy (struct interval_tree *tree) +interval_tree_destroy (struct itree_tree *tree) { eassert (tree->root == NULL); /* if (tree->iter) @@ -594,7 +594,7 @@ interval_tree_destroy (struct interval_tree *tree) /* Return the number of nodes in TREE. */ intmax_t -interval_tree_size (struct interval_tree *tree) +interval_tree_size (struct itree_tree *tree) { return tree->size; } @@ -602,12 +602,12 @@ interval_tree_size (struct interval_tree *tree) /* Perform the familiar left-rotation on node NODE. */ static void -interval_tree_rotate_left (struct interval_tree *tree, - struct interval_node *node) +interval_tree_rotate_left (struct itree_tree *tree, + struct itree_node *node) { eassert (node->right != NULL); - struct interval_node *right = node->right; + struct itree_node *right = node->right; interval_tree_inherit_offset (tree->otick, node); interval_tree_inherit_offset (tree->otick, right); @@ -645,12 +645,12 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Perform the familiar right-rotation on node NODE. */ static void -interval_tree_rotate_right (struct interval_tree *tree, - struct interval_node *node) +interval_tree_rotate_right (struct itree_tree *tree, + struct itree_node *node) { eassert (tree && node && node->left != NULL); - struct interval_node *left = node->left; + struct itree_node *left = node->left; interval_tree_inherit_offset (tree->otick, node); interval_tree_inherit_offset (tree->otick, left); @@ -684,8 +684,8 @@ interval_tree_rotate_right (struct interval_tree *tree, Rebalance the parents as needed to re-establish the RB invariants. */ static void -interval_tree_insert_fix (struct interval_tree *tree, - struct interval_node *node) +interval_tree_insert_fix (struct itree_tree *tree, + struct itree_node *node) { eassert (tree->root->red == false); @@ -699,7 +699,7 @@ interval_tree_insert_fix (struct interval_tree *tree, { /* We're on the left side of our grandparent, and OTHER is our "uncle". */ - struct interval_node *uncle = node->parent->parent->right; + struct itree_node *uncle = node->parent->parent->right; if (null_safe_is_red (uncle)) /* case 1.a */ { @@ -729,7 +729,7 @@ interval_tree_insert_fix (struct interval_tree *tree, else { /* This is the symmetrical case of above. */ - struct interval_node *uncle = node->parent->parent->left; + struct itree_node *uncle = node->parent->parent->left; if (null_safe_is_red (uncle)) /* case 1.b */ { @@ -763,7 +763,7 @@ interval_tree_insert_fix (struct interval_tree *tree, Note, that inserting a node twice results in undefined behaviour. */ static void -interval_tree_insert (struct interval_tree *tree, struct interval_node *node) +interval_tree_insert (struct itree_tree *tree, struct itree_node *node) { eassert (node->begin <= node->end && node != NULL); /* FIXME: The assertion below fails because `delete_all_overlays` @@ -772,8 +772,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) && node->parent == NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - struct interval_node *parent = NULL; - struct interval_node *child = tree->root; + struct itree_node *parent = NULL; + struct itree_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, to "confirm" that the begin/end fields are up to date. */ @@ -821,7 +821,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } void -itree_insert_node (struct interval_tree *tree, struct interval_node *node, +itree_insert_node (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { node->begin = begin; @@ -833,8 +833,8 @@ itree_insert_node (struct interval_tree *tree, struct interval_node *node, /* Safely modify a node's interval. */ void -interval_node_set_region (struct interval_tree *tree, - struct interval_node *node, +interval_node_set_region (struct itree_tree *tree, + struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { interval_tree_validate (tree, node); @@ -856,10 +856,10 @@ interval_node_set_region (struct interval_tree *tree, /* Return true, if NODE is a member of TREE. */ static bool -interval_tree_contains (struct interval_tree *tree, struct interval_node *node) +interval_tree_contains (struct itree_tree *tree, struct itree_node *node) { eassert (node); - struct interval_node *other; + struct itree_node *other; ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) if (other == node) { @@ -871,7 +871,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) } static bool -itree_limit_is_stable (struct interval_node *node) +itree_limit_is_stable (struct itree_node *node) { if (node == NULL) return true; @@ -879,8 +879,8 @@ itree_limit_is_stable (struct interval_node *node) return (newlimit == node->limit); } -static struct interval_node* -interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) +static struct itree_node* +interval_tree_subtree_min (uintmax_t otick, struct itree_node *node) { if (node == NULL) return node; @@ -895,9 +895,9 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) so re-balance the parents to re-establish the RB invariants. */ static void -interval_tree_remove_fix (struct interval_tree *tree, - struct interval_node *node, - struct interval_node *parent) +interval_tree_remove_fix (struct itree_tree *tree, + struct itree_node *node, + struct itree_node *parent) { if (parent == NULL) eassert (node == tree->root); @@ -910,7 +910,7 @@ interval_tree_remove_fix (struct interval_tree *tree, if (node == parent->left) { - struct interval_node *other = parent->right; + struct itree_node *other = parent->right; if (null_safe_is_red (other)) /* case 1.a */ { @@ -947,7 +947,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - struct interval_node *other = parent->left; + struct itree_node *other = parent->left; if (null_safe_is_red (other)) /* 1.b */ { @@ -991,7 +991,7 @@ interval_tree_remove_fix (struct interval_tree *tree, /* Return accumulated offsets of NODE's parents. */ static ptrdiff_t -itree_total_offset (struct interval_node *node) +itree_total_offset (struct itree_node *node) { eassert (node != NULL); ptrdiff_t offset = 0; @@ -1011,9 +1011,9 @@ itree_total_offset (struct interval_node *node) unchanged. Caller is responsible for recalculation of `limit`. Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_replace_child (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) +interval_tree_replace_child (struct itree_tree *tree, + struct itree_node *source, + struct itree_node *dest) { eassert (tree && dest != NULL); eassert (source == NULL @@ -1037,9 +1037,9 @@ interval_tree_replace_child (struct interval_tree *tree, recalculation of `limit`. Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) +interval_tree_transplant (struct itree_tree *tree, + struct itree_node *source, + struct itree_node *dest) { interval_tree_replace_child (tree, source, dest); source->left = dest->left; @@ -1053,8 +1053,8 @@ interval_tree_transplant (struct interval_tree *tree, /* Remove NODE from TREE and return it. NODE must exist in TREE. */ -struct interval_node* -interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +struct itree_node* +interval_tree_remove (struct itree_tree *tree, struct itree_node *node) { eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ @@ -1063,7 +1063,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) `node` has at most one child this is `node` itself. Otherwise, it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); - struct interval_node *splice + struct itree_node *splice = (node->left == NULL || node->right == NULL) ? node : interval_tree_subtree_min (tree->otick, node->right); @@ -1072,12 +1072,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) `subtree` will not be modified other than changing its parent to `splice`. */ eassert (splice->left == NULL || splice->right == NULL); - struct interval_node *subtree + struct itree_node *subtree = (splice->left != NULL) ? splice->left : splice->right; /* Save a pointer to the parent of where `subtree` will eventually be in `subtree_parent`. */ - struct interval_node *subtree_parent + struct itree_node *subtree_parent = (splice->parent != node) ? splice->parent : splice; /* If `splice` is black removing it may violate Red-Black @@ -1135,8 +1135,8 @@ itree_iterator_busy_p (void) given ORDER. Only one iterator per tree can be running at any time. */ struct itree_iterator * -itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum interval_tree_order order, +itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum itree_order order, const char *file, int line) { /* struct itree_iterator *iter = tree->iter; */ @@ -1181,7 +1181,7 @@ itree_iterator_finish (struct itree_iterator *iter) front_advance setting. */ void -interval_tree_insert_gap (struct interval_tree *tree, +interval_tree_insert_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1193,7 +1193,7 @@ interval_tree_insert_gap (struct interval_tree *tree, /* Nodes with front_advance starting at pos may mess up the tree order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); - struct interval_node *node = NULL; + struct itree_node *node = NULL; ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER) { if (node->begin == pos && node->front_advance @@ -1265,7 +1265,7 @@ interval_tree_insert_gap (struct interval_tree *tree, intersecting it. */ void -interval_tree_delete_gap (struct interval_tree *tree, +interval_tree_delete_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1277,7 +1277,7 @@ interval_tree_delete_gap (struct interval_tree *tree, might unintentionally bring shifted nodes back into our search space. */ const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); - struct interval_node *node; + struct itree_node *node; interval_stack_push (stack, tree->root); nodeptr_and_flag nav; @@ -1327,7 +1327,7 @@ interval_tree_delete_gap (struct interval_tree *tree, a NODE2 strictly bigger than NODE1 should also be included). */ static inline bool -interval_node_intersects (const struct interval_node *node, +interval_node_intersects (const struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { return (begin < node->end && node->begin < end) @@ -1337,13 +1337,13 @@ interval_node_intersects (const struct interval_node *node, /* Return the next node of the iterator in the order given when it was started; or NULL if there are no more nodes. */ -struct interval_node * +struct itree_node * itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - struct interval_node * const null = NULL; - struct interval_node *node; + struct itree_node * const null = NULL; + struct itree_node *node; /* The `visited` flag stored in each node is used here (and only here): We keep a "workstack" of nodes we need to consider. This stack @@ -1363,8 +1363,8 @@ itree_iterator_next (struct itree_iterator *g) visited = nav_flag (nav), node && !visited)) { - struct interval_node * const left = node->left; - struct interval_node * const right = node->right; + struct itree_node * const left = node->left; + struct itree_node * const right = node->right; interval_tree_inherit_offset (g->otick, node); eassert (itree_limit_is_stable (node)); diff --git a/src/itree.h b/src/itree.h index b0f7a1d193b..b4116a1fb75 100644 --- a/src/itree.h +++ b/src/itree.h @@ -36,15 +36,14 @@ along with GNU Emacs. If not, see . */ returns as a side-effect. See ITREE_FOREACH. */ -struct interval_node; -struct interval_node +struct itree_node { /* The normal parent, left and right links found in binary trees. See also `red`, below, which completes the Red-Black tree representation. */ - struct interval_node *parent; - struct interval_node *left; - struct interval_node *right; + struct itree_node *parent; + struct itree_node *left; + struct itree_node *right; /* The following five fields comprise the interval abstraction. @@ -63,7 +62,7 @@ struct interval_node OTICK determines whether BEGIN, END, LIMIT and OFFSET are considered dirty. A node is clean when its OTICK is equal to the - OTICK of its tree (see struct interval_tree). Otherwise, it is + OTICK of its tree (see struct itree_tree). Otherwise, it is dirty. In a clean node, BEGIN, END and LIMIT are correct buffer @@ -95,44 +94,44 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -struct interval_tree +struct itree_tree { - struct interval_node *root; + struct itree_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ }; -enum interval_tree_order { +enum itree_order { ITREE_ASCENDING, ITREE_DESCENDING, ITREE_PRE_ORDER, }; -void interval_node_init (struct interval_node *, bool, bool, Lisp_Object); -ptrdiff_t interval_node_begin (struct interval_tree *, struct interval_node *); -ptrdiff_t interval_node_end (struct interval_tree *, struct interval_node *); -void interval_node_set_region (struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); -struct interval_tree *interval_tree_create (void); -void interval_tree_destroy (struct interval_tree *); -intmax_t interval_tree_size (struct interval_tree *); -void interval_tree_clear (struct interval_tree *); -void itree_insert_node (struct interval_tree *tree, struct interval_node *node, +void interval_node_init (struct itree_node *, bool, bool, Lisp_Object); +ptrdiff_t interval_node_begin (struct itree_tree *, struct itree_node *); +ptrdiff_t interval_node_end (struct itree_tree *, struct itree_node *); +void interval_node_set_region (struct itree_tree *, struct itree_node *, ptrdiff_t, ptrdiff_t); +struct itree_tree *interval_tree_create (void); +void interval_tree_destroy (struct itree_tree *); +intmax_t interval_tree_size (struct itree_tree *); +void interval_tree_clear (struct itree_tree *); +void itree_insert_node (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end); -struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); +struct itree_node *interval_tree_remove (struct itree_tree *, struct itree_node *); +void interval_tree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); /* Iteration functions. Almost all code should use ITREE_FOREACH instead. */ bool itree_iterator_busy_p (void); struct itree_iterator * -itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum interval_tree_order order, +itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum itree_order order, const char *file, int line); void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, ptrdiff_t); void itree_iterator_finish (struct itree_iterator *); -struct interval_node *itree_iterator_next (struct itree_iterator *); +struct itree_node *itree_iterator_next (struct itree_iterator *); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, diff --git a/src/lisp.h b/src/lisp.h index 7cd78712814..b256d65efcd 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2604,7 +2604,7 @@ struct Lisp_Overlay union vectorlike_header header; Lisp_Object plist; struct buffer *buffer; /* eassert (live buffer || NULL). */ - struct interval_node *interval; + struct itree_node *interval; } GCALIGNED_STRUCT; struct Lisp_Misc_Ptr diff --git a/src/pdumper.c b/src/pdumper.c index 1a2ecea71e6..1830c6bd18b 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2134,13 +2134,13 @@ dump_marker (struct dump_context *ctx, const struct Lisp_Marker *marker) } static dump_off -dump_interval_node (struct dump_context *ctx, struct interval_node *node, +dump_interval_node (struct dump_context *ctx, struct itree_node *node, dump_off parent_offset) { #if CHECK_STRUCTS && !defined (HASH_interval_node_5765524F7E) # error "interval_node changed. See CHECK_STRUCTS comment in config.h." #endif - struct interval_node out; + struct itree_node out; dump_object_start (ctx, &out, sizeof (out)); if (node->parent) dump_field_fixup_later (ctx, &out, node, &node->parent); @@ -2161,17 +2161,17 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, if (node->parent) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, parent), + offset + dump_offsetof (struct itree_node, parent), dump_interval_node (ctx, node->parent, offset)); if (node->left) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, left), + offset + dump_offsetof (struct itree_node, left), dump_interval_node (ctx, node->left, offset)); if (node->right) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, right), + offset + dump_offsetof (struct itree_node, right), dump_interval_node (ctx, node->right, offset)); return offset; } diff --git a/src/textprop.c b/src/textprop.c index b34246f5bc7..45ead993a60 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -634,7 +634,7 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, if (BUFFERP (object)) { struct buffer *b = XBUFFER (object); - struct interval_node *node; + struct itree_node *node; struct sortvec items[2]; struct sortvec *result = NULL; Lisp_Object result_tem = Qnil; diff --git a/src/xdisp.c b/src/xdisp.c index d585d57fd05..ca7e2820f42 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -6534,7 +6534,7 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) struct overlay_entry entriesbuf[20]; ptrdiff_t size = ARRAYELTS (entriesbuf); struct overlay_entry *entries = entriesbuf; - struct interval_node *node; + struct itree_node *node; USE_SAFE_ALLOCA; @@ -7001,7 +7001,7 @@ back_to_previous_line_start (struct it *it) static bool strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) { - struct interval_node *node; + struct itree_node *node; /* Process overlays. */ ITREE_FOREACH (node, current_buffer->overlays, startpos, endpos, DESCENDING) { From 37a1145410f7d61883ea689255ee7e564c2fb3d0 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Wed, 19 Oct 2022 16:16:09 -0700 Subject: [PATCH 65/70] Rename all exported itree.h functions with the itree_ prefix For the most part, I replaced the interval_tree_ prefix with itree_, interval_node_ with itree_node_, etc. * src/alloc.c: Rename everywhere as appropriate. * src/alloc.c: ditto. * src/buffer.c: ditto. * src/buffer.h: ditto. * src/itree.c: ditto. * src/itree.h: ditto. --- src/alloc.c | 2 +- src/buffer.c | 26 +++++++++++++------------- src/buffer.h | 4 ++-- src/itree.c | 30 +++++++++++++++--------------- src/itree.h | 33 +++++++++++++++++---------------- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index a08249b1b11..f69c65dedc1 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3712,7 +3712,7 @@ build_overlay (bool front_advance, bool rear_advance, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); struct itree_node *node = xmalloc (sizeof (*node)); - interval_node_init (node, front_advance, rear_advance, overlay); + itree_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; set_overlay_plist (overlay, plist); diff --git a/src/buffer.c b/src/buffer.c index 228c6e60560..3286cd338ff 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -644,9 +644,9 @@ add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov, { eassert (! ov->buffer); if (! b->overlays) - b->overlays = interval_tree_create (); + b->overlays = itree_create (); ov->buffer = b; - itree_insert_node (b->overlays, ov->interval, begin, end); + itree_insert (b->overlays, ov->interval, begin, end); } /* Copy overlays of buffer FROM to buffer TO. */ @@ -911,7 +911,7 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) { eassert (b->overlays); eassert (ov->buffer == b); - interval_tree_remove (ov->buffer->overlays, ov->interval); + itree_remove (ov->buffer->overlays, ov->interval); ov->buffer = NULL; } @@ -951,7 +951,7 @@ delete_all_overlays (struct buffer *b) /* Where are the nodes freed ? --ap */ XOVERLAY (node->data)->buffer = NULL; } - interval_tree_clear (b->overlays); + itree_clear (b->overlays); } static void @@ -960,7 +960,7 @@ free_buffer_overlays (struct buffer *b) /* Actually this does not free any overlay, but the tree only. --ap */ if (b->overlays) { - interval_tree_destroy (b->overlays); + itree_destroy (b->overlays); b->overlays = NULL; } } @@ -980,7 +980,7 @@ set_overlays_multibyte (bool multibyte) struct itree_node **nodes = NULL; struct itree_tree *tree = current_buffer->overlays; - const intmax_t size = interval_tree_size (tree); + const intmax_t size = itree_size (tree); /* We can't use `interval_node_set_region` at the same time as we iterate over the itree, so we need an auxiliary storage @@ -999,8 +999,8 @@ set_overlays_multibyte (bool multibyte) if (multibyte) { - ptrdiff_t begin = interval_node_begin (tree, node); - ptrdiff_t end = interval_node_end (tree, node); + ptrdiff_t begin = itree_node_begin (tree, node); + ptrdiff_t end = itree_node_end (tree, node); /* This models the behavior of markers. (The behavior of text-intervals differs slightly.) */ @@ -1010,12 +1010,12 @@ set_overlays_multibyte (bool multibyte) while (end < Z_BYTE && !CHAR_HEAD_P (FETCH_BYTE (end))) end++; - interval_node_set_region (tree, node, BYTE_TO_CHAR (begin), + itree_node_set_region (tree, node, BYTE_TO_CHAR (begin), BYTE_TO_CHAR (end)); } else { - interval_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), + itree_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), CHAR_TO_BYTE (node->end)); } } @@ -3446,7 +3446,7 @@ adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length) but we may need to update the value of the overlay center. */ if (! current_buffer->overlays) return; - interval_tree_insert_gap (current_buffer->overlays, pos, length); + itree_insert_gap (current_buffer->overlays, pos, length); } void @@ -3454,7 +3454,7 @@ adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length) { if (! current_buffer->overlays) return; - interval_tree_delete_gap (current_buffer->overlays, pos, length); + itree_delete_gap (current_buffer->overlays, pos, length); } @@ -3594,7 +3594,7 @@ buffer. */) add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay), n_beg, n_end); } else - interval_node_set_region (b->overlays, XOVERLAY (overlay)->interval, + itree_node_set_region (b->overlays, XOVERLAY (overlay)->interval, n_beg, n_end); /* If the overlay has changed buffers, do a thorough redisplay. */ diff --git a/src/buffer.h b/src/buffer.h index ee0c8dd8361..ce1b7b27b0f 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1398,7 +1398,7 @@ overlay_start (struct Lisp_Overlay *ov) { if (! ov->buffer) return -1; - return interval_node_begin (ov->buffer->overlays, ov->interval); + return itree_node_begin (ov->buffer->overlays, ov->interval); } INLINE ptrdiff_t @@ -1406,7 +1406,7 @@ overlay_end (struct Lisp_Overlay *ov) { if (! ov->buffer) return -1; - return interval_node_end (ov->buffer->overlays, ov->interval); + return itree_node_end (ov->buffer->overlays, ov->interval); } /* Return the start of OV in its buffer, or -1 if OV is not associated diff --git a/src/itree.c b/src/itree.c index 6d54a36c3bb..501226b7e28 100644 --- a/src/itree.c +++ b/src/itree.c @@ -511,7 +511,7 @@ interval_tree_validate (struct itree_tree *tree, struct itree_node *node) /* Initialize an allocated node. */ void -interval_node_init (struct itree_node *node, +itree_node_init (struct itree_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { @@ -528,7 +528,7 @@ interval_node_init (struct itree_node *node, /* Return NODE's begin value, computing it if necessary. */ ptrdiff_t -interval_node_begin (struct itree_tree *tree, +itree_node_begin (struct itree_tree *tree, struct itree_node *node) { interval_tree_validate (tree, node); @@ -538,7 +538,7 @@ interval_node_begin (struct itree_tree *tree, /* Return NODE's end value, computing it if necessary. */ ptrdiff_t -interval_node_end (struct itree_tree *tree, +itree_node_end (struct itree_tree *tree, struct itree_node *node) { interval_tree_validate (tree, node); @@ -548,7 +548,7 @@ interval_node_end (struct itree_tree *tree, /* Allocate an interval_tree. Free with interval_tree_destroy. */ struct itree_tree* -interval_tree_create (void) +itree_create (void) { /* FIXME? Maybe avoid the initialization of itree_null in the same way that is used to call mem_init in alloc.c? It's not really @@ -556,14 +556,14 @@ interval_tree_create (void) itree_init (); struct itree_tree *tree = xmalloc (sizeof (*tree)); - interval_tree_clear (tree); + itree_clear (tree); return tree; } /* Reset the tree TREE to its empty state. */ void -interval_tree_clear (struct itree_tree *tree) +itree_clear (struct itree_tree *tree) { tree->root = NULL; tree->otick = 1; @@ -583,7 +583,7 @@ interval_tree_init (struct interval_tree *tree) /* Release a tree, freeing its allocated memory. */ void -interval_tree_destroy (struct itree_tree *tree) +itree_destroy (struct itree_tree *tree) { eassert (tree->root == NULL); /* if (tree->iter) @@ -594,7 +594,7 @@ interval_tree_destroy (struct itree_tree *tree) /* Return the number of nodes in TREE. */ intmax_t -interval_tree_size (struct itree_tree *tree) +itree_size (struct itree_tree *tree) { return tree->size; } @@ -821,7 +821,7 @@ interval_tree_insert (struct itree_tree *tree, struct itree_node *node) } void -itree_insert_node (struct itree_tree *tree, struct itree_node *node, +itree_insert (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { node->begin = begin; @@ -833,14 +833,14 @@ itree_insert_node (struct itree_tree *tree, struct itree_node *node, /* Safely modify a node's interval. */ void -interval_node_set_region (struct itree_tree *tree, +itree_node_set_region (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { interval_tree_validate (tree, node); if (begin != node->begin) { - interval_tree_remove (tree, node); + itree_remove (tree, node); node->begin = min (begin, PTRDIFF_MAX - 1); node->end = max (node->begin, end); interval_tree_insert (tree, node); @@ -1054,7 +1054,7 @@ interval_tree_transplant (struct itree_tree *tree, /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct itree_node* -interval_tree_remove (struct itree_tree *tree, struct itree_node *node) +itree_remove (struct itree_tree *tree, struct itree_node *node) { eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ @@ -1181,7 +1181,7 @@ itree_iterator_finish (struct itree_iterator *iter) front_advance setting. */ void -interval_tree_insert_gap (struct itree_tree *tree, +itree_insert_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1201,7 +1201,7 @@ interval_tree_insert_gap (struct itree_tree *tree, interval_stack_push (saved, node); } for (int i = 0; i < saved->length; ++i) - interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); + itree_remove (tree, nav_nodeptr (saved->nodes[i])); /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ @@ -1265,7 +1265,7 @@ interval_tree_insert_gap (struct itree_tree *tree, intersecting it. */ void -interval_tree_delete_gap (struct itree_tree *tree, +itree_delete_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) diff --git a/src/itree.h b/src/itree.h index b4116a1fb75..3df07ac1b7e 100644 --- a/src/itree.h +++ b/src/itree.h @@ -75,10 +75,9 @@ struct itree_node adjustment before use as buffer positions. NOTE: BEGIN and END must not be modified while the node is part - of a tree. Use interval_tree_insert_gap and - interval_tree_delete_gap instead. + of a tree. Use itree_insert_gap and itree_delete_gap instead. - NOTE: The interval generators ensure nodes are clean before + NOTE: The interval iterators ensure nodes are clean before yielding them, so BEGIN and END may be safely used as buffer positions then. */ @@ -107,19 +106,21 @@ enum itree_order { ITREE_PRE_ORDER, }; -void interval_node_init (struct itree_node *, bool, bool, Lisp_Object); -ptrdiff_t interval_node_begin (struct itree_tree *, struct itree_node *); -ptrdiff_t interval_node_end (struct itree_tree *, struct itree_node *); -void interval_node_set_region (struct itree_tree *, struct itree_node *, ptrdiff_t, ptrdiff_t); -struct itree_tree *interval_tree_create (void); -void interval_tree_destroy (struct itree_tree *); -intmax_t interval_tree_size (struct itree_tree *); -void interval_tree_clear (struct itree_tree *); -void itree_insert_node (struct itree_tree *tree, struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end); -struct itree_node *interval_tree_remove (struct itree_tree *, struct itree_node *); -void interval_tree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void itree_node_init (struct itree_node *, bool, bool, Lisp_Object); +ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *); +ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *); +void itree_node_set_region (struct itree_tree *, struct itree_node *, + ptrdiff_t, ptrdiff_t); +struct itree_tree *itree_create (void); +void itree_destroy (struct itree_tree *); +intmax_t itree_size (struct itree_tree *); +void itree_clear (struct itree_tree *); +void itree_insert (struct itree_tree *tree, struct itree_node *node, + ptrdiff_t begin, ptrdiff_t end); +struct itree_node *itree_remove (struct itree_tree *, + struct itree_node *); +void itree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); /* Iteration functions. Almost all code should use ITREE_FOREACH instead. */ From a2fde77b5cc15ec5a1c29ca72c97c806204818a9 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Fri, 21 Oct 2022 16:07:08 -0700 Subject: [PATCH 66/70] Fix handling of overlays that begin at END in 'overlays_in' When passed EMPTY, 'overlays_in' should return empty overlays at END. It was doing so, but it was also returning any other overlay that happened to begin at END. bug#58672 * src/buffer.c (overlays_in): Don't return overlays at END unless they are empty overlays. (disable_line_numbers_overlay_at_eob): Pass 'false' instead of 'NULL' for the bool 'empty' arg. * test/src/buffer-tests.el (sorted-overlays-in): New helper function. (test-overlays-in-empty-range): New test exhaustively covering these edge conditions. (test-overlays-in-empty-range-bug58672): Simple test for one case. --- src/buffer.c | 11 ++++++---- test/src/buffer-tests.el | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 3286cd338ff..fe6b515493e 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2940,7 +2940,8 @@ the normal hook `change-major-mode-hook'. */) [BEG, END). If EMPTY is true, include empty overlays in that range and also at - END, provided END denotes the position at the end of the buffer. + END, provided END denotes the position at the end of the accessible + part of the buffer. Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. @@ -2968,7 +2969,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, beg, - /* Find empty OV at Z ? */ + /* Find empty OV at ZV ? */ (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) { if (node->begin > end) @@ -2985,6 +2986,8 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ITREE_FOREACH_ABORT (); break; } + if (empty && node->begin != node->end) + continue; } if (! empty && node->begin == node->end) @@ -3111,11 +3114,11 @@ disable_line_numbers_overlay_at_eob (void) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (ZV, ZV, 0, &v, &size, NULL, NULL); + n = overlays_in (ZV, ZV, 0, &v, &size, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (ZV, ZV, 0, &v, &n, NULL, NULL); + overlays_in (ZV, ZV, 0, &v, &n, false, NULL); } for (i = 0; i < n; ++i) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 3833f88c5c8..6d5d9913a01 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -937,6 +937,51 @@ with parameters from the *Messages* buffer modification." (deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) (deftest-overlays-in-1 af 10 11 (a) (a 10 10)) +(defun sorted-overlays-in (beg end) + (sort + (mapcar (lambda (overlay) + (list (overlay-start overlay) + (overlay-end overlay))) + (overlays-in beg end)) + (lambda (first second) + (cl-loop for a in first + for b in second + thereis (< a b) + until (> a b))))) + +;; behavior for empty range +(ert-deftest test-overlays-in-empty-range () + (with-temp-buffer + (insert (make-string 4 ?x)) + (cl-loop for start from (point-min) to (point-max) + do (cl-loop for end from start to (point-max) + do (when (<= start end) + (make-overlay start end)))) + + (cl-loop for pos from (point-min) to (point-max) + do (ert-info ((format "after (overlay-recenter %d)" pos)) + (overlay-recenter pos) + (should (equal + '((1 1)) + (sorted-overlays-in (point-min) (point-min)))) + (should (equal + '((1 3) (1 4) (1 5) (2 2)) + (sorted-overlays-in 2 2))) + (should (equal + '((1 4) (1 5) (2 4) (2 5) (3 3)) + (sorted-overlays-in 3 3))) + (should (equal + '((1 5) (2 5) (3 5) (4 4)) + (sorted-overlays-in 4 4))) + (should (equal + '((5 5)) + (sorted-overlays-in (point-max) (point-max)))))))) + +(ert-deftest test-overlays-in-empty-range-bug58672 () + (with-temp-buffer + (insert (make-string 10 ?=)) + (make-overlay 5 7 nil nil t) + (should (equal nil (overlays-in 5 5))))) ;; behavior at point-max (ert-deftest test-overlays-in-2 () From f2a51774a934f29848c5796b46faa5e3b022c401 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Fri, 21 Oct 2022 19:45:13 -0700 Subject: [PATCH 67/70] Fix a narrow-to-region vs. overlays-at bug See bug#58703. * src/buffer.c (overlays_in): Add a new TRAILING arg expressing the behavior wanted by `overlays-at', namely to include all overlays beginning at the POS passed to `overlays-at', even if POS is the end of the narrowed region. Pass true and the search range is extended to ZV+1 if END is greater than ZV, just as is done for EMPTY. (overlays_at): Pass 'true' for the new trailing arg. At present this is the only caller passing 'true'. (mouse_face_overlay_overlaps): Pass 'false' for the new trailing arg. (disable_line_numbers_overlay_at_eob): ditto. (Foverlays_in): ditto. * src/editfns.c (overlays_around): ditto. * test/src/buffer-tests.el (sorted-overlays): Add a spot test for this. --- src/buffer.c | 31 +++++++++++++++++++++---------- src/buffer.h | 2 +- src/editfns.c | 2 +- test/src/buffer-tests.el | 39 +++++++++++++++++++++++++++------------ 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index fe6b515493e..dcb3c3944b7 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2943,6 +2943,10 @@ the normal hook `change-major-mode-hook'. */) END, provided END denotes the position at the end of the accessible part of the buffer. + If TRAILING is true, include overlays that begin at END, provided + END denotes the position at the end of the accessible part of the + buffer. + Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. Store in *NEXT_PTR the next position after POS where an overlay starts, @@ -2959,7 +2963,8 @@ the normal hook `change-major-mode-hook'. */) ptrdiff_t overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, - Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, bool empty, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, + bool empty, bool trailing, ptrdiff_t *next_ptr) { ptrdiff_t idx = 0; @@ -2968,9 +2973,14 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, Lisp_Object *vec = *vec_ptr; struct itree_node *node; - ITREE_FOREACH (node, current_buffer->overlays, beg, - /* Find empty OV at ZV ? */ - (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) + /* Extend the search range if overlays beginning at ZV are + wanted. */ + ptrdiff_t search_end = ZV; + if (end >= ZV && (empty || trailing)) + ++search_end; + + ITREE_FOREACH (node, current_buffer->overlays, beg, search_end, + ASCENDING) { if (node->begin > end) { @@ -3022,7 +3032,8 @@ overlays_at (ptrdiff_t pos, bool extend, Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, ptrdiff_t *next_ptr) { - return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, false, next_ptr); + return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, + false, true, next_ptr); } ptrdiff_t @@ -3085,11 +3096,11 @@ mouse_face_overlay_overlaps (Lisp_Object overlay) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (start, end, 0, &v, &size, true, NULL); + n = overlays_in (start, end, 0, &v, &size, true, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (start, end, 0, &v, &n, true, NULL); + overlays_in (start, end, 0, &v, &n, true, false, NULL); } for (i = 0; i < n; ++i) @@ -3114,11 +3125,11 @@ disable_line_numbers_overlay_at_eob (void) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (ZV, ZV, 0, &v, &size, false, NULL); + n = overlays_in (ZV, ZV, 0, &v, &size, false, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (ZV, ZV, 0, &v, &n, false, NULL); + overlays_in (ZV, ZV, 0, &v, &n, false, false, NULL); } for (i = 0; i < n; ++i) @@ -3798,7 +3809,7 @@ end of the accessible part of the buffer. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ noverlays = overlays_in (XFIXNUM (beg), XFIXNUM (end), 1, &overlay_vec, &len, - true, NULL); + true, false, NULL); /* Make a list of them all. */ result = Flist (noverlays, overlay_vec); diff --git a/src/buffer.h b/src/buffer.h index ce1b7b27b0f..3ea4125645d 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1173,7 +1173,7 @@ extern void compact_buffer (struct buffer *); extern void evaporate_overlays (ptrdiff_t); extern ptrdiff_t overlays_at (ptrdiff_t, bool, Lisp_Object **, ptrdiff_t *, ptrdiff_t *); extern ptrdiff_t overlays_in (ptrdiff_t, ptrdiff_t, bool, Lisp_Object **, - ptrdiff_t *, bool, ptrdiff_t *); + ptrdiff_t *, bool, bool, ptrdiff_t *); extern ptrdiff_t previous_overlay_change (ptrdiff_t); extern ptrdiff_t next_overlay_change (ptrdiff_t); extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *); diff --git a/src/editfns.c b/src/editfns.c index 1af6ea1b11d..00380175859 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -270,7 +270,7 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) static ptrdiff_t overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - return overlays_in (pos - 1, pos, false, &vec, &len, false, NULL); + return overlays_in (pos - 1, pos, false, &vec, &len, false, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 6d5d9913a01..c6d176cc171 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -860,6 +860,33 @@ with parameters from the *Messages* buffer modification." (should-length 1 (overlays-at 15)) (should-length 1 (overlays-at (point-max)))))) +(defun sorted-overlays (overlays) + (sort + (mapcar (lambda (overlay) + (list (overlay-start overlay) + (overlay-end overlay))) + overlays) + (lambda (first second) + (cl-loop for a in first + for b in second + thereis (< a b) + until (> a b))))) + +(defun sorted-overlays-at (pos) + (sorted-overlays (overlays-at pos))) + +(defun sorted-overlays-in (beg end) + (sorted-overlays (overlays-in beg end))) + +(ert-deftest test-overlays-at-narrow-to-region-end () + ;; See bug#58703. + (with-temp-buffer + (insert (make-string 30 ?x)) + (make-overlay 10 11) + (narrow-to-region 10 10) + (should (equal + '((10 11)) + (sorted-overlays-at 10))))) ;; +==========================================================================+ ;; | overlay-in @@ -937,18 +964,6 @@ with parameters from the *Messages* buffer modification." (deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) (deftest-overlays-in-1 af 10 11 (a) (a 10 10)) -(defun sorted-overlays-in (beg end) - (sort - (mapcar (lambda (overlay) - (list (overlay-start overlay) - (overlay-end overlay))) - (overlays-in beg end)) - (lambda (first second) - (cl-loop for a in first - for b in second - thereis (< a b) - until (> a b))))) - ;; behavior for empty range (ert-deftest test-overlays-in-empty-range () (with-temp-buffer From 555bc1f8b3ed8b02fb5acb013ed24073b0666585 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 22 Oct 2022 20:46:30 -0700 Subject: [PATCH 68/70] Add `get-pos-property' tests covering bug#58706 * test/src/buffer-tests.el (get-pos-property-overlay-beg): New test. (get-pos-property-overlay-empty-rear-advance): ditto. (get-pos-property-overlay-past-rear-advance): ditto. (get-pos-property-overlay-at-narrowed-end): ditto. --- test/src/buffer-tests.el | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index c6d176cc171..e0207325241 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1108,6 +1108,49 @@ with parameters from the *Messages* buffer modification." (should-not (delete-all-overlays)))) +;; +==========================================================================+ +;; | get-pos-property +;; +==========================================================================+ + +(ert-deftest get-pos-property-overlay-beg () + "Test `get-pos-property' at the beginning of an overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9999 10001))) + (overlay-put overlay 'forty-two 42)) + (should (equal 42 (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-empty-rear-advance () + "Test `get-pos-property' at the end of an empty rear-advance overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9999 9999 nil nil t))) + (overlay-put overlay 'forty-two 42)) + (should (equal 42 (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-past-rear-advance () + "Test `get-pos-property' past the end of an empty rear-advance overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9998 9998 nil nil t))) + (overlay-put overlay 'forty-two 42)) + (should (equal nil (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-at-narrowed-end () + "Test `get-pos-property' at the end of a narrowed region. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 11000 ?x)) + (narrow-to-region 9998 10000) + (let ((overlay (make-overlay 10000 10000 nil t nil))) + (overlay-put overlay 'forty-two 42)) + (should (equal nil (get-pos-property 9999 'forty-two))))) + +;; FIXME: add more `get-pos-property' tests + ;; +==========================================================================+ ;; | get-char-property(-and-overlay) ;; +==========================================================================+ From b78be2bf7a9ac8b71d25529d5736373f51852c36 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sat, 22 Oct 2022 20:48:10 -0700 Subject: [PATCH 69/70] Fix `get-pos-property' for the new overlay implementation. Some of the trickier edge cases weren't handled properly. See bug#58706. * src/editfns.c (overlays_around): Extend the search range past POS by one to fetch overlays beginning at POS. Fetch empty overlays, as they may be extended by an insertion and thus be relevant to `get-pos-property'. Make a note that the function now, unfortunately, may return out of range overlays. (Fget_pos_property): Deal with 'overlays_around' returning out of range overlays. --- src/editfns.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/editfns.c b/src/editfns.c index 00380175859..c892c02927d 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -265,12 +265,20 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) /* Find all the overlays in the current buffer that touch position POS. Return the number found, and store them in a vector in VEC - of length LEN. */ + of length LEN. + + Note: this can return overlays that do not touch POS. The caller + should filter these out. */ static ptrdiff_t overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - return overlays_in (pos - 1, pos, false, &vec, &len, false, false, NULL); + /* Find all potentially rear-advance overlays at (POS - 1). Find + all overlays at POS, so end at (POS + 1). Find even empty + overlays, which due to the way 'overlays-in' works implies that + we might also fetch empty overlays starting at (POS + 1). */ + return overlays_in (pos - 1, pos + 1, false, &vec, &len, + true, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, @@ -333,7 +341,9 @@ at POSITION. */) if ((OVERLAY_START (ol) == posn && OVERLAY_FRONT_ADVANCE_P (ol)) || (OVERLAY_END (ol) == posn - && ! OVERLAY_REAR_ADVANCE_P (ol))) + && ! OVERLAY_REAR_ADVANCE_P (ol)) + || OVERLAY_START (ol) > posn + || OVERLAY_END (ol) < posn) ; /* The overlay will not cover a char inserted at point. */ else { From 9d7ba2b1998afc3664c37d9d1b6f6ca2d68356e9 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 23 Oct 2022 08:58:00 -0700 Subject: [PATCH 70/70] Fix compiler warning about possible null pointer dereference * src/itree.c (interval_tree_remove_fix): Use 'eassert' to both check for non-null and suppress a compiler warning. --- src/itree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/itree.c b/src/itree.c index 501226b7e28..e824f2c8914 100644 --- a/src/itree.c +++ b/src/itree.c @@ -919,6 +919,7 @@ interval_tree_remove_fix (struct itree_tree *tree, interval_tree_rotate_left (tree, parent); other = parent->right; } + eassume (other != NULL); if (null_safe_is_black (other->left) /* 2.a */ && null_safe_is_black (other->right)) @@ -956,6 +957,7 @@ interval_tree_remove_fix (struct itree_tree *tree, interval_tree_rotate_right (tree, parent); other = parent->left; } + eassume (other != NULL); if (null_safe_is_black (other->right) /* 2.b */ && null_safe_is_black (other->left))