VirtualBox

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

Last change on this file since 67674 was 67674, checked in by vboxsync, 7 years ago

RTCString: Added append methods for appending a substring.

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

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