VirtualBox

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

最後變更 在這個檔案從67789是 67688,由 vboxsync 提交於 8 年 前

RTCString: Added 4 standard like replace() methods.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 17.8 KB
 
1/* $Id: ministring.cpp 67688 2017-06-29 11:46:07Z 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.alldomusa.eu.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
204
205RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement)
206{
207 return replaceWorker(offStart, cchLength, rStrReplacement.c_str(), rStrReplacement.length());
208}
209
210RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
211 size_t offReplacement, size_t cchReplacement)
212{
213 Assert(this != &rStrReplacement);
214 if (cchReplacement > 0)
215 {
216 if (offReplacement < rStrReplacement.length())
217 {
218 size_t cchMaxReplacement = rStrReplacement.length() - offReplacement;
219 return replaceWorker(offStart, cchLength, rStrReplacement.c_str() + offReplacement,
220 RT_MIN(cchReplacement, cchMaxReplacement));
221 }
222 /* Our non-standard handling of out_of_range situations. */
223 AssertMsgFailed(("offReplacement=%zu (cchReplacement=%zu) rStrReplacement.length()=%zu\n",
224 offReplacement, cchReplacement, rStrReplacement.length()));
225 }
226 return replaceWorker(offStart, cchLength, "", 0);
227}
228
229RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement)
230{
231 return replaceWorker(offStart, cchLength, pszReplacement, strlen(pszReplacement));
232}
233
234RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement)
235{
236 return replaceWorker(offStart, cchLength, pszReplacement, RTStrNLen(pszReplacement, cchReplacement));
237}
238
239RTCString &RTCString::replaceWorker(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc)
240{
241 /*
242 * Our non-standard handling of out_of_range situations.
243 */
244 size_t const cchOldLength = length();
245 AssertMsgReturn(offStart < cchOldLength, ("offStart=%zu (cchLength=%zu); length()=%zu\n", offStart, cchLength, cchOldLength),
246 *this);
247
248 /*
249 * Correct the length parameter.
250 */
251 size_t cchMaxLength = cchOldLength - offStart;
252 if (cchMaxLength < cchLength)
253 cchLength = cchMaxLength;
254
255 /*
256 * Adjust string allocation if necessary.
257 */
258 size_t cchNew = cchOldLength - cchLength + cchSrc;
259 if (cchNew >= m_cbAllocated)
260 {
261 reserve(RT_ALIGN_Z(cchNew + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
262 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
263#ifndef RT_EXCEPTIONS_ENABLED
264 AssertRelease(capacity() > cchNew);
265#endif
266 }
267
268 /*
269 * Make the change.
270 */
271 size_t cchAfter = cchOldLength - offStart - cchLength;
272 if (cchAfter > 0)
273 memmove(&m_psz[offStart + cchSrc], &m_psz[offStart + cchLength], cchAfter);
274 memcpy(&m_psz[offStart], pszSrc, cchSrc);
275 m_psz[cchNew] = '\0';
276 m_cch = cchNew;
277
278 return *this;
279}
280
281
282size_t RTCString::find(const char *pszNeedle, size_t offStart /*= 0*/) const
283{
284 if (offStart < length())
285 {
286 const char *pszThis = c_str();
287 if (pszThis)
288 {
289 if (pszNeedle && *pszNeedle != '\0')
290 {
291 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
292 if (pszHit)
293 return pszHit - pszThis;
294 }
295 }
296 }
297
298 return npos;
299}
300
301size_t RTCString::find(const RTCString *pStrNeedle, size_t offStart /*= 0*/) const
302{
303 if (offStart < length())
304 {
305 const char *pszThis = c_str();
306 if (pszThis)
307 {
308 if (pStrNeedle)
309 {
310 const char *pszNeedle = pStrNeedle->c_str();
311 if (pszNeedle && *pszNeedle != '\0')
312 {
313 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
314 if (pszHit)
315 return pszHit - pszThis;
316 }
317 }
318 }
319 }
320
321 return npos;
322}
323
324void RTCString::findReplace(char chFind, char chReplace)
325{
326 Assert((unsigned int)chFind < 128U);
327 Assert((unsigned int)chReplace < 128U);
328
329 for (size_t i = 0; i < length(); ++i)
330 {
331 char *p = &m_psz[i];
332 if (*p == chFind)
333 *p = chReplace;
334 }
335}
336
337size_t RTCString::count(char ch) const
338{
339 Assert((unsigned int)ch < 128U);
340
341 size_t c = 0;
342 const char *psz = m_psz;
343 if (psz)
344 {
345 char chCur;
346 while ((chCur = *psz++) != '\0')
347 if (chCur == ch)
348 c++;
349 }
350 return c;
351}
352
353#if 0 /** @todo implement these when needed. */
354size_t RTCString::count(const char *psz, CaseSensitivity cs = CaseSensitive) const
355{
356}
357
358size_t RTCString::count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const
359{
360
361}
362#endif
363
364
365RTCString &RTCString::strip()
366{
367 stripRight();
368 return stripLeft();
369}
370
371
372RTCString &RTCString::stripLeft()
373{
374 char *psz = m_psz;
375 size_t const cch = m_cch;
376 size_t off = 0;
377 while (off < cch && RT_C_IS_SPACE(psz[off]))
378 off++;
379 if (off > 0)
380 {
381 if (off != cch)
382 {
383 memmove(psz, &psz[off], cch - off + 1);
384 m_cch = cch - off;
385 }
386 else
387 setNull();
388 }
389 return *this;
390}
391
392
393RTCString &RTCString::stripRight()
394{
395 char *psz = m_psz;
396 size_t cch = m_cch;
397 while (cch > 0 && RT_C_IS_SPACE(psz[cch - 1]))
398 cch--;
399 if (m_cch != cch)
400 {
401 m_cch = cch;
402 psz[cch] = '\0';
403 }
404 return *this;
405}
406
407
408
409RTCString RTCString::substrCP(size_t pos /*= 0*/, size_t n /*= npos*/) const
410{
411 RTCString ret;
412
413 if (n)
414 {
415 const char *psz;
416
417 if ((psz = c_str()))
418 {
419 RTUNICP cp;
420
421 // walk the UTF-8 characters until where the caller wants to start
422 size_t i = pos;
423 while (*psz && i--)
424 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
425 return ret; // return empty string on bad encoding
426
427 const char *pFirst = psz;
428
429 if (n == npos)
430 // all the rest:
431 ret = pFirst;
432 else
433 {
434 i = n;
435 while (*psz && i--)
436 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
437 return ret; // return empty string on bad encoding
438
439 size_t cbCopy = psz - pFirst;
440 if (cbCopy)
441 {
442 ret.reserve(cbCopy + 1); // may throw bad_alloc
443#ifndef RT_EXCEPTIONS_ENABLED
444 AssertRelease(capacity() >= cbCopy + 1);
445#endif
446 memcpy(ret.m_psz, pFirst, cbCopy);
447 ret.m_cch = cbCopy;
448 ret.m_psz[cbCopy] = '\0';
449 }
450 }
451 }
452 }
453
454 return ret;
455}
456
457bool RTCString::endsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
458{
459 size_t l1 = length();
460 if (l1 == 0)
461 return false;
462
463 size_t l2 = that.length();
464 if (l1 < l2)
465 return false;
466 /** @todo r=bird: If l2 is 0, then m_psz can be NULL and we will crash. See
467 * also handling of l2 == in startsWith. */
468
469 size_t l = l1 - l2;
470 if (cs == CaseSensitive)
471 return ::RTStrCmp(&m_psz[l], that.m_psz) == 0;
472 return ::RTStrICmp(&m_psz[l], that.m_psz) == 0;
473}
474
475bool RTCString::startsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
476{
477 size_t l1 = length();
478 size_t l2 = that.length();
479 if (l1 == 0 || l2 == 0) /** @todo r=bird: this differs from endsWith, and I think other IPRT code. If l2 == 0, it matches anything. */
480 return false;
481
482 if (l1 < l2)
483 return false;
484
485 if (cs == CaseSensitive)
486 return ::RTStrNCmp(m_psz, that.m_psz, l2) == 0;
487 return ::RTStrNICmp(m_psz, that.m_psz, l2) == 0;
488}
489
490bool RTCString::contains(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
491{
492 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
493 * endsWith only does half way). */
494 if (cs == CaseSensitive)
495 return ::RTStrStr(m_psz, that.m_psz) != NULL;
496 return ::RTStrIStr(m_psz, that.m_psz) != NULL;
497}
498
499bool RTCString::contains(const char *pszNeedle, CaseSensitivity cs /*= CaseSensitive*/) const
500{
501 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
502 * endsWith only does half way). */
503 if (cs == CaseSensitive)
504 return ::RTStrStr(m_psz, pszNeedle) != NULL;
505 return ::RTStrIStr(m_psz, pszNeedle) != NULL;
506}
507
508int RTCString::toInt(uint64_t &i) const
509{
510 if (!m_psz)
511 return VERR_NO_DIGITS;
512 return RTStrToUInt64Ex(m_psz, NULL, 0, &i);
513}
514
515int RTCString::toInt(uint32_t &i) const
516{
517 if (!m_psz)
518 return VERR_NO_DIGITS;
519 return RTStrToUInt32Ex(m_psz, NULL, 0, &i);
520}
521
522RTCList<RTCString, RTCString *>
523RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const
524{
525 RTCList<RTCString> strRet;
526 if (!m_psz)
527 return strRet;
528 if (a_rstrSep.isEmpty())
529 {
530 strRet.append(RTCString(m_psz));
531 return strRet;
532 }
533
534 size_t cch = m_cch;
535 char const *pszTmp = m_psz;
536 while (cch > 0)
537 {
538 char const *pszNext = strstr(pszTmp, a_rstrSep.c_str());
539 if (!pszNext)
540 {
541 strRet.append(RTCString(pszTmp, cch));
542 break;
543 }
544 size_t cchNext = pszNext - pszTmp;
545 if ( cchNext > 0
546 || mode == KeepEmptyParts)
547 strRet.append(RTCString(pszTmp, cchNext));
548 pszTmp += cchNext + a_rstrSep.length();
549 cch -= cchNext + a_rstrSep.length();
550 }
551
552 return strRet;
553}
554
555/* static */
556RTCString
557RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList,
558 const RTCString &a_rstrPrefix /* = "" */,
559 const RTCString &a_rstrSep /* = "" */)
560{
561 RTCString strRet;
562 if (a_rList.size() > 1)
563 {
564 /* calc the required size */
565 size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1;
566 cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1;
567 for (size_t i = 0; i < a_rList.size(); ++i)
568 cbNeeded += a_rList.at(i).length();
569 strRet.reserve(cbNeeded);
570
571 /* do the appending. */
572 for (size_t i = 0; i < a_rList.size() - 1; ++i)
573 {
574 if (a_rstrPrefix.isNotEmpty())
575 strRet.append(a_rstrPrefix);
576 strRet.append(a_rList.at(i));
577 strRet.append(a_rstrSep);
578 }
579 strRet.append(a_rList.last());
580 }
581 /* special case: one list item. */
582 else if (a_rList.size() > 0)
583 {
584 if (a_rstrPrefix.isNotEmpty())
585 strRet.append(a_rstrPrefix);
586 strRet.append(a_rList.last());
587 }
588
589 return strRet;
590}
591
592/* static */
593RTCString
594RTCString::join(const RTCList<RTCString, RTCString *> &a_rList,
595 const RTCString &a_rstrSep /* = "" */)
596{
597 return RTCString::joinEx(a_rList,
598 "" /* a_rstrPrefix */, a_rstrSep);
599}
600
601const RTCString operator+(const RTCString &a_rStr1, const RTCString &a_rStr2)
602{
603 RTCString strRet(a_rStr1);
604 strRet += a_rStr2;
605 return strRet;
606}
607
608const RTCString operator+(const RTCString &a_rStr1, const char *a_pszStr2)
609{
610 RTCString strRet(a_rStr1);
611 strRet += a_pszStr2;
612 return strRet;
613}
614
615const RTCString operator+(const char *a_psz1, const RTCString &a_rStr2)
616{
617 RTCString strRet(a_psz1);
618 strRet += a_rStr2;
619 return strRet;
620}
621
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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