VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/strformattype.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.7 KB
Line 
1/* $Id: strformattype.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - IPRT String Formatter Extensions, Dynamic Types.
4 */
5
6/*
7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Global Variables *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_STRING
42#include <iprt/string.h>
43#include "internal/iprt.h"
44
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/err.h>
48#include <iprt/stdarg.h>
49#include "internal/string.h"
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55#ifdef RT_STRICT
56# define RTSTRFORMATTYPE_WITH_LOCKING
57#endif
58#ifdef RTSTRFORMATTYPE_WITH_LOCKING
59# define RTSTRFORMATTYPE_LOCK_OFFSET 0x7fff0000
60#endif
61
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66/**
67 * Description of a registered formatting type.
68 *
69 * In GC we'll be using offsets instead of pointers just to try avoid having to
70 * do the bothersome relocating. This of course assumes that all the relevant
71 * code stays within the same mapping.
72 */
73typedef struct RTSTRDYNFMT
74{
75 /** The length of the type. */
76 uint8_t cchType;
77 /** The type name. */
78 char szType[47];
79 /** The handler function.
80 * In GC the offset is relative to g_aTypes[0], so that &g_aTypes[0] + offHandler
81 * gives the actual address. */
82#ifdef IN_RC
83 int32_t offHandler;
84#else
85 PFNRTSTRFORMATTYPE pfnHandler;
86#endif
87 /** Callback argument. */
88 void * volatile pvUser;
89#if ARCH_BITS == 32
90 /** Size alignment padding. */
91 char abPadding[8];
92#endif
93} RTSTRDYNFMT;
94AssertCompileSizeAlignment(RTSTRDYNFMT, 32);
95typedef RTSTRDYNFMT *PRTSTRDYNFMT;
96typedef RTSTRDYNFMT const *PCRTSTRDYNFMT;
97
98
99/*********************************************************************************************************************************
100* Global Variables *
101*********************************************************************************************************************************/
102/** The registered types, sorted for binary lookup.
103 * We use a static array here because it avoids RTMemAlloc dependencies+leaks. */
104static RTSTRDYNFMT g_aTypes[64];
105/** The number of registered types. */
106static uint32_t g_cTypes = 0;
107#ifdef RTSTRFORMATTYPE_WITH_LOCKING
108/** This is just a thing we assert/spin on.
109 * Zero == unlocked, negative == write locked, positive == read locked.
110 *
111 * The user should do all the serialization and we'll smack his fingers in
112 * strict builds if he doesn't. */
113static int32_t volatile g_i32Spinlock = 0;
114#endif
115
116
117/**
118 * Locks the stuff for updating.
119 *
120 * Mostly for check that the caller is doing his job.
121 */
122DECLINLINE(void) rtstrFormatTypeWriteLock(void)
123{
124#if defined(RTSTRFORMATTYPE_WITH_LOCKING)
125 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0)))
126 {
127 unsigned volatile i;
128
129 AssertFailed();
130 for (i = 0;; i++)
131 if ( !g_i32Spinlock
132 && ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0))
133 break;
134 }
135#endif
136}
137
138
139/**
140 * Undoing rtstrFormatTypeWriteLock.
141 */
142DECLINLINE(void) rtstrFormatTypeWriteUnlock(void)
143{
144#if defined(RTSTRFORMATTYPE_WITH_LOCKING)
145 Assert(g_i32Spinlock < 0);
146 ASMAtomicAddS32(&g_i32Spinlock, RTSTRFORMATTYPE_LOCK_OFFSET);
147#endif
148}
149
150
151/**
152 * Locks the stuff for reading.
153 *
154 * This is just cheap stuff to make sure the caller is doing the right thing.
155 */
156DECLINLINE(void) rtstrFormatTypeReadLock(void)
157{
158#if defined(RTSTRFORMATTYPE_WITH_LOCKING)
159 if (RT_UNLIKELY(ASMAtomicIncS32(&g_i32Spinlock) < 0))
160 {
161 unsigned volatile i;
162
163 AssertFailed();
164 for (i = 0;; i++)
165 if (ASMAtomicUoReadS32(&g_i32Spinlock) > 0)
166 break;
167 }
168#endif
169}
170
171
172/**
173 * Undoing rtstrFormatTypeReadLock.
174 */
175DECLINLINE(void) rtstrFormatTypeReadUnlock(void)
176{
177#if defined(RTSTRFORMATTYPE_WITH_LOCKING)
178 Assert(g_i32Spinlock > 0);
179 ASMAtomicDecS32(&g_i32Spinlock);
180#endif
181}
182
183
184/**
185 * Compares a type string with a type entry, the string doesn't need to be terminated.
186 *
187 * @returns Same as memcmp.
188 * @param pszType The type string, doesn't need to be terminated.
189 * @param cchType The number of chars in @a pszType to compare.
190 * @param pType The type entry to compare with.
191 */
192DECLINLINE(int) rtstrFormatTypeCompare(const char *pszType, size_t cchType, PCRTSTRDYNFMT pType)
193{
194 size_t cch = RT_MIN(cchType, pType->cchType);
195 int iDiff = memcmp(pszType, pType->szType, cch);
196 if (!iDiff)
197 {
198 if (cchType == pType->cchType)
199 return 0;
200 iDiff = cchType < pType->cchType ? -1 : 1;
201 }
202 return iDiff;
203}
204
205
206/**
207 * Looks up a type entry.
208 *
209 * @returns The type index, -1 on failure.
210 * @param pszType The type to look up. This doesn't have to be terminated.
211 * @param cchType The length of the type.
212 */
213DECLINLINE(int32_t) rtstrFormatTypeLookup(const char *pszType, size_t cchType)
214{
215 /*
216 * Lookup the type - binary search.
217 */
218 int32_t iStart = 0;
219 int32_t iEnd = g_cTypes - 1;
220 int32_t i = iEnd / 2;
221 for (;;)
222 {
223 int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]);
224 if (!iDiff)
225 return i;
226 if (iEnd == iStart)
227 break;
228 if (iDiff < 0)
229 iEnd = i - 1;
230 else
231 iStart = i + 1;
232 if (iEnd < iStart)
233 break;
234 i = iStart + (iEnd - iStart) / 2;
235 }
236 return -1;
237}
238
239
240/**
241 * Register a format handler for a type.
242 *
243 * The format handler is used to handle '%R[type]' format types, where the argument
244 * in the vector is a pointer value (a bit restrictive, but keeps it simple).
245 *
246 * The caller must ensure that no other thread will be making use of any of
247 * the dynamic formatting type facilities simultaneously with this call.
248 *
249 * @returns IPRT status code.
250 * @retval VINF_SUCCESS on success.
251 * @retval VERR_ALREADY_EXISTS if the type has already been registered.
252 * @retval VERR_TOO_MANY_OPEN_FILES if all the type slots has been allocated already.
253 *
254 * @param pszType The type name.
255 * @param pfnHandler The handler address. See FNRTSTRFORMATTYPE for details.
256 * @param pvUser The user argument to pass to the handler. See RTStrFormatTypeSetUser
257 * for how to update this later.
258 */
259RTDECL(int) RTStrFormatTypeRegister(const char *pszType, PFNRTSTRFORMATTYPE pfnHandler, void *pvUser)
260{
261 int rc;
262 size_t cchType;
263 uint32_t cTypes;
264
265 /*
266 * Validate input.
267 */
268 AssertPtr(pfnHandler);
269 AssertPtr(pszType);
270 cchType = strlen(pszType);
271 AssertReturn(cchType < RT_SIZEOFMEMB(RTSTRDYNFMT, szType), VERR_INVALID_PARAMETER);
272
273 /*
274 * Try add it.
275 */
276 rtstrFormatTypeWriteLock();
277
278 /* check that there are empty slots. */
279 cTypes = g_cTypes;
280 if (cTypes < RT_ELEMENTS(g_aTypes))
281 {
282 /* find where to insert it. */
283 uint32_t i = 0;
284 rc = VINF_SUCCESS;
285 while (i < cTypes)
286 {
287 int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]);
288 if (!iDiff)
289 {
290 rc = VERR_ALREADY_EXISTS;
291 break;
292 }
293 if (iDiff < 0)
294 break;
295 i++;
296 }
297 if (RT_SUCCESS(rc))
298 {
299 /* make room. */
300 uint32_t cToMove = cTypes - i;
301 if (cToMove)
302 memmove(&g_aTypes[i + 1], &g_aTypes[i], cToMove * sizeof(g_aTypes[i]));
303
304 /* insert the new entry. */
305 memset(&g_aTypes[i], 0, sizeof(g_aTypes[i]));
306 memcpy(&g_aTypes[i].szType[0], pszType, cchType + 1);
307 g_aTypes[i].cchType = (uint8_t)cchType;
308 g_aTypes[i].pvUser = pvUser;
309#ifdef IN_RC
310 g_aTypes[i].offHandler = (intptr_t)pfnHandler - (intptr_t)&g_aTypes[0];
311#else
312 g_aTypes[i].pfnHandler = pfnHandler;
313#endif
314 ASMAtomicIncU32(&g_cTypes);
315 rc = VINF_SUCCESS;
316 }
317 }
318 else
319 rc = VERR_TOO_MANY_OPEN_FILES; /** @todo fix error code */
320
321 rtstrFormatTypeWriteUnlock();
322
323 return rc;
324}
325RT_EXPORT_SYMBOL(RTStrFormatTypeRegister);
326
327
328/**
329 * Deregisters a format type.
330 *
331 * The caller must ensure that no other thread will be making use of any of
332 * the dynamic formatting type facilities simultaneously with this call.
333 *
334 * @returns IPRT status code.
335 * @retval VINF_SUCCESS on success.
336 * @retval VERR_FILE_NOT_FOUND if not found.
337 *
338 * @param pszType The type to deregister.
339 */
340RTDECL(int) RTStrFormatTypeDeregister(const char *pszType)
341{
342 int32_t i;
343
344 /*
345 * Validate input.
346 */
347 AssertPtr(pszType);
348
349 /*
350 * Locate the entry and remove it.
351 */
352 rtstrFormatTypeWriteLock();
353 i = rtstrFormatTypeLookup(pszType, strlen(pszType));
354 if (i >= 0)
355 {
356 const uint32_t cTypes = g_cTypes;
357 int32_t cToMove = cTypes - i - 1;
358 if (cToMove > 0)
359 memmove(&g_aTypes[i], &g_aTypes[i + 1], cToMove * sizeof(g_aTypes[i]));
360 memset(&g_aTypes[cTypes - 1], 0, sizeof(g_aTypes[0]));
361 ASMAtomicDecU32(&g_cTypes);
362 }
363 rtstrFormatTypeWriteUnlock();
364
365 Assert(i >= 0);
366 return i >= 0
367 ? VINF_SUCCESS
368 : VERR_FILE_NOT_FOUND; /** @todo fix status code */
369}
370RT_EXPORT_SYMBOL(RTStrFormatTypeDeregister);
371
372
373/**
374 * Sets the user argument for a type.
375 *
376 * This can be used if a user argument needs relocating in GC.
377 *
378 * @returns IPRT status code.
379 * @retval VINF_SUCCESS on success.
380 * @retval VERR_FILE_NOT_FOUND if not found.
381 *
382 * @param pszType The type to update.
383 * @param pvUser The new user argument value.
384 */
385RTDECL(int) RTStrFormatTypeSetUser(const char *pszType, void *pvUser)
386{
387 int32_t i;
388
389 /*
390 * Validate input.
391 */
392 AssertPtr(pszType);
393
394 /*
395 * Locate the entry and update it.
396 */
397 rtstrFormatTypeReadLock();
398
399 i = rtstrFormatTypeLookup(pszType, strlen(pszType));
400 if (i >= 0)
401 ASMAtomicWritePtr(&g_aTypes[i].pvUser, pvUser);
402
403 rtstrFormatTypeReadUnlock();
404
405 Assert(i >= 0);
406 return i >= 0
407 ? VINF_SUCCESS
408 : VERR_FILE_NOT_FOUND; /** @todo fix status code */
409}
410RT_EXPORT_SYMBOL(RTStrFormatTypeSetUser);
411
412
413/**
414 * Formats a type using a registered callback handler.
415 *
416 * This will handle %R[type].
417 *
418 * @returns The number of bytes formatted.
419 * @param pfnOutput Pointer to output function.
420 * @param pvArgOutput Argument for the output function.
421 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
422 * after the format specifier.
423 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
424 * @param cchWidth Format Width. -1 if not specified.
425 * @param cchPrecision Format Precision. -1 if not specified.
426 * @param fFlags Flags (RTSTR_NTFS_*).
427 * @param chArgSize The argument size specifier, 'l' or 'L'.
428 */
429DECLHIDDEN(size_t) rtstrFormatType(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char **ppszFormat,
430 va_list *pArgs, int cchWidth, int cchPrecision, unsigned fFlags, char chArgSize)
431{
432 size_t cch;
433 int32_t i;
434 char const *pszTypeEnd;
435 char const *pszType;
436 char ch;
437 void *pvValue = va_arg(*pArgs, void *);
438 NOREF(chArgSize);
439
440 /*
441 * Parse out the type.
442 */
443 pszType = *ppszFormat + 2;
444 *ppszFormat = pszType;
445 Assert(pszType[-1] == '[');
446 Assert(pszType[-2] == 'R');
447 pszTypeEnd = pszType;
448 while ((ch = *pszTypeEnd) != ']')
449 {
450 AssertReturn(ch != '\0', 0);
451 AssertReturn(ch != '%', 0);
452 AssertReturn(ch != '[', 0);
453 pszTypeEnd++;
454 }
455 *ppszFormat = pszTypeEnd + 1;
456
457 /*
458 * Locate the entry and call the handler.
459 */
460 rtstrFormatTypeReadLock();
461
462 i = rtstrFormatTypeLookup(pszType, pszTypeEnd - pszType);
463 if (RT_LIKELY(i >= 0))
464 {
465#ifdef IN_RC
466 PFNRTSTRFORMATTYPE pfnHandler = (PFNRTSTRFORMATTYPE)((intptr_t)&g_aTypes[0] + g_aTypes[i].offHandler);
467#else
468 PFNRTSTRFORMATTYPE pfnHandler = g_aTypes[i].pfnHandler;
469#endif
470 void *pvUser = ASMAtomicReadPtr(&g_aTypes[i].pvUser);
471
472 rtstrFormatTypeReadUnlock();
473
474 cch = pfnHandler(pfnOutput, pvArgOutput, g_aTypes[i].szType, pvValue, cchWidth, cchPrecision, fFlags, pvUser);
475 }
476 else
477 {
478 rtstrFormatTypeReadUnlock();
479
480 cch = pfnOutput(pvArgOutput, RT_STR_TUPLE("<missing:%R["));
481 cch += pfnOutput(pvArgOutput, pszType, pszTypeEnd - pszType);
482 cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("]>"));
483 }
484
485 return cch;
486}
487
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use