diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstRTAvl.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstRTAvl.cpp | 1688 |
1 files changed, 1688 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstRTAvl.cpp b/src/VBox/Runtime/testcase/tstRTAvl.cpp new file mode 100644 index 00000000..a0c29033 --- /dev/null +++ b/src/VBox/Runtime/testcase/tstRTAvl.cpp @@ -0,0 +1,1688 @@ +/* $Id: tstRTAvl.cpp $ */ +/** @file + * IPRT Testcase - AVL trees. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/avl.h> +#include <iprt/cpp/hardavlrange.h> + +#include <iprt/asm.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/stdarg.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/test.h> +#include <iprt/time.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct TRACKER +{ + /** The max key value (exclusive). */ + uint32_t MaxKey; + /** The last allocated key. */ + uint32_t LastAllocatedKey; + /** The number of set bits in the bitmap. */ + uint32_t cSetBits; + /** The bitmap size. */ + uint32_t cbBitmap; + /** Bitmap containing the allocated nodes. */ + uint8_t abBitmap[1]; +} TRACKER, *PTRACKER; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest; +static RTRAND g_hRand; + + +/** + * Creates a new tracker. + * + * @returns Pointer to the new tracker. + * @param MaxKey The max key value for the tracker. (exclusive) + */ +static PTRACKER TrackerCreate(uint32_t MaxKey) +{ + uint32_t cbBitmap = RT_ALIGN_32(MaxKey, 64) / 8; + PTRACKER pTracker = (PTRACKER)RTMemAllocZ(RT_UOFFSETOF_DYN(TRACKER, abBitmap[cbBitmap])); + if (pTracker) + { + pTracker->MaxKey = MaxKey; + pTracker->LastAllocatedKey = MaxKey; + pTracker->cbBitmap = cbBitmap; + Assert(pTracker->cSetBits == 0); + } + return pTracker; +} + + +/** + * Destroys a tracker. + * + * @param pTracker The tracker. + */ +static void TrackerDestroy(PTRACKER pTracker) +{ + RTMemFree(pTracker); +} + + +/** + * Inserts a key range into the tracker. + * + * @returns success indicator. + * @param pTracker The tracker. + * @param Key The first key in the range. + * @param KeyLast The last key in the range. (inclusive) + */ +static bool TrackerInsert(PTRACKER pTracker, uint32_t Key, uint32_t KeyLast) +{ + bool fRc = !ASMBitTestAndSet(pTracker->abBitmap, Key); + if (fRc) + pTracker->cSetBits++; + while (KeyLast != Key) + { + if (!ASMBitTestAndSet(pTracker->abBitmap, KeyLast)) + pTracker->cSetBits++; + else + fRc = false; + KeyLast--; + } + return fRc; +} + + +/** + * Removes a key range from the tracker. + * + * @returns success indicator. + * @param pTracker The tracker. + * @param Key The first key in the range. + * @param KeyLast The last key in the range. (inclusive) + */ +static bool TrackerRemove(PTRACKER pTracker, uint32_t Key, uint32_t KeyLast) +{ + bool fRc = ASMBitTestAndClear(pTracker->abBitmap, Key); + if (fRc) + pTracker->cSetBits--; + while (KeyLast != Key) + { + if (ASMBitTestAndClear(pTracker->abBitmap, KeyLast)) + pTracker->cSetBits--; + else + fRc = false; + KeyLast--; + } + return fRc; +} + + +/** + * Random key range allocation. + * + * @returns success indicator. + * @param pTracker The tracker. + * @param pKey Where to store the first key in the allocated range. + * @param pKeyLast Where to store the first key in the allocated range. + * @param cMaxKey The max range length. + * @remark The caller has to call TrackerInsert. + */ +static bool TrackerNewRandomEx(PTRACKER pTracker, uint32_t *pKey, uint32_t *pKeyLast, uint32_t cMaxKeys) +{ + /* + * Find a key. + */ + uint32_t Key = RTRandAdvU32Ex(g_hRand, 0, pTracker->MaxKey - 1); + if (ASMBitTest(pTracker->abBitmap, Key)) + { + if (pTracker->cSetBits >= pTracker->MaxKey) + return false; + + int Key2 = ASMBitNextClear(pTracker->abBitmap, pTracker->MaxKey, Key); + if (Key2 > 0) + Key = Key2; + else + { + /* we're missing a ASMBitPrevClear function, so just try another, lower, value.*/ + for (;;) + { + const uint32_t KeyPrev = Key; + Key = RTRandAdvU32Ex(g_hRand, 0, KeyPrev - 1); + if (!ASMBitTest(pTracker->abBitmap, Key)) + break; + Key2 = ASMBitNextClear(pTracker->abBitmap, RT_ALIGN_32(KeyPrev, 32), Key); + if (Key2 > 0) + { + Key = Key2; + break; + } + } + } + } + + /* + * Determine the range. + */ + uint32_t KeyLast; + if (cMaxKeys == 1 || !pKeyLast) + KeyLast = Key; + else + { + uint32_t cKeys = RTRandAdvU32Ex(g_hRand, 0, RT_MIN(pTracker->MaxKey - Key, cMaxKeys - 1)); + KeyLast = Key + cKeys; + int Key2 = ASMBitNextSet(pTracker->abBitmap, RT_ALIGN_32(KeyLast, 32), Key); + if ( Key2 > 0 + && (unsigned)Key2 <= KeyLast) + KeyLast = Key2 - 1; + } + + /* + * Return. + */ + *pKey = Key; + if (pKeyLast) + *pKeyLast = KeyLast; + return true; +} + + +/** + * Random single key allocation. + * + * @returns success indicator. + * @param pTracker The tracker. + * @param pKey Where to store the allocated key. + * @remark The caller has to call TrackerInsert. + */ +static bool TrackerNewRandom(PTRACKER pTracker, uint32_t *pKey) +{ + return TrackerNewRandomEx(pTracker, pKey, NULL, 1); +} + + +/** + * Random single key 'lookup'. + * + * @returns success indicator. + * @param pTracker The tracker. + * @param pKey Where to store the allocated key. + * @remark The caller has to call TrackerRemove. + */ +static bool TrackerFindRandom(PTRACKER pTracker, uint32_t *pKey) +{ + uint32_t Key = RTRandAdvU32Ex(g_hRand, 0, pTracker->MaxKey - 1); + if (!ASMBitTest(pTracker->abBitmap, Key)) + { + if (!pTracker->cSetBits) + return false; + + int Key2 = ASMBitNextSet(pTracker->abBitmap, pTracker->MaxKey, Key); + if (Key2 > 0) + Key = Key2; + else + { + /* we're missing a ASMBitPrevSet function, so here's a quick replacement hack. */ + uint32_t const *pu32Bitmap = (uint32_t const *)&pTracker->abBitmap[0]; + Key >>= 5; + do + { + uint32_t u32; + if ((u32 = pu32Bitmap[Key]) != 0) + { + *pKey = ASMBitLastSetU32(u32) - 1 + (Key << 5); + return true; + } + } while (Key-- > 0); + + Key2 = ASMBitFirstSet(pTracker->abBitmap, pTracker->MaxKey); + if (Key2 == -1) + { + RTTestIFailed("cSetBits=%u - but ASMBitFirstSet failed to find any", pTracker->cSetBits); + return false; + } + Key = Key2; + } + } + + *pKey = Key; + return true; +} + + +/** + * Gets the number of keys in the tree. + */ +static uint32_t TrackerGetCount(PTRACKER pTracker) +{ + return pTracker->cSetBits; +} + + +/* +bool TrackerAllocSeq(PTRACKER pTracker, uint32_t *pKey, uint32_t *pKeyLast, uint32_t cMaxKeys) +{ + return false; +}*/ + + +/** + * Prints an unbuffered char. + * @param ch The char. + */ +static void ProgressChar(char ch) +{ + //RTTestIPrintf(RTTESTLVL_INFO, "%c", ch); + RTTestIPrintf(RTTESTLVL_SUB_TEST, "%c", ch); +} + +/** + * Prints a progress indicator label. + * @param cMax The max number of operations (exclusive). + * @param pszFormat The format string. + * @param ... The arguments to the format string. + */ +DECLINLINE(void) ProgressPrintf(unsigned cMax, const char *pszFormat, ...) +{ + if (cMax < 10000) + return; + + va_list va; + va_start(va, pszFormat); + //RTTestIPrintfV(RTTESTLVL_INFO, pszFormat, va); + RTTestIPrintfV(RTTESTLVL_SUB_TEST, pszFormat, va); + va_end(va); +} + + +/** + * Prints a progress indicator dot. + * @param iCur The current operation. (can be descending too) + * @param cMax The max number of operations (exclusive). + */ +DECLINLINE(void) Progress(unsigned iCur, unsigned cMax) +{ + if (cMax < 10000) + return; + if (!(iCur % (cMax / 20))) + ProgressChar('.'); +} + + +static int avlogcphys(unsigned cMax) +{ + /* + * Simple linear insert and remove. + */ + if (cMax >= 10000) + RTTestISubF("oGCPhys(%d): linear left", cMax); + PAVLOGCPHYSTREE pTree = (PAVLOGCPHYSTREE)RTMemAllocZ(sizeof(*pTree)); + unsigned i; + for (i = 0; i < cMax; i++) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvloGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("linear left insert i=%d\n", i); + return 1; + } + /* negative. */ + AVLOGCPHYSNODECORE Node = *pNode; + if (RTAvloGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("linear left negative insert i=%d\n", i); + return 1; + } + } + + ProgressPrintf(cMax, "~"); + for (i = 0; i < cMax; i++) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = RTAvloGCPhysRemove(pTree, i); + if (!pNode) + { + RTTestIFailed("linear left remove i=%d\n", i); + return 1; + } + memset(pNode, 0xcc, sizeof(*pNode)); + RTMemFree(pNode); + + /* negative */ + pNode = RTAvloGCPhysRemove(pTree, i); + if (pNode) + { + RTTestIFailed("linear left negative remove i=%d\n", i); + return 1; + } + } + + /* + * Simple linear insert and remove from the right. + */ + if (cMax >= 10000) + RTTestISubF("oGCPhys(%d): linear right", cMax); + for (i = 0; i < cMax; i++) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvloGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("linear right insert i=%d\n", i); + return 1; + } + /* negative. */ + AVLOGCPHYSNODECORE Node = *pNode; + if (RTAvloGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("linear right negative insert i=%d\n", i); + return 1; + } + } + + ProgressPrintf(cMax, "~"); + while (i-- > 0) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = RTAvloGCPhysRemove(pTree, i); + if (!pNode) + { + RTTestIFailed("linear right remove i=%d\n", i); + return 1; + } + memset(pNode, 0xcc, sizeof(*pNode)); + RTMemFree(pNode); + + /* negative */ + pNode = RTAvloGCPhysRemove(pTree, i); + if (pNode) + { + RTTestIFailed("linear right negative remove i=%d\n", i); + return 1; + } + } + + /* + * Linear insert but root based removal. + */ + if (cMax >= 10000) + RTTestISubF("oGCPhys(%d): linear root", cMax); + for (i = 0; i < cMax; i++) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvloGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("linear root insert i=%d\n", i); + return 1; + } + /* negative. */ + AVLOGCPHYSNODECORE Node = *pNode; + if (RTAvloGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("linear root negative insert i=%d\n", i); + return 1; + } + } + + ProgressPrintf(cMax, "~"); + while (i-- > 0) + { + Progress(i, cMax); + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)((intptr_t)pTree + *pTree); + RTGCPHYS Key = pNode->Key; + pNode = RTAvloGCPhysRemove(pTree, Key); + if (!pNode) + { + RTTestIFailed("linear root remove i=%d Key=%d\n", i, (unsigned)Key); + return 1; + } + memset(pNode, 0xcc, sizeof(*pNode)); + RTMemFree(pNode); + + /* negative */ + pNode = RTAvloGCPhysRemove(pTree, Key); + if (pNode) + { + RTTestIFailed("linear root negative remove i=%d Key=%d\n", i, (unsigned)Key); + return 1; + } + } + if (*pTree) + { + RTTestIFailed("sparse remove didn't remove it all!\n"); + return 1; + } + + /* + * Make a sparsely populated tree and remove the nodes using best fit in 5 cycles. + */ + const unsigned cMaxSparse = RT_ALIGN(cMax, 32); + if (cMaxSparse >= 10000) + RTTestISubF("oGCPhys(%d): sparse", cMax); + for (i = 0; i < cMaxSparse; i += 8) + { + Progress(i, cMaxSparse); + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvloGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("sparse insert i=%d\n", i); + return 1; + } + /* negative. */ + AVLOGCPHYSNODECORE Node = *pNode; + if (RTAvloGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("sparse negative insert i=%d\n", i); + return 1; + } + } + + /* Remove using best fit in 5 cycles. */ + ProgressPrintf(cMaxSparse, "~"); + unsigned j; + for (j = 0; j < 4; j++) + { + for (i = 0; i < cMaxSparse; i += 8 * 4) + { + Progress(i, cMax); // good enough + PAVLOGCPHYSNODECORE pNode = RTAvloGCPhysRemoveBestFit(pTree, i, true); + if (!pNode) + { + RTTestIFailed("sparse remove i=%d j=%d\n", i, j); + return 1; + } + if (pNode->Key - (unsigned long)i >= 8 * 4) + { + RTTestIFailed("sparse remove i=%d j=%d Key=%d\n", i, j, (unsigned)pNode->Key); + return 1; + } + memset(pNode, 0xdd, sizeof(*pNode)); + RTMemFree(pNode); + } + } + if (*pTree) + { + RTTestIFailed("sparse remove didn't remove it all!\n"); + return 1; + } + RTMemFree(pTree); + ProgressPrintf(cMaxSparse, "\n"); + return 0; +} + + +static DECLCALLBACK(int) avlogcphysCallbackCounter(PAVLOGCPHYSNODECORE pNode, void *pvUser) +{ + RT_NOREF(pNode); + *(uint32_t *)pvUser += 1; + return 0; +} + +int avlogcphysRand(unsigned cMax, unsigned cMax2, uint32_t fCountMask) +{ + PAVLOGCPHYSTREE pTree = (PAVLOGCPHYSTREE)RTMemAllocZ(sizeof(*pTree)); + unsigned i; + + /* + * Random tree. + */ + if (cMax >= 10000) + RTTestISubF("oGCPhys(%d, %d): random", cMax, cMax2); + PTRACKER pTracker = TrackerCreate(cMax2); + if (!pTracker) + { + RTTestIFailed("failed to create %d tracker!\n", cMax2); + return 1; + } + + /* Insert a number of nodes in random order. */ + for (i = 0; i < cMax; i++) + { + Progress(i, cMax); + uint32_t Key; + if (!TrackerNewRandom(pTracker, &Key)) + { + RTTestIFailed("failed to allocate node no. %d\n", i); + TrackerDestroy(pTracker); + return 1; + } + PAVLOGCPHYSNODECORE pNode = (PAVLOGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = Key; + if (!RTAvloGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("random insert i=%d Key=%#x\n", i, Key); + return 1; + } + /* negative. */ + AVLOGCPHYSNODECORE Node = *pNode; + if (RTAvloGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("linear negative insert i=%d Key=%#x\n", i, Key); + return 1; + } + TrackerInsert(pTracker, Key, Key); + + if (!(i & fCountMask)) + { + uint32_t cCount = 0; + RTAvloGCPhysDoWithAll(pTree, i & 1, avlogcphysCallbackCounter, &cCount); + if (cCount != TrackerGetCount(pTracker)) + RTTestIFailed("wrong tree count after random insert i=%d: %u, expected %u", i, cCount, TrackerGetCount(pTracker)); + } + } + + { + uint32_t cCount = 0; + RTAvloGCPhysDoWithAll(pTree, i & 1, avlogcphysCallbackCounter, &cCount); + if (cCount != TrackerGetCount(pTracker)) + RTTestIFailed("wrong tree count after random insert i=%d: %u, expected %u", i, cCount, TrackerGetCount(pTracker)); + } + + + /* delete the nodes in random order. */ + ProgressPrintf(cMax, "~"); + while (i-- > 0) + { + Progress(i, cMax); + uint32_t Key; + if (!TrackerFindRandom(pTracker, &Key)) + { + RTTestIFailed("failed to find free node no. %d\n", i); + TrackerDestroy(pTracker); + return 1; + } + + PAVLOGCPHYSNODECORE pNode = RTAvloGCPhysRemove(pTree, Key); + if (!pNode) + { + RTTestIFailed("random remove i=%d Key=%#x\n", i, Key); + return 1; + } + if (pNode->Key != Key) + { + RTTestIFailed("random remove i=%d Key=%#x pNode->Key=%#x\n", i, Key, (unsigned)pNode->Key); + return 1; + } + TrackerRemove(pTracker, Key, Key); + memset(pNode, 0xdd, sizeof(*pNode)); + RTMemFree(pNode); + + if (!(i & fCountMask)) + { + uint32_t cCount = 0; + RTAvloGCPhysDoWithAll(pTree, i & 1, avlogcphysCallbackCounter, &cCount); + if (cCount != TrackerGetCount(pTracker)) + RTTestIFailed("wrong tree count after random remove i=%d: %u, expected %u", i, cCount, TrackerGetCount(pTracker)); + } + } + { + uint32_t cCount = 0; + RTAvloGCPhysDoWithAll(pTree, i & 1, avlogcphysCallbackCounter, &cCount); + if (cCount != TrackerGetCount(pTracker)) + RTTestIFailed("wrong tree count after random insert i=%d: %u, expected %u", i, cCount, TrackerGetCount(pTracker)); + } + if (*pTree) + { + RTTestIFailed("random remove didn't remove it all!\n"); + return 1; + } + ProgressPrintf(cMax, "\n"); + TrackerDestroy(pTracker); + RTMemFree(pTree); + return 0; +} + + + +int avlrogcphys(void) +{ + unsigned i; + unsigned j; + unsigned k; + PAVLROGCPHYSTREE pTree = (PAVLROGCPHYSTREE)RTMemAllocZ(sizeof(*pTree)); + + AssertCompileSize(AVLOGCPHYSNODECORE, 24); + AssertCompileSize(AVLROGCPHYSNODECORE, 32); + + RTTestISubF("RTAvlroGCPhys"); + + /* + * Simple linear insert, get and remove. + */ + /* insert */ + for (i = 0; i < 65536; i += 4) + { + PAVLROGCPHYSNODECORE pNode = (PAVLROGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + pNode->KeyLast = i + 3; + if (!RTAvlroGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("linear insert i=%d\n", (unsigned)i); + return 1; + } + + /* negative. */ + AVLROGCPHYSNODECORE Node = *pNode; + for (j = i + 3; j > i - 32; j--) + { + for (k = i; k < i + 32; k++) + { + Node.Key = RT_MIN(j, k); + Node.KeyLast = RT_MAX(k, j); + if (RTAvlroGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("linear negative insert i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + } + } + + /* do gets. */ + for (i = 0; i < 65536; i += 4) + { + PAVLROGCPHYSNODECORE pNode = RTAvlroGCPhysGet(pTree, i); + if (!pNode) + { + RTTestIFailed("linear get i=%d\n", i); + return 1; + } + if (pNode->Key > i || pNode->KeyLast < i) + { + RTTestIFailed("linear get i=%d Key=%d KeyLast=%d\n", i, (unsigned)pNode->Key, (unsigned)pNode->KeyLast); + return 1; + } + + for (j = 0; j < 4; j++) + { + if (RTAvlroGCPhysRangeGet(pTree, i + j) != pNode) + { + RTTestIFailed("linear range get i=%d j=%d\n", i, j); + return 1; + } + } + + /* negative. */ + if ( RTAvlroGCPhysGet(pTree, i + 1) + || RTAvlroGCPhysGet(pTree, i + 2) + || RTAvlroGCPhysGet(pTree, i + 3)) + { + RTTestIFailed("linear negative get i=%d + n\n", i); + return 1; + } + + } + + /* remove */ + for (i = 0; i < 65536; i += 4) + { + PAVLROGCPHYSNODECORE pNode = RTAvlroGCPhysRemove(pTree, i); + if (!pNode) + { + RTTestIFailed("linear remove i=%d\n", i); + return 1; + } + memset(pNode, 0xcc, sizeof(*pNode)); + RTMemFree(pNode); + + /* negative */ + if ( RTAvlroGCPhysRemove(pTree, i) + || RTAvlroGCPhysRemove(pTree, i + 1) + || RTAvlroGCPhysRemove(pTree, i + 2) + || RTAvlroGCPhysRemove(pTree, i + 3)) + { + RTTestIFailed("linear negative remove i=%d + n\n", i); + return 1; + } + } + + /* + * Make a sparsely populated tree. + */ + for (i = 0; i < 65536; i += 8) + { + PAVLROGCPHYSNODECORE pNode = (PAVLROGCPHYSNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + pNode->KeyLast = i + 3; + if (!RTAvlroGCPhysInsert(pTree, pNode)) + { + RTTestIFailed("sparse insert i=%d\n", i); + return 1; + } + /* negative. */ + AVLROGCPHYSNODECORE Node = *pNode; + const RTGCPHYS jMin = i > 32 ? i - 32 : 1; + const RTGCPHYS kMax = i + 32; + for (j = pNode->KeyLast; j >= jMin; j--) + { + for (k = pNode->Key; k < kMax; k++) + { + Node.Key = RT_MIN(j, k); + Node.KeyLast = RT_MAX(k, j); + if (RTAvlroGCPhysInsert(pTree, &Node)) + { + RTTestIFailed("sparse negative insert i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + } + } + + /* + * Get and Remove using range matching in 5 cycles. + */ + for (j = 0; j < 4; j++) + { + for (i = 0; i < 65536; i += 8 * 4) + { + /* gets */ + RTGCPHYS KeyBase = i + j * 8; + PAVLROGCPHYSNODECORE pNode = RTAvlroGCPhysGet(pTree, KeyBase); + if (!pNode) + { + RTTestIFailed("sparse get i=%d j=%d KeyBase=%d\n", i, j, (unsigned)KeyBase); + return 1; + } + if (pNode->Key > KeyBase || pNode->KeyLast < KeyBase) + { + RTTestIFailed("sparse get i=%d j=%d KeyBase=%d pNode->Key=%d\n", i, j, (unsigned)KeyBase, (unsigned)pNode->Key); + return 1; + } + for (k = KeyBase; k < KeyBase + 4; k++) + { + if (RTAvlroGCPhysRangeGet(pTree, k) != pNode) + { + RTTestIFailed("sparse range get i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + + /* negative gets */ + for (k = i + j; k < KeyBase + 8; k++) + { + if ( k != KeyBase + && RTAvlroGCPhysGet(pTree, k)) + { + RTTestIFailed("sparse negative get i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + for (k = i + j; k < KeyBase; k++) + { + if (RTAvlroGCPhysRangeGet(pTree, k)) + { + RTTestIFailed("sparse negative range get i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + for (k = KeyBase + 4; k < KeyBase + 8; k++) + { + if (RTAvlroGCPhysRangeGet(pTree, k)) + { + RTTestIFailed("sparse negative range get i=%d j=%d k=%d\n", i, j, k); + return 1; + } + } + + /* remove */ + RTGCPHYS Key = KeyBase + ((i / 19) % 4); + if (RTAvlroGCPhysRangeRemove(pTree, Key) != pNode) + { + RTTestIFailed("sparse remove i=%d j=%d Key=%d\n", i, j, (unsigned)Key); + return 1; + } + memset(pNode, 0xdd, sizeof(*pNode)); + RTMemFree(pNode); + } + } + if (*pTree) + { + RTTestIFailed("sparse remove didn't remove it all!\n"); + return 1; + } + + + /* + * Realworld testcase. + */ + struct + { + AVLROGCPHYSTREE Tree; + AVLROGCPHYSNODECORE aNode[4]; + } s1, s2, s3; + RT_ZERO(s1); + RT_ZERO(s2); + RT_ZERO(s3); + + s1.aNode[0].Key = 0x00030000; + s1.aNode[0].KeyLast = 0x00030fff; + s1.aNode[1].Key = 0x000a0000; + s1.aNode[1].KeyLast = 0x000bffff; + s1.aNode[2].Key = 0xe0000000; + s1.aNode[2].KeyLast = 0xe03fffff; + s1.aNode[3].Key = 0xfffe0000; + s1.aNode[3].KeyLast = 0xfffe0ffe; + for (i = 0; i < RT_ELEMENTS(s1.aNode); i++) + { + PAVLROGCPHYSNODECORE pNode = &s1.aNode[i]; + if (!RTAvlroGCPhysInsert(&s1.Tree, pNode)) + { + RTTestIFailed("real insert i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysInsert(&s1.Tree, pNode)) + { + RTTestIFailed("real negative insert i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysGet(&s1.Tree, pNode->Key) != pNode) + { + RTTestIFailed("real get (1) i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysGet(&s1.Tree, pNode->KeyLast) != NULL) + { + RTTestIFailed("real negative get (2) i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysRangeGet(&s1.Tree, pNode->Key) != pNode) + { + RTTestIFailed("real range get (1) i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysRangeGet(&s1.Tree, pNode->Key + 1) != pNode) + { + RTTestIFailed("real range get (2) i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysRangeGet(&s1.Tree, pNode->KeyLast) != pNode) + { + RTTestIFailed("real range get (3) i=%d\n", i); + return 1; + } + } + + s3 = s1; + s1 = s2; + for (i = 0; i < RT_ELEMENTS(s3.aNode); i++) + { + PAVLROGCPHYSNODECORE pNode = &s3.aNode[i]; + if (RTAvlroGCPhysGet(&s3.Tree, pNode->Key) != pNode) + { + RTTestIFailed("real get (10) i=%d\n", i); + return 1; + } + if (RTAvlroGCPhysRangeGet(&s3.Tree, pNode->Key) != pNode) + { + RTTestIFailed("real range get (10) i=%d\n", i); + return 1; + } + + j = pNode->Key + 1; + do + { + if (RTAvlroGCPhysGet(&s3.Tree, j) != NULL) + { + RTTestIFailed("real negative get (11) i=%d j=%#x\n", i, j); + return 1; + } + if (RTAvlroGCPhysRangeGet(&s3.Tree, j) != pNode) + { + RTTestIFailed("real range get (11) i=%d j=%#x\n", i, j); + return 1; + } + } while (j++ < pNode->KeyLast); + } + + return 0; +} + + +int avlul(void) +{ + RTTestISubF("RTAvlUL"); + + /* + * Simple linear insert and remove. + */ + PAVLULNODECORE pTree = 0; + unsigned cInserted = 0; + unsigned i; + + /* insert */ + for (i = 0; i < 65536; i++, cInserted++) + { + PAVLULNODECORE pNode = (PAVLULNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvlULInsert(&pTree, pNode)) + { + RTTestIFailed("linear insert i=%d\n", i); + return 1; + } + + /* negative. */ + AVLULNODECORE Node = *pNode; + if (RTAvlULInsert(&pTree, &Node)) + { + RTTestIFailed("linear negative insert i=%d\n", i); + return 1; + } + + /* check height */ + uint8_t const cHeight = pTree ? pTree->uchHeight : 0; + uint32_t const cMax = cHeight > 0 ? RT_BIT_32(cHeight) : 1; + if (cInserted > cMax || cInserted < (cMax >> 2)) + RTTestIFailed("bad tree height after linear insert i=%d: cMax=%#x, cInserted=%#x\n", i, cMax, cInserted); + } + + for (i = 0; i < 65536; i++, cInserted--) + { + PAVLULNODECORE pNode = RTAvlULRemove(&pTree, i); + if (!pNode) + { + RTTestIFailed("linear remove i=%d\n", i); + return 1; + } + pNode->pLeft = (PAVLULNODECORE)(uintptr_t)0xaaaaaaaa; + pNode->pRight = (PAVLULNODECORE)(uintptr_t)0xbbbbbbbb; + pNode->uchHeight = 'e'; + RTMemFree(pNode); + + /* negative */ + pNode = RTAvlULRemove(&pTree, i); + if (pNode) + { + RTTestIFailed("linear negative remove i=%d\n", i); + return 1; + } + + /* check height */ + uint8_t const cHeight = pTree ? pTree->uchHeight : 0; + uint32_t const cMax = cHeight > 0 ? RT_BIT_32(cHeight) : 1; + if (cInserted > cMax || cInserted < (cMax >> 2)) + RTTestIFailed("bad tree height after linear removal i=%d: cMax=%#x, cInserted=%#x\n", i, cMax, cInserted); + } + + /* + * Make a sparsely populated tree. + */ + for (i = 0; i < 65536; i += 8, cInserted++) + { + PAVLULNODECORE pNode = (PAVLULNODECORE)RTMemAlloc(sizeof(*pNode)); + pNode->Key = i; + if (!RTAvlULInsert(&pTree, pNode)) + { + RTTestIFailed("linear insert i=%d\n", i); + return 1; + } + + /* negative. */ + AVLULNODECORE Node = *pNode; + if (RTAvlULInsert(&pTree, &Node)) + { + RTTestIFailed("linear negative insert i=%d\n", i); + return 1; + } + + /* check height */ + uint8_t const cHeight = pTree ? pTree->uchHeight : 0; + uint32_t const cMax = cHeight > 0 ? RT_BIT_32(cHeight) : 1; + if (cInserted > cMax || cInserted < (cMax >> 2)) + RTTestIFailed("bad tree height after sparse insert i=%d: cMax=%#x, cInserted=%#x\n", i, cMax, cInserted); + } + + /* + * Remove using best fit in 5 cycles. + */ + unsigned j; + for (j = 0; j < 4; j++) + { + for (i = 0; i < 65536; i += 8 * 4, cInserted--) + { + PAVLULNODECORE pNode = RTAvlULRemoveBestFit(&pTree, i, true); + //PAVLULNODECORE pNode = RTAvlULRemove(&pTree, i + j * 8); + if (!pNode) + { + RTTestIFailed("sparse remove i=%d j=%d\n", i, j); + return 1; + } + pNode->pLeft = (PAVLULNODECORE)(uintptr_t)0xdddddddd; + pNode->pRight = (PAVLULNODECORE)(uintptr_t)0xcccccccc; + pNode->uchHeight = 'E'; + RTMemFree(pNode); + + /* check height */ + uint8_t const cHeight = pTree ? pTree->uchHeight : 0; + uint32_t const cMax = cHeight > 0 ? RT_BIT_32(cHeight) : 1; + if (cInserted > cMax || cInserted < (cMax >> 2)) + RTTestIFailed("bad tree height after sparse removal i=%d: cMax=%#x, cInserted=%#x\n", i, cMax, cInserted); + } + } + + return 0; +} + + +/********************************************************************************************************************************* +* RTCHardAvlRangeTreeGCPhys * +*********************************************************************************************************************************/ + +typedef struct TESTNODE +{ + RTGCPHYS Key; + RTGCPHYS KeyLast; + uint32_t idxLeft; + uint32_t idxRight; + uint8_t cHeight; +} MYTESTNODE; + +static DECLCALLBACK(int) hardAvlRangeTreeGCPhysEnumCallbackAscBy4(TESTNODE *pNode, void *pvUser) +{ + PRTGCPHYS pExpect = (PRTGCPHYS)pvUser; + if (pNode->Key != *pExpect) + RTTestIFailed("Key=%RGp, expected %RGp\n", pNode->Key, *pExpect); + *pExpect = pNode->Key + 4; + return VINF_SUCCESS; +} + + +static DECLCALLBACK(int) hardAvlRangeTreeGCPhysEnumCallbackDescBy4(TESTNODE *pNode, void *pvUser) +{ + PRTGCPHYS pExpect = (PRTGCPHYS)pvUser; + if (pNode->Key != *pExpect) + RTTestIFailed("Key=%RGp, expected %RGp\n", pNode->Key, *pExpect); + *pExpect = pNode->Key - 4; + return VINF_SUCCESS; +} + + +static DECLCALLBACK(int) hardAvlRangeTreeGCPhysEnumCallbackCount(TESTNODE *pNode, void *pvUser) +{ + *(uint32_t *)pvUser += 1; + RT_NOREF(pNode); + return VINF_SUCCESS; +} + + +static uint32_t PickClearBit(uint64_t *pbm, uint32_t cItems) +{ + uint32_t idx = RTRandAdvU32Ex(g_hRand, 0, cItems - 1); + if (ASMBitTest(pbm, idx) == 0) + return idx; + + /* Scan forward as we've got code for that already: */ + uint32_t const idxOrg = idx; + idx = ASMBitNextClear(pbm, cItems, idx); + if ((int32_t)idx >= 0) + return idx; + + /* Scan backwards bit-by-bit because we don't have code for this: */ + for (idx = idxOrg - 1; idx < cItems; idx--) + if (ASMBitTest(pbm, idx) == 0) + return idx; + + AssertFailed(); + RTTestIFailed("no clear bit in bitmap!\n"); + return 0; +} + + +static uint32_t PickClearBitAndSetIt(uint64_t *pbm, uint32_t cItems) +{ + uint32_t idx = PickClearBit(pbm, cItems); + RTTESTI_CHECK(ASMBitTestAndSet(pbm, idx) == false); + return idx; +} + + +static uint32_t PickSetBit(uint64_t *pbm, uint32_t cItems) +{ + uint32_t idx = RTRandAdvU32Ex(g_hRand, 0, cItems - 1); + if (ASMBitTest(pbm, idx) == 1) + return idx; + + /* Scan forward as we've got code for that already: */ + uint32_t const idxOrg = idx; + idx = ASMBitNextSet(pbm, cItems, idx); + if ((int32_t)idx >= 0) + return idx; + + /* Scan backwards bit-by-bit because we don't have code for this: */ + for (idx = idxOrg - 1; idx < cItems; idx--) + if (ASMBitTest(pbm, idx) == 1) + return idx; + + AssertFailed(); + RTTestIFailed("no set bit in bitmap!\n"); + return 0; +} + + +static uint32_t PickSetBitAndClearIt(uint64_t *pbm, uint32_t cItems) +{ + uint32_t idx = PickSetBit(pbm, cItems); + RTTESTI_CHECK(ASMBitTestAndClear(pbm, idx) == true); + return idx; +} + + +/** + * @return meaningless value, just for shortening 'return RTTestIFailed();'. + */ +int hardAvlRangeTreeGCPhys(RTTEST hTest) +{ + RTTestISubF("RTCHardAvlRangeTreeGCPhys"); + + /* + * Tree and allocator variables. + */ + RTCHardAvlTreeSlabAllocator<MYTESTNODE> Allocator; + RTCHardAvlRangeTree<MYTESTNODE, RTGCPHYS> Tree(&Allocator); + AssertCompileSize(Tree, sizeof(uint32_t) * 2 + sizeof(uint64_t) * 3); + AssertCompileSize(Allocator, sizeof(void *) * 2 + sizeof(uint32_t) * 4); + + /* Initialize the allocator with a decent slab of memory. */ + const uint32_t cItems = 8192; + void *pvItems; + RTTESTI_CHECK_RC_RET(RTTestGuardedAlloc(hTest, sizeof(MYTESTNODE) * cItems, + sizeof(uint64_t), false, &pvItems), VINF_SUCCESS, 1); + void *pbmBitmap; + RTTESTI_CHECK_RC_RET(RTTestGuardedAlloc(hTest, RT_ALIGN_32(cItems, 64) / 64 * 8, + sizeof(uint64_t), false, &pbmBitmap), VINF_SUCCESS, 1); + Allocator.initSlabAllocator(cItems, (TESTNODE *)pvItems, (uint64_t *)pbmBitmap); + + uint32_t cInserted = 0; + + /* + * Simple linear insert, get and remove. + */ + /* insert */ + for (unsigned i = 0; i < cItems * 4; i += 4, cInserted++) + { + MYTESTNODE *pNode = Allocator.allocateNode(); + if (!pNode) + return RTTestIFailed("out of nodes: i=%#x", i); + pNode->Key = i; + pNode->KeyLast = i + 3; + int rc = Tree.insert(&Allocator, pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("linear insert i=%#x failed: %Rrc", i, rc); + + /* look it up again immediately */ + for (unsigned j = 0; j < 4; j++) + { + MYTESTNODE *pNode2; + rc = Tree.lookup(&Allocator, i + j, &pNode2); + if (rc != VINF_SUCCESS || pNode2 != pNode) + return RTTestIFailed("get after insert i=%#x j=%#x: %Rrc pNode=%p pNode2=%p", i, j, rc, pNode, pNode2); + } + + /* Do negative inserts if we've got more free nodes. */ + if (i / 4 + 1 < cItems) + { + MYTESTNODE *pNode2 = Allocator.allocateNode(); + if (!pNode2) + return RTTestIFailed("out of nodes: i=%#x (#2)", i); + RTTESTI_CHECK(pNode2 != pNode); + + *pNode2 = *pNode; + for (unsigned j = i >= 32 ? i - 32 : 0; j <= i + 3; j++) + { + for (unsigned k = i; k < i + 32; k++) + { + pNode2->Key = RT_MIN(j, k); + pNode2->KeyLast = RT_MAX(k, j); + rc = Tree.insert(&Allocator, pNode2); + if (rc != VERR_ALREADY_EXISTS) + return RTTestIFailed("linear negative insert: %Rrc, expected VERR_ALREADY_EXISTS; i=%#x j=%#x k=%#x; Key2=%RGp KeyLast2=%RGp vs Key=%RGp KeyLast=%RGp", + rc, i, j, k, pNode2->Key, pNode2->KeyLast, pNode->Key, pNode->KeyLast); + } + if (j == 0) + break; + } + + rc = Allocator.freeNode(pNode2); + if (rc != VINF_SUCCESS) + return RTTestIFailed("freeNode(pNode2=%p) failed: %Rrc (i=%#x)", pNode2, rc, i); + } + + /* check the height */ + uint8_t const cHeight = Tree.getHeight(&Allocator); + uint32_t const cMax = RT_BIT_32(cHeight); + if (cInserted > cMax || cInserted < (cMax >> 4)) + RTTestIFailed("wrong tree height after linear insert i=%#x: cMax=%#x, cInserted=%#x, cHeight=%u\n", + i, cMax, cInserted, cHeight); + } + + /* do gets. */ + for (unsigned i = 0; i < cItems * 4; i += 4) + { + MYTESTNODE *pNode; + int rc = Tree.lookup(&Allocator, i, &pNode); + if (rc != VINF_SUCCESS || pNode == NULL) + return RTTestIFailed("linear get i=%#x: %Rrc pNode=%p", i, rc, pNode); + if (i < pNode->Key || i > pNode->KeyLast) + return RTTestIFailed("linear get i=%#x Key=%RGp KeyLast=%RGp\n", i, pNode->Key, pNode->KeyLast); + + for (unsigned j = 1; j < 4; j++) + { + MYTESTNODE *pNode2; + rc = Tree.lookup(&Allocator, i + j, &pNode2); + if (rc != VINF_SUCCESS || pNode2 != pNode) + return RTTestIFailed("linear get i=%#x j=%#x: %Rrc pNode=%p pNode2=%p", i, j, rc, pNode, pNode2); + } + } + + /* negative get */ + for (unsigned i = cItems * 4; i < cItems * 4 * 2; i += 1) + { + MYTESTNODE *pNode = (MYTESTNODE *)(uintptr_t)i; + int rc = Tree.lookup(&Allocator, i, &pNode); + if (rc != VERR_NOT_FOUND || pNode != NULL) + return RTTestIFailed("linear negative get i=%#x: %Rrc pNode=%p, expected VERR_NOT_FOUND and NULL", i, rc, pNode); + } + + /* enumerate */ + { + RTGCPHYS Expect = 0; + int rc = Tree.doWithAllFromLeft(&Allocator, hardAvlRangeTreeGCPhysEnumCallbackAscBy4, &Expect); + if (rc != VINF_SUCCESS) + RTTestIFailed("enumeration after linear insert failed: %Rrc", rc); + + Expect -= 4; + rc = Tree.doWithAllFromRight(&Allocator, hardAvlRangeTreeGCPhysEnumCallbackDescBy4, &Expect); + if (rc != VINF_SUCCESS) + RTTestIFailed("enumeration after linear insert failed: %Rrc", rc); + } + + /* remove */ + for (unsigned i = 0, j = 0; i < cItems * 4; i += 4, j += 3, cInserted--) + { + MYTESTNODE *pNode; + int rc = Tree.remove(&Allocator, i + (j % 4), &pNode); + if (rc != VINF_SUCCESS || pNode == NULL) + return RTTestIFailed("linear remove(%#x): %Rrc pNode=%p", i + (j % 4), rc, pNode); + if (i < pNode->Key || i > pNode->KeyLast) + return RTTestIFailed("linear remove i=%#x Key=%RGp KeyLast=%RGp\n", i, pNode->Key, pNode->KeyLast); + + memset(pNode, 0xcc, sizeof(*pNode)); + Allocator.freeNode(pNode); + + /* negative */ + for (unsigned k = i; k < i + 4; k++) + { + pNode = (MYTESTNODE *)(uintptr_t)k; + rc = Tree.remove(&Allocator, k, &pNode); + if (rc != VERR_NOT_FOUND || pNode != NULL) + return RTTestIFailed("linear negative remove(%#x): %Rrc pNode=%p", k, rc, pNode); + } + + /* check the height */ + uint8_t const cHeight = Tree.getHeight(&Allocator); + uint32_t const cMax = RT_BIT_32(cHeight); + if (cInserted > cMax || cInserted < (cMax >> 4)) + RTTestIFailed("wrong tree height after linear remove i=%#x: cMax=%#x, cInserted=%#x cHeight=%d\n", + i, cMax, cInserted, cHeight); + } + + /* + * Randomized stuff. + */ + uint64_t uSeed = RTRandU64(); + RTRandAdvSeed(g_hRand, uSeed); + RTTestIPrintf(RTTESTLVL_ALWAYS, "Random seed #1: %#RX64\n", uSeed); + + RTGCPHYS const cbStep = RTGCPHYS_MAX / cItems + 1; + uint64_t * const pbmPresent = (uint64_t *)RTMemAllocZ(RT_ALIGN_32(cItems, 64) / 64 * 8); + RTTESTI_CHECK_RET(pbmPresent, 1); + + /* insert all in random order */ + cInserted = 0; + for (unsigned i = 0; i < cItems; i++) + { + MYTESTNODE *pNode = Allocator.allocateNode(); + if (!pNode) + return RTTestIFailed("out of nodes: i=%#x #3", i); + + uint32_t const idx = PickClearBitAndSetIt(pbmPresent, cItems); + pNode->Key = idx * cbStep; + pNode->KeyLast = pNode->Key + cbStep - 1; + int rc = Tree.insert(&Allocator, pNode); + if (rc == VINF_SUCCESS) + cInserted++; + else + RTTestIFailed("random insert failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp)", rc, i, idx, pNode->Key, pNode->KeyLast); + + MYTESTNODE *pNode2 = (MYTESTNODE *)(intptr_t)i; + rc = Tree.lookup(&Allocator, pNode->Key, &pNode2); + if (rc != VINF_SUCCESS || pNode2 != pNode) + return RTTestIFailed("lookup after random insert %#x: %Rrc pNode=%p pNode2=%p idx=%#x", i, rc, pNode, pNode2, idx); + + uint32_t cCount = 0; + rc = Tree.doWithAllFromLeft(&Allocator, hardAvlRangeTreeGCPhysEnumCallbackCount, &cCount); + if (rc != VINF_SUCCESS) + RTTestIFailed("enum after random insert %#x: %Rrc idx=%#x", i, rc, idx); + else if (cCount != cInserted) + RTTestIFailed("wrong count after random removal %#x: %#x, expected %#x", i, cCount, cInserted); + + /* check the height */ + uint8_t const cHeight = Tree.getHeight(&Allocator); + uint32_t const cMax = RT_BIT_32(cHeight); + if (cInserted > cMax || cInserted < (cMax >> 4)) + RTTestIFailed("wrong tree height after random insert %#x: cMax=%#x, cInserted=%#x, cHeight=%u\n", + i, cMax, cInserted, cHeight); + } + + /* remove all in random order, doing adjacent lookups while at it. */ + for (unsigned i = 0; i < cItems; i++) + { + uint32_t const idx = PickSetBitAndClearIt(pbmPresent, cItems); + RTGCPHYS const Key = idx * cbStep; + + /* pre-removal lookup tests */ + MYTESTNODE *pNode = (MYTESTNODE *)(intptr_t)i; + int rc = Tree.lookupMatchingOrBelow(&Allocator, Key, &pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("pre-remove lookupMatchingOrBelow failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp)", + rc, i, idx, Key, Key + cbStep - 1); + else if (pNode->Key != Key) + RTTestIFailed("pre-remove lookupMatchingOrBelow returned the wrong node: Key=%RGp, expected %RGp", pNode->Key, Key); + + pNode = (MYTESTNODE *)(intptr_t)i; + rc = Tree.lookupMatchingOrAbove(&Allocator, Key, &pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("pre-remove lookupMatchingOrAbove failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp)", + rc, i, idx, Key, Key + cbStep - 1); + else if (pNode->Key != Key) + RTTestIFailed("pre-remove lookupMatchingOrAbove returned the wrong node: Key=%RGp, expected %RGp", pNode->Key, Key); + + /* remove */ + pNode = (MYTESTNODE *)(intptr_t)i; + rc = Tree.remove(&Allocator, Key, &pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("random remove failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp)", + rc, i, idx, Key, Key + cbStep - 1); + else + { + cInserted--; + if ( pNode->Key != Key + || pNode->KeyLast != Key + cbStep - 1) + RTTestIFailed("random remove returned wrong node: %RGp ... %RGp, expected %RGp ... %RGp (i=%#x, idx=%#x)", + pNode->Key, pNode->KeyLast, Key, Key + cbStep - 1, i, idx); + else + { + MYTESTNODE *pNode2 = (MYTESTNODE *)(intptr_t)i; + rc = Tree.lookup(&Allocator, Key, &pNode2); + if (rc != VERR_NOT_FOUND) + RTTestIFailed("lookup after random removal %#x: %Rrc pNode=%p pNode2=%p idx=%#x", i, rc, pNode, pNode2, idx); + + uint32_t cCount = 0; + rc = Tree.doWithAllFromLeft(&Allocator, hardAvlRangeTreeGCPhysEnumCallbackCount, &cCount); + if (rc != VINF_SUCCESS) + RTTestIFailed("enum after random removal %#x: %Rrc idx=%#x", i, rc, idx); + else if (cCount != cInserted) + RTTestIFailed("wrong count after random removal %#x: %#x, expected %#x", i, cCount, cInserted); + } + + rc = Allocator.freeNode(pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("free after random removal %#x failed: %Rrc pNode=%p idx=%#x", i, rc, pNode, idx); + + /* post-removal lookup tests */ + pNode = (MYTESTNODE *)(intptr_t)i; + rc = Tree.lookupMatchingOrBelow(&Allocator, Key, &pNode); + uint32_t idxAbove; + if (rc == VINF_SUCCESS) + { + uint32_t idxRet = pNode->Key / cbStep; + RTTESTI_CHECK(ASMBitTest(pbmPresent, idxRet) == true); + idxAbove = (uint32_t)ASMBitNextSet(pbmPresent, cItems, idxRet); + if (idxAbove <= idx) + RTTestIFailed("post-remove lookupMatchingOrBelow wrong: idxRet=%#x idx=%#x idxAbove=%#x", + idxRet, idx, idxAbove); + } + else if (rc == VERR_NOT_FOUND) + { + idxAbove = (uint32_t)ASMBitFirstSet(pbmPresent, cItems); + if (idxAbove <= idx) + RTTestIFailed("post-remove lookupMatchingOrBelow wrong: VERR_NOT_FOUND idx=%#x idxAbove=%#x", idx, idxAbove); + } + else + { + RTTestIFailed("post-remove lookupMatchingOrBelow failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp)", + rc, i, idx, Key, Key + cbStep - 1); + idxAbove = (uint32_t)ASMBitNextSet(pbmPresent, cItems, idx); + } + + pNode = (MYTESTNODE *)(intptr_t)i; + rc = Tree.lookupMatchingOrAbove(&Allocator, Key, &pNode); + if (rc == VINF_SUCCESS) + { + uint32_t idxRet = pNode->Key / cbStep; + if (idxRet != idxAbove) + RTTestIFailed("post-remove lookupMatchingOrAbove wrong: idxRet=%#x idxAbove=%#x idx=%#x", + idxRet, idxAbove, idx); + } + else if (rc == VERR_NOT_FOUND) + { + if (idxAbove != UINT32_MAX) + RTTestIFailed("post-remove lookupMatchingOrAbove wrong: VERR_NOT_FOUND idxAbove=%#x idx=%#x", idxAbove, idx); + } + else + { + RTTestIFailed("post-remove lookupMatchingOrAbove failed: %Rrc, i=%#x, idx=%#x (%RGp ... %RGp) idxAbove=%#x", + rc, i, idx, Key, Key + cbStep - 1, idxAbove); + } + } + + /* check the height */ + uint8_t const cHeight = Tree.getHeight(&Allocator); + uint32_t const cMax = RT_BIT_32(cHeight); + if (cInserted > cMax || cInserted < (cMax >> 4)) + RTTestIFailed("wrong tree height after random removal %#x: cMax=%#x, cInserted=%#x, cHeight=%u\n", + i, cMax, cInserted, cHeight); + } + + /* + * Randomized operation. + */ + uSeed = RTRandU64(); + RTRandAdvSeed(g_hRand, uSeed); + RTTestIPrintf(RTTESTLVL_ALWAYS, "Random seed #2: %#RX64\n", uSeed); + uint64_t cItemsEnumed = 0; + bool fAdding = true; + uint64_t const nsStart = RTTimeNanoTS(); + unsigned i; + for (i = 0, cInserted = 0; i < _64M; i++) + { + /* The operation. */ + bool fDelete; + if (cInserted == cItems) + { + fDelete = true; + fAdding = false; + } + else if (cInserted == 0) + { + fDelete = false; + fAdding = true; + } + else + fDelete = fAdding ? RTRandU32Ex(0, 3) == 1 : RTRandU32Ex(0, 3) != 0; + + if (!fDelete) + { + uint32_t const idxInsert = PickClearBitAndSetIt(pbmPresent, cItems); + + MYTESTNODE *pNode = Allocator.allocateNode(); + if (!pNode) + return RTTestIFailed("out of nodes: cInserted=%#x cItems=%#x i=%#x", cInserted, cItems, i); + pNode->Key = idxInsert * cbStep; + pNode->KeyLast = pNode->Key + cbStep - 1; + int rc = Tree.insert(&Allocator, pNode); + if (rc == VINF_SUCCESS) + cInserted += 1; + else + { + RTTestIFailed("random insert failed: %Rrc - %RGp ... %RGp cInserted=%#x cItems=%#x i=%#x", + rc, pNode->Key, pNode->KeyLast, cInserted, cItems, i); + Allocator.freeNode(pNode); + } + } + else + { + uint32_t const idxDelete = PickSetBitAndClearIt(pbmPresent, cItems); + + MYTESTNODE *pNode = (MYTESTNODE *)(intptr_t)idxDelete; + int rc = Tree.remove(&Allocator, idxDelete * cbStep, &pNode); + if (rc == VINF_SUCCESS) + { + if ( pNode->Key != idxDelete * cbStep + || pNode->KeyLast != idxDelete * cbStep + cbStep - 1) + RTTestIFailed("random remove returned wrong node: %RGp ... %RGp, expected %RGp ... %RGp (cInserted=%#x cItems=%#x i=%#x)", + pNode->Key, pNode->KeyLast, idxDelete * cbStep, idxDelete * cbStep + cbStep - 1, + cInserted, cItems, i); + + cInserted -= 1; + rc = Allocator.freeNode(pNode); + if (rc != VINF_SUCCESS) + RTTestIFailed("free after random removal failed: %Rrc - pNode=%p i=%#x", rc, pNode, i); + } + else + RTTestIFailed("random remove failed: %Rrc - %RGp ... %RGp cInserted=%#x cItems=%#x i=%#x", + rc, idxDelete * cbStep, idxDelete * cbStep + cbStep - 1, cInserted, cItems, i); + } + + /* Count the tree items. This will make sure the tree is balanced in strict builds. */ + uint32_t cCount = 0; + int rc = Tree.doWithAllFromLeft(&Allocator, hardAvlRangeTreeGCPhysEnumCallbackCount, &cCount); + if (rc != VINF_SUCCESS) + RTTestIFailed("enum after random %s failed: %Rrc - i=%#x", fDelete ? "removal" : "insert", rc, i); + else if (cCount != cInserted) + RTTestIFailed("wrong count after random %s: %#x, expected %#x - i=%#x", + fDelete ? "removal" : "insert", cCount, cInserted, i); + cItemsEnumed += cCount; + + /* check the height */ + uint8_t const cHeight = Tree.getHeight(&Allocator); + uint32_t const cMax = RT_BIT_32(cHeight); + if (cInserted > cMax || cInserted < (cMax >> 4)) + RTTestIFailed("wrong tree height after random %s: cMax=%#x, cInserted=%#x, cHeight=%u - i=%#x\n", + fDelete ? "removal" : "insert", cMax, cInserted, cHeight, i); + + /* Check for timeout. */ + if ( (i & 0xffff) == 0 + && RTTimeNanoTS() - nsStart >= RT_NS_15SEC) + break; + } + uint64_t cNsElapsed = RTTimeNanoTS() - nsStart; + RTTestIPrintf(RTTESTLVL_ALWAYS, "Performed %'u operations and enumerated %'RU64 nodes in %'RU64 ns\n", + i, cItemsEnumed, cNsElapsed); + + RTTestIValue("Operations rate", (uint64_t)i * RT_NS_1SEC / RT_MAX(cNsElapsed, 1), RTTESTUNIT_OCCURRENCES_PER_SEC); + RTTestIValue("Nodes enumeration rate", + (uint64_t)((double)cItemsEnumed * (double)RT_NS_1SEC / (double)RT_MAX(cNsElapsed, 1)), + RTTESTUNIT_OCCURRENCES_PER_SEC); + + return 0; +} + + +int main() +{ + /* + * Init. + */ + RTTEST hTest; + int rc = RTTestInitAndCreate("tstRTAvl", &hTest); + if (rc) + return rc; + RTTestBanner(hTest); + g_hTest = hTest; + + rc = RTRandAdvCreateParkMiller(&g_hRand); + if (RT_FAILURE(rc)) + { + RTTestIFailed("RTRandAdvCreateParkMiller -> %Rrc", rc); + return RTTestSummaryAndDestroy(hTest); + } + + /* + * Testing. + */ + unsigned i; + RTTestSub(hTest, "oGCPhys(32..2048)"); + for (i = 32; i < 2048; i++) + if (avlogcphys(i)) + break; + + avlogcphys(_64K); + avlogcphys(_512K); + avlogcphys(_4M); + + RTTestISubF("oGCPhys(32..2048, *1K)"); + for (i = 32; i < 4096; i++) + if (avlogcphysRand(i, i + _1K, 0xff)) + break; + for (; i <= _4M; i *= 2) + if (avlogcphysRand(i, i * 8, i * 2 - 1)) + break; + + avlrogcphys(); + avlul(); + + hardAvlRangeTreeGCPhys(hTest); + + /* + * Done. + */ + return RTTestSummaryAndDestroy(hTest); +} + |