1 /* $Id: strformattype.cpp $ */
3 * IPRT - IPRT String Formatter Extensions, Dynamic Types.
7 * Copyright (C) 2008-2016 Oracle Corporation
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
28 /*********************************************************************************************************************************
30 *********************************************************************************************************************************/
31 #define LOG_GROUP RTLOGGROUP_STRING
32 #include <iprt/string.h>
33 #include "internal/iprt.h"
35 #include <iprt/assert.h>
36 #include <iprt/stdarg.h>
38 #include "internal/string.h"
41 /*********************************************************************************************************************************
42 * Defined Constants And Macros *
43 *********************************************************************************************************************************/
45 # define RTSTRFORMATTYPE_WITH_LOCKING
47 #ifdef RTSTRFORMATTYPE_WITH_LOCKING
48 # define RTSTRFORMATTYPE_LOCK_OFFSET 0x7fff0000
52 /*********************************************************************************************************************************
53 * Structures and Typedefs *
54 *********************************************************************************************************************************/
56 * Description of a registered formatting type.
58 * In GC we'll be using offsets instead of pointers just to try avoid having to
59 * do the bothersome relocating. This of course assumes that all the relevant
60 * code stays within the same mapping.
62 typedef struct RTSTRDYNFMT
64 /** The length of the type. */
68 /** The handler function.
69 * In GC the offset is relative to g_aTypes[0], so that &g_aTypes[0] + offHandler
70 * gives the actual address. */
74 PFNRTSTRFORMATTYPE pfnHandler
;
76 /** Callback argument. */
77 void * volatile pvUser
;
79 /** Size alignment padding. */
83 AssertCompileSizeAlignment(RTSTRDYNFMT
, 32);
84 typedef RTSTRDYNFMT
*PRTSTRDYNFMT
;
85 typedef RTSTRDYNFMT
const *PCRTSTRDYNFMT
;
88 /*********************************************************************************************************************************
90 *********************************************************************************************************************************/
91 /** The registered types, sorted for binary lookup.
92 * We use a static array here because it avoids RTMemAlloc dependencies+leaks. */
93 static RTSTRDYNFMT g_aTypes
[64];
94 /** The number of registered types. */
95 static uint32_t g_cTypes
= 0;
96 #ifdef RTSTRFORMATTYPE_WITH_LOCKING
97 /** This is just a thing we assert/spin on.
98 * Zero == unlocked, negative == write locked, positive == read locked.
100 * The user should do all the serialization and we'll smack his fingers in
101 * strict builds if he doesn't. */
102 static int32_t volatile g_i32Spinlock
= 0;
107 * Locks the stuff for updating.
109 * Mostly for check that the caller is doing his job.
111 DECLINLINE(void) rtstrFormatTypeWriteLock(void)
113 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
114 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&g_i32Spinlock
, -RTSTRFORMATTYPE_LOCK_OFFSET
, 0)))
121 && ASMAtomicCmpXchgS32(&g_i32Spinlock
, -RTSTRFORMATTYPE_LOCK_OFFSET
, 0))
129 * Undoing rtstrFormatTypeWriteLock.
131 DECLINLINE(void) rtstrFormatTypeWriteUnlock(void)
133 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
134 Assert(g_i32Spinlock
< 0);
135 ASMAtomicAddS32(&g_i32Spinlock
, RTSTRFORMATTYPE_LOCK_OFFSET
);
141 * Locks the stuff for reading.
143 * This is just cheap stuff to make sure the caller is doing the right thing.
145 DECLINLINE(void) rtstrFormatTypeReadLock(void)
147 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
148 if (RT_UNLIKELY(ASMAtomicIncS32(&g_i32Spinlock
) < 0))
154 if (ASMAtomicUoReadS32(&g_i32Spinlock
) > 0)
162 * Undoing rtstrFormatTypeReadLock.
164 DECLINLINE(void) rtstrFormatTypeReadUnlock(void)
166 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
167 Assert(g_i32Spinlock
> 0);
168 ASMAtomicDecS32(&g_i32Spinlock
);
174 * Compares a type string with a type entry, the string doesn't need to be terminated.
176 * @returns Same as memcmp.
177 * @param pszType The type string, doesn't need to be terminated.
178 * @param cchType The number of chars in @a pszType to compare.
179 * @param pType The type entry to compare with.
181 DECLINLINE(int) rtstrFormatTypeCompare(const char *pszType
, size_t cchType
, PCRTSTRDYNFMT pType
)
183 size_t cch
= RT_MIN(cchType
, pType
->cchType
);
184 int iDiff
= memcmp(pszType
, pType
->szType
, cch
);
187 if (cchType
== pType
->cchType
)
189 iDiff
= cchType
< pType
->cchType
? -1 : 1;
196 * Looks up a type entry.
198 * @returns The type index, -1 on failure.
199 * @param pszType The type to look up. This doesn't have to be terminated.
200 * @param cchType The length of the type.
202 DECLINLINE(int32_t) rtstrFormatTypeLookup(const char *pszType
, size_t cchType
)
205 * Lookup the type - binary search.
208 int32_t iEnd
= g_cTypes
- 1;
209 int32_t i
= iEnd
/ 2;
212 int iDiff
= rtstrFormatTypeCompare(pszType
, cchType
, &g_aTypes
[i
]);
223 i
= iStart
+ (iEnd
- iStart
) / 2;
230 * Register a format handler for a type.
232 * The format handler is used to handle '%R[type]' format types, where the argument
233 * in the vector is a pointer value (a bit restrictive, but keeps it simple).
235 * The caller must ensure that no other thread will be making use of any of
236 * the dynamic formatting type facilities simultaneously with this call.
238 * @returns IPRT status code.
239 * @retval VINF_SUCCESS on success.
240 * @retval VERR_ALREADY_EXISTS if the type has already been registered.
241 * @retval VERR_TOO_MANY_OPEN_FILES if all the type slots has been allocated already.
243 * @param pszType The type name.
244 * @param pfnHandler The handler address. See FNRTSTRFORMATTYPE for details.
245 * @param pvUser The user argument to pass to the handler. See RTStrFormatTypeSetUser
246 * for how to update this later.
248 RTDECL(int) RTStrFormatTypeRegister(const char *pszType
, PFNRTSTRFORMATTYPE pfnHandler
, void *pvUser
)
257 AssertPtr(pfnHandler
);
259 cchType
= strlen(pszType
);
260 AssertReturn(cchType
< RT_SIZEOFMEMB(RTSTRDYNFMT
, szType
), VERR_INVALID_PARAMETER
);
265 rtstrFormatTypeWriteLock();
267 /* check that there are empty slots. */
269 if (cTypes
< RT_ELEMENTS(g_aTypes
))
271 /* find where to insert it. */
276 int iDiff
= rtstrFormatTypeCompare(pszType
, cchType
, &g_aTypes
[i
]);
279 rc
= VERR_ALREADY_EXISTS
;
289 uint32_t cToMove
= cTypes
- i
;
291 memmove(&g_aTypes
[i
+ 1], &g_aTypes
[i
], cToMove
* sizeof(g_aTypes
[i
]));
293 /* insert the new entry. */
294 memset(&g_aTypes
[i
], 0, sizeof(g_aTypes
[i
]));
295 memcpy(&g_aTypes
[i
].szType
[0], pszType
, cchType
+ 1);
296 g_aTypes
[i
].cchType
= (uint8_t)cchType
;
297 g_aTypes
[i
].pvUser
= pvUser
;
299 g_aTypes
[i
].offHandler
= (intptr_t)pfnHandler
- (intptr_t)&g_aTypes
[0];
301 g_aTypes
[i
].pfnHandler
= pfnHandler
;
303 ASMAtomicIncU32(&g_cTypes
);
308 rc
= VERR_TOO_MANY_OPEN_FILES
; /** @todo fix error code */
310 rtstrFormatTypeWriteUnlock();
314 RT_EXPORT_SYMBOL(RTStrFormatTypeRegister
);
318 * Deregisters a format type.
320 * The caller must ensure that no other thread will be making use of any of
321 * the dynamic formatting type facilities simultaneously with this call.
323 * @returns IPRT status code.
324 * @retval VINF_SUCCESS on success.
325 * @retval VERR_FILE_NOT_FOUND if not found.
327 * @param pszType The type to deregister.
329 RTDECL(int) RTStrFormatTypeDeregister(const char *pszType
)
339 * Locate the entry and remove it.
341 rtstrFormatTypeWriteLock();
342 i
= rtstrFormatTypeLookup(pszType
, strlen(pszType
));
345 const uint32_t cTypes
= g_cTypes
;
346 int32_t cToMove
= cTypes
- i
- 1;
348 memmove(&g_aTypes
[i
], &g_aTypes
[i
+ 1], cToMove
* sizeof(g_aTypes
[i
]));
349 memset(&g_aTypes
[cTypes
- 1], 0, sizeof(g_aTypes
[0]));
350 ASMAtomicDecU32(&g_cTypes
);
352 rtstrFormatTypeWriteUnlock();
357 : VERR_FILE_NOT_FOUND
; /** @todo fix status code */
359 RT_EXPORT_SYMBOL(RTStrFormatTypeDeregister
);
363 * Sets the user argument for a type.
365 * This can be used if a user argument needs relocating in GC.
367 * @returns IPRT status code.
368 * @retval VINF_SUCCESS on success.
369 * @retval VERR_FILE_NOT_FOUND if not found.
371 * @param pszType The type to update.
372 * @param pvUser The new user argument value.
374 RTDECL(int) RTStrFormatTypeSetUser(const char *pszType
, void *pvUser
)
384 * Locate the entry and update it.
386 rtstrFormatTypeReadLock();
388 i
= rtstrFormatTypeLookup(pszType
, strlen(pszType
));
390 ASMAtomicWritePtr(&g_aTypes
[i
].pvUser
, pvUser
);
392 rtstrFormatTypeReadUnlock();
397 : VERR_FILE_NOT_FOUND
; /** @todo fix status code */
399 RT_EXPORT_SYMBOL(RTStrFormatTypeSetUser
);
403 * Formats a type using a registered callback handler.
405 * This will handle %R[type].
407 * @returns The number of bytes formatted.
408 * @param pfnOutput Pointer to output function.
409 * @param pvArgOutput Argument for the output function.
410 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
411 * after the format specifier.
412 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
413 * @param cchWidth Format Width. -1 if not specified.
414 * @param cchPrecision Format Precision. -1 if not specified.
415 * @param fFlags Flags (RTSTR_NTFS_*).
416 * @param chArgSize The argument size specifier, 'l' or 'L'.
418 DECLHIDDEN(size_t) rtstrFormatType(PFNRTSTROUTPUT pfnOutput
, void *pvArgOutput
, const char **ppszFormat
,
419 va_list *pArgs
, int cchWidth
, int cchPrecision
, unsigned fFlags
, char chArgSize
)
423 char const *pszTypeEnd
;
426 void *pvValue
= va_arg(*pArgs
, void *);
430 * Parse out the type.
432 pszType
= *ppszFormat
+ 2;
433 *ppszFormat
= pszType
;
434 Assert(pszType
[-1] == '[');
435 Assert(pszType
[-2] == 'R');
436 pszTypeEnd
= pszType
;
437 while ((ch
= *pszTypeEnd
) != ']')
439 AssertReturn(ch
!= '\0', 0);
440 AssertReturn(ch
!= '%', 0);
441 AssertReturn(ch
!= '[', 0);
444 *ppszFormat
= pszTypeEnd
+ 1;
447 * Locate the entry and call the handler.
449 rtstrFormatTypeReadLock();
451 i
= rtstrFormatTypeLookup(pszType
, pszTypeEnd
- pszType
);
452 if (RT_LIKELY(i
>= 0))
455 PFNRTSTRFORMATTYPE pfnHandler
= (PFNRTSTRFORMATTYPE
)((intptr_t)&g_aTypes
[0] + g_aTypes
[i
].offHandler
);
457 PFNRTSTRFORMATTYPE pfnHandler
= g_aTypes
[i
].pfnHandler
;
459 void *pvUser
= ASMAtomicReadPtr(&g_aTypes
[i
].pvUser
);
461 rtstrFormatTypeReadUnlock();
463 cch
= pfnHandler(pfnOutput
, pvArgOutput
, g_aTypes
[i
].szType
, pvValue
, cchWidth
, cchPrecision
, fFlags
, pvUser
);
467 rtstrFormatTypeReadUnlock();
469 cch
= pfnOutput(pvArgOutput
, RT_STR_TUPLE("<missing:%R["));
470 cch
+= pfnOutput(pvArgOutput
, pszType
, pszTypeEnd
- pszType
);
471 cch
+= pfnOutput(pvArgOutput
, RT_STR_TUPLE("]>"));