• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "Python.h"
2 #include "rotatingtree.h"
3 
4 #define KEY_LOWER_THAN(key1, key2)  ((char*)(key1) < (char*)(key2))
5 
6 /* The randombits() function below is a fast-and-dirty generator that
7  * is probably irregular enough for our purposes.  Note that it's biased:
8  * I think that ones are slightly more probable than zeroes.  It's not
9  * important here, though.
10  */
11 
12 static unsigned int random_value = 1;
13 static unsigned int random_stream = 0;
14 static PyMutex random_mutex = {0};
15 
16 static int
randombits(int bits)17 randombits(int bits)
18 {
19     int result;
20     PyMutex_Lock(&random_mutex);
21     if (random_stream < (1U << bits)) {
22         random_value *= 1082527;
23         random_stream = random_value;
24     }
25     result = random_stream & ((1<<bits)-1);
26     random_stream >>= bits;
27     PyMutex_Unlock(&random_mutex);
28     return result;
29 }
30 
31 
32 /* Insert a new node into the tree.
33    (*root) is modified to point to the new root. */
34 void
RotatingTree_Add(rotating_node_t ** root,rotating_node_t * node)35 RotatingTree_Add(rotating_node_t **root, rotating_node_t *node)
36 {
37     while (*root != NULL) {
38         if (KEY_LOWER_THAN(node->key, (*root)->key))
39             root = &((*root)->left);
40         else
41             root = &((*root)->right);
42     }
43     node->left = NULL;
44     node->right = NULL;
45     *root = node;
46 }
47 
48 /* Locate the node with the given key.  This is the most complicated
49    function because it occasionally rebalances the tree to move the
50    resulting node closer to the root. */
51 rotating_node_t *
RotatingTree_Get(rotating_node_t ** root,void * key)52 RotatingTree_Get(rotating_node_t **root, void *key)
53 {
54     if (randombits(3) != 4) {
55         /* Fast path, no rebalancing */
56         rotating_node_t *node = *root;
57         while (node != NULL) {
58             if (node->key == key)
59                 return node;
60             if (KEY_LOWER_THAN(key, node->key))
61                 node = node->left;
62             else
63                 node = node->right;
64         }
65         return NULL;
66     }
67     else {
68         rotating_node_t **pnode = root;
69         rotating_node_t *node = *pnode;
70         rotating_node_t *next;
71         int rotate;
72         if (node == NULL)
73             return NULL;
74         while (1) {
75             if (node->key == key)
76                 return node;
77             rotate = !randombits(1);
78             if (KEY_LOWER_THAN(key, node->key)) {
79                 next = node->left;
80                 if (next == NULL)
81                     return NULL;
82                 if (rotate) {
83                     node->left = next->right;
84                     next->right = node;
85                     *pnode = next;
86                 }
87                 else
88                     pnode = &(node->left);
89             }
90             else {
91                 next = node->right;
92                 if (next == NULL)
93                     return NULL;
94                 if (rotate) {
95                     node->right = next->left;
96                     next->left = node;
97                     *pnode = next;
98                 }
99                 else
100                     pnode = &(node->right);
101             }
102             node = next;
103         }
104     }
105 }
106 
107 /* Enumerate all nodes in the tree.  The callback enumfn() should return
108    zero to continue the enumeration, or non-zero to interrupt it.
109    A non-zero value is directly returned by RotatingTree_Enum(). */
110 int
RotatingTree_Enum(rotating_node_t * root,rotating_tree_enum_fn enumfn,void * arg)111 RotatingTree_Enum(rotating_node_t *root, rotating_tree_enum_fn enumfn,
112                   void *arg)
113 {
114     int result;
115     rotating_node_t *node;
116     while (root != NULL) {
117         result = RotatingTree_Enum(root->left, enumfn, arg);
118         if (result != 0) return result;
119         node = root->right;
120         result = enumfn(root, arg);
121         if (result != 0) return result;
122         root = node;
123     }
124     return 0;
125 }
126