1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1997 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 
25 #include "splay.h"
26 
27 /*
28  * This macro compares two node keys i and j and returns:
29  *
30  *  negative value: when i is smaller than j
31  *  zero          : when i is equal   to   j
32  *  positive when : when i is larger  than j
33  */
34 #define compare(i,j) Curl_splaycomparekeys((i),(j))
35 
36 /*
37  * Splay using the key i (which may or may not be in the tree.) The starting
38  * root is t.
39  */
Curl_splay(struct timeval i,struct Curl_tree * t)40 struct Curl_tree *Curl_splay(struct timeval i,
41                              struct Curl_tree *t)
42 {
43   struct Curl_tree N, *l, *r, *y;
44   long comp;
45 
46   if(t == NULL)
47     return t;
48   N.smaller = N.larger = NULL;
49   l = r = &N;
50 
51   for(;;) {
52     comp = compare(i, t->key);
53     if(comp < 0) {
54       if(t->smaller == NULL)
55         break;
56       if(compare(i, t->smaller->key) < 0) {
57         y = t->smaller;                           /* rotate smaller */
58         t->smaller = y->larger;
59         y->larger = t;
60         t = y;
61         if(t->smaller == NULL)
62           break;
63       }
64       r->smaller = t;                               /* link smaller */
65       r = t;
66       t = t->smaller;
67     }
68     else if(comp > 0) {
69       if(t->larger == NULL)
70         break;
71       if(compare(i, t->larger->key) > 0) {
72         y = t->larger;                          /* rotate larger */
73         t->larger = y->smaller;
74         y->smaller = t;
75         t = y;
76         if(t->larger == NULL)
77           break;
78       }
79       l->larger = t;                              /* link larger */
80       l = t;
81       t = t->larger;
82     }
83     else
84       break;
85   }
86 
87   l->larger = t->smaller;                                /* assemble */
88   r->smaller = t->larger;
89   t->smaller = N.larger;
90   t->larger = N.smaller;
91 
92   return t;
93 }
94 
95 /* Insert key i into the tree t.  Return a pointer to the resulting tree or
96  * NULL if something went wrong.
97  *
98  * @unittest: 1309
99  */
Curl_splayinsert(struct timeval i,struct Curl_tree * t,struct Curl_tree * node)100 struct Curl_tree *Curl_splayinsert(struct timeval i,
101                                    struct Curl_tree *t,
102                                    struct Curl_tree *node)
103 {
104   static const struct timeval KEY_NOTUSED = {-1, -1}; /* will *NEVER* appear */
105 
106   if(node == NULL)
107     return t;
108 
109   if(t != NULL) {
110     t = Curl_splay(i, t);
111     if(compare(i, t->key)==0) {
112       /* There already exists a node in the tree with the very same key. Build
113          a linked list of nodes. We make the new 'node' struct the new master
114          node and make the previous node the first one in the 'same' list. */
115 
116       node->same = t;
117       node->key = i;
118       node->smaller = t->smaller;
119       node->larger = t->larger;
120 
121       t->smaller = node; /* in the sub node for this same key, we use the
122                             smaller pointer to point back to the master
123                             node */
124 
125       t->key = KEY_NOTUSED; /* and we set the key in the sub node to NOTUSED
126                                to quickly identify this node as a subnode */
127 
128       return node; /* new root node */
129     }
130   }
131 
132   if(t == NULL) {
133     node->smaller = node->larger = NULL;
134   }
135   else if(compare(i, t->key) < 0) {
136     node->smaller = t->smaller;
137     node->larger = t;
138     t->smaller = NULL;
139 
140   }
141   else {
142     node->larger = t->larger;
143     node->smaller = t;
144     t->larger = NULL;
145   }
146   node->key = i;
147 
148   node->same = NULL; /* no identical node (yet) */
149   return node;
150 }
151 
152 /* Finds and deletes the best-fit node from the tree. Return a pointer to the
153    resulting tree.  best-fit means the node with the given or lower key */
Curl_splaygetbest(struct timeval i,struct Curl_tree * t,struct Curl_tree ** removed)154 struct Curl_tree *Curl_splaygetbest(struct timeval i,
155                                     struct Curl_tree *t,
156                                     struct Curl_tree **removed)
157 {
158   struct Curl_tree *x;
159 
160   if(!t) {
161     *removed = NULL; /* none removed since there was no root */
162     return NULL;
163   }
164 
165   t = Curl_splay(i, t);
166   if(compare(i, t->key) < 0) {
167     /* too big node, try the smaller chain */
168     if(t->smaller)
169       t=Curl_splay(t->smaller->key, t);
170     else {
171       /* fail */
172       *removed = NULL;
173       return t;
174     }
175   }
176 
177   if(compare(i, t->key) >= 0) {               /* found it */
178     /* FIRST! Check if there is a list with identical keys */
179     x = t->same;
180     if(x) {
181       /* there is, pick one from the list */
182 
183       /* 'x' is the new root node */
184 
185       x->key = t->key;
186       x->larger = t->larger;
187       x->smaller = t->smaller;
188 
189       *removed = t;
190       return x; /* new root */
191     }
192 
193     if(t->smaller == NULL) {
194       x = t->larger;
195     }
196     else {
197       x = Curl_splay(i, t->smaller);
198       x->larger = t->larger;
199     }
200     *removed = t;
201 
202     return x;
203   }
204   else {
205     *removed = NULL; /* no match */
206     return t;        /* It wasn't there */
207   }
208 }
209 
210 
211 /* Deletes the very node we point out from the tree if it's there. Stores a
212  * pointer to the new resulting tree in 'newroot'.
213  *
214  * Returns zero on success and non-zero on errors! TODO: document error codes.
215  * When returning error, it does not touch the 'newroot' pointer.
216  *
217  * NOTE: when the last node of the tree is removed, there's no tree left so
218  * 'newroot' will be made to point to NULL.
219  *
220  * @unittest: 1309
221  */
Curl_splayremovebyaddr(struct Curl_tree * t,struct Curl_tree * removenode,struct Curl_tree ** newroot)222 int Curl_splayremovebyaddr(struct Curl_tree *t,
223                            struct Curl_tree *removenode,
224                            struct Curl_tree **newroot)
225 {
226   static const struct timeval KEY_NOTUSED = {-1, -1}; /* will *NEVER* appear */
227   struct Curl_tree *x;
228 
229   if(!t || !removenode)
230     return 1;
231 
232   if(compare(KEY_NOTUSED, removenode->key) == 0) {
233     /* Key set to NOTUSED means it is a subnode within a 'same' linked list
234        and thus we can unlink it easily. The 'smaller' link of a subnode
235        links to the parent node. */
236     if(removenode->smaller == NULL)
237       return 3;
238 
239     removenode->smaller->same = removenode->same;
240     if(removenode->same)
241       removenode->same->smaller = removenode->smaller;
242 
243     /* Ensures that double-remove gets caught. */
244     removenode->smaller = NULL;
245 
246     /* voila, we're done! */
247     *newroot = t; /* return the same root */
248     return 0;
249   }
250 
251   t = Curl_splay(removenode->key, t);
252 
253   /* First make sure that we got the same root node as the one we want
254      to remove, as otherwise we might be trying to remove a node that
255      isn't actually in the tree.
256 
257      We cannot just compare the keys here as a double remove in quick
258      succession of a node with key != KEY_NOTUSED && same != NULL
259      could return the same key but a different node. */
260   if(t != removenode)
261     return 2;
262 
263   /* Check if there is a list with identical sizes, as then we're trying to
264      remove the root node of a list of nodes with identical keys. */
265   x = t->same;
266   if(x) {
267     /* 'x' is the new root node, we just make it use the root node's
268        smaller/larger links */
269 
270     x->key = t->key;
271     x->larger = t->larger;
272     x->smaller = t->smaller;
273   }
274   else {
275     /* Remove the root node */
276     if(t->smaller == NULL)
277       x = t->larger;
278     else {
279       x = Curl_splay(removenode->key, t->smaller);
280       x->larger = t->larger;
281     }
282   }
283 
284   *newroot = x; /* store new root pointer */
285 
286   return 0;
287 }
288 
289