VirtualBox

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

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

IPRT: Added strip, stripLeft, and stripRight to RTCString.

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