summaryrefslogtreecommitdiffstats
path: root/include/iprt/cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /include/iprt/cpp
parentInitial commit. (diff)
downloadvirtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz
virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'include/iprt/cpp')
-rw-r--r--include/iprt/cpp/Makefile.kup0
-rw-r--r--include/iprt/cpp/autores.h216
-rw-r--r--include/iprt/cpp/exception.h118
-rw-r--r--include/iprt/cpp/hardavlrange.h1284
-rw-r--r--include/iprt/cpp/hardavlslaballocator.h218
-rw-r--r--include/iprt/cpp/list.h1143
-rw-r--r--include/iprt/cpp/lock.h179
-rw-r--r--include/iprt/cpp/meta.h125
-rw-r--r--include/iprt/cpp/ministring.h1638
-rw-r--r--include/iprt/cpp/mtlist.h185
-rw-r--r--include/iprt/cpp/path.h249
-rw-r--r--include/iprt/cpp/restanyobject.h139
-rw-r--r--include/iprt/cpp/restarray.h463
-rw-r--r--include/iprt/cpp/restbase.h1106
-rw-r--r--include/iprt/cpp/restclient.h826
-rw-r--r--include/iprt/cpp/restoutput.h280
-rw-r--r--include/iprt/cpp/reststringmap.h499
-rw-r--r--include/iprt/cpp/utils.h149
-rw-r--r--include/iprt/cpp/xml.h1247
19 files changed, 10064 insertions, 0 deletions
diff --git a/include/iprt/cpp/Makefile.kup b/include/iprt/cpp/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/include/iprt/cpp/Makefile.kup
diff --git a/include/iprt/cpp/autores.h b/include/iprt/cpp/autores.h
new file mode 100644
index 00000000..a620de7a
--- /dev/null
+++ b/include/iprt/cpp/autores.h
@@ -0,0 +1,216 @@
+/** @file
+ * IPRT - C++ Resource Management.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_autores_h
+#define IPRT_INCLUDED_cpp_autores_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/cpp/utils.h>
+
+
+
+/** @defgroup grp_rt_cpp_autores C++ Resource Management
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * A callable class template which returns the correct value against which an
+ * IPRT type must be compared to see if it is invalid.
+ *
+ * @warning This template *must* be specialised for the types it is to work with.
+ */
+template <class T>
+inline T RTAutoResNil(void)
+{
+ AssertFatalMsgFailed(("Unspecialized template!\n"));
+ return (T)0;
+}
+
+/** Specialisation of RTAutoResNil for RTFILE */
+template <>
+inline RTFILE RTAutoResNil(void)
+{
+ return NIL_RTFILE;
+}
+
+/**
+ * A function template which calls the correct destructor for an IPRT type.
+ *
+ * @warning This template *must* be specialised for the types it is to work with.
+ */
+template <class T>
+inline void RTAutoResDestruct(T a_h)
+{
+ AssertFatalMsgFailed(("Unspecialized template!\n"));
+ NOREF(a_h);
+}
+
+/**
+ * An auto pointer-type class for resources which take a C-style destructor
+ * (RTMemFree() or equivalent).
+ *
+ * The idea of this class is to manage resources which the current code is
+ * responsible for freeing. By wrapping the resource in an RTCAutoRes, you
+ * ensure that the resource will be freed when you leave the scope in which
+ * the RTCAutoRes is defined, unless you explicitly release the resource.
+ *
+ * A typical use case is when a function is allocating a number of resources.
+ * If any single allocation fails then all other resources must be freed. If
+ * all allocations succeed, then the resources should be returned to the
+ * caller. By placing all allocated resources in RTCAutoRes containers, you
+ * ensure that they will be freed on failure, and only have to take care of
+ * releasing them when you return them.
+ *
+ * @param T The type of the resource.
+ * @param Destruct The function to be used to free the resource.
+ * This parameter must be supplied if there is no
+ * specialisation of RTAutoDestruct available for @a T.
+ * @param NilRes The function returning the NIL value for T. Required.
+ * This parameter must be supplied if there is no
+ * specialisation of RTAutoResNil available for @a T.
+ *
+ * @note The class can not be initialised directly using assignment, due
+ * to the lack of a copy constructor. This is intentional.
+ */
+template <class T, void Destruct(T) = RTAutoResDestruct<T>, T NilRes(void) = RTAutoResNil<T> >
+class RTCAutoRes
+ : public RTCNonCopyable
+{
+protected:
+ /** The resource handle. */
+ T m_hRes;
+
+public:
+ /**
+ * Constructor
+ *
+ * @param a_hRes The handle to resource to manage. Defaults to NIL.
+ */
+ RTCAutoRes(T a_hRes = NilRes())
+ : m_hRes(a_hRes)
+ {
+ }
+
+ /**
+ * Destructor.
+ *
+ * This destroys any resource currently managed by the object.
+ */
+ ~RTCAutoRes()
+ {
+ if (m_hRes != NilRes())
+ Destruct(m_hRes);
+ }
+
+ /**
+ * Assignment from a value.
+ *
+ * This destroys any resource currently managed by the object
+ * before taking on the new one.
+ *
+ * @param a_hRes The handle to the new resource.
+ */
+ RTCAutoRes &operator=(T a_hRes)
+ {
+ if (m_hRes != NilRes())
+ Destruct(m_hRes);
+ m_hRes = a_hRes;
+ return *this;
+ }
+
+ /**
+ * Checks if the resource handle is NIL or not.
+ */
+ bool operator!()
+ {
+ return m_hRes == NilRes();
+ }
+
+ /**
+ * Give up ownership the current resource, handing it to the caller.
+ *
+ * @returns The current resource handle.
+ *
+ * @note Nothing happens to the resource when the object goes out of scope.
+ */
+ T release(void)
+ {
+ T Tmp = m_hRes;
+ m_hRes = NilRes();
+ return Tmp;
+ }
+
+ /**
+ * Deletes the current resources.
+ *
+ * @param a_hRes Handle to a new resource to manage. Defaults to NIL.
+ */
+ void reset(T a_hRes = NilRes())
+ {
+ if (a_hRes != m_hRes)
+ {
+ if (m_hRes != NilRes())
+ Destruct(m_hRes);
+ m_hRes = a_hRes;
+ }
+ }
+
+ /**
+ * Get the raw resource handle.
+ *
+ * Typically used passing the handle to some IPRT function while
+ * the object remains in scope.
+ *
+ * @returns The raw resource handle.
+ */
+ T get(void)
+ {
+ return m_hRes;
+ }
+};
+
+/** @} */
+
+
+/* include after template definition */
+#include <iprt/mem.h>
+
+#endif /* !IPRT_INCLUDED_cpp_autores_h */
+
diff --git a/include/iprt/cpp/exception.h b/include/iprt/cpp/exception.h
new file mode 100644
index 00000000..0c9f09b7
--- /dev/null
+++ b/include/iprt/cpp/exception.h
@@ -0,0 +1,118 @@
+/** @file
+ * IPRT - C++ Base Exceptions.
+ */
+
+/*
+ * Copyright (C) 2006-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_exception_h
+#define IPRT_INCLUDED_cpp_exception_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/ministring.h>
+#include <exception>
+
+#if RT_MSC_PREREQ(RT_MSC_VER_VC140)
+# pragma warning(push)
+# pragma warning(disable:4275) /* non dll-interface class 'std::exception' used as base for dll-interface class 'RTCError' */
+#endif
+
+
+/** @defgroup grp_rt_cpp_exceptions C++ Exceptions
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Base exception class for IPRT, derived from std::exception.
+ * The XML exceptions are based on this.
+ */
+class RT_DECL_CLASS RTCError
+ : public std::exception
+{
+public:
+
+ RTCError(const char *pszMessage)
+ : m_strMsg(pszMessage)
+ {
+ }
+
+ RTCError(const RTCString &a_rstrMessage)
+ : m_strMsg(a_rstrMessage)
+ {
+ }
+
+ RTCError(const RTCError &a_rSrc)
+ : std::exception(a_rSrc),
+ m_strMsg(a_rSrc.what())
+ {
+ }
+
+ virtual ~RTCError() throw()
+ {
+ }
+
+ void operator=(const RTCError &a_rSrc)
+ {
+ m_strMsg = a_rSrc.what();
+ }
+
+ void setWhat(const char *a_pszMessage)
+ {
+ m_strMsg = a_pszMessage;
+ }
+
+ virtual const char *what() const throw()
+ {
+ return m_strMsg.c_str();
+ }
+
+private:
+ /**
+ * Hidden default constructor making sure that the extended one above is
+ * always used.
+ */
+ RTCError();
+
+protected:
+ /** The exception message. */
+ RTCString m_strMsg;
+};
+
+/** @} */
+
+#if RT_MSC_PREREQ(RT_MSC_VER_VC140)
+# pragma warning(pop)
+#endif
+#endif /* !IPRT_INCLUDED_cpp_exception_h */
+
diff --git a/include/iprt/cpp/hardavlrange.h b/include/iprt/cpp/hardavlrange.h
new file mode 100644
index 00000000..7020968e
--- /dev/null
+++ b/include/iprt/cpp/hardavlrange.h
@@ -0,0 +1,1284 @@
+/** @file
+ * IPRT - Hardened AVL tree, unique key ranges.
+ */
+
+/*
+ * Copyright (C) 2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_hardavlrange_h
+#define IPRT_INCLUDED_cpp_hardavlrange_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/hardavlslaballocator.h>
+
+/** @defgroup grp_rt_cpp_hardavl Hardened AVL Trees
+ * @{
+ */
+
+/**
+ * Check that the tree heights make sense for the current node.
+ *
+ * This is a RT_STRICT test as it's expensive and we should have sufficient
+ * other checks to ensure safe AVL tree operation.
+ *
+ * @note the a_cStackEntries parameter is a hack to avoid running into gcc's
+ * "the address of 'AVLStack' will never be NULL" errors.
+ */
+#ifdef RT_STRICT
+# define RTHARDAVL_STRICT_CHECK_HEIGHTS(a_pNode, a_pAvlStack, a_cStackEntries) do { \
+ NodeType * const pLeftNodeX = a_pAllocator->ptrFromInt(readIdx(&(a_pNode)->idxLeft)); \
+ AssertReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNodeX), m_cErrors++, a_pAllocator->ptrErrToStatus((a_pNode))); \
+ NodeType * const pRightNodeX = a_pAllocator->ptrFromInt(readIdx(&(a_pNode)->idxRight)); \
+ AssertReturnStmt(a_pAllocator->isPtrRetOkay(pRightNodeX), m_cErrors++, a_pAllocator->ptrErrToStatus((a_pNode))); \
+ uint8_t const cLeftHeightX = pLeftNodeX ? pLeftNodeX->cHeight : 0; \
+ uint8_t const cRightHeightX = pRightNodeX ? pRightNodeX->cHeight : 0; \
+ if (RT_LIKELY((a_pNode)->cHeight == RT_MAX(cLeftHeightX, cRightHeightX) + 1)) { /*likely*/ } \
+ else \
+ { \
+ RTAssertMsg2("line %u: %u l=%u r=%u\n", __LINE__, (a_pNode)->cHeight, cLeftHeightX, cRightHeightX); \
+ if ((a_cStackEntries)) dumpStack(a_pAllocator, (a_pAvlStack)); \
+ AssertMsgReturnStmt((a_pNode)->cHeight == RT_MAX(cLeftHeightX, cRightHeightX) + 1, \
+ ("%u l=%u r=%u\n", (a_pNode)->cHeight, cLeftHeightX, cRightHeightX), \
+ m_cErrors++, VERR_HARDAVL_BAD_HEIGHT); \
+ } \
+ AssertMsgReturnStmt(RT_ABS(cLeftHeightX - cRightHeightX) <= 1, ("l=%u r=%u\n", cLeftHeightX, cRightHeightX), \
+ m_cErrors++, VERR_HARDAVL_UNBALANCED); \
+ Assert(!pLeftNodeX || pLeftNodeX->Key < (a_pNode)->Key); \
+ Assert(!pRightNodeX || pRightNodeX->Key > (a_pNode)->Key); \
+ } while (0)
+#else
+# define RTHARDAVL_STRICT_CHECK_HEIGHTS(a_pNode, a_pAvlStack, a_cStackEntries) do { } while (0)
+#endif
+
+
+/**
+ * Hardened AVL tree for nodes with key ranges.
+ *
+ * This is very crude and therefore expects the NodeType to feature:
+ * - Key and KeyLast members of KeyType.
+ * - idxLeft and idxRight members with type uint32_t.
+ * - cHeight members of type uint8_t.
+ *
+ * The code is very C-ish because of it's sources and initial use (ring-0
+ * without C++ exceptions enabled).
+ */
+template<typename NodeType, typename KeyType>
+struct RTCHardAvlRangeTree
+{
+ /** The root index. */
+ uint32_t m_idxRoot;
+ /** The error count. */
+ uint32_t m_cErrors;
+ /** @name Statistics
+ * @{ */
+ uint64_t m_cInserts;
+ uint64_t m_cRemovals;
+ uint64_t m_cRebalancingOperations;
+ /** @} */
+
+ /** The max stack depth. */
+ enum { kMaxStack = 28 };
+ /** The max height value we allow. */
+ enum { kMaxHeight = kMaxStack + 1 };
+
+ /** A stack used internally to avoid recursive calls.
+ * This is used with operations invoking i_rebalance(). */
+ typedef struct HardAvlStack
+ {
+ /** Number of entries on the stack. */
+ unsigned cEntries;
+ /** The stack. */
+ uint32_t *apidxEntries[kMaxStack];
+ } HardAvlStack;
+
+ /** @name Key comparisons
+ * @{ */
+ static inline int areKeyRangesIntersecting(KeyType a_Key1First, KeyType a_Key2First,
+ KeyType a_Key1Last, KeyType a_Key2Last) RT_NOEXCEPT
+ {
+ return a_Key1First <= a_Key2Last && a_Key1Last >= a_Key2First;
+ }
+
+ static inline int isKeyInRange(KeyType a_Key, KeyType a_KeyFirst, KeyType a_KeyLast) RT_NOEXCEPT
+ {
+ return a_Key <= a_KeyLast && a_Key >= a_KeyFirst;
+ }
+
+ static inline int isKeyGreater(KeyType a_Key1, KeyType a_Key2) RT_NOEXCEPT
+ {
+ return a_Key1 > a_Key2;
+ }
+ /** @} */
+
+ /**
+ * Read an index value trying to prevent the compiler from re-reading it.
+ */
+ DECL_FORCE_INLINE(uint32_t) readIdx(uint32_t volatile *pidx) RT_NOEXCEPT
+ {
+ uint32_t idx = *pidx;
+ ASMCompilerBarrier();
+ return idx;
+ }
+
+ RTCHardAvlRangeTree() RT_NOEXCEPT
+ : m_idxRoot(0)
+ , m_cErrors(0)
+ { }
+
+ RTCHardAvlRangeTree(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator) RT_NOEXCEPT
+ {
+ initWithAllocator(a_pAllocator);
+ }
+
+ void initWithAllocator(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator) RT_NOEXCEPT
+ {
+ m_idxRoot = a_pAllocator->kNilIndex;
+ m_cErrors = 0;
+ }
+
+ /**
+ * Inserts a node into the AVL-tree.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_ALREADY_EXISTS if a node with overlapping key range exists.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_pNode Pointer to the node which is to be added.
+ *
+ * @code
+ * Find the location of the node (using binary tree algorithm.):
+ * LOOP until KAVL_NULL leaf pointer
+ * BEGIN
+ * Add node pointer pointer to the AVL-stack.
+ * IF new-node-key < node key THEN
+ * left
+ * ELSE
+ * right
+ * END
+ * Fill in leaf node and insert it.
+ * Rebalance the tree.
+ * @endcode
+ */
+ int insert(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, NodeType *a_pNode) RT_NOEXCEPT
+ {
+ KeyType const Key = a_pNode->Key;
+ KeyType const KeyLast = a_pNode->KeyLast;
+ AssertMsgReturn(Key <= KeyLast, ("Key=%#RX64 KeyLast=%#RX64\n", (uint64_t)Key, (uint64_t)KeyLast),
+ VERR_HARDAVL_INSERT_INVALID_KEY_RANGE);
+
+ uint32_t *pidxCurNode = &m_idxRoot;
+ HardAvlStack AVLStack;
+ AVLStack.cEntries = 0;
+ for (;;)
+ {
+ NodeType *pCurNode = a_pAllocator->ptrFromInt(readIdx(pidxCurNode));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pCurNode), ("*pidxCurNode=%#x pCurNode=%p\n", *pidxCurNode, pCurNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pCurNode));
+ if (!pCurNode)
+ break;
+
+ unsigned const cEntries = AVLStack.cEntries;
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(AVLStack.apidxEntries),
+ ("%p[%#x/%p] %p[%#x] %p[%#x] %p[%#x] %p[%#x] %p[%#x]\n", pidxCurNode, *pidxCurNode, pCurNode,
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ AVLStack.apidxEntries[cEntries] = pidxCurNode;
+ AVLStack.cEntries = cEntries + 1;
+
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pCurNode, &AVLStack, AVLStack.cEntries);
+
+ /* Range check: */
+ if (areKeyRangesIntersecting(pCurNode->Key, Key, pCurNode->KeyLast, KeyLast))
+ return VERR_ALREADY_EXISTS;
+
+ /* Descend: */
+ if (isKeyGreater(pCurNode->Key, Key))
+ pidxCurNode = &pCurNode->idxLeft;
+ else
+ pidxCurNode = &pCurNode->idxRight;
+ }
+
+ a_pNode->idxLeft = a_pAllocator->kNilIndex;
+ a_pNode->idxRight = a_pAllocator->kNilIndex;
+ a_pNode->cHeight = 1;
+
+ uint32_t const idxNode = a_pAllocator->ptrToInt(a_pNode);
+ AssertMsgReturn(a_pAllocator->isIdxRetOkay(idxNode), ("pNode=%p idxNode=%#x\n", a_pNode, idxNode),
+ a_pAllocator->idxErrToStatus(idxNode));
+ *pidxCurNode = idxNode;
+
+ m_cInserts++;
+ return i_rebalance(a_pAllocator, &AVLStack);
+ }
+
+ /**
+ * Removes a node from the AVL-tree by a key value.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if not found.
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_Key A key value in the range of the node to be removed.
+ * @param a_ppRemoved Where to return the pointer to the removed node.
+ *
+ * @code
+ * Find the node which is to be removed:
+ * LOOP until not found
+ * BEGIN
+ * Add node pointer pointer to the AVL-stack.
+ * IF the keys matches THEN break!
+ * IF remove key < node key THEN
+ * left
+ * ELSE
+ * right
+ * END
+ * IF found THEN
+ * BEGIN
+ * IF left node not empty THEN
+ * BEGIN
+ * Find the right most node in the left tree while adding the pointer to the pointer to it's parent to the stack:
+ * Start at left node.
+ * LOOP until right node is empty
+ * BEGIN
+ * Add to stack.
+ * go right.
+ * END
+ * Link out the found node.
+ * Replace the node which is to be removed with the found node.
+ * Correct the stack entry for the pointer to the left tree.
+ * END
+ * ELSE
+ * BEGIN
+ * Move up right node.
+ * Remove last stack entry.
+ * END
+ * Balance tree using stack.
+ * END
+ * return pointer to the removed node (if found).
+ * @endcode
+ */
+ int remove(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, KeyType a_Key, NodeType **a_ppRemoved) RT_NOEXCEPT
+ {
+ *a_ppRemoved = NULL;
+
+ /*
+ * Walk the tree till we locate the node that is to be deleted.
+ */
+ uint32_t *pidxDeleteNode = &m_idxRoot;
+ NodeType *pDeleteNode;
+ HardAvlStack AVLStack;
+ AVLStack.cEntries = 0;
+ for (;;)
+ {
+ pDeleteNode = a_pAllocator->ptrFromInt(readIdx(pidxDeleteNode));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pDeleteNode),
+ ("*pidxCurNode=%#x pDeleteNode=%p\n", *pidxDeleteNode, pDeleteNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pDeleteNode));
+ if (pDeleteNode)
+ { /*likely*/ }
+ else
+ return VERR_NOT_FOUND;
+
+ unsigned const cEntries = AVLStack.cEntries;
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(AVLStack.apidxEntries),
+ ("%p[%#x/%p] %p[%#x] %p[%#x] %p[%#x] %p[%#x] %p[%#x]\n",
+ pidxDeleteNode, *pidxDeleteNode, pDeleteNode,
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ AVLStack.apidxEntries[cEntries] = pidxDeleteNode;
+ AVLStack.cEntries = cEntries + 1;
+
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pDeleteNode, &AVLStack, AVLStack.cEntries);
+
+ /* Range check: */
+ if (isKeyInRange(a_Key, pDeleteNode->Key, pDeleteNode->KeyLast))
+ break;
+
+ /* Descend: */
+ if (isKeyGreater(pDeleteNode->Key, a_Key))
+ pidxDeleteNode = &pDeleteNode->idxLeft;
+ else
+ pidxDeleteNode = &pDeleteNode->idxRight;
+ }
+
+ /*
+ * Do the deletion.
+ */
+ uint32_t const idxDeleteLeftNode = readIdx(&pDeleteNode->idxLeft);
+ if (idxDeleteLeftNode != a_pAllocator->kNilIndex)
+ {
+ /*
+ * Replace the deleted node with the rightmost node in the left subtree.
+ */
+ NodeType * const pDeleteLeftNode = a_pAllocator->ptrFromInt(idxDeleteLeftNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pDeleteLeftNode),
+ ("idxDeleteLeftNode=%#x pDeleteLeftNode=%p\n", idxDeleteLeftNode, pDeleteLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pDeleteLeftNode));
+
+ uint32_t const idxDeleteRightNode = readIdx(&pDeleteNode->idxRight);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxDeleteRightNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+
+ const unsigned iStackEntry = AVLStack.cEntries;
+
+ uint32_t *pidxLeftBiggest = &pDeleteNode->idxLeft;
+ uint32_t idxLeftBiggestNode = idxDeleteLeftNode;
+ NodeType *pLeftBiggestNode = pDeleteLeftNode;
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pLeftBiggestNode, &AVLStack, AVLStack.cEntries);
+
+ uint32_t idxRightTmp;
+ while ((idxRightTmp = readIdx(&pLeftBiggestNode->idxRight)) != a_pAllocator->kNilIndex)
+ {
+ unsigned const cEntries = AVLStack.cEntries;
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(AVLStack.apidxEntries),
+ ("%p[%#x/%p] %p[%#x] %p[%#x] %p[%#x] %p[%#x] %p[%#x]\n",
+ pidxLeftBiggest, *pidxLeftBiggest, pLeftBiggestNode,
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 1],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 2],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 3],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 4],
+ AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5], *AVLStack.apidxEntries[RT_ELEMENTS(AVLStack.apidxEntries) - 5]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ AVLStack.apidxEntries[cEntries] = pidxLeftBiggest;
+ AVLStack.cEntries = cEntries + 1;
+
+ pidxLeftBiggest = &pLeftBiggestNode->idxRight;
+ idxLeftBiggestNode = idxRightTmp;
+ pLeftBiggestNode = a_pAllocator->ptrFromInt(idxRightTmp);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftBiggestNode),
+ ("idxLeftBiggestNode=%#x pLeftBiggestNode=%p\n", idxLeftBiggestNode, pLeftBiggestNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftBiggestNode));
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pLeftBiggestNode, &AVLStack, AVLStack.cEntries);
+ }
+
+ uint32_t const idxLeftBiggestLeftNode = readIdx(&pLeftBiggestNode->idxLeft);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxLeftBiggestLeftNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+
+ /* link out pLeftBiggestNode */
+ *pidxLeftBiggest = idxLeftBiggestLeftNode;
+
+ /* link it in place of the deleted node. */
+ if (idxDeleteLeftNode != idxLeftBiggestNode)
+ pLeftBiggestNode->idxLeft = idxDeleteLeftNode;
+ pLeftBiggestNode->idxRight = idxDeleteRightNode;
+ pLeftBiggestNode->cHeight = AVLStack.cEntries > iStackEntry ? pDeleteNode->cHeight : 0;
+
+ *pidxDeleteNode = idxLeftBiggestNode;
+
+ if (AVLStack.cEntries > iStackEntry)
+ AVLStack.apidxEntries[iStackEntry] = &pLeftBiggestNode->idxLeft;
+ }
+ else
+ {
+ /* No left node, just pull up the right one. */
+ uint32_t const idxDeleteRightNode = readIdx(&pDeleteNode->idxRight);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxDeleteRightNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+ *pidxDeleteNode = idxDeleteRightNode;
+ AVLStack.cEntries--;
+ }
+ *a_ppRemoved = pDeleteNode;
+
+ m_cRemovals++;
+ return i_rebalance(a_pAllocator, &AVLStack);
+ }
+
+ /**
+ * Looks up a node from the tree.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if not found.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_Key A key value in the range of the desired node.
+ * @param a_ppFound Where to return the pointer to the node.
+ */
+ int lookup(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, KeyType a_Key, NodeType **a_ppFound) RT_NOEXCEPT
+ {
+ *a_ppFound = NULL;
+
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+#ifdef RT_STRICT
+ HardAvlStack AVLStack;
+ AVLStack.apidxEntries[0] = &m_idxRoot;
+ AVLStack.cEntries = 1;
+#endif
+ unsigned cDepth = 0;
+ while (pNode)
+ {
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, &AVLStack, AVLStack.cEntries);
+ AssertReturn(cDepth <= kMaxHeight, VERR_HARDAVL_LOOKUP_TOO_DEEP);
+ cDepth++;
+
+ if (isKeyInRange(a_Key, pNode->Key, pNode->KeyLast))
+ {
+ *a_ppFound = pNode;
+ return VINF_SUCCESS;
+ }
+ if (isKeyGreater(pNode->Key, a_Key))
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxLeft;
+#endif
+ uint32_t const idxLeft = readIdx(&pNode->idxLeft);
+ pNode = a_pAllocator->ptrFromInt(idxLeft);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("idxLeft=%#x pNode=%p\n", idxLeft, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+ }
+ else
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxRight;
+#endif
+ uint32_t const idxRight = readIdx(&pNode->idxRight);
+ pNode = a_pAllocator->ptrFromInt(idxRight);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("idxRight=%#x pNode=%p\n", idxRight, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+ }
+ }
+
+ return VERR_NOT_FOUND;
+ }
+
+ /**
+ * Looks up node matching @a a_Key or if no exact match the closest smaller than it.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if not found.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_Key A key value in the range of the desired node.
+ * @param a_ppFound Where to return the pointer to the node.
+ */
+ int lookupMatchingOrBelow(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, KeyType a_Key,
+ NodeType **a_ppFound) RT_NOEXCEPT
+ {
+ *a_ppFound = NULL;
+
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+#ifdef RT_STRICT
+ HardAvlStack AVLStack;
+ AVLStack.apidxEntries[0] = &m_idxRoot;
+ AVLStack.cEntries = 1;
+#endif
+ unsigned cDepth = 0;
+ NodeType *pNodeLast = NULL;
+ while (pNode)
+ {
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, &AVLStack, AVLStack.cEntries);
+ AssertReturn(cDepth <= kMaxHeight, VERR_HARDAVL_LOOKUP_TOO_DEEP);
+ cDepth++;
+
+ if (isKeyInRange(a_Key, pNode->Key, pNode->KeyLast))
+ {
+ *a_ppFound = pNode;
+ return VINF_SUCCESS;
+ }
+ if (isKeyGreater(pNode->Key, a_Key))
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxLeft;
+#endif
+ uint32_t const idxLeft = readIdx(&pNode->idxLeft);
+ NodeType *pLeftNode = a_pAllocator->ptrFromInt(idxLeft);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode), ("idxLeft=%#x pLeftNode=%p\n", idxLeft, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+ if (pLeftNode)
+ pNode = pLeftNode;
+ else if (!pNodeLast)
+ break;
+ else
+ {
+ *a_ppFound = pNodeLast;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxRight;
+#endif
+ uint32_t const idxRight = readIdx(&pNode->idxRight);
+ NodeType *pRightNode = a_pAllocator->ptrFromInt(idxRight);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode), ("idxRight=%#x pRightNode=%p\n", idxRight, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+ if (pRightNode)
+ {
+ pNodeLast = pNode;
+ pNode = pRightNode;
+ }
+ else
+ {
+ *a_ppFound = pNode;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ return VERR_NOT_FOUND;
+ }
+
+ /**
+ * Looks up node matching @a a_Key or if no exact match the closest larger than it.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if not found.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_Key A key value in the range of the desired node.
+ * @param a_ppFound Where to return the pointer to the node.
+ */
+ int lookupMatchingOrAbove(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, KeyType a_Key,
+ NodeType **a_ppFound) RT_NOEXCEPT
+ {
+ *a_ppFound = NULL;
+
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+#ifdef RT_STRICT
+ HardAvlStack AVLStack;
+ AVLStack.apidxEntries[0] = &m_idxRoot;
+ AVLStack.cEntries = 1;
+#endif
+ unsigned cDepth = 0;
+ NodeType *pNodeLast = NULL;
+ while (pNode)
+ {
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, &AVLStack, AVLStack.cEntries);
+ AssertReturn(cDepth <= kMaxHeight, VERR_HARDAVL_LOOKUP_TOO_DEEP);
+ cDepth++;
+
+ if (isKeyInRange(a_Key, pNode->Key, pNode->KeyLast))
+ {
+ *a_ppFound = pNode;
+ return VINF_SUCCESS;
+ }
+ if (isKeyGreater(pNode->Key, a_Key))
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxLeft;
+#endif
+ uint32_t const idxLeft = readIdx(&pNode->idxLeft);
+ NodeType *pLeftNode = a_pAllocator->ptrFromInt(idxLeft);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode), ("idxLeft=%#x pLeftNode=%p\n", idxLeft, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+ if (pLeftNode)
+ {
+ pNodeLast = pNode;
+ pNode = pLeftNode;
+ }
+ else
+ {
+ *a_ppFound = pNode;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+#ifdef RT_STRICT
+ AVLStack.apidxEntries[AVLStack.cEntries++] = &pNode->idxRight;
+#endif
+ uint32_t const idxRight = readIdx(&pNode->idxRight);
+ NodeType *pRightNode = a_pAllocator->ptrFromInt(idxRight);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode), ("idxRight=%#x pRightNode=%p\n", idxRight, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+ if (pRightNode)
+ pNode = pRightNode;
+ else if (!pNodeLast)
+ break;
+ else
+ {
+ *a_ppFound = pNodeLast;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ return VERR_NOT_FOUND;
+ }
+
+ /**
+ * A callback for doWithAllFromLeft and doWithAllFromRight.
+ *
+ * @returns IPRT status code. Any non-zero status causes immediate return from
+ * the enumeration function.
+ * @param pNode The current node.
+ * @param pvUser The user argument.
+ */
+ typedef DECLCALLBACKTYPE(int, FNCALLBACK,(NodeType *pNode, void *pvUser));
+ /** Pointer to a callback for doWithAllFromLeft and doWithAllFromRight. */
+ typedef FNCALLBACK *PFNCALLBACK;
+
+ /**
+ * Iterates thru all nodes in the tree from left (smaller) to right.
+ *
+ * @returns IPRT status code.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_pfnCallBack Pointer to callback function.
+ * @param a_pvUser Callback user argument.
+ *
+ * @note This is very similar code to doWithAllFromRight() and destroy().
+ */
+ int doWithAllFromLeft(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator,
+ PFNCALLBACK a_pfnCallBack, void *a_pvUser) RT_NOEXCEPT
+ {
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+ if (!pNode)
+ return VINF_SUCCESS;
+
+ /*
+ * We simulate recursive calling here. For safety reasons, we do not
+ * pop before going down the right tree like the original code did.
+ */
+ uint32_t cNodesLeft = a_pAllocator->m_cNodes;
+ NodeType *apEntries[kMaxStack];
+ uint8_t abState[kMaxStack];
+ unsigned cEntries = 1;
+ abState[0] = 0;
+ apEntries[0] = pNode;
+ while (cEntries > 0)
+ {
+ pNode = apEntries[cEntries - 1];
+ switch (abState[cEntries - 1])
+ {
+ /* Go left. */
+ case 0:
+ {
+ abState[cEntries - 1] = 1;
+
+ NodeType * const pLeftNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxLeft));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode),
+ ("idxLeft=%#x pLeftNode=%p\n", pNode->idxLeft, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+ if (pLeftNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pLeftNode, pNode->idxLeft, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pLeftNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ /* center then right. */
+ case 1:
+ {
+ abState[cEntries - 1] = 2;
+
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, NULL, 0);
+
+ int rc = a_pfnCallBack(pNode, a_pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ NodeType * const pRightNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxRight));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode),
+ ("idxRight=%#x pRightNode=%p\n", pNode->idxRight, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+ if (pRightNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pRightNode, pNode->idxRight, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pRightNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ default:
+ /* pop it. */
+ cEntries -= 1;
+ break;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /**
+ * Iterates thru all nodes in the tree from right (larger) to left (smaller).
+ *
+ * @returns IPRT status code.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_pfnCallBack Pointer to callback function.
+ * @param a_pvUser Callback user argument.
+ *
+ * @note This is very similar code to doWithAllFromLeft() and destroy().
+ */
+ int doWithAllFromRight(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator,
+ PFNCALLBACK a_pfnCallBack, void *a_pvUser) RT_NOEXCEPT
+ {
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+ if (!pNode)
+ return VINF_SUCCESS;
+
+ /*
+ * We simulate recursive calling here. For safety reasons, we do not
+ * pop before going down the right tree like the original code did.
+ */
+ uint32_t cNodesLeft = a_pAllocator->m_cNodes;
+ NodeType *apEntries[kMaxStack];
+ uint8_t abState[kMaxStack];
+ unsigned cEntries = 1;
+ abState[0] = 0;
+ apEntries[0] = pNode;
+ while (cEntries > 0)
+ {
+ pNode = apEntries[cEntries - 1];
+ switch (abState[cEntries - 1])
+ {
+ /* Go right. */
+ case 0:
+ {
+ abState[cEntries - 1] = 1;
+
+ NodeType * const pRightNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxRight));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode),
+ ("idxRight=%#x pRightNode=%p\n", pNode->idxRight, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+ if (pRightNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pRightNode, pNode->idxRight, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pRightNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ /* center then left. */
+ case 1:
+ {
+ abState[cEntries - 1] = 2;
+
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, NULL, 0);
+
+ int rc = a_pfnCallBack(pNode, a_pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ NodeType * const pLeftNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxLeft));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode),
+ ("idxLeft=%#x pLeftNode=%p\n", pNode->idxLeft, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+ if (pLeftNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pLeftNode, pNode->idxLeft, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pLeftNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ default:
+ /* pop it. */
+ cEntries -= 1;
+ break;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /**
+ * A callback for destroy to do additional cleanups before the node is freed.
+ *
+ * @param pNode The current node.
+ * @param pvUser The user argument.
+ */
+ typedef DECLCALLBACKTYPE(void, FNDESTROYCALLBACK,(NodeType *pNode, void *pvUser));
+ /** Pointer to a callback for destroy. */
+ typedef FNDESTROYCALLBACK *PFNDESTROYCALLBACK;
+
+ /**
+ * Destroys the tree, starting with the root node.
+ *
+ * This will invoke the freeNode() method on the allocate for every node after
+ * first doing the callback to let the caller free additional resources
+ * referenced by the node.
+ *
+ * @returns IPRT status code.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_pfnCallBack Pointer to callback function. Optional.
+ * @param a_pvUser Callback user argument.
+ *
+ * @note This is mostly the same code as the doWithAllFromLeft().
+ */
+ int destroy(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator,
+ PFNDESTROYCALLBACK a_pfnCallBack = NULL, void *a_pvUser = NULL) RT_NOEXCEPT
+ {
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+ if (!pNode)
+ return VINF_SUCCESS;
+
+ /*
+ * We simulate recursive calling here. For safety reasons, we do not
+ * pop before going down the right tree like the original code did.
+ */
+ uint32_t cNodesLeft = a_pAllocator->m_cNodes;
+ NodeType *apEntries[kMaxStack];
+ uint8_t abState[kMaxStack];
+ unsigned cEntries = 1;
+ abState[0] = 0;
+ apEntries[0] = pNode;
+ while (cEntries > 0)
+ {
+ pNode = apEntries[cEntries - 1];
+ switch (abState[cEntries - 1])
+ {
+ /* Go left. */
+ case 0:
+ {
+ abState[cEntries - 1] = 1;
+
+ NodeType * const pLeftNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxLeft));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode),
+ ("idxLeft=%#x pLeftNode=%p\n", pNode->idxLeft, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+ if (pLeftNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pLeftNode, pNode->idxLeft, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pLeftNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ /* right. */
+ case 1:
+ {
+ abState[cEntries - 1] = 2;
+
+ NodeType * const pRightNode = a_pAllocator->ptrFromInt(readIdx(&pNode->idxRight));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode),
+ ("idxRight=%#x pRightNode=%p\n", pNode->idxRight, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+ if (pRightNode)
+ {
+#if RT_GNUC_PREREQ_EX(4,7,1) && defined(RTASSERT_HAVE_STATIC_ASSERT) /* 32-bit 4.4.7 has trouble, dunno when it started working */
+ AssertCompile(kMaxStack > 6); /* exactly. Seems having static_assert is required. */
+#endif
+ AssertMsgReturnStmt(cEntries < RT_ELEMENTS(apEntries),
+ ("%p[%#x] %p %p %p %p %p %p\n", pRightNode, pNode->idxRight, apEntries[kMaxStack - 1],
+ apEntries[kMaxStack - 2], apEntries[kMaxStack - 3], apEntries[kMaxStack - 4],
+ apEntries[kMaxStack - 5], apEntries[kMaxStack - 6]),
+ m_cErrors++, VERR_HARDAVL_STACK_OVERFLOW);
+ apEntries[cEntries] = pRightNode;
+ abState[cEntries] = 0;
+ cEntries++;
+
+ AssertReturn(cNodesLeft > 0, VERR_HARDAVL_TRAVERSED_TOO_MANY_NODES);
+ cNodesLeft--;
+ break;
+ }
+ RT_FALL_THROUGH();
+ }
+
+ default:
+ {
+ /* pop it and destroy it. */
+ if (a_pfnCallBack)
+ a_pfnCallBack(pNode, a_pvUser);
+
+ int rc = a_pAllocator->freeNode(pNode);
+ AssertRCReturnStmt(rc, m_cErrors++, rc);
+
+ cEntries -= 1;
+ break;
+ }
+ }
+ }
+
+ Assert(m_idxRoot == a_pAllocator->kNilIndex);
+ return VINF_SUCCESS;
+ }
+
+
+ /**
+ * Gets the tree height value (reads cHeigh from the root node).
+ *
+ * @retval UINT8_MAX if bogus tree.
+ */
+ uint8_t getHeight(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator) RT_NOEXCEPT
+ {
+ NodeType *pNode = a_pAllocator->ptrFromInt(readIdx(&m_idxRoot));
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode), ("m_idxRoot=%#x pNode=%p\n", m_idxRoot, pNode),
+ m_cErrors++, UINT8_MAX);
+ if (pNode)
+ return pNode->cHeight;
+ return 0;
+ }
+
+#ifdef RT_STRICT
+
+ static void dumpStack(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, HardAvlStack const *pStack) RT_NOEXCEPT
+ {
+ uint32_t const * const *paidx = pStack->apidxEntries;
+ RTAssertMsg2("stack: %u:\n", pStack->cEntries);
+ for (unsigned i = 0; i < pStack->cEntries; i++)
+ {
+ uint32_t idx = *paidx[i];
+ uint32_t idxNext = i + 1 < pStack->cEntries ? *paidx[i + 1] : UINT32_MAX;
+ NodeType const *pNode = a_pAllocator->ptrFromInt(idx);
+ RTAssertMsg2(" #%02u: %p[%#06x] pNode=%p h=%02d l=%#06x%c r=%#06x%c\n", i, paidx[i], idx, pNode, pNode->cHeight,
+ pNode->idxLeft, pNode->idxLeft == idxNext ? '*' : ' ',
+ pNode->idxRight, pNode->idxRight == idxNext ? '*' : ' ');
+ }
+ }
+
+ static void printTree(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, uint32_t a_idxRoot,
+ unsigned a_uLevel = 0, unsigned a_uMaxLevel = 8, const char *a_pszDir = "") RT_NOEXCEPT
+ {
+ if (a_idxRoot == a_pAllocator->kNilIndex)
+ RTAssertMsg2("%*snil\n", a_uLevel * 6, a_pszDir);
+ else if (a_uLevel < a_uMaxLevel)
+ {
+ NodeType *pNode = a_pAllocator->ptrFromInt(a_idxRoot);
+ printTree(a_pAllocator, readIdx(&pNode->idxRight), a_uLevel + 1, a_uMaxLevel, "/ ");
+ RTAssertMsg2("%*s%#x/%u\n", a_uLevel * 6, a_pszDir, a_idxRoot, pNode->cHeight);
+ printTree(a_pAllocator, readIdx(&pNode->idxLeft), a_uLevel + 1, a_uMaxLevel, "\\ ");
+ }
+ else
+ RTAssertMsg2("%*stoo deep\n", a_uLevel * 6, a_pszDir);
+ }
+
+#endif
+
+private:
+ /**
+ * Rewinds a stack of pointers to pointers to nodes, rebalancing the tree.
+ *
+ * @returns IPRT status code.
+ *
+ * @param a_pAllocator Pointer to the allocator.
+ * @param a_pStack Pointer to stack to rewind.
+ * @param a_fLog Log is done (DEBUG builds only).
+ *
+ * @code
+ * LOOP thru all stack entries
+ * BEGIN
+ * Get pointer to pointer to node (and pointer to node) from the stack.
+ * IF 2 higher left subtree than in right subtree THEN
+ * BEGIN
+ * IF higher (or equal) left-sub-subtree than right-sub-subtree THEN
+ * * n+2|n+3
+ * / \ / \
+ * n+2 n ==> n+1 n+1|n+2
+ * / \ / \
+ * n+1 n|n+1 n|n+1 n
+ *
+ * Or with keys:
+ *
+ * 4 2
+ * / \ / \
+ * 2 5 ==> 1 4
+ * / \ / \
+ * 1 3 3 5
+ *
+ * ELSE
+ * * n+2
+ * / \ / \
+ * n+2 n n+1 n+1
+ * / \ ==> / \ / \
+ * n n+1 n L R n
+ * / \
+ * L R
+ *
+ * Or with keys:
+ * 6 4
+ * / \ / \
+ * 2 7 ==> 2 6
+ * / \ / \ / \
+ * 1 4 1 3 5 7
+ * / \
+ * 3 5
+ * END
+ * ELSE IF 2 higher in right subtree than in left subtree THEN
+ * BEGIN
+ * Same as above but left <==> right. (invert the picture)
+ * ELSE
+ * IF correct height THEN break
+ * ELSE correct height.
+ * END
+ * @endcode
+ * @internal
+ */
+ int i_rebalance(RTCHardAvlTreeSlabAllocator<NodeType> *a_pAllocator, HardAvlStack *a_pStack, bool a_fLog = false) RT_NOEXCEPT
+ {
+ RT_NOREF(a_fLog);
+
+ while (a_pStack->cEntries > 0)
+ {
+ /* pop */
+ uint32_t * const pidxNode = a_pStack->apidxEntries[--a_pStack->cEntries];
+ uint32_t const idxNode = readIdx(pidxNode);
+ NodeType * const pNode = a_pAllocator->ptrFromInt(idxNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pNode),
+ ("pidxNode=%p[%#x] pNode=%p\n", pidxNode, *pidxNode, pNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pNode));
+
+ /* Read node properties: */
+ uint32_t const idxLeftNode = readIdx(&pNode->idxLeft);
+ NodeType * const pLeftNode = a_pAllocator->ptrFromInt(idxLeftNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftNode),
+ ("idxLeftNode=%#x pLeftNode=%p\n", idxLeftNode, pLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftNode));
+
+ uint32_t const idxRightNode = readIdx(&pNode->idxRight);
+ NodeType * const pRightNode = a_pAllocator->ptrFromInt(idxRightNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightNode),
+ ("idxRight=%#x pRightNode=%p\n", idxRightNode, pRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightNode));
+
+ uint8_t const cLeftHeight = pLeftNode ? pLeftNode->cHeight : 0;
+ AssertReturnStmt(cLeftHeight <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_LEFT_HEIGHT);
+
+ uint8_t const cRightHeight = pRightNode ? pRightNode->cHeight : 0;
+ AssertReturnStmt(cRightHeight <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_RIGHT_HEIGHT);
+
+ /* Decide what needs doing: */
+ if (cRightHeight + 1 < cLeftHeight)
+ {
+ Assert(cRightHeight + 2 == cLeftHeight);
+ AssertReturnStmt(pLeftNode, m_cErrors++, VERR_HARDAVL_UNEXPECTED_NULL_LEFT);
+
+ uint32_t const idxLeftLeftNode = readIdx(&pLeftNode->idxLeft);
+ NodeType * const pLeftLeftNode = a_pAllocator->ptrFromInt(idxLeftLeftNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftLeftNode),
+ ("idxLeftLeftNode=%#x pLeftLeftNode=%p\n", idxLeftLeftNode, pLeftLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftLeftNode));
+
+ uint32_t const idxLeftRightNode = readIdx(&pLeftNode->idxRight);
+ NodeType * const pLeftRightNode = a_pAllocator->ptrFromInt(idxLeftRightNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pLeftRightNode),
+ ("idxLeftRightNode=%#x pLeftRightNode=%p\n", idxLeftRightNode, pLeftRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pLeftRightNode));
+
+ uint8_t const cLeftRightHeight = pLeftRightNode ? pLeftRightNode->cHeight : 0;
+ if ((pLeftLeftNode ? pLeftLeftNode->cHeight : 0) >= cLeftRightHeight)
+ {
+ AssertReturnStmt(cLeftRightHeight + 2 <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_NEW_HEIGHT);
+ pNode->idxLeft = idxLeftRightNode;
+ pNode->cHeight = (uint8_t)(cLeftRightHeight + 1);
+ pLeftNode->cHeight = (uint8_t)(cLeftRightHeight + 2);
+ pLeftNode->idxRight = idxNode;
+ *pidxNode = idxLeftNode;
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #1\n", a_pStack->cEntries);
+#endif
+ }
+ else
+ {
+ AssertReturnStmt(cLeftRightHeight <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_RIGHT_HEIGHT);
+ AssertReturnStmt(pLeftRightNode, m_cErrors++, VERR_HARDAVL_UNEXPECTED_NULL_RIGHT);
+
+ uint32_t const idxLeftRightLeftNode = readIdx(&pLeftRightNode->idxLeft);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxLeftRightLeftNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+ uint32_t const idxLeftRightRightNode = readIdx(&pLeftRightNode->idxRight);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxLeftRightRightNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+ pLeftNode->idxRight = idxLeftRightLeftNode;
+ pNode->idxLeft = idxLeftRightRightNode;
+
+ pLeftRightNode->idxLeft = idxLeftNode;
+ pLeftRightNode->idxRight = idxNode;
+ pLeftNode->cHeight = cLeftRightHeight;
+ pNode->cHeight = cLeftRightHeight;
+ pLeftRightNode->cHeight = cLeftHeight;
+ *pidxNode = idxLeftRightNode;
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #2\n", a_pStack->cEntries);
+#endif
+ }
+ m_cRebalancingOperations++;
+ }
+ else if (cLeftHeight + 1 < cRightHeight)
+ {
+ Assert(cLeftHeight + 2 == cRightHeight);
+ AssertReturnStmt(pRightNode, m_cErrors++, VERR_HARDAVL_UNEXPECTED_NULL_RIGHT);
+
+ uint32_t const idxRightLeftNode = readIdx(&pRightNode->idxLeft);
+ NodeType * const pRightLeftNode = a_pAllocator->ptrFromInt(idxRightLeftNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightLeftNode),
+ ("idxRightLeftNode=%#x pRightLeftNode=%p\n", idxRightLeftNode, pRightLeftNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightLeftNode));
+
+ uint32_t const idxRightRightNode = readIdx(&pRightNode->idxRight);
+ NodeType * const pRightRightNode = a_pAllocator->ptrFromInt(idxRightRightNode);
+ AssertMsgReturnStmt(a_pAllocator->isPtrRetOkay(pRightRightNode),
+ ("idxRightRightNode=%#x pRightRightNode=%p\n", idxRightRightNode, pRightRightNode),
+ m_cErrors++, a_pAllocator->ptrErrToStatus(pRightRightNode));
+
+ uint8_t const cRightLeftHeight = pRightLeftNode ? pRightLeftNode->cHeight : 0;
+ if ((pRightRightNode ? pRightRightNode->cHeight : 0) >= cRightLeftHeight)
+ {
+ AssertReturnStmt(cRightLeftHeight + 2 <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_NEW_HEIGHT);
+
+ pNode->idxRight = idxRightLeftNode;
+ pRightNode->idxLeft = idxNode;
+ pNode->cHeight = (uint8_t)(cRightLeftHeight + 1);
+ pRightNode->cHeight = (uint8_t)(cRightLeftHeight + 2);
+ *pidxNode = idxRightNode;
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #3 h=%d, *pidxNode=%#x\n", a_pStack->cEntries, pRightNode->cHeight, *pidxNode);
+#endif
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pRightNode, NULL, 0);
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, NULL, 0);
+ }
+ else
+ {
+ AssertReturnStmt(cRightLeftHeight <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_LEFT_HEIGHT);
+ AssertReturnStmt(pRightLeftNode, m_cErrors++, VERR_HARDAVL_UNEXPECTED_NULL_LEFT);
+
+ uint32_t const idxRightLeftRightNode = readIdx(&pRightLeftNode->idxRight);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxRightLeftRightNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+ uint32_t const idxRightLeftLeftNode = readIdx(&pRightLeftNode->idxLeft);
+ AssertReturnStmt(a_pAllocator->isIntValid(idxRightLeftLeftNode), m_cErrors++, VERR_HARDAVL_INDEX_OUT_OF_BOUNDS);
+ pRightNode->idxLeft = idxRightLeftRightNode;
+ pNode->idxRight = idxRightLeftLeftNode;
+
+ pRightLeftNode->idxRight = idxRightNode;
+ pRightLeftNode->idxLeft = idxNode;
+ pRightNode->cHeight = cRightLeftHeight;
+ pNode->cHeight = cRightLeftHeight;
+ pRightLeftNode->cHeight = cRightHeight;
+ *pidxNode = idxRightLeftNode;
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #4 h=%d, *pidxNode=%#x\n", a_pStack->cEntries, pRightLeftNode->cHeight, *pidxNode);
+#endif
+ }
+ m_cRebalancingOperations++;
+ }
+ else
+ {
+ uint8_t const cHeight = (uint8_t)(RT_MAX(cLeftHeight, cRightHeight) + 1);
+ AssertReturnStmt(cHeight <= kMaxHeight, m_cErrors++, VERR_HARDAVL_BAD_NEW_HEIGHT);
+ if (cHeight == pNode->cHeight)
+ {
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #5, h=%d - done\n", a_pStack->cEntries, cHeight);
+#endif
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pNode, NULL, 0);
+ if (pLeftNode)
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pLeftNode, NULL, 0);
+ if (pRightNode)
+ RTHARDAVL_STRICT_CHECK_HEIGHTS(pRightNode, NULL, 0);
+ break;
+ }
+#ifdef DEBUG
+ if (a_fLog) RTAssertMsg2("rebalance: %#2u: op #5, h=%d - \n", a_pStack->cEntries, cHeight);
+#endif
+ pNode->cHeight = cHeight;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_hardavlrange_h */
+
diff --git a/include/iprt/cpp/hardavlslaballocator.h b/include/iprt/cpp/hardavlslaballocator.h
new file mode 100644
index 00000000..515782b5
--- /dev/null
+++ b/include/iprt/cpp/hardavlslaballocator.h
@@ -0,0 +1,218 @@
+/** @file
+ * IPRT - Hardened AVL tree slab allocator.
+ */
+
+/*
+ * Copyright (C) 2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_hardavlslaballocator_h
+#define IPRT_INCLUDED_cpp_hardavlslaballocator_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+/** @addtogroup grp_rt_cpp_hardavl
+ * @{
+ */
+
+
+/**
+ * Slab allocator for the hardened AVL tree.
+ */
+template<typename NodeType>
+struct RTCHardAvlTreeSlabAllocator
+{
+ /** Pointer to an array of nodes. */
+ NodeType *m_paNodes;
+ /** Node allocation bitmap: 1 = free, 0 = allocated. */
+ uint64_t *m_pbmAlloc;
+ /** Max number of nodes in m_paNodes and valid bits in m_pbmAlloc. */
+ uint32_t m_cNodes;
+ /** Pointer error counter. */
+ uint32_t m_cErrors;
+ /** Allocation hint. */
+ uint32_t m_idxAllocHint;
+ uint32_t m_uPadding;
+
+ enum
+ {
+ kNilIndex = 0,
+ kErr_IndexOutOfBound = -1,
+ kErr_PointerOutOfBound = -2,
+ kErr_MisalignedPointer = -3,
+ kErr_NodeIsFree = -4,
+ kErr_Last = kErr_NodeIsFree
+ };
+
+ RTCHardAvlTreeSlabAllocator() RT_NOEXCEPT
+ : m_paNodes(NULL)
+ , m_pbmAlloc(NULL)
+ , m_cNodes(0)
+ , m_cErrors(0)
+ , m_idxAllocHint(0)
+ , m_uPadding(0)
+ {}
+
+ inline void initSlabAllocator(uint32_t a_cNodes, NodeType *a_paNodes, uint64_t *a_pbmAlloc) RT_NOEXCEPT
+ {
+ m_cNodes = a_cNodes;
+ m_paNodes = a_paNodes;
+ m_pbmAlloc = a_pbmAlloc;
+
+ /* Initialize the allocation bit. */
+ RT_BZERO(a_pbmAlloc, (a_cNodes + 63) / 64 * 8);
+ ASMBitSetRange(a_pbmAlloc, 0, a_cNodes);
+ }
+
+ inline NodeType *ptrFromInt(uint32_t a_idxNode1) RT_NOEXCEPT
+ {
+ if (a_idxNode1 == (uint32_t)kNilIndex)
+ return NULL;
+ AssertMsgReturnStmt(a_idxNode1 <= m_cNodes, ("a_idxNode1=%#x m_cNodes=%#x\n", a_idxNode1, m_cNodes),
+ m_cErrors++, (NodeType *)(intptr_t)kErr_IndexOutOfBound);
+ AssertMsgReturnStmt(ASMBitTest(m_pbmAlloc, a_idxNode1 - 1) == false, ("a_idxNode1=%#x\n", a_idxNode1),
+ m_cErrors++, (NodeType *)(intptr_t)kErr_NodeIsFree);
+ return &m_paNodes[a_idxNode1 - 1];
+ }
+
+ static inline bool isPtrRetOkay(NodeType *a_pNode) RT_NOEXCEPT
+ {
+ return (uintptr_t)a_pNode < (uintptr_t)kErr_Last;
+ }
+
+ static inline int ptrErrToStatus(NodeType *a_pNode) RT_NOEXCEPT
+ {
+ return (int)(intptr_t)a_pNode - (VERR_HARDAVL_INDEX_OUT_OF_BOUNDS - kErr_IndexOutOfBound);
+ }
+
+ inline uint32_t ptrToInt(NodeType *a_pNode) RT_NOEXCEPT
+ {
+ if (a_pNode == NULL)
+ return 0;
+ uintptr_t const offNode = (uintptr_t)a_pNode - (uintptr_t)m_paNodes;
+ uintptr_t const idxNode0 = offNode / sizeof(m_paNodes[0]);
+ AssertMsgReturnStmt((offNode % sizeof(m_paNodes[0])) == 0,
+ ("pNode=%p / offNode=%#zx vs m_paNodes=%p L %#x, each %#x bytes\n",
+ a_pNode, offNode, m_paNodes, m_cNodes, sizeof(m_paNodes[0])),
+ m_cErrors++, (uint32_t)kErr_MisalignedPointer);
+ AssertMsgReturnStmt(idxNode0 < m_cNodes,
+ ("pNode=%p vs m_paNodes=%p L %#x\n", a_pNode, m_paNodes, m_cNodes),
+ m_cErrors++, (uint32_t)kErr_PointerOutOfBound);
+ AssertMsgReturnStmt(ASMBitTest(m_pbmAlloc, idxNode0) == false, ("a_pNode=%p idxNode0=%#x\n", a_pNode, idxNode0),
+ m_cErrors++, (uint32_t)kErr_NodeIsFree);
+ return idxNode0 + 1;
+ }
+
+ static inline bool isIdxRetOkay(uint32_t a_idxNode) RT_NOEXCEPT
+ {
+ return a_idxNode < (uint32_t)kErr_Last;
+ }
+
+ static inline int idxErrToStatus(uint32_t a_idxNode) RT_NOEXCEPT
+ {
+ return (int)a_idxNode - (VERR_HARDAVL_INDEX_OUT_OF_BOUNDS - kErr_IndexOutOfBound);
+ }
+
+ inline bool isIntValid(uint32_t a_idxNode1) RT_NOEXCEPT
+ {
+ return a_idxNode1 <= m_cNodes;
+ }
+
+ inline int freeNode(NodeType *a_pNode) RT_NOEXCEPT
+ {
+ uint32_t idxNode1 = ptrToInt(a_pNode);
+ if (idxNode1 == (uint32_t)kNilIndex)
+ return 0;
+ if (idxNode1 < (uint32_t)kErr_Last)
+ {
+ AssertMsgReturnStmt(ASMAtomicBitTestAndSet(m_pbmAlloc, idxNode1 - 1) == false,
+ ("a_pNode=%p idxNode1=%#x\n", a_pNode, idxNode1),
+ m_cErrors++, kErr_NodeIsFree);
+ return 0;
+ }
+ return (int)idxNode1;
+ }
+
+ inline NodeType *allocateNode(void) RT_NOEXCEPT
+ {
+ /*
+ * Use the hint first, then scan the whole bitmap.
+ * Note! We don't expect concurrent allocation calls, so no need to repeat.
+ */
+ uint32_t const idxHint = m_idxAllocHint;
+ uint32_t idxNode0;
+ if ( idxHint >= m_cNodes
+ || (int32_t)(idxNode0 = (uint32_t)ASMBitNextSet(m_pbmAlloc, m_cNodes, idxHint)) < 0)
+ idxNode0 = (uint32_t)ASMBitFirstSet(m_pbmAlloc, m_cNodes);
+ if ((int32_t)idxNode0 >= 0)
+ {
+ if (ASMAtomicBitTestAndClear(m_pbmAlloc, idxNode0) == true)
+ {
+ m_idxAllocHint = idxNode0;
+ return &m_paNodes[idxNode0];
+ }
+ AssertMsgFailed(("idxNode0=%#x\n", idxNode0));
+ m_cErrors++;
+ }
+ return NULL;
+ }
+};
+
+
+/**
+ * Placeholder structure for ring-3 slab allocator.
+ */
+typedef struct RTCHardAvlTreeSlabAllocatorR3_T
+{
+ /** Pointer to an array of nodes. */
+ RTR3PTR m_paNodes;
+ /** Node allocation bitmap: 1 = free, 0 = allocated. */
+ RTR3PTR m_pbmAlloc;
+ /** Max number of nodes in m_paNodes and valid bits in m_pbmAlloc. */
+ uint32_t m_cNodes;
+ /** Pointer error counter. */
+ uint32_t m_cErrors;
+ /** Allocation hint. */
+ uint32_t m_idxAllocHint;
+ uint32_t m_uPadding;
+} RTCHardAvlTreeSlabAllocatorR3_T;
+AssertCompileSize(RTCHardAvlTreeSlabAllocatorR3_T,
+ sizeof(RTCHardAvlTreeSlabAllocator<RTUINT128U>) - (sizeof(void *) - sizeof(RTR3PTR)) * 2);
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_hardavlslaballocator_h */
+
diff --git a/include/iprt/cpp/list.h b/include/iprt/cpp/list.h
new file mode 100644
index 00000000..864c3329
--- /dev/null
+++ b/include/iprt/cpp/list.h
@@ -0,0 +1,1143 @@
+/** @file
+ * IPRT - Generic List Class.
+ */
+
+/*
+ * Copyright (C) 2011-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_list_h
+#define IPRT_INCLUDED_cpp_list_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/meta.h>
+#include <iprt/mem.h>
+#include <iprt/string.h> /* for memcpy */
+#include <iprt/assert.h>
+
+#include <new> /* For std::bad_alloc */
+
+/** @defgroup grp_rt_cpp_list C++ List support
+ * @ingroup grp_rt_cpp
+ *
+ * @brief Generic C++ list class support.
+ *
+ * This list classes manage any amount of data in a fast and easy to use way.
+ * They have no dependencies on STL, only on generic memory management methods
+ * of IRPT. This allows list handling in situations where the use of STL
+ * container classes is forbidden.
+ *
+ * Not all of the functionality of STL container classes is implemented. There
+ * are no iterators or any other high level access/modifier methods (e.g.
+ * std::algorithms).
+ *
+ * The implementation is array based which allows fast access to the items.
+ * Appending items is usually also fast, cause the internal array is
+ * preallocated. To minimize the memory overhead, native types (that is
+ * everything smaller then the size of void*) are directly saved in the array.
+ * If bigger types are used (e.g. RTCString) the internal array is an array of
+ * pointers to the objects.
+ *
+ * The size of the internal array will usually not shrink, but grow
+ * automatically. Only certain methods, like RTCList::clear or the "=" operator
+ * will reset any previously allocated memory. You can call
+ * RTCList::setCapacity for manual adjustment. If the size of an new list will
+ * be known, calling the constructor with the necessary capacity will speed up
+ * the insertion of the new items.
+ *
+ * For the full public interface these list classes offer see RTCListBase.
+ *
+ * There are some requirements for the types used which follow:
+ * -# They need a default and a copy constructor.
+ * -# Some methods (e.g. RTCList::contains) need an equal operator.
+ * -# If the type is some complex class (that is, having a constructor which
+ * allocates members on the heap) it has to be greater than sizeof(void*) to
+ * be used correctly. If this is not the case you can manually overwrite the
+ * list behavior. Just add T* as a second parameter to the list template if
+ * your class is called T. Another possibility is to specialize the list for
+ * your target class. See below for more information.
+ *
+ * The native types like int, bool, ptr, ..., are meeting this criteria, so
+ * they are save to use.
+ *
+ * Please note that the return type of some of the getter methods are slightly
+ * different depending on the list type. Native types return the item by value,
+ * items with a size greater than sizeof(void*) by reference. As native types
+ * saved directly in the internal array, returning a reference to them (and
+ * saving them in a reference as well) would make them invalid (or pointing to
+ * a wrong item) when the list is changed in the meanwhile. Returning a
+ * reference for bigger types isn't problematic and makes sure we get out the
+ * best speed of the list. The one exception to this rule is the index
+ * operator[]. This operator always return a reference to make it possible to
+ * use it as a lvalue. Its your responsibility to make sure the list isn't
+ * changed when using the value as reference returned by this operator.
+ *
+ * The list class is reentrant. For a thread-safe variant see RTCMTList.
+ *
+ * Implementation details:
+ * It is possible to specialize any type. This might be necessary to get the
+ * best speed out of the list. Examples are the 64-bit types, which use the
+ * native (no pointers) implementation even on a 32-bit host. Consult the
+ * source code for more details.
+ *
+ * Current specialized implementations:
+ * - int64_t: RTCList<int64_t>
+ * - uint64_t: RTCList<uint64_t>
+ *
+ * @{
+ */
+
+/**
+ * The guard definition.
+ */
+template <bool G>
+class RTCListGuard;
+
+/**
+ * The default guard which does nothing.
+ */
+template <>
+class RTCListGuard<false>
+{
+public:
+ inline void enterRead() const {}
+ inline void leaveRead() const {}
+ inline void enterWrite() {}
+ inline void leaveWrite() {}
+
+ /* Define our own new and delete. */
+#ifdef RT_NEED_NEW_AND_DELETE
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+};
+
+/**
+ * General helper template for managing native values in RTCListBase.
+ */
+template <typename T1, typename T2>
+class RTCListHelper
+{
+public:
+ static inline void set(T2 *p, size_t i, const T1 &v) { p[i] = v; }
+ static inline T1 & at(T2 *p, size_t i) { return p[i]; }
+ static inline const T1 &atConst(T2 const *p, size_t i) { return p[i]; }
+ static inline size_t find(T2 *p, const T1 &v, size_t cElements)
+ {
+ size_t i = cElements;
+ while (i-- > 0)
+ if (p[i] == v)
+ return i;
+ return cElements;
+ }
+ static inline void copyTo(T2 *p, T2 *const p1 , size_t iTo, size_t cSize)
+ {
+ if (cSize > 0)
+ memcpy(&p[iTo], &p1[0], sizeof(T1) * cSize);
+ }
+ static inline void erase(T2 * /* p */, size_t /* i */) { /* Nothing to do here. */ }
+ static inline void eraseRange(T2 * /* p */, size_t /* cFrom */, size_t /* cSize */) { /* Nothing to do here. */ }
+};
+
+/**
+ * Specialized helper template for managing pointer values in RTCListBase.
+ */
+template <typename T1>
+class RTCListHelper<T1, T1*>
+{
+public:
+ static inline void set(T1 **p, size_t i, const T1 &v) { p[i] = new T1(v); }
+ static inline T1 & at(T1 **p, size_t i) { return *p[i]; }
+ static inline const T1 &atConst(T1 * const *p, size_t i) { return *p[i]; }
+ static inline size_t find(T1 **p, const T1 &v, size_t cElements)
+ {
+ size_t i = cElements;
+ while (i-- > 0)
+ if (*p[i] == v)
+ return i;
+ return cElements;
+ }
+ static inline void copyTo(T1 **p, T1 **const p1 , size_t iTo, size_t cSize)
+ {
+ for (size_t i = 0; i < cSize; ++i)
+ p[iTo + i] = new T1(*p1[i]);
+ }
+ static inline void erase(T1 **p, size_t i) { delete p[i]; }
+ static inline void eraseRange(T1 **p, size_t iFrom, size_t cItems)
+ {
+ while (cItems-- > 0)
+ delete p[iFrom++];
+ }
+};
+
+/**
+ * This is the base class for all other list classes. It implements the
+ * necessary list functionality in a type independent way and offers the public
+ * list interface to the user.
+ */
+template <class T, typename ITYPE, bool MT>
+class RTCListBase
+{
+ /** @name Traits.
+ *
+ * Defines the return type of most of the getter methods. If the internal
+ * used type is a pointer, we return a reference. If not we return by
+ * value.
+ *
+ * @{
+ */
+ typedef typename RTCIfPtr<ITYPE, T&, T>::result GET_RTYPE;
+ typedef typename RTCIfPtr<ITYPE, const T&, T>::result GET_CRTYPE;
+ /** @} */
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCListBase(size_t cCapacity = kDefaultCapacity)
+ : m_pArray(0)
+ , m_cElements(0)
+ , m_cCapacity(0)
+ {
+ if (cCapacity > 0)
+ growArray(cCapacity);
+ }
+
+ /**
+ * Creates a copy of another list.
+ *
+ * The other list will be fully copied and the capacity will be the same as
+ * the size of the other list.
+ *
+ * @param other The list to copy.
+ * @throws std::bad_alloc
+ */
+ RTCListBase(const RTCListBase<T, ITYPE, MT>& other)
+ : m_pArray(0)
+ , m_cElements(0)
+ , m_cCapacity(0)
+ {
+ other.m_guard.enterRead();
+
+ size_t const cElementsOther = other.m_cElements;
+ resizeArrayNoErase(cElementsOther);
+ RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, cElementsOther);
+ m_cElements = cElementsOther;
+
+ other.m_guard.leaveRead();
+ }
+
+ /**
+ * Destructor.
+ */
+ ~RTCListBase()
+ {
+ RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
+ if (m_pArray)
+ {
+ RTMemFree(m_pArray);
+ m_pArray = NULL;
+ }
+ m_cElements = m_cCapacity = 0;
+ }
+
+ /**
+ * Sets a new capacity within the list.
+ *
+ * If the new capacity is bigger than the old size, it will be simply
+ * preallocated more space for the new items. If the new capacity is
+ * smaller than the previous size, items at the end of the list will be
+ * deleted.
+ *
+ * @param cCapacity The new capacity within the list.
+ * @throws std::bad_alloc
+ */
+ void setCapacity(size_t cCapacity)
+ {
+ m_guard.enterWrite();
+ resizeArray(cCapacity);
+ m_guard.leaveWrite();
+ }
+
+ /**
+ * Return the current capacity of the list.
+ *
+ * @return The actual capacity.
+ */
+ size_t capacity() const
+ {
+ m_guard.enterRead();
+ size_t cRet = m_cCapacity;
+ m_guard.leaveRead();
+ return cRet;
+ }
+
+ /**
+ * Check if an list contains any items.
+ *
+ * @return True if there is more than zero items, false otherwise.
+ */
+ bool isEmpty() const
+ {
+ m_guard.enterRead();
+ bool fEmpty = m_cElements == 0;
+ m_guard.leaveRead();
+ return fEmpty;
+ }
+
+ /**
+ * Return the current count of elements within the list.
+ *
+ * @return The current element count.
+ */
+ size_t size() const
+ {
+ m_guard.enterRead();
+ size_t cRet = m_cElements;
+ m_guard.leaveRead();
+ return cRet;
+ }
+
+ /**
+ * Inserts an item to the list at position @a i.
+ *
+ * @param i The position of the new item. The must be within or at the
+ * exact end of the list. Indexes specified beyond the end of
+ * the list will be changed to an append() operation and strict
+ * builds will raise an assert.
+ * @param val The new item.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &insert(size_t i, const T &val)
+ {
+ m_guard.enterWrite();
+
+ AssertMsgStmt(i <= m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements);
+
+ if (m_cElements == m_cCapacity)
+ growArray(m_cCapacity + kDefaultCapacity);
+
+ memmove(&m_pArray[i + 1], &m_pArray[i], (m_cElements - i) * sizeof(ITYPE));
+ RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
+ ++m_cElements;
+
+ m_guard.leaveWrite();
+ return *this;
+ }
+
+ /**
+ * Inserts a list to the list at position @a i.
+ *
+ * @param i The position of the new item. The must be within or at the
+ * exact end of the list. Indexes specified beyond the end of
+ * the list will be changed to an append() operation and strict
+ * builds will raise an assert.
+ * @param other The other list. This MUST not be the same as the destination
+ * list, will assert and return without doing anything if this
+ * happens.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &insert(size_t i, const RTCListBase<T, ITYPE, MT> &other)
+ {
+ AssertReturn(this != &other, *this);
+
+ other.m_guard.enterRead();
+ m_guard.enterWrite();
+
+ AssertMsgStmt(i <= m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements);
+
+ size_t cElementsOther = other.m_cElements;
+ if (RT_LIKELY(cElementsOther > 0))
+ {
+ if (m_cCapacity - m_cElements < cElementsOther)
+ growArray(m_cCapacity + (cElementsOther - (m_cCapacity - m_cElements)));
+ if (i < m_cElements)
+ memmove(&m_pArray[i + cElementsOther], &m_pArray[i], (m_cElements - i) * sizeof(ITYPE));
+
+ RTCListHelper<T, ITYPE>::copyTo(&m_pArray[i], other.m_pArray, 0, cElementsOther);
+ m_cElements += cElementsOther;
+ }
+
+ m_guard.leaveWrite();
+ other.m_guard.leaveRead();
+ return *this;
+ }
+
+ /**
+ * Prepend an item to the list.
+ *
+ * @param val The new item.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &prepend(const T &val)
+ {
+ return insert(0, val);
+ }
+
+ /**
+ * Prepend a list of type T to the list.
+ *
+ * @param other The list to prepend.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &prepend(const RTCListBase<T, ITYPE, MT> &other)
+ {
+ return insert(0, other);
+ }
+
+ /**
+ * Append a default item to the list.
+ *
+ * @return a mutable reference to the item
+ * @throws std::bad_alloc
+ */
+ GET_RTYPE append()
+ {
+ m_guard.enterWrite();
+ if (m_cElements == m_cCapacity)
+ growArray(m_cCapacity + kDefaultCapacity);
+ RTCListHelper<T, ITYPE>::set(m_pArray, m_cElements, T());
+ GET_RTYPE rRet = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements);
+ ++m_cElements;
+ m_guard.leaveWrite();
+
+ return rRet;
+ }
+
+ /**
+ * Append an item to the list.
+ *
+ * @param val The new item.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &append(const T &val)
+ {
+ m_guard.enterWrite();
+ if (m_cElements == m_cCapacity)
+ growArray(m_cCapacity + kDefaultCapacity);
+ RTCListHelper<T, ITYPE>::set(m_pArray, m_cElements, val);
+ ++m_cElements;
+ m_guard.leaveWrite();
+
+ return *this;
+ }
+
+ /**
+ * Append a list of type T to the list.
+ *
+ * @param other The list to append. Must not be the same as the destination
+ * list, will assert and return without doing anything.
+ * @return a reference to this list.
+ * @throws std::bad_alloc
+ */
+ RTCListBase<T, ITYPE, MT> &append(const RTCListBase<T, ITYPE, MT> &other)
+ {
+ AssertReturn(this != &other, *this);
+
+ other.m_guard.enterRead();
+ m_guard.enterWrite();
+
+ insert(m_cElements, other);
+
+ m_guard.leaveWrite();
+ other.m_guard.leaveRead();
+ return *this;
+ }
+
+ /**
+ * Copy the items of the other list into this list.
+ *
+ * All previous items of this list are deleted.
+ *
+ * @param other The list to copy.
+ * @return a reference to this list.
+ */
+ RTCListBase<T, ITYPE, MT> &operator=(const RTCListBase<T, ITYPE, MT>& other)
+ {
+ /* Prevent self assignment */
+ if (RT_LIKELY(this != &other))
+ {
+ other.m_guard.enterRead();
+ m_guard.enterWrite();
+
+ /* Delete all items. */
+ RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
+
+ /* Need we to realloc memory. */
+ if (other.m_cElements != m_cCapacity)
+ resizeArrayNoErase(other.m_cElements);
+ m_cElements = other.m_cElements;
+
+ /* Copy new items. */
+ RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, other.m_cElements);
+
+ m_guard.leaveWrite();
+ other.m_guard.leaveRead();
+ }
+ return *this;
+ }
+
+ /**
+ * Compares if this list's items match the other list.
+ *
+ * @returns \c true if both lists contain the same items, \c false if not.
+ * @param other The list to compare this list with.
+ */
+ bool operator==(const RTCListBase<T, ITYPE, MT>& other)
+ {
+ /* Prevent self comparrison */
+ if (RT_LIKELY(this == &other))
+ return true;
+
+ other.m_guard.enterRead();
+ m_guard.enterRead();
+
+ bool fEqual = true;
+ if (other.m_cElements == m_cElements)
+ {
+ for (size_t i = 0; i < m_cElements; i++)
+ {
+ if (RTCListHelper<T, ITYPE>::at(m_pArray, i) != RTCListHelper<T, ITYPE>::at(other.m_pArray, i))
+ {
+ fEqual = false;
+ break;
+ }
+ }
+ }
+ else
+ fEqual = false;
+
+ m_guard.leaveRead();
+ other.m_guard.leaveRead();
+
+ return fEqual;
+ }
+
+ /**
+ * Compares if this list's items do not match the other list.
+ *
+ * @returns \c true if the lists do not match, \c false if otherwise.
+ * @param other The list to compare this list with.
+ */
+ bool operator!=(const RTCListBase<T, ITYPE, MT>& other)
+ {
+ return !(*this == other);
+ }
+
+ /**
+ * Replace an item in the list.
+ *
+ * @param i The position of the item to replace. If this is out of range,
+ * the request will be ignored, strict builds will assert.
+ * @param val The new value.
+ * @return a reference to this list.
+ */
+ RTCListBase<T, ITYPE, MT> &replace(size_t i, const T &val)
+ {
+ m_guard.enterWrite();
+
+ if (i < m_cElements)
+ {
+ RTCListHelper<T, ITYPE>::erase(m_pArray, i);
+ RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
+ }
+ else
+ AssertMsgFailed(("i=%zu m_cElements=%zu\n", i, m_cElements));
+
+ m_guard.leaveWrite();
+ return *this;
+ }
+
+ /**
+ * Applies a filter of type T to this list.
+ *
+ * @param other The list which contains the elements to filter out from this list.
+ * @return a reference to this list.
+ */
+ RTCListBase<T, ITYPE, MT> &filter(const RTCListBase<T, ITYPE, MT> &other)
+ {
+ AssertReturn(this != &other, *this);
+
+ other.m_guard.enterRead();
+ m_guard.enterWrite();
+
+ for (size_t i = 0; i < m_cElements; i++)
+ {
+ for (size_t f = 0; f < other.m_cElements; f++)
+ {
+ if (RTCListHelper<T, ITYPE>::at(m_pArray, i) == RTCListHelper<T, ITYPE>::at(other.m_pArray, f))
+ removeAtLocked(i);
+ }
+ }
+
+ m_guard.leaveWrite();
+ other.m_guard.leaveRead();
+ return *this;
+ }
+
+ /**
+ * Return the first item as constant object.
+ *
+ * @return A reference or pointer to the first item.
+ *
+ * @note No boundary checks are done. Make sure there is at least one
+ * element.
+ */
+ GET_CRTYPE first() const
+ {
+ m_guard.enterRead();
+ Assert(m_cElements > 0);
+ GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the first item.
+ *
+ * @return A reference or pointer to the first item.
+ *
+ * @note No boundary checks are done. Make sure there is at least one
+ * element.
+ */
+ GET_RTYPE first()
+ {
+ m_guard.enterRead();
+ Assert(m_cElements > 0);
+ GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the last item as constant object.
+ *
+ * @return A reference or pointer to the last item.
+ *
+ * @note No boundary checks are done. Make sure there is at least one
+ * element.
+ */
+ GET_CRTYPE last() const
+ {
+ m_guard.enterRead();
+ Assert(m_cElements > 0);
+ GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements - 1);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the last item.
+ *
+ * @return A reference or pointer to the last item.
+ *
+ * @note No boundary checks are done. Make sure there is at least one
+ * element.
+ */
+ GET_RTYPE last()
+ {
+ m_guard.enterRead();
+ Assert(m_cElements > 0);
+ GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements - 1);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the item at position @a i as constant object.
+ *
+ * @param i The position of the item to return. This better not be out of
+ * bounds, however should it be the last element of the array
+ * will be return and strict builds will raise an assertion.
+ * Should the array be empty, a crash is very likely.
+ * @return The item at position @a i.
+ */
+ GET_CRTYPE at(size_t i) const
+ {
+ m_guard.enterRead();
+ AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
+ GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the item at position @a i.
+ *
+ * @param i The position of the item to return. This better not be out of
+ * bounds, however should it be the last element of the array
+ * will be return and strict builds will raise an assertion.
+ * Should the array be empty, a crash is very likely.
+ * @return The item at position @a i.
+ */
+ GET_RTYPE at(size_t i)
+ {
+ m_guard.enterRead();
+ AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
+ GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the item at position @a i as mutable reference.
+ *
+ * @param i The position of the item to return. This better not be out of
+ * bounds, however should it be the last element of the array
+ * will be return and strict builds will raise an assertion.
+ * Should the array be empty, a crash is very likely.
+ * @return The item at position @a i.
+ */
+ T &operator[](size_t i)
+ {
+ m_guard.enterRead();
+ AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
+ T &res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
+ m_guard.leaveRead();
+ return res;
+ }
+
+ /**
+ * Return the item at position @a i as immutable reference.
+ *
+ * @param i The position of the item to return. This better not be out of
+ * bounds, however should it be the last element of the array
+ * will be return and strict builds will raise an assertion.
+ * Should the array be empty, a crash is very likely.
+ * @return The item at position @a i.
+ */
+ const T &operator[](size_t i) const
+ {
+ m_guard.enterRead();
+ AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
+ const T &rRet = RTCListHelper<T, ITYPE>::atConst(m_pArray, i);
+ m_guard.leaveRead();
+ return rRet;
+ }
+
+ /**
+ * Return a copy of the item at position @a i or default value if out of range.
+ *
+ * @param i The position of the item to return.
+ * @return Copy of the item at position @a i or default value.
+ */
+ T value(size_t i) const
+ {
+ m_guard.enterRead();
+ if (RT_LIKELY(i < m_cElements))
+ {
+ T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
+ m_guard.leaveRead();
+ return res;
+ }
+ m_guard.leaveRead();
+ return T();
+ }
+
+ /**
+ * Return a copy of the item at position @a i, or @a defaultVal if out of range.
+ *
+ * @param i The position of the item to return.
+ * @param defaultVal The value to return in case @a i is invalid.
+ * @return Copy of the item at position @a i or @a defaultVal.
+ */
+ T value(size_t i, const T &defaultVal) const
+ {
+ m_guard.enterRead();
+ if (RT_LIKELY(i < m_cElements))
+ {
+ T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
+ m_guard.leaveRead();
+ return res;
+ }
+ m_guard.leaveRead();
+ return defaultVal;
+ }
+
+ /**
+ * Check if @a val is contained in the array.
+ *
+ * @param val The value to check for.
+ * @return true if it is found, false otherwise.
+ */
+ bool contains(const T &val) const
+ {
+ m_guard.enterRead();
+ bool fRc = RTCListHelper<T, ITYPE>::find(m_pArray, val, m_cElements) < m_cElements;
+ m_guard.leaveRead();
+ return fRc;
+ }
+
+ /**
+ * Remove the first item.
+ *
+ * @note You should make sure the list isn't empty. Strict builds will assert.
+ * The other builds will quietly ignore the request.
+ */
+ void removeFirst()
+ {
+ removeAt(0);
+ }
+
+ /**
+ * Remove the last item.
+ *
+ * @note You should make sure the list isn't empty. Strict builds will assert.
+ * The other builds will quietly ignore the request.
+ */
+ void removeLast()
+ {
+ m_guard.enterWrite();
+ removeAtLocked(m_cElements - 1);
+ m_guard.leaveWrite();
+ }
+
+ /**
+ * Remove the item at position @a i.
+ *
+ * @param i The position of the item to remove. Out of bounds values will
+ * be ignored and an assertion will be raised in strict builds.
+ */
+ void removeAt(size_t i)
+ {
+ m_guard.enterWrite();
+ removeAtLocked(i);
+ m_guard.leaveWrite();
+ }
+
+ /**
+ * Remove a range of items from the list.
+ *
+ * @param iStart The start position of the items to remove.
+ * @param iEnd The end position of the items to remove (excluded).
+ */
+ void removeRange(size_t iStart, size_t iEnd)
+ {
+ AssertReturnVoid(iStart <= iEnd);
+ m_guard.enterWrite();
+
+ AssertMsgStmt(iEnd <= m_cElements, ("iEnd=%zu m_cElements=%zu\n", iEnd, m_cElements), iEnd = m_cElements);
+ AssertMsgStmt(iStart < m_cElements, ("iStart=%zu m_cElements=%zu\n", iStart, m_cElements), iStart = m_cElements);
+ size_t const cElements = iEnd - iStart;
+ if (cElements > 0)
+ {
+ Assert(iStart < m_cElements);
+ RTCListHelper<T, ITYPE>::eraseRange(m_pArray, iStart, cElements);
+ if (m_cElements > iEnd)
+ memmove(&m_pArray[iStart], &m_pArray[iEnd], (m_cElements - iEnd) * sizeof(ITYPE));
+ m_cElements -= cElements;
+ }
+
+ m_guard.leaveWrite();
+ }
+
+ /**
+ * Delete all items in the list.
+ */
+ void clear()
+ {
+ m_guard.enterWrite();
+
+ /* Values cleanup */
+ RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
+ if (m_cElements != kDefaultCapacity)
+ resizeArrayNoErase(kDefaultCapacity);
+ m_cElements = 0;
+
+ m_guard.leaveWrite();
+ }
+
+ /**
+ * Return the raw array.
+ *
+ * For native types this is a pointer to continuous memory of the items. For
+ * pointer types this is a continuous memory of pointers to the items.
+ *
+ * @warning If you change anything in the underlaying list, this memory
+ * will very likely become invalid. So take care when using this
+ * method and better try to avoid using it.
+ *
+ * @returns the raw memory.
+ */
+ ITYPE *raw() const
+ {
+ m_guard.enterRead();
+ ITYPE *pRet = m_pArray;
+ m_guard.leaveRead();
+ return pRet;
+ }
+
+ RTCListBase<T, ITYPE, MT> &operator<<(const T &val)
+ {
+ return append(val);
+ }
+
+ /* Define our own new and delete. */
+#ifdef RT_NEED_NEW_AND_DELETE
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+
+ /**
+ * The default capacity of the list. This is also used as grow factor.
+ */
+ static const size_t kDefaultCapacity;
+
+protected:
+
+ /**
+ * Generic resizes the array, surplus elements are erased.
+ *
+ * @param cElementsNew The new array size.
+ * @throws std::bad_alloc.
+ */
+ void resizeArray(size_t cElementsNew)
+ {
+ /* Same size? */
+ if (cElementsNew == m_cCapacity)
+ return;
+
+ /* If we get smaller we have to delete some of the objects at the end
+ of the list. */
+ if ( cElementsNew < m_cElements
+ && m_pArray)
+ RTCListHelper<T, ITYPE>::eraseRange(m_pArray, cElementsNew, m_cElements - cElementsNew);
+
+ resizeArrayNoErase(cElementsNew);
+ }
+
+ /**
+ * Resizes the array without doing the erase() thing on surplus elements.
+ *
+ * @param cElementsNew The new array size.
+ * @throws std::bad_alloc.
+ */
+ void resizeArrayNoErase(size_t cElementsNew)
+ {
+ /* Same size? */
+ if (cElementsNew == m_cCapacity)
+ return;
+
+ /* Resize the array. */
+ if (cElementsNew > 0)
+ {
+ void *pvNew = RTMemRealloc(m_pArray, sizeof(ITYPE) * cElementsNew);
+ if (!pvNew)
+ {
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#endif
+ return;
+ }
+ m_pArray = static_cast<ITYPE*>(pvNew);
+ }
+ /* If we get zero we delete the array it self. */
+ else if (m_pArray)
+ {
+ RTMemFree(m_pArray);
+ m_pArray = NULL;
+ }
+
+ m_cCapacity = cElementsNew;
+ if (m_cElements > cElementsNew)
+ m_cElements = cElementsNew;
+ }
+
+ /**
+ * Special realloc method which require that the array will grow.
+ *
+ * @param cElementsNew The new array size.
+ * @throws std::bad_alloc.
+ * @note No boundary checks are done!
+ */
+ void growArray(size_t cElementsNew)
+ {
+ Assert(cElementsNew > m_cCapacity);
+ void *pvNew = RTMemRealloc(m_pArray, sizeof(ITYPE) * cElementsNew);
+ if (pvNew)
+ {
+ m_cCapacity = cElementsNew;
+ m_pArray = static_cast<ITYPE*>(pvNew);
+ }
+ else
+ {
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#endif
+ }
+ }
+
+ /**
+ * Remove the item at position @a i.
+ *
+ * @param i The position of the item to remove. Out of bounds values will
+ * be ignored and an assertion will be raised in strict builds.
+ * @remarks
+ */
+ void removeAtLocked(size_t i)
+ {
+ AssertMsgReturnVoid(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements));
+
+ RTCListHelper<T, ITYPE>::erase(m_pArray, i);
+ if (i < m_cElements - 1)
+ memmove(&m_pArray[i], &m_pArray[i + 1], (m_cElements - i - 1) * sizeof(ITYPE));
+ --m_cElements;
+ }
+
+
+ /** The internal list array. */
+ ITYPE *m_pArray;
+ /** The current count of items in use. */
+ size_t m_cElements;
+ /** The current capacity of the internal array. */
+ size_t m_cCapacity;
+ /** The guard used to serialize the access to the items. */
+ RTCListGuard<MT> m_guard;
+};
+
+template <class T, typename ITYPE, bool MT>
+const size_t RTCListBase<T, ITYPE, MT>::kDefaultCapacity = 10;
+
+/**
+ * Template class which automatically determines the type of list to use.
+ *
+ * @see RTCListBase
+ */
+template <class T, typename ITYPE = typename RTCIf<(sizeof(T) > sizeof(void*)), T*, T>::result>
+class RTCList : public RTCListBase<T, ITYPE, false>
+{
+ /* Traits */
+ typedef RTCListBase<T, ITYPE, false> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ RTCList(const BASE &other)
+ : BASE(other) {}
+
+ /* Define our own new and delete. */
+#ifdef RT_NEED_NEW_AND_DELETE
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+};
+
+/**
+ * Specialized class for using the native type list for unsigned 64-bit
+ * values even on a 32-bit host.
+ *
+ * @see RTCListBase
+ */
+template <>
+class RTCList<uint64_t>: public RTCListBase<uint64_t, uint64_t, false>
+{
+ /* Traits */
+ typedef RTCListBase<uint64_t, uint64_t, false> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ /* Define our own new and delete. */
+#ifdef RT_NEED_NEW_AND_DELETE
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+};
+
+/**
+ * Specialized class for using the native type list for signed 64-bit
+ * values even on a 32-bit host.
+ *
+ * @see RTCListBase
+ */
+template <>
+class RTCList<int64_t>: public RTCListBase<int64_t, int64_t, false>
+{
+ /* Traits */
+ typedef RTCListBase<int64_t, int64_t, false> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ /* Define our own new and delete. */
+#ifdef RT_NEED_NEW_AND_DELETE
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_list_h */
+
diff --git a/include/iprt/cpp/lock.h b/include/iprt/cpp/lock.h
new file mode 100644
index 00000000..3a0c4bcd
--- /dev/null
+++ b/include/iprt/cpp/lock.h
@@ -0,0 +1,179 @@
+/** @file
+ * IPRT - Classes for Scope-based Locking.
+ */
+
+/*
+ * Copyright (C) 2007-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_lock_h
+#define IPRT_INCLUDED_cpp_lock_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/critsect.h>
+#ifdef RT_LOCK_STRICT
+# include <iprt/lockvalidator.h>
+#endif
+
+RT_C_DECLS_BEGIN
+
+/** @defgroup grp_rt_cpp_lock C++ Scope-based Locking
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+class RTCLock;
+
+/**
+ * The mutex lock.
+ *
+ * This is used as an object data member if the intention is to lock
+ * a single object. This can also be used statically, initialized in
+ * a global variable, for class wide purposes.
+ *
+ * This is best used together with RTCLock.
+ */
+class RTCLockMtx
+{
+friend class RTCLock;
+
+private:
+ RTCRITSECT mMtx;
+
+public:
+ RTCLockMtx()
+ {
+#ifdef RT_LOCK_STRICT_ORDER
+ RTCritSectInitEx(&mMtx, 0 /*fFlags*/,
+ RTLockValidatorClassCreateUnique(RT_SRC_POS, NULL),
+ RTLOCKVAL_SUB_CLASS_NONE, NULL);
+#else
+ RTCritSectInit(&mMtx);
+#endif
+ }
+
+ /** Use to when creating locks that belongs in the same "class". */
+ RTCLockMtx(RT_SRC_POS_DECL, uint32_t uSubClass = RTLOCKVAL_SUB_CLASS_NONE)
+ {
+#ifdef RT_LOCK_STRICT_ORDER
+ RTCritSectInitEx(&mMtx, 0 /*fFlags*/,
+ RTLockValidatorClassForSrcPos(RT_SRC_POS_ARGS, NULL),
+ uSubClass, NULL);
+#else
+ NOREF(uSubClass);
+ RTCritSectInit(&mMtx);
+ RT_SRC_POS_NOREF();
+#endif
+ }
+
+ ~RTCLockMtx()
+ {
+ RTCritSectDelete(&mMtx);
+ }
+
+ /* lock() and unlock() are private so that only friend RTCLock can access
+ them. */
+private:
+ inline void lock()
+ {
+ RTCritSectEnter(&mMtx);
+ }
+
+ inline void unlock()
+ {
+ RTCritSectLeave(&mMtx);
+ }
+};
+
+
+/**
+ * The stack object for automatic locking and unlocking.
+ *
+ * This is a helper class for automatic locks, to simplify requesting a
+ * RTCLockMtx and to not forget releasing it. To request a RTCLockMtx, simply
+ * create an instance of RTCLock on the stack and pass the mutex to it:
+ *
+ * @code
+ extern RTCLockMtx gMtx; // wherever this is
+ ...
+ if (...)
+ {
+ RTCLock lock(gMtx);
+ ... // do stuff
+ // when lock goes out of scope, destructor releases the mutex
+ }
+ @endcode
+ *
+ * You can also explicitly release the mutex by calling RTCLock::release().
+ * This might be helpful if the lock doesn't go out of scope early enough
+ * for your mutex to be released.
+ */
+class RTCLock
+{
+private:
+ /** Reference to the lock we're holding. */
+ RTCLockMtx &m_rMtx;
+ /** Whether we're currently holding the lock of if it was already
+ * explictily released by the release() method. */
+ bool m_fLocked;
+
+public:
+ RTCLock(RTCLockMtx &a_rMtx)
+ : m_rMtx(a_rMtx)
+ {
+ m_rMtx.lock();
+ m_fLocked = true;
+ }
+
+ ~RTCLock()
+ {
+ if (m_fLocked)
+ m_rMtx.unlock();
+ }
+
+ inline void release()
+ {
+ if (m_fLocked)
+ {
+ m_rMtx.unlock();
+ m_fLocked = false;
+ }
+ }
+};
+
+
+/** @} */
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_cpp_lock_h */
+
diff --git a/include/iprt/cpp/meta.h b/include/iprt/cpp/meta.h
new file mode 100644
index 00000000..e6b9796e
--- /dev/null
+++ b/include/iprt/cpp/meta.h
@@ -0,0 +1,125 @@
+/** @file
+ * IPRT - C++ Meta programming.
+ */
+
+/*
+ * Copyright (C) 2011-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_meta_h
+#define IPRT_INCLUDED_cpp_meta_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/** @defgroup grp_rt_cpp_meta C++ Meta programming utilities
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Check for a condition on compile time and dependent of the result TrueResult
+ * or FalseResult will be defined.
+ *
+ * @param Condition Condition to check.
+ * @param TrueResult Result when condition is true.
+ * @param FalseResult Result when condition is false
+ */
+template <bool Condition, typename TrueResult, typename FalseResult>
+struct RTCIf;
+
+/**
+ * Check for a condition on compile time and dependent of the result TrueResult
+ * or FalseResult will be defined.
+ *
+ * True specialization of RTCIf.
+ *
+ * @param TrueResult Result when condition is true.
+ * @param FalseResult Result when condition is false
+ */
+template <typename TrueResult, typename FalseResult>
+struct RTCIf<true, TrueResult, FalseResult>
+{
+ typedef TrueResult result;
+};
+
+/**
+ * Check for a condition on compile time and dependent of the result TrueResult
+ * or FalseResult will be defined.
+ *
+ * False specialization of RTCIf.
+ *
+ * @param TrueResult Result when condition is true.
+ * @param FalseResult Result when condition is false
+ */
+template <typename TrueResult, typename FalseResult>
+struct RTCIf<false, TrueResult, FalseResult>
+{
+ typedef FalseResult result;
+};
+
+/**
+ * Check if @a T is a pointer or not at compile time and dependent of the
+ * result TrueResult or FalseResult will be defined.
+ *
+ * False version of RTCIfPtr.
+ *
+ * @param Condition Condition to check.
+ * @param TrueResult Result when condition is true.
+ * @param FalseResult Result when condition is false
+ */
+template <class T, typename TrueResult, typename FalseResult>
+struct RTCIfPtr
+{
+ typedef FalseResult result;
+};
+
+/**
+ * Check if @a T is a pointer or not at compile time and dependent of the
+ * result TrueResult or FalseResult will be defined.
+ *
+ * True specialization of RTCIfPtr.
+ *
+ * @param Condition Condition to check.
+ * @param TrueResult Result when condition is true.
+ * @param FalseResult Result when condition is false
+ */
+template <class T, typename TrueResult, typename FalseResult>
+struct RTCIfPtr<T*, TrueResult, FalseResult>
+{
+ typedef TrueResult result;
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_meta_h */
+
diff --git a/include/iprt/cpp/ministring.h b/include/iprt/cpp/ministring.h
new file mode 100644
index 00000000..80ad7cb5
--- /dev/null
+++ b/include/iprt/cpp/ministring.h
@@ -0,0 +1,1638 @@
+/** @file
+ * IPRT - C++ string class.
+ */
+
+/*
+ * Copyright (C) 2007-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_ministring_h
+#define IPRT_INCLUDED_cpp_ministring_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+#include <iprt/cpp/list.h>
+
+#include <new>
+
+
+/** @defgroup grp_rt_cpp_string C++ String support
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/** @brief C++ string class.
+ *
+ * This is a C++ string class that does not depend on anything else except IPRT
+ * memory management functions. Semantics are like in std::string, except it
+ * can do a lot less.
+ *
+ * Note that RTCString does not differentiate between NULL strings
+ * and empty strings. In other words, RTCString("") and RTCString(NULL)
+ * behave the same. In both cases, RTCString allocates no memory, reports
+ * a zero length and zero allocated bytes for both, and returns an empty
+ * C-style string from c_str().
+ *
+ * @note RTCString ASSUMES that all strings it deals with are valid UTF-8.
+ * The caller is responsible for not breaking this assumption.
+ */
+#ifdef VBOX
+ /** @remarks Much of the code in here used to be in com::Utf8Str so that
+ * com::Utf8Str can now derive from RTCString and only contain code
+ * that is COM-specific, such as com::Bstr conversions. Compared to
+ * the old Utf8Str though, RTCString always knows the length of its
+ * member string and the size of the buffer so it can use memcpy()
+ * instead of strdup().
+ */
+#endif
+class RT_DECL_CLASS RTCString
+{
+public:
+#if defined(RT_NEED_NEW_AND_DELETE) && ( !defined(RTMEM_WRAP_SOME_NEW_AND_DELETE_TO_EF) \
+ || defined(RTMEM_NO_WRAP_SOME_NEW_AND_DELETE_TO_EF))
+ RTMEM_IMPLEMENT_NEW_AND_DELETE();
+#else
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+#endif
+
+ /**
+ * Creates an empty string that has no memory allocated.
+ */
+ RTCString()
+ : m_psz(NULL),
+ m_cch(0),
+ m_cbAllocated(0)
+ {
+ }
+
+ /**
+ * Creates a copy of another RTCString.
+ *
+ * This allocates s.length() + 1 bytes for the new instance, unless s is empty.
+ *
+ * @param a_rSrc The source string.
+ *
+ * @throws std::bad_alloc
+ */
+ RTCString(const RTCString &a_rSrc)
+ {
+ copyFromN(a_rSrc.m_psz, a_rSrc.m_cch);
+ }
+
+ /**
+ * Creates a copy of a C-style string.
+ *
+ * This allocates strlen(pcsz) + 1 bytes for the new instance, unless s is empty.
+ *
+ * @param pcsz The source string.
+ *
+ * @throws std::bad_alloc
+ */
+ RTCString(const char *pcsz)
+ {
+ copyFromN(pcsz, pcsz ? strlen(pcsz) : 0);
+ }
+
+ /**
+ * Create a partial copy of another RTCString.
+ *
+ * @param a_rSrc The source string.
+ * @param a_offSrc The byte offset into the source string.
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string.
+ */
+ RTCString(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc = npos)
+ {
+ if (a_offSrc < a_rSrc.m_cch)
+ copyFromN(&a_rSrc.m_psz[a_offSrc], RT_MIN(a_cchSrc, a_rSrc.m_cch - a_offSrc));
+ else
+ {
+ m_psz = NULL;
+ m_cch = 0;
+ m_cbAllocated = 0;
+ }
+ }
+
+ /**
+ * Create a partial copy of a C-style string.
+ *
+ * @param a_pszSrc The source string (UTF-8).
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string. This must not
+ * be '0' as the compiler could easily mistake
+ * that for the va_list constructor.
+ */
+ RTCString(const char *a_pszSrc, size_t a_cchSrc)
+ {
+ size_t cchMax = a_pszSrc ? RTStrNLen(a_pszSrc, a_cchSrc) : 0;
+ copyFromN(a_pszSrc, RT_MIN(a_cchSrc, cchMax));
+ }
+
+ /**
+ * Create a string containing @a a_cTimes repetitions of the character @a
+ * a_ch.
+ *
+ * @param a_cTimes The number of times the character is repeated.
+ * @param a_ch The character to fill the string with.
+ */
+ RTCString(size_t a_cTimes, char a_ch)
+ : m_psz(NULL),
+ m_cch(0),
+ m_cbAllocated(0)
+ {
+ Assert((unsigned)a_ch < 0x80);
+ if (a_cTimes)
+ {
+ reserve(a_cTimes + 1);
+ memset(m_psz, a_ch, a_cTimes);
+ m_psz[a_cTimes] = '\0';
+ m_cch = a_cTimes;
+ }
+ }
+
+ /**
+ * Create a new string given the format string and its arguments.
+ *
+ * @param a_pszFormat Pointer to the format string (UTF-8),
+ * @see pg_rt_str_format.
+ * @param a_va Argument vector containing the arguments
+ * specified by the format string.
+ * @sa printfV
+ * @remarks Not part of std::string.
+ */
+ RTCString(const char *a_pszFormat, va_list a_va) RT_IPRT_FORMAT_ATTR(1, 0)
+ : m_psz(NULL),
+ m_cch(0),
+ m_cbAllocated(0)
+ {
+ printfV(a_pszFormat, a_va);
+ }
+
+ /**
+ * Destructor.
+ */
+ virtual ~RTCString()
+ {
+ cleanup();
+ }
+
+ /**
+ * String length in bytes.
+ *
+ * Returns the length of the member string in bytes, which is equal to strlen(c_str()).
+ * In other words, this does not count unicode codepoints; use utf8length() for that.
+ * The byte length is always cached so calling this is cheap and requires no
+ * strlen() invocation.
+ *
+ * @returns m_cbLength.
+ */
+ size_t length() const
+ {
+ return m_cch;
+ }
+
+ /**
+ * String length in unicode codepoints.
+ *
+ * As opposed to length(), which returns the length in bytes, this counts
+ * the number of unicode codepoints. This is *not* cached so calling this
+ * is expensive.
+ *
+ * @returns Number of codepoints in the member string.
+ */
+ size_t uniLength() const
+ {
+ return m_psz ? RTStrUniLen(m_psz) : 0;
+ }
+
+ /**
+ * The allocated buffer size (in bytes).
+ *
+ * Returns the number of bytes allocated in the internal string buffer, which is
+ * at least length() + 1 if length() > 0; for an empty string, this returns 0.
+ *
+ * @returns m_cbAllocated.
+ */
+ size_t capacity() const
+ {
+ return m_cbAllocated;
+ }
+
+ /**
+ * Make sure at that least cb of buffer space is reserved.
+ *
+ * Requests that the contained memory buffer have at least cb bytes allocated.
+ * This may expand or shrink the string's storage, but will never truncate the
+ * contained string. In other words, cb will be ignored if it's smaller than
+ * length() + 1.
+ *
+ * @param cb New minimum size (in bytes) of member memory buffer.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ void reserve(size_t cb)
+ {
+ if ( ( cb != m_cbAllocated
+ && cb > m_cch + 1)
+ || ( m_psz == NULL
+ && cb > 0))
+ {
+ int rc = RTStrRealloc(&m_psz, cb);
+ if (RT_SUCCESS(rc))
+ m_cbAllocated = cb;
+#ifdef RT_EXCEPTIONS_ENABLED
+ else
+ throw std::bad_alloc();
+#endif
+ }
+ }
+
+ /**
+ * A C like version of the reserve method, i.e. return code instead of throw.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ * @param cb New minimum size (in bytes) of member memory buffer.
+ */
+ int reserveNoThrow(size_t cb) RT_NOEXCEPT
+ {
+ if ( ( cb != m_cbAllocated
+ && cb > m_cch + 1)
+ || ( m_psz == NULL
+ && cb > 0))
+ {
+ int rc = RTStrRealloc(&m_psz, cb);
+ if (RT_SUCCESS(rc))
+ m_cbAllocated = cb;
+ else
+ return rc;
+ }
+ return VINF_SUCCESS;
+ }
+
+ /**
+ * Deallocates all memory.
+ */
+ inline void setNull()
+ {
+ cleanup();
+ }
+
+ /**
+ * Assigns a copy of pcsz to @a this.
+ *
+ * @param pcsz The source string.
+ *
+ * @throws std::bad_alloc On allocation failure. The object is left describing
+ * a NULL string.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &operator=(const char *pcsz)
+ {
+ if (m_psz != pcsz)
+ {
+ cleanup();
+ copyFromN(pcsz, pcsz ? strlen(pcsz) : 0);
+ }
+ return *this;
+ }
+
+ /**
+ * Assigns a copy of s to @a this.
+ *
+ * @param s The source string.
+ *
+ * @throws std::bad_alloc On allocation failure. The object is left describing
+ * a NULL string.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &operator=(const RTCString &s)
+ {
+ if (this != &s)
+ {
+ cleanup();
+ copyFromN(s.m_psz, s.m_cch);
+ }
+ return *this;
+ }
+
+ /**
+ * Assigns a copy of another RTCString.
+ *
+ * @param a_rSrc Reference to the source string.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ RTCString &assign(const RTCString &a_rSrc);
+
+ /**
+ * Assigns a copy of another RTCString.
+ *
+ * @param a_rSrc Reference to the source string.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int assignNoThrow(const RTCString &a_rSrc) RT_NOEXCEPT;
+
+ /**
+ * Assigns a copy of a C-style string.
+ *
+ * @param a_pszSrc Pointer to the C-style source string.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @remarks ASSUMES valid
+ */
+ RTCString &assign(const char *a_pszSrc);
+
+ /**
+ * Assigns a copy of a C-style string.
+ *
+ * @param a_pszSrc Pointer to the C-style source string.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ * @remarks ASSUMES valid
+ */
+ int assignNoThrow(const char *a_pszSrc) RT_NOEXCEPT;
+
+ /**
+ * Assigns a partial copy of another RTCString.
+ *
+ * @param a_rSrc The source string.
+ * @param a_offSrc The byte offset into the source string.
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ RTCString &assign(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc = npos);
+
+ /**
+ * Assigns a partial copy of another RTCString.
+ *
+ * @param a_rSrc The source string.
+ * @param a_offSrc The byte offset into the source string.
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int assignNoThrow(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc = npos) RT_NOEXCEPT;
+
+ /**
+ * Assigns a partial copy of a C-style string.
+ *
+ * @param a_pszSrc The source string (UTF-8).
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ RTCString &assign(const char *a_pszSrc, size_t a_cchSrc);
+
+ /**
+ * Assigns a partial copy of a C-style string.
+ *
+ * @param a_pszSrc The source string (UTF-8).
+ * @param a_cchSrc The max number of chars (encoded UTF-8 bytes)
+ * to copy from the source string.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int assignNoThrow(const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT;
+
+ /**
+ * Assigs a string containing @a a_cTimes repetitions of the character @a a_ch.
+ *
+ * @param a_cTimes The number of times the character is repeated.
+ * @param a_ch The character to fill the string with.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ RTCString &assign(size_t a_cTimes, char a_ch);
+
+ /**
+ * Assigs a string containing @a a_cTimes repetitions of the character @a a_ch.
+ *
+ * @param a_cTimes The number of times the character is repeated.
+ * @param a_ch The character to fill the string with.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int assignNoThrow(size_t a_cTimes, char a_ch) RT_NOEXCEPT;
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &printf(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &printfV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Appends the string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &append(const RTCString &rThat);
+
+ /**
+ * Appends the string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendNoThrow(const RTCString &rThat) RT_NOEXCEPT;
+
+ /**
+ * Appends the string @a pszSrc to @a this.
+ *
+ * @param pszSrc The C-style string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &append(const char *pszSrc);
+
+ /**
+ * Appends the string @a pszSrc to @a this.
+ *
+ * @param pszSrc The C-style string to append.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendNoThrow(const char *pszSrc) RT_NOEXCEPT;
+
+ /**
+ * Appends the a substring from @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (byte offset,
+ * not codepoint).
+ * @param cchMax The maximum number of bytes to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &append(const RTCString &rThat, size_t offStart, size_t cchMax = RTSTR_MAX);
+
+ /**
+ * Appends the a substring from @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (byte offset,
+ * not codepoint).
+ * @param cchMax The maximum number of bytes to append.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Appends the first @a cchMax chars from string @a pszThat to @a this.
+ *
+ * @param pszThat The C-style string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &append(const char *pszThat, size_t cchMax);
+
+ /**
+ * Appends the first @a cchMax chars from string @a pszThat to @a this.
+ *
+ * @param pszThat The C-style string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT;
+
+ /**
+ * Appends the given character to @a this.
+ *
+ * @param ch The character to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &append(char ch);
+
+ /**
+ * Appends the given character to @a this.
+ *
+ * @param ch The character to append.
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendNoThrow(char ch) RT_NOEXCEPT;
+
+ /**
+ * Appends the given unicode code point to @a this.
+ *
+ * @param uc The unicode code point to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ RTCString &appendCodePoint(RTUNICP uc);
+
+ /**
+ * Appends the given unicode code point to @a this.
+ *
+ * @param uc The unicode code point to append.
+ * @returns VINF_SUCCESS, VERR_INVALID_UTF8_ENCODING or VERR_NO_STRING_MEMORY.
+ */
+ int appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT;
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &appendPrintf(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendPrintfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &appendPrintfV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ */
+ int appendPrintfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Shortcut to append(), RTCString variant.
+ *
+ * @param rThat The string to append.
+ * @returns Reference to the object.
+ */
+ RTCString &operator+=(const RTCString &rThat)
+ {
+ return append(rThat);
+ }
+
+ /**
+ * Shortcut to append(), const char* variant.
+ *
+ * @param pszThat The C-style string to append.
+ * @returns Reference to the object.
+ */
+ RTCString &operator+=(const char *pszThat)
+ {
+ return append(pszThat);
+ }
+
+ /**
+ * Shortcut to append(), char variant.
+ *
+ * @param ch The character to append.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &operator+=(char ch)
+ {
+ return append(ch);
+ }
+
+ /**
+ * Converts the member string to upper case.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &toUpper() RT_NOEXCEPT
+ {
+ if (length())
+ {
+ /* Folding an UTF-8 string may result in a shorter encoding (see
+ testcase), so recalculate the length afterwards. */
+ ::RTStrToUpper(m_psz);
+ size_t cchNew = strlen(m_psz);
+ Assert(cchNew <= m_cch);
+ m_cch = cchNew;
+ }
+ return *this;
+ }
+
+ /**
+ * Converts the member string to lower case.
+ *
+ * @returns Reference to the object.
+ */
+ RTCString &toLower() RT_NOEXCEPT
+ {
+ if (length())
+ {
+ /* Folding an UTF-8 string may result in a shorter encoding (see
+ testcase), so recalculate the length afterwards. */
+ ::RTStrToLower(m_psz);
+ size_t cchNew = strlen(m_psz);
+ Assert(cchNew <= m_cch);
+ m_cch = cchNew;
+ }
+ return *this;
+ }
+
+ /**
+ * Erases a sequence from the string.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start erasing.
+ * @param cchLength How much following @a offStart to erase.
+ */
+ RTCString &erase(size_t offStart = 0, size_t cchLength = npos) RT_NOEXCEPT;
+
+ /**
+ * Replaces a span of @a this string with a replacement string.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param rStrReplacement The replacement string.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ *
+ * @note Non-standard behaviour if offStart is beyond the end of the string.
+ * No change will occure and strict builds hits a debug assertion.
+ */
+ RTCString &replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement);
+
+ /**
+ * Replaces a span of @a this string with a replacement string.
+ *
+ * @returns VINF_SUCCESS, VERR_OUT_OF_RANGE or VERR_NO_STRING_MEMORY.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param rStrReplacement The replacement string.
+ */
+ int replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement) RT_NOEXCEPT;
+
+ /**
+ * Replaces a span of @a this string with a replacement substring.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param rStrReplacement The string from which a substring is taken.
+ * @param offReplacement The offset into @a rStrReplacement where the
+ * replacement substring starts.
+ * @param cchReplacement The maximum length of the replacement substring.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ *
+ * @note Non-standard behaviour if offStart or offReplacement is beyond the
+ * end of the repective strings. No change is made in the former case,
+ * while we consider it an empty string in the latter. In both
+ * situation a debug assertion is raised in strict builds.
+ */
+ RTCString &replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
+ size_t offReplacement, size_t cchReplacement);
+
+ /**
+ * Replaces a span of @a this string with a replacement substring.
+ *
+ * @returns VINF_SUCCESS, VERR_OUT_OF_RANGE or VERR_NO_STRING_MEMORY.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param rStrReplacement The string from which a substring is taken.
+ * @param offReplacement The offset into @a rStrReplacement where the
+ * replacement substring starts.
+ * @param cchReplacement The maximum length of the replacement substring.
+ */
+ int replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
+ size_t offReplacement, size_t cchReplacement) RT_NOEXCEPT;
+
+ /**
+ * Replaces a span of @a this string with the replacement string.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszReplacement The replacement string.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ *
+ * @note Non-standard behaviour if offStart is beyond the end of the string.
+ * No change will occure and strict builds hits a debug assertion.
+ */
+ RTCString &replace(size_t offStart, size_t cchLength, const char *pszReplacement);
+
+ /**
+ * Replaces a span of @a this string with the replacement string.
+ *
+ * @returns VINF_SUCCESS, VERR_OUT_OF_RANGE or VERR_NO_STRING_MEMORY.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszReplacement The replacement string.
+ */
+ int replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement) RT_NOEXCEPT;
+
+ /**
+ * Replaces a span of @a this string with the replacement string.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszReplacement The replacement string.
+ * @param cchReplacement How much of @a pszReplacement to use at most. If a
+ * zero terminator is found before reaching this value,
+ * we'll stop there.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ *
+ * @note Non-standard behaviour if offStart is beyond the end of the string.
+ * No change will occure and strict builds hits a debug assertion.
+ */
+ RTCString &replace(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement);
+
+ /**
+ * Replaces a span of @a this string with the replacement string.
+ *
+ * @returns VINF_SUCCESS, VERR_OUT_OF_RANGE or VERR_NO_STRING_MEMORY.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszReplacement The replacement string.
+ * @param cchReplacement How much of @a pszReplacement to use at most. If a
+ * zero terminator is found before reaching this value,
+ * we'll stop there.
+ */
+ int replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement) RT_NOEXCEPT;
+
+ /**
+ * Truncates the string to a max length of @a cchMax.
+ *
+ * If the string is shorter than @a cchMax characters, no change is made.
+ *
+ * If the @a cchMax is not at the start of a UTF-8 sequence, it will be adjusted
+ * down to the start of the UTF-8 sequence. Thus, after a truncation, the
+ * length() may be smaller than @a cchMax.
+ *
+ */
+ RTCString &truncate(size_t cchMax) RT_NOEXCEPT;
+
+ /**
+ * Index operator.
+ *
+ * Returns the byte at the given index, or a null byte if the index is not
+ * smaller than length(). This does _not_ count codepoints but simply points
+ * into the member C-style string.
+ *
+ * @param i The index into the string buffer.
+ * @returns char at the index or null.
+ */
+ inline char operator[](size_t i) const RT_NOEXCEPT
+ {
+ if (i < length())
+ return m_psz[i];
+ return '\0';
+ }
+
+ /**
+ * Returns the contained string as a const C-style string pointer.
+ *
+ * This never returns NULL; if the string is empty, this returns a pointer to
+ * static null byte.
+ *
+ * @returns const pointer to C-style string.
+ */
+ inline const char *c_str() const RT_NOEXCEPT
+ {
+ return (m_psz) ? m_psz : "";
+ }
+
+ /**
+ * Returns a non-const raw pointer that allows to modify the string directly.
+ * As opposed to c_str() and raw(), this DOES return NULL for an empty string
+ * because we cannot return a non-const pointer to a static "" global.
+ *
+ * @warning
+ * -# Be sure not to modify data beyond the allocated memory! Call
+ * capacity() to find out how large that buffer is.
+ * -# After any operation that modifies the length of the string,
+ * you _must_ call RTCString::jolt(), or subsequent copy operations
+ * may go nowhere. Better not use mutableRaw() at all.
+ */
+ char *mutableRaw() RT_NOEXCEPT
+ {
+ return m_psz;
+ }
+
+ /**
+ * Clean up after using mutableRaw.
+ *
+ * Intended to be called after something has messed with the internal string
+ * buffer (e.g. after using mutableRaw() or Utf8Str::asOutParam()). Resets the
+ * internal lengths correctly. Otherwise subsequent copy operations may go
+ * nowhere.
+ */
+ void jolt() RT_NOEXCEPT
+ {
+ if (m_psz)
+ {
+ m_cch = strlen(m_psz);
+ m_cbAllocated = m_cch + 1; /* (Required for the Utf8Str::asOutParam case) */
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ }
+ }
+
+ /**
+ * Returns @c true if the member string has no length.
+ *
+ * This is @c true for instances created from both NULL and "" input
+ * strings.
+ *
+ * This states nothing about how much memory might be allocated.
+ *
+ * @returns @c true if empty, @c false if not.
+ */
+ bool isEmpty() const RT_NOEXCEPT
+ {
+ return length() == 0;
+ }
+
+ /**
+ * Returns @c false if the member string has no length.
+ *
+ * This is @c false for instances created from both NULL and "" input
+ * strings.
+ *
+ * This states nothing about how much memory might be allocated.
+ *
+ * @returns @c false if empty, @c true if not.
+ */
+ bool isNotEmpty() const RT_NOEXCEPT
+ {
+ return length() != 0;
+ }
+
+ /** Case sensitivity selector. */
+ enum CaseSensitivity
+ {
+ CaseSensitive,
+ CaseInsensitive
+ };
+
+ /**
+ * Compares the member string to a C-string.
+ *
+ * @param pcszThat The string to compare with.
+ * @param cs Whether comparison should be case-sensitive.
+ * @returns 0 if equal, negative if this is smaller than @a pcsz, positive
+ * if larger.
+ */
+ int compare(const char *pcszThat, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
+ {
+ /* This klugde is for m_cch=0 and m_psz=NULL. pcsz=NULL and psz=""
+ are treated the same way so that str.compare(str2.c_str()) works. */
+ if (length() == 0)
+ return pcszThat == NULL || *pcszThat == '\0' ? 0 : -1;
+
+ if (cs == CaseSensitive)
+ return ::RTStrCmp(m_psz, pcszThat);
+ return ::RTStrICmp(m_psz, pcszThat);
+ }
+
+ /**
+ * Compares the member string to another RTCString.
+ *
+ * @param rThat The string to compare with.
+ * @param cs Whether comparison should be case-sensitive.
+ * @returns 0 if equal, negative if this is smaller than @a pcsz, positive
+ * if larger.
+ */
+ int compare(const RTCString &rThat, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
+ {
+ if (cs == CaseSensitive)
+ return ::RTStrCmp(m_psz, rThat.m_psz);
+ return ::RTStrICmp(m_psz, rThat.m_psz);
+ }
+
+ /**
+ * Compares the two strings.
+ *
+ * @returns true if equal, false if not.
+ * @param rThat The string to compare with.
+ */
+ bool equals(const RTCString &rThat) const RT_NOEXCEPT
+ {
+ return rThat.length() == length()
+ && ( length() == 0
+ || memcmp(rThat.m_psz, m_psz, length()) == 0);
+ }
+
+ /**
+ * Compares the two strings.
+ *
+ * @returns true if equal, false if not.
+ * @param pszThat The string to compare with.
+ */
+ bool equals(const char *pszThat) const RT_NOEXCEPT
+ {
+ /* This klugde is for m_cch=0 and m_psz=NULL. pcsz=NULL and psz=""
+ are treated the same way so that str.equals(str2.c_str()) works. */
+ if (length() == 0)
+ return pszThat == NULL || *pszThat == '\0';
+ return RTStrCmp(pszThat, m_psz) == 0;
+ }
+
+ /**
+ * Compares the two strings ignoring differences in case.
+ *
+ * @returns true if equal, false if not.
+ * @param that The string to compare with.
+ */
+ bool equalsIgnoreCase(const RTCString &that) const RT_NOEXCEPT
+ {
+ /* Unfolded upper and lower case characters may require different
+ amount of encoding space, so the length optimization doesn't work. */
+ return RTStrICmp(that.m_psz, m_psz) == 0;
+ }
+
+ /**
+ * Compares the two strings ignoring differences in case.
+ *
+ * @returns true if equal, false if not.
+ * @param pszThat The string to compare with.
+ */
+ bool equalsIgnoreCase(const char *pszThat) const RT_NOEXCEPT
+ {
+ /* This klugde is for m_cch=0 and m_psz=NULL. pcsz=NULL and psz=""
+ are treated the same way so that str.equalsIgnoreCase(str2.c_str()) works. */
+ if (length() == 0)
+ return pszThat == NULL || *pszThat == '\0';
+ return RTStrICmp(pszThat, m_psz) == 0;
+ }
+
+ /** @name Comparison operators.
+ * @{ */
+ bool operator==(const RTCString &that) const { return equals(that); }
+ bool operator!=(const RTCString &that) const { return !equals(that); }
+ bool operator<( const RTCString &that) const { return compare(that) < 0; }
+ bool operator>( const RTCString &that) const { return compare(that) > 0; }
+
+ bool operator==(const char *pszThat) const { return equals(pszThat); }
+ bool operator!=(const char *pszThat) const { return !equals(pszThat); }
+ bool operator<( const char *pszThat) const { return compare(pszThat) < 0; }
+ bool operator>( const char *pszThat) const { return compare(pszThat) > 0; }
+ /** @} */
+
+ /** Max string offset value.
+ *
+ * When returned by a method, this indicates failure. When taken as input,
+ * typically a default, it means all the way to the string terminator.
+ */
+ static const size_t npos;
+
+ /**
+ * Find the given substring.
+ *
+ * Looks for @a pszNeedle in @a this starting at @a offStart and returns its
+ * position as a byte (not codepoint) offset, counting from the beginning of
+ * @a this as 0.
+ *
+ * @param pszNeedle The substring to find.
+ * @param offStart The (byte) offset into the string buffer to start
+ * searching.
+ *
+ * @returns 0 based position of pszNeedle. npos if not found.
+ */
+ size_t find(const char *pszNeedle, size_t offStart = 0) const RT_NOEXCEPT;
+ size_t find_first_of(const char *pszNeedle, size_t offStart = 0) const RT_NOEXCEPT
+ { return find(pszNeedle, offStart); }
+
+ /**
+ * Find the given substring.
+ *
+ * Looks for @a pStrNeedle in @a this starting at @a offStart and returns its
+ * position as a byte (not codepoint) offset, counting from the beginning of
+ * @a this as 0.
+ *
+ * @param pStrNeedle The substring to find.
+ * @param offStart The (byte) offset into the string buffer to start
+ * searching.
+ *
+ * @returns 0 based position of pStrNeedle. npos if not found or pStrNeedle is
+ * NULL or an empty string.
+ */
+ size_t find(const RTCString *pStrNeedle, size_t offStart = 0) const RT_NOEXCEPT;
+
+ /**
+ * Find the given substring.
+ *
+ * Looks for @a rStrNeedle in @a this starting at @a offStart and returns its
+ * position as a byte (not codepoint) offset, counting from the beginning of
+ * @a this as 0.
+ *
+ * @param rStrNeedle The substring to find.
+ * @param offStart The (byte) offset into the string buffer to start
+ * searching.
+ *
+ * @returns 0 based position of pStrNeedle. npos if not found or pStrNeedle is
+ * NULL or an empty string.
+ */
+ size_t find(const RTCString &rStrNeedle, size_t offStart = 0) const RT_NOEXCEPT;
+ size_t find_first_of(const RTCString &rStrNeedle, size_t offStart = 0) const RT_NOEXCEPT
+ { return find(rStrNeedle, offStart); }
+
+ /**
+ * Find the given character (byte).
+ *
+ * @returns 0 based position of chNeedle. npos if not found or pStrNeedle is
+ * NULL or an empty string.
+ * @param chNeedle The character (byte) to find.
+ * @param offStart The (byte) offset into the string buffer to start
+ * searching. Default is start of the string.
+ *
+ * @note This searches for a C character value, not a codepoint. Use the
+ * string version to locate codepoints above U+7F.
+ */
+ size_t find(char chNeedle, size_t offStart = 0) const RT_NOEXCEPT;
+ size_t find_first_of(char chNeedle, size_t offStart = 0) const RT_NOEXCEPT
+ { return find(chNeedle, offStart); }
+
+ /**
+ * Replaces all occurences of cFind with cReplace in the member string.
+ * In order not to produce invalid UTF-8, the characters must be ASCII
+ * values less than 128; this is not verified.
+ *
+ * @param chFind Character to replace. Must be ASCII < 128.
+ * @param chReplace Character to replace cFind with. Must be ASCII < 128.
+ */
+ void findReplace(char chFind, char chReplace) RT_NOEXCEPT;
+
+ /**
+ * Count the occurences of the specified character in the string.
+ *
+ * @param ch What to search for. Must be ASCII < 128.
+ * @remarks QString::count
+ */
+ size_t count(char ch) const RT_NOEXCEPT;
+
+ /**
+ * Count the occurences of the specified sub-string in the string.
+ *
+ * @param psz What to search for.
+ * @param cs Case sensitivity selector.
+ * @remarks QString::count
+ */
+ size_t count(const char *psz, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Count the occurences of the specified sub-string in the string.
+ *
+ * @param pStr What to search for.
+ * @param cs Case sensitivity selector.
+ * @remarks QString::count
+ */
+ size_t count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Strips leading and trailing spaces.
+ *
+ * @returns this
+ */
+ RTCString &strip() RT_NOEXCEPT;
+
+ /**
+ * Strips leading spaces.
+ *
+ * @returns this
+ */
+ RTCString &stripLeft() RT_NOEXCEPT;
+
+ /**
+ * Strips trailing spaces.
+ *
+ * @returns this
+ */
+ RTCString &stripRight() RT_NOEXCEPT;
+
+ /**
+ * Returns a substring of @a this as a new Utf8Str.
+ *
+ * Works exactly like its equivalent in std::string. With the default
+ * parameters "0" and "npos", this always copies the entire string. The
+ * "pos" and "n" arguments represent bytes; it is the caller's responsibility
+ * to ensure that the offsets do not copy invalid UTF-8 sequences. When
+ * used in conjunction with find() and length(), this will work.
+ *
+ * @param pos Index of first byte offset to copy from @a this,
+ * counting from 0.
+ * @param n Number of bytes to copy, starting with the one at "pos".
+ * The copying will stop if the null terminator is encountered before
+ * n bytes have been copied.
+ */
+ RTCString substr(size_t pos = 0, size_t n = npos) const
+ {
+ return RTCString(*this, pos, n);
+ }
+
+ /**
+ * Returns a substring of @a this as a new Utf8Str. As opposed to substr(), this
+ * variant takes codepoint offsets instead of byte offsets.
+ *
+ * @param pos Index of first unicode codepoint to copy from
+ * @a this, counting from 0.
+ * @param n Number of unicode codepoints to copy, starting with
+ * the one at "pos". The copying will stop if the null
+ * terminator is encountered before n codepoints have
+ * been copied.
+ */
+ RTCString substrCP(size_t pos = 0, size_t n = npos) const;
+
+ /**
+ * Returns true if @a this ends with @a that.
+ *
+ * @param that Suffix to test for.
+ * @param cs Case sensitivity selector.
+ * @returns true if match, false if mismatch.
+ */
+ bool endsWith(const RTCString &that, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Returns true if @a this begins with @a that.
+ * @param that Prefix to test for.
+ * @param cs Case sensitivity selector.
+ * @returns true if match, false if mismatch.
+ */
+ bool startsWith(const RTCString &that, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Checks if the string starts with the given word, ignoring leading blanks.
+ *
+ * @param pszWord The word to test for.
+ * @param enmCase Case sensitivity selector.
+ * @returns true if match, false if mismatch.
+ */
+ bool startsWithWord(const char *pszWord, CaseSensitivity enmCase = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Checks if the string starts with the given word, ignoring leading blanks.
+ *
+ * @param rThat Prefix to test for.
+ * @param enmCase Case sensitivity selector.
+ * @returns true if match, false if mismatch.
+ */
+ bool startsWithWord(const RTCString &rThat, CaseSensitivity enmCase = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Returns true if @a this contains @a that (strstr).
+ *
+ * @param that Substring to look for.
+ * @param cs Case sensitivity selector.
+ * @returns true if found, false if not found.
+ */
+ bool contains(const RTCString &that, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Returns true if @a this contains @a pszNeedle (strstr).
+ *
+ * @param pszNeedle Substring to look for.
+ * @param cs Case sensitivity selector.
+ * @returns true if found, false if not found.
+ */
+ bool contains(const char *pszNeedle, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT;
+
+ /**
+ * Attempts to convert the member string into a 32-bit integer.
+ *
+ * @returns 32-bit unsigned number on success.
+ * @returns 0 on failure.
+ */
+ int32_t toInt32() const RT_NOEXCEPT
+ {
+ return RTStrToInt32(c_str());
+ }
+
+ /**
+ * Attempts to convert the member string into an unsigned 32-bit integer.
+ *
+ * @returns 32-bit unsigned number on success.
+ * @returns 0 on failure.
+ */
+ uint32_t toUInt32() const RT_NOEXCEPT
+ {
+ return RTStrToUInt32(c_str());
+ }
+
+ /**
+ * Attempts to convert the member string into an 64-bit integer.
+ *
+ * @returns 64-bit unsigned number on success.
+ * @returns 0 on failure.
+ */
+ int64_t toInt64() const RT_NOEXCEPT
+ {
+ return RTStrToInt64(c_str());
+ }
+
+ /**
+ * Attempts to convert the member string into an unsigned 64-bit integer.
+ *
+ * @returns 64-bit unsigned number on success.
+ * @returns 0 on failure.
+ */
+ uint64_t toUInt64() const RT_NOEXCEPT
+ {
+ return RTStrToUInt64(c_str());
+ }
+
+ /**
+ * Attempts to convert the member string into an unsigned 64-bit integer.
+ *
+ * @param i Where to return the value on success.
+ * @returns IPRT error code, see RTStrToInt64.
+ */
+ int toInt(uint64_t &i) const RT_NOEXCEPT;
+
+ /**
+ * Attempts to convert the member string into an unsigned 32-bit integer.
+ *
+ * @param i Where to return the value on success.
+ * @returns IPRT error code, see RTStrToInt32.
+ */
+ int toInt(uint32_t &i) const RT_NOEXCEPT;
+
+ /** Splitting behavior regarding empty sections in the string. */
+ enum SplitMode
+ {
+ KeepEmptyParts, /**< Empty parts are added as empty strings to the result list. */
+ RemoveEmptyParts /**< Empty parts are skipped. */
+ };
+
+ /**
+ * Splits a string separated by strSep into its parts.
+ *
+ * @param a_rstrSep The separator to search for.
+ * @param a_enmMode How should empty parts be handled.
+ * @returns separated strings as string list.
+ * @throws std::bad_alloc On allocation error.
+ */
+ RTCList<RTCString, RTCString *> split(const RTCString &a_rstrSep,
+ SplitMode a_enmMode = RemoveEmptyParts) const;
+
+ /**
+ * Joins a list of strings together using the provided separator and
+ * an optional prefix for each item in the list.
+ *
+ * @param a_rList The list to join.
+ * @param a_rstrPrefix The prefix used for appending to each item.
+ * @param a_rstrSep The separator used for joining.
+ * @returns joined string.
+ * @throws std::bad_alloc On allocation error.
+ */
+ static RTCString joinEx(const RTCList<RTCString, RTCString *> &a_rList,
+ const RTCString &a_rstrPrefix /* = "" */,
+ const RTCString &a_rstrSep /* = "" */);
+
+ /**
+ * Joins a list of strings together using the provided separator.
+ *
+ * @param a_rList The list to join.
+ * @param a_rstrSep The separator used for joining.
+ * @returns joined string.
+ * @throws std::bad_alloc On allocation error.
+ */
+ static RTCString join(const RTCList<RTCString, RTCString *> &a_rList,
+ const RTCString &a_rstrSep = "");
+
+ /**
+ * Swaps two strings in a fast way.
+ *
+ * Exception safe.
+ *
+ * @param a_rThat The string to swap with.
+ */
+ inline void swap(RTCString &a_rThat) RT_NOEXCEPT
+ {
+ char *pszTmp = m_psz;
+ size_t cchTmp = m_cch;
+ size_t cbAllocatedTmp = m_cbAllocated;
+
+ m_psz = a_rThat.m_psz;
+ m_cch = a_rThat.m_cch;
+ m_cbAllocated = a_rThat.m_cbAllocated;
+
+ a_rThat.m_psz = pszTmp;
+ a_rThat.m_cch = cchTmp;
+ a_rThat.m_cbAllocated = cbAllocatedTmp;
+ }
+
+protected:
+
+ /**
+ * Hide operator bool() to force people to use isEmpty() explicitly.
+ */
+ operator bool() const;
+
+ /**
+ * Destructor implementation, also used to clean up in operator=() before
+ * assigning a new string.
+ */
+ void cleanup() RT_NOEXCEPT
+ {
+ if (m_psz)
+ {
+ RTStrFree(m_psz);
+ m_psz = NULL;
+ m_cch = 0;
+ m_cbAllocated = 0;
+ }
+ }
+
+ /**
+ * Protected internal helper to copy a string.
+ *
+ * This ignores the previous object state, so either call this from a
+ * constructor or call cleanup() first. copyFromN() unconditionally sets
+ * the members to a copy of the given other strings and makes no
+ * assumptions about previous contents. Can therefore be used both in copy
+ * constructors, when member variables have no defined value, and in
+ * assignments after having called cleanup().
+ *
+ * @param pcszSrc The source string.
+ * @param cchSrc The number of chars (bytes) to copy from the
+ * source strings. RTSTR_MAX is NOT accepted.
+ *
+ * @throws std::bad_alloc On allocation failure. The object is left
+ * describing a NULL string.
+ */
+ void copyFromN(const char *pcszSrc, size_t cchSrc)
+ {
+ if (cchSrc)
+ {
+ m_psz = RTStrAlloc(cchSrc + 1);
+ if (RT_LIKELY(m_psz))
+ {
+ m_cch = cchSrc;
+ m_cbAllocated = cchSrc + 1;
+ memcpy(m_psz, pcszSrc, cchSrc);
+ m_psz[cchSrc] = '\0';
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#endif
+ }
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+ }
+ }
+
+ /**
+ * Appends exactly @a cchSrc chars from @a pszSrc to @a this.
+ *
+ * This is an internal worker for the append() methods.
+ *
+ * @returns Reference to the object.
+ * @param pszSrc The source string.
+ * @param cchSrc The source string length (exact).
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ *
+ */
+ RTCString &appendWorker(const char *pszSrc, size_t cchSrc);
+
+ /**
+ * Appends exactly @a cchSrc chars from @a pszSrc to @a this.
+ *
+ * This is an internal worker for the appendNoThrow() methods.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STRING_MEMORY.
+ * @param pszSrc The source string.
+ * @param cchSrc The source string length (exact).
+ */
+ int appendWorkerNoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT;
+
+ /**
+ * Replaces exatly @a cchLength chars at @a offStart with @a cchSrc from @a
+ * pszSrc.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszSrc The replacement string.
+ * @param cchSrc The exactly length of the replacement string.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ RTCString &replaceWorker(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc);
+
+ /**
+ * Replaces exatly @a cchLength chars at @a offStart with @a cchSrc from @a
+ * pszSrc.
+ *
+ * @returns VINF_SUCCESS, VERR_OUT_OF_RANGE or VERR_NO_STRING_MEMORY.
+ * @param offStart Where in @a this string to start replacing.
+ * @param cchLength How much following @a offStart to replace. npos is
+ * accepted.
+ * @param pszSrc The replacement string.
+ * @param cchSrc The exactly length of the replacement string.
+ */
+ int replaceWorkerNoThrow(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc) RT_NOEXCEPT;
+
+ static DECLCALLBACK(size_t) printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars);
+ static DECLCALLBACK(size_t) printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT;
+
+ char *m_psz; /**< The string buffer. */
+ size_t m_cch; /**< strlen(m_psz) - i.e. no terminator included. */
+ size_t m_cbAllocated; /**< Size of buffer that m_psz points to; at least m_cbLength + 1. */
+};
+
+/** @} */
+
+
+/** @addtogroup grp_rt_cpp_string
+ * @{
+ */
+
+/**
+ * Concatenate two strings.
+ *
+ * @param a_rstr1 String one.
+ * @param a_rstr2 String two.
+ * @returns the concatenate string.
+ *
+ * @relates RTCString
+ */
+RTDECL(const RTCString) operator+(const RTCString &a_rstr1, const RTCString &a_rstr2);
+
+/**
+ * Concatenate two strings.
+ *
+ * @param a_rstr1 String one.
+ * @param a_psz2 String two.
+ * @returns the concatenate string.
+ *
+ * @relates RTCString
+ */
+RTDECL(const RTCString) operator+(const RTCString &a_rstr1, const char *a_psz2);
+
+/**
+ * Concatenate two strings.
+ *
+ * @param a_psz1 String one.
+ * @param a_rstr2 String two.
+ * @returns the concatenate string.
+ *
+ * @relates RTCString
+ */
+RTDECL(const RTCString) operator+(const char *a_psz1, const RTCString &a_rstr2);
+
+/**
+ * Class with RTCString::printf as constructor for your convenience.
+ *
+ * Constructing a RTCString string object from a format string and a variable
+ * number of arguments can easily be confused with the other RTCString
+ * constructors, thus this child class.
+ *
+ * The usage of this class is like the following:
+ * @code
+ RTCStringFmt strName("program name = %s", argv[0]);
+ @endcode
+ */
+class RTCStringFmt : public RTCString
+{
+public:
+
+ /**
+ * Constructs a new string given the format string and the list of the
+ * arguments for the format string.
+ *
+ * @param a_pszFormat Pointer to the format string (UTF-8),
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ */
+ explicit RTCStringFmt(const char *a_pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2)
+ {
+ va_list va;
+ va_start(va, a_pszFormat);
+ printfV(a_pszFormat, va);
+ va_end(va);
+ }
+
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+
+protected:
+ RTCStringFmt() {}
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_ministring_h */
+
diff --git a/include/iprt/cpp/mtlist.h b/include/iprt/cpp/mtlist.h
new file mode 100644
index 00000000..915fcbad
--- /dev/null
+++ b/include/iprt/cpp/mtlist.h
@@ -0,0 +1,185 @@
+/** @file
+ * IPRT - Generic thread-safe list Class.
+ */
+
+/*
+ * Copyright (C) 2011-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_mtlist_h
+#define IPRT_INCLUDED_cpp_mtlist_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/list.h>
+#include <iprt/semaphore.h>
+
+/** @addtogroup grp_rt_cpp_list
+ * @{
+ */
+
+/**
+ * A guard class for thread-safe read/write access.
+ */
+template <>
+class RTCListGuard<true>
+{
+public:
+ RTCListGuard() : m_hRWSem(NIL_RTSEMRW)
+ {
+#if defined(RT_LOCK_STRICT_ORDER) && defined(IN_RING3)
+ RTLOCKVALCLASS hClass;
+ int rc = RTLockValidatorClassCreate(&hClass, true /*fAutodidact*/, RT_SRC_POS, "RTCListGuard");
+ AssertStmt(RT_SUCCESS(rc), hClass = NIL_RTLOCKVALCLASS);
+ rc = RTSemRWCreateEx(&m_hRWSem, 0 /*fFlags*/, hClass, RTLOCKVAL_SUB_CLASS_NONE, NULL /*pszNameFmt*/);
+ AssertRC(rc);
+#else
+ int rc = RTSemRWCreateEx(&m_hRWSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, 0, NULL);
+ AssertRC(rc);
+#endif
+ }
+
+ ~RTCListGuard()
+ {
+ RTSemRWDestroy(m_hRWSem);
+ m_hRWSem = NIL_RTSEMRW;
+ }
+
+ inline void enterRead() const { int rc = RTSemRWRequestRead(m_hRWSem, RT_INDEFINITE_WAIT); AssertRC(rc); }
+ inline void leaveRead() const { int rc = RTSemRWReleaseRead(m_hRWSem); AssertRC(rc); }
+ inline void enterWrite() { int rc = RTSemRWRequestWrite(m_hRWSem, RT_INDEFINITE_WAIT); AssertRC(rc); }
+ inline void leaveWrite() { int rc = RTSemRWReleaseWrite(m_hRWSem); AssertRC(rc); }
+
+ /* Define our own new and delete. */
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+
+private:
+ mutable RTSEMRW m_hRWSem;
+};
+
+/**
+ * @brief Generic thread-safe list class.
+ *
+ * RTCMTList is a thread-safe implementation of the list class. It uses a
+ * read/write semaphore to serialize the access to the items. Several readers
+ * can simultaneous access different or the same item. If one thread is writing
+ * to an item, the other accessors are blocked until the write has finished.
+ *
+ * Although the access is guarded, the user has to make sure the list content
+ * is consistent when iterating over the list or doing any other kind of access
+ * which makes assumptions about the list content. For a finer control of access
+ * restrictions, use your own locking mechanism and the standard list
+ * implementation.
+ *
+ * @see RTCListBase
+ */
+template <class T, typename ITYPE = typename RTCIf<(sizeof(T) > sizeof(void*)), T*, T>::result>
+class RTCMTList : public RTCListBase<T, ITYPE, true>
+{
+ /* Traits */
+ typedef RTCListBase<T, ITYPE, true> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCMTList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ /* Define our own new and delete. */
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+};
+
+/**
+ * Specialized thread-safe list class for using the native type list for
+ * unsigned 64-bit values even on a 32-bit host.
+ *
+ * @see RTCListBase
+ */
+template <>
+class RTCMTList<uint64_t>: public RTCListBase<uint64_t, uint64_t, true>
+{
+ /* Traits */
+ typedef RTCListBase<uint64_t, uint64_t, true> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCMTList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ /* Define our own new and delete. */
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+};
+
+/**
+ * Specialized thread-safe list class for using the native type list for
+ * signed 64-bit values even on a 32-bit host.
+ *
+ * @see RTCListBase
+ */
+template <>
+class RTCMTList<int64_t>: public RTCListBase<int64_t, int64_t, true>
+{
+ /* Traits */
+ typedef RTCListBase<int64_t, int64_t, true> BASE;
+
+public:
+ /**
+ * Creates a new list.
+ *
+ * This preallocates @a cCapacity elements within the list.
+ *
+ * @param cCapacity The initial capacity the list has.
+ * @throws std::bad_alloc
+ */
+ RTCMTList(size_t cCapacity = BASE::kDefaultCapacity)
+ : BASE(cCapacity) {}
+
+ /* Define our own new and delete. */
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_mtlist_h */
+
diff --git a/include/iprt/cpp/path.h b/include/iprt/cpp/path.h
new file mode 100644
index 00000000..1a3c7f99
--- /dev/null
+++ b/include/iprt/cpp/path.h
@@ -0,0 +1,249 @@
+/** @file
+ * IPRT - C++ path utilities.
+ */
+
+/*
+ * Copyright (C) 2017-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_path_h
+#define IPRT_INCLUDED_cpp_path_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include <iprt/cpp/ministring.h>
+
+
+/** @defgroup grp_rt_cpp_path C++ Path Utilities
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * RTPathAbs() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrAbs Reference to the destination string.
+ * @param pszRelative The relative source string.
+ */
+DECLINLINE(int) RTPathAbsCxx(RTCString &rStrAbs, const char *pszRelative)
+{
+ Assert(rStrAbs.c_str() != pszRelative);
+ int rc = rStrAbs.reserveNoThrow(RTPATH_MAX);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cTries = 8;
+ for (;;)
+ {
+ char *pszDst = rStrAbs.mutableRaw();
+ size_t cbCap = rStrAbs.capacity();
+ rc = RTPathAbsEx(NULL, pszRelative, RTPATH_STR_F_STYLE_HOST, pszDst, &cbCap);
+ if (RT_SUCCESS(rc))
+ break;
+ *pszDst = '\0';
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ if (--cTries == 0)
+ break;
+ rc = rStrAbs.reserveNoThrow(RT_MIN(RT_ALIGN_Z(cbCap, 64), RTPATH_MAX));
+ if (RT_FAILURE(rc))
+ break;
+ }
+ rStrAbs.jolt();
+ }
+ return rc;
+}
+
+
+/**
+ * RTPathAbs() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrAbs Reference to the destination string.
+ * @param rStrRelative Reference to the relative source string.
+ */
+DECLINLINE(int) RTPathAbsCxx(RTCString &rStrAbs, RTCString const &rStrRelative)
+{
+ return RTPathAbsCxx(rStrAbs, rStrRelative.c_str());
+}
+
+
+
+/**
+ * RTPathAbsEx() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrAbs Reference to the destination string.
+ * @param pszBase The base path, optional.
+ * @param pszRelative The relative source string.
+ * @param fFlags RTPATH_STR_F_STYLE_XXX and RTPATHABS_F_XXX flags.
+ */
+DECLINLINE(int) RTPathAbsExCxx(RTCString &rStrAbs, const char *pszBase, const char *pszRelative, uint32_t fFlags = RTPATH_STR_F_STYLE_HOST)
+{
+ Assert(rStrAbs.c_str() != pszRelative);
+ int rc = rStrAbs.reserveNoThrow(RTPATH_MAX);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cTries = 8;
+ for (;;)
+ {
+ char *pszDst = rStrAbs.mutableRaw();
+ size_t cbCap = rStrAbs.capacity();
+ rc = RTPathAbsEx(pszBase, pszRelative, fFlags, pszDst, &cbCap);
+ if (RT_SUCCESS(rc))
+ break;
+ *pszDst = '\0';
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ if (--cTries == 0)
+ break;
+ rc = rStrAbs.reserveNoThrow(RT_MIN(RT_ALIGN_Z(cbCap, 64), RTPATH_MAX));
+ if (RT_FAILURE(rc))
+ break;
+ }
+ rStrAbs.jolt();
+ }
+ return rc;
+}
+
+
+DECLINLINE(int) RTPathAbsExCxx(RTCString &rStrAbs, RTCString const &rStrBase, RTCString const &rStrRelative, uint32_t fFlags = RTPATH_STR_F_STYLE_HOST)
+{
+ return RTPathAbsExCxx(rStrAbs, rStrBase.c_str(), rStrRelative.c_str(), fFlags);
+}
+
+
+DECLINLINE(int) RTPathAbsExCxx(RTCString &rStrAbs, const char *pszBase, RTCString const &rStrRelative, uint32_t fFlags = RTPATH_STR_F_STYLE_HOST)
+{
+ return RTPathAbsExCxx(rStrAbs, pszBase, rStrRelative.c_str(), fFlags);
+}
+
+
+DECLINLINE(int) RTPathAbsExCxx(RTCString &rStrAbs, RTCString const &rStrBase, const char *pszRelative, uint32_t fFlags = RTPATH_STR_F_STYLE_HOST)
+{
+ return RTPathAbsExCxx(rStrAbs, rStrBase.c_str(), pszRelative, fFlags);
+}
+
+
+
+/**
+ * RTPathAppPrivateNoArch() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrDst Reference to the destination string.
+ */
+DECLINLINE(int) RTPathAppPrivateNoArchCxx(RTCString &rStrDst)
+{
+ int rc = rStrDst.reserveNoThrow(RTPATH_MAX);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszDst = rStrDst.mutableRaw();
+ rc = RTPathAppPrivateNoArch(pszDst, rStrDst.capacity());
+ if (RT_FAILURE(rc))
+ *pszDst = '\0';
+ rStrDst.jolt();
+ }
+ return rc;
+
+}
+
+
+/**
+ * RTPathAppend() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrDst Reference to the destination string.
+ * @param pszAppend One or more components to append to the path already
+ * present in @a rStrDst.
+ */
+DECLINLINE(int) RTPathAppendCxx(RTCString &rStrDst, const char *pszAppend)
+{
+ Assert(rStrDst.c_str() != pszAppend);
+ size_t cbEstimate = rStrDst.length() + 1 + strlen(pszAppend) + 1;
+ int rc;
+ if (rStrDst.capacity() >= cbEstimate)
+ rc = VINF_SUCCESS;
+ else
+ rc = rStrDst.reserveNoThrow(RT_ALIGN_Z(cbEstimate, 8));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathAppend(rStrDst.mutableRaw(), rStrDst.capacity(), pszAppend);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ rc = rStrDst.reserveNoThrow(RTPATH_MAX);
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(rStrDst.mutableRaw(), rStrDst.capacity(), pszAppend);
+ }
+ rStrDst.jolt();
+ }
+ return rc;
+}
+
+
+/**
+ * RTPathAppend() wrapper for working directly on a RTCString instance.
+ *
+ * @returns IPRT status code.
+ * @param rStrDst Reference to the destination string.
+ * @param rStrAppend One or more components to append to the path already
+ * present in @a rStrDst.
+ */
+DECLINLINE(int) RTPathAppendCxx(RTCString &rStrDst, RTCString const &rStrAppend)
+{
+ Assert(&rStrDst != &rStrAppend);
+ size_t cbEstimate = rStrDst.length() + 1 + rStrAppend.length() + 1;
+ int rc;
+ if (rStrDst.capacity() >= cbEstimate)
+ rc = VINF_SUCCESS;
+ else
+ rc = rStrDst.reserveNoThrow(RT_ALIGN_Z(cbEstimate, 8));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathAppend(rStrDst.mutableRaw(), rStrDst.capacity(), rStrAppend.c_str());
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ rc = rStrDst.reserveNoThrow(RTPATH_MAX);
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(rStrDst.mutableRaw(), rStrDst.capacity(), rStrAppend.c_str());
+ }
+ rStrDst.jolt();
+ }
+ return rc;
+}
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_path_h */
+
diff --git a/include/iprt/cpp/restanyobject.h b/include/iprt/cpp/restanyobject.h
new file mode 100644
index 00000000..a83a769b
--- /dev/null
+++ b/include/iprt/cpp/restanyobject.h
@@ -0,0 +1,139 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) Any Object Class.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_restanyobject_h
+#define IPRT_INCLUDED_cpp_restanyobject_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/restbase.h>
+#include <iprt/cpp/restarray.h>
+#include <iprt/cpp/reststringmap.h>
+
+
+/** @defgroup grp_rt_cpp_restanyobj C++ Representational State Transfer (REST) Any Object Class.
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Wrapper object that can represent any kind of basic REST object.
+ *
+ * This class is the result of a couple of design choices made in our REST
+ * data model. If could have been avoided if we used pointers all over
+ * the place and didn't rely entirely on the object specific implementations
+ * of deserializeFromJson and fromString to do the deserializing or everything.
+ *
+ * The assumption, though, was that most of the data we're dealing with has a
+ * known structure and maps to fixed types. So, the data model was optimized
+ * for that rather than flexiblity here.
+ */
+class RT_DECL_CLASS RTCRestAnyObject : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestAnyObject() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestAnyObject();
+
+ /** Copy constructor. */
+ RTCRestAnyObject(RTCRestAnyObject const &a_rThat);
+ /** Copy assignment operator. */
+ RTCRestAnyObject &operator=(RTCRestAnyObject const &a_rThat);
+
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestAnyObject const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, boolean variant. */
+ int assignCopy(RTCRestBool const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, int64_t variant. */
+ int assignCopy(RTCRestInt64 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, int32_t variant. */
+ int assignCopy(RTCRestInt32 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, int16_t variant. */
+ int assignCopy(RTCRestInt16 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, double variant. */
+ int assignCopy(RTCRestDouble const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, string variant. */
+ int assignCopy(RTCRestString const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, array variant. */
+ int assignCopy(RTCRestArray<RTCRestAnyObject> const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method, string map variant. */
+ int assignCopy(RTCRestStringMap<RTCRestAnyObject> const &a_rThat) RT_NOEXCEPT;
+
+ /** Safe value assignment method, boolean variant. */
+ int assignValue(bool a_fValue) RT_NOEXCEPT;
+ /** Safe value assignment method, int64_t variant. */
+ int assignValue(int64_t a_iValue) RT_NOEXCEPT;
+ /** Safe value assignment method, int32_t variant. */
+ int assignValue(int32_t a_iValue) RT_NOEXCEPT;
+ /** Safe value assignment method, int16_t variant. */
+ int assignValue(int16_t a_iValue) RT_NOEXCEPT;
+ /** Safe value assignment method, double variant. */
+ int assignValue(double a_iValue) RT_NOEXCEPT;
+ /** Safe value assignment method, string variant. */
+ int assignValue(RTCString const &a_rValue) RT_NOEXCEPT;
+ /** Safe value assignment method, C-string variant. */
+ int assignValue(const char *a_pszValue) RT_NOEXCEPT;
+
+ /** Make a clone of this object. */
+ inline RTCRestAnyObject *clone() const RT_NOEXCEPT { return (RTCRestAnyObject *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int setNull(void) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** Deserialization w/ instantiation. */
+ static FNDESERIALIZEINSTANCEFROMJSON deserializeInstanceFromJson;
+
+protected:
+ /** The data. */
+ RTCRestObjectBase *m_pData;
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_restanyobject_h */
+
diff --git a/include/iprt/cpp/restarray.h b/include/iprt/cpp/restarray.h
new file mode 100644
index 00000000..56509c4e
--- /dev/null
+++ b/include/iprt/cpp/restarray.h
@@ -0,0 +1,463 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) Array Template Class.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_restarray_h
+#define IPRT_INCLUDED_cpp_restarray_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpp/restbase.h>
+
+
+/** @defgroup grp_rt_cpp_restarray C++ Representational State Transfer (REST) Array Template Class.
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Abstract base class for the RTCRestArray template.
+ */
+class RT_DECL_CLASS RTCRestArrayBase : public RTCRestObjectBase
+{
+public:
+ /** Default destructor. */
+ RTCRestArrayBase() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestArrayBase();
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Clear the content of the map.
+ */
+ void clear() RT_NOEXCEPT;
+
+ /**
+ * Check if an list contains any items.
+ *
+ * @return True if there is more than zero items, false otherwise.
+ */
+ inline bool isEmpty() const RT_NOEXCEPT
+ {
+ return m_cElements == 0;
+ }
+
+ /**
+ * Gets the number of entries in the map.
+ */
+ inline size_t size() const RT_NOEXCEPT
+ {
+ return m_cElements;
+ }
+
+ /**
+ * Returns the base object pointer at a given index.
+ *
+ * @returns The base object at @a a_idx, NULL if out of range.
+ * @param a_idx The array index.
+ */
+ inline RTCRestObjectBase *atBase(size_t a_idx) RT_NOEXCEPT
+ {
+ if (a_idx < m_cElements)
+ return m_papElements[a_idx];
+ return NULL;
+ }
+
+ /**
+ * Returns the const base object pointer at a given index.
+ *
+ * @returns The base object at @a a_idx, NULL if out of range.
+ * @param a_idx The array index.
+ */
+ inline RTCRestObjectBase const *atBase(size_t a_idx) const RT_NOEXCEPT
+ {
+ if (a_idx < m_cElements)
+ return m_papElements[a_idx];
+ return NULL;
+ }
+
+ /**
+ * Removes the element at @a a_idx.
+ * @returns true if @a a_idx is valid, false if out of range.
+ * @param a_idx The index of the element to remove.
+ * The value ~(size_t)0 is an alias for the final element.
+ */
+ bool removeAt(size_t a_idx) RT_NOEXCEPT;
+
+ /**
+ * Makes sure the array can hold at the given number of entries.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param a_cEnsureCapacity The number of elements to ensure capacity to hold.
+ */
+ int ensureCapacity(size_t a_cEnsureCapacity) RT_NOEXCEPT;
+
+
+protected:
+ /** The array. */
+ RTCRestObjectBase **m_papElements;
+ /** Number of elements in the array. */
+ size_t m_cElements;
+ /** The number of elements m_papElements can hold.
+ * The difference between m_cCapacity and m_cElements are all NULLs. */
+ size_t m_cCapacity;
+
+ /**
+ * Helper for creating a clone.
+ *
+ * @returns Pointer to new array on success, NULL if out of memory.
+ */
+ virtual RTCRestArrayBase *createClone(void) const RT_NOEXCEPT = 0;
+
+ /**
+ * Wrapper around the value constructor.
+ *
+ * @returns Pointer to new value object on success, NULL if out of memory.
+ */
+ virtual RTCRestObjectBase *createValue(void) RT_NOEXCEPT = 0;
+
+ /**
+ * For accessing the static deserializeInstanceFromJson() method of the value.
+ */
+ virtual int deserializeValueInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT = 0;
+
+ /**
+ * Worker for the copy assignment method and copyArrayWorkerMayThrow().
+ *
+ * This will use createEntryCopy to do the copying.
+ *
+ * @returns VINF_SUCCESS on success, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_rThat The array to copy. Caller makes 100% sure the it has
+ * the same type as the destination.
+ */
+ int copyArrayWorkerNoThrow(RTCRestArrayBase const &a_rThat) RT_NOEXCEPT;
+
+ /**
+ * Wrapper around copyArrayWorkerNoThrow for the copy constructor and the
+ * assignment operator.
+ */
+ void copyArrayWorkerMayThrow(RTCRestArrayBase const &a_rThat);
+
+ /**
+ * Worker for performing inserts.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_idx Where to insert it. The value ~(size_t)0 is an alias for m_cElements.
+ * @param a_pValue The value to insert. Ownership is transferred to the map on success.
+ * @param a_fReplace Whether to replace existing entry rather than insert.
+ */
+ int insertWorker(size_t a_idx, RTCRestObjectBase *a_pValue, bool a_fReplace) RT_NOEXCEPT;
+
+ /**
+ * Worker for performing inserts.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_idx Where to insert it. The value ~(size_t)0 is an alias for m_cElements.
+ * @param a_rValue The value to copy into the map.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ */
+ int insertCopyWorker(size_t a_idx, RTCRestObjectBase const &a_rValue, bool a_fReplace) RT_NOEXCEPT;
+
+private:
+ /** Copy constructor on this class should never be used. */
+ RTCRestArrayBase(RTCRestArrayBase const &a_rThat);
+ /** Copy assignment operator on this class should never be used. */
+ RTCRestArrayBase &operator=(RTCRestArrayBase const &a_rThat);
+};
+
+
+
+/**
+ * Limited array class.
+ */
+template<class ElementType> class RTCRestArray : public RTCRestArrayBase
+{
+public:
+ /** Default constructor - empty array. */
+ RTCRestArray() RT_NOEXCEPT
+ : RTCRestArrayBase()
+ {
+ }
+
+ /** Destructor. */
+ ~RTCRestArray()
+ {
+ }
+
+ /** Copy constructor. */
+ RTCRestArray(RTCRestArray const &a_rThat)
+ : RTCRestArrayBase()
+ {
+ copyArrayWorkerMayThrow(a_rThat);
+ }
+
+ /** Copy assignment operator. */
+ inline RTCRestArray &operator=(RTCRestArray const &a_rThat)
+ {
+ copyArrayWorkerMayThrow(a_rThat);
+ return *this;
+ }
+
+ /** Safe copy assignment method. */
+ inline int assignCopy(RTCRestArray const &a_rThat) RT_NOEXCEPT
+ {
+ return copyArrayWorkerNoThrow(a_rThat);
+ }
+
+ /** Make a clone of this object. */
+ inline RTCRestArray *clone() const RT_NOEXCEPT
+ {
+ return (RTCRestArray *)baseClone();
+ }
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT
+ {
+ return new (std::nothrow) RTCRestArray<ElementType>();
+ }
+
+ /** Factory method for elements. */
+ static DECLCALLBACK(RTCRestObjectBase *) createElementInstance(void) RT_NOEXCEPT
+ {
+ return new (std::nothrow) ElementType();
+ }
+
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+ {
+ *a_ppInstance = new (std::nothrow) RTCRestArray<ElementType>();
+ if (*a_ppInstance)
+ return (*a_ppInstance)->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+ }
+
+
+ /**
+ * Insert the given object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_INVALID_POINTER, VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_idx The insertion index. ~(size_t)0 is an alias for the end.
+ * @param a_pThat The object to insert. The array takes ownership of the object on success.
+ */
+ inline int insert(size_t a_idx, ElementType *a_pThat) RT_NOEXCEPT
+ {
+ return insertWorker(a_idx, a_pThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Insert a copy of the object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_idx The insertion index. ~(size_t)0 is an alias for the end.
+ * @param a_rThat The object to insert a copy of.
+ */
+ inline int insertCopy(size_t a_idx, ElementType const &a_rThat) RT_NOEXCEPT
+ {
+ return insertCopyWorker(a_idx, a_rThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Appends the given object to the array.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_INVALID_POINTER, VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_pThat The object to insert. The array takes ownership of the object on success.
+ */
+ inline int append(ElementType *a_pThat) RT_NOEXCEPT
+ {
+ return insertWorker(~(size_t)0, a_pThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Appends a copy of the object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_rThat The object to insert a copy of.
+ */
+ inline int appendCopy(ElementType const &a_rThat) RT_NOEXCEPT
+ {
+ return insertCopyWorker(~(size_t)0, a_rThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Prepends the given object to the array.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_INVALID_POINTER, VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_pThat The object to insert. The array takes ownership of the object on success.
+ */
+ inline int prepend(ElementType *a_pThat) RT_NOEXCEPT
+ {
+ return insertWorker(0, a_pThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Prepends a copy of the object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_rThat The object to insert a copy of.
+ */
+ inline int prependCopy(ElementType const &a_rThat) RT_NOEXCEPT
+ {
+ return insertCopyWorker(0, a_rThat, false /*a_fReplace*/);
+ }
+
+ /**
+ * Insert the given object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_INVALID_POINTER, VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_idx The index of the existing object to replace.
+ * @param a_pThat The replacement object. The array takes ownership of the object on success.
+ */
+ inline int replace(size_t a_idx, ElementType *a_pThat) RT_NOEXCEPT
+ {
+ return insertWorker(a_idx, a_pThat, true /*a_fReplace*/);
+ }
+
+ /**
+ * Insert a copy of the object at the specified index.
+ *
+ * @returns VINF_SUCCESS on success.
+ * VERR_NO_MEMORY, VERR_NO_STR_MEMORY or VERR_OUT_OF_RANGE on failure.
+ * @param a_idx The index of the existing object to replace.
+ * @param a_rThat The object to insert a copy of.
+ */
+ inline int replaceCopy(size_t a_idx, ElementType const &a_rThat) RT_NOEXCEPT
+ {
+ return insertCopyWorker(a_idx, a_rThat, true /*a_fReplace*/);
+ }
+
+ /**
+ * Returns the object at a given index.
+ *
+ * @returns The object at @a a_idx, NULL if out of range.
+ * @param a_idx The array index.
+ */
+ inline ElementType *at(size_t a_idx) RT_NOEXCEPT
+ {
+ if (a_idx < m_cElements)
+ return (ElementType *)m_papElements[a_idx];
+ return NULL;
+ }
+
+ /**
+ * Returns the object at a given index, const variant.
+ *
+ * @returns The object at @a a_idx, NULL if out of range.
+ * @param a_idx The array index.
+ */
+ inline ElementType const *at(size_t a_idx) const RT_NOEXCEPT
+ {
+ if (a_idx < m_cElements)
+ return (ElementType const *)m_papElements[a_idx];
+ return NULL;
+ }
+
+ /**
+ * Returns the first object in the array.
+ * @returns The first object, NULL if empty.
+ */
+ inline ElementType *first() RT_NOEXCEPT
+ {
+ return at(0);
+ }
+
+ /**
+ * Returns the first object in the array, const variant.
+ * @returns The first object, NULL if empty.
+ */
+ inline ElementType const *first() const RT_NOEXCEPT
+ {
+ return at(0);
+ }
+
+ /**
+ * Returns the last object in the array.
+ * @returns The last object, NULL if empty.
+ */
+ inline ElementType *last() RT_NOEXCEPT
+ {
+ return at(m_cElements - 1);
+ }
+
+ /**
+ * Returns the last object in the array, const variant.
+ * @returns The last object, NULL if empty.
+ */
+ inline ElementType const *last() const RT_NOEXCEPT
+ {
+ return at(m_cElements - 1);
+ }
+
+
+protected:
+ virtual RTCRestArrayBase *createClone(void) const RT_NOEXCEPT RT_OVERRIDE
+ {
+ return new (std::nothrow) RTCRestArray();
+ }
+
+ virtual RTCRestObjectBase *createValue(void) RT_NOEXCEPT RT_OVERRIDE
+ {
+ return new (std::nothrow) ElementType();
+ }
+
+ virtual int deserializeValueInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT RT_OVERRIDE
+ {
+ return ElementType::deserializeInstanceFromJson(a_rCursor, a_ppInstance);
+ }
+};
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_restarray_h */
+
diff --git a/include/iprt/cpp/restbase.h b/include/iprt/cpp/restbase.h
new file mode 100644
index 00000000..912610df
--- /dev/null
+++ b/include/iprt/cpp/restbase.h
@@ -0,0 +1,1106 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) Base Classes.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_restbase_h
+#define IPRT_INCLUDED_cpp_restbase_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/errcore.h> /* VERR_NO_MEMORY */
+#include <iprt/json.h>
+#include <iprt/stdarg.h>
+#include <iprt/time.h>
+#include <iprt/cpp/ministring.h>
+
+
+/** @defgroup grp_rt_cpp_restbase C++ Representational State Transfer (REST) Base Classes.
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/* forward decl: */
+class RTCRestOutputBase;
+class RTCRestJsonPrimaryCursor;
+
+/**
+ * JSON cursor structure.
+ *
+ * This reduces the number of parameters passed around when deserializing JSON
+ * input and also helps constructing full object name for logging and error reporting.
+ */
+struct RT_DECL_CLASS RTCRestJsonCursor
+{
+ /** Handle to the value being parsed. */
+ RTJSONVAL m_hValue;
+ /** Name of the value. */
+ const char *m_pszName;
+ /** Pointer to the parent, NULL if primary. */
+ struct RTCRestJsonCursor const *m_pParent;
+ /** Pointer to the primary cursor structure. */
+ RTCRestJsonPrimaryCursor *m_pPrimary;
+
+ RTCRestJsonCursor(struct RTCRestJsonCursor const &a_rParent) RT_NOEXCEPT
+ : m_hValue(NIL_RTJSONVAL), m_pszName(NULL), m_pParent(&a_rParent), m_pPrimary(a_rParent.m_pPrimary)
+ { }
+
+ RTCRestJsonCursor(RTJSONVAL hValue, const char *pszName, struct RTCRestJsonCursor *pParent) RT_NOEXCEPT
+ : m_hValue(hValue), m_pszName(pszName), m_pParent(pParent), m_pPrimary(pParent->m_pPrimary)
+ { }
+
+ RTCRestJsonCursor(RTJSONVAL hValue, const char *pszName) RT_NOEXCEPT
+ : m_hValue(hValue), m_pszName(pszName), m_pParent(NULL), m_pPrimary(NULL)
+ { }
+
+ ~RTCRestJsonCursor()
+ {
+ if (m_hValue != NIL_RTJSONVAL)
+ {
+ RTJsonValueRelease(m_hValue);
+ m_hValue = NIL_RTJSONVAL;
+ }
+ }
+};
+
+
+/**
+ * The primary JSON cursor class.
+ */
+class RT_DECL_CLASS RTCRestJsonPrimaryCursor
+{
+public:
+ /** The cursor for the first level. */
+ RTCRestJsonCursor m_Cursor;
+ /** Error info keeper. */
+ PRTERRINFO m_pErrInfo;
+
+ /** Creates a primary json cursor with optiona error info. */
+ RTCRestJsonPrimaryCursor(RTJSONVAL hValue, const char *pszName, PRTERRINFO pErrInfo = NULL) RT_NOEXCEPT
+ : m_Cursor(hValue, pszName)
+ , m_pErrInfo(pErrInfo)
+ {
+ m_Cursor.m_pPrimary = this;
+ }
+
+ virtual ~RTCRestJsonPrimaryCursor()
+ { }
+
+ /**
+ * Add an error message.
+ *
+ * @returns a_rc
+ * @param a_rCursor The cursor reporting the error.
+ * @param a_rc The status code.
+ * @param a_pszFormat Format string.
+ * @param ... Format string arguments.
+ */
+ virtual int addError(RTCRestJsonCursor const &a_rCursor, int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT;
+
+ /**
+ * Reports that the current field is not known.
+ *
+ * @returns Status to propagate.
+ * @param a_rCursor The cursor for the field.
+ */
+ virtual int unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT;
+
+ /**
+ * Copies the full path into pszDst.
+ *
+ * @returns pszDst
+ * @param a_rCursor The cursor to start walking at.
+ * @param a_pszDst Where to put the path.
+ * @param a_cbDst Size of the destination buffer.
+ */
+ virtual char *getPath(RTCRestJsonCursor const &a_rCursor, char *a_pszDst, size_t a_cbDst) const RT_NOEXCEPT;
+};
+
+
+/**
+ * Abstract base class for REST primitive types and data objects (via
+ * RTCRestDataObject).
+ *
+ * The only information this keeps is the null indicator.
+ */
+class RT_DECL_CLASS RTCRestObjectBase
+{
+public:
+ RTCRestObjectBase() RT_NOEXCEPT;
+ RTCRestObjectBase(RTCRestObjectBase const &a_rThat) RT_NOEXCEPT;
+ virtual ~RTCRestObjectBase();
+
+ /** Copy assignment operator. */
+ RTCRestObjectBase &operator=(RTCRestObjectBase const &a_rThat) RT_NOEXCEPT;
+
+ /**
+ * Create a copy of this object.
+ *
+ * @returns Pointer to copy.
+ */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT = 0;
+
+ /**
+ * Tests if the object is @a null.
+ * @returns true if null, false if not.
+ */
+ inline bool isNull(void) const RT_NOEXCEPT { return m_fNullIndicator; };
+
+ /**
+ * Sets the object to @a null and fills it with defaults.
+ * @returns IPRT status code (from resetToDefault).
+ */
+ virtual int setNull(void) RT_NOEXCEPT;
+
+ /**
+ * Sets the object to not-null state (i.e. undoes setNull()).
+ * @remarks Only really important for strings.
+ */
+ virtual void setNotNull(void) RT_NOEXCEPT;
+
+ /**
+ * Resets the object to all default values.
+ * @returns IPRT status code.
+ */
+ virtual int resetToDefault() RT_NOEXCEPT = 0;
+
+ /**
+ * Serialize the object as JSON.
+ *
+ * @returns a_rDst
+ * @param a_rDst The destination for the serialization.
+ */
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT = 0;
+
+ /**
+ * Deserialize object from the given JSON iterator.
+ *
+ * @returns IPRT status code.
+ * @param a_rCursor The JSON cursor.
+ */
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT = 0;
+
+ /**
+ * Polymorphic JSON deserialization helper that instantiate the matching class using
+ * the discriminator field.
+ *
+ * @returns IPRT status code.
+ * @param a_rCursor The JSON cursor.
+ * @param a_ppInstance Where to return the deserialized instance.
+ * May return an object on failure.
+ */
+ typedef DECLCALLBACKTYPE(int, FNDESERIALIZEINSTANCEFROMJSON,(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance));
+ /** Pointer to a FNDESERIALIZEINSTANCEFROMJSON function. */
+ typedef FNDESERIALIZEINSTANCEFROMJSON *PFNDESERIALIZEINSTANCEFROMJSON;
+
+ /**
+ * Flags for toString().
+ *
+ * The kCollectionFormat_xxx bunch controls multiple values in arrays
+ * are formatted. They are ignored by everyone else.
+ *
+ * @note When adding collection format types, make sure to also
+ * update RTCRestArrayBase::toString().
+ * @note Bit 24 is reserved (for kHdrField_MapCollection).
+ */
+ enum
+ {
+ kCollectionFormat_Unspecified = 0, /**< Not specified. */
+ kCollectionFormat_csv, /**< Comma-separated list. */
+ kCollectionFormat_ssv, /**< Space-separated list. */
+ kCollectionFormat_tsv, /**< Tab-separated list. */
+ kCollectionFormat_pipes, /**< Pipe-separated list. */
+ kCollectionFormat_multi, /**< Special collection type that must be handled by caller of toString. */
+ kCollectionFormat_Mask = 7, /**< Collection type mask. */
+
+ kToString_Append = 8 /**< Append to the string/object (rather than assigning). */
+ };
+
+ /**
+ * String conversion.
+ *
+ * The default implementation of is a wrapper around serializeAsJson().
+ *
+ * @returns IPRT status code.
+ * @param a_pDst Pointer to the destionation string.
+ * @param a_fFlags kCollectionFormat_xxx.
+ */
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT;
+
+ /**
+ * String convertsion, naive variant.
+ *
+ * @returns String represenation.
+ */
+ RTCString toString() const;
+
+ /**
+ * Convert from (header) string value.
+ *
+ * The default implementation of is a wrapper around deserializeFromJson().
+ *
+ * @returns IPRT status code.
+ * @param a_rValue The string value string to parse.
+ * @param a_pszName Field name or similar.
+ * @param a_pErrInfo Where to return additional error info. Optional.
+ * @param a_fFlags kCollectionFormat_xxx.
+ */
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT;
+
+ /** Type classification */
+ typedef enum kTypeClass
+ {
+ kTypeClass_Invalid = 0,
+ kTypeClass_Bool, /**< Primitive: bool. */
+ kTypeClass_Int64, /**< Primitive: int64_t. */
+ kTypeClass_Int32, /**< Primitive: int32_t. */
+ kTypeClass_Int16, /**< Primitive: int16_t. */
+ kTypeClass_Double, /**< Primitive: double. */
+ kTypeClass_String, /**< Primitive: string. */
+ kTypeClass_Date, /**< Date. */
+ kTypeClass_Uuid, /**< UUID. */
+ kTypeClass_Binary, /**< Binary blob. */
+ kTypeClass_DataObject, /**< Data object child (RTCRestDataObject). */
+ kTypeClass_AnyObject, /**< Any kind of object (RTCRestAnyObject). */
+ kTypeClass_Array, /**< Array (containing any kind of object). */
+ kTypeClass_StringMap, /**< String map (containing any kind of object). */
+ kTypeClass_StringEnum /**< String enum. */
+ } kTypeClass;
+
+ /**
+ * Returns the object type class.
+ */
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT = 0;
+
+ /**
+ * Returns the object type name.
+ */
+ virtual const char *typeName(void) const RT_NOEXCEPT = 0;
+
+protected:
+ /** Null indicator.
+ * @remarks The null values could be mapped onto C/C++ NULL pointer values,
+ * with the consequence that all data members in objects and such would
+ * have had to been allocated individually, even simple @a bool members.
+ * Given that we're overly paranoid about heap allocations (std::bad_alloc),
+ * it's more fitting to use a null indicator for us.
+ */
+ bool m_fNullIndicator;
+};
+
+
+/**
+ * Class wrapping 'bool'.
+ */
+class RT_DECL_CLASS RTCRestBool : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestBool() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestBool(RTCRestBool const &a_rThat) RT_NOEXCEPT;
+ /** From value constructor. */
+ RTCRestBool(bool fValue) RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestBool();
+ /** Copy assignment operator. */
+ RTCRestBool &operator=(RTCRestBool const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestBool const &a_rThat) RT_NOEXCEPT;
+ /** Assign value and clear null indicator. */
+ void assignValue(bool a_fValue) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestBool *clone() const RT_NOEXCEPT { return (RTCRestBool *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+public:
+ /** The value. */
+ bool m_fValue;
+};
+
+
+/**
+ * Class wrapping 'int64_t'.
+ */
+class RT_DECL_CLASS RTCRestInt64 : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestInt64() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestInt64(RTCRestInt64 const &a_rThat) RT_NOEXCEPT;
+ /** From value constructor. */
+ RTCRestInt64(int64_t a_iValue) RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestInt64();
+ /** Copy assignment operator. */
+ RTCRestInt64 &operator=(RTCRestInt64 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestInt64 const &a_rThat) RT_NOEXCEPT;
+ /** Assign value and clear null indicator. */
+ void assignValue(int64_t a_iValue) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestInt64 *clone() const RT_NOEXCEPT { return (RTCRestInt64 *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+public:
+ /** The value. */
+ int64_t m_iValue;
+};
+
+
+/**
+ * Class wrapping 'int32_t'.
+ */
+class RT_DECL_CLASS RTCRestInt32 : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestInt32() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestInt32(RTCRestInt32 const &a_rThat) RT_NOEXCEPT;
+ /** From value constructor. */
+ RTCRestInt32(int32_t iValue) RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestInt32() RT_NOEXCEPT;
+ /** Copy assignment operator. */
+ RTCRestInt32 &operator=(RTCRestInt32 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestInt32 const &a_rThat) RT_NOEXCEPT;
+ /** Assign value and clear null indicator. */
+ void assignValue(int32_t a_iValue) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestInt32 *clone() const { return (RTCRestInt32 *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+public:
+ /** The value. */
+ int32_t m_iValue;
+};
+
+
+/**
+ * Class wrapping 'int16_t'.
+ */
+class RT_DECL_CLASS RTCRestInt16 : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestInt16() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestInt16(RTCRestInt16 const &a_rThat) RT_NOEXCEPT;
+ /** From value constructor. */
+ RTCRestInt16(int16_t iValue) RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestInt16();
+ /** Copy assignment operator. */
+ RTCRestInt16 &operator=(RTCRestInt16 const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestInt16 const &a_rThat) RT_NOEXCEPT;
+ /** Assign value and clear null indicator. */
+ void assignValue(int16_t a_iValue) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestInt16 *clone() const RT_NOEXCEPT { return (RTCRestInt16 *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+public:
+ /** The value. */
+ int16_t m_iValue;
+};
+
+
+/**
+ * Class wrapping 'double'.
+ */
+class RT_DECL_CLASS RTCRestDouble : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestDouble() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestDouble(RTCRestDouble const &a_rThat) RT_NOEXCEPT;
+ /** From value constructor. */
+ RTCRestDouble(double rdValue) RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestDouble();
+ /** Copy assignment operator. */
+ RTCRestDouble &operator=(RTCRestDouble const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestDouble const &a_rThat) RT_NOEXCEPT;
+ /** Assign value and clear null indicator. */
+ void assignValue(double a_rdValue) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestDouble *clone() const RT_NOEXCEPT { return (RTCRestDouble *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+public:
+ /** The value. */
+ double m_rdValue;
+};
+
+
+/**
+ * Class wrapping 'RTCString'.
+ */
+class RT_DECL_CLASS RTCRestString : public RTCRestObjectBase, public RTCString
+{
+public:
+ /** Default constructor. */
+ RTCRestString() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestString();
+
+ /** Copy constructor. */
+ RTCRestString(RTCRestString const &a_rThat);
+ /** From value constructor. */
+ RTCRestString(RTCString const &a_rThat);
+ /** From value constructor. */
+ RTCRestString(const char *a_pszSrc);
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestString const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(RTCString const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ int assignCopy(const char *a_pszThat) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestString *clone() const RT_NOEXCEPT { return (RTCRestString *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int setNull(void) RT_NOEXCEPT RT_OVERRIDE; /* (ambigious, so overrider it to make sure.) */
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+ /** @name RTCString assignment methods we need to replace to manage the null indicator
+ * @{ */
+ int assignNoThrow(const RTCString &a_rSrc) RT_NOEXCEPT;
+ int assignNoThrow(const char *a_pszSrc) RT_NOEXCEPT;
+ int assignNoThrow(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc = npos) RT_NOEXCEPT;
+ int assignNoThrow(const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT;
+ int assignNoThrow(size_t a_cTimes, char a_ch) RT_NOEXCEPT;
+ int printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 2);
+ int printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 0);
+ RTCRestString &operator=(const char *a_pcsz);
+ RTCRestString &operator=(const RTCString &a_rThat);
+ RTCRestString &operator=(const RTCRestString &a_rThat);
+ RTCRestString &assign(const RTCString &a_rSrc);
+ RTCRestString &assign(const char *a_pszSrc);
+ RTCRestString &assign(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc = npos);
+ RTCRestString &assign(const char *a_pszSrc, size_t a_cchSrc);
+ RTCRestString &assign(size_t a_cTimes, char a_ch);
+ RTCRestString &printf(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+ RTCRestString &printfV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+ /** @} */
+};
+
+
+/**
+ * Date class.
+ *
+ * There are numerous ways of formatting a timestamp and the specifications
+ * we're currently working with doesn't have a way of telling it seems.
+ * Thus, decoding need to have fail safes built in so the user can give hints.
+ * The formatting likewise needs to be told which format to use by the user.
+ *
+ * Two side-effects of the format stuff is that the default constructor creates
+ * an object that is null, and resetToDefault will do the same bug leave the
+ * format as a hint.
+ */
+class RT_DECL_CLASS RTCRestDate : public RTCRestObjectBase
+{
+public:
+ /** Default constructor.
+ * @note The result is a null-object. */
+ RTCRestDate() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestDate(RTCRestDate const &a_rThat);
+ /** Destructor. */
+ virtual ~RTCRestDate();
+ /** Copy assignment operator. */
+ RTCRestDate &operator=(RTCRestDate const &a_rThat);
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestDate const &a_rThat) RT_NOEXCEPT;
+ /** Make a clone of this object. */
+ inline RTCRestDate *clone() const RT_NOEXCEPT{ return (RTCRestDate *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = 0) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+ /** Date formats. */
+ typedef enum
+ {
+ kFormat_Invalid = 0,
+ kFormat_Rfc2822, /**< Format it according to RFC-2822. */
+ kFormat_Rfc7131, /**< Format it according to RFC-7131 (HTTP). */
+ kFormat_Rfc3339, /**< Format it according to RFC-3339 (ISO-8601) (no fraction). */
+ kFormat_Rfc3339_Fraction_2, /**< Format it according to RFC-3339 (ISO-8601) with two digit fraction (hundreths). */
+ kFormat_Rfc3339_Fraction_3, /**< Format it according to RFC-3339 (ISO-8601) with three digit fraction (milliseconds). */
+ kFormat_Rfc3339_Fraction_6, /**< Format it according to RFC-3339 (ISO-8601) with six digit fraction (microseconds). */
+ kFormat_Rfc3339_Fraction_9, /**< Format it according to RFC-3339 (ISO-8601) with nine digit fraction (nanoseconds). */
+ kFormat_End
+ } kFormat;
+
+ /**
+ * Assigns the value, formats it as a string and clears the null indicator.
+ *
+ * @returns VINF_SUCCESS, VERR_NO_STR_MEMORY or VERR_INVALID_PARAMETER.
+ * @param a_pTimeSpec The time spec to set.
+ * @param a_enmFormat The date format to use when formatting it.
+ */
+ int assignValue(PCRTTIMESPEC a_pTimeSpec, kFormat a_enmFormat) RT_NOEXCEPT;
+ int assignValueRfc2822(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT; /**< Convenience method for email/whatnot. */
+ int assignValueRfc7131(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT; /**< Convenience method for HTTP date. */
+ int assignValueRfc3339(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT; /**< Convenience method for ISO-8601 timstamp. */
+
+ /**
+ * Assigns the current UTC time and clears the null indicator .
+ *
+ * @returns VINF_SUCCESS, VERR_NO_STR_MEMORY or VERR_INVALID_PARAMETER.
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param a_enmFormat The date format to use when formatting it.
+ */
+ int assignNow(kFormat a_enmFormat) RT_NOEXCEPT;
+ int assignNowRfc2822() RT_NOEXCEPT; /**< Convenience method for email/whatnot. */
+ int assignNowRfc7131() RT_NOEXCEPT; /**< Convenience method for HTTP date. */
+ int assignNowRfc3339() RT_NOEXCEPT; /**< Convenience method for ISO-8601 timstamp. */
+
+ /**
+ * Sets the format to help deal with decoding issues.
+ *
+ * This can also be used to change the date format for an okay timespec.
+ * @returns IPRT status code.
+ * @param a_enmFormat The date format to try/set.
+ */
+ int setFormat(kFormat a_enmFormat) RT_NOEXCEPT;
+
+ /** Check if the value is okay (m_TimeSpec & m_Exploded). */
+ inline bool isOkay() const RT_NOEXCEPT { return m_fTimeSpecOkay; }
+ /** Get the timespec value. */
+ inline RTTIMESPEC const &getTimeSpec() const RT_NOEXCEPT { return m_TimeSpec; }
+ /** Get the exploded time. */
+ inline RTTIME const &getExploded() const RT_NOEXCEPT { return m_Exploded; }
+ /** Gets the format. */
+ inline kFormat getFormat() const RT_NOEXCEPT { return m_enmFormat; }
+ /** Get the formatted/raw string value. */
+ inline RTCString const &getString() const RT_NOEXCEPT { return m_strFormatted; }
+
+ /** Get nanoseconds since unix epoch. */
+ inline int64_t getEpochNano() const RT_NOEXCEPT { return RTTimeSpecGetNano(&m_TimeSpec); }
+ /** Get seconds since unix epoch. */
+ inline int64_t getEpochSeconds() const RT_NOEXCEPT { return RTTimeSpecGetSeconds(&m_TimeSpec); }
+ /** Checks if UTC time. */
+ inline bool isUtc() const RT_NOEXCEPT { return (m_Exploded.fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_LOCAL; }
+ /** Checks if local time. */
+ inline bool isLocal() const RT_NOEXCEPT { return (m_Exploded.fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL; }
+
+protected:
+ /** The value. */
+ RTTIMESPEC m_TimeSpec;
+ /** The exploded time value. */
+ RTTIME m_Exploded;
+ /** Set if m_TimeSpec is okay, consult m_strFormatted if not. */
+ bool m_fTimeSpecOkay;
+ /** The format / format hint. */
+ kFormat m_enmFormat;
+ /** The formatted date string.
+ * This will be the raw input string for a deserialized value, where as for
+ * a value set by the user it will be the formatted value. */
+ RTCString m_strFormatted;
+
+ /**
+ * Explodes and formats the m_TimeSpec value.
+ *
+ * Sets m_Exploded, m_strFormatted, m_fTimeSpecOkay, and m_enmFormat, clears m_fNullIndicator.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param a_enmFormat The format to use.
+ */
+ int explodeAndFormat(kFormat a_enmFormat) RT_NOEXCEPT;
+
+ /**
+ * Formats the m_Exploded value.
+ *
+ * Sets m_strFormatted, m_fTimeSpecOkay, and m_enmFormat, clears m_fNullIndicator.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param a_enmFormat The format to use.
+ */
+ int format(kFormat a_enmFormat) RT_NOEXCEPT;
+
+ /**
+ * Internal worker that attempts to decode m_strFormatted.
+ *
+ * Sets m_fTimeSpecOkay.
+ *
+ * @returns IPRT status code.
+ * @param enmFormat Specific format to try, kFormat_Invalid (default) to try guess it.
+ */
+ int decodeFormattedString(kFormat enmFormat = kFormat_Invalid) RT_NOEXCEPT;
+};
+
+
+/** We should provide a proper UUID class eventually. Currently it is not used. */
+typedef RTCRestString RTCRestUuid;
+
+
+/**
+ * String enum base class.
+ */
+class RT_DECL_CLASS RTCRestStringEnumBase : public RTCRestObjectBase
+{
+public:
+ /** Enum map entry. */
+ typedef struct ENUMMAPENTRY
+ {
+ const char *pszName;
+ uint32_t cchName;
+ int32_t iValue;
+ } ENUMMAPENTRY;
+
+ /** Default constructor. */
+ RTCRestStringEnumBase() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestStringEnumBase();
+
+ /** Copy constructor. */
+ RTCRestStringEnumBase(RTCRestStringEnumBase const &a_rThat);
+ /** Copy assignment operator. */
+ RTCRestStringEnumBase &operator=(RTCRestStringEnumBase const &a_rThat);
+
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestStringEnumBase const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ inline int assignCopy(RTCString const &a_rThat) RT_NOEXCEPT { return setByString(a_rThat); }
+ /** Safe copy assignment method. */
+ inline int assignCopy(const char *a_pszThat) RT_NOEXCEPT { return setByString(a_pszThat); }
+
+ /* Overridden methods: */
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Sets the value given a C-string value.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VWRN_NOT_FOUND if not mappable to enum value.
+ * @retval VERR_NO_STR_MEMORY if not mappable and we're out of memory.
+ * @param a_pszValue The string value.
+ * @param a_cchValue The string value length. Optional.
+ */
+ int setByString(const char *a_pszValue, size_t a_cchValue = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Sets the value given a string value.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VWRN_NOT_FOUND if not mappable to enum value.
+ * @retval VERR_NO_STR_MEMORY if not mappable and we're out of memory.
+ * @param a_rValue The string value.
+ */
+ int setByString(RTCString const &a_rValue) RT_NOEXCEPT;
+
+ /**
+ * Gets the string value.
+ */
+ const char *getString() const RT_NOEXCEPT;
+
+ /** Maps the given string value to an enum. */
+ int stringToEnum(const char *a_pszValue, size_t a_cchValue = RTSTR_MAX) RT_NOEXCEPT;
+ /** Maps the given string value to an enum. */
+ int stringToEnum(RTCString const &a_rStrValue) RT_NOEXCEPT;
+ /** Maps the given string value to an enum. */
+ const char *enumToString(int a_iEnumValue, size_t *a_pcchString) RT_NOEXCEPT;
+
+
+protected:
+ /** The enum value. */
+ int m_iEnumValue;
+ /** The string value if not a match. */
+ RTCString m_strValue;
+
+ /**
+ * Worker for setting the object to the given enum value.
+ *
+ * @retval true on success.
+ * @retval false if a_iEnumValue can't be translated.
+ * @param a_iEnumValue The enum value to set.
+ */
+ bool setWorker(int a_iEnumValue) RT_NOEXCEPT;
+
+ /** Helper for implementing RTCRestObjectBase::clone(). */
+ RTCRestObjectBase *cloneWorker(RTCRestStringEnumBase *a_pDst) const RT_NOEXCEPT;
+
+ /**
+ * Gets the mapping table.
+ *
+ * @returns Pointer to the translation table.
+ * @param pcEntries Where to return the translation table size.
+ */
+ virtual ENUMMAPENTRY const *getMappingTable(size_t *pcEntries) const RT_NOEXCEPT = 0;
+};
+
+
+/**
+ * String enum template class.
+ *
+ * Takes the enum type as argument.
+ */
+template <typename EnumType>
+class RTCRestStringEnum : public RTCRestStringEnumBase
+{
+public:
+ typedef EnumType Type; /**< The enum type. */
+
+ /** Default constructor */
+ RTCRestStringEnum() RT_NOEXCEPT : RTCRestStringEnumBase() { }
+ /** Constructor with initial enum value. */
+ RTCRestStringEnum(Type a_enmValue) RT_NOEXCEPT : RTCRestStringEnumBase() { set(a_enmValue); }
+ /** Constructor with string default. */
+ RTCRestStringEnum(const char *a_pszDefault) : RTCRestStringEnumBase() { setByString(a_pszDefault); }
+ /** Copy constructor */
+ RTCRestStringEnum(RTCRestStringEnum const &a_rThat) : RTCRestStringEnumBase(a_rThat) { }
+ /** Make a clone of this object. */
+ inline RTCRestStringEnum *clone() const RT_NOEXCEPT { return (RTCRestStringEnum *)baseClone(); }
+
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE
+ {
+ return cloneWorker(new (std::nothrow) RTCRestStringEnum());
+ }
+
+ /** Copy assignment operator. */
+ RTCRestStringEnum &operator=(RTCRestStringEnum const &a_rThat) RT_NOEXCEPT
+ {
+ RTCRestStringEnumBase::operator=(a_rThat);
+ return *this;
+ }
+
+ /**
+ * Gets the enum value.
+ * @returns enum value.
+ * @retval kXxxxInvalid means there was no mapping for the string, or that
+ * no value has been assigned yet.
+ */
+ Type get() const RT_NOEXCEPT { return (Type)m_iEnumValue; }
+
+ /**
+ * Sets the object value to @a a_enmType
+ *
+ * @returns true if a_enmType is valid, false if not.
+ * @param a_enmType The new value.
+ */
+ bool set(Type a_enmType) RT_NOEXCEPT { return setWorker((int)a_enmType); }
+
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE { return "RTCRestStringEnum<EnumType>"; }
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT
+ {
+ return new (std::nothrow) RTCRestStringEnum();
+ }
+
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+ {
+ *a_ppInstance = new (std::nothrow) RTCRestStringEnum();
+ if (*a_ppInstance)
+ return (*a_ppInstance)->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+ }
+
+protected:
+ /** Enum mapping table. */
+ static const ENUMMAPENTRY s_aMappingTable[];
+ /** Enum mapping table size. */
+ static const size_t s_cMappingTable;
+
+ virtual ENUMMAPENTRY const *getMappingTable(size_t *pcEntries) const RT_NOEXCEPT RT_OVERRIDE
+ {
+ *pcEntries = s_cMappingTable;
+ return s_aMappingTable;
+ }
+};
+
+
+/**
+ * Class for handling binary blobs (strings).
+ *
+ * There are specializations of this class for body parameters and responses,
+ * see RTCRestBinaryParameter and RTCRestBinaryResponse.
+ */
+class RT_DECL_CLASS RTCRestBinary : public RTCRestObjectBase
+{
+public:
+ /** Default constructor. */
+ RTCRestBinary() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestBinary();
+
+ /** Safe copy assignment method. */
+ virtual int assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT;
+ /** Safe buffer copy method. */
+ virtual int assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT;
+
+ /** Use the specified data buffer directly. */
+ virtual int assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT;
+ /** Use the specified data buffer directly. */
+ virtual int assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT;
+ /** Frees the data held by the object and resets it default state. */
+ virtual void freeData() RT_NOEXCEPT;
+
+ /** Returns a pointer to the data blob. */
+ inline const uint8_t *getPtr() const RT_NOEXCEPT { return m_pbData; }
+ /** Gets the size of the data. */
+ inline size_t getSize() const RT_NOEXCEPT { return m_cbData; }
+
+ /** Make a clone of this object. */
+ inline RTCRestBinary *clone() const RT_NOEXCEPT { return (RTCRestBinary *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int setNull(void) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault(void) RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT;
+
+protected:
+ /** Pointer to data blob. */
+ uint8_t *m_pbData;
+ /** Amount of valid data in the blob. */
+ size_t m_cbData;
+ /** Number of bytes allocated for the m_pbData buffer. */
+ size_t m_cbAllocated;
+ /** Set if the data is freeable, only ever clear if user data. */
+ bool m_fFreeable;
+ /** Set if the data blob is readonly user provided data. */
+ bool m_fReadOnly;
+
+private:
+ /* No copy constructor or copy assignment: */
+ RTCRestBinary(RTCRestBinary const &a_rThat);
+ RTCRestBinary &operator=(RTCRestBinary const &a_rThat);
+};
+
+
+/**
+ * Abstract base class for REST data model classes.
+ */
+class RT_DECL_CLASS RTCRestDataObject : public RTCRestObjectBase
+{
+public:
+ RTCRestDataObject() RT_NOEXCEPT;
+ RTCRestDataObject(RTCRestDataObject const &a_rThat) RT_NOEXCEPT;
+ virtual ~RTCRestDataObject();
+
+ /* Overridden methods:*/
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Serialize the object members as JSON.
+ *
+ * @returns a_rDst
+ * @param a_rDst The destination for the serialization.
+ */
+ virtual RTCRestOutputBase &serializeMembersAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT;
+
+ /**
+ * Deserialize object from the given JSON iterator.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if field is unknown. Top level caller will do
+ * invoke unknownField() on it.
+ *
+ * @param a_rCursor The JSON cursor with the current member.
+ * @param a_cchName The length of a_rCursor.m_pszName.
+ */
+ virtual int deserializeMemberFromJson(RTCRestJsonCursor const &a_rCursor, size_t a_cchName) RT_NOEXCEPT;
+
+protected:
+ /** The is-set bits for all the fields. */
+ uint64_t m_fIsSet;
+
+ /** Copy assignment operator. */
+ RTCRestDataObject &operator=(RTCRestDataObject const &a_rThat) RT_NOEXCEPT;
+
+ /** Safe copy assignment method. */
+ virtual int assignCopy(RTCRestDataObject const &a_rThat) RT_NOEXCEPT;
+};
+
+
+/**
+ * Abstract base class for polymorphic REST data model classes.
+ */
+class RT_DECL_CLASS RTCRestPolyDataObject : public RTCRestDataObject
+{
+public:
+ RTCRestPolyDataObject() RT_NOEXCEPT;
+ RTCRestPolyDataObject(RTCRestPolyDataObject const &a_rThat) RT_NOEXCEPT;
+ virtual ~RTCRestPolyDataObject();
+
+ /* Overridden methods:*/
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Checks if the instance is of a child class (@c true) or of the parent (@c false). */
+ virtual bool isChild() const RT_NOEXCEPT;
+
+protected:
+
+ /** Copy assignment operator. */
+ RTCRestPolyDataObject &operator=(RTCRestPolyDataObject const &a_rThat) RT_NOEXCEPT;
+};
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_restbase_h */
+
diff --git a/include/iprt/cpp/restclient.h b/include/iprt/cpp/restclient.h
new file mode 100644
index 00000000..b62a89ea
--- /dev/null
+++ b/include/iprt/cpp/restclient.h
@@ -0,0 +1,826 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) Client Classes.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_restclient_h
+#define IPRT_INCLUDED_cpp_restclient_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/http.h>
+#include <iprt/cpp/restbase.h>
+#include <iprt/cpp/reststringmap.h>
+
+
+/** @defgroup grp_rt_cpp_restclient C++ Representational State Transfer (REST) Client Classes.
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Specialization of RTCRestBinary for use with body parameters in a client.
+ *
+ * This enables registering data callbacks for provinding data to upload.
+ */
+class RT_DECL_CLASS RTCRestBinaryParameter : public RTCRestBinary
+{
+public:
+ /** Default constructor. */
+ RTCRestBinaryParameter() RT_NOEXCEPT;
+
+ /** Safe copy assignment method. */
+ virtual int assignCopy(RTCRestBinaryParameter const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method.
+ * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
+ virtual int assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT RT_OVERRIDE;
+ /** Safe copy assignment method.
+ * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
+ virtual int assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Use the specified data buffer directly.
+ * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
+ virtual int assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
+ /**
+ * Use the specified data buffer directly.
+ * @note This will assert and work like assignReadOnly. */
+ virtual int assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Make a clone of this object. */
+ inline RTCRestBinaryParameter *clone() const RT_NOEXCEPT { return (RTCRestBinaryParameter *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+
+ /**
+ * Retrieves the callback data.
+ */
+ inline void *getCallbackData() const RT_NOEXCEPT { return m_pvCallbackData; }
+
+ /**
+ * Sets the content-type for an upload.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param a_pszContentType The content type to set.
+ * If NULL, no content type is set.
+ */
+ int setContentType(const char *a_pszContentType) RT_NOEXCEPT;
+
+ /**
+ * Gets the content type that was set.
+ */
+ inline RTCString const &getContentType() const RT_NOEXCEPT { return m_strContentType; }
+
+ /**
+ * Gets the content-length value (UINT64_MAX if not available).
+ */
+ inline uint64_t getContentLength() const RT_NOEXCEPT { return m_cbContentLength; }
+
+ /**
+ * Callback for producing bytes to upload.
+ *
+ * @returns IPRT status code.
+ * @param a_pThis The related string object.
+ * @param a_pvDst Where to put the bytes.
+ * @param a_cbDst Max number of bytes to produce.
+ * @param a_offContent The byte offset corresponding to the start of @a a_pvDst.
+ * @param a_pcbActual Where to return the number of bytes actually produced.
+ *
+ * @remarks Use getCallbackData to get the user data.
+ *
+ * @note The @a a_offContent parameter does not imply random access or anthing
+ * like that, it is just a convenience provided by the caller. The value
+ * is the sum of the previously returned @a *pcbActual values.
+ */
+ typedef DECLCALLBACKTYPE(int, FNPRODUCER,(RTCRestBinaryParameter *a_pThis, void *a_pvDst, size_t a_cbDst,
+ uint64_t a_offContent, size_t *a_pcbActual)) /*RT_NOEXCEPT*/;
+ /** Pointer to a byte producer callback. */
+ typedef FNPRODUCER *PFNPRODUCER;
+
+ /**
+ * Sets the producer callback.
+ *
+ * @param a_pfnProducer The callback function for producing data.
+ * @param a_pvCallbackData Data the can be retrieved from the callback
+ * using getCallbackData().
+ * @param a_cbContentLength The amount of data that will be uploaded and
+ * to be set as the value of the content-length
+ * header field. Pass UINT64_MAX if not known.
+ *
+ * @note This will drop any buffer previously registered using setUploadData().
+ */
+ void setProducerCallback(PFNPRODUCER a_pfnProducer, void *a_pvCallbackData = NULL, uint64_t a_cbContentLength = UINT64_MAX) RT_NOEXCEPT;
+
+ /**
+ * Preprares transmission via the @a a_hHttp client instance.
+ *
+ * @returns IPRT status code.
+ * @param a_hHttp The HTTP client instance.
+ * @internal
+ */
+ virtual int xmitPrepare(RTHTTP a_hHttp) const RT_NOEXCEPT;
+
+ /**
+ * For completing and/or undoing setup from xmitPrepare.
+ *
+ * @param a_hHttp The HTTP client instance.
+ * @internal
+ */
+ virtual void xmitComplete(RTHTTP a_hHttp) const RT_NOEXCEPT;
+
+protected:
+ /** Number of bytes corresponding to content-length.
+ * UINT64_MAX if not known. Used both for unploads and downloads. */
+ uint64_t m_cbContentLength;
+ /** The content type if set (upload only). */
+ RTCString m_strContentType;
+ /** Pointer to user-registered producer callback function (upload only). */
+ PFNPRODUCER m_pfnProducer;
+ /** User argument for both callbacks (both). */
+ void *m_pvCallbackData;
+
+ /** @copydoc FNRTHTTPUPLOADCALLBACK */
+ static DECLCALLBACK(int) xmitHttpCallback(RTHTTP hHttp, void *pvBuf, size_t cbBuf, uint64_t offContent,
+ size_t *pcbActual, void *pvUser) RT_NOEXCEPT;
+
+private:
+ /* No copy constructor or copy assignment: */
+ RTCRestBinaryParameter(RTCRestBinaryParameter const &a_rThat);
+ RTCRestBinaryParameter &operator=(RTCRestBinaryParameter const &a_rThat);
+};
+
+
+/**
+ * Specialization of RTCRestBinary for use with responses in a client.
+ *
+ * This enables registering data callbacks for consuming downloaded data.
+ */
+class RT_DECL_CLASS RTCRestBinaryResponse : public RTCRestBinary
+{
+public:
+ /** Default constructor. */
+ RTCRestBinaryResponse() RT_NOEXCEPT;
+
+ /** Safe copy assignment method. */
+ virtual int assignCopy(RTCRestBinaryResponse const &a_rThat) RT_NOEXCEPT;
+ /** Safe copy assignment method. */
+ virtual int assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT RT_OVERRIDE;
+ /** Safe copy assignment method.
+ * @note This will assert and fail as it makes no sense for a download. */
+ virtual int assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Use the specified data buffer directly.
+ * @note This will assert and fail as it makes no sense for a download.
+ */
+ virtual int assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
+ /**
+ * Use the specified data buffer directly.
+ * @note This will drop any previously registered producer callback and user data.
+ */
+ virtual int assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Make a clone of this object. */
+ inline RTCRestBinaryResponse *clone() const RT_NOEXCEPT { return (RTCRestBinaryResponse *)baseClone(); }
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;
+
+ /**
+ * Retrieves the callback data.
+ */
+ inline void *getCallbackData() const RT_NOEXCEPT { return m_pvCallbackData; }
+
+ /**
+ * Sets the max size to download to memory.
+ *
+ * This also indicates the intention to download to a memory buffer, so it
+ * will drop any previously registered consumer callback and its user data.
+ *
+ * @param a_cbMaxDownload Maximum number of bytes to download to memory.
+ * If 0, a default is selected (currently 32MiB for
+ * 32-bit hosts and 128MiB for 64-bit).
+ */
+ void setMaxDownloadSize(size_t a_cbMaxDownload) RT_NOEXCEPT;
+
+ /**
+ * Gets the content-length value (UINT64_MAX if not available).
+ */
+ inline uint64_t getContentLength() const RT_NOEXCEPT { return m_cbContentLength; }
+
+ /**
+ * Callback for consuming downloaded bytes.
+ *
+ * @returns IPRT status code.
+ * @param a_pThis The related string object.
+ * @param a_pvSrc Buffer containing the bytes.
+ * @param a_cbSrc The number of bytes in the buffer.
+ * @param a_uHttpStatus The HTTP status code.
+ * @param a_offContent The byte offset corresponding to the start of @a a_pvSrc.
+ * @param a_cbContent The content length field value, UINT64_MAX if not available.
+ *
+ * @remarks Use getCallbackData to get the user data.
+ *
+ * @note The @a a_offContent parameter does not imply random access or anthing
+ * like that, it is just a convenience provided by the caller. The value
+ * is the sum of the previous @a a_cbSrc values.
+ */
+ typedef DECLCALLBACKTYPE(int, FNCONSUMER,(RTCRestBinaryResponse *a_pThis, const void *a_pvSrc, size_t a_cbSrc,
+ uint32_t a_uHttpStatus, uint64_t a_offContent, uint64_t a_cbContent)) /*RT_NOEXCEPT*/;
+ /** Pointer to a byte consumer callback. */
+ typedef FNCONSUMER *PFNCONSUMER;
+
+ /**
+ * Sets the consumer callback.
+ *
+ * @param a_pfnConsumer The callback function for consuming downloaded data.
+ * NULL if data should be stored in m_pbData (the default).
+ * @param a_pvCallbackData Data the can be retrieved from the callback
+ * using getCallbackData().
+ */
+ void setConsumerCallback(PFNCONSUMER a_pfnConsumer, void *a_pvCallbackData = NULL) RT_NOEXCEPT;
+
+ /**
+ * Preprares for receiving via the @a a_hHttp client instance.
+ *
+ * @returns IPRT status code.
+ * @param a_hHttp The HTTP client instance.
+ * @param a_fCallbackFlags The HTTP callback flags (status code spec).
+ * @internal
+ */
+ virtual int receivePrepare(RTHTTP a_hHttp, uint32_t a_fCallbackFlags) RT_NOEXCEPT;
+
+ /**
+ * For completing and/or undoing setup from receivePrepare.
+ *
+ * @param a_hHttp The HTTP client instance.
+ * @internal
+ */
+ virtual void receiveComplete(RTHTTP a_hHttp) RT_NOEXCEPT;
+
+protected:
+ /** Number of bytes corresponding to content-length.
+ * UINT64_MAX if not known. Used both for unploads and downloads. */
+ uint64_t m_cbContentLength;
+ /** Number of bytes downloaded thus far. */
+ uint64_t m_cbDownloaded;
+ /** Pointer to user-registered consumer callback function (download only). */
+ PFNCONSUMER m_pfnConsumer;
+ /** User argument for both callbacks (both). */
+ void *m_pvCallbackData;
+ /** Maximum data to download to memory (download only). */
+ size_t m_cbMaxDownload;
+
+ /** @copydoc FNRTHTTPDOWNLOADCALLBACK */
+ static DECLCALLBACK(int) receiveHttpCallback(RTHTTP hHttp, void const *pvBuf, size_t cbBuf, uint32_t uHttpStatus,
+ uint64_t offContent, uint64_t cbContent, void *pvUser) RT_NOEXCEPT;
+
+private:
+ /* No copy constructor or copy assignment: */
+ RTCRestBinaryResponse(RTCRestBinaryResponse const &a_rThat);
+ RTCRestBinaryResponse &operator=(RTCRestBinaryResponse const &a_rThat);
+};
+
+
+/**
+ * Base class for REST client requests.
+ *
+ * This encapsulates parameters and helps transform them into a HTTP request.
+ *
+ * Parameters can be transfered in a number of places:
+ * - Path part of the URL.
+ * - Query part of the URL.
+ * - HTTP header fields.
+ * - FORM body.
+ * - JSON body.
+ * - XML body.
+ * - ...
+ *
+ * They can be require or optional. The latter may have default values. In
+ * swagger 3 they can also be nullable, which means the null-indicator cannot
+ * be used for tracking optional parameters.
+ */
+class RT_DECL_CLASS RTCRestClientRequestBase
+{
+public:
+ RTCRestClientRequestBase() RT_NOEXCEPT;
+ virtual ~RTCRestClientRequestBase();
+ RTCRestClientRequestBase(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT;
+ RTCRestClientRequestBase &operator=(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT;
+
+ /**
+ * Reset all members to default values.
+ * @returns IPRT status code.
+ */
+ virtual int resetToDefault() RT_NOEXCEPT = 0;
+
+ /**
+ * Getter for the operation name. Provided by the generated
+ * subclasses so that base class code may use it for more
+ * informative logs.
+ */
+ virtual const char *getOperationName() const RT_NOEXCEPT = 0;
+
+ /**
+ * Prepares the HTTP handle for transmitting this request.
+ *
+ * @returns IPRT status code.
+ * @param a_pStrPath Where to set path parameters. Will be appended to the base path.
+ * @param a_pStrQuery Where to set query parameters.
+ * @param a_hHttp Where to set header parameters and such.
+ * @param a_pStrBody Where to set body parameters.
+ */
+ virtual int xmitPrepare(RTCString *a_pStrPath, RTCString *a_pStrQuery, RTHTTP a_hHttp, RTCString *a_pStrBody) const RT_NOEXCEPT = 0;
+
+ /**
+ * Always called after the request has been transmitted.
+ *
+ * @param a_rcStatus Negative numbers are IPRT errors, positive are HTTP status codes.
+ * @param a_hHttp The HTTP handle the request was performed on.
+ */
+ virtual void xmitComplete(int a_rcStatus, RTHTTP a_hHttp) const RT_NOEXCEPT = 0;
+
+ /**
+ * Checks if there are were any assignment errors.
+ */
+ inline bool hasAssignmentErrors() const RT_NOEXCEPT { return m_fErrorSet != 0; }
+
+protected:
+ /** Set of fields that have been explicitly assigned a value. */
+ uint64_t m_fIsSet;
+ /** Set of fields where value assigning failed. */
+ uint64_t m_fErrorSet;
+
+ /** Path parameter descriptor. */
+ typedef struct
+ {
+ const char *pszName; /**< The name string to replace (including {}). */
+ size_t cchName; /**< Length of pszName. */
+ uint32_t fFlags; /**< The toString flags. */
+ uint8_t iBitNo; /**< The parameter bit number. */
+ } PATHPARAMDESC;
+
+ /** Path parameter state. */
+ typedef struct
+ {
+ RTCRestObjectBase const *pObj; /**< Pointer to the parameter object. */
+ size_t offName; /**< Maintained by worker. */
+ } PATHPARAMSTATE;
+
+ /**
+ * Do path parameters.
+ *
+ * @returns IPRT status code
+ * @param a_pStrPath The destination path.
+ * @param a_pszPathTemplate The path template string.
+ * @param a_cchPathTemplate The length of the path template string.
+ * @param a_paPathParams The path parameter descriptors (static).
+ * @param a_paPathParamStates The path parameter objects and states.
+ * @param a_cPathParams Number of path parameters.
+ */
+ int doPathParameters(RTCString *a_pStrPath, const char *a_pszPathTemplate, size_t a_cchPathTemplate,
+ PATHPARAMDESC const *a_paPathParams, PATHPARAMSTATE *a_paPathParamStates, size_t a_cPathParams) const RT_NOEXCEPT;
+
+ /** Query parameter descriptor. */
+ typedef struct
+ {
+ const char *pszName; /**< The parameter name. */
+ uint32_t fFlags; /**< The toString flags. */
+ bool fRequired; /**< Required or not. */
+ uint8_t iBitNo; /**< The parameter bit number. */
+ } QUERYPARAMDESC;
+
+ /**
+ * Do query parameters.
+ *
+ * @returns IPRT status code
+ * @param a_pStrQuery The destination string.
+ * @param a_paQueryParams The query parameter descriptors.
+ * @param a_papQueryParamObjs The query parameter objects, parallel to @a a_paQueryParams.
+ * @param a_cQueryParams Number of query parameters.
+ */
+ int doQueryParameters(RTCString *a_pStrQuery, QUERYPARAMDESC const *a_paQueryParams,
+ RTCRestObjectBase const **a_papQueryParamObjs, size_t a_cQueryParams) const RT_NOEXCEPT;
+
+ /** Header parameter descriptor. */
+ typedef struct
+ {
+ const char *pszName; /**< The parameter name. */
+ uint32_t fFlags; /**< The toString flags. */
+ bool fRequired; /**< Required or not. */
+ uint8_t iBitNo; /**< The parameter bit number. */
+ bool fMapCollection; /**< Collect headers starting with pszName into a map. */
+ } HEADERPARAMDESC;
+
+ /**
+ * Do header parameters.
+ *
+ * @returns IPRT status code
+ * @param a_hHttp Where to set header parameters.
+ * @param a_paHeaderParams The header parameter descriptors.
+ * @param a_papHeaderParamObjs The header parameter objects, parallel to @a a_paHeaderParams.
+ * @param a_cHeaderParams Number of header parameters.
+ */
+ int doHeaderParameters(RTHTTP a_hHttp, HEADERPARAMDESC const *a_paHeaderParams,
+ RTCRestObjectBase const **a_papHeaderParamObjs, size_t a_cHeaderParams) const RT_NOEXCEPT;
+};
+
+
+/**
+ * Base class for REST client responses.
+ */
+class RT_DECL_CLASS RTCRestClientResponseBase
+{
+public:
+ /** Default constructor. */
+ RTCRestClientResponseBase() RT_NOEXCEPT;
+ /** Destructor. */
+ virtual ~RTCRestClientResponseBase();
+ /** Copy constructor. */
+ RTCRestClientResponseBase(RTCRestClientResponseBase const &a_rThat);
+ /** Copy assignment operator. */
+ RTCRestClientResponseBase &operator=(RTCRestClientResponseBase const &a_rThat);
+
+ /**
+ * Resets the object state.
+ */
+ virtual void reset(void) RT_NOEXCEPT;
+
+ /**
+ * Getter for the operation name. Provided by the generated
+ * subclasses so that base class code may use it for more
+ * informative logs.
+ */
+ virtual const char *getOperationName() const RT_NOEXCEPT = 0;
+
+ /**
+ * Prepares the HTTP handle for receiving the response.
+ *
+ * This may install callbacks and such like.
+ *
+ * When overridden, the parent class must always be called.
+ *
+ * @returns IPRT status code.
+ * @param a_hHttp The HTTP handle to prepare for receiving.
+ */
+ virtual int receivePrepare(RTHTTP a_hHttp) RT_NOEXCEPT;
+
+ /**
+ * Called when the HTTP request has been completely received.
+ *
+ * @param a_rcStatus Negative numbers are IPRT errors, positive are HTTP status codes.
+ * @param a_hHttp The HTTP handle the request was performed on.
+ * This can be NIL_RTHTTP should something fail early, in
+ * which case it is possible receivePrepare() wasn't called.
+ *
+ * @note Called before consumeBody() but after consumeHeader().
+ */
+ virtual void receiveComplete(int a_rcStatus, RTHTTP a_hHttp) RT_NOEXCEPT;
+
+ /**
+ * Callback that consumes HTTP body data from the server.
+ *
+ * @param a_pchData Body data.
+ * @param a_cbData Amount of body data.
+ *
+ * @note Called after consumeHeader().
+ */
+ virtual void consumeBody(const char *a_pchData, size_t a_cbData) RT_NOEXCEPT;
+
+ /**
+ * Called after status, headers and body all have been presented.
+ *
+ * @returns IPRT status code.
+ */
+ virtual void receiveFinal() RT_NOEXCEPT;
+
+ /**
+ * Getter for m_rcStatus.
+ * @returns Negative numbers are IPRT errors, positive are HTTP status codes.
+ */
+ inline int getStatus() const RT_NOEXCEPT { return m_rcStatus; }
+
+ /**
+ * Getter for m_rcHttp.
+ * @returns HTTP status code or VERR_NOT_AVAILABLE.
+ */
+ inline int getHttpStatus() const RT_NOEXCEPT { return m_rcHttp; }
+
+ /**
+ * Getter for m_pErrInfo.
+ */
+ inline PCRTERRINFO getErrInfo(void) const RT_NOEXCEPT { return m_pErrInfo; }
+
+ /**
+ * Getter for m_strContentType.
+ */
+ inline RTCString const &getContentType(void) const RT_NOEXCEPT { return m_strContentType; }
+
+
+protected:
+ /** Negative numbers are IPRT errors, positive are HTTP status codes. */
+ int m_rcStatus;
+ /** The HTTP status code, VERR_NOT_AVAILABLE if not set. */
+ int m_rcHttp;
+ /** Error information. */
+ PRTERRINFO m_pErrInfo;
+ /** The value of the Content-Type header field. */
+ RTCString m_strContentType;
+
+ PRTERRINFO getErrInfoInternal(void) RT_NOEXCEPT;
+ void deleteErrInfo(void) RT_NOEXCEPT;
+ void copyErrInfo(PCRTERRINFO pErrInfo) RT_NOEXCEPT;
+
+ /**
+ * Reports an error (or warning if a_rc non-negative).
+ *
+ * @returns a_rc
+ * @param a_rc The status code to report and return. The first
+ * error status is assigned to m_rcStatus, subsequent
+ * ones as well as informational statuses are not
+ * recorded by m_rcStatus.
+ * @param a_pszFormat The message format string.
+ * @param ... Message arguments.
+ */
+ int addError(int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT;
+
+ /**
+ * Deserializes a header field value.
+ *
+ * @returns IPRT status code.
+ * @param a_pObj The object to deserialize into.
+ * @param a_pchValue Pointer to the value (not zero terminated).
+ * Not necessarily valid UTF-8!
+ * @param a_cchValue The value length.
+ * @param a_fFlags Flags to pass to fromString().
+ * @param a_pszErrorTag The error tag (field name).
+ */
+ int deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue,
+ uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT;
+
+ /**
+ * Deserializes a header field value.
+ *
+ * @returns IPRT status code.
+ * @param a_pMap The string map object to deserialize into.
+ * @param a_pchField Pointer to the map field name. (Caller dropped the prefix.)
+ * Not necessarily valid UTF-8!
+ * @param a_cchField Length of field name.
+ * @param a_pchValue Pointer to the value (not zero terminated).
+ * Not necessarily valid UTF-8!
+ * @param a_cchValue The value length.
+ * @param a_fFlags Flags to pass to fromString().
+ * @param a_pszErrorTag The error tag (field name).
+ */
+ int deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField,
+ const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT;
+
+ /**
+ * Helper that does the deserializing of the response body
+ * via deserializeBodyFromJsonCursor().
+ *
+ * @param a_pchData The body blob.
+ * @param a_cbData The size of the body blob.
+ * @param a_pszBodyName The name of the body parameter.
+ */
+ void deserializeBody(const char *a_pchData, size_t a_cbData, const char *a_pszBodyName) RT_NOEXCEPT;
+
+ /**
+ * Called by deserializeBody to do the actual body deserialization.
+ *
+ * @param a_rCursor The JSON cursor.
+ */
+ virtual void deserializeBodyFromJsonCursor(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT;
+
+ /**
+ * Primary json cursor for parsing bodies.
+ */
+ class PrimaryJsonCursorForBody : public RTCRestJsonPrimaryCursor
+ {
+ public:
+ RTCRestClientResponseBase *m_pThat; /**< Pointer to response object. */
+ PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName, RTCRestClientResponseBase *a_pThat) RT_NOEXCEPT;
+ virtual int addError(RTCRestJsonCursor const &a_rCursor, int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT RT_OVERRIDE;
+ virtual int unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ };
+
+
+ /**
+ * Consumes a header.
+ *
+ * Child classes can override this to pick up their header fields, but must
+ * always call the parent class.
+ *
+ * @returns IPRT status code.
+ * @param a_uMatchWord Match word constructed by RTHTTP_MAKE_HDR_MATCH_WORD
+ * @param a_pchField The field name (not zero terminated).
+ * Not necessarily valid UTF-8!
+ * @param a_cchField The length of the field.
+ * @param a_pchValue The field value (not zero terminated).
+ * @param a_cchValue The length of the value.
+ */
+ virtual int consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField,
+ const char *a_pchValue, size_t a_cchValue) RT_NOEXCEPT;
+
+private:
+ /** Callback for use with RTHttpSetHeaderCallback. */
+ static DECLCALLBACK(int) receiveHttpHeaderCallback(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField,
+ const char *pchValue, size_t cchValue, void *pvUser) RT_NOEXCEPT;
+};
+
+
+/**
+ * Base class for REST client responses.
+ */
+class RT_DECL_CLASS RTCRestClientApiBase
+{
+public:
+ RTCRestClientApiBase() RT_NOEXCEPT;
+ virtual ~RTCRestClientApiBase();
+
+ /** @name Host and Base path (URL) handling.
+ * @{ */
+ /**
+ * Gets the server URL.
+ */
+ const char *getServerUrl(void) const RT_NOEXCEPT;
+
+ /**
+ * Sets the whole server URL.
+ * @returns IPRT status code.
+ * @param a_pszUrl The new server URL. NULL/empty to reset to default.
+ */
+ int setServerUrl(const char *a_pszUrl) RT_NOEXCEPT;
+
+ /**
+ * Sets the scheme part of the the server URL.
+ * @returns IPRT status code.
+ * @param a_pszScheme The new scheme. Does not accept NULL or empty string.
+ */
+ int setServerScheme(const char *a_pszScheme) RT_NOEXCEPT;
+
+ /**
+ * Sets the authority (hostname + port) part of the the server URL.
+ * @returns IPRT status code.
+ * @param a_pszAuthority The new authority. Does not accept NULL or empty string.
+ */
+ int setServerAuthority(const char *a_pszAuthority) RT_NOEXCEPT;
+
+ /**
+ * Sets the base path part of the the server URL.
+ * @returns IPRT status code.
+ * @param a_pszBasePath The new base path. Does not accept NULL or empty string.
+ */
+ int setServerBasePath(const char *a_pszBasePath) RT_NOEXCEPT;
+
+ /**
+ * Gets the default server URL as specified in the specs.
+ * @returns Server URL.
+ */
+ virtual const char *getDefaultServerUrl() const RT_NOEXCEPT = 0;
+
+ /**
+ * Gets the default server base path as specified in the specs.
+ * @returns Host string (start of URL).
+ */
+ virtual const char *getDefaultServerBasePath() const RT_NOEXCEPT = 0;
+ /** @} */
+
+ /**
+ * Sets the CA file to use for HTTPS.
+ */
+ int setCAFile(const char *pcszCAFile) RT_NOEXCEPT;
+ /** @overload */
+ int setCAFile(const RTCString &strCAFile) RT_NOEXCEPT;
+
+ /** Flags to doCall. */
+ enum
+ {
+ kDoCall_OciReqSignExcludeBody = 1, /**< Exclude the body when doing OCI request signing. */
+ kDoCall_RequireBody = 2 /**< The body is required. */
+ };
+
+protected:
+ /** Handle to the HTTP connection object. */
+ RTHTTP m_hHttp;
+ /** The server URL to use. If empty use the default. */
+ RTCString m_strServerUrl;
+ /** The CA file to use. If empty use the default. */
+ RTCString m_strCAFile;
+
+ /* Make non-copyable (RTCNonCopyable causes warnings): */
+ RTCRestClientApiBase(RTCRestClientApiBase const &);
+ RTCRestClientApiBase *operator=(RTCRestClientApiBase const &);
+
+ /**
+ * Re-initializes the HTTP instance.
+ *
+ * @returns IPRT status code.
+ */
+ virtual int reinitHttpInstance() RT_NOEXCEPT;
+
+ /**
+ * Hook that's called when doCall has fully assembled the request.
+ *
+ * Can be used for request signing and similar final steps.
+ *
+ * @returns IPRT status code.
+ * @param a_hHttp The HTTP client instance.
+ * @param a_rStrFullUrl The full URL.
+ * @param a_enmHttpMethod The HTTP request method.
+ * @param a_rStrXmitBody The body text.
+ * @param a_fFlags kDoCall_XXX.
+ */
+ virtual int xmitReady(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
+ RTCString const &a_rStrXmitBody, uint32_t a_fFlags) RT_NOEXCEPT;
+
+ /**
+ * Implements stuff for making an API call.
+ *
+ * @returns a_pResponse->getStatus()
+ * @param a_rRequest Reference to the request object.
+ * @param a_enmHttpMethod The HTTP request method.
+ * @param a_pResponse Pointer to the response object.
+ * @param a_pszMethod The method name, for logging purposes.
+ * @param a_fFlags kDoCall_XXX.
+ */
+ virtual int doCall(RTCRestClientRequestBase const &a_rRequest, RTHTTPMETHOD a_enmHttpMethod,
+ RTCRestClientResponseBase *a_pResponse, const char *a_pszMethod, uint32_t a_fFlags) RT_NOEXCEPT;
+
+ /**
+ * Implements OCI style request signing.
+ *
+ * @returns IPRT status code.
+ * @param a_hHttp The HTTP client instance.
+ * @param a_rStrFullUrl The full URL.
+ * @param a_enmHttpMethod The HTTP request method.
+ * @param a_rStrXmitBody The body text.
+ * @param a_fFlags kDoCall_XXX.
+ * @param a_hKey The key to use for signing.
+ * @param a_rStrKeyId The key ID.
+ *
+ * @remarks The signing scheme is covered by a series of drafts RFC, the latest being:
+ * https://tools.ietf.org/html/draft-cavage-http-signatures-10
+ */
+ int ociSignRequest(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
+ RTCString const &a_rStrXmitBody, uint32_t a_fFlags, RTCRKEY a_hKey, RTCString const &a_rStrKeyId) RT_NOEXCEPT;
+
+ /**
+ * Worker for the server URL modifiers.
+ *
+ * @returns IPRT status code.
+ * @param a_pszServerUrl The current server URL (for comparing).
+ * @param a_offDst The offset of the component in the current server URL.
+ * @param a_cchDst The current component length.
+ * @param a_pszSrc The new URL component value.
+ * @param a_cchSrc The length of the new component.
+ */
+ int setServerUrlPart(const char *a_pszServerUrl, size_t a_offDst, size_t a_cchDst, const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT;
+};
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_restclient_h */
+
diff --git a/include/iprt/cpp/restoutput.h b/include/iprt/cpp/restoutput.h
new file mode 100644
index 00000000..2f2c57d5
--- /dev/null
+++ b/include/iprt/cpp/restoutput.h
@@ -0,0 +1,280 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) Output Classes.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_restoutput_h
+#define IPRT_INCLUDED_cpp_restoutput_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/stdarg.h>
+#include <iprt/cpp/ministring.h>
+
+
+/** @defgroup grp_rt_cpp_restoutput C++ Representational State Transfer (REST) Output Classes.
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+
+/**
+ * Abstract base class for serializing data objects.
+ */
+class RT_DECL_CLASS RTCRestOutputBase
+{
+public:
+ RTCRestOutputBase() RT_NOEXCEPT;
+ virtual ~RTCRestOutputBase();
+
+ /**
+ * Raw output function.
+ *
+ * @returns Number of bytes outputted.
+ * @param a_pchString The string to output (not necessarily terminated).
+ * @param a_cchToWrite The length of the string
+ */
+ virtual size_t output(const char *a_pchString, size_t a_cchToWrite) RT_NOEXCEPT = 0;
+
+ /**
+ * RTStrPrintf like function (see @ref pg_rt_str_format).
+ *
+ * @returns Number of bytes outputted.
+ * @param pszFormat The format string.
+ * @param ... Argument specfied in @a pszFormat.
+ */
+ inline size_t printf(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(2, 3)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ size_t cchWritten = this->vprintf(pszFormat, va);
+ va_end(va);
+ return cchWritten;
+ }
+
+ /**
+ * RTStrPrintfV like function (see @ref pg_rt_str_format).
+ *
+ * @returns Number of bytes outputted.
+ * @param pszFormat The format string.
+ * @param va Argument specfied in @a pszFormat.
+ */
+ size_t vprintf(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(2, 0);
+
+ /**
+ * Begins an array.
+ * @returns Previous output state. Pass to endArray() when done.
+ */
+ virtual uint32_t beginArray() RT_NOEXCEPT;
+
+ /**
+ * Ends an array.
+ * @param a_uOldState Previous output state (returned by beginArray()).
+ */
+ virtual void endArray(uint32_t a_uOldState) RT_NOEXCEPT;
+
+ /**
+ * Begins an object.
+ * @returns Previous output state. Pass to endObject() when done.
+ */
+ virtual uint32_t beginObject() RT_NOEXCEPT;
+
+ /**
+ * Ends an array.
+ * @param a_uOldState Previous output state (returned by beginObject()).
+ */
+ virtual void endObject(uint32_t a_uOldState) RT_NOEXCEPT;
+
+ /**
+ * Outputs a value separator.
+ * This is called before a value, not after.
+ */
+ virtual void valueSeparator() RT_NOEXCEPT;
+
+ /**
+ * Outputs a value separator, name and name separator.
+ */
+ virtual void valueSeparatorAndName(const char *a_pszName, size_t a_cchName) RT_NOEXCEPT;
+
+ /** Outputs a null-value. */
+ void nullValue() RT_NOEXCEPT;
+
+protected:
+ /** The current indentation level (bits 15:0) and separator state (bit 31). */
+ uint32_t m_uState;
+
+ /** @callback_method_impl{FNRTSTROUTPUT} */
+ static DECLCALLBACK(size_t) printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT;
+};
+
+
+/**
+ * Abstract base class for pretty output.
+ */
+class RT_DECL_CLASS RTCRestOutputPrettyBase : public RTCRestOutputBase
+{
+public:
+ RTCRestOutputPrettyBase() RT_NOEXCEPT;
+ virtual ~RTCRestOutputPrettyBase();
+
+ /**
+ * Begins an array.
+ * @returns Previous output state. Pass to endArray() when done.
+ */
+ virtual uint32_t beginArray() RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Ends an array.
+ * @param a_uOldState Previous output state (returned by beginArray()).
+ */
+ virtual void endArray(uint32_t a_uOldState) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Begins an object.
+ * @returns Previous output state. Pass to endObject() when done.
+ */
+ virtual uint32_t beginObject() RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Ends an array.
+ * @param a_uOldState Previous output state (returned by beginObject()).
+ */
+ virtual void endObject(uint32_t a_uOldState) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Outputs a value separator.
+ * This is called before a value, not after.
+ */
+ virtual void valueSeparator() RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Outputs a value separator, name and name separator.
+ */
+ virtual void valueSeparatorAndName(const char *a_pszName, size_t a_cchName) RT_NOEXCEPT RT_OVERRIDE;
+
+protected:
+ /** Helper for outputting the correct amount of indentation. */
+ void outputIndentation() RT_NOEXCEPT;
+};
+
+
+/**
+ * Serialize to a string object.
+ */
+class RT_DECL_CLASS RTCRestOutputToString : public RTCRestOutputBase
+{
+public:
+ /**
+ * Creates an instance that appends to @a a_pDst.
+ * @param a_pDst Pointer to the destination string object.
+ * NULL is not accepted and will assert.
+ * @param a_fAppend Whether to append to the current string value, or
+ * nuke the string content before starting the output.
+ */
+ RTCRestOutputToString(RTCString *a_pDst, bool a_fAppend = false) RT_NOEXCEPT;
+ virtual ~RTCRestOutputToString();
+
+ virtual size_t output(const char *a_pchString, size_t a_cchToWrite) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Finalizes the output and releases the string object to the caller.
+ *
+ * @returns The released string object. NULL if we ran out of memory or if
+ * called already.
+ *
+ * @remark This sets m_pDst to NULL and the object cannot be use for any
+ * more output afterwards.
+ */
+ virtual RTCString *finalize() RT_NOEXCEPT;
+
+protected:
+ /** Pointer to the destination string. NULL after finalize(). */
+ RTCString *m_pDst;
+ /** Set if we ran out of memory and should ignore subsequent calls. */
+ bool m_fOutOfMemory;
+
+ /* Make non-copyable (RTCNonCopyable causes warnings): */
+ RTCRestOutputToString(RTCRestOutputToString const &);
+ RTCRestOutputToString *operator=(RTCRestOutputToString const &);
+};
+
+
+/**
+ * Serialize pretty JSON to a string object.
+ */
+class RT_DECL_CLASS RTCRestOutputPrettyToString : public RTCRestOutputPrettyBase
+{
+public:
+ /**
+ * Creates an instance that appends to @a a_pDst.
+ * @param a_pDst Pointer to the destination string object.
+ * NULL is not accepted and will assert.
+ * @param a_fAppend Whether to append to the current string value, or
+ * nuke the string content before starting the output.
+ */
+ RTCRestOutputPrettyToString(RTCString *a_pDst, bool a_fAppend = false) RT_NOEXCEPT;
+ virtual ~RTCRestOutputPrettyToString();
+
+ virtual size_t output(const char *a_pchString, size_t a_cchToWrite) RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Finalizes the output and releases the string object to the caller.
+ *
+ * @returns The released string object. NULL if we ran out of memory or if
+ * called already.
+ *
+ * @remark This sets m_pDst to NULL and the object cannot be use for any
+ * more output afterwards.
+ */
+ virtual RTCString *finalize() RT_NOEXCEPT;
+
+protected:
+ /** Pointer to the destination string. NULL after finalize(). */
+ RTCString *m_pDst;
+ /** Set if we ran out of memory and should ignore subsequent calls. */
+ bool m_fOutOfMemory;
+
+ /* Make non-copyable (RTCNonCopyable causes warnings): */
+ RTCRestOutputPrettyToString(RTCRestOutputToString const &);
+ RTCRestOutputPrettyToString *operator=(RTCRestOutputToString const &);
+};
+
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_restoutput_h */
+
diff --git a/include/iprt/cpp/reststringmap.h b/include/iprt/cpp/reststringmap.h
new file mode 100644
index 00000000..cff201bb
--- /dev/null
+++ b/include/iprt/cpp/reststringmap.h
@@ -0,0 +1,499 @@
+/** @file
+ * IPRT - C++ Representational State Transfer (REST) String Map Template.
+ */
+
+/*
+ * Copyright (C) 2008-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_reststringmap_h
+#define IPRT_INCLUDED_cpp_reststringmap_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/list.h>
+#include <iprt/string.h>
+#include <iprt/cpp/restbase.h>
+
+
+/** @defgroup grp_rt_cpp_reststingmap C++ Representational State Transfer (REST) String Map Template
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/**
+ * Abstract base class for the RTCRestStringMap template.
+ */
+class RT_DECL_CLASS RTCRestStringMapBase : public RTCRestObjectBase
+{
+public:
+ /** Default destructor. */
+ RTCRestStringMapBase() RT_NOEXCEPT;
+ /** Copy constructor. */
+ RTCRestStringMapBase(RTCRestStringMapBase const &a_rThat);
+ /** Destructor. */
+ virtual ~RTCRestStringMapBase();
+ /** Copy assignment operator. */
+ RTCRestStringMapBase &operator=(RTCRestStringMapBase const &a_rThat);
+
+ /* Overridden methods: */
+ virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
+ virtual RTCRestOutputBase &serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual int deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
+ // later?
+ //virtual int toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const RT_NOEXCEPT RT_OVERRIDE;
+ //virtual int fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+ // uint32_t a_fFlags = kCollectionFormat_Unspecified) RT_NOEXCEPT RT_OVERRIDE;
+ virtual kTypeClass typeClass(void) const RT_NOEXCEPT RT_OVERRIDE;
+ virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;
+
+ /**
+ * Clear the content of the map.
+ */
+ void clear() RT_NOEXCEPT;
+
+ /**
+ * Checks if the map is empty.
+ */
+ inline bool isEmpty() const RT_NOEXCEPT { return m_cEntries == 0; }
+
+ /**
+ * Gets the number of entries in the map.
+ */
+ size_t size() const RT_NOEXCEPT;
+
+ /**
+ * Checks if the map contains the given key.
+ * @returns true if key found, false if not.
+ * @param a_pszKey The key to check fo.
+ */
+ bool containsKey(const char *a_pszKey) const RT_NOEXCEPT;
+
+ /**
+ * Checks if the map contains the given key.
+ * @returns true if key found, false if not.
+ * @param a_rStrKey The key to check fo.
+ */
+ bool containsKey(RTCString const &a_rStrKey) const RT_NOEXCEPT;
+
+ /**
+ * Remove any key-value pair with the given key.
+ * @returns true if anything was removed, false if not found.
+ * @param a_pszKey The key to remove.
+ */
+ bool remove(const char *a_pszKey) RT_NOEXCEPT;
+
+ /**
+ * Remove any key-value pair with the given key.
+ * @returns true if anything was removed, false if not found.
+ * @param a_rStrKey The key to remove.
+ */
+ bool remove(RTCString const &a_rStrKey) RT_NOEXCEPT;
+
+ /**
+ * Creates a new value and inserts it under the given key, returning the new value.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_ppValue Where to return the pointer to the value.
+ * @param a_pszKey The key to put it under.
+ * @param a_cchKey The length of the key. Default is the entire string.
+ * @param a_fReplace Whether to replace or fail on key collision.
+ */
+ int putNewValue(RTCRestObjectBase **a_ppValue, const char *a_pszKey, size_t a_cchKey = RTSTR_MAX, bool a_fReplace = false) RT_NOEXCEPT;
+
+ /**
+ * Creates a new value and inserts it under the given key, returning the new value.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_ppValue Where to return the pointer to the value.
+ * @param a_rStrKey The key to put it under.
+ * @param a_fReplace Whether to replace or fail on key collision.
+ */
+ int putNewValue(RTCRestObjectBase **a_ppValue, RTCString const &a_rStrKey, bool a_fReplace = false) RT_NOEXCEPT;
+
+protected:
+ /** Map entry. */
+ typedef struct MapEntry
+ {
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** List node for enumeration. */
+ RTLISTNODE ListEntry;
+ /** The key.
+ * @remarks Core.pszString points to the value of this object. So, consider it const. */
+ RTCString strKey;
+ /** The value. */
+ RTCRestObjectBase *pValue;
+ } MapEntry;
+ /** The map tree. */
+ RTSTRSPACE m_Map;
+ /** The enumeration list head (MapEntry). */
+ RTLISTANCHOR m_ListHead;
+ /** Number of map entries. */
+ size_t m_cEntries;
+
+public:
+ /** @name Map Iteration
+ * @{ */
+ /** Const iterator. */
+ class ConstIterator
+ {
+ private:
+ MapEntry *m_pCur;
+ ConstIterator() RT_NOEXCEPT;
+ protected:
+ ConstIterator(MapEntry *a_pEntry) RT_NOEXCEPT : m_pCur(a_pEntry) { }
+ public:
+ ConstIterator(ConstIterator const &a_rThat) RT_NOEXCEPT : m_pCur(a_rThat.m_pCur) { }
+
+ /** Gets the key string. */
+ inline RTCString const &getKey() RT_NOEXCEPT { return m_pCur->strKey; }
+ /** Gets poitner to the value object. */
+ inline RTCRestObjectBase const *getValue() RT_NOEXCEPT { return m_pCur->pValue; }
+
+ /** Advance to the next map entry. */
+ inline ConstIterator &operator++() RT_NOEXCEPT
+ {
+ m_pCur = RTListNodeGetNextCpp(&m_pCur->ListEntry, MapEntry, ListEntry);
+ return *this;
+ }
+
+ /** Advance to the previous map entry. */
+ inline ConstIterator &operator--() RT_NOEXCEPT
+ {
+ m_pCur = RTListNodeGetPrevCpp(&m_pCur->ListEntry, MapEntry, ListEntry);
+ return *this;
+ }
+
+ /** Compare equal. */
+ inline bool operator==(ConstIterator const &a_rThat) RT_NOEXCEPT { return m_pCur == a_rThat.m_pCur; }
+ /** Compare not equal. */
+ inline bool operator!=(ConstIterator const &a_rThat) RT_NOEXCEPT { return m_pCur != a_rThat.m_pCur; }
+
+ /* Map class must be friend so it can use the MapEntry constructor. */
+ friend class RTCRestStringMapBase;
+ };
+
+ /** Returns iterator for the first map entry (unless it's empty and it's also the end). */
+ inline ConstIterator begin() const RT_NOEXCEPT
+ {
+ if (!RTListIsEmpty(&m_ListHead))
+ return ConstIterator(RTListNodeGetNextCpp(&m_ListHead, MapEntry, ListEntry));
+ return end();
+ }
+ /** Returns iterator for the last map entry (unless it's empty and it's also the end). */
+ inline ConstIterator last() const RT_NOEXCEPT
+ {
+ if (!RTListIsEmpty(&m_ListHead))
+ return ConstIterator(RTListNodeGetPrevCpp(&m_ListHead, MapEntry, ListEntry));
+ return end();
+ }
+ /** Returns the end iterator. This does not ever refer to an actual map entry. */
+ inline ConstIterator end() const RT_NOEXCEPT
+ {
+ return ConstIterator(RT_FROM_CPP_MEMBER(&m_ListHead, MapEntry, ListEntry));
+ }
+ /** @} */
+
+
+protected:
+ /**
+ * Helper for creating a clone.
+ *
+ * @returns Pointer to new map object on success, NULL if out of memory.
+ */
+ virtual RTCRestStringMapBase *createClone(void) const RT_NOEXCEPT = 0;
+
+ /**
+ * Wrapper around the value constructor.
+ *
+ * @returns Pointer to new value object on success, NULL if out of memory.
+ */
+ virtual RTCRestObjectBase *createValue(void) RT_NOEXCEPT = 0;
+
+ /**
+ * For accessing the static deserializeInstanceFromJson() method of the value.
+ */
+ virtual int deserializeValueInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT = 0;
+
+ /**
+ * Worker for the copy assignment method and copyMapWorkerMayThrow.
+ *
+ * This will use createEntryCopy to do the copying.
+ *
+ * @returns VINF_SUCCESS on success, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_rThat The map to copy. Caller makes 100% sure the it has
+ * the same type as the destination.
+ */
+ int copyMapWorkerNoThrow(RTCRestStringMapBase const &a_rThat) RT_NOEXCEPT;
+
+ /**
+ * Wrapper around copyMapWorkerNoThrow() that throws allocation errors, making
+ * it suitable for copy constructors and assignment operators.
+ */
+ void copyMapWorkerMayThrow(RTCRestStringMapBase const &a_rThat);
+
+ /**
+ * Worker for performing inserts.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_pszKey The key.
+ * @param a_pValue The value to insert. Ownership is transferred to the map on success.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ * @param a_cchKey The key length, the whole string by default.
+ */
+ int putWorker(const char *a_pszKey, RTCRestObjectBase *a_pValue, bool a_fReplace, size_t a_cchKey = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Worker for performing inserts.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_pszKey The key.
+ * @param a_rValue The value to copy into the map.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ * @param a_cchKey The key length, the whole string by default.
+ */
+ int putCopyWorker(const char *a_pszKey, RTCRestObjectBase const &a_rValue, bool a_fReplace, size_t a_cchKey = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Worker for getting the value corresponding to the given key.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_pszKey The key which value to look up.
+ */
+ RTCRestObjectBase *getWorker(const char *a_pszKey) RT_NOEXCEPT;
+
+ /**
+ * Worker for getting the value corresponding to the given key, const variant.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_pszKey The key which value to look up.
+ */
+ RTCRestObjectBase const *getWorker(const char *a_pszKey) const RT_NOEXCEPT;
+
+private:
+ static DECLCALLBACK(int) stringSpaceDestructorCallback(PRTSTRSPACECORE pStr, void *pvUser) RT_NOEXCEPT;
+};
+
+
+/**
+ * Limited map class.
+ */
+template<class ValueType> class RTCRestStringMap : public RTCRestStringMapBase
+{
+public:
+ /** Default constructor, creates emtpy map. */
+ RTCRestStringMap() RT_NOEXCEPT
+ : RTCRestStringMapBase()
+ {}
+
+ /** Copy constructor. */
+ RTCRestStringMap(RTCRestStringMap const &a_rThat)
+ : RTCRestStringMapBase()
+ {
+ copyMapWorkerMayThrow(a_rThat);
+ }
+
+ /** Destructor. */
+ virtual ~RTCRestStringMap()
+ {
+ /* nothing to do here. */
+ }
+
+ /** Copy assignment operator. */
+ RTCRestStringMap &operator=(RTCRestStringMap const &a_rThat)
+ {
+ copyMapWorkerMayThrow(a_rThat);
+ return *this;
+ }
+
+ /** Safe copy assignment method. */
+ int assignCopy(RTCRestStringMap const &a_rThat) RT_NOEXCEPT
+ {
+ return copyMapWorkerNoThrow(a_rThat);
+ }
+
+ /** Make a clone of this object. */
+ inline RTCRestStringMap *clone() const RT_NOEXCEPT
+ {
+ return (RTCRestStringMap *)baseClone();
+ }
+
+ /** Factory method. */
+ static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT
+ {
+ return new (std::nothrow) RTCRestStringMap<ValueType>();
+ }
+
+ /** Factory method for values. */
+ static DECLCALLBACK(RTCRestObjectBase *) createValueInstance(void) RT_NOEXCEPT
+ {
+ return new (std::nothrow) ValueType();
+ }
+
+ /** @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON */
+ static DECLCALLBACK(int) deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+ {
+ *a_ppInstance = new (std::nothrow) RTCRestStringMap<ValueType>();
+ if (*a_ppInstance)
+ return (*a_ppInstance)->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+ }
+
+ /**
+ * Inserts the given object into the map.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_pszKey The key.
+ * @param a_pValue The value to insert. Ownership is transferred to the map on success.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ */
+ inline int put(const char *a_pszKey, ValueType *a_pValue, bool a_fReplace = false) RT_NOEXCEPT
+ {
+ return putWorker(a_pszKey, a_pValue, a_fReplace);
+ }
+
+ /**
+ * Inserts the given object into the map.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_rStrKey The key.
+ * @param a_pValue The value to insert. Ownership is transferred to the map on success.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ */
+ inline int put(RTCString const &a_rStrKey, ValueType *a_pValue, bool a_fReplace = false) RT_NOEXCEPT
+ {
+ return putWorker(a_rStrKey.c_str(), a_pValue, a_fReplace, a_rStrKey.length());
+ }
+
+ /**
+ * Inserts a copy of the given object into the map.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_pszKey The key.
+ * @param a_rValue The value to insert a copy of.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ */
+ inline int putCopy(const char *a_pszKey, const ValueType &a_rValue, bool a_fReplace = false) RT_NOEXCEPT
+ {
+ return putCopyWorker(a_pszKey, a_rValue, a_fReplace);
+ }
+
+ /**
+ * Inserts a copy of the given object into the map.
+ *
+ * @returns VINF_SUCCESS or VWRN_ALREADY_EXISTS on success.
+ * VERR_ALREADY_EXISTS, VERR_NO_MEMORY or VERR_NO_STR_MEMORY on failure.
+ * @param a_rStrKey The key.
+ * @param a_rValue The value to insert a copy of.
+ * @param a_fReplace Whether to replace existing key-value pair with matching key.
+ */
+ inline int putCopy(RTCString const &a_rStrKey, const ValueType &a_rValue, bool a_fReplace = false) RT_NOEXCEPT
+ {
+ return putCopyWorker(a_rStrKey.c_str(), a_rValue, a_fReplace, a_rStrKey.length());
+ }
+
+ /**
+ * Gets the value corresponding to the given key.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_pszKey The key which value to look up.
+ */
+ inline ValueType *get(const char *a_pszKey) RT_NOEXCEPT
+ {
+ return (ValueType *)getWorker(a_pszKey);
+ }
+
+ /**
+ * Gets the value corresponding to the given key.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_rStrKey The key which value to look up.
+ */
+ inline ValueType *get(RTCString const &a_rStrKey) RT_NOEXCEPT
+ {
+ return (ValueType *)getWorker(a_rStrKey.c_str());
+ }
+
+ /**
+ * Gets the const value corresponding to the given key.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_pszKey The key which value to look up.
+ */
+ inline ValueType const *get(const char *a_pszKey) const RT_NOEXCEPT
+ {
+ return (ValueType const *)getWorker(a_pszKey);
+ }
+
+ /**
+ * Gets the const value corresponding to the given key.
+ *
+ * @returns Pointer to the value object if found, NULL if key not in the map.
+ * @param a_rStrKey The key which value to look up.
+ */
+ inline ValueType const *get(RTCString const &a_rStrKey) const RT_NOEXCEPT
+ {
+ return (ValueType const *)getWorker(a_rStrKey.c_str());
+ }
+
+ /** @todo enumerator*/
+
+protected:
+ virtual RTCRestStringMapBase *createClone(void) const RT_NOEXCEPT RT_OVERRIDE
+ {
+ return new (std::nothrow) RTCRestStringMap();
+ }
+
+ virtual RTCRestObjectBase *createValue(void) RT_NOEXCEPT RT_OVERRIDE
+ {
+ return new (std::nothrow) ValueType();
+ }
+
+ virtual int deserializeValueInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT RT_OVERRIDE
+ {
+ return ValueType::deserializeInstanceFromJson(a_rCursor, a_ppInstance);
+ }
+};
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_reststringmap_h */
+
diff --git a/include/iprt/cpp/utils.h b/include/iprt/cpp/utils.h
new file mode 100644
index 00000000..f5b36fa8
--- /dev/null
+++ b/include/iprt/cpp/utils.h
@@ -0,0 +1,149 @@
+/** @file
+ * IPRT - C++ Utilities (useful templates, defines and such).
+ */
+
+/*
+ * Copyright (C) 2006-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_utils_h
+#define IPRT_INCLUDED_cpp_utils_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/** @defgroup grp_rt_cpp IPRT C++ APIs */
+
+/** @defgroup grp_rt_cpp_util C++ Utilities
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+#define DPTR(CLASS) CLASS##Private *d = static_cast<CLASS##Private *>(d_ptr)
+#define QPTR(CLASS) CLASS *q = static_cast<CLASS *>(q_ptr)
+
+/**
+ * A simple class used to prevent copying and assignment.
+ *
+ * Inherit from this class in order to prevent automatic generation
+ * of the copy constructor and assignment operator in your class.
+ */
+class RTCNonCopyable
+{
+protected:
+ RTCNonCopyable() {}
+ ~RTCNonCopyable() {}
+private:
+ RTCNonCopyable(RTCNonCopyable const &);
+ RTCNonCopyable &operator=(RTCNonCopyable const &);
+};
+
+
+/**
+ * Shortcut to |const_cast<C &>()| that automatically derives the correct
+ * type (class) for the const_cast template's argument from its own argument.
+ *
+ * Can be used to temporarily cancel the |const| modifier on the left-hand side
+ * of assignment expressions, like this:
+ * @code
+ * const Class That;
+ * ...
+ * unconst(That) = SomeValue;
+ * @endcode
+ *
+ * @todo What to do about the prefix here?
+ */
+template <class C>
+inline C &unconst(const C &that)
+{
+ return const_cast<C &>(that);
+}
+
+
+/**
+ * Shortcut to |const_cast<C *>()| that automatically derives the correct
+ * type (class) for the const_cast template's argument from its own argument.
+ *
+ * Can be used to temporarily cancel the |const| modifier on the left-hand side
+ * of assignment expressions, like this:
+ * @code
+ * const Class *pThat;
+ * ...
+ * unconst(pThat) = SomeValue;
+ * @endcode
+ *
+ * @todo What to do about the prefix here?
+ */
+template <class C>
+inline C *unconst(const C *that)
+{
+ return const_cast<C *>(that);
+}
+
+
+/**
+ * Macro for generating a non-const getter version from a const getter.
+ *
+ * @param a_RetType The getter return type.
+ * @param a_Class The class name.
+ * @param a_Getter The getter name.
+ * @param a_ArgDecls The argument declaration for the getter method.
+ * @param a_ArgList The argument list for the call.
+ */
+#define RT_CPP_GETTER_UNCONST(a_RetType, a_Class, a_Getter, a_ArgDecls, a_ArgList) \
+ a_RetType a_Getter a_ArgDecls \
+ { \
+ return static_cast< a_Class const *>(this)-> a_Getter a_ArgList; \
+ }
+
+
+/**
+ * Macro for generating a non-const getter version from a const getter,
+ * unconsting the return value as well.
+ *
+ * @param a_RetType The getter return type.
+ * @param a_Class The class name.
+ * @param a_Getter The getter name.
+ * @param a_ArgDecls The argument declaration for the getter method.
+ * @param a_ArgList The argument list for the call.
+ */
+#define RT_CPP_GETTER_UNCONST_RET(a_RetType, a_Class, a_Getter, a_ArgDecls, a_ArgList) \
+ a_RetType a_Getter a_ArgDecls \
+ { \
+ return const_cast<a_RetType>(static_cast< a_Class const *>(this)-> a_Getter a_ArgList); \
+ }
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_cpp_utils_h */
+
diff --git a/include/iprt/cpp/xml.h b/include/iprt/cpp/xml.h
new file mode 100644
index 00000000..9459f9d3
--- /dev/null
+++ b/include/iprt/cpp/xml.h
@@ -0,0 +1,1247 @@
+/** @file
+ * IPRT - XML Helper APIs.
+ */
+
+/*
+ * Copyright (C) 2007-2022 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
+ */
+
+#ifndef IPRT_INCLUDED_cpp_xml_h
+#define IPRT_INCLUDED_cpp_xml_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef IN_RING3
+# error "There are no XML APIs available in Ring-0 Context!"
+#endif
+#ifdef IPRT_NO_CRT
+# error "Not available in no-CRT mode because it depends on exceptions, std::list, std::map and stdio.h."
+#endif
+
+#include <iprt/list.h>
+#include <iprt/cpp/exception.h>
+#include <iprt/cpp/utils.h>
+
+#include <list>
+#include <memory>
+
+
+/** @defgroup grp_rt_cpp_xml C++ XML support
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/* Forwards */
+typedef struct _xmlParserInput xmlParserInput;
+typedef xmlParserInput *xmlParserInputPtr;
+typedef struct _xmlParserCtxt xmlParserCtxt;
+typedef xmlParserCtxt *xmlParserCtxtPtr;
+typedef struct _xmlError xmlError;
+typedef xmlError *xmlErrorPtr;
+
+typedef struct _xmlAttr xmlAttr;
+typedef struct _xmlNode xmlNode;
+
+#define RT_XML_CONTENT_SMALL _8K
+#define RT_XML_CONTENT_LARGE _128K
+#define RT_XML_ATTR_TINY 64
+#define RT_XML_ATTR_SMALL _1K
+#define RT_XML_ATTR_MEDIUM _8K
+#define RT_XML_ATTR_LARGE _64K
+
+/** @} */
+
+namespace xml
+{
+
+/**
+ * @addtogroup grp_rt_cpp_xml
+ * @{
+ */
+
+// Exceptions
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS LogicError : public RTCError
+{
+public:
+
+ LogicError(const char *aMsg = NULL)
+ : RTCError(aMsg)
+ {}
+
+ LogicError(RT_SRC_POS_DECL);
+};
+
+class RT_DECL_CLASS RuntimeError : public RTCError
+{
+public:
+
+ RuntimeError(const char *aMsg = NULL)
+ : RTCError(aMsg)
+ {}
+};
+
+class RT_DECL_CLASS XmlError : public RuntimeError
+{
+public:
+ XmlError(xmlErrorPtr aErr);
+
+ static char* Format(xmlErrorPtr aErr);
+};
+
+// Logical errors
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS ENotImplemented : public LogicError
+{
+public:
+ ENotImplemented(const char *aMsg = NULL) : LogicError(aMsg) {}
+ ENotImplemented(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS EInvalidArg : public LogicError
+{
+public:
+ EInvalidArg(const char *aMsg = NULL) : LogicError(aMsg) {}
+ EInvalidArg(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS EDocumentNotEmpty : public LogicError
+{
+public:
+ EDocumentNotEmpty(const char *aMsg = NULL) : LogicError(aMsg) {}
+ EDocumentNotEmpty(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS ENodeIsNotElement : public LogicError
+{
+public:
+ ENodeIsNotElement(const char *aMsg = NULL) : LogicError(aMsg) {}
+ ENodeIsNotElement(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+// Runtime errors
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS EIPRTFailure : public RuntimeError
+{
+public:
+
+ EIPRTFailure(int aRC, const char *pszContextFmt, ...);
+
+ int rc() const
+ {
+ return mRC;
+ }
+
+private:
+ int mRC;
+};
+
+/**
+ * The Stream class is a base class for I/O streams.
+ */
+class RT_DECL_CLASS Stream
+{
+public:
+
+ virtual ~Stream() {}
+
+ virtual const char *uri() const = 0;
+
+ /**
+ * Returns the current read/write position in the stream. The returned
+ * position is a zero-based byte offset from the beginning of the file.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual uint64_t pos() const = 0;
+
+ /**
+ * Sets the current read/write position in the stream.
+ *
+ * @param aPos Zero-based byte offset from the beginning of the stream.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual void setPos (uint64_t aPos) = 0;
+};
+
+/**
+ * The Input class represents an input stream.
+ *
+ * This input stream is used to read the settings tree from.
+ * This is an abstract class that must be subclassed in order to fill it with
+ * useful functionality.
+ */
+class RT_DECL_CLASS Input : virtual public Stream
+{
+public:
+
+ /**
+ * Reads from the stream to the supplied buffer.
+ *
+ * @param aBuf Buffer to store read data to.
+ * @param aLen Buffer length.
+ *
+ * @return Number of bytes read.
+ */
+ virtual int read (char *aBuf, int aLen) = 0;
+};
+
+/**
+ *
+ */
+class RT_DECL_CLASS Output : virtual public Stream
+{
+public:
+
+ /**
+ * Writes to the stream from the supplied buffer.
+ *
+ * @param aBuf Buffer to write data from.
+ * @param aLen Buffer length.
+ *
+ * @return Number of bytes written.
+ */
+ virtual int write (const char *aBuf, int aLen) = 0;
+
+ /**
+ * Truncates the stream from the current position and upto the end.
+ * The new file size will become exactly #pos() bytes.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual void truncate() = 0;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The File class is a stream implementation that reads from and writes to
+ * regular files.
+ *
+ * The File class uses IPRT File API for file operations. Note that IPRT File
+ * API is not thread-safe. This means that if you pass the same RTFILE handle to
+ * different File instances that may be simultaneously used on different
+ * threads, you should care about serialization; otherwise you will get garbage
+ * when reading from or writing to such File instances.
+ */
+class RT_DECL_CLASS File : public Input, public Output
+{
+public:
+
+ /**
+ * Possible file access modes.
+ */
+ enum Mode { Mode_Read, Mode_WriteCreate, Mode_Overwrite, Mode_ReadWrite };
+
+ /**
+ * Opens a file with the given name in the given mode. If @a aMode is Read
+ * or ReadWrite, the file must exist. If @a aMode is Write, the file must
+ * not exist. Otherwise, an EIPRTFailure exception will be thrown.
+ *
+ * @param aMode File mode.
+ * @param aFileName File name.
+ * @param aFlushIt Whether to flush a writable file before closing it.
+ */
+ File(Mode aMode, const char *aFileName, bool aFlushIt = false);
+
+ /**
+ * Uses the given file handle to perform file operations. This file
+ * handle must be already open in necessary mode (read, or write, or mixed).
+ *
+ * The read/write position of the given handle will be reset to the
+ * beginning of the file on success.
+ *
+ * Note that the given file handle will not be automatically closed upon
+ * this object destruction.
+ *
+ * @note It you pass the same RTFILE handle to more than one File instance,
+ * please make sure you have provided serialization in case if these
+ * instasnces are to be simultaneously used by different threads.
+ * Otherwise you may get garbage when reading or writing.
+ *
+ * @param aHandle Open file handle.
+ * @param aFileName File name (for reference).
+ * @param aFlushIt Whether to flush a writable file before closing it.
+ */
+ File(RTFILE aHandle, const char *aFileName = NULL, bool aFlushIt = false);
+
+ /**
+ * Destroys the File object. If the object was created from a file name
+ * the corresponding file will be automatically closed. If the object was
+ * created from a file handle, it will remain open.
+ */
+ virtual ~File();
+
+ const char *uri() const;
+
+ uint64_t pos() const;
+ void setPos(uint64_t aPos);
+
+ /**
+ * See Input::read(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ int read(char *aBuf, int aLen);
+
+ /**
+ * See Output::write(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ int write(const char *aBuf, int aLen);
+
+ /**
+ * See Output::truncate(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ void truncate();
+
+private:
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+
+ /* auto_ptr data doesn't have proper copy semantics */
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(File);
+};
+
+/**
+ * The MemoryBuf class represents a stream implementation that reads from the
+ * memory buffer.
+ */
+class RT_DECL_CLASS MemoryBuf : public Input
+{
+public:
+
+ MemoryBuf (const char *aBuf, size_t aLen, const char *aURI = NULL);
+
+ virtual ~MemoryBuf();
+
+ const char *uri() const;
+
+ int read(char *aBuf, int aLen);
+ uint64_t pos() const;
+ void setPos(uint64_t aPos);
+
+private:
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+
+ /* auto_ptr data doesn't have proper copy semantics */
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(MemoryBuf);
+};
+
+
+/*
+ * GlobalLock
+ *
+ *
+ */
+
+
+typedef DECLCALLBACKTYPE_EX(xmlParserInput *, RT_NOTHING, FNEXTERNALENTITYLOADER,(const char *aURI, const char *aID,
+ xmlParserCtxt *aCtxt));
+typedef FNEXTERNALENTITYLOADER *PFNEXTERNALENTITYLOADER; /**< xmlExternalEntityLoader w/ noexcept. */
+
+class RT_DECL_CLASS GlobalLock
+{
+public:
+ GlobalLock();
+ ~GlobalLock();
+
+ void setExternalEntityLoader(PFNEXTERNALENTITYLOADER pFunc);
+
+ static xmlParserInput* callDefaultLoader(const char *aURI,
+ const char *aID,
+ xmlParserCtxt *aCtxt);
+
+private:
+ /* Obscure class data. */
+ struct Data;
+ struct Data *m;
+};
+
+class ElementNode;
+typedef std::list<const ElementNode*> ElementNodesList;
+
+class AttributeNode;
+
+class ContentNode;
+
+/**
+ * Node base class.
+ *
+ * Cannot be used directly, but ElementNode, ContentNode and AttributeNode
+ * derive from this. This does implement useful public methods though.
+ *
+ *
+ */
+class RT_DECL_CLASS Node
+{
+public:
+ virtual ~Node();
+
+ const char *getName() const;
+ const char *getPrefix() const;
+ const char *getNamespaceURI() const;
+ bool nameEqualsNS(const char *pcszNamespace, const char *pcsz) const;
+ bool nameEquals(const char *pcsz) const
+ {
+ return nameEqualsNS(NULL, pcsz);
+ }
+ bool nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace = NULL) const;
+
+ const char *getValue() const;
+ const char *getValueN(size_t cchValueLimit) const;
+ bool copyValue(int32_t &i) const;
+ bool copyValue(uint32_t &i) const;
+ bool copyValue(int64_t &i) const;
+ bool copyValue(uint64_t &i) const;
+
+ /** @name Introspection.
+ * @{ */
+ /** Is this an ElementNode instance.
+ * @returns true / false */
+ bool isElement() const
+ {
+ return m_Type == IsElement;
+ }
+
+ /** Is this an ContentNode instance.
+ * @returns true / false */
+ bool isContent() const
+ {
+ return m_Type == IsContent;
+ }
+
+ /** Is this an AttributeNode instance.
+ * @returns true / false */
+ bool isAttribute() const
+ {
+ return m_Type == IsAttribute;
+ }
+
+ int getLineNumber() const;
+ /** @} */
+
+ /** @name General tree enumeration.
+ *
+ * Use the introspection methods isElement() and isContent() before doing static
+ * casting. Parents are always or ElementNode type, but siblings and children
+ * can be of both ContentNode and ElementNode types.
+ *
+ * @remarks Attribute node are in the attributes list, while both content and
+ * element nodes are in the list of children. See ElementNode.
+ *
+ * @remarks Careful mixing tree walking with node removal!
+ * @{
+ */
+ /** Get the parent node
+ * @returns Pointer to the parent node, or NULL if root. */
+ const Node *getParent() const
+ {
+ return m_pParent;
+ }
+
+ /** Get the previous sibling.
+ * @returns Pointer to the previous sibling node, NULL if first child.
+ */
+ const Node *getPrevSibiling() const
+ {
+ if (!m_pParentListAnchor)
+ return NULL;
+ return RTListGetPrevCpp(m_pParentListAnchor, this, const Node, m_listEntry);
+ }
+
+ /** Get the next sibling.
+ * @returns Pointer to the next sibling node, NULL if last child. */
+ const Node *getNextSibiling() const
+ {
+ if (!m_pParentListAnchor)
+ return NULL;
+ return RTListGetNextCpp(m_pParentListAnchor, this, const Node, m_listEntry);
+ }
+ /** @} */
+
+protected:
+ /** Node types. */
+ typedef enum { IsElement, IsAttribute, IsContent } EnumType;
+
+ /** The type of node this is an instance of. */
+ EnumType m_Type;
+ /** The parent node (always an element), NULL if root. */
+ Node *m_pParent;
+
+ xmlNode *m_pLibNode; ///< != NULL if this is an element or content node
+ xmlAttr *m_pLibAttr; ///< != NULL if this is an attribute node
+ const char *m_pcszNamespacePrefix; ///< not always set
+ const char *m_pcszNamespaceHref; ///< full http:// spec
+ const char *m_pcszName; ///< element or attribute name, points either into pLibNode or pLibAttr;
+ ///< NULL if this is a content node
+
+ /** Child list entry of this node. (List head m_pParent->m_children or
+ * m_pParent->m_attribute depending on the type.) */
+ RTLISTNODE m_listEntry;
+ /** Pointer to the parent list anchor.
+ * This allows us to use m_listEntry both for children and attributes. */
+ PRTLISTANCHOR m_pParentListAnchor;
+
+ // hide the default constructor so people use only our factory methods
+ Node(EnumType type,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlNode *pLibNode,
+ xmlAttr *pLibAttr);
+ Node(const Node &x); // no copying
+
+ friend class AttributeNode;
+ friend class ElementNode; /* C list hack. */
+};
+
+/**
+ * Node subclass that represents an attribute of an element.
+ *
+ * For attributes, Node::getName() returns the attribute name, and Node::getValue()
+ * returns the attribute value, if any.
+ *
+ * Since the Node constructor is private, one can create new attribute nodes
+ * only through the following factory methods:
+ *
+ * -- ElementNode::setAttribute()
+ */
+class RT_DECL_CLASS AttributeNode : public Node
+{
+public:
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ AttributeNode(const ElementNode *pElmRoot,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlAttr *pLibAttr);
+ AttributeNode(const AttributeNode &x); // no copying
+
+ friend class Node;
+ friend class ElementNode;
+};
+
+/**
+ * Node subclass that represents an element.
+ *
+ * For elements, Node::getName() returns the element name, and Node::getValue()
+ * returns the text contents, if any.
+ *
+ * Since the Node constructor is private, one can create element nodes
+ * only through the following factory methods:
+ *
+ * -- Document::createRootElement()
+ * -- ElementNode::createChild()
+ */
+class RT_DECL_CLASS ElementNode : public Node
+{
+public:
+ int getChildElements(ElementNodesList &children, const char *pcszMatch = NULL) const;
+
+ const ElementNode *findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const;
+ const ElementNode *findChildElement(const char *pcszMatch) const
+ {
+ return findChildElementNS(NULL, pcszMatch);
+ }
+ const ElementNode *findChildElementFromId(const char *pcszId) const;
+
+ /** Finds the first decendant matching the name at the end of @a pcszPath and
+ * optionally namespace.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const ElementNode *findChildElementP(const char *pcszPath, const char *pcszNamespace = NULL) const;
+
+ /** Finds the first child with matching the give name and optionally namspace,
+ * returning its value.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const char *findChildElementValueP(const char *pcszPath, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getValue();
+ return NULL;
+ }
+
+ /** Finds the first child with matching the give name and optionally namspace,
+ * returning its value. Checks the length against the limit.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const char *findChildElementValuePN(const char *pcszPath, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getValueN(cchValueLimit);
+ return NULL;
+ }
+
+ /** Combines findChildElementNS and findAttributeValue.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszChild The child element name.
+ * @param pcszAttribute The attribute name.
+ * @param pcszChildNamespace The namespace to match @a pcszChild with, NULL
+ * (default) match any namespace.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementNS and findAttributeValue
+ * @note The findChildElementAttributeValueP() method would do the same thing
+ * given the same inputs, but it would be slightly slower, thus the
+ * separate method.
+ */
+ const char *findChildElementAttributeValue(const char *pcszChild, const char *pcszAttribute,
+ const char *pcszChildNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementNS(pcszChildNamespace, pcszChild);
+ if (pElem)
+ return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
+ return NULL;
+ }
+
+ /** Combines findChildElementP and findAttributeValue.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszPath Path to the child element. Slashes can be used
+ * to make a simple path to any decendant.
+ * @param pcszAttribute The attribute name.
+ * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
+ * (default) match any namespace. When using a
+ * path, this matches all elements along the way.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementP and findAttributeValue
+ */
+ const char *findChildElementAttributeValueP(const char *pcszPath, const char *pcszAttribute,
+ const char *pcszPathNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
+ if (pElem)
+ return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
+ return NULL;
+ }
+
+ /** Combines findChildElementP and findAttributeValueN.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszPath The attribute name. Slashes can be used to make a
+ * simple path to any decendant.
+ * @param pcszAttribute The attribute name.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
+ * (default) match any namespace. When using a
+ * path, this matches all elements along the way.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementP and findAttributeValue
+ */
+ const char *findChildElementAttributeValuePN(const char *pcszPath, const char *pcszAttribute,
+ size_t cchValueLimit,
+ const char *pcszPathNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
+ if (pElem)
+ return pElem->findAttributeValueN(pcszAttribute, cchValueLimit, pcszAttributeNamespace);
+ return NULL;
+ }
+
+
+ /** @name Tree enumeration.
+ * @{ */
+
+ /** Get the next tree element in a full tree enumeration.
+ *
+ * By starting with the root node, this can be used to enumerate the entire tree
+ * (or sub-tree if @a pElmRoot is used).
+ *
+ * @returns Pointer to the next element in the tree, NULL if we're done.
+ * @param pElmRoot The root of the tree we're enumerating. NULL if
+ * it's the entire tree.
+ */
+ ElementNode const *getNextTreeElement(ElementNode const *pElmRoot = NULL) const;
+ RT_CPP_GETTER_UNCONST_RET(ElementNode *, ElementNode, getNextTreeElement, (const ElementNode *pElmRoot = NULL), (pElmRoot))
+
+ /** Get the first child node.
+ * @returns Pointer to the first child node, NULL if no children. */
+ const Node *getFirstChild() const
+ {
+ return RTListGetFirstCpp(&m_children, const Node, m_listEntry);
+ }
+ RT_CPP_GETTER_UNCONST_RET(Node *, ElementNode, getFirstChild,(),())
+
+ /** Get the last child node.
+ * @returns Pointer to the last child node, NULL if no children. */
+ const Node *getLastChild() const
+ {
+ return RTListGetLastCpp(&m_children, const Node, m_listEntry);
+ }
+
+ /** Get the first child node.
+ * @returns Pointer to the first child node, NULL if no children. */
+ const ElementNode *getFirstChildElement() const;
+
+ /** Get the last child node.
+ * @returns Pointer to the last child node, NULL if no children. */
+ const ElementNode *getLastChildElement() const;
+
+ /** Get the previous sibling element.
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @see getNextSibilingElement, getPrevSibling
+ */
+ const ElementNode *getPrevSibilingElement() const;
+
+ /** Get the next sibling element.
+ * @returns Pointer to the next sibling element, NULL if last child element.
+ * @see getPrevSibilingElement, getNextSibling
+ */
+ const ElementNode *getNextSibilingElement() const;
+
+ /** Find the previous element matching the given name and namespace (optionally).
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @param pcszName The element name to match.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @note Changed the order of the arguments.
+ */
+ const ElementNode *findPrevSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
+
+ /** Find the next element matching the given name and namespace (optionally).
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @param pcszName The element name to match.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @note Changed the order of the arguments.
+ */
+ const ElementNode *findNextSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
+ /** @} */
+
+ /** @name Attribute enumeration
+ * @{ */
+
+ /** Get the first attribute node.
+ * @returns Pointer to the first child node, NULL if no attributes. */
+ const AttributeNode *getFirstAttribute() const
+ {
+ return RTListGetFirstCpp(&m_attributes, const AttributeNode, m_listEntry);
+ }
+
+ /** Get the last attribute node.
+ * @returns Pointer to the last child node, NULL if no attributes. */
+ const AttributeNode *getLastAttribute() const
+ {
+ return RTListGetLastCpp(&m_attributes, const AttributeNode, m_listEntry);
+ }
+
+ /** @} */
+
+ const AttributeNode *findAttribute(const char *pcszMatch, const char *pcszNamespace = NULL) const;
+ /** Find the first attribute with the given name, returning its value string.
+ * @returns Pointer to the attribute string value.
+ * @param pcszName The attribute name.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @see getAttributeValue
+ */
+ const char *findAttributeValue(const char *pcszName, const char *pcszNamespace = NULL) const
+ {
+ const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
+ if (pAttr)
+ return pAttr->getValue();
+ return NULL;
+ }
+ /** Find the first attribute with the given name, returning its value string.
+ * @returns Pointer to the attribute string value.
+ * @param pcszName The attribute name.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @see getAttributeValue
+ */
+ const char *findAttributeValueN(const char *pcszName, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ {
+ const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
+ if (pAttr)
+ return pAttr->getValueN(cchValueLimit);
+ return NULL;
+ }
+
+ bool getAttributeValue(const char *pcszMatch, const char *&pcsz, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &pcsz, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
+ bool getAttributeValuePath(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, int32_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, uint32_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, int64_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, uint64_t &u, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &u, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, bool &f, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &f, pcszNamespace); }
+ bool getAttributeValueN(const char *pcszMatch, const char *&pcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &pcsz, cchValueLimit, pcszNamespace); }
+ bool getAttributeValueN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
+ bool getAttributeValuePathN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
+
+ /** @name Variants that for clarity does not use references for output params.
+ * @{ */
+ bool getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
+ bool getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, int32_t *pi, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, uint32_t *pu, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, uint64_t *pu, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, bool *pf, const char *pcszNamespace = NULL) const;
+ bool getAttributeValueN(const char *pcszMatch, const char **ppcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ bool getAttributeValueN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ bool getAttributeValuePathN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ /** @} */
+
+ /** @name Convenience methods for convering the element value.
+ * @{ */
+ bool getElementValue(int32_t *piValue) const;
+ bool getElementValue(uint32_t *puValue) const;
+ bool getElementValue(int64_t *piValue) const;
+ bool getElementValue(uint64_t *puValue) const;
+ bool getElementValue(bool *pfValue) const;
+ /** @} */
+
+ /** @name Convenience findChildElementValueP and getElementValue.
+ * @{ */
+ bool getChildElementValueP(const char *pcszPath, int32_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(piValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, uint32_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(puValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, int64_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(piValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, uint64_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(puValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, bool *pfValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(pfValue);
+ }
+
+ /** @} */
+
+ /** @name Convenience findChildElementValueP and getElementValue with a
+ * default value being return if the child element isn't present.
+ *
+ * @remarks These will return false on conversion errors.
+ * @{ */
+ bool getChildElementValueDefP(const char *pcszPath, int32_t iDefault, int32_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(piValue);
+ *piValue = iDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, uint32_t uDefault, uint32_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(puValue);
+ *puValue = uDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, int64_t iDefault, int64_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(piValue);
+ *piValue = iDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, uint64_t uDefault, uint64_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(puValue);
+ *puValue = uDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, bool fDefault, bool *pfValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(pfValue);
+ *pfValue = fDefault;
+ return true;
+ }
+ /** @} */
+
+ ElementNode *createChild(const char *pcszElementName);
+
+ ContentNode *addContent(const char *pcszContent);
+ ContentNode *addContent(const RTCString &strContent)
+ {
+ return addContent(strContent.c_str());
+ }
+
+ ContentNode *setContent(const char *pcszContent);
+ ContentNode *setContent(const RTCString &strContent)
+ {
+ return setContent(strContent.c_str());
+ }
+
+ AttributeNode *setAttribute(const char *pcszName, const char *pcszValue);
+ AttributeNode *setAttribute(const char *pcszName, const RTCString &strValue)
+ {
+ return setAttribute(pcszName, strValue.c_str());
+ }
+ AttributeNode *setAttributePath(const char *pcszName, const RTCString &strValue);
+ AttributeNode *setAttribute(const char *pcszName, int32_t i);
+ AttributeNode *setAttribute(const char *pcszName, uint32_t i);
+ AttributeNode *setAttribute(const char *pcszName, int64_t i);
+ AttributeNode *setAttribute(const char *pcszName, uint64_t i);
+ AttributeNode *setAttributeHex(const char *pcszName, uint32_t i);
+ AttributeNode *setAttribute(const char *pcszName, bool f);
+
+ virtual ~ElementNode();
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ ElementNode(const ElementNode *pElmRoot, Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
+ ElementNode(const ElementNode &x); // no copying
+
+ /** We keep a pointer to the root element for attribute namespace handling. */
+ const ElementNode *m_pElmRoot;
+
+ /** List of child elements and content nodes. */
+ RTLISTANCHOR m_children;
+ /** List of attributes nodes. */
+ RTLISTANCHOR m_attributes;
+
+ static void buildChildren(ElementNode *pElmRoot);
+
+ friend class Node;
+ friend class Document;
+ friend class XmlFileParser;
+};
+
+/**
+ * Node subclass that represents content (non-element text).
+ *
+ * Since the Node constructor is private, one can create new content nodes
+ * only through the following factory methods:
+ *
+ * -- ElementNode::addContent()
+ */
+class RT_DECL_CLASS ContentNode : public Node
+{
+public:
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
+ ContentNode(const ContentNode &x); // no copying
+
+ friend class Node;
+ friend class ElementNode;
+};
+
+
+/**
+ * Handy helper class with which one can loop through all or some children
+ * of a particular element. See NodesLoop::forAllNodes() for details.
+ */
+class RT_DECL_CLASS NodesLoop
+{
+public:
+ NodesLoop(const ElementNode &node, const char *pcszMatch = NULL);
+ ~NodesLoop();
+ const ElementNode* forAllNodes() const;
+
+private:
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+/**
+ * The XML document class. An instance of this needs to be created by a user
+ * of the XML classes and then passed to
+ *
+ * -- XmlMemParser or XmlFileParser to read an XML document; those classes then
+ * fill the caller's Document with ElementNode, ContentNode and AttributeNode
+ * instances. The typical sequence then is:
+ * @code
+ Document doc;
+ XmlFileParser parser;
+ parser.read("file.xml", doc);
+ Element *pElmRoot = doc.getRootElement();
+ @endcode
+ *
+ * -- XmlMemWriter or XmlFileWriter to write out an XML document after it has
+ * been created and filled. Example:
+ *
+ * @code
+ Document doc;
+ Element *pElmRoot = doc.createRootElement();
+ // add children
+ xml::XmlFileWriter writer(doc);
+ writer.write("file.xml", true);
+ @endcode
+ */
+class RT_DECL_CLASS Document
+{
+public:
+ Document();
+ ~Document();
+
+ Document(const Document &x);
+ Document& operator=(const Document &x);
+
+ const ElementNode* getRootElement() const;
+ ElementNode* getRootElement();
+
+ ElementNode* createRootElement(const char *pcszRootElementName,
+ const char *pcszComment = NULL);
+
+private:
+ friend class XmlMemParser;
+ friend class XmlFileParser;
+ friend class XmlMemWriter;
+ friend class XmlStringWriter;
+ friend class XmlFileWriter;
+
+ void refreshInternals();
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+/*
+ * XmlParserBase
+ *
+ */
+
+class RT_DECL_CLASS XmlParserBase
+{
+protected:
+ XmlParserBase();
+ ~XmlParserBase();
+
+ xmlParserCtxtPtr m_ctxt;
+};
+
+/*
+ * XmlMemParser
+ *
+ */
+
+class RT_DECL_CLASS XmlMemParser : public XmlParserBase
+{
+public:
+ XmlMemParser();
+ ~XmlMemParser();
+
+ void read(const void* pvBuf, size_t cbSize, const RTCString &strFilename, Document &doc);
+};
+
+/*
+ * XmlFileParser
+ *
+ */
+
+class RT_DECL_CLASS XmlFileParser : public XmlParserBase
+{
+public:
+ XmlFileParser();
+ ~XmlFileParser();
+
+ void read(const RTCString &strFilename, Document &doc);
+
+private:
+ /* Obscure class data */
+ struct Data;
+ struct Data *m;
+
+ static int ReadCallback(void *aCtxt, char *aBuf, int aLen) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
+};
+
+/**
+ * XmlMemWriter
+ */
+class RT_DECL_CLASS XmlMemWriter
+{
+public:
+ XmlMemWriter();
+ ~XmlMemWriter();
+
+ void write(const Document &doc, void** ppvBuf, size_t *pcbSize);
+
+private:
+ void* m_pBuf;
+};
+
+
+/**
+ * XmlStringWriter - writes the XML to an RTCString instance.
+ */
+class RT_DECL_CLASS XmlStringWriter
+{
+public:
+ XmlStringWriter();
+
+ int write(const Document &rDoc, RTCString *pStrDst);
+
+private:
+ static int WriteCallbackForSize(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
+ static int WriteCallbackForReal(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *pvUser) RT_NOTHROW_PROTO;
+
+ /** Pointer to the destination string while we're in the write() call. */
+ RTCString *m_pStrDst;
+ /** Set by WriteCallback if we cannot grow the destination string. */
+ bool m_fOutOfMemory;
+};
+
+
+/**
+ * XmlFileWriter
+ */
+class RT_DECL_CLASS XmlFileWriter
+{
+public:
+ XmlFileWriter(Document &doc);
+ ~XmlFileWriter();
+
+ /**
+ * Writes the XML document to the specified file.
+ *
+ * @param pcszFilename The name of the output file.
+ * @param fSafe If @c true, some extra safety precautions will be
+ * taken when writing the file:
+ * -# The file is written with a '-tmp' suffix.
+ * -# It is flushed to disk after writing.
+ * -# Any original file is renamed to '-prev'.
+ * -# The '-tmp' file is then renamed to the
+ * specified name.
+ * -# The directory changes are flushed to disk.
+ * The suffixes are available via s_pszTmpSuff and
+ * s_pszPrevSuff.
+ */
+ void write(const char *pcszFilename, bool fSafe);
+
+ static int WriteCallback(void *aCtxt, const char *aBuf, int aLen) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
+
+ /** The suffix used by XmlFileWriter::write() for the temporary file. */
+ static const char * const s_pszTmpSuff;
+ /** The suffix used by XmlFileWriter::write() for the previous (backup) file. */
+ static const char * const s_pszPrevSuff;
+
+private:
+ void writeInternal(const char *pcszFilename, bool fSafe);
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+#if defined(_MSC_VER)
+#pragma warning (default:4251)
+#endif
+
+/** @} */
+
+} // end namespace xml
+
+#endif /* !IPRT_INCLUDED_cpp_xml_h */
+