diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/string/strformattype.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/string/strformattype.cpp')
-rw-r--r-- | src/VBox/Runtime/common/string/strformattype.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/string/strformattype.cpp b/src/VBox/Runtime/common/string/strformattype.cpp new file mode 100644 index 00000000..27f25c31 --- /dev/null +++ b/src/VBox/Runtime/common/string/strformattype.cpp @@ -0,0 +1,487 @@ +/* $Id: strformattype.cpp $ */ +/** @file + * IPRT - IPRT String Formatter Extensions, Dynamic Types. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_STRING +#include <iprt/string.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/stdarg.h> +#include "internal/string.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#ifdef RT_STRICT +# define RTSTRFORMATTYPE_WITH_LOCKING +#endif +#ifdef RTSTRFORMATTYPE_WITH_LOCKING +# define RTSTRFORMATTYPE_LOCK_OFFSET 0x7fff0000 +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Description of a registered formatting type. + * + * In GC we'll be using offsets instead of pointers just to try avoid having to + * do the bothersome relocating. This of course assumes that all the relevant + * code stays within the same mapping. + */ +typedef struct RTSTRDYNFMT +{ + /** The length of the type. */ + uint8_t cchType; + /** The type name. */ + char szType[47]; + /** The handler function. + * In GC the offset is relative to g_aTypes[0], so that &g_aTypes[0] + offHandler + * gives the actual address. */ +#ifdef IN_RC + int32_t offHandler; +#else + PFNRTSTRFORMATTYPE pfnHandler; +#endif + /** Callback argument. */ + void * volatile pvUser; +#if ARCH_BITS == 32 + /** Size alignment padding. */ + char abPadding[8]; +#endif +} RTSTRDYNFMT; +AssertCompileSizeAlignment(RTSTRDYNFMT, 32); +typedef RTSTRDYNFMT *PRTSTRDYNFMT; +typedef RTSTRDYNFMT const *PCRTSTRDYNFMT; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The registered types, sorted for binary lookup. + * We use a static array here because it avoids RTMemAlloc dependencies+leaks. */ +static RTSTRDYNFMT g_aTypes[64]; +/** The number of registered types. */ +static uint32_t g_cTypes = 0; +#ifdef RTSTRFORMATTYPE_WITH_LOCKING +/** This is just a thing we assert/spin on. + * Zero == unlocked, negative == write locked, positive == read locked. + * + * The user should do all the serialization and we'll smack his fingers in + * strict builds if he doesn't. */ +static int32_t volatile g_i32Spinlock = 0; +#endif + + +/** + * Locks the stuff for updating. + * + * Mostly for check that the caller is doing his job. + */ +DECLINLINE(void) rtstrFormatTypeWriteLock(void) +{ +#if defined(RTSTRFORMATTYPE_WITH_LOCKING) + if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0))) + { + unsigned volatile i; + + AssertFailed(); + for (i = 0;; i++) + if ( !g_i32Spinlock + && ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0)) + break; + } +#endif +} + + +/** + * Undoing rtstrFormatTypeWriteLock. + */ +DECLINLINE(void) rtstrFormatTypeWriteUnlock(void) +{ +#if defined(RTSTRFORMATTYPE_WITH_LOCKING) + Assert(g_i32Spinlock < 0); + ASMAtomicAddS32(&g_i32Spinlock, RTSTRFORMATTYPE_LOCK_OFFSET); +#endif +} + + +/** + * Locks the stuff for reading. + * + * This is just cheap stuff to make sure the caller is doing the right thing. + */ +DECLINLINE(void) rtstrFormatTypeReadLock(void) +{ +#if defined(RTSTRFORMATTYPE_WITH_LOCKING) + if (RT_UNLIKELY(ASMAtomicIncS32(&g_i32Spinlock) < 0)) + { + unsigned volatile i; + + AssertFailed(); + for (i = 0;; i++) + if (ASMAtomicUoReadS32(&g_i32Spinlock) > 0) + break; + } +#endif +} + + +/** + * Undoing rtstrFormatTypeReadLock. + */ +DECLINLINE(void) rtstrFormatTypeReadUnlock(void) +{ +#if defined(RTSTRFORMATTYPE_WITH_LOCKING) + Assert(g_i32Spinlock > 0); + ASMAtomicDecS32(&g_i32Spinlock); +#endif +} + + +/** + * Compares a type string with a type entry, the string doesn't need to be terminated. + * + * @returns Same as memcmp. + * @param pszType The type string, doesn't need to be terminated. + * @param cchType The number of chars in @a pszType to compare. + * @param pType The type entry to compare with. + */ +DECLINLINE(int) rtstrFormatTypeCompare(const char *pszType, size_t cchType, PCRTSTRDYNFMT pType) +{ + size_t cch = RT_MIN(cchType, pType->cchType); + int iDiff = memcmp(pszType, pType->szType, cch); + if (!iDiff) + { + if (cchType == pType->cchType) + return 0; + iDiff = cchType < pType->cchType ? -1 : 1; + } + return iDiff; +} + + +/** + * Looks up a type entry. + * + * @returns The type index, -1 on failure. + * @param pszType The type to look up. This doesn't have to be terminated. + * @param cchType The length of the type. + */ +DECLINLINE(int32_t) rtstrFormatTypeLookup(const char *pszType, size_t cchType) +{ + /* + * Lookup the type - binary search. + */ + int32_t iStart = 0; + int32_t iEnd = g_cTypes - 1; + int32_t i = iEnd / 2; + for (;;) + { + int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]); + if (!iDiff) + return i; + if (iEnd == iStart) + break; + if (iDiff < 0) + iEnd = i - 1; + else + iStart = i + 1; + if (iEnd < iStart) + break; + i = iStart + (iEnd - iStart) / 2; + } + return -1; +} + + +/** + * Register a format handler for a type. + * + * The format handler is used to handle '%R[type]' format types, where the argument + * in the vector is a pointer value (a bit restrictive, but keeps it simple). + * + * The caller must ensure that no other thread will be making use of any of + * the dynamic formatting type facilities simultaneously with this call. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_ALREADY_EXISTS if the type has already been registered. + * @retval VERR_TOO_MANY_OPEN_FILES if all the type slots has been allocated already. + * + * @param pszType The type name. + * @param pfnHandler The handler address. See FNRTSTRFORMATTYPE for details. + * @param pvUser The user argument to pass to the handler. See RTStrFormatTypeSetUser + * for how to update this later. + */ +RTDECL(int) RTStrFormatTypeRegister(const char *pszType, PFNRTSTRFORMATTYPE pfnHandler, void *pvUser) +{ + int rc; + size_t cchType; + uint32_t cTypes; + + /* + * Validate input. + */ + AssertPtr(pfnHandler); + AssertPtr(pszType); + cchType = strlen(pszType); + AssertReturn(cchType < RT_SIZEOFMEMB(RTSTRDYNFMT, szType), VERR_INVALID_PARAMETER); + + /* + * Try add it. + */ + rtstrFormatTypeWriteLock(); + + /* check that there are empty slots. */ + cTypes = g_cTypes; + if (cTypes < RT_ELEMENTS(g_aTypes)) + { + /* find where to insert it. */ + uint32_t i = 0; + rc = VINF_SUCCESS; + while (i < cTypes) + { + int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]); + if (!iDiff) + { + rc = VERR_ALREADY_EXISTS; + break; + } + if (iDiff < 0) + break; + i++; + } + if (RT_SUCCESS(rc)) + { + /* make room. */ + uint32_t cToMove = cTypes - i; + if (cToMove) + memmove(&g_aTypes[i + 1], &g_aTypes[i], cToMove * sizeof(g_aTypes[i])); + + /* insert the new entry. */ + memset(&g_aTypes[i], 0, sizeof(g_aTypes[i])); + memcpy(&g_aTypes[i].szType[0], pszType, cchType + 1); + g_aTypes[i].cchType = (uint8_t)cchType; + g_aTypes[i].pvUser = pvUser; +#ifdef IN_RC + g_aTypes[i].offHandler = (intptr_t)pfnHandler - (intptr_t)&g_aTypes[0]; +#else + g_aTypes[i].pfnHandler = pfnHandler; +#endif + ASMAtomicIncU32(&g_cTypes); + rc = VINF_SUCCESS; + } + } + else + rc = VERR_TOO_MANY_OPEN_FILES; /** @todo fix error code */ + + rtstrFormatTypeWriteUnlock(); + + return rc; +} +RT_EXPORT_SYMBOL(RTStrFormatTypeRegister); + + +/** + * Deregisters a format type. + * + * The caller must ensure that no other thread will be making use of any of + * the dynamic formatting type facilities simultaneously with this call. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_FILE_NOT_FOUND if not found. + * + * @param pszType The type to deregister. + */ +RTDECL(int) RTStrFormatTypeDeregister(const char *pszType) +{ + int32_t i; + + /* + * Validate input. + */ + AssertPtr(pszType); + + /* + * Locate the entry and remove it. + */ + rtstrFormatTypeWriteLock(); + i = rtstrFormatTypeLookup(pszType, strlen(pszType)); + if (i >= 0) + { + const uint32_t cTypes = g_cTypes; + int32_t cToMove = cTypes - i - 1; + if (cToMove > 0) + memmove(&g_aTypes[i], &g_aTypes[i + 1], cToMove * sizeof(g_aTypes[i])); + memset(&g_aTypes[cTypes - 1], 0, sizeof(g_aTypes[0])); + ASMAtomicDecU32(&g_cTypes); + } + rtstrFormatTypeWriteUnlock(); + + Assert(i >= 0); + return i >= 0 + ? VINF_SUCCESS + : VERR_FILE_NOT_FOUND; /** @todo fix status code */ +} +RT_EXPORT_SYMBOL(RTStrFormatTypeDeregister); + + +/** + * Sets the user argument for a type. + * + * This can be used if a user argument needs relocating in GC. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_FILE_NOT_FOUND if not found. + * + * @param pszType The type to update. + * @param pvUser The new user argument value. + */ +RTDECL(int) RTStrFormatTypeSetUser(const char *pszType, void *pvUser) +{ + int32_t i; + + /* + * Validate input. + */ + AssertPtr(pszType); + + /* + * Locate the entry and update it. + */ + rtstrFormatTypeReadLock(); + + i = rtstrFormatTypeLookup(pszType, strlen(pszType)); + if (i >= 0) + ASMAtomicWritePtr(&g_aTypes[i].pvUser, pvUser); + + rtstrFormatTypeReadUnlock(); + + Assert(i >= 0); + return i >= 0 + ? VINF_SUCCESS + : VERR_FILE_NOT_FOUND; /** @todo fix status code */ +} +RT_EXPORT_SYMBOL(RTStrFormatTypeSetUser); + + +/** + * Formats a type using a registered callback handler. + * + * This will handle %R[type]. + * + * @returns The number of bytes formatted. + * @param pfnOutput Pointer to output function. + * @param pvArgOutput Argument for the output function. + * @param ppszFormat Pointer to the format string pointer. Advance this till the char + * after the format specifier. + * @param pArgs Pointer to the argument list. Use this to fetch the arguments. + * @param cchWidth Format Width. -1 if not specified. + * @param cchPrecision Format Precision. -1 if not specified. + * @param fFlags Flags (RTSTR_NTFS_*). + * @param chArgSize The argument size specifier, 'l' or 'L'. + */ +DECLHIDDEN(size_t) rtstrFormatType(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char **ppszFormat, + va_list *pArgs, int cchWidth, int cchPrecision, unsigned fFlags, char chArgSize) +{ + size_t cch; + int32_t i; + char const *pszTypeEnd; + char const *pszType; + char ch; + void *pvValue = va_arg(*pArgs, void *); + NOREF(chArgSize); + + /* + * Parse out the type. + */ + pszType = *ppszFormat + 2; + *ppszFormat = pszType; + Assert(pszType[-1] == '['); + Assert(pszType[-2] == 'R'); + pszTypeEnd = pszType; + while ((ch = *pszTypeEnd) != ']') + { + AssertReturn(ch != '\0', 0); + AssertReturn(ch != '%', 0); + AssertReturn(ch != '[', 0); + pszTypeEnd++; + } + *ppszFormat = pszTypeEnd + 1; + + /* + * Locate the entry and call the handler. + */ + rtstrFormatTypeReadLock(); + + i = rtstrFormatTypeLookup(pszType, pszTypeEnd - pszType); + if (RT_LIKELY(i >= 0)) + { +#ifdef IN_RC + PFNRTSTRFORMATTYPE pfnHandler = (PFNRTSTRFORMATTYPE)((intptr_t)&g_aTypes[0] + g_aTypes[i].offHandler); +#else + PFNRTSTRFORMATTYPE pfnHandler = g_aTypes[i].pfnHandler; +#endif + void *pvUser = ASMAtomicReadPtr(&g_aTypes[i].pvUser); + + rtstrFormatTypeReadUnlock(); + + cch = pfnHandler(pfnOutput, pvArgOutput, g_aTypes[i].szType, pvValue, cchWidth, cchPrecision, fFlags, pvUser); + } + else + { + rtstrFormatTypeReadUnlock(); + + cch = pfnOutput(pvArgOutput, RT_STR_TUPLE("<missing:%R[")); + cch += pfnOutput(pvArgOutput, pszType, pszTypeEnd - pszType); + cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("]>")); + } + + return cch; +} + |