diff options
| author | Matt Armstrong | 2022-10-10 09:07:42 -0700 |
|---|---|---|
| committer | Matt Armstrong | 2022-10-10 19:37:51 -0700 |
| commit | 51a8e375ebea2e6e05eed623bddfb323b8e408f0 (patch) | |
| tree | bdf6ee616d2b349617869b66943e14c4ef483ad9 | |
| parent | 67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5 (diff) | |
| download | emacs-51a8e375ebea2e6e05eed623bddfb323b8e408f0.tar.gz emacs-51a8e375ebea2e6e05eed623bddfb323b8e408f0.zip | |
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.
| -rw-r--r-- | src/itree.c | 80 |
1 files 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 | |||
| 222 | }; | 222 | }; |
| 223 | 223 | ||
| 224 | static struct check_subtree_result | 224 | static struct check_subtree_result |
| 225 | check_subtree (struct interval_node *node, uintmax_t tree_otick, | 225 | check_subtree (struct interval_node *node, |
| 226 | bool check_red_black_invariants, uintmax_t tree_otick, | ||
| 226 | int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, | 227 | int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, |
| 227 | ptrdiff_t max_begin) | 228 | ptrdiff_t max_begin) |
| 228 | { | 229 | { |
| @@ -251,23 +252,9 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, | |||
| 251 | eassert (node->left == ITREE_NULL || node->left->parent == node); | 252 | eassert (node->left == ITREE_NULL || node->left->parent == node); |
| 252 | eassert (node->right == ITREE_NULL || node->right->parent == node); | 253 | eassert (node->right == ITREE_NULL || node->right->parent == node); |
| 253 | 254 | ||
| 254 | /* We don't normally check the RB invariants here (neither the | 255 | /* No red nodes have red parents. */ |
| 255 | absence of red+red nor the equal-black-depth), so that we can use | 256 | if (check_red_black_invariants && node->parent != ITREE_NULL) |
| 256 | this check even while the tree is temporarily breaking some of | 257 | eassert (!node->red || !node->parent->red); |
| 257 | those invarints. You can enable them if you want. */ | ||
| 258 | if (false) | ||
| 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 | } | ||
| 271 | 258 | ||
| 272 | /* Validate otick. A node's otick must be <= to the tree's otick | 259 | /* Validate otick. A node's otick must be <= to the tree's otick |
| 273 | and <= to its parent's otick. | 260 | and <= to its parent's otick. |
| @@ -292,11 +279,13 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, | |||
| 292 | eassert (end <= limit); | 279 | eassert (end <= limit); |
| 293 | 280 | ||
| 294 | struct check_subtree_result left_result | 281 | struct check_subtree_result left_result |
| 295 | = check_subtree (node->left, tree_otick, max_depth - 1, offset, | 282 | = check_subtree (node->left, check_red_black_invariants, |
| 296 | min_begin, begin); | 283 | tree_otick, max_depth - 1, offset, min_begin, |
| 284 | begin); | ||
| 297 | struct check_subtree_result right_result | 285 | struct check_subtree_result right_result |
| 298 | = check_subtree (node->right, tree_otick, max_depth - 1, offset, | 286 | = check_subtree (node->right, check_red_black_invariants, |
| 299 | begin, max_begin); | 287 | tree_otick, max_depth - 1, offset, begin, |
| 288 | max_begin); | ||
| 300 | 289 | ||
| 301 | eassert (left_result.limit <= limit); | 290 | eassert (left_result.limit <= limit); |
| 302 | eassert (right_result.limit <= limit); | 291 | eassert (right_result.limit <= limit); |
| @@ -304,24 +293,29 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, | |||
| 304 | result.complete = left_result.complete && right_result.complete; | 293 | result.complete = left_result.complete && right_result.complete; |
| 305 | if (result.complete) | 294 | if (result.complete) |
| 306 | { | 295 | { |
| 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; | 296 | result.size = 1 + left_result.size + right_result.size; |
| 315 | result.limit | 297 | result.limit |
| 316 | = max (end, max (left_result.limit, right_result.limit)); | 298 | = max (end, max (left_result.limit, right_result.limit)); |
| 317 | 299 | ||
| 318 | eassert (limit == result.limit); | 300 | eassert (limit == result.limit); |
| 301 | |||
| 302 | if (check_red_black_invariants) | ||
| 303 | { | ||
| 304 | /* Every path from a node to a descendent leaf contains the | ||
| 305 | same number of black nodes. Often said this way: all | ||
| 306 | nodes have the same "black height". */ | ||
| 307 | eassert (left_result.black_height | ||
| 308 | == right_result.black_height); | ||
| 309 | result.black_height | ||
| 310 | = (node->red ? 0 : 1) + left_result.black_height; | ||
| 311 | } | ||
| 319 | } | 312 | } |
| 320 | 313 | ||
| 321 | return result; | 314 | return result; |
| 322 | } | 315 | } |
| 323 | 316 | ||
| 324 | /* Validate invariants for TREE. | 317 | /* Validate invariants for TREE. If CHECK_RED_BLACK_INVARIANTS, red |
| 318 | nodes with red children are considered invalid. | ||
| 325 | 319 | ||
| 326 | This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 | 320 | This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 |
| 327 | (i.e. Emacs is not configured with | 321 | (i.e. Emacs is not configured with |
| @@ -330,7 +324,8 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, | |||
| 330 | entire tree and validates all invariants. | 324 | entire tree and validates all invariants. |
| 331 | */ | 325 | */ |
| 332 | static bool | 326 | static bool |
| 333 | check_tree (struct interval_tree *tree) | 327 | check_tree_common (struct interval_tree *tree, |
| 328 | bool check_red_black_invariants) | ||
| 334 | { | 329 | { |
| 335 | eassert (tree != NULL); | 330 | eassert (tree != NULL); |
| 336 | eassert (tree->size >= 0); | 331 | eassert (tree->size >= 0); |
| @@ -353,8 +348,9 @@ check_tree (struct interval_tree *tree) | |||
| 353 | 348 | ||
| 354 | struct interval_node *node = tree->root; | 349 | struct interval_node *node = tree->root; |
| 355 | struct check_subtree_result result | 350 | struct check_subtree_result result |
| 356 | = check_subtree (node, tree->otick, max_height, node->offset, | 351 | = check_subtree (node, check_red_black_invariants, tree->otick, |
| 357 | PTRDIFF_MIN, PTRDIFF_MAX); | 352 | max_height, node->offset, PTRDIFF_MIN, |
| 353 | PTRDIFF_MAX); | ||
| 358 | eassert (result.complete); | 354 | eassert (result.complete); |
| 359 | eassert (result.size == tree->size); | 355 | eassert (result.size == tree->size); |
| 360 | 356 | ||
| @@ -362,6 +358,22 @@ check_tree (struct interval_tree *tree) | |||
| 362 | return true; | 358 | return true; |
| 363 | } | 359 | } |
| 364 | 360 | ||
| 361 | /* Check the tree with all invariant checks enabled. */ | ||
| 362 | static bool | ||
| 363 | check_tree (struct interval_tree *tree) | ||
| 364 | { | ||
| 365 | return check_tree_common (tree, true); | ||
| 366 | } | ||
| 367 | |||
| 368 | /* Check the tree with all invariant checks enabled, except for the | ||
| 369 | red-black tree invariants. Useful for asserting the other | ||
| 370 | invariants while inserting or removing. */ | ||
| 371 | static bool | ||
| 372 | check_tree_no_rb (struct interval_tree *tree) | ||
| 373 | { | ||
| 374 | return check_tree_common (tree, false); | ||
| 375 | } | ||
| 376 | |||
| 365 | /* +===================================================================================+ | 377 | /* +===================================================================================+ |
| 366 | * | Stack | 378 | * | Stack |
| 367 | * +===================================================================================+ */ | 379 | * +===================================================================================+ */ |
| @@ -626,7 +638,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) | |||
| 626 | 638 | ||
| 627 | /* Fix/update the tree */ | 639 | /* Fix/update the tree */ |
| 628 | ++tree->size; | 640 | ++tree->size; |
| 629 | eassert (check_tree (tree)); | 641 | eassert (check_tree_no_rb (tree)); |
| 630 | interval_tree_insert_fix (tree, node); | 642 | interval_tree_insert_fix (tree, node); |
| 631 | } | 643 | } |
| 632 | 644 | ||