VirtualBox

source: vbox/trunk/src/VBox/Main/glue/string.cpp@ 80793

Last change on this file since 80793 was 80793, checked in by vboxsync, 6 years ago

Bstr: Added printf, printfNoThrow, printfV and printfVNoThrow methods (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: string.cpp 80793 2019-09-15 11:27:47Z vboxsync $ */
2/** @file
3 * MS COM / XPCOM Abstraction Layer - UTF-8 and UTF-16 string classes.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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
18#include "VBox/com/string.h"
19
20#include <iprt/err.h>
21#include <iprt/path.h>
22#include <iprt/log.h>
23#include <iprt/string.h>
24#include <iprt/uni.h>
25
26namespace com
27{
28
29// BSTR representing a null wide char with 32 bits of length prefix (0);
30// this will work on Windows as well as other platforms where BSTR does
31// not use length prefixes
32const OLECHAR g_achEmptyBstr[3] = { 0, 0, 0 };
33const BSTR g_bstrEmpty = (BSTR)&g_achEmptyBstr[2];
34
35/* static */
36const Bstr Bstr::Empty; /* default ctor is OK */
37
38
39Bstr &Bstr::printf(const char *pszFormat, ...)
40{
41 va_list va;
42 va_start(va, pszFormat);
43 HRESULT hrc = printfVNoThrow(pszFormat, va);
44 va_end(va);
45 if (hrc == S_OK)
46 { /* likely */ }
47 else
48 throw std::bad_alloc();
49 return *this;
50}
51
52HRESULT Bstr::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
53{
54 va_list va;
55 va_start(va, pszFormat);
56 HRESULT hrc = printfVNoThrow(pszFormat, va);
57 va_end(va);
58 return hrc;
59}
60
61
62Bstr &Bstr::printfV(const char *pszFormat, va_list va)
63{
64 HRESULT hrc = printfVNoThrow(pszFormat, va);
65 if (hrc == S_OK)
66 { /* likely */ }
67 else
68 throw std::bad_alloc();
69 return *this;
70}
71
72struct BSTRNOTHROW
73{
74 Bstr *pThis;
75 size_t cwcAlloc;
76 size_t offDst;
77 HRESULT hrc;
78};
79
80/**
81 * Callback used with RTStrFormatV by Bstr::printfVNoThrow.
82 *
83 * @returns The number of bytes added (not used).
84 *
85 * @param pvArg Pointer to a BSTRNOTHROW structure.
86 * @param pachChars The characters to append.
87 * @param cbChars The number of characters. 0 on the final callback.
88 */
89/*static*/ DECLCALLBACK(size_t)
90Bstr::printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT
91{
92 BSTRNOTHROW *pArgs = (BSTRNOTHROW *)pvArg;
93 if (cbChars)
94 {
95 size_t cwcAppend;
96 int rc = ::RTStrCalcUtf16LenEx(pachChars, cbChars, &cwcAppend);
97 AssertRCReturnStmt(rc, pArgs->hrc = E_UNEXPECTED, 0);
98
99 /*
100 * Ensure we've got sufficient memory.
101 */
102 Bstr *pThis = pArgs->pThis;
103 size_t const cwcBoth = pArgs->offDst + cwcAppend;
104 if (cwcBoth >= pArgs->cwcAlloc)
105 {
106 if (pArgs->hrc == S_OK)
107 {
108 /* Double the buffer size, if it's less that _1M. Align sizes like
109 for append. */
110 size_t cwcAlloc = RT_ALIGN_Z(pArgs->cwcAlloc, 128);
111 cwcAlloc += RT_MIN(cwcAlloc, _1M);
112 if (cwcAlloc <= cwcBoth)
113 cwcAlloc = RT_ALIGN_Z(cwcBoth + 1, 512);
114 if (pArgs->cwcAlloc)
115 AssertMsgReturnStmt(::SysReAllocStringLen(&pThis->m_bstr, NULL, (unsigned)cwcAlloc),
116 ("cwcAlloc=%#zx\n", cwcAlloc), pArgs->hrc = E_OUTOFMEMORY, 0);
117 else
118 {
119 Assert(pThis->m_bstr == NULL);
120 pThis->m_bstr = ::SysAllocStringLen(NULL, (unsigned)cwcAlloc);
121 AssertMsgReturnStmt(pThis->m_bstr, ("cwcAlloc=%#zx\n", cwcAlloc), pArgs->hrc = E_OUTOFMEMORY, 0);
122 }
123 pArgs->cwcAlloc = cwcAlloc;
124 }
125 else
126 return 0;
127 }
128
129 /*
130 * Do the conversion.
131 */
132 PRTUTF16 pwszDst = pThis->m_bstr + pArgs->offDst;
133 Assert(pArgs->cwcAlloc > pArgs->offDst);
134 rc = ::RTStrToUtf16Ex(pachChars, cbChars, &pwszDst, pArgs->cwcAlloc - pArgs->offDst, &cwcAppend);
135 AssertRCReturnStmt(rc, pArgs->hrc = E_UNEXPECTED, 0);
136 }
137 return cbChars;
138}
139
140HRESULT Bstr::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
141{
142 cleanup();
143 BSTRNOTHROW Args = { this, 0, 0, S_OK };
144 RTStrFormatV(printfOutputCallbackNoThrow, this, NULL, NULL, pszFormat, va);
145 if (Args.hrc == S_OK)
146 {
147#ifdef RT_OS_WINDOWS
148 if (Args.offDst + 1 == Args.cwcAlloc)
149 return S_OK;
150
151 /* Official way: Reallocate the string. We could of course just update the size-prefix if we dare... */
152 if (SysReAllocStringLen(&m_bstr, NULL, (unsigned)(Args.offDst + 1)))
153 return S_OK;
154 AssertMsgFailed(("offDst=%#zx\n", Args.offDst));
155 Args.hrc = E_OUTOFMEMORY;
156#else
157 return S_OK;
158#endif
159 }
160 cleanup();
161 return Args.hrc;
162}
163
164void Bstr::copyFromN(const char *a_pszSrc, size_t a_cchMax)
165{
166 /*
167 * Initialize m_bstr first in case of throws further down in the code, then
168 * check for empty input (m_bstr == NULL means empty, there are no NULL
169 * strings).
170 */
171 m_bstr = NULL;
172 if (!a_cchMax || !a_pszSrc || !*a_pszSrc)
173 return;
174
175 /*
176 * Calculate the length and allocate a BSTR string buffer of the right
177 * size, i.e. optimize heap usage.
178 */
179 size_t cwc;
180 int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc);
181 if (RT_SUCCESS(vrc))
182 {
183 m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR)));
184 if (RT_LIKELY(m_bstr))
185 {
186 PRTUTF16 pwsz = (PRTUTF16)m_bstr;
187 vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL);
188 if (RT_SUCCESS(vrc))
189 return;
190
191 /* This should not happen! */
192 AssertRC(vrc);
193 cleanup();
194 }
195 }
196 else /* ASSUME: input is valid Utf-8. Fake out of memory error. */
197 AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc));
198 throw std::bad_alloc();
199}
200
201int Bstr::compareUtf8(const char *a_pszRight, CaseSensitivity a_enmCase /*= CaseSensitive*/) const
202{
203 PCRTUTF16 pwszLeft = m_bstr;
204
205 /*
206 * Special case for null/empty strings. Unlike RTUtf16Cmp we
207 * treat null and empty equally.
208 */
209 if (!pwszLeft)
210 return !a_pszRight || *a_pszRight == '\0' ? 0 : -1;
211 if (!a_pszRight)
212 return *pwszLeft == '\0' ? 0 : 1;
213
214 /*
215 * Compare with a UTF-8 string by enumerating them char by char.
216 */
217 for (;;)
218 {
219 RTUNICP ucLeft;
220 int rc = RTUtf16GetCpEx(&pwszLeft, &ucLeft);
221 AssertRCReturn(rc, 1);
222
223 RTUNICP ucRight;
224 rc = RTStrGetCpEx(&a_pszRight, &ucRight);
225 AssertRCReturn(rc, -1);
226 if (ucLeft == ucRight)
227 {
228 if (ucLeft)
229 continue;
230 return 0;
231 }
232
233 if (a_enmCase == CaseInsensitive)
234 {
235 if (RTUniCpToUpper(ucLeft) == RTUniCpToUpper(ucRight))
236 continue;
237 if (RTUniCpToLower(ucLeft) == RTUniCpToLower(ucRight))
238 continue;
239 }
240
241 return ucLeft < ucRight ? -1 : 1;
242 }
243}
244
245
246/* static */
247const Utf8Str Utf8Str::Empty; /* default ctor is OK */
248
249#if defined(VBOX_WITH_XPCOM)
250void Utf8Str::cloneTo(char **pstr) const
251{
252 size_t cb = length() + 1;
253 *pstr = (char *)nsMemory::Alloc(cb);
254 if (RT_LIKELY(*pstr))
255 memcpy(*pstr, c_str(), cb);
256 else
257 throw std::bad_alloc();
258}
259
260HRESULT Utf8Str::cloneToEx(char **pstr) const
261{
262 size_t cb = length() + 1;
263 *pstr = (char *)nsMemory::Alloc(cb);
264 if (RT_LIKELY(*pstr))
265 {
266 memcpy(*pstr, c_str(), cb);
267 return S_OK;
268 }
269 return E_OUTOFMEMORY;
270}
271#endif
272
273Utf8Str& Utf8Str::stripTrailingSlash()
274{
275 if (length())
276 {
277 ::RTPathStripTrailingSlash(m_psz);
278 jolt();
279 }
280 return *this;
281}
282
283Utf8Str& Utf8Str::stripFilename()
284{
285 if (length())
286 {
287 RTPathStripFilename(m_psz);
288 jolt();
289 }
290 return *this;
291}
292
293Utf8Str& Utf8Str::stripPath()
294{
295 if (length())
296 {
297 char *pszName = ::RTPathFilename(m_psz);
298 if (pszName)
299 {
300 size_t cchName = length() - (pszName - m_psz);
301 memmove(m_psz, pszName, cchName + 1);
302 jolt();
303 }
304 else
305 cleanup();
306 }
307 return *this;
308}
309
310Utf8Str& Utf8Str::stripSuffix()
311{
312 if (length())
313 {
314 RTPathStripSuffix(m_psz);
315 jolt();
316 }
317 return *this;
318}
319
320size_t Utf8Str::parseKeyValue(Utf8Str &a_rKey, Utf8Str &a_rValue, size_t a_offStart /* = 0*/,
321 const Utf8Str &a_rPairSeparator /*= ","*/, const Utf8Str &a_rKeyValueSeparator /*= "="*/) const
322{
323 /* Find the end of the next pair, skipping empty pairs.
324 Note! The skipping allows us to pass the return value of a parseKeyValue()
325 call as offStart to the next call. */
326 size_t offEnd;
327 while ( a_offStart == (offEnd = find(&a_rPairSeparator, a_offStart))
328 && offEnd != npos)
329 a_offStart++;
330
331 /* Look for a key/value separator before the end of the pair.
332 ASSUMES npos value returned by find when the substring is not found is
333 really high. */
334 size_t offKeyValueSep = find(&a_rKeyValueSeparator, a_offStart);
335 if (offKeyValueSep < offEnd)
336 {
337 a_rKey = substr(a_offStart, offKeyValueSep - a_offStart);
338 if (offEnd == npos)
339 offEnd = m_cch; /* No confusing npos when returning strings. */
340 a_rValue = substr(offKeyValueSep + 1, offEnd - offKeyValueSep - 1);
341 }
342 else
343 {
344 a_rKey.setNull();
345 a_rValue.setNull();
346 }
347
348 return offEnd;
349}
350
351/**
352 * Internal function used in Utf8Str copy constructors and assignment when
353 * copying from a UTF-16 string.
354 *
355 * As with the RTCString::copyFrom() variants, this unconditionally sets the
356 * members to a copy of the given other strings and makes no assumptions about
357 * previous contents. This can therefore be used both in copy constructors,
358 * when member variables have no defined value, and in assignments after having
359 * called cleanup().
360 *
361 * This variant converts from a UTF-16 string, most probably from
362 * a Bstr assignment.
363 *
364 * @param a_pbstr The source string. The caller guarantees that this
365 * is valid UTF-16.
366 * @param a_cwcMax The number of characters to be copied. If set to RTSTR_MAX,
367 * the entire string will be copied.
368 *
369 * @sa RTCString::copyFromN
370 */
371void Utf8Str::copyFrom(CBSTR a_pbstr, size_t a_cwcMax)
372{
373 if (a_pbstr && *a_pbstr)
374 {
375 int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr,
376 a_cwcMax, // size_t cwcString: translate entire string
377 &m_psz, // char **ppsz: output buffer
378 0, // size_t cch: if 0, func allocates buffer in *ppsz
379 &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator.
380 if (RT_SUCCESS(vrc))
381 m_cbAllocated = m_cch + 1;
382 else
383 {
384 if ( vrc != VERR_NO_STR_MEMORY
385 && vrc != VERR_NO_MEMORY)
386 {
387 /* ASSUME: input is valid Utf-16. Fake out of memory error. */
388 AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr));
389 }
390
391 m_cch = 0;
392 m_cbAllocated = 0;
393 m_psz = NULL;
394
395 throw std::bad_alloc();
396 }
397 }
398 else
399 {
400 m_cch = 0;
401 m_cbAllocated = 0;
402 m_psz = NULL;
403 }
404}
405
406/**
407 * A variant of Utf8Str::copyFrom that does not throw any exceptions but returns
408 * E_OUTOFMEMORY instead.
409 *
410 * @param a_pbstr The source string.
411 * @returns S_OK or E_OUTOFMEMORY.
412 */
413HRESULT Utf8Str::copyFromEx(CBSTR a_pbstr)
414{
415 if (a_pbstr && *a_pbstr)
416 {
417 int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr,
418 RTSTR_MAX, // size_t cwcString: translate entire string
419 &m_psz, // char **ppsz: output buffer
420 0, // size_t cch: if 0, func allocates buffer in *ppsz
421 &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator.
422 if (RT_SUCCESS(vrc))
423 m_cbAllocated = m_cch + 1;
424 else
425 {
426 if ( vrc != VERR_NO_STR_MEMORY
427 && vrc != VERR_NO_MEMORY)
428 {
429 /* ASSUME: input is valid Utf-16. Fake out of memory error. */
430 AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr));
431 }
432
433 m_cch = 0;
434 m_cbAllocated = 0;
435 m_psz = NULL;
436
437 return E_OUTOFMEMORY;
438 }
439 }
440 else
441 {
442 m_cch = 0;
443 m_cbAllocated = 0;
444 m_psz = NULL;
445 }
446 return S_OK;
447}
448
449
450/**
451 * A variant of Utf8Str::copyFromN that does not throw any exceptions but
452 * returns E_OUTOFMEMORY instead.
453 *
454 * @param a_pcszSrc The source string.
455 * @param a_offSrc Start offset to copy from.
456 * @param a_cchSrc How much to copy
457 * @returns S_OK or E_OUTOFMEMORY.
458 *
459 * @remarks This calls cleanup() first, so the caller doesn't have to. (Saves
460 * code space.)
461 */
462HRESULT Utf8Str::copyFromExNComRC(const char *a_pcszSrc, size_t a_offSrc, size_t a_cchSrc)
463{
464 Assert(!a_cchSrc || !m_psz || (uintptr_t)&a_pcszSrc[a_offSrc] - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
465 cleanup();
466 if (a_cchSrc)
467 {
468 m_psz = RTStrAlloc(a_cchSrc + 1);
469 if (RT_LIKELY(m_psz))
470 {
471 m_cch = a_cchSrc;
472 m_cbAllocated = a_cchSrc + 1;
473 memcpy(m_psz, a_pcszSrc + a_offSrc, a_cchSrc);
474 m_psz[a_cchSrc] = '\0';
475 }
476 else
477 {
478 m_cch = 0;
479 m_cbAllocated = 0;
480 return E_OUTOFMEMORY;
481 }
482 }
483 else
484 {
485 m_cch = 0;
486 m_cbAllocated = 0;
487 m_psz = NULL;
488 }
489 return S_OK;
490}
491
492} /* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette