]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - ubuntu/vbox/vboxguest/common/string/strformattype.c
def13a7a6eabfd2d288ffa95bb1055a4e3bd338b
[mirror_ubuntu-bionic-kernel.git] / ubuntu / vbox / vboxguest / common / string / strformattype.c
1 /* $Id: strformattype.cpp $ */
2 /** @file
3 * IPRT - IPRT String Formatter Extensions, Dynamic Types.
4 */
5
6 /*
7 * Copyright (C) 2008-2016 Oracle Corporation
8 *
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.
16 *
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.
22 *
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.
25 */
26
27
28 /*********************************************************************************************************************************
29 * Global Variables *
30 *********************************************************************************************************************************/
31 #define LOG_GROUP RTLOGGROUP_STRING
32 #include <iprt/string.h>
33 #include "internal/iprt.h"
34
35 #include <iprt/assert.h>
36 #include <iprt/stdarg.h>
37 #include <iprt/asm.h>
38 #include "internal/string.h"
39
40
41 /*********************************************************************************************************************************
42 * Defined Constants And Macros *
43 *********************************************************************************************************************************/
44 #ifdef RT_STRICT
45 # define RTSTRFORMATTYPE_WITH_LOCKING
46 #endif
47 #ifdef RTSTRFORMATTYPE_WITH_LOCKING
48 # define RTSTRFORMATTYPE_LOCK_OFFSET 0x7fff0000
49 #endif
50
51
52 /*********************************************************************************************************************************
53 * Structures and Typedefs *
54 *********************************************************************************************************************************/
55 /**
56 * Description of a registered formatting type.
57 *
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.
61 */
62 typedef struct RTSTRDYNFMT
63 {
64 /** The length of the type. */
65 uint8_t cchType;
66 /** The type name. */
67 char szType[47];
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. */
71 #ifdef IN_RC
72 int32_t offHandler;
73 #else
74 PFNRTSTRFORMATTYPE pfnHandler;
75 #endif
76 /** Callback argument. */
77 void * volatile pvUser;
78 #if ARCH_BITS == 32
79 /** Size alignment padding. */
80 char abPadding[8];
81 #endif
82 } RTSTRDYNFMT;
83 AssertCompileSizeAlignment(RTSTRDYNFMT, 32);
84 typedef RTSTRDYNFMT *PRTSTRDYNFMT;
85 typedef RTSTRDYNFMT const *PCRTSTRDYNFMT;
86
87
88 /*********************************************************************************************************************************
89 * Global Variables *
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.
99 *
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;
103 #endif
104
105
106 /**
107 * Locks the stuff for updating.
108 *
109 * Mostly for check that the caller is doing his job.
110 */
111 DECLINLINE(void) rtstrFormatTypeWriteLock(void)
112 {
113 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
114 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0)))
115 {
116 unsigned volatile i;
117
118 AssertFailed();
119 for (i = 0;; i++)
120 if ( !g_i32Spinlock
121 && ASMAtomicCmpXchgS32(&g_i32Spinlock, -RTSTRFORMATTYPE_LOCK_OFFSET, 0))
122 break;
123 }
124 #endif
125 }
126
127
128 /**
129 * Undoing rtstrFormatTypeWriteLock.
130 */
131 DECLINLINE(void) rtstrFormatTypeWriteUnlock(void)
132 {
133 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
134 Assert(g_i32Spinlock < 0);
135 ASMAtomicAddS32(&g_i32Spinlock, RTSTRFORMATTYPE_LOCK_OFFSET);
136 #endif
137 }
138
139
140 /**
141 * Locks the stuff for reading.
142 *
143 * This is just cheap stuff to make sure the caller is doing the right thing.
144 */
145 DECLINLINE(void) rtstrFormatTypeReadLock(void)
146 {
147 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
148 if (RT_UNLIKELY(ASMAtomicIncS32(&g_i32Spinlock) < 0))
149 {
150 unsigned volatile i;
151
152 AssertFailed();
153 for (i = 0;; i++)
154 if (ASMAtomicUoReadS32(&g_i32Spinlock) > 0)
155 break;
156 }
157 #endif
158 }
159
160
161 /**
162 * Undoing rtstrFormatTypeReadLock.
163 */
164 DECLINLINE(void) rtstrFormatTypeReadUnlock(void)
165 {
166 #if defined(RTSTRFORMATTYPE_WITH_LOCKING)
167 Assert(g_i32Spinlock > 0);
168 ASMAtomicDecS32(&g_i32Spinlock);
169 #endif
170 }
171
172
173 /**
174 * Compares a type string with a type entry, the string doesn't need to be terminated.
175 *
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.
180 */
181 DECLINLINE(int) rtstrFormatTypeCompare(const char *pszType, size_t cchType, PCRTSTRDYNFMT pType)
182 {
183 size_t cch = RT_MIN(cchType, pType->cchType);
184 int iDiff = memcmp(pszType, pType->szType, cch);
185 if (!iDiff)
186 {
187 if (cchType == pType->cchType)
188 return 0;
189 iDiff = cchType < pType->cchType ? -1 : 1;
190 }
191 return iDiff;
192 }
193
194
195 /**
196 * Looks up a type entry.
197 *
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.
201 */
202 DECLINLINE(int32_t) rtstrFormatTypeLookup(const char *pszType, size_t cchType)
203 {
204 /*
205 * Lookup the type - binary search.
206 */
207 int32_t iStart = 0;
208 int32_t iEnd = g_cTypes - 1;
209 int32_t i = iEnd / 2;
210 for (;;)
211 {
212 int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]);
213 if (!iDiff)
214 return i;
215 if (iEnd == iStart)
216 break;
217 if (iDiff < 0)
218 iEnd = i - 1;
219 else
220 iStart = i + 1;
221 if (iEnd < iStart)
222 break;
223 i = iStart + (iEnd - iStart) / 2;
224 }
225 return -1;
226 }
227
228
229 /**
230 * Register a format handler for a type.
231 *
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).
234 *
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.
237 *
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.
242 *
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.
247 */
248 RTDECL(int) RTStrFormatTypeRegister(const char *pszType, PFNRTSTRFORMATTYPE pfnHandler, void *pvUser)
249 {
250 int rc;
251 size_t cchType;
252 uint32_t cTypes;
253
254 /*
255 * Validate input.
256 */
257 AssertPtr(pfnHandler);
258 AssertPtr(pszType);
259 cchType = strlen(pszType);
260 AssertReturn(cchType < RT_SIZEOFMEMB(RTSTRDYNFMT, szType), VERR_INVALID_PARAMETER);
261
262 /*
263 * Try add it.
264 */
265 rtstrFormatTypeWriteLock();
266
267 /* check that there are empty slots. */
268 cTypes = g_cTypes;
269 if (cTypes < RT_ELEMENTS(g_aTypes))
270 {
271 /* find where to insert it. */
272 uint32_t i = 0;
273 rc = VINF_SUCCESS;
274 while (i < cTypes)
275 {
276 int iDiff = rtstrFormatTypeCompare(pszType, cchType, &g_aTypes[i]);
277 if (!iDiff)
278 {
279 rc = VERR_ALREADY_EXISTS;
280 break;
281 }
282 if (iDiff < 0)
283 break;
284 i++;
285 }
286 if (RT_SUCCESS(rc))
287 {
288 /* make room. */
289 uint32_t cToMove = cTypes - i;
290 if (cToMove)
291 memmove(&g_aTypes[i + 1], &g_aTypes[i], cToMove * sizeof(g_aTypes[i]));
292
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;
298 #ifdef IN_RC
299 g_aTypes[i].offHandler = (intptr_t)pfnHandler - (intptr_t)&g_aTypes[0];
300 #else
301 g_aTypes[i].pfnHandler = pfnHandler;
302 #endif
303 ASMAtomicIncU32(&g_cTypes);
304 rc = VINF_SUCCESS;
305 }
306 }
307 else
308 rc = VERR_TOO_MANY_OPEN_FILES; /** @todo fix error code */
309
310 rtstrFormatTypeWriteUnlock();
311
312 return rc;
313 }
314 RT_EXPORT_SYMBOL(RTStrFormatTypeRegister);
315
316
317 /**
318 * Deregisters a format type.
319 *
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.
322 *
323 * @returns IPRT status code.
324 * @retval VINF_SUCCESS on success.
325 * @retval VERR_FILE_NOT_FOUND if not found.
326 *
327 * @param pszType The type to deregister.
328 */
329 RTDECL(int) RTStrFormatTypeDeregister(const char *pszType)
330 {
331 int32_t i;
332
333 /*
334 * Validate input.
335 */
336 AssertPtr(pszType);
337
338 /*
339 * Locate the entry and remove it.
340 */
341 rtstrFormatTypeWriteLock();
342 i = rtstrFormatTypeLookup(pszType, strlen(pszType));
343 if (i >= 0)
344 {
345 const uint32_t cTypes = g_cTypes;
346 int32_t cToMove = cTypes - i - 1;
347 if (cToMove > 0)
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);
351 }
352 rtstrFormatTypeWriteUnlock();
353
354 Assert(i >= 0);
355 return i >= 0
356 ? VINF_SUCCESS
357 : VERR_FILE_NOT_FOUND; /** @todo fix status code */
358 }
359 RT_EXPORT_SYMBOL(RTStrFormatTypeDeregister);
360
361
362 /**
363 * Sets the user argument for a type.
364 *
365 * This can be used if a user argument needs relocating in GC.
366 *
367 * @returns IPRT status code.
368 * @retval VINF_SUCCESS on success.
369 * @retval VERR_FILE_NOT_FOUND if not found.
370 *
371 * @param pszType The type to update.
372 * @param pvUser The new user argument value.
373 */
374 RTDECL(int) RTStrFormatTypeSetUser(const char *pszType, void *pvUser)
375 {
376 int32_t i;
377
378 /*
379 * Validate input.
380 */
381 AssertPtr(pszType);
382
383 /*
384 * Locate the entry and update it.
385 */
386 rtstrFormatTypeReadLock();
387
388 i = rtstrFormatTypeLookup(pszType, strlen(pszType));
389 if (i >= 0)
390 ASMAtomicWritePtr(&g_aTypes[i].pvUser, pvUser);
391
392 rtstrFormatTypeReadUnlock();
393
394 Assert(i >= 0);
395 return i >= 0
396 ? VINF_SUCCESS
397 : VERR_FILE_NOT_FOUND; /** @todo fix status code */
398 }
399 RT_EXPORT_SYMBOL(RTStrFormatTypeSetUser);
400
401
402 /**
403 * Formats a type using a registered callback handler.
404 *
405 * This will handle %R[type].
406 *
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'.
417 */
418 DECLHIDDEN(size_t) rtstrFormatType(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char **ppszFormat,
419 va_list *pArgs, int cchWidth, int cchPrecision, unsigned fFlags, char chArgSize)
420 {
421 size_t cch;
422 int32_t i;
423 char const *pszTypeEnd;
424 char const *pszType;
425 char ch;
426 void *pvValue = va_arg(*pArgs, void *);
427 NOREF(chArgSize);
428
429 /*
430 * Parse out the type.
431 */
432 pszType = *ppszFormat + 2;
433 *ppszFormat = pszType;
434 Assert(pszType[-1] == '[');
435 Assert(pszType[-2] == 'R');
436 pszTypeEnd = pszType;
437 while ((ch = *pszTypeEnd) != ']')
438 {
439 AssertReturn(ch != '\0', 0);
440 AssertReturn(ch != '%', 0);
441 AssertReturn(ch != '[', 0);
442 pszTypeEnd++;
443 }
444 *ppszFormat = pszTypeEnd + 1;
445
446 /*
447 * Locate the entry and call the handler.
448 */
449 rtstrFormatTypeReadLock();
450
451 i = rtstrFormatTypeLookup(pszType, pszTypeEnd - pszType);
452 if (RT_LIKELY(i >= 0))
453 {
454 #ifdef IN_RC
455 PFNRTSTRFORMATTYPE pfnHandler = (PFNRTSTRFORMATTYPE)((intptr_t)&g_aTypes[0] + g_aTypes[i].offHandler);
456 #else
457 PFNRTSTRFORMATTYPE pfnHandler = g_aTypes[i].pfnHandler;
458 #endif
459 void *pvUser = ASMAtomicReadPtr(&g_aTypes[i].pvUser);
460
461 rtstrFormatTypeReadUnlock();
462
463 cch = pfnHandler(pfnOutput, pvArgOutput, g_aTypes[i].szType, pvValue, cchWidth, cchPrecision, fFlags, pvUser);
464 }
465 else
466 {
467 rtstrFormatTypeReadUnlock();
468
469 cch = pfnOutput(pvArgOutput, RT_STR_TUPLE("<missing:%R["));
470 cch += pfnOutput(pvArgOutput, pszType, pszTypeEnd - pszType);
471 cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("]>"));
472 }
473
474 return cch;
475 }
476