1 /* 2 * PROJECT: Skiplist implementation for the ReactOS Project 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: All implemented functions operating on the Skiplist 5 * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org) 6 */ 7 8 #include <intrin.h> 9 #include <windef.h> 10 #include <winbase.h> 11 #include "skiplist.h" 12 13 /** 14 * @name _GetRandomLevel 15 * 16 * Returns a random level for the next element to be inserted. 17 * This level is geometrically distributed for p = 0.5, so perfectly suitable for an efficient Skiplist implementation. 18 * 19 * @return 20 * A value between 0 and SKIPLIST_LEVELS - 1. 21 */ 22 static __inline CHAR 23 _GetRandomLevel() 24 { 25 // Using a simple fixed seed and the Park-Miller Lehmer Minimal Standard Random Number Generator gives an acceptable distribution for our "random" levels. 26 static DWORD dwRandom = 1; 27 28 DWORD dwLevel = 0; 29 DWORD dwShifted; 30 31 // Generate 31 uniformly distributed pseudo-random bits using the Park-Miller Lehmer Minimal Standard Random Number Generator. 32 dwRandom = (DWORD)(((ULONGLONG)dwRandom * 48271UL) % 2147483647UL); 33 34 // Shift out (31 - SKIPLIST_LEVELS) bits to the right to have no more than SKIPLIST_LEVELS bits set. 35 dwShifted = dwRandom >> (31 - SKIPLIST_LEVELS); 36 37 // BitScanForward doesn't operate on a zero input value. 38 if (dwShifted) 39 { 40 // BitScanForward sets dwLevel to the zero-based position of the first set bit (from LSB to MSB). 41 // This makes dwLevel a geometrically distributed value between 0 and SKIPLIST_LEVELS - 1 for p = 0.5. 42 BitScanForward(&dwLevel, dwShifted); 43 } 44 45 // dwLevel can't have a value higher than 30 this way, so a CHAR is more than enough. 46 return (CHAR)dwLevel; 47 } 48 49 /** 50 * @name _InsertElementSkiplistWithInformation 51 * 52 * Determines a level for the new element and inserts it at the given position in the Skiplist. 53 * This function is internally used by the Skiplist insertion functions. 54 * 55 * @param Skiplist 56 * Pointer to the SKIPLIST structure to operate on. 57 * 58 * @param Element 59 * The element to insert. 60 * 61 * @param pUpdate 62 * Array containing the last nodes before our new node on each level. 63 * 64 * @param dwDistance 65 * Array containing the distance to the last node before our new node on each level. 66 * 67 * @return 68 * TRUE if the node was successfully inserted, FALSE if no memory could be allocated for it. 69 */ 70 static BOOL 71 _InsertElementSkiplistWithInformation(PSKIPLIST Skiplist, PVOID Element, PSKIPLIST_NODE* pUpdate, DWORD* dwDistance) 72 { 73 CHAR chNewLevel; 74 CHAR i; 75 PSKIPLIST_NODE pNode; 76 77 // Get the highest level, on which the node shall be inserted. 78 chNewLevel = _GetRandomLevel(); 79 80 // Check if the new level is higher than the maximum level we currently have in the Skiplist. 81 if (chNewLevel > Skiplist->MaximumLevel) 82 { 83 // It is, so we also need to insert the new node right after the Head node on some levels. 84 // These are the levels higher than the current maximum level up to the new level. 85 // We also need to set the distance of these elements to the new node count to account for the calculations below. 86 for (i = Skiplist->MaximumLevel + 1; i <= chNewLevel; i++) 87 { 88 pUpdate[i] = &Skiplist->Head; 89 pUpdate[i]->Distance[i] = Skiplist->NodeCount + 1; 90 } 91 92 // The new level is the new maximum level of the entire Skiplist. 93 Skiplist->MaximumLevel = chNewLevel; 94 } 95 96 // Finally create our new Skiplist node. 97 pNode = Skiplist->AllocateRoutine(sizeof(SKIPLIST_NODE)); 98 if (!pNode) 99 return FALSE; 100 101 pNode->Element = Element; 102 103 // For each used level, insert us between the saved node for this level and its current next node. 104 for (i = 0; i <= chNewLevel; i++) 105 { 106 pNode->Next[i] = pUpdate[i]->Next[i]; 107 pUpdate[i]->Next[i] = pNode; 108 109 // We know the walked distance in this level: dwDistance[i] 110 // We also know the element index of the new node: dwDistance[0] 111 // The new node's distance is now the walked distance in this level plus the difference between the saved node's distance and the element index. 112 pNode->Distance[i] = dwDistance[i] + (pUpdate[i]->Distance[i] - dwDistance[0]); 113 114 // The saved node's distance is now the element index plus one (to account for the added node) minus the walked distance in this level. 115 pUpdate[i]->Distance[i] = dwDistance[0] + 1 - dwDistance[i]; 116 } 117 118 // For all levels above the new node's level, we need to increment the distance, because we've just added our new node. 119 for (i = chNewLevel + 1; i <= Skiplist->MaximumLevel; i++) 120 ++pUpdate[i]->Distance[i]; 121 122 // We've successfully added a node :) 123 ++Skiplist->NodeCount; 124 return TRUE; 125 } 126 127 /** 128 * @name DeleteElementSkiplist 129 * 130 * Deletes an element from the Skiplist. The efficiency of this operation is O(log N) on average. 131 * 132 * Instead of the result of a LookupElementSkiplist call, it's sufficient to provide a dummy element with just enough information for your CompareRoutine. 133 * A lookup for the element to be deleted needs to be performed in any case. 134 * 135 * @param Skiplist 136 * Pointer to the SKIPLIST structure to operate on. 137 * 138 * @param Element 139 * Information about the element to be deleted. 140 * 141 * @return 142 * Returns the deleted element or NULL if no such element was found. 143 * You can then free memory for the deleted element if necessary. 144 */ 145 PVOID 146 DeleteElementSkiplist(PSKIPLIST Skiplist, PVOID Element) 147 { 148 CHAR i; 149 PSKIPLIST_NODE pLastComparedNode = NULL; 150 PSKIPLIST_NODE pNode = &Skiplist->Head; 151 PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS]; 152 PVOID pReturnValue; 153 154 // Find the node on every currently used level, after which the node to be deleted must follow. 155 // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found. 156 for (i = Skiplist->MaximumLevel + 1; --i >= 0;) 157 { 158 while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0) 159 pNode = pNode->Next[i]; 160 161 // Reduce the number of comparisons by not comparing the same node on different levels twice. 162 pLastComparedNode = pNode->Next[i]; 163 pUpdate[i] = pNode; 164 } 165 166 // Check if the node we're looking for has been found. 167 pNode = pNode->Next[0]; 168 if (!pNode || Skiplist->CompareRoutine(pNode->Element, Element) != 0) 169 { 170 // It hasn't been found, so there's nothing to delete. 171 return NULL; 172 } 173 174 // Beginning at the lowest level, remove the node from each level of the list and merge distances. 175 // We can stop as soon as we found the first level that doesn't contain the node. 176 for (i = 0; i <= Skiplist->MaximumLevel && pUpdate[i]->Next[i] == pNode; i++) 177 { 178 pUpdate[i]->Distance[i] += pNode->Distance[i] - 1; 179 pUpdate[i]->Next[i] = pNode->Next[i]; 180 } 181 182 // Now decrement the distance of the corresponding node in levels higher than the deleted node's level to account for the deleted node. 183 while (i <= Skiplist->MaximumLevel) 184 { 185 --pUpdate[i]->Distance[i]; 186 i++; 187 } 188 189 // Return the deleted element (so the caller can free it if necessary) and free the memory for the node itself (allocated by us). 190 pReturnValue = pNode->Element; 191 Skiplist->FreeRoutine(pNode); 192 193 // Find all levels which now contain no more nodes and reduce the maximum level of the entire Skiplist accordingly. 194 while (Skiplist->MaximumLevel > 0 && !Skiplist->Head.Next[Skiplist->MaximumLevel]) 195 --Skiplist->MaximumLevel; 196 197 // We've successfully deleted the node :) 198 --Skiplist->NodeCount; 199 return pReturnValue; 200 } 201 202 /** 203 * @name InitializeSkiplist 204 * 205 * Initializes a new Skiplist structure. 206 * 207 * @param Skiplist 208 * Pointer to the SKIPLIST structure to operate on. 209 * 210 * @param AllocateRoutine 211 * Pointer to a SKIPLIST_ALLOCATE_ROUTINE for allocating memory for new Skiplist nodes. 212 * 213 * @param CompareRoutine 214 * Pointer to a SKIPLIST_COMPARE_ROUTINE for comparing two elements of the Skiplist. 215 * 216 * @param FreeRoutine 217 * Pointer to a SKIPLIST_FREE_ROUTINE for freeing memory allocated with AllocateRoutine. 218 */ 219 void 220 InitializeSkiplist(PSKIPLIST Skiplist, PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine, PSKIPLIST_COMPARE_ROUTINE CompareRoutine, PSKIPLIST_FREE_ROUTINE FreeRoutine) 221 { 222 // Store the routines. 223 Skiplist->AllocateRoutine = AllocateRoutine; 224 Skiplist->CompareRoutine = CompareRoutine; 225 Skiplist->FreeRoutine = FreeRoutine; 226 227 // Initialize the members and pointers. 228 // The Distance array is only used when a node is non-NULL, so it doesn't need initialization. 229 Skiplist->MaximumLevel = 0; 230 Skiplist->NodeCount = 0; 231 ZeroMemory(Skiplist->Head.Next, sizeof(Skiplist->Head.Next)); 232 } 233 234 /** 235 * @name InsertElementSkiplist 236 * 237 * Inserts a new element into the Skiplist. The efficiency of this operation is O(log N) on average. 238 * Uses CompareRoutine to find the right position for the insertion. 239 * 240 * @param Skiplist 241 * Pointer to the SKIPLIST structure to operate on. 242 * 243 * @param Element 244 * The element to insert. 245 * 246 * @return 247 * TRUE if the node was successfully inserted, FALSE if it already exists or no memory could be allocated for it. 248 */ 249 BOOL 250 InsertElementSkiplist(PSKIPLIST Skiplist, PVOID Element) 251 { 252 CHAR i; 253 DWORD dwDistance[SKIPLIST_LEVELS + 1] = { 0 }; 254 PSKIPLIST_NODE pLastComparedNode = NULL; 255 PSKIPLIST_NODE pNode = &Skiplist->Head; 256 PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS]; 257 258 // Find the node on every currently used level, after which the new node needs to be inserted. 259 // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found. 260 for (i = Skiplist->MaximumLevel + 1; --i >= 0;) 261 { 262 // When entering this level, we begin at the distance of the last level we walked through. 263 dwDistance[i] = dwDistance[i + 1]; 264 265 while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0) 266 { 267 // Save our position in every level when walking through the nodes. 268 dwDistance[i] += pNode->Distance[i]; 269 270 // Advance to the next node. 271 pNode = pNode->Next[i]; 272 } 273 274 // Reduce the number of comparisons by not comparing the same node on different levels twice. 275 pLastComparedNode = pNode->Next[i]; 276 pUpdate[i] = pNode; 277 } 278 279 // Check if the node already exists in the Skiplist. 280 pNode = pNode->Next[0]; 281 if (pNode && Skiplist->CompareRoutine(pNode->Element, Element) == 0) 282 { 283 // All elements to be inserted mustn't exist in the list, so we see this as a failure. 284 return FALSE; 285 } 286 287 // The rest of the procedure is the same for both insertion functions. 288 return _InsertElementSkiplistWithInformation(Skiplist, Element, pUpdate, dwDistance); 289 } 290 291 /** 292 * @name InsertTailElementSkiplist 293 * 294 * Inserts a new element at the end of the Skiplist. The efficiency of this operation is O(log N) on average. 295 * In contrast to InsertElementSkiplist, this function is more efficient by not calling CompareRoutine at all and always inserting the element at the end. 296 * You're responsible for calling this function only when you can guarantee that InsertElementSkiplist would also insert the element at the end. 297 * 298 * @param Skiplist 299 * Pointer to the SKIPLIST structure to operate on. 300 * 301 * @param Element 302 * The element to insert. 303 * 304 * @return 305 * TRUE if the node was successfully inserted, FALSE if it already exists or no memory could be allocated for it. 306 */ 307 BOOL 308 InsertTailElementSkiplist(PSKIPLIST Skiplist, PVOID Element) 309 { 310 CHAR i; 311 DWORD dwDistance[SKIPLIST_LEVELS + 1] = { 0 }; 312 PSKIPLIST_NODE pNode = &Skiplist->Head; 313 PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS]; 314 315 // Find the last node on every currently used level, after which the new node needs to be inserted. 316 // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found. 317 for (i = Skiplist->MaximumLevel + 1; --i >= 0;) 318 { 319 // When entering this level, we begin at the distance of the last level we walked through. 320 dwDistance[i] = dwDistance[i + 1]; 321 322 while (pNode->Next[i]) 323 { 324 // Save our position in every level when walking through the nodes. 325 dwDistance[i] += pNode->Distance[i]; 326 327 // Advance to the next node. 328 pNode = pNode->Next[i]; 329 } 330 331 pUpdate[i] = pNode; 332 } 333 334 // The rest of the procedure is the same for both insertion functions. 335 return _InsertElementSkiplistWithInformation(Skiplist, Element, pUpdate, dwDistance); 336 } 337 338 /** 339 * @name LookupElementSkiplist 340 * 341 * Looks up an element in the Skiplist. The efficiency of this operation is O(log N) on average. 342 * 343 * @param Skiplist 344 * Pointer to the SKIPLIST structure to operate on. 345 * 346 * @param Element 347 * Information about the element to look for. 348 * 349 * @param ElementIndex 350 * Pointer to a DWORD that will contain the zero-based index of the element in the Skiplist. 351 * If you're not interested in the index, you can set this parameter to NULL. 352 * 353 * @return 354 * Returns the found element or NULL if no such element was found. 355 */ 356 PVOID 357 LookupElementSkiplist(PSKIPLIST Skiplist, PVOID Element, PDWORD ElementIndex) 358 { 359 CHAR i; 360 DWORD dwIndex = 0; 361 PSKIPLIST_NODE pLastComparedNode = NULL; 362 PSKIPLIST_NODE pNode = &Skiplist->Head; 363 364 // Do the efficient lookup in Skiplists: 365 // * Start from the maximum level. 366 // * Walk through all nodes on this level that come before the node we're looking for. 367 // * When we have reached such a node, go down a level and continue there. 368 // * Repeat these steps till we're in level 0, right in front of the node we're looking for. 369 for (i = Skiplist->MaximumLevel + 1; --i >= 0;) 370 { 371 while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0) 372 { 373 dwIndex += pNode->Distance[i]; 374 pNode = pNode->Next[i]; 375 } 376 377 // Reduce the number of comparisons by not comparing the same node on different levels twice. 378 pLastComparedNode = pNode->Next[i]; 379 } 380 381 // We must be right in front of the node we're looking for now, otherwise it doesn't exist in the Skiplist at all. 382 pNode = pNode->Next[0]; 383 if (!pNode || Skiplist->CompareRoutine(pNode->Element, Element) != 0) 384 { 385 // It hasn't been found, so there's nothing to return. 386 return NULL; 387 } 388 389 // Return the index of the element if the caller is interested. 390 if (ElementIndex) 391 *ElementIndex = dwIndex; 392 393 // Return the stored element of the found node. 394 return pNode->Element; 395 } 396 397 /** 398 * @name LookupNodeByIndexSkiplist 399 * 400 * Looks up a node in the Skiplist at the given position. The efficiency of this operation is O(log N) on average. 401 * 402 * @param Skiplist 403 * Pointer to the SKIPLIST structure to operate on. 404 * 405 * @param ElementIndex 406 * Zero-based position of the node in the Skiplist. 407 * 408 * @return 409 * Returns the found node or NULL if the position is invalid. 410 */ 411 PSKIPLIST_NODE 412 LookupNodeByIndexSkiplist(PSKIPLIST Skiplist, DWORD ElementIndex) 413 { 414 CHAR i; 415 DWORD dwIndex = 0; 416 PSKIPLIST_NODE pNode = &Skiplist->Head; 417 418 // The only way the node can't be found is when the index is out of range. 419 if (ElementIndex >= Skiplist->NodeCount) 420 return NULL; 421 422 // Do the efficient lookup in Skiplists: 423 // * Start from the maximum level. 424 // * Walk through all nodes on this level that come before the node we're looking for. 425 // * When we have reached such a node, go down a level and continue there. 426 // * Repeat these steps till we're in level 0, right in front of the node we're looking for. 427 for (i = Skiplist->MaximumLevel + 1; --i >= 0;) 428 { 429 // We compare with <= instead of < here, because the added distances make up a 1-based index while ElementIndex is zero-based, 430 // so we have to jump one node further. 431 while (pNode->Next[i] && dwIndex + pNode->Distance[i] <= ElementIndex) 432 { 433 dwIndex += pNode->Distance[i]; 434 pNode = pNode->Next[i]; 435 } 436 } 437 438 // We are right in front of the node we're looking for now. 439 return pNode->Next[0]; 440 } 441