1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 https://curl.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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#include "splay.h" 28 29/* 30 * This macro compares two node keys i and j and returns: 31 * 32 * negative value: when i is smaller than j 33 * zero : when i is equal to j 34 * positive when : when i is larger than j 35 */ 36#define compare(i,j) Curl_splaycomparekeys((i),(j)) 37 38/* 39 * Splay using the key i (which may or may not be in the tree.) The starting 40 * root is t. 41 */ 42struct Curl_tree *Curl_splay(struct curltime i, 43 struct Curl_tree *t) 44{ 45 struct Curl_tree N, *l, *r, *y; 46 47 if(!t) 48 return t; 49 N.smaller = N.larger = NULL; 50 l = r = &N; 51 52 for(;;) { 53 long comp = compare(i, t->key); 54 if(comp < 0) { 55 if(!t->smaller) 56 break; 57 if(compare(i, t->smaller->key) < 0) { 58 y = t->smaller; /* rotate smaller */ 59 t->smaller = y->larger; 60 y->larger = t; 61 t = y; 62 if(!t->smaller) 63 break; 64 } 65 r->smaller = t; /* link smaller */ 66 r = t; 67 t = t->smaller; 68 } 69 else if(comp > 0) { 70 if(!t->larger) 71 break; 72 if(compare(i, t->larger->key) > 0) { 73 y = t->larger; /* rotate larger */ 74 t->larger = y->smaller; 75 y->smaller = t; 76 t = y; 77 if(!t->larger) 78 break; 79 } 80 l->larger = t; /* link larger */ 81 l = t; 82 t = t->larger; 83 } 84 else 85 break; 86 } 87 88 l->larger = t->smaller; /* assemble */ 89 r->smaller = t->larger; 90 t->smaller = N.larger; 91 t->larger = N.smaller; 92 93 return t; 94} 95 96/* Insert key i into the tree t. Return a pointer to the resulting tree or 97 * NULL if something went wrong. 98 * 99 * @unittest: 1309 100 */ 101struct Curl_tree *Curl_splayinsert(struct curltime i, 102 struct Curl_tree *t, 103 struct Curl_tree *node) 104{ 105 static const struct curltime KEY_NOTUSED = { 106 ~0, -1 107 }; /* will *NEVER* appear */ 108 109 if(!node) 110 return t; 111 112 if(t) { 113 t = Curl_splay(i, t); 114 if(compare(i, t->key) == 0) { 115 /* There already exists a node in the tree with the very same key. Build 116 a doubly-linked circular list of nodes. We add the new 'node' struct 117 to the end of this list. */ 118 119 node->key = KEY_NOTUSED; /* we set the key in the sub node to NOTUSED 120 to quickly identify this node as a subnode */ 121 node->samen = t; 122 node->samep = t->samep; 123 t->samep->samen = node; 124 t->samep = node; 125 126 return t; /* the root node always stays the same */ 127 } 128 } 129 130 if(!t) { 131 node->smaller = node->larger = NULL; 132 } 133 else if(compare(i, t->key) < 0) { 134 node->smaller = t->smaller; 135 node->larger = t; 136 t->smaller = NULL; 137 138 } 139 else { 140 node->larger = t->larger; 141 node->smaller = t; 142 t->larger = NULL; 143 } 144 node->key = i; 145 146 /* no identical nodes (yet), we are the only one in the list of nodes */ 147 node->samen = node; 148 node->samep = node; 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 smallest node if it is not larger than 154 the key */ 155struct Curl_tree *Curl_splaygetbest(struct curltime i, 156 struct Curl_tree *t, 157 struct Curl_tree **removed) 158{ 159 static const struct curltime tv_zero = {0, 0}; 160 struct Curl_tree *x; 161 162 if(!t) { 163 *removed = NULL; /* none removed since there was no root */ 164 return NULL; 165 } 166 167 /* find smallest */ 168 t = Curl_splay(tv_zero, t); 169 if(compare(i, t->key) < 0) { 170 /* even the smallest is too big */ 171 *removed = NULL; 172 return t; 173 } 174 175 /* FIRST! Check if there is a list with identical keys */ 176 x = t->samen; 177 if(x != t) { 178 /* there is, pick one from the list */ 179 180 /* 'x' is the new root node */ 181 182 x->key = t->key; 183 x->larger = t->larger; 184 x->smaller = t->smaller; 185 x->samep = t->samep; 186 t->samep->samen = x; 187 188 *removed = t; 189 return x; /* new root */ 190 } 191 192 /* we splayed the tree to the smallest element, there is no smaller */ 193 x = t->larger; 194 *removed = t; 195 196 return x; 197} 198 199 200/* Deletes the very node we point out from the tree if it's there. Stores a 201 * pointer to the new resulting tree in 'newroot'. 202 * 203 * Returns zero on success and non-zero on errors! 204 * When returning error, it does not touch the 'newroot' pointer. 205 * 206 * NOTE: when the last node of the tree is removed, there's no tree left so 207 * 'newroot' will be made to point to NULL. 208 * 209 * @unittest: 1309 210 */ 211int Curl_splayremove(struct Curl_tree *t, 212 struct Curl_tree *removenode, 213 struct Curl_tree **newroot) 214{ 215 static const struct curltime KEY_NOTUSED = { 216 ~0, -1 217 }; /* will *NEVER* appear */ 218 struct Curl_tree *x; 219 220 if(!t || !removenode) 221 return 1; 222 223 if(compare(KEY_NOTUSED, removenode->key) == 0) { 224 /* Key set to NOTUSED means it is a subnode within a 'same' linked list 225 and thus we can unlink it easily. */ 226 if(removenode->samen == removenode) 227 /* A non-subnode should never be set to KEY_NOTUSED */ 228 return 3; 229 230 removenode->samep->samen = removenode->samen; 231 removenode->samen->samep = removenode->samep; 232 233 /* Ensures that double-remove gets caught. */ 234 removenode->samen = removenode; 235 236 *newroot = t; /* return the same root */ 237 return 0; 238 } 239 240 t = Curl_splay(removenode->key, t); 241 242 /* First make sure that we got the same root node as the one we want 243 to remove, as otherwise we might be trying to remove a node that 244 isn't actually in the tree. 245 246 We cannot just compare the keys here as a double remove in quick 247 succession of a node with key != KEY_NOTUSED && same != NULL 248 could return the same key but a different node. */ 249 if(t != removenode) 250 return 2; 251 252 /* Check if there is a list with identical sizes, as then we're trying to 253 remove the root node of a list of nodes with identical keys. */ 254 x = t->samen; 255 if(x != t) { 256 /* 'x' is the new root node, we just make it use the root node's 257 smaller/larger links */ 258 259 x->key = t->key; 260 x->larger = t->larger; 261 x->smaller = t->smaller; 262 x->samep = t->samep; 263 t->samep->samen = x; 264 } 265 else { 266 /* Remove the root node */ 267 if(!t->smaller) 268 x = t->larger; 269 else { 270 x = Curl_splay(removenode->key, t->smaller); 271 x->larger = t->larger; 272 } 273 } 274 275 *newroot = x; /* store new root pointer */ 276 277 return 0; 278} 279