1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1997 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: splay.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
29 * This macro compares two node keys i and j and returns:
31 * negative value: when i is smaller than j
32 * zero : when i is equal to j
33 * positive when : when i is larger than j
35 #define compare(i,j) Curl_splaycomparekeys((i),(j))
38 * Splay using the key i (which may or may not be in the tree.) The starting
41 struct Curl_tree
*Curl_splay(struct timeval i
,
44 struct Curl_tree N
, *l
, *r
, *y
;
49 N
.smaller
= N
.larger
= NULL
;
53 comp
= compare(i
, t
->key
);
55 if(t
->smaller
== NULL
)
57 if(compare(i
, t
->smaller
->key
) < 0) {
58 y
= t
->smaller
; /* rotate smaller */
59 t
->smaller
= y
->larger
;
62 if(t
->smaller
== NULL
)
65 r
->smaller
= t
; /* link smaller */
72 if(compare(i
, t
->larger
->key
) > 0) {
73 y
= t
->larger
; /* rotate larger */
74 t
->larger
= y
->smaller
;
80 l
->larger
= t
; /* link larger */
88 l
->larger
= t
->smaller
; /* assemble */
89 r
->smaller
= t
->larger
;
90 t
->smaller
= N
.larger
;
91 t
->larger
= N
.smaller
;
96 /* Insert key i into the tree t. Return a pointer to the resulting tree or
97 NULL if something went wrong. */
98 struct Curl_tree
*Curl_splayinsert(struct timeval i
,
100 struct Curl_tree
*node
)
102 static struct timeval KEY_NOTUSED
= {-1,-1}; /* key that will *NEVER* appear */
109 if(compare(i
, t
->key
)==0) {
110 /* There already exists a node in the tree with the very same key. Build
111 a linked list of nodes. We make the new 'node' struct the new master
112 node and make the previous node the first one in the 'same' list. */
116 node
->smaller
= t
->smaller
;
117 node
->larger
= t
->larger
;
119 t
->smaller
= node
; /* in the sub node for this same key, we use the
120 smaller pointer to point back to the master
123 t
->key
= KEY_NOTUSED
; /* and we set the key in the sub node to NOTUSED
124 to quickly identify this node as a subnode */
126 return node
; /* new root node */
131 node
->smaller
= node
->larger
= NULL
;
133 else if(compare(i
, t
->key
) < 0) {
134 node
->smaller
= t
->smaller
;
140 node
->larger
= t
->larger
;
146 node
->same
= NULL
; /* no identical node (yet) */
151 /* Deletes 'i' from the tree if it's there (with an exact match). Returns a
152 pointer to the resulting tree.
154 Function not used in libcurl.
156 struct Curl_tree
*Curl_splayremove(struct timeval i
,
158 struct Curl_tree
**removed
)
162 *removed
= NULL
; /* default to no removed */
168 if(compare(i
, t
->key
) == 0) { /* found it */
170 /* FIRST! Check if there is a list with identical sizes */
171 if((x
= t
->same
) != NULL
) {
172 /* there is, pick one from the list */
174 /* 'x' is the new root node */
177 x
->larger
= t
->larger
;
178 x
->smaller
= t
->smaller
;
181 return x
; /* new root */
184 if(t
->smaller
== NULL
) {
188 x
= Curl_splay(i
, t
->smaller
);
189 x
->larger
= t
->larger
;
196 return t
; /* It wasn't there */
200 /* Finds and deletes the best-fit node from the tree. Return a pointer to the
201 resulting tree. best-fit means the node with the given or lower key */
202 struct Curl_tree
*Curl_splaygetbest(struct timeval i
,
204 struct Curl_tree
**removed
)
209 *removed
= NULL
; /* none removed since there was no root */
214 if(compare(i
, t
->key
) < 0) {
215 /* too big node, try the smaller chain */
217 t
=Curl_splay(t
->smaller
->key
, t
);
225 if(compare(i
, t
->key
) >= 0) { /* found it */
226 /* FIRST! Check if there is a list with identical keys */
229 /* there is, pick one from the list */
231 /* 'x' is the new root node */
234 x
->larger
= t
->larger
;
235 x
->smaller
= t
->smaller
;
238 return x
; /* new root */
241 if(t
->smaller
== NULL
) {
245 x
= Curl_splay(i
, t
->smaller
);
246 x
->larger
= t
->larger
;
253 *removed
= NULL
; /* no match */
254 return t
; /* It wasn't there */
259 /* Deletes the very node we point out from the tree if it's there. Stores a
260 pointer to the new resulting tree in 'newroot'.
262 Returns zero on success and non-zero on errors! TODO: document error codes.
263 When returning error, it does not touch the 'newroot' pointer.
265 NOTE: when the last node of the tree is removed, there's no tree left so
266 'newroot' will be made to point to NULL.
268 int Curl_splayremovebyaddr(struct Curl_tree
*t
,
269 struct Curl_tree
*removenode
,
270 struct Curl_tree
**newroot
)
272 static struct timeval KEY_NOTUSED
= {-1,-1}; /* key that will *NEVER* appear */
275 if(!t
|| !removenode
)
278 if(compare(KEY_NOTUSED
, removenode
->key
) == 0) {
279 /* Key set to NOTUSED means it is a subnode within a 'same' linked list
280 and thus we can unlink it easily. The 'smaller' link of a subnode
281 links to the parent node. */
282 if(removenode
->smaller
== NULL
)
285 removenode
->smaller
->same
= removenode
->same
;
287 removenode
->same
->smaller
= removenode
->smaller
;
289 /* Ensures that double-remove gets caught. */
290 removenode
->smaller
= NULL
;
292 /* voila, we're done! */
293 *newroot
= t
; /* return the same root */
297 t
= Curl_splay(removenode
->key
, t
);
299 /* First make sure that we got the same root node as the one we want
300 to remove, as otherwise we might be trying to remove a node that
301 isn't actually in the tree.
303 We cannot just compare the keys here as a double remove in quick
304 succession of a node with key != KEY_NOTUSED && same != NULL
305 could return the same key but a different node. */
309 /* Check if there is a list with identical sizes, as then we're trying to
310 remove the root node of a list of nodes with identical keys. */
313 /* 'x' is the new root node, we just make it use the root node's
314 smaller/larger links */
317 x
->larger
= t
->larger
;
318 x
->smaller
= t
->smaller
;
321 /* Remove the root node */
322 if(t
->smaller
== NULL
)
325 x
= Curl_splay(removenode
->key
, t
->smaller
);
326 x
->larger
= t
->larger
;
330 *newroot
= x
; /* store new root pointer */
337 void Curl_splayprint(struct Curl_tree
* t
, int d
, char output
)
339 struct Curl_tree
*node
;
345 Curl_splayprint(t
->larger
, d
+1, output
);
352 printf("%ld[%d]", (long)t
->key
.tv_usec
, i
);
354 printf("%ld.%ld[%d]", (long)t
->key
.tv_sec
, (long)t
->key
.tv_usec
, i
);
358 for(count
=0, node
= t
->same
; node
; node
= node
->same
, count
++)
363 printf(" [%d more]\n", count
);
368 Curl_splayprint(t
->smaller
, d
+1, output
);
378 /* A sample use of these functions. Start with the empty tree, insert some
379 stuff into it, and then delete it */
380 int main(int argc
, argv_item_t argv
[])
382 struct Curl_tree
*root
, *t
;
387 static const long sizes
[]={
388 50, 60, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, 300,
389 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200,
390 300, 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120,
391 200, 300, 220, 80, 90};
393 root
= NULL
; /* the empty tree */
395 for (i
= 0; i
< MAX
; i
++) {
397 ptrs
[i
] = t
= (struct Curl_tree
*)malloc(sizeof(struct Curl_tree
));
401 key
.tv_usec
= sizes
[i
];
403 key
.tv_usec
= (541*i
)%1023;
408 t
->payload
= (void *)key
.tv_usec
; /* for simplicity */
410 puts("out of memory!");
413 root
= Curl_splayinsert(key
, root
, t
);
418 Curl_splayprint(root
, 0, 1);
422 for (i
= 0; i
< MAX
; i
++) {
425 printf("Tree look:\n");
426 Curl_splayprint(root
, 0, 1);
427 printf("remove pointer %d, payload %ld\n", rem
,
428 (long)((struct Curl_tree
*)ptrs
[rem
])->payload
);
429 rc
= Curl_splayremovebyaddr(root
, (struct Curl_tree
*)ptrs
[rem
], &root
);
432 printf("remove %d failed!\n", rem
);
439 #endif /* TEST_SPLAY */