2 * Copyright (C) 2010 Joseph Adams <joeyadams3.14159@gmail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #include "smatch_slist.h"
29 static AvlNode
*mkNode(const struct sm_state
*sm
);
30 static void freeNode(AvlNode
*node
);
32 static AvlNode
*lookup(const struct stree
*avl
, AvlNode
*node
, const struct sm_state
*sm
);
34 static bool insert_sm(struct stree
*avl
, AvlNode
**p
, const struct sm_state
*sm
);
35 static bool remove_sm(struct stree
*avl
, AvlNode
**p
, const struct sm_state
*sm
, AvlNode
**ret
);
36 static bool removeExtremum(AvlNode
**p
, int side
, AvlNode
**ret
);
38 static int sway(AvlNode
**p
, int sway
);
39 static void balance(AvlNode
**p
, int side
);
41 static bool checkBalances(AvlNode
*node
, int *height
);
42 static bool checkOrder(struct stree
*avl
);
43 static size_t countNode(AvlNode
*node
);
48 * Utility macros for converting between
49 * "balance" values (-1 or 1) and "side" values (0 or 1).
56 #define bal(side) ((side) == 0 ? -1 : 1)
57 #define side(bal) ((bal) == 1 ? 1 : 0)
59 static struct stree
*avl_new(void)
61 struct stree
*avl
= malloc(sizeof(*avl
));
67 avl
->base_stree
= NULL
;
68 avl
->has_states
= calloc(num_checks
, sizeof(char));
75 void free_stree(struct stree
**avl
)
80 assert((*avl
)->references
> 0);
83 if ((*avl
)->references
!= 0) {
90 freeNode((*avl
)->root
);
95 struct sm_state
*avl_lookup(const struct stree
*avl
, const struct sm_state
*sm
)
101 if (sm
->owner
!= USHRT_MAX
&&
102 !avl
->has_states
[sm
->owner
])
104 found
= lookup(avl
, avl
->root
, sm
);
107 return (struct sm_state
*)found
->sm
;
110 AvlNode
*avl_lookup_node(const struct stree
*avl
, const struct sm_state
*sm
)
112 return lookup(avl
, avl
->root
, sm
);
115 size_t stree_count(const struct stree
*avl
)
122 static struct stree
*clone_stree_real(struct stree
*orig
)
124 struct stree
*new = avl_new();
128 avl_insert(&new, i
.sm
);
130 new->base_stree
= orig
->base_stree
;
134 bool avl_insert(struct stree
**avl
, const struct sm_state
*sm
)
140 if ((*avl
)->references
> 1) {
141 (*avl
)->references
--;
142 *avl
= clone_stree_real(*avl
);
144 old_count
= (*avl
)->count
;
145 /* fortunately we never call get_state() on "unnull_path" */
146 if (sm
->owner
!= USHRT_MAX
)
147 (*avl
)->has_states
[sm
->owner
] = 1;
148 insert_sm(*avl
, &(*avl
)->root
, sm
);
149 return (*avl
)->count
!= old_count
;
152 bool avl_remove(struct stree
**avl
, const struct sm_state
*sm
)
154 AvlNode
*node
= NULL
;
158 /* it's fairly rare for smatch to call avl_remove */
159 if ((*avl
)->references
> 1) {
160 (*avl
)->references
--;
161 *avl
= clone_stree_real(*avl
);
164 remove_sm(*avl
, &(*avl
)->root
, sm
, &node
);
166 if ((*avl
)->count
== 0)
177 static AvlNode
*mkNode(const struct sm_state
*sm
)
179 AvlNode
*node
= malloc(sizeof(*node
));
181 assert(node
!= NULL
);
190 static void freeNode(AvlNode
*node
)
193 freeNode(node
->lr
[0]);
194 freeNode(node
->lr
[1]);
199 static AvlNode
*lookup(const struct stree
*avl
, AvlNode
*node
, const struct sm_state
*sm
)
206 cmp
= cmp_tracker(sm
, node
->sm
);
209 return lookup(avl
, node
->lr
[0], sm
);
211 return lookup(avl
, node
->lr
[1], sm
);
216 * Insert an sm into a subtree, rebalancing if necessary.
218 * Return true if the subtree's height increased.
220 static bool insert_sm(struct stree
*avl
, AvlNode
**p
, const struct sm_state
*sm
)
228 int cmp
= cmp_tracker(sm
, node
->sm
);
235 if (!insert_sm(avl
, &node
->lr
[side(cmp
)], sm
))
238 /* If tree's balance became -1 or 1, it means the tree's height grew due to insertion. */
239 return sway(p
, cmp
) != 0;
244 * Remove the node matching the given sm.
245 * If present, return the removed node through *ret .
246 * The returned node's lr and balance are meaningless.
248 * Return true if the subtree's height decreased.
250 static bool remove_sm(struct stree
*avl
, AvlNode
**p
, const struct sm_state
*sm
, AvlNode
**ret
)
252 if (p
== NULL
|| *p
== NULL
) {
256 int cmp
= cmp_tracker(sm
, node
->sm
);
262 if (node
->lr
[0] != NULL
&& node
->lr
[1] != NULL
) {
263 AvlNode
*replacement
;
267 /* Pick a subtree to pull the replacement from such that
268 * this node doesn't have to be rebalanced. */
269 side
= node
->balance
<= 0 ? 0 : 1;
271 shrunk
= removeExtremum(&node
->lr
[side
], 1 - side
, &replacement
);
273 replacement
->lr
[0] = node
->lr
[0];
274 replacement
->lr
[1] = node
->lr
[1];
275 replacement
->balance
= node
->balance
;
281 replacement
->balance
-= bal(side
);
283 /* If tree's balance became 0, it means the tree's height shrank due to removal. */
284 return replacement
->balance
== 0;
287 if (node
->lr
[0] != NULL
)
295 if (!remove_sm(avl
, &node
->lr
[side(cmp
)], sm
, ret
))
298 /* If tree's balance became 0, it means the tree's height shrank due to removal. */
299 return sway(p
, -cmp
) == 0;
305 * Remove either the left-most (if side == 0) or right-most (if side == 1)
306 * node in a subtree, returning the removed node through *ret .
307 * The returned node's lr and balance are meaningless.
309 * The subtree must not be empty (i.e. *p must not be NULL).
311 * Return true if the subtree's height decreased.
313 static bool removeExtremum(AvlNode
**p
, int side
, AvlNode
**ret
)
317 if (node
->lr
[side
] == NULL
) {
319 *p
= node
->lr
[1 - side
];
323 if (!removeExtremum(&node
->lr
[side
], side
, ret
))
326 /* If tree's balance became 0, it means the tree's height shrank due to removal. */
327 return sway(p
, -bal(side
)) == 0;
331 * Rebalance a node if necessary. Think of this function
332 * as a higher-level interface to balance().
334 * sway must be either -1 or 1, and indicates what was added to
335 * the balance of this node by a prior operation.
337 * Return the new balance of the subtree.
339 static int sway(AvlNode
**p
, int sway
)
341 if ((*p
)->balance
!= sway
)
342 (*p
)->balance
+= sway
;
344 balance(p
, side(sway
));
346 return (*p
)->balance
;
350 * Perform tree rotations on an unbalanced node.
352 * side == 0 means the node's balance is -2 .
353 * side == 1 means the node's balance is +2 .
355 static void balance(AvlNode
**p
, int side
)
358 *child
= node
->lr
[side
];
359 int opposite
= 1 - side
;
362 if (child
->balance
!= -bal
) {
363 /* Left-left (side == 0) or right-right (side == 1) */
364 node
->lr
[side
] = child
->lr
[opposite
];
365 child
->lr
[opposite
] = node
;
368 child
->balance
-= bal
;
369 node
->balance
= -child
->balance
;
372 /* Left-right (side == 0) or right-left (side == 1) */
373 AvlNode
*grandchild
= child
->lr
[opposite
];
375 node
->lr
[side
] = grandchild
->lr
[opposite
];
376 child
->lr
[opposite
] = grandchild
->lr
[side
];
377 grandchild
->lr
[side
] = child
;
378 grandchild
->lr
[opposite
] = node
;
384 if (grandchild
->balance
== bal
)
385 node
->balance
= -bal
;
386 else if (grandchild
->balance
== -bal
)
387 child
->balance
= bal
;
389 grandchild
->balance
= 0;
394 /************************* avl_check_invariants() *************************/
396 bool avl_check_invariants(struct stree
*avl
)
400 return checkBalances(avl
->root
, &dummy
)
402 && countNode(avl
->root
) == avl
->count
;
405 static bool checkBalances(AvlNode
*node
, int *height
)
410 if (!checkBalances(node
->lr
[0], &h0
))
412 if (!checkBalances(node
->lr
[1], &h1
))
415 if (node
->balance
!= h1
- h0
|| node
->balance
< -1 || node
->balance
> 1)
418 *height
= (h0
> h1
? h0
: h1
) + 1;
426 static bool checkOrder(struct stree
*avl
)
429 const struct sm_state
*last
= NULL
;
430 bool last_set
= false;
432 avl_foreach(i
, avl
) {
433 if (last_set
&& cmp_tracker(last
, i
.sm
) >= 0)
442 static size_t countNode(AvlNode
*node
)
445 return 1 + countNode(node
->lr
[0]) + countNode(node
->lr
[1]);
451 /************************* Traversal *************************/
453 void avl_iter_begin(AvlIter
*iter
, struct stree
*avl
, AvlDirection dir
)
457 iter
->stack_index
= 0;
458 iter
->direction
= dir
;
460 if (!avl
|| !avl
->root
) {
467 while (node
->lr
[dir
] != NULL
) {
468 iter
->stack
[iter
->stack_index
++] = node
;
469 node
= node
->lr
[dir
];
472 iter
->sm
= (struct sm_state
*) node
->sm
;
476 void avl_iter_next(AvlIter
*iter
)
478 AvlNode
*node
= iter
->node
;
479 AvlDirection dir
= iter
->direction
;
484 node
= node
->lr
[1 - dir
];
486 while (node
->lr
[dir
] != NULL
) {
487 iter
->stack
[iter
->stack_index
++] = node
;
488 node
= node
->lr
[dir
];
490 } else if (iter
->stack_index
> 0) {
491 node
= iter
->stack
[--iter
->stack_index
];
499 iter
->sm
= (struct sm_state
*) node
->sm
;
502 struct stree
*clone_stree(struct stree
*orig
)
511 void set_stree_id(struct stree
**stree
, int stree_id
)
513 if ((*stree
)->stree_id
!= 0)
514 *stree
= clone_stree_real(*stree
);
516 (*stree
)->stree_id
= stree_id
;
519 int get_stree_id(struct stree
*stree
)
523 return stree
->stree_id
;