aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Armstrong2022-10-10 08:48:41 -0700
committerMatt Armstrong2022-10-10 19:37:51 -0700
commit67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5 (patch)
tree689c6c3aaf8d24cad9aaf470765010e08d6e53a8
parent246acbddbeb3e9a390fe78242259182af0c2cc18 (diff)
downloademacs-67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5.tar.gz
emacs-67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5.zip
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.
-rw-r--r--src/itree.c156
1 files 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)
205 iter = interval_generator_create (NULL); 205 iter = interval_generator_create (NULL);
206} 206}
207 207
208static ptrdiff_t 208struct check_subtree_result
209recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, 209{
210 ptrdiff_t offset, ptrdiff_t min_begin, 210 /* Were all nodes visited? */
211 ptrdiff_t max_begin, intmax_t *size) 211 bool complete;
212
213 /* Computed node count of the tree. */
214 int size;
215
216 /* Computed limit of the tree (max END). */
217 ptrdiff_t limit;
218
219 /* Computed black height of the tree (count of black nodes from the
220 bottom up to the root). */
221 int black_height;
222};
223
224static struct check_subtree_result
225check_subtree (struct interval_node *node, uintmax_t tree_otick,
226 int max_depth, ptrdiff_t offset, ptrdiff_t min_begin,
227 ptrdiff_t max_begin)
212{ 228{
229 struct check_subtree_result result = { .complete = false,
230 .size = 0,
231 .limit = PTRDIFF_MIN,
232 .black_height = 0 };
213 if (node == ITREE_NULL) 233 if (node == ITREE_NULL)
214 return PTRDIFF_MIN; 234 {
215 ++*size; 235 /* Every nil node of a Red-Black tree is black */
236 result.black_height = 1;
237 result.complete = true;
238 return result;
239 };
240
241 if (max_depth == 0)
242 {
243 result.complete = false;
244 return result;
245 }
216 246
217 /* Validate structure. */ 247 /* Validate structure. */
218 eassert ( 248 eassert (
@@ -221,14 +251,35 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick,
221 eassert (node->left == ITREE_NULL || node->left->parent == node); 251 eassert (node->left == ITREE_NULL || node->left->parent == node);
222 eassert (node->right == ITREE_NULL || node->right->parent == node); 252 eassert (node->right == ITREE_NULL || node->right->parent == node);
223 253
224 /* We don't check the RB invariants here (neither the absence of 254 /* We don't normally check the RB invariants here (neither the
225 red+red nor the equal-black-depth), so that we can use this check 255 absence of red+red nor the equal-black-depth), so that we can use
226 even while the tree is temporarily breaking some of those invarints. */ 256 this check even while the tree is temporarily breaking some of
227 /* Red nodes cannot have red parents. */ 257 those invarints. You can enable them if you want. */
228 /* eassert (node->parent == ITREE_NULL 258 if (false)
229 || !(node->red && node->parent->red)); */ 259 {
260 /* If a node is red then both of its children are black. Red
261 nodes cannot have red parents. */
262 if (node->red)
263 {
264 eassert (node->left == ITREE_NULL
265 || node->left->red == false);
266 eassert (node->right == ITREE_NULL
267 || node->right->red == false);
268 eassert (node->parent == ITREE_NULL || !node->parent->red);
269 }
270 }
230 271
231 eassert (node->offset == 0 || node->otick < tree_otick); 272 /* Validate otick. A node's otick must be <= to the tree's otick
273 and <= to its parent's otick.
274
275 Note: we cannot assert that (NODE.otick == NODE.parent.otick)
276 implies (NODE.offset == 0) because interval_tree_inherit_offset()
277 doesn't always update otick. It could, but it is not clear there
278 is a need. */
279 eassert (node->otick <= tree_otick);
280 eassert (node->parent == ITREE_NULL
281 || node->otick <= node->parent->otick);
282 eassert (node->otick != tree_otick || node->offset == 0);
232 283
233 offset += node->offset; 284 offset += node->offset;
234 ptrdiff_t begin = node->begin + offset; 285 ptrdiff_t begin = node->begin + offset;
@@ -240,29 +291,74 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick,
240 eassert (begin <= max_begin); 291 eassert (begin <= max_begin);
241 eassert (end <= limit); 292 eassert (end <= limit);
242 293
243 ptrdiff_t left_limit 294 struct check_subtree_result left_result
244 = recurse_check_tree (node->left, tree_otick, offset, min_begin, 295 = check_subtree (node->left, tree_otick, max_depth - 1, offset,
245 begin, size); 296 min_begin, begin);
246 ptrdiff_t right_limit 297 struct check_subtree_result right_result
247 = recurse_check_tree (node->right, tree_otick, offset, begin, 298 = check_subtree (node->right, tree_otick, max_depth - 1, offset,
248 max_begin, size); 299 begin, max_begin);
249 eassert (left_limit <= limit); 300
250 eassert (right_limit <= limit); 301 eassert (left_result.limit <= limit);
251 eassert (limit == max (end, max (left_limit, right_limit))); 302 eassert (right_result.limit <= limit);
252 return limit; 303
304 result.complete = left_result.complete && right_result.complete;
305 if (result.complete)
306 {
307 /* Every path from a node to a descendent leaf contains the same
308 number of black nodes. Often said this way: all nodes have
309 the same "black height". */
310 eassert (left_result.black_height == right_result.black_height);
311 result.black_height
312 = (node->red ? 0 : 1) + left_result.black_height;
313
314 result.size = 1 + left_result.size + right_result.size;
315 result.limit
316 = max (end, max (left_result.limit, right_result.limit));
317
318 eassert (limit == result.limit);
319 }
320
321 return result;
253} 322}
254 323
324/* Validate invariants for TREE.
325
326 This runs in constant time when ENABLE_OVERLAY_CHECKING is 0
327 (i.e. Emacs is not configured with
328 "--enable_checking=yes,overlays"). In this mode it can't check all
329 the invariants. When ENABLE_OVERLAY_CHECKING is 1 it checks the
330 entire tree and validates all invariants.
331*/
255static bool 332static bool
256check_tree (struct interval_tree *tree) 333check_tree (struct interval_tree *tree)
257{ 334{
258 eassert (tree != NULL); 335 eassert (tree != NULL);
336 eassert (tree->size >= 0);
259 eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); 337 eassert ((tree->size == 0) == (tree->root == ITREE_NULL));
260 if (tree->root == ITREE_NULL) 338 if (tree->root == ITREE_NULL)
261 return true; 339 return true;
262 340
263 intmax_t size = 0; 341 /* Limit the traversal depth to what 'interval_tree_max_height'
264 recurse_check_tree (tree->root, tree->otick, 0, 342 returns. Later, verify that this is enough height to traverse
265 PTRDIFF_MIN, PTRDIFF_MAX, &size); 343 the complete tree. */
344 const int max_height = interval_tree_max_height (tree);
345 eassert (max_height >= 0);
346 eassert (max_height <= 120);
347
348 /* NOTE: if this check is too expensive an easy fix is to reduce
349 max_height to for large trees, then relax the assertion on
350 result.complete. Assertions in check_subtree will still be made
351 at the bottom of the tree (where they are probably most
352 interesting), but some will be skipped closer to the root. */
353
354 struct interval_node *node = tree->root;
355 struct check_subtree_result result
356 = check_subtree (node, tree->otick, max_height, node->offset,
357 PTRDIFF_MIN, PTRDIFF_MAX);
358 eassert (result.complete);
359 eassert (result.size == tree->size);
360
361 /* The only way this function fails is eassert(). */
266 return true; 362 return true;
267} 363}
268 364
@@ -1145,10 +1241,10 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node)
1145 } 1241 }
1146 1242
1147 /* Offsets can be inherited from dirty nodes (with out of date 1243 /* Offsets can be inherited from dirty nodes (with out of date
1148 otick) during insert and remove. Offsets aren't inherited 1244 otick) during removal, since we do not travel down from the root
1149 downward from the root for these operations so rotations are 1245 in that case. In this case rotations are performed on
1150 performed on potentially "dirty" nodes, where we only make sure 1246 potentially "dirty" nodes, where we only need to make sure the
1151 the *local* offsets are all zero. */ 1247 *local* offsets are zero. */
1152 1248
1153 if (node->offset) 1249 if (node->offset)
1154 { 1250 {