diff --git a/src/google/protobuf-c/gskrbtreemacros.h b/src/google/protobuf-c/gskrbtreemacros.h new file mode 100644 index 0000000..f5a233a --- /dev/null +++ b/src/google/protobuf-c/gskrbtreemacros.h @@ -0,0 +1,981 @@ +#ifndef __GSK_RBTREE_MACROS_H_ +#define __GSK_RBTREE_MACROS_H_ + +/* Macros for construction of a red-black tree. + * Like our other macro-based data-structures, + * this doesn't allocate memory, and it doesn't + * use any helper functions. + * + * It supports "invasive" tree structures, + * for example, you can have nodes that are members + * of two different trees. + * + * A rbtree is a tuple: + * top + * type* + * is_red + * set_is_red + * parent + * left + * right + * comparator + * that should be defined by a macro like "GET_TREE()". + * See tests/test-rbtree-macros. + * + * The methods are: + * INSERT(tree, node, collision_node) + * Try to insert 'node' into tree. + * If an equivalent node exists, + * return it in collision_node, and do nothing else. + * Otherwise, node is added to the tree + * (and collision_node is set to NULL). + * REMOVE(tree, node) + * Remove a node from the tree. + * LOOKUP(tree, node, out) + * Find a node in the tree. + * Finds the node equivalent with + * 'node', and returns it in 'out', + * or sets out to NULL if no node exists. + * LOOKUP_COMPARATOR(tree, key, key_comparator, out) + * Find a node in the tree, based on a key + * which may not be in the same format as the node. + * FIRST(tree, out) + * Set 'out' to the first node in the tree. + * LAST(tree, out) + * Set 'out' to the last node in the tree. + * PREV(tree, cur, out) + * Set 'out' to the previous node in the tree before cur. + * NEXT(tree, cur, out) + * Set 'out' to the next node in the tree after cur. + * INFIMUM_COMPARATOR(tree, key, key_comparator, out) + * Find the last node in the tree which is before or equal to 'key'. + * SUPREMUM_COMPARATOR(tree, key, key_comparator, out) + * Find the first node in the tree which is after or equal to 'key'. + * INFIMUM(tree, key, out) + * Find the last node in the tree which is before or equal to 'key'. + * SUPREMUM(tree, key, out) + * Find the first node in the tree which is after or equal to 'key'. + * + * XXX: how about rename INFIMUM, SUPREMUM as LOOKUP_LE, LOOKUP_GE * respectively? + * That is, LOOKUP_LE returns the largest elt less-than-or-equal-to the key + * and LOOKUP_GE returns the smallest elt greater-than-or-equal-to the key. + * You can think of it as a lookup that allows "slip" in the direction indicated. + * LOOKUP_LT and LOOKUP_GT would be natural methods too-- for lookups + * that don't permit equality. + * + * + * Occasionally, you may need the "RBCTREE", which has the is_red flag, + * but also keeps a "count" field at every node telling the size of that subtree. + * This then has all the methods of RBTREE, plus: + * GET_BY_INDEX(tree, n, out) + * Return the n-th element in the tree. + * GET_BY_INDEX_UNCHECKED(tree, n, out) + * Return the n-th element in the tree; + * n MUST be less than or equal to the number of + * nodes in the tree. + * GET_NODE_INDEX(tree, node, n_out) + * Sets n_out to the index of node in the tree. + * + * An rbctree is a tuple: + * top + * type* + * is_red + * set_is_red + * get_count + * set_count + * parent + * left + * right + * comparator + */ + +/* + * By and large, the red-black tree algorithms used here are from + * the classic text: + * _Introduction to Algorithms_. Thomas Cormen, Charles Leiserson, + * and Donald Rivest. MIT Press. 1990. + * Citations appears as Algorithms:300 indicating page 300 (for example). + * The "rbctree" is my name for this idea (daveb), + * which i suspect has been thought of and implemented before. + */ +#define GSK_RBTREE_INSERT(tree, node, collision_node) \ + GSK_RBTREE_INSERT_(tree, node, collision_node) +#define GSK_RBTREE_REMOVE(tree, node) \ + GSK_RBTREE_REMOVE_(tree, node) +#define GSK_RBTREE_LOOKUP(tree, key, out) \ + GSK_RBTREE_LOOKUP_(tree, key, out) +#define GSK_RBTREE_LOOKUP_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBTREE_LOOKUP_COMPARATOR_(tree, key, key_comparator, out) + +#define GSK_RBTREE_FIRST(tree, out) \ + GSK_RBTREE_FIRST_(tree, out) +#define GSK_RBTREE_LAST(tree, out) \ + GSK_RBTREE_LAST_(tree, out) +#define GSK_RBTREE_NEXT(tree, in, out) \ + GSK_RBTREE_NEXT_(tree, in, out) +#define GSK_RBTREE_PREV(tree, in, out) \ + GSK_RBTREE_PREV_(tree, in, out) + +#define GSK_RBTREE_SUPREMUM(tree, key, out) \ + GSK_RBTREE_SUPREMUM_(tree, key, out) +#define GSK_RBTREE_SUPREMUM_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBTREE_SUPREMUM_COMPARATOR_(tree, key, key_comparator, out) +#define GSK_RBTREE_INFIMUM(tree, key, out) \ + GSK_RBTREE_INFIMUM_(tree, key, out) +#define GSK_RBTREE_INFIMUM_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBTREE_INFIMUM_COMPARATOR_(tree, key, key_comparator, out) + +#if 1 +#undef G_STMT_START +#define G_STMT_START do +#undef G_STMT_END +#define G_STMT_END while(0) +#endif + +#define GSK_RBTREE_INSERT_(top,type,is_red,set_is_red,parent,left,right,comparator, node,collision_node) \ +G_STMT_START{ \ + type _gsk_last = NULL; \ + type _gsk_at = (top); \ + gboolean _gsk_last_was_left = FALSE; \ + collision_node = NULL; \ + while (_gsk_at != NULL) \ + { \ + int _gsk_compare_rv; \ + _gsk_last = _gsk_at; \ + comparator(_gsk_at, (node), _gsk_compare_rv); \ + if (_gsk_compare_rv > 0) \ + { \ + _gsk_last_was_left = TRUE; \ + _gsk_at = _gsk_at->left; \ + } \ + else if (_gsk_compare_rv < 0) \ + { \ + _gsk_last_was_left = FALSE; \ + _gsk_at = _gsk_at->right; \ + } \ + else \ + break; \ + } \ + if (_gsk_at != NULL) \ + { \ + /* collision */ \ + collision_node = _gsk_at; \ + } \ + else if (_gsk_last == NULL) \ + { \ + /* only node in tree */ \ + top = (node); \ + set_is_red ((node), 0); \ + (node)->left = (node)->right = (node)->parent = NULL; \ + } \ + else \ + { \ + (node)->parent = _gsk_last; \ + (node)->left = (node)->right = NULL; \ + if (_gsk_last_was_left) \ + _gsk_last->left = (node); \ + else \ + _gsk_last->right = (node); \ + \ + /* fixup */ \ + _gsk_at = (node); \ + set_is_red (_gsk_at, 1); \ + while (top != _gsk_at && is_red(_gsk_at->parent)) \ + { \ + if (_gsk_at->parent == _gsk_at->parent->parent->left) \ + { \ + type _gsk_y = _gsk_at->parent->parent->right; \ + if (_gsk_y != NULL && is_red (_gsk_y)) \ + { \ + set_is_red (_gsk_at->parent, 0); \ + set_is_red (_gsk_y, 0); \ + set_is_red (_gsk_at->parent->parent, 1); \ + _gsk_at = _gsk_at->parent->parent; \ + } \ + else \ + { \ + if (_gsk_at == _gsk_at->parent->right) \ + { \ + _gsk_at = _gsk_at->parent; \ + GSK_RBTREE_ROTATE_LEFT (top,type,parent,left,right, _gsk_at);\ + } \ + set_is_red(_gsk_at->parent, 0); \ + set_is_red(_gsk_at->parent->parent, 1); \ + GSK_RBTREE_ROTATE_RIGHT (top,type,parent,left,right, \ + _gsk_at->parent->parent); \ + } \ + } \ + else \ + { \ + type _gsk_y = _gsk_at->parent->parent->left; \ + if (_gsk_y != NULL && is_red (_gsk_y)) \ + { \ + set_is_red (_gsk_at->parent, 0); \ + set_is_red (_gsk_y, 0); \ + set_is_red (_gsk_at->parent->parent, 1); \ + _gsk_at = _gsk_at->parent->parent; \ + } \ + else \ + { \ + if (_gsk_at == _gsk_at->parent->left) \ + { \ + _gsk_at = _gsk_at->parent; \ + GSK_RBTREE_ROTATE_RIGHT (top,type,parent,left,right, \ + _gsk_at); \ + } \ + set_is_red(_gsk_at->parent, 0); \ + set_is_red(_gsk_at->parent->parent, 1); \ + GSK_RBTREE_ROTATE_LEFT (top,type,parent,left,right, \ + _gsk_at->parent->parent); \ + } \ + } \ + } \ + set_is_red((top), 0); \ + } \ +}G_STMT_END + +#define GSK_RBTREE_REMOVE_(top,type,is_red,set_is_red,parent,left,right,comparator, node) \ +/* Algorithms:273. */ \ +G_STMT_START{ \ + type _gsk_rb_del_z = (node); \ + type _gsk_rb_del_x; \ + type _gsk_rb_del_y; \ + type _gsk_rb_del_nullpar = NULL; /* Used to emulate sentinel nodes */ \ + gboolean _gsk_rb_del_fixup; \ + if (_gsk_rb_del_z->left == NULL || _gsk_rb_del_z->right == NULL) \ + _gsk_rb_del_y = _gsk_rb_del_z; \ + else \ + { \ + GSK_RBTREE_NEXT_ (top,type,is_red,set_is_red,parent,left,right,comparator,\ + _gsk_rb_del_z, _gsk_rb_del_y); \ + } \ + _gsk_rb_del_x = _gsk_rb_del_y->left ? _gsk_rb_del_y->left \ + : _gsk_rb_del_y->right; \ + if (_gsk_rb_del_x) \ + _gsk_rb_del_x->parent = _gsk_rb_del_y->parent; \ + else \ + _gsk_rb_del_nullpar = _gsk_rb_del_y->parent; \ + if (!_gsk_rb_del_y->parent) \ + top = _gsk_rb_del_x; \ + else \ + { \ + if (_gsk_rb_del_y == _gsk_rb_del_y->parent->left) \ + _gsk_rb_del_y->parent->left = _gsk_rb_del_x; \ + else \ + _gsk_rb_del_y->parent->right = _gsk_rb_del_x; \ + } \ + _gsk_rb_del_fixup = !is_red(_gsk_rb_del_y); \ + if (_gsk_rb_del_y != _gsk_rb_del_z) \ + { \ + set_is_red(_gsk_rb_del_y, is_red(_gsk_rb_del_z)); \ + _gsk_rb_del_y->left = _gsk_rb_del_z->left; \ + _gsk_rb_del_y->right = _gsk_rb_del_z->right; \ + _gsk_rb_del_y->parent = _gsk_rb_del_z->parent; \ + if (_gsk_rb_del_y->parent) \ + { \ + if (_gsk_rb_del_y->parent->left == _gsk_rb_del_z) \ + _gsk_rb_del_y->parent->left = _gsk_rb_del_y; \ + else \ + _gsk_rb_del_y->parent->right = _gsk_rb_del_y; \ + } \ + else \ + top = _gsk_rb_del_y; \ + \ + if (_gsk_rb_del_y->left) \ + _gsk_rb_del_y->left->parent = _gsk_rb_del_y; \ + if (_gsk_rb_del_y->right) \ + _gsk_rb_del_y->right->parent = _gsk_rb_del_y; \ + if (_gsk_rb_del_nullpar == _gsk_rb_del_z) \ + _gsk_rb_del_nullpar = _gsk_rb_del_y; \ + } \ + if (_gsk_rb_del_fixup) \ + { \ + /* delete fixup (Algorithms, p 274) */ \ + while (_gsk_rb_del_x != top \ + && !(_gsk_rb_del_x != NULL && is_red (_gsk_rb_del_x))) \ + { \ + type _gsk_rb_del_xparent = _gsk_rb_del_x ? _gsk_rb_del_x->parent \ + : _gsk_rb_del_nullpar; \ + if (_gsk_rb_del_x == _gsk_rb_del_xparent->left) \ + { \ + type _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + if (_gsk_rb_del_w != NULL && is_red (_gsk_rb_del_w)) \ + { \ + set_is_red (_gsk_rb_del_w, 0); \ + set_is_red (_gsk_rb_del_xparent, 1); \ + GSK_RBTREE_ROTATE_LEFT (top,type,parent,left,right, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + } \ + if (!(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left)) \ + && !(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right))) \ + { \ + set_is_red (_gsk_rb_del_w, 1); \ + _gsk_rb_del_x = _gsk_rb_del_xparent; \ + } \ + else \ + { \ + if (!(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right)))\ + { \ + if (_gsk_rb_del_w->left) \ + set_is_red (_gsk_rb_del_w->left, 0); \ + set_is_red (_gsk_rb_del_w, 1); \ + GSK_RBTREE_ROTATE_RIGHT (top,type,parent,left,right, \ + _gsk_rb_del_w); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + } \ + set_is_red (_gsk_rb_del_w, is_red (_gsk_rb_del_xparent)); \ + set_is_red (_gsk_rb_del_xparent, 0); \ + set_is_red (_gsk_rb_del_w->right, 0); \ + GSK_RBTREE_ROTATE_LEFT (top,type,parent,left,right, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_x = top; \ + } \ + } \ + else \ + { \ + type _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + if (_gsk_rb_del_w && is_red (_gsk_rb_del_w)) \ + { \ + set_is_red (_gsk_rb_del_w, 0); \ + set_is_red (_gsk_rb_del_xparent, 1); \ + GSK_RBTREE_ROTATE_RIGHT (top,type,parent,left,right, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + } \ + if (!(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right)) \ + && !(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left))) \ + { \ + set_is_red (_gsk_rb_del_w, 1); \ + _gsk_rb_del_x = _gsk_rb_del_xparent; \ + } \ + else \ + { \ + if (!(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left))) \ + { \ + set_is_red (_gsk_rb_del_w->right, 0); \ + set_is_red (_gsk_rb_del_w, 1); \ + GSK_RBTREE_ROTATE_LEFT (top,type,parent,left,right, \ + _gsk_rb_del_w); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + } \ + set_is_red (_gsk_rb_del_w, is_red (_gsk_rb_del_xparent)); \ + set_is_red (_gsk_rb_del_xparent, 0); \ + if (_gsk_rb_del_w->left) \ + set_is_red (_gsk_rb_del_w->left, 0); \ + GSK_RBTREE_ROTATE_RIGHT (top,type,parent,left,right, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_x = top; \ + } \ + } \ + } \ + if (_gsk_rb_del_x != NULL) \ + set_is_red(_gsk_rb_del_x, 0); \ + } \ + _gsk_rb_del_z->left = NULL; \ + _gsk_rb_del_z->right = NULL; \ + _gsk_rb_del_z->parent = NULL; \ +}G_STMT_END + +#define GSK_RBTREE_LOOKUP_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, \ + key,key_comparator,out) \ +G_STMT_START{ \ + type _gsk_lookup_at = (top); \ + while (_gsk_lookup_at) \ + { \ + int _gsk_compare_rv; \ + key_comparator(key,_gsk_lookup_at,_gsk_compare_rv); \ + if (_gsk_compare_rv < 0) \ + _gsk_lookup_at = _gsk_lookup_at->left; \ + else if (_gsk_compare_rv > 0) \ + _gsk_lookup_at = _gsk_lookup_at->right; \ + else \ + break; \ + } \ + out = _gsk_lookup_at; \ +}G_STMT_END + /* see comments for 'SUPREMUM'; it is the same with the sense of the comparators + * and left,right reversed. */ +#define GSK_RBTREE_INFIMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, \ + key,key_comparator,out) \ +G_STMT_START{ \ + type _gsk_lookup_at = (top); \ + type _gsk_lookup_rv = NULL; \ + while (_gsk_lookup_at) \ + { \ + int _gsk_compare_rv; \ + key_comparator(key,_gsk_lookup_at,_gsk_compare_rv); \ + if (_gsk_compare_rv >= 0) \ + { \ + _gsk_lookup_rv = _gsk_lookup_at; \ + _gsk_lookup_at = _gsk_lookup_at->right; \ + } \ + else \ + _gsk_lookup_at = _gsk_lookup_at->left; \ + } \ + out = _gsk_lookup_rv; \ +}G_STMT_END +/* see introductory comments for a less mathematical + * definition. but what 'supremum' computes is: + * sup(tree, key) = min S(tree,key) or NULL if S(tree, key) + * is empty, where S(tree,key) = { t | t \in tree && t >= key }. + * The 'min' is determined by the 'comparator', + * and the 't >= key' is determined by the 'key_comparator'. + * But they must be consistent. + * + * so, here's a recursive description. suppose we wish to compute + * the supremum sup(a, key), where 'a' is the node in the tree shown: + * a + * / \ + * b c + * Is 'a >= key'? Then 'a' is in S(a, key), + * and hence sup(a,key) exists. But a "better" supremum, + * in terms of being the 'min' in the tree, + * may lie in 'b'. Nothing better may lie in 'c', since it + * is larger, and we are computing a minimum of S. + * + * But if 'a < key', then 'a' is not in S. hence 'b' and its children + * are not in S. Hence sup(a) = sup(c), including the possibility that + * sup(C) = NULL. + * + * Therefore, + * + * sup(b) if 'a >= key', and sub(b) exists, [case0] + * sup(a) = a if 'a >= key', and sub(b) does not exist, [case1] + * sup(c) if 'a < key' and sub(c) exists, [case2] + * NULL if 'a < key' and sub(c) does not exist. [case3] + * + * the non-recursive algo follows once you realize that it's just + * a tree descent, keeping track of the best candidate you've found. + * TODO: there's got to be a better way to describe it. + */ +#define GSK_RBTREE_SUPREMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, \ + key,key_comparator,out) \ +G_STMT_START{ \ + type _gsk_lookup_at = (top); \ + type _gsk_lookup_rv = NULL; \ + while (_gsk_lookup_at) \ + { \ + int _gsk_compare_rv; \ + key_comparator(key,_gsk_lookup_at,_gsk_compare_rv); \ + if (_gsk_compare_rv <= 0) \ + { \ + _gsk_lookup_rv = _gsk_lookup_at; \ + _gsk_lookup_at = _gsk_lookup_at->left; \ + } \ + else \ + _gsk_lookup_at = _gsk_lookup_at->right; \ + } \ + out = _gsk_lookup_rv; \ +}G_STMT_END +#define GSK_RBTREE_LOOKUP_(top,type,is_red,set_is_red,parent,left,right,comparator, key,out) \ + GSK_RBTREE_LOOKUP_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) +#define GSK_RBTREE_SUPREMUM_(top,type,is_red,set_is_red,parent,left,right,comparator, key,out) \ + GSK_RBTREE_SUPREMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) +#define GSK_RBTREE_INFIMUM_(top,type,is_red,set_is_red,parent,left,right,comparator, key,out) \ + GSK_RBTREE_INFIMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) + +/* these macros don't need the is_red/set_is_red macros, nor the comparator, + so omit them, to keep the lines a bit shorter. */ +#define GSK_RBTREE_ROTATE_RIGHT(top,type,parent,left,right, node) \ + GSK_RBTREE_ROTATE_LEFT(top,type,parent,right,left, node) +#define GSK_RBTREE_ROTATE_LEFT(top,type,parent,left,right, node) \ +G_STMT_START{ \ + type _gsk_rot_x = (node); \ + type _gsk_rot_y = _gsk_rot_x->right; \ + \ + _gsk_rot_x->right = _gsk_rot_y->left; \ + if (_gsk_rot_y->left) \ + _gsk_rot_y->left->parent = _gsk_rot_x; \ + _gsk_rot_y->parent = _gsk_rot_x->parent; \ + if (_gsk_rot_x->parent == NULL) \ + top = _gsk_rot_y; \ + else if (_gsk_rot_x == _gsk_rot_x->parent->left) \ + _gsk_rot_x->parent->left = _gsk_rot_y; \ + else \ + _gsk_rot_x->parent->right = _gsk_rot_y; \ + _gsk_rot_y->left = _gsk_rot_x; \ + _gsk_rot_x->parent = _gsk_rot_y; \ +}G_STMT_END + +/* iteration */ +#define GSK_RBTREE_NEXT_(top,type,is_red,set_is_red,parent,left,right,comparator, in, out) \ +G_STMT_START{ \ + type _gsk_next_at = (in); \ + g_assert (_gsk_next_at != NULL); \ + if (_gsk_next_at->right != NULL) \ + { \ + _gsk_next_at = _gsk_next_at->right; \ + while (_gsk_next_at->left != NULL) \ + _gsk_next_at = _gsk_next_at->left; \ + out = _gsk_next_at; \ + } \ + else \ + { \ + type _gsk_next_parent = (in)->parent; \ + while (_gsk_next_parent != NULL \ + && _gsk_next_at == _gsk_next_parent->right) \ + { \ + _gsk_next_at = _gsk_next_parent; \ + _gsk_next_parent = _gsk_next_parent->parent; \ + } \ + out = _gsk_next_parent; \ + } \ +}G_STMT_END + +/* prev is just next with left/right child reversed. */ +#define GSK_RBTREE_PREV_(top,type,is_red,set_is_red,parent,left,right,comparator, in, out) \ + GSK_RBTREE_NEXT_(top,type,is_red,set_is_red,parent,right,left,comparator, in, out) + +#define GSK_RBTREE_FIRST_(top,type,is_red,set_is_red,parent,left,right,comparator, out) \ +G_STMT_START{ \ + type _gsk_first_at = (top); \ + if (_gsk_first_at != NULL) \ + while (_gsk_first_at->left != NULL) \ + _gsk_first_at = _gsk_first_at->left; \ + out = _gsk_first_at; \ +}G_STMT_END +#define GSK_RBTREE_LAST_(top,type,is_red,set_is_red,parent,left,right,comparator, out) \ + GSK_RBTREE_FIRST_(top,type,is_red,set_is_red,parent,right,left,comparator, out) + + /* --- RBC-Tree --- */ +#define GSK_RBCTREE_INSERT(tree, node, collision_node) \ + GSK_RBCTREE_INSERT_(tree, node, collision_node) +#define GSK_RBCTREE_REMOVE(tree, node) \ + GSK_RBCTREE_REMOVE_(tree, node) +#define GSK_RBCTREE_LOOKUP(tree, key, out) \ + GSK_RBCTREE_LOOKUP_(tree, key, out) +#define GSK_RBCTREE_LOOKUP_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBCTREE_LOOKUP_COMPARATOR_(tree, key, key_comparator, out) + +#define GSK_RBCTREE_FIRST(tree, out) \ + GSK_RBCTREE_FIRST_(tree, out) +#define GSK_RBCTREE_LAST(tree, out) \ + GSK_RBCTREE_LAST_(tree, out) +#define GSK_RBCTREE_NEXT(tree, in, out) \ + GSK_RBCTREE_NEXT_(tree, in, out) +#define GSK_RBCTREE_PREV(tree, in, out) \ + GSK_RBCTREE_PREV_(tree, in, out) + +#define GSK_RBCTREE_SUPREMUM(tree, key, out) \ + GSK_RBCTREE_SUPREMUM_(tree, key, out) +#define GSK_RBCTREE_SUPREMUM_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBCTREE_SUPREMUM_COMPARATOR_(tree, key, key_comparator, out) +#define GSK_RBCTREE_INFIMUM(tree, key, out) \ + GSK_RBCTREE_INFIMUM_(tree, key, out) +#define GSK_RBCTREE_INFIMUM_COMPARATOR(tree, key, key_comparator, out) \ + GSK_RBCTREE_INFIMUM_COMPARATOR_(tree, key, key_comparator, out) + +#define GSK_RBCTREE_GET_BY_INDEX(tree, index, out) \ + GSK_RBCTREE_GET_BY_INDEX_(tree, index, out) +#define GSK_RBCTREE_GET_BY_INDEX_UNCHECKED(tree, index, out) \ + GSK_RBCTREE_GET_BY_INDEX_UNCHECKED_(tree, index, out) +#define GSK_RBCTREE_GET_NODE_INDEX(tree, node, index_out) \ + GSK_RBCTREE_GET_NODE_INDEX_(tree, node, index_out) + +#if 1 +#undef G_STMT_START +#define G_STMT_START do +#undef G_STMT_END +#define G_STMT_END while(0) +#endif + + +#define GSK_RBCTREE_INSERT_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, node,collision_node) \ +G_STMT_START{ \ + type _gsk_last = NULL; \ + type _gsk_at = (top); \ + gboolean _gsk_last_was_left = FALSE; \ + collision_node = NULL; \ + while (_gsk_at != NULL) \ + { \ + int _gsk_compare_rv; \ + _gsk_last = _gsk_at; \ + comparator(_gsk_at, (node), _gsk_compare_rv); \ + if (_gsk_compare_rv > 0) \ + { \ + _gsk_last_was_left = TRUE; \ + _gsk_at = _gsk_at->left; \ + } \ + else if (_gsk_compare_rv < 0) \ + { \ + _gsk_last_was_left = FALSE; \ + _gsk_at = _gsk_at->right; \ + } \ + else \ + break; \ + } \ + if (_gsk_at != NULL) \ + { \ + /* collision */ \ + collision_node = _gsk_at; \ + } \ + else if (_gsk_last == NULL) \ + { \ + /* only node in tree */ \ + top = (node); \ + set_is_red ((node), 0); \ + (node)->left = (node)->right = (node)->parent = NULL; \ + set_count ((node), 1); \ + } \ + else \ + { \ + (node)->parent = _gsk_last; \ + (node)->left = (node)->right = NULL; \ + if (_gsk_last_was_left) \ + _gsk_last->left = (node); \ + else \ + _gsk_last->right = (node); \ + \ + /* fixup counts */ \ + set_count ((node), 1); \ + for (_gsk_at = _gsk_last; _gsk_at; _gsk_at = _gsk_at->parent) \ + { \ + guint _gsk_new_count = get_count(_gsk_at) + 1; \ + set_count(_gsk_at, _gsk_new_count); \ + } \ + \ + /* fixup */ \ + _gsk_at = (node); \ + set_is_red (_gsk_at, 1); \ + while (_gsk_at->parent != NULL && is_red(_gsk_at->parent)) \ + { \ + if (_gsk_at->parent == _gsk_at->parent->parent->left) \ + { \ + type _gsk_y = _gsk_at->parent->parent->right; \ + if (_gsk_y != NULL && is_red (_gsk_y)) \ + { \ + set_is_red (_gsk_at->parent, 0); \ + set_is_red (_gsk_y, 0); \ + set_is_red (_gsk_at->parent->parent, 1); \ + _gsk_at = _gsk_at->parent->parent; \ + } \ + else \ + { \ + if (_gsk_at == _gsk_at->parent->right) \ + { \ + _gsk_at = _gsk_at->parent; \ + GSK_RBCTREE_ROTATE_LEFT (top,type,parent,left,right,get_count,set_count, _gsk_at);\ + } \ + set_is_red(_gsk_at->parent, 0); \ + set_is_red(_gsk_at->parent->parent, 1); \ + GSK_RBCTREE_ROTATE_RIGHT (top,type,parent,left,right,get_count,set_count,\ + _gsk_at->parent->parent); \ + } \ + } \ + else \ + { \ + type _gsk_y = _gsk_at->parent->parent->left; \ + if (_gsk_y != NULL && is_red (_gsk_y)) \ + { \ + set_is_red (_gsk_at->parent, 0); \ + set_is_red (_gsk_y, 0); \ + set_is_red (_gsk_at->parent->parent, 1); \ + _gsk_at = _gsk_at->parent->parent; \ + } \ + else \ + { \ + if (_gsk_at == _gsk_at->parent->left) \ + { \ + _gsk_at = _gsk_at->parent; \ + GSK_RBCTREE_ROTATE_RIGHT (top,type,parent,left,right,get_count,set_count,\ + _gsk_at); \ + } \ + set_is_red(_gsk_at->parent, 0); \ + set_is_red(_gsk_at->parent->parent, 1); \ + GSK_RBCTREE_ROTATE_LEFT (top,type,parent,left,right,get_count,set_count,\ + _gsk_at->parent->parent); \ + } \ + } \ + } \ + set_is_red((top), 0); \ + } \ +}G_STMT_END + +#define GSK_RBCTREE_REMOVE_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, node) \ +/* Algorithms:273. */ \ +G_STMT_START{ \ + type _gsk_rb_del_z = (node); \ + type _gsk_rb_del_x; \ + type _gsk_rb_del_y; \ + type _gsk_rb_del_nullpar = NULL; /* Used to emulate sentinel nodes */ \ + gboolean _gsk_rb_del_fixup; \ + if (_gsk_rb_del_z->left == NULL || _gsk_rb_del_z->right == NULL) \ + _gsk_rb_del_y = _gsk_rb_del_z; \ + else \ + { \ + GSK_RBTREE_NEXT_ (top,type,is_red,set_is_red,parent,left,right,comparator,\ + _gsk_rb_del_z, _gsk_rb_del_y); \ + } \ + _gsk_rb_del_x = _gsk_rb_del_y->left ? _gsk_rb_del_y->left \ + : _gsk_rb_del_y->right; \ + if (_gsk_rb_del_x) \ + _gsk_rb_del_x->parent = _gsk_rb_del_y->parent; \ + else \ + _gsk_rb_del_nullpar = _gsk_rb_del_y->parent; \ + if (!_gsk_rb_del_y->parent) \ + top = _gsk_rb_del_x; \ + else \ + { \ + if (_gsk_rb_del_y == _gsk_rb_del_y->parent->left) \ + _gsk_rb_del_y->parent->left = _gsk_rb_del_x; \ + else \ + _gsk_rb_del_y->parent->right = _gsk_rb_del_x; \ + _GSK_RBCTREE_FIX_COUNT_AND_UP(type,parent,left,right,get_count,set_count, _gsk_rb_del_y->parent);\ + } \ + _gsk_rb_del_fixup = !is_red(_gsk_rb_del_y); \ + if (_gsk_rb_del_y != _gsk_rb_del_z) \ + { \ + set_is_red(_gsk_rb_del_y, is_red(_gsk_rb_del_z)); \ + _gsk_rb_del_y->left = _gsk_rb_del_z->left; \ + _gsk_rb_del_y->right = _gsk_rb_del_z->right; \ + _gsk_rb_del_y->parent = _gsk_rb_del_z->parent; \ + if (_gsk_rb_del_y->parent) \ + { \ + if (_gsk_rb_del_y->parent->left == _gsk_rb_del_z) \ + _gsk_rb_del_y->parent->left = _gsk_rb_del_y; \ + else \ + _gsk_rb_del_y->parent->right = _gsk_rb_del_y; \ + } \ + else \ + { \ + top = _gsk_rb_del_y; \ + } \ + /* TODO: look at pictures to see if "_AND_UP" is necessary */ \ + _GSK_RBCTREE_FIX_COUNT_AND_UP(type,parent,left,right,get_count,set_count, _gsk_rb_del_y);\ + \ + if (_gsk_rb_del_y->left) \ + _gsk_rb_del_y->left->parent = _gsk_rb_del_y; \ + if (_gsk_rb_del_y->right) \ + _gsk_rb_del_y->right->parent = _gsk_rb_del_y; \ + if (_gsk_rb_del_nullpar == _gsk_rb_del_z) \ + _gsk_rb_del_nullpar = _gsk_rb_del_y; \ + } \ + if (_gsk_rb_del_fixup) \ + { \ + /* delete fixup (Algorithms, p 274) */ \ + while (_gsk_rb_del_x != top \ + && !(_gsk_rb_del_x != NULL && is_red (_gsk_rb_del_x))) \ + { \ + type _gsk_rb_del_xparent = _gsk_rb_del_x ? _gsk_rb_del_x->parent \ + : _gsk_rb_del_nullpar; \ + if (_gsk_rb_del_x == _gsk_rb_del_xparent->left) \ + { \ + type _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + if (_gsk_rb_del_w != NULL && is_red (_gsk_rb_del_w)) \ + { \ + set_is_red (_gsk_rb_del_w, 0); \ + set_is_red (_gsk_rb_del_xparent, 1); \ + GSK_RBCTREE_ROTATE_LEFT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + } \ + if (!(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left)) \ + && !(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right))) \ + { \ + set_is_red (_gsk_rb_del_w, 1); \ + _gsk_rb_del_x = _gsk_rb_del_xparent; \ + } \ + else \ + { \ + if (!(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right)))\ + { \ + if (_gsk_rb_del_w->left) \ + set_is_red (_gsk_rb_del_w->left, 0); \ + set_is_red (_gsk_rb_del_w, 1); \ + GSK_RBCTREE_ROTATE_RIGHT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_w); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->right; \ + } \ + set_is_red (_gsk_rb_del_w, is_red (_gsk_rb_del_xparent)); \ + set_is_red (_gsk_rb_del_xparent, 0); \ + set_is_red (_gsk_rb_del_w->right, 0); \ + GSK_RBCTREE_ROTATE_LEFT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_x = top; \ + } \ + } \ + else \ + { \ + type _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + if (_gsk_rb_del_w && is_red (_gsk_rb_del_w)) \ + { \ + set_is_red (_gsk_rb_del_w, 0); \ + set_is_red (_gsk_rb_del_xparent, 1); \ + GSK_RBCTREE_ROTATE_RIGHT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + } \ + if (!(_gsk_rb_del_w->right && is_red (_gsk_rb_del_w->right)) \ + && !(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left))) \ + { \ + set_is_red (_gsk_rb_del_w, 1); \ + _gsk_rb_del_x = _gsk_rb_del_xparent; \ + } \ + else \ + { \ + if (!(_gsk_rb_del_w->left && is_red (_gsk_rb_del_w->left))) \ + { \ + set_is_red (_gsk_rb_del_w->right, 0); \ + set_is_red (_gsk_rb_del_w, 1); \ + GSK_RBCTREE_ROTATE_LEFT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_w); \ + _gsk_rb_del_w = _gsk_rb_del_xparent->left; \ + } \ + set_is_red (_gsk_rb_del_w, is_red (_gsk_rb_del_xparent)); \ + set_is_red (_gsk_rb_del_xparent, 0); \ + if (_gsk_rb_del_w->left) \ + set_is_red (_gsk_rb_del_w->left, 0); \ + GSK_RBCTREE_ROTATE_RIGHT (top,type,parent,left,right,get_count,set_count, \ + _gsk_rb_del_xparent); \ + _gsk_rb_del_x = top; \ + } \ + } \ + } \ + if (_gsk_rb_del_x != NULL) \ + set_is_red(_gsk_rb_del_x, 0); \ + } \ + _gsk_rb_del_z->left = NULL; \ + _gsk_rb_del_z->right = NULL; \ + _gsk_rb_del_z->parent = NULL; \ +}G_STMT_END + +#define GSK_RBCTREE_LOOKUP_COMPARATOR_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, \ + key,key_comparator,out) \ + GSK_RBTREE_LOOKUP_COMPARATOR_(top,type,is_red,set_count,parent,left,right,comparator, key,key_comparator,out) +#define GSK_RBCTREE_INFIMUM_COMPARATOR_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, \ + key,key_comparator,out) \ + GSK_RBTREE_INFIMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,key_comparator,out) +#define GSK_RBCTREE_SUPREMUM_COMPARATOR_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, \ + key,key_comparator,out) \ + GSK_RBTREE_SUPREMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,key_comparator,out) +#define GSK_RBCTREE_LOOKUP_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, key,out) \ + GSK_RBTREE_LOOKUP_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) +#define GSK_RBCTREE_SUPREMUM_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, key,out) \ + GSK_RBTREE_SUPREMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) +#define GSK_RBCTREE_INFIMUM_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, key,out) \ + GSK_RBTREE_INFIMUM_COMPARATOR_(top,type,is_red,set_is_red,parent,left,right,comparator, key,comparator,out) +#define GSK_RBCTREE_NEXT_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, in, out) \ + GSK_RBTREE_NEXT_(top,type,is_red,set_is_red,parent,left,right,comparator, in, out) +#define GSK_RBCTREE_PREV_(top,type,is_red,set_is_red,parent,left,right,comparator, in, out) \ + GSK_RBTREE_PREV_(top,type,is_red,set_is_red,parent,left,right,comparator, in, out) +#define GSK_RBCTREE_FIRST_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, out) \ + GSK_RBTREE_FIRST_(top,type,is_red,set_is_red,parent,left,right,comparator, out) +#define GSK_RBCTREE_LAST_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, out) \ + GSK_RBTREE_LAST_(top,type,is_red,set_is_red,parent,left,right,comparator, out) + +#define GSK_RBCTREE_GET_BY_INDEX_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, index,out) \ + G_STMT_START{ \ + if (top == NULL || (index) >= get_count(top)) \ + out = NULL; \ + else \ + GSK_RBCTREE_GET_BY_INDEX_UNCHECKED_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, index,out);\ + }G_STMT_END +#define GSK_RBCTREE_GET_BY_INDEX_UNCHECKED_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, index,out) \ + G_STMT_START{ \ + type _gsk_at = (top); \ + guint _gsk_index = (index); \ + for (;;) \ + { \ + guint _gsk_left_size = _gsk_at->left ? get_count(_gsk_at->left) : 0; \ + if (_gsk_index < _gsk_left_size) \ + _gsk_at = _gsk_at->left; \ + else if (_gsk_index == _gsk_left_size) \ + break; \ + else \ + { \ + _gsk_index -= (_gsk_left_size + 1); \ + _gsk_at = _gsk_at->right; \ + } \ + } \ + out = _gsk_at; \ + }G_STMT_END + +#define GSK_RBCTREE_GET_NODE_INDEX_(top,type,is_red,set_is_red,get_count,set_count,parent,left,right,comparator, node,index_out) \ + G_STMT_START{ \ + type _gsk_at = (node); \ + guint _gsk_rv = _gsk_at->left ? get_count (_gsk_at->left) : 0; \ + while (_gsk_at->parent != NULL) \ + { \ + if (_gsk_at->parent->left == _gsk_at) \ + ; \ + else \ + { \ + if (_gsk_at->parent->left) \ + _gsk_rv += get_count((_gsk_at->parent->left)); \ + _gsk_rv += 1; \ + } \ + _gsk_at = _gsk_at->parent; \ + } \ + index_out = _gsk_rv; \ + }G_STMT_END + +#define GSK_RBCTREE_ROTATE_RIGHT(top,type,parent,left,right,get_count,set_count, node) \ + GSK_RBCTREE_ROTATE_LEFT(top,type,parent,right,left,get_count,set_count, node) +#define GSK_RBCTREE_ROTATE_LEFT(top,type,parent,left,right,get_count,set_count, node) \ +G_STMT_START{ \ + type _gsk_rot_x = (node); \ + type _gsk_rot_y = _gsk_rot_x->right; \ + \ + _gsk_rot_x->right = _gsk_rot_y->left; \ + if (_gsk_rot_y->left) \ + _gsk_rot_y->left->parent = _gsk_rot_x; \ + _gsk_rot_y->parent = _gsk_rot_x->parent; \ + if (_gsk_rot_x->parent == NULL) \ + top = _gsk_rot_y; \ + else if (_gsk_rot_x == _gsk_rot_x->parent->left) \ + _gsk_rot_x->parent->left = _gsk_rot_y; \ + else \ + _gsk_rot_x->parent->right = _gsk_rot_y; \ + _gsk_rot_y->left = _gsk_rot_x; \ + _gsk_rot_x->parent = _gsk_rot_y; \ + \ + /* fixup counts. */ \ + /* to re-derive this here's what rotate_left(x) does pictorially: */ \ + /* x y */ \ + /* / \ / \ */ \ + /* a y == rotate_left(x) ==> x c */ \ + /* / \ / \ */ \ + /* b c a b */ \ + /* */ \ + /* so: n0 = get_count(x) (==a+b+c+2) */ \ + /* n1 = get_count(c) (c may be null) */ \ + /* n2 = n0 - n1 - 1; */ \ + /* set_count(x, n2) */ \ + /* set_count(y, n0) */ \ + /* c is _gsk_rot_y->right at this point */ \ + /* */ \ + /* equivalently: */ \ + /* y->count = x->count; */ \ + /* x->count -= c->count + 1 */ \ + { \ + guint _gsk_rot_n0 = get_count(_gsk_rot_x); \ + guint _gsk_rot_n1 = _gsk_rot_y->right ? get_count(_gsk_rot_y->right) : 0; \ + guint _gsk_rot_n2 = _gsk_rot_n0 - _gsk_rot_n1 - 1; \ + set_count (_gsk_rot_x, _gsk_rot_n2); \ + set_count (_gsk_rot_y, _gsk_rot_n0); \ + } \ +}G_STMT_END + + /* utility: recompute node's count, based on count of its children */ +#define _GSK_RBCTREE_FIX_COUNT(type,parent,left,right,get_count,set_count, node) \ +G_STMT_START{ \ + guint _gsk_fixcount_count = 1; \ + if ((node)->left != NULL) \ + _gsk_fixcount_count += get_count((node)->left); \ + if ((node)->right != NULL) \ + _gsk_fixcount_count += get_count((node)->right); \ + set_count((node), _gsk_fixcount_count); \ +}G_STMT_END + + /* utility: recompute node's count, based on count of its children */ +#define _GSK_RBCTREE_FIX_COUNT_AND_UP(type,parent,left,right,get_count,set_count, node) \ +G_STMT_START{ \ + type _tmp_fix_count_up; \ + for (_tmp_fix_count_up = (node); \ + _tmp_fix_count_up != NULL; \ + _tmp_fix_count_up = _tmp_fix_count_up->parent) \ + _GSK_RBCTREE_FIX_COUNT (type,parent,left,right,get_count,set_count, _tmp_fix_count_up);\ +}G_STMT_END + +#endif diff --git a/src/google/protobuf-c/protobuf-c-dispatch.c b/src/google/protobuf-c/protobuf-c-dispatch.c index de93bca..ccf1806 100644 --- a/src/google/protobuf-c/protobuf-c-dispatch.c +++ b/src/google/protobuf-c/protobuf-c-dispatch.c @@ -1,4 +1,5 @@ #include "protobuf-c-dispatch.h" +#include "gskrbtreemacros.h" #define ALLOC_WITH_ALLOCATOR(allocator, size) ((allocator)->alloc ((allocator)->allocator_data, (size))) #define FREE_WITH_ALLOCATOR(allocator, ptr) ((allocator)->free ((allocator)->allocator_data, (ptr))) @@ -8,7 +9,7 @@ typedef struct _Callback Callback; struct _Callback { - ProtobufC_DispatchCallback func; + ProtobufCDispatchCallback func; void *data; }; @@ -23,16 +24,50 @@ struct _FDMap typedef struct _RealDispatch RealDispatch; struct _RealDispatch { - ProtobufC_Dispatch base; + ProtobufCDispatch base; Callback *callbacks; /* parallels notifies_desired */ size_t notifies_desired_alloced; size_t changes_alloced; FDMap *fd_map; /* map indexed by fd */ size_t fd_map_size; /* number of elements of fd_map */ + + ProtobufCDispatchTimer *timer_tree; }; +struct _ProtobufCDispatchTimer +{ + /* the actual timeout time */ + unsigned long timeout_secs; + unsigned timeout_usecs; + + /* red-black tree stuff */ + ProtobufCDispatchTimer *left, *right, *parent; + protobuf_c_boolean is_red; + + /* user callback */ + ProtobufCDispatchTimerFunc func; + void *func_data; +}; + +/* Define the tree of timers, as per gskrbtreemacros.h */ +#define TIMER_GET_IS_RED(n) ((n)->is_red) +#define TIMER_SET_IS_RED(n,v) ((n)->is_red = (v)) +#define TIMERS_COMPARE(a,b, rv) \ + if (a->timeout_secs < b->timeout_secs) rv = -1; \ + else if (a->timeout_secs > b->timeout_secs) rv = 1; \ + else if (a->timeout_usecs < b->timeout_usecs) rv = -1; \ + else if (a->timeout_usecs > b->timeout_usecs) rv = 1; \ + else if (a < b) rv = -1; \ + else if (a > b) rv = 1; \ + else rv = 0; +#define GET_TIMER_TREE(d) \ + (d)->timer_tree, ProtobufCDispatchTimer *, \ + TIMER_GET_IS_RED, TIMER_SET_IS_RED, \ + parent, left, right, \ + TIMERS_COMPARE + /* Create or destroy a Dispatch */ -ProtobufC_Dispatch *protobuf_c_dispatch_new (void) +ProtobufCDispatch *protobuf_c_dispatch_new (void) { RealDispatch *rv = ALLOC (sizeof (RealDispatch)); rv->base.n_changes = 0; @@ -48,7 +83,7 @@ ProtobufC_Dispatch *protobuf_c_dispatch_new (void) } void -protobuf_c_dispatch_free(ProtobufC_Dispatch *dispatch) +protobuf_c_dispatch_free(ProtobufCDispatch *dispatch) { RealDispatch *d = (RealDispatch *) dispatch; FREE (d->base.notifies_desired); @@ -152,10 +187,10 @@ deallocate_notify_desired_index (RealDispatch *d, /* Registering file-descriptors to watch. */ void -protobuf_c_dispatch_watch_fd (ProtobufC_Dispatch *dispatch, +protobuf_c_dispatch_watch_fd (ProtobufCDispatch *dispatch, int fd, unsigned events, - ProtobufC_DispatchCallback callback, + ProtobufCDispatchCallback callback, void *callback_data) { RealDispatch *d = (RealDispatch *) dispatch; @@ -199,7 +234,7 @@ protobuf_c_dispatch_watch_fd (ProtobufC_Dispatch *dispatch, } void -protobuf_c_dispatch_close_fd (ProtobufC_Dispatch *dispatch, +protobuf_c_dispatch_close_fd (ProtobufCDispatch *dispatch, int fd) { protobuf_c_dispatch_fd_closed (dispatch, fd); @@ -207,7 +242,7 @@ protobuf_c_dispatch_close_fd (ProtobufC_Dispatch *dispatch, } void -protobuf_c_dispatch_fd_closed(ProtobufC_Dispatch *dispatch, +protobuf_c_dispatch_fd_closed(ProtobufCDispatch *dispatch, int fd) { unsigned f = fd; @@ -220,8 +255,16 @@ protobuf_c_dispatch_fd_closed(ProtobufC_Dispatch *dispatch, deallocate_notify_desired_index (d, f); } +static void +free_timer (ProtobufCDispatchTimer *timer) +{ + RealDispatch *d = timer->dispatch; + timer->right = d->recycled_timeouts; + d->recycled_timeouts = timer; +} + void -protobuf_c_dispatch_dispatch (ProtobufC_Dispatch *dispatch, +protobuf_c_dispatch_dispatch (ProtobufCDispatch *dispatch, size_t n_notifies, ProtobufC_FDNotify *notifies) { @@ -249,10 +292,42 @@ protobuf_c_dispatch_dispatch (ProtobufC_Dispatch *dispatch, d->callbacks[nd_ind].func (fd, events, d->callbacks[nd_ind].data); } } + + + /* handle timers */ + gettimeofday (&tv, NULL); + while (d->timer_tree != NULL) + { + ProtobufCDispatchTimer *min_timer; + GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min_timer); + if (min_timer.timeout_secs < tv.tv_secs + || (min_timer.timeout_secs == tv.tv_secs + && min_timer.timeout_usecs <= tv.tv_usecs)) + { + ProtobufCDispatchTimerFunc func = min_timer->func; + void *func_data = min_timer->func_data; + GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), min_timer); + /* Set to NULL as a way to tell protobuf_c_dispatch_remove_timer() + that we are in the middle of notifying */ + min_timer->func = NULL; + min_timer->func_data = NULL; + func (&d->base, func_data); + free_timer (min_timer); + } + else + { + d->base.has_timeout = 1; + d->base.timeout_secs = min_timer->timeout_secs; + d->base.timeout_usecs = min_timer->timeout_usecs; + break; + } + } + if (d->timer_tree == NULL) + d->base.has_timeout = 0; } void -protobuf_c_dispatch_clear_changes (ProtobufC_Dispatch *dispatch) +protobuf_c_dispatch_clear_changes (ProtobufCDispatch *dispatch) { RealDispatch *d = (RealDispatch *) dispatch; unsigned i; @@ -264,10 +339,8 @@ protobuf_c_dispatch_clear_changes (ProtobufC_Dispatch *dispatch) dispatch->n_changes = 0; } - void -protobuf_c_dispatch_run (ProtobufC_Dispatch *dispatch, - int timeout) +protobuf_c_dispatch_run (ProtobufCDispatch *dispatch) { struct pollfd *fds; void *to_free = NULL; @@ -319,3 +392,58 @@ protobuf_c_dispatch_run (ProtobufC_Dispatch *dispatch, if (to_free2) FREE (to_free2); } + +ProtobufCDispatchTimer * +protobuf_c_dispatch_add_timer(ProtobufCDispatch *dispatch, + unsigned timeout_secs, + unsigned timeout_usecs, + ProtobufCDispatchTimerFunc func, + void *func_data) +{ + RealDispatch *d = (RealDispatch *) dispatch; + protobuf_c_assert (func != NULL); + if (d->recycled_timeouts != NULL) + { + rv = d->recycled_timeouts; + d->recycled_timeouts = rv->right; + } + else + { + rv = d->allocator->alloc (d->allocator, sizeof (ProtobufCDispatchTimer)); + } + rv->timeout_secs = timeout_secs; + rv->timeout_usecs = timeout_usecs; + rv->func = func; + rv->func_data = func_data; + rv->dispatch = d; + GSK_RBTREE_INSERT (GET_TIMER_TREE (d), rv, conflict); + return rv; +} + +void protobuf_c_dispatch_remove_timer (ProtobufCDispatchTimer *timer) +{ + protobuf_c_boolean may_be_first; + RealDispatch *d = timer->dispatch; + + /* ignore mid-notify removal */ + if (timer->func == NULL) + return; + + may_be_first = d->base.timeout_usecs == timer->timeout_usecs + && d->base.timeout_secs == timer->timeout_secs; + + GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), timer); + + if (may_be_first) + { + if (d->timer_tree == NULL) + d->base.has_timeout = 0; + else + { + ProtobufCDispatchTimer *min; + GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min); + d->timeout_secs = min->timeout_secs; + d->timeout_usecs = min->timeout_usecs; + } + } +} diff --git a/src/google/protobuf-c/protobuf-c-dispatch.h b/src/google/protobuf-c/protobuf-c-dispatch.h index 18172e4..136388e 100644 --- a/src/google/protobuf-c/protobuf-c-dispatch.h +++ b/src/google/protobuf-c/protobuf-c-dispatch.h @@ -5,27 +5,45 @@ typedef enum } ProtobufC_Events; /* Create or destroy a Dispatch */ -ProtobufC_Dispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator); -void protobuf_c_dispatch_free(ProtobufC_Dispatch *dispatch); +ProtobufCDispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator); +void protobuf_c_dispatch_free(ProtobufCDispatch *dispatch); -ProtobufCAllocator *protobuf_c_dispatch_peek_allocator (ProtobufC_Dispatch *); +ProtobufCAllocator *protobuf_c_dispatch_peek_allocator (ProtobufCDispatch *); -typedef void (*ProtobufC_DispatchCallback) (int fd, +typedef void (*ProtobufCDispatchCallback) (int fd, unsigned events, void *callback_data); /* Registering file-descriptors to watch. */ -void protobuf_c_dispatch_watch_fd (ProtobufC_Dispatch *dispatch, +void protobuf_c_dispatch_watch_fd (ProtobufCDispatch *dispatch, int fd, unsigned events, - ProtobufC_DispatchCallback callback, + ProtobufCDispatchCallback callback, void *callback_data); -void protobuf_c_dispatch_close_fd (ProtobufC_Dispatch *dispatch, +void protobuf_c_dispatch_close_fd (ProtobufCDispatch *dispatch, int fd); -void protobuf_c_dispatch_fd_closed(ProtobufC_Dispatch *dispatch, +void protobuf_c_dispatch_fd_closed(ProtobufCDispatch *dispatch, int fd); +/* Timers */ +typedef void (*ProtobufCDispatchTimerFunc) (ProtobufCDispatch *dispatch, + void *func_data); +ProtobufCDispatchTimer * + protobuf_c_dispatch_add_timer(ProtobufCDispatch *dispatch, + unsigned timeout_secs, + unsigned timeout_usecs, + ProtobufCDispatchTimerFunc func, + void *func_data); +void protobuf_c_dispatch_remove_timer (ProtobufCDispatchTimer *); +/* Idle functions */ +typedef void (*ProtobufCDispatchIdleFunc) (ProtobufCDispatch *dispatch, + void *func_data); +ProtobufCDispatchIdle * + protobuf_c_dispatch_add_idle (ProtobufCDispatch *dispatch, + ProtobufCDispatchIdleFunc func, + void *func_data); +void protobuf_c_dispatch_remove_idle (ProtobufCDispatchIdle *); /* --- API for use in standalone application --- */ /* Where you are happy just to run poll(2). */ @@ -34,15 +52,14 @@ void protobuf_c_dispatch_fd_closed(ProtobufC_Dispatch *dispatch, * Run one main-loop iteration, using poll(2) (or some system-level event system); * 'timeout' is in milliseconds, -1 for no timeout. */ -void protobuf_c_dispatch_run (ProtobufC_Dispatch *dispatch, - int timeout); +void protobuf_c_dispatch_run (ProtobufCDispatch *dispatch); /* --- API for those who want to embed a dispatch into their own main-loop --- */ -void protobuf_c_dispatch_dispatch (ProtobufC_Dispatch *dispatch, +void protobuf_c_dispatch_dispatch (ProtobufCDispatch *dispatch, size_t n_notifies, ProtobufC_FDNotify *notifies); -void protobuf_c_dispatch_clear_changes (ProtobufC_Dispatch *); +void protobuf_c_dispatch_clear_changes (ProtobufCDispatch *); #ifdef WIN32 typedef SOCKET ProtobufC_FD; @@ -56,7 +73,7 @@ typedef struct { } ProtobufC_FDNotify; -struct _ProtobufC_Dispatch +struct _ProtobufCDispatch { /* changes to the events you are interested in. */ size_t n_changes; @@ -66,6 +83,15 @@ struct _ProtobufC_Dispatch size_t n_notifies_desired; ProtobufC_FDNotify *notifies_desired; + /* number of milliseconds to wait if no events occur */ + protobuf_c_boolean has_timeout; + unsigned long timeout_secs; + unsigned timeout_usecs; + + /* true if there is an idle function, in which case polling with + timeout 0 is appropriate */ + protobuf_c_boolean has_idle; + /* private data follows */ }; diff --git a/src/google/protobuf-c/protobuf-c-rpc.c b/src/google/protobuf-c/protobuf-c-rpc.c index 4211abd..b43ab43 100644 --- a/src/google/protobuf-c/protobuf-c-rpc.c +++ b/src/google/protobuf-c/protobuf-c-rpc.c @@ -4,10 +4,12 @@ typedef struct _ProtobufC_RPC_Client ProtobufC_RPC_Client; typedef enum { + PROTOBUF_C_CLIENT_STATE_INIT, PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP, PROTOBUF_C_CLIENT_STATE_CONNECTING, PROTOBUF_C_CLIENT_STATE_CONNECTED, - PROTOBUF_C_CLIENT_STATE_FAILED_WAITING + PROTOBUF_C_CLIENT_STATE_FAILED_WAITING, + PROTOBUF_C_CLIENT_STATE_FAILED /* if no autoretry */ } ProtobufC_ClientState; struct _ProtobufC_RPC_Client @@ -21,36 +23,41 @@ struct _ProtobufC_RPC_Client char *name; ProtobufC_ClientState client_state; ProtobufC_FD fd; + protobuf_c_boolean autoretry; + unsigned autoretry_millis; + union { + struct { + ProtobufCDispatch_Idle *idle; + } init; + struct { + ProtobufCDispatch_Timer *timer; + } failed_waiting; + }; }; +void +handle_init_idle (ProtobufCDispatch * -ProtobufCService * -protobuf_c_rpc_client_new (ProtobufC_RPC_Options *options, - const ProtobufCServiceDescriptor *descriptor) +ProtobufCService *protobuf_c_rpc_client_new (ProtobufC_RPC_AddressType type, + const char *name, + const ProtobufCServiceDescriptor *descriptor, + ProtobufCDispatch *dispatch); { - ProtobufCAllocator *allocator = options->allocator; ProtobufCDispatch *dispatch = options->dispatch ? options->dispatch : protobuf_c_dispatch_default (); - ProtobufC - ... + ProtobufCAllocator *allocator = protobuf_c_dispatch_peek_allocator (dispatch); + ProtobufC_RPC_Client *rv = allocator->alloc (allocator, sizeof (ProtobufC_RPC_Client)); + rv->base.descriptor = descriptor; + rv->base.invoke = invoke_client_rpc; + rv->base.destroy = destroy_client_rpc; + protobuf_c_data_buffer_init (&rv->incoming); + protobuf_c_data_buffer_init (&rv->outgoing); + rv->allocator = allocator; + rv->dispatch = dispatch; + rv->address_type = type; + rv->name = strcpy (allocator->alloc (allocator, strlen (name) + 1), name); + rv->client_state = PROTOBUF_C_CLIENT_STATE_INIT; + rv->fd = -1; + rv->info.init = protobuf_c_dispatch_add_idle (dispatch, handle_init_idle, rv); + return &rv->base; } - -void protobuf_c_rpc_server_new (ProtobufC_RPC_Options *options, - ProtobufCService *service); - unsigned port, - const ProtobufCServiceDescriptor *descriptor, - ProtobufCAllocator *allocator, - ProtobufCDispatch *dispatch); -ProtobufCService *protobuf_c_rpc_client_new_local(const char *socket_name, - const ProtobufCServiceDescriptor *descripto, - ProtobufCAllocator *allocator, - ProtobufCDispatch *dispatch); -void protobuf_c_rpc_server_new_tcp (ProtobufCService *service, - unsigned port, - ProtobufCAllocator *allocator, - ProtobufCDispatch *dispatch); -void protobuf_c_rpc_server_new_local(ProtobufCService *service, - const char *socket_nam, - ProtobufCAllocator *allocator, - ProtobufCDispatch *dispatch); - ----