VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/ministring.cpp@ 67645

Last change on this file since 67645 was 67645, checked in by vboxsync, 8 years ago

IPRT: RTCString::find: Made it return npos if the string to search for is empty or NULL (strstr will return the first string if empty and crash if NULL). Nobody seems to try search for empty strings. Added a find method taking a string object.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: ministring.cpp 67645 2017-06-27 16:36:58Z vboxsync $ */
2/** @file
3 * IPRT - Mini C++ string class.
4 *
5 * This is a base for both Utf8Str and other places where IPRT may want to use
6 * a lean C++ string class.
7 */
8
9/*
10 * Copyright (C) 2007-2016 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 *
20 * The contents of this file may alternatively be used under the terms
21 * of the Common Development and Distribution License Version 1.0
22 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
23 * VirtualBox OSE distribution, in which case the provisions of the
24 * CDDL are applicable instead of those of the GPL.
25 *
26 * You may elect to license modified versions of this file under the
27 * terms and conditions of either the GPL or the CDDL or both.
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#include <iprt/cpp/ministring.h>
35
36
37/*********************************************************************************************************************************
38* Global Variables *
39*********************************************************************************************************************************/
40const size_t RTCString::npos = ~(size_t)0;
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46/** Allocation block alignment used when appending bytes to a string. */
47#define IPRT_MINISTRING_APPEND_ALIGNMENT 64
48
49
50RTCString &RTCString::printf(const char *pszFormat, ...)
51{
52 va_list va;
53 va_start(va, pszFormat);
54 printfV(pszFormat, va);
55 va_end(va);
56 return *this;
57}
58
59/**
60 * Callback used with RTStrFormatV by RTCString::printfV.
61 *
62 * @returns The number of bytes added (not used).
63 *
64 * @param pvArg The string object.
65 * @param pachChars The characters to append.
66 * @param cbChars The number of characters. 0 on the final callback.
67 */
68/*static*/ DECLCALLBACK(size_t)
69RTCString::printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars)
70{
71 RTCString *pThis = (RTCString *)pvArg;
72 if (cbChars)
73 {
74 size_t cchBoth = pThis->m_cch + cbChars;
75 if (cchBoth >= pThis->m_cbAllocated)
76 {
77 /* Double the buffer size, if it's less that _4M. Align sizes like
78 for append. */
79 size_t cbAlloc = RT_ALIGN_Z(pThis->m_cbAllocated, IPRT_MINISTRING_APPEND_ALIGNMENT);
80 cbAlloc += RT_MIN(cbAlloc, _4M);
81 if (cbAlloc <= cchBoth)
82 cbAlloc = RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT);
83 pThis->reserve(cbAlloc);
84#ifndef RT_EXCEPTIONS_ENABLED
85 AssertReleaseReturn(pThis->capacity() > cchBoth, 0);
86#endif
87 }
88
89 memcpy(&pThis->m_psz[pThis->m_cch], pachChars, cbChars);
90 pThis->m_cch = cchBoth;
91 pThis->m_psz[cchBoth] = '\0';
92 }
93 return cbChars;
94}
95
96RTCString &RTCString::printfV(const char *pszFormat, va_list va)
97{
98 cleanup();
99 RTStrFormatV(printfOutputCallback, this, NULL, NULL, pszFormat, va);
100 return *this;
101}
102
103RTCString &RTCString::append(const RTCString &that)
104{
105 size_t cchThat = that.length();
106 if (cchThat)
107 {
108 size_t cchThis = length();
109 size_t cchBoth = cchThis + cchThat;
110
111 if (cchBoth >= m_cbAllocated)
112 {
113 reserve(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
114 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
115#ifndef RT_EXCEPTIONS_ENABLED
116 AssertRelease(capacity() > cchBoth);
117#endif
118 }
119
120 memcpy(m_psz + cchThis, that.m_psz, cchThat);
121 m_psz[cchBoth] = '\0';
122 m_cch = cchBoth;
123 }
124 return *this;
125}
126
127RTCString &RTCString::append(const char *pszThat)
128{
129 size_t cchThat = strlen(pszThat);
130 if (cchThat)
131 {
132 size_t cchThis = length();
133 size_t cchBoth = cchThis + cchThat;
134
135 if (cchBoth >= m_cbAllocated)
136 {
137 reserve(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
138 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
139#ifndef RT_EXCEPTIONS_ENABLED
140 AssertRelease(capacity() > cchBoth);
141#endif
142 }
143
144 memcpy(&m_psz[cchThis], pszThat, cchThat);
145 m_psz[cchBoth] = '\0';
146 m_cch = cchBoth;
147 }
148 return *this;
149}
150
151RTCString& RTCString::append(char ch)
152{
153 Assert((unsigned char)ch < 0x80); /* Don't create invalid UTF-8. */
154 if (ch)
155 {
156 // allocate in chunks of 20 in case this gets called several times
157 if (m_cch + 1 >= m_cbAllocated)
158 {
159 reserve(RT_ALIGN_Z(m_cch + 2, IPRT_MINISTRING_APPEND_ALIGNMENT));
160 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
161#ifndef RT_EXCEPTIONS_ENABLED
162 AssertRelease(capacity() > m_cch + 1);
163#endif
164 }
165
166 m_psz[m_cch] = ch;
167 m_psz[++m_cch] = '\0';
168 }
169 return *this;
170}
171
172RTCString &RTCString::appendCodePoint(RTUNICP uc)
173{
174 /*
175 * Single byte encoding.
176 */
177 if (uc < 0x80)
178 return RTCString::append((char)uc);
179
180 /*
181 * Multibyte encoding.
182 * Assume max encoding length when resizing the string, that's simpler.
183 */
184 AssertReturn(uc <= UINT32_C(0x7fffffff), *this);
185
186 if (m_cch + 6 >= m_cbAllocated)
187 {
188 reserve(RT_ALIGN_Z(m_cch + 6 + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
189 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
190#ifndef RT_EXCEPTIONS_ENABLED
191 AssertRelease(capacity() > m_cch + 6);
192#endif
193 }
194
195 char *pszNext = RTStrPutCp(&m_psz[m_cch], uc);
196 m_cch = pszNext - m_psz;
197 *pszNext = '\0';
198
199 return *this;
200}
201
202size_t RTCString::find(const char *pszNeedle, size_t offStart /*= 0*/) const
203{
204 if (offStart < length())
205 {
206 const char *pszThis = c_str();
207 if (pszThis)
208 {
209 if (pszNeedle && *pszNeedle != '\0')
210 {
211 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
212 if (pszHit)
213 return pszHit - pszThis;
214 }
215 }
216 }
217
218 return npos;
219}
220
221size_t RTCString::find(const RTCString *pStrNeedle, size_t offStart /*= 0*/) const
222{
223 if (offStart < length())
224 {
225 const char *pszThis = c_str();
226 if (pszThis)
227 {
228 if (pStrNeedle)
229 {
230 const char *pszNeedle = pStrNeedle->c_str();
231 if (pszNeedle && *pszNeedle != '\0')
232 {
233 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
234 if (pszHit)
235 return pszHit - pszThis;
236 }
237 }
238 }
239 }
240
241 return npos;
242}
243
244void RTCString::findReplace(char chFind, char chReplace)
245{
246 Assert((unsigned int)chFind < 128U);
247 Assert((unsigned int)chReplace < 128U);
248
249 for (size_t i = 0; i < length(); ++i)
250 {
251 char *p = &m_psz[i];
252 if (*p == chFind)
253 *p = chReplace;
254 }
255}
256
257size_t RTCString::count(char ch) const
258{
259 Assert((unsigned int)ch < 128U);
260
261 size_t c = 0;
262 const char *psz = m_psz;
263 if (psz)
264 {
265 char chCur;
266 while ((chCur = *psz++) != '\0')
267 if (chCur == ch)
268 c++;
269 }
270 return c;
271}
272
273#if 0 /** @todo implement these when needed. */
274size_t RTCString::count(const char *psz, CaseSensitivity cs = CaseSensitive) const
275{
276}
277
278size_t RTCString::count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const
279{
280
281}
282#endif
283
284RTCString RTCString::substrCP(size_t pos /*= 0*/, size_t n /*= npos*/) const
285{
286 RTCString ret;
287
288 if (n)
289 {
290 const char *psz;
291
292 if ((psz = c_str()))
293 {
294 RTUNICP cp;
295
296 // walk the UTF-8 characters until where the caller wants to start
297 size_t i = pos;
298 while (*psz && i--)
299 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
300 return ret; // return empty string on bad encoding
301
302 const char *pFirst = psz;
303
304 if (n == npos)
305 // all the rest:
306 ret = pFirst;
307 else
308 {
309 i = n;
310 while (*psz && i--)
311 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
312 return ret; // return empty string on bad encoding
313
314 size_t cbCopy = psz - pFirst;
315 if (cbCopy)
316 {
317 ret.reserve(cbCopy + 1); // may throw bad_alloc
318#ifndef RT_EXCEPTIONS_ENABLED
319 AssertRelease(capacity() >= cbCopy + 1);
320#endif
321 memcpy(ret.m_psz, pFirst, cbCopy);
322 ret.m_cch = cbCopy;
323 ret.m_psz[cbCopy] = '\0';
324 }
325 }
326 }
327 }
328
329 return ret;
330}
331
332bool RTCString::endsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
333{
334 size_t l1 = length();
335 if (l1 == 0)
336 return false;
337
338 size_t l2 = that.length();
339 if (l1 < l2)
340 return false;
341 /** @todo r=bird: If l2 is 0, then m_psz can be NULL and we will crash. See
342 * also handling of l2 == in startsWith. */
343
344 size_t l = l1 - l2;
345 if (cs == CaseSensitive)
346 return ::RTStrCmp(&m_psz[l], that.m_psz) == 0;
347 return ::RTStrICmp(&m_psz[l], that.m_psz) == 0;
348}
349
350bool RTCString::startsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
351{
352 size_t l1 = length();
353 size_t l2 = that.length();
354 if (l1 == 0 || l2 == 0) /** @todo r=bird: this differs from endsWith, and I think other IPRT code. If l2 == 0, it matches anything. */
355 return false;
356
357 if (l1 < l2)
358 return false;
359
360 if (cs == CaseSensitive)
361 return ::RTStrNCmp(m_psz, that.m_psz, l2) == 0;
362 return ::RTStrNICmp(m_psz, that.m_psz, l2) == 0;
363}
364
365bool RTCString::contains(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
366{
367 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
368 * endsWith only does half way). */
369 if (cs == CaseSensitive)
370 return ::RTStrStr(m_psz, that.m_psz) != NULL;
371 return ::RTStrIStr(m_psz, that.m_psz) != NULL;
372}
373
374int RTCString::toInt(uint64_t &i) const
375{
376 if (!m_psz)
377 return VERR_NO_DIGITS;
378 return RTStrToUInt64Ex(m_psz, NULL, 0, &i);
379}
380
381int RTCString::toInt(uint32_t &i) const
382{
383 if (!m_psz)
384 return VERR_NO_DIGITS;
385 return RTStrToUInt32Ex(m_psz, NULL, 0, &i);
386}
387
388RTCList<RTCString, RTCString *>
389RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const
390{
391 RTCList<RTCString> strRet;
392 if (!m_psz)
393 return strRet;
394 if (a_rstrSep.isEmpty())
395 {
396 strRet.append(RTCString(m_psz));
397 return strRet;
398 }
399
400 size_t cch = m_cch;
401 char const *pszTmp = m_psz;
402 while (cch > 0)
403 {
404 char const *pszNext = strstr(pszTmp, a_rstrSep.c_str());
405 if (!pszNext)
406 {
407 strRet.append(RTCString(pszTmp, cch));
408 break;
409 }
410 size_t cchNext = pszNext - pszTmp;
411 if ( cchNext > 0
412 || mode == KeepEmptyParts)
413 strRet.append(RTCString(pszTmp, cchNext));
414 pszTmp += cchNext + a_rstrSep.length();
415 cch -= cchNext + a_rstrSep.length();
416 }
417
418 return strRet;
419}
420
421/* static */
422RTCString
423RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList,
424 const RTCString &a_rstrPrefix /* = "" */,
425 const RTCString &a_rstrSep /* = "" */)
426{
427 RTCString strRet;
428 if (a_rList.size() > 1)
429 {
430 /* calc the required size */
431 size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1;
432 cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1;
433 for (size_t i = 0; i < a_rList.size(); ++i)
434 cbNeeded += a_rList.at(i).length();
435 strRet.reserve(cbNeeded);
436
437 /* do the appending. */
438 for (size_t i = 0; i < a_rList.size() - 1; ++i)
439 {
440 if (a_rstrPrefix.isNotEmpty())
441 strRet.append(a_rstrPrefix);
442 strRet.append(a_rList.at(i));
443 strRet.append(a_rstrSep);
444 }
445 strRet.append(a_rList.last());
446 }
447 /* special case: one list item. */
448 else if (a_rList.size() > 0)
449 {
450 if (a_rstrPrefix.isNotEmpty())
451 strRet.append(a_rstrPrefix);
452 strRet.append(a_rList.last());
453 }
454
455 return strRet;
456}
457
458/* static */
459RTCString
460RTCString::join(const RTCList<RTCString, RTCString *> &a_rList,
461 const RTCString &a_rstrSep /* = "" */)
462{
463 return RTCString::joinEx(a_rList,
464 "" /* a_rstrPrefix */, a_rstrSep);
465}
466
467const RTCString operator+(const RTCString &a_rStr1, const RTCString &a_rStr2)
468{
469 RTCString strRet(a_rStr1);
470 strRet += a_rStr2;
471 return strRet;
472}
473
474const RTCString operator+(const RTCString &a_rStr1, const char *a_pszStr2)
475{
476 RTCString strRet(a_rStr1);
477 strRet += a_pszStr2;
478 return strRet;
479}
480
481const RTCString operator+(const char *a_psz1, const RTCString &a_rStr2)
482{
483 RTCString strRet(a_psz1);
484 strRet += a_rStr2;
485 return strRet;
486}
487
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