VirtualBox

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

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

iprt/cpp/ministring.h: Added a much of NoThrow variants of the assign, append and replace methods, returning an IPRT status code instead of throwing stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.3 KB
Line 
1/* $Id: ministring.cpp 73907 2018-08-27 09:54:04Z 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-2017 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#include <iprt/uni.h>
37
38
39/*********************************************************************************************************************************
40* Global Variables *
41*********************************************************************************************************************************/
42const size_t RTCString::npos = ~(size_t)0;
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48/** Allocation block alignment used when appending bytes to a string. */
49#define IPRT_MINISTRING_APPEND_ALIGNMENT 64
50
51
52RTCString &RTCString::assign(const RTCString &a_rSrc)
53{
54 size_t const cchSrc = a_rSrc.length();
55 if (cchSrc > 0)
56 {
57 reserve(cchSrc + 1);
58 memcpy(m_psz, a_rSrc.c_str(), cchSrc);
59 m_psz[cchSrc] = '\0';
60 m_cch = cchSrc;
61 return *this;
62 }
63 setNull();
64 return *this;
65
66}
67
68int RTCString::assignNoThrow(const RTCString &a_rSrc) RT_NOEXCEPT
69{
70 AssertReturn(&a_rSrc != this, VINF_SUCCESS);
71 size_t const cchSrc = a_rSrc.length();
72 if (cchSrc > 0)
73 {
74 int rc = reserveNoThrow(cchSrc + 1);
75 if (RT_SUCCESS(rc))
76 {
77 memcpy(m_psz, a_rSrc.c_str(), cchSrc);
78 m_psz[cchSrc] = '\0';
79 m_cch = cchSrc;
80 return VINF_SUCCESS;
81 }
82 return rc;
83 }
84 setNull();
85 return VINF_SUCCESS;
86
87}
88
89RTCString &RTCString::assign(const char *a_pszSrc)
90{
91 if (a_pszSrc)
92 {
93 size_t cchSrc = strlen(a_pszSrc);
94 if (cchSrc)
95 {
96 reserve(cchSrc + 1);
97 memcpy(m_psz, a_pszSrc, cchSrc);
98 m_psz[cchSrc] = '\0';
99 m_cch = cchSrc;
100 return *this;
101 }
102 }
103 setNull();
104 return *this;
105}
106
107int RTCString::assignNoThrow(const char *a_pszSrc) RT_NOEXCEPT
108{
109 if (a_pszSrc)
110 {
111 size_t cchSrc = strlen(a_pszSrc);
112 if (cchSrc)
113 {
114 int rc = reserveNoThrow(cchSrc + 1);
115 if (RT_SUCCESS(rc))
116 {
117 memcpy(m_psz, a_pszSrc, cchSrc);
118 m_psz[cchSrc] = '\0';
119 m_cch = cchSrc;
120 return VINF_SUCCESS;
121 }
122 return rc;
123 }
124 }
125 setNull();
126 return VINF_SUCCESS;
127}
128
129RTCString &RTCString::assign(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/)
130{
131 AssertReturn(&a_rSrc != this, *this);
132 if (a_offSrc < a_rSrc.length())
133 {
134 size_t cchMax = a_rSrc.length() - a_offSrc;
135 if (a_cchSrc > cchMax)
136 a_cchSrc = cchMax;
137 reserve(a_cchSrc + 1);
138 memcpy(m_psz, a_rSrc.c_str() + a_offSrc, a_cchSrc);
139 m_psz[a_cchSrc] = '\0';
140 m_cch = a_cchSrc;
141 }
142 else
143 setNull();
144 return *this;
145}
146
147int RTCString::assignNoThrow(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/) RT_NOEXCEPT
148{
149 AssertReturn(&a_rSrc != this, VINF_SUCCESS);
150 if (a_offSrc < a_rSrc.length())
151 {
152 size_t cchMax = a_rSrc.length() - a_offSrc;
153 if (a_cchSrc > cchMax)
154 a_cchSrc = cchMax;
155 int rc = reserveNoThrow(a_cchSrc + 1);
156 if (RT_SUCCESS(rc))
157 {
158 memcpy(m_psz, a_rSrc.c_str() + a_offSrc, a_cchSrc);
159 m_psz[a_cchSrc] = '\0';
160 m_cch = a_cchSrc;
161 return VINF_SUCCESS;
162 }
163 return rc;
164 }
165 setNull();
166 return VINF_SUCCESS;
167}
168
169RTCString &RTCString::assign(const char *a_pszSrc, size_t a_cchSrc)
170{
171 if (a_cchSrc)
172 {
173 a_cchSrc = RTStrNLen(a_pszSrc, a_cchSrc);
174 reserve(a_cchSrc + 1);
175 memcpy(m_psz, a_pszSrc, a_cchSrc);
176 m_psz[a_cchSrc] = '\0';
177 m_cch = a_cchSrc;
178 }
179 else
180 setNull();
181 return *this;
182}
183
184int RTCString::assignNoThrow(const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT
185{
186 if (a_cchSrc)
187 {
188 a_cchSrc = RTStrNLen(a_pszSrc, a_cchSrc);
189 int rc = reserveNoThrow(a_cchSrc + 1);
190 if (RT_SUCCESS(rc))
191 {
192 memcpy(m_psz, a_pszSrc, a_cchSrc);
193 m_psz[a_cchSrc] = '\0';
194 m_cch = a_cchSrc;
195 return VINF_SUCCESS;
196 }
197 return rc;
198 }
199 setNull();
200 return VINF_SUCCESS;
201}
202
203RTCString &RTCString::assign(size_t a_cTimes, char a_ch)
204{
205 reserve(a_cTimes + 1);
206 memset(m_psz, a_ch, a_cTimes);
207 return *this;
208}
209
210
211int RTCString::assignNoThrow(size_t a_cTimes, char a_ch) RT_NOEXCEPT
212{
213 int rc = reserveNoThrow(a_cTimes + 1);
214 if (RT_SUCCESS(rc))
215 {
216 memset(m_psz, a_ch, a_cTimes);
217 return VINF_SUCCESS;
218 }
219 return rc;
220}
221
222
223RTCString &RTCString::printf(const char *pszFormat, ...)
224{
225 va_list va;
226 va_start(va, pszFormat);
227 printfV(pszFormat, va);
228 va_end(va);
229 return *this;
230}
231
232int RTCString::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
233{
234 va_list va;
235 va_start(va, pszFormat);
236 int rc = printfVNoThrow(pszFormat, va);
237 va_end(va);
238 return rc;
239}
240
241/**
242 * Callback used with RTStrFormatV by RTCString::printfV.
243 *
244 * @returns The number of bytes added (not used).
245 *
246 * @param pvArg The string object.
247 * @param pachChars The characters to append.
248 * @param cbChars The number of characters. 0 on the final callback.
249 */
250/*static*/ DECLCALLBACK(size_t)
251RTCString::printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars)
252{
253 RTCString *pThis = (RTCString *)pvArg;
254 if (cbChars)
255 {
256 size_t const cchBoth = pThis->m_cch + cbChars;
257 if (cchBoth >= pThis->m_cbAllocated)
258 {
259 /* Double the buffer size, if it's less that _4M. Align sizes like
260 for append. */
261 size_t cbAlloc = RT_ALIGN_Z(pThis->m_cbAllocated, IPRT_MINISTRING_APPEND_ALIGNMENT);
262 cbAlloc += RT_MIN(cbAlloc, _4M);
263 if (cbAlloc <= cchBoth)
264 cbAlloc = RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT);
265 pThis->reserve(cbAlloc);
266#ifndef RT_EXCEPTIONS_ENABLED
267 AssertReleaseReturn(pThis->capacity() > cchBoth, 0);
268#endif
269 }
270
271 memcpy(&pThis->m_psz[pThis->m_cch], pachChars, cbChars);
272 pThis->m_cch = cchBoth;
273 pThis->m_psz[cchBoth] = '\0';
274 }
275 return cbChars;
276}
277
278RTCString &RTCString::printfV(const char *pszFormat, va_list va)
279{
280 cleanup();
281 RTStrFormatV(printfOutputCallback, this, NULL, NULL, pszFormat, va);
282 return *this;
283}
284
285struct RTCSTRINGOTHROW
286{
287 RTCString *pThis;
288 int rc;
289};
290
291/**
292 * Callback used with RTStrFormatV by RTCString::printfVNoThrow.
293 *
294 * @returns The number of bytes added (not used).
295 *
296 * @param pvArg Pointer to a RTCSTRINGOTHROW structure.
297 * @param pachChars The characters to append.
298 * @param cbChars The number of characters. 0 on the final callback.
299 */
300/*static*/ DECLCALLBACK(size_t)
301RTCString::printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT
302{
303 RTCString *pThis = ((RTCSTRINGOTHROW *)pvArg)->pThis;
304 if (cbChars)
305 {
306 size_t const cchBoth = pThis->m_cch + cbChars;
307 if (cchBoth >= pThis->m_cbAllocated)
308 {
309 /* Double the buffer size, if it's less that _4M. Align sizes like
310 for append. */
311 size_t cbAlloc = RT_ALIGN_Z(pThis->m_cbAllocated, IPRT_MINISTRING_APPEND_ALIGNMENT);
312 cbAlloc += RT_MIN(cbAlloc, _4M);
313 if (cbAlloc <= cchBoth)
314 cbAlloc = RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT);
315 int rc = pThis->reserveNoThrow(cbAlloc);
316 if (RT_SUCCESS(rc))
317 { /* likely */ }
318 else
319 {
320 ((RTCSTRINGOTHROW *)pvArg)->rc = rc;
321 return cbChars;
322 }
323 }
324
325 memcpy(&pThis->m_psz[pThis->m_cch], pachChars, cbChars);
326 pThis->m_cch = cchBoth;
327 pThis->m_psz[cchBoth] = '\0';
328 }
329 return cbChars;
330}
331
332int RTCString::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
333{
334 cleanup();
335 RTCSTRINGOTHROW Args = { this, VINF_SUCCESS };
336 RTStrFormatV(printfOutputCallback, &Args, NULL, NULL, pszFormat, va);
337 return Args.rc;
338}
339
340RTCString &RTCString::append(const RTCString &that)
341{
342 Assert(&that != this);
343 return appendWorker(that.c_str(), that.length());
344}
345
346int RTCString::appendNoThrow(const RTCString &that) RT_NOEXCEPT
347{
348 Assert(&that != this);
349 return appendWorkerNoThrow(that.c_str(), that.length());
350}
351
352RTCString &RTCString::append(const char *pszThat)
353{
354 return appendWorker(pszThat, strlen(pszThat));
355}
356
357int RTCString::appendNoThrow(const char *pszThat) RT_NOEXCEPT
358{
359 return appendWorkerNoThrow(pszThat, strlen(pszThat));
360}
361
362RTCString &RTCString::append(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/)
363{
364 if (offStart < rThat.length())
365 {
366 size_t cchLeft = rThat.length() - offStart;
367 return appendWorker(rThat.c_str() + offStart, RT_MIN(cchLeft, cchMax));
368 }
369 return *this;
370}
371
372int RTCString::appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/) RT_NOEXCEPT
373{
374 if (offStart < rThat.length())
375 {
376 size_t cchLeft = rThat.length() - offStart;
377 return appendWorkerNoThrow(rThat.c_str() + offStart, RT_MIN(cchLeft, cchMax));
378 }
379 return VINF_SUCCESS;
380}
381
382RTCString &RTCString::append(const char *pszThat, size_t cchMax)
383{
384 return appendWorker(pszThat, RTStrNLen(pszThat, cchMax));
385}
386
387int RTCString::appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT
388{
389 return appendWorkerNoThrow(pszThat, RTStrNLen(pszThat, cchMax));
390}
391
392RTCString &RTCString::appendWorker(const char *pszSrc, size_t cchSrc)
393{
394 if (cchSrc)
395 {
396 size_t cchThis = length();
397 size_t cchBoth = cchThis + cchSrc;
398
399 if (cchBoth >= m_cbAllocated)
400 {
401 reserve(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
402 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
403#ifndef RT_EXCEPTIONS_ENABLED
404 AssertRelease(capacity() > cchBoth);
405#endif
406 }
407
408 memcpy(&m_psz[cchThis], pszSrc, cchSrc);
409 m_psz[cchBoth] = '\0';
410 m_cch = cchBoth;
411 }
412 return *this;
413}
414
415int RTCString::appendWorkerNoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT
416{
417 if (cchSrc)
418 {
419 size_t cchThis = length();
420 size_t cchBoth = cchThis + cchSrc;
421
422 if (cchBoth >= m_cbAllocated)
423 {
424 int rc = reserveNoThrow(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
425 if (RT_SUCCESS(rc))
426 { /* likely */ }
427 else
428 return rc;
429 }
430
431 memcpy(&m_psz[cchThis], pszSrc, cchSrc);
432 m_psz[cchBoth] = '\0';
433 m_cch = cchBoth;
434 }
435 return VINF_SUCCESS;
436}
437
438RTCString &RTCString::append(char ch)
439{
440 Assert((unsigned char)ch < 0x80); /* Don't create invalid UTF-8. */
441 if (ch)
442 {
443 // allocate in chunks of 20 in case this gets called several times
444 if (m_cch + 1 >= m_cbAllocated)
445 {
446 reserve(RT_ALIGN_Z(m_cch + 2, IPRT_MINISTRING_APPEND_ALIGNMENT));
447 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
448#ifndef RT_EXCEPTIONS_ENABLED
449 AssertRelease(capacity() > m_cch + 1);
450#endif
451 }
452
453 m_psz[m_cch] = ch;
454 m_psz[++m_cch] = '\0';
455 }
456 return *this;
457}
458
459int RTCString::appendNoThrow(char ch) RT_NOEXCEPT
460{
461 Assert((unsigned char)ch < 0x80); /* Don't create invalid UTF-8. */
462 if (ch)
463 {
464 // allocate in chunks of 20 in case this gets called several times
465 if (m_cch + 1 >= m_cbAllocated)
466 {
467 int rc = reserveNoThrow(RT_ALIGN_Z(m_cch + 2, IPRT_MINISTRING_APPEND_ALIGNMENT));
468 if (RT_SUCCESS(rc))
469 { /* likely */ }
470 else
471 return rc;
472 }
473
474 m_psz[m_cch] = ch;
475 m_psz[++m_cch] = '\0';
476 }
477 return VINF_SUCCESS;
478}
479
480RTCString &RTCString::appendCodePoint(RTUNICP uc)
481{
482 /*
483 * Single byte encoding.
484 */
485 if (uc < 0x80)
486 return RTCString::append((char)uc);
487
488 /*
489 * Multibyte encoding.
490 * Assume max encoding length when resizing the string, that's simpler.
491 */
492 AssertReturn(uc <= UINT32_C(0x7fffffff), *this);
493
494 if (m_cch + 6 >= m_cbAllocated)
495 {
496 reserve(RT_ALIGN_Z(m_cch + 6 + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
497 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
498#ifndef RT_EXCEPTIONS_ENABLED
499 AssertRelease(capacity() > m_cch + 6);
500#endif
501 }
502
503 char *pszNext = RTStrPutCp(&m_psz[m_cch], uc);
504 m_cch = pszNext - m_psz;
505 *pszNext = '\0';
506
507 return *this;
508}
509
510int RTCString::appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT
511{
512 /*
513 * Single byte encoding.
514 */
515 if (uc < 0x80)
516 return RTCString::appendNoThrow((char)uc);
517
518 /*
519 * Multibyte encoding.
520 * Assume max encoding length when resizing the string, that's simpler.
521 */
522 AssertReturn(uc <= UINT32_C(0x7fffffff), VERR_INVALID_UTF8_ENCODING);
523
524 if (m_cch + 6 >= m_cbAllocated)
525 {
526 int rc = reserveNoThrow(RT_ALIGN_Z(m_cch + 6 + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
527 if (RT_SUCCESS(rc))
528 { /* likely */ }
529 else
530 return rc;
531 }
532
533 char *pszNext = RTStrPutCp(&m_psz[m_cch], uc);
534 m_cch = pszNext - m_psz;
535 *pszNext = '\0';
536
537 return VINF_SUCCESS;
538}
539
540
541RTCString &RTCString::erase(size_t offStart /*= 0*/, size_t cchLength /*= npos*/)
542{
543 size_t cch = length();
544 if (offStart < cch)
545 {
546 if (cchLength >= cch - offStart)
547 {
548 /* Trail removal, nothing to move. */
549 m_cch = offStart;
550 m_psz[offStart] = '\0';
551 }
552 else if (cchLength > 0)
553 {
554 /* Pull up the tail to offStart. */
555 size_t cchAfter = cch - offStart - cchLength;
556 memmove(&m_psz[offStart], &m_psz[offStart + cchLength], cchAfter);
557 m_cch = cch -= cchLength;
558 m_psz[cch] = '\0';
559 }
560 }
561 return *this;
562}
563
564RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement)
565{
566 return replaceWorker(offStart, cchLength, rStrReplacement.c_str(), rStrReplacement.length());
567}
568
569int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement) RT_NOEXCEPT
570{
571 return replaceWorkerNoThrow(offStart, cchLength, rStrReplacement.c_str(), rStrReplacement.length());
572}
573
574RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
575 size_t offReplacement, size_t cchReplacement)
576{
577 Assert(this != &rStrReplacement);
578 if (cchReplacement > 0)
579 {
580 if (offReplacement < rStrReplacement.length())
581 {
582 size_t cchMaxReplacement = rStrReplacement.length() - offReplacement;
583 return replaceWorker(offStart, cchLength, rStrReplacement.c_str() + offReplacement,
584 RT_MIN(cchReplacement, cchMaxReplacement));
585 }
586 /* Our non-standard handling of out_of_range situations. */
587 AssertMsgFailed(("offReplacement=%zu (cchReplacement=%zu) rStrReplacement.length()=%zu\n",
588 offReplacement, cchReplacement, rStrReplacement.length()));
589 }
590 return replaceWorker(offStart, cchLength, "", 0);
591}
592
593int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
594 size_t offReplacement, size_t cchReplacement)
595{
596 Assert(this != &rStrReplacement);
597 if (cchReplacement > 0)
598 {
599 if (offReplacement < rStrReplacement.length())
600 {
601 size_t cchMaxReplacement = rStrReplacement.length() - offReplacement;
602 return replaceWorkerNoThrow(offStart, cchLength, rStrReplacement.c_str() + offReplacement,
603 RT_MIN(cchReplacement, cchMaxReplacement));
604 }
605 return VERR_OUT_OF_RANGE;
606 }
607 return replaceWorkerNoThrow(offStart, cchLength, "", 0);
608}
609
610RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement)
611{
612 return replaceWorker(offStart, cchLength, pszReplacement, strlen(pszReplacement));
613}
614
615int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement) RT_NOEXCEPT
616{
617 return replaceWorkerNoThrow(offStart, cchLength, pszReplacement, strlen(pszReplacement));
618}
619
620RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement)
621{
622 return replaceWorker(offStart, cchLength, pszReplacement, RTStrNLen(pszReplacement, cchReplacement));
623}
624
625int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement) RT_NOEXCEPT
626{
627 return replaceWorkerNoThrow(offStart, cchLength, pszReplacement, RTStrNLen(pszReplacement, cchReplacement));
628}
629
630RTCString &RTCString::replaceWorker(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc)
631{
632 /*
633 * Our non-standard handling of out_of_range situations.
634 */
635 size_t const cchOldLength = length();
636 AssertMsgReturn(offStart < cchOldLength, ("offStart=%zu (cchLength=%zu); length()=%zu\n", offStart, cchLength, cchOldLength),
637 *this);
638
639 /*
640 * Correct the length parameter.
641 */
642 size_t cchMaxLength = cchOldLength - offStart;
643 if (cchMaxLength < cchLength)
644 cchLength = cchMaxLength;
645
646 /*
647 * Adjust string allocation if necessary.
648 */
649 size_t cchNew = cchOldLength - cchLength + cchSrc;
650 if (cchNew >= m_cbAllocated)
651 {
652 reserve(RT_ALIGN_Z(cchNew + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
653 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
654#ifndef RT_EXCEPTIONS_ENABLED
655 AssertRelease(capacity() > cchNew);
656#endif
657 }
658
659 /*
660 * Make the change.
661 */
662 size_t cchAfter = cchOldLength - offStart - cchLength;
663 if (cchAfter > 0)
664 memmove(&m_psz[offStart + cchSrc], &m_psz[offStart + cchLength], cchAfter);
665 memcpy(&m_psz[offStart], pszSrc, cchSrc);
666 m_psz[cchNew] = '\0';
667 m_cch = cchNew;
668
669 return *this;
670}
671
672int RTCString::replaceWorkerNoThrow(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc) RT_NOEXCEPT
673{
674 /*
675 * Our non-standard handling of out_of_range situations.
676 */
677 size_t const cchOldLength = length();
678 AssertMsgReturn(offStart < cchOldLength, ("offStart=%zu (cchLength=%zu); length()=%zu\n", offStart, cchLength, cchOldLength),
679 VERR_OUT_OF_RANGE);
680
681 /*
682 * Correct the length parameter.
683 */
684 size_t cchMaxLength = cchOldLength - offStart;
685 if (cchMaxLength < cchLength)
686 cchLength = cchMaxLength;
687
688 /*
689 * Adjust string allocation if necessary.
690 */
691 size_t cchNew = cchOldLength - cchLength + cchSrc;
692 if (cchNew >= m_cbAllocated)
693 {
694 int rc = reserveNoThrow(RT_ALIGN_Z(cchNew + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
695 if (RT_SUCCESS(rc))
696 { /* likely */ }
697 else
698 return rc;
699 }
700
701 /*
702 * Make the change.
703 */
704 size_t cchAfter = cchOldLength - offStart - cchLength;
705 if (cchAfter > 0)
706 memmove(&m_psz[offStart + cchSrc], &m_psz[offStart + cchLength], cchAfter);
707 memcpy(&m_psz[offStart], pszSrc, cchSrc);
708 m_psz[cchNew] = '\0';
709 m_cch = cchNew;
710
711 return VINF_SUCCESS;
712}
713
714
715size_t RTCString::find(const char *pszNeedle, size_t offStart /*= 0*/) const
716{
717 if (offStart < length())
718 {
719 const char *pszThis = c_str();
720 if (pszThis)
721 {
722 if (pszNeedle && *pszNeedle != '\0')
723 {
724 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
725 if (pszHit)
726 return pszHit - pszThis;
727 }
728 }
729 }
730
731 return npos;
732}
733
734size_t RTCString::find(const RTCString *pStrNeedle, size_t offStart /*= 0*/) const
735{
736 if (offStart < length())
737 {
738 const char *pszThis = c_str();
739 if (pszThis)
740 {
741 if (pStrNeedle)
742 {
743 const char *pszNeedle = pStrNeedle->c_str();
744 if (pszNeedle && *pszNeedle != '\0')
745 {
746 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
747 if (pszHit)
748 return pszHit - pszThis;
749 }
750 }
751 }
752 }
753
754 return npos;
755}
756
757void RTCString::findReplace(char chFind, char chReplace)
758{
759 Assert((unsigned int)chFind < 128U);
760 Assert((unsigned int)chReplace < 128U);
761
762 for (size_t i = 0; i < length(); ++i)
763 {
764 char *p = &m_psz[i];
765 if (*p == chFind)
766 *p = chReplace;
767 }
768}
769
770size_t RTCString::count(char ch) const
771{
772 Assert((unsigned int)ch < 128U);
773
774 size_t c = 0;
775 const char *psz = m_psz;
776 if (psz)
777 {
778 char chCur;
779 while ((chCur = *psz++) != '\0')
780 if (chCur == ch)
781 c++;
782 }
783 return c;
784}
785
786#if 0 /** @todo implement these when needed. */
787size_t RTCString::count(const char *psz, CaseSensitivity cs = CaseSensitive) const
788{
789}
790
791size_t RTCString::count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const
792{
793
794}
795#endif
796
797
798RTCString &RTCString::strip()
799{
800 stripRight();
801 return stripLeft();
802}
803
804
805RTCString &RTCString::stripLeft()
806{
807 char *psz = m_psz;
808 size_t const cch = m_cch;
809 size_t off = 0;
810 while (off < cch && RT_C_IS_SPACE(psz[off]))
811 off++;
812 if (off > 0)
813 {
814 if (off != cch)
815 {
816 memmove(psz, &psz[off], cch - off + 1);
817 m_cch = cch - off;
818 }
819 else
820 setNull();
821 }
822 return *this;
823}
824
825
826RTCString &RTCString::stripRight()
827{
828 char *psz = m_psz;
829 size_t cch = m_cch;
830 while (cch > 0 && RT_C_IS_SPACE(psz[cch - 1]))
831 cch--;
832 if (m_cch != cch)
833 {
834 m_cch = cch;
835 psz[cch] = '\0';
836 }
837 return *this;
838}
839
840
841
842RTCString RTCString::substrCP(size_t pos /*= 0*/, size_t n /*= npos*/) const
843{
844 RTCString ret;
845
846 if (n)
847 {
848 const char *psz;
849
850 if ((psz = c_str()))
851 {
852 RTUNICP cp;
853
854 // walk the UTF-8 characters until where the caller wants to start
855 size_t i = pos;
856 while (*psz && i--)
857 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
858 return ret; // return empty string on bad encoding
859
860 const char *pFirst = psz;
861
862 if (n == npos)
863 // all the rest:
864 ret = pFirst;
865 else
866 {
867 i = n;
868 while (*psz && i--)
869 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
870 return ret; // return empty string on bad encoding
871
872 size_t cbCopy = psz - pFirst;
873 if (cbCopy)
874 {
875 ret.reserve(cbCopy + 1); // may throw bad_alloc
876#ifndef RT_EXCEPTIONS_ENABLED
877 AssertRelease(capacity() >= cbCopy + 1);
878#endif
879 memcpy(ret.m_psz, pFirst, cbCopy);
880 ret.m_cch = cbCopy;
881 ret.m_psz[cbCopy] = '\0';
882 }
883 }
884 }
885 }
886
887 return ret;
888}
889
890bool RTCString::endsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
891{
892 size_t l1 = length();
893 if (l1 == 0)
894 return false;
895
896 size_t l2 = that.length();
897 if (l1 < l2)
898 return false;
899 /** @todo r=bird: If l2 is 0, then m_psz can be NULL and we will crash. See
900 * also handling of l2 == in startsWith. */
901
902 size_t l = l1 - l2;
903 if (cs == CaseSensitive)
904 return ::RTStrCmp(&m_psz[l], that.m_psz) == 0;
905 return ::RTStrICmp(&m_psz[l], that.m_psz) == 0;
906}
907
908bool RTCString::startsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
909{
910 size_t l1 = length();
911 size_t l2 = that.length();
912 if (l1 == 0 || l2 == 0) /** @todo r=bird: this differs from endsWith, and I think other IPRT code. If l2 == 0, it matches anything. */
913 return false;
914
915 if (l1 < l2)
916 return false;
917
918 if (cs == CaseSensitive)
919 return ::RTStrNCmp(m_psz, that.m_psz, l2) == 0;
920 return ::RTStrNICmp(m_psz, that.m_psz, l2) == 0;
921}
922
923bool RTCString::startsWithWord(const char *pszWord, CaseSensitivity enmCase /*= CaseSensitive*/) const
924{
925 const char *pszSrc = RTStrStripL(c_str()); /** @todo RTStrStripL doesn't use RTUniCpIsSpace (nbsp) */
926 size_t cchWord = strlen(pszWord);
927 if ( enmCase == CaseSensitive
928 ? RTStrNCmp(pszSrc, pszWord, cchWord) == 0
929 : RTStrNICmp(pszSrc, pszWord, cchWord) == 0)
930 {
931 if ( pszSrc[cchWord] == '\0'
932 || RT_C_IS_SPACE(pszSrc[cchWord])
933 || RT_C_IS_PUNCT(pszSrc[cchWord]) )
934 return true;
935 RTUNICP uc = RTStrGetCp(&pszSrc[cchWord]);
936 if (RTUniCpIsSpace(uc))
937 return true;
938 }
939 return false;
940}
941
942bool RTCString::startsWithWord(const RTCString &rThat, CaseSensitivity enmCase /*= CaseSensitive*/) const
943{
944 return startsWithWord(rThat.c_str(), enmCase);
945}
946
947bool RTCString::contains(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const
948{
949 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
950 * endsWith only does half way). */
951 if (cs == CaseSensitive)
952 return ::RTStrStr(m_psz, that.m_psz) != NULL;
953 return ::RTStrIStr(m_psz, that.m_psz) != NULL;
954}
955
956bool RTCString::contains(const char *pszNeedle, CaseSensitivity cs /*= CaseSensitive*/) const
957{
958 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
959 * endsWith only does half way). */
960 if (cs == CaseSensitive)
961 return ::RTStrStr(m_psz, pszNeedle) != NULL;
962 return ::RTStrIStr(m_psz, pszNeedle) != NULL;
963}
964
965int RTCString::toInt(uint64_t &i) const
966{
967 if (!m_psz)
968 return VERR_NO_DIGITS;
969 return RTStrToUInt64Ex(m_psz, NULL, 0, &i);
970}
971
972int RTCString::toInt(uint32_t &i) const
973{
974 if (!m_psz)
975 return VERR_NO_DIGITS;
976 return RTStrToUInt32Ex(m_psz, NULL, 0, &i);
977}
978
979RTCList<RTCString, RTCString *>
980RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const
981{
982 RTCList<RTCString> strRet;
983 if (!m_psz)
984 return strRet;
985 if (a_rstrSep.isEmpty())
986 {
987 strRet.append(RTCString(m_psz));
988 return strRet;
989 }
990
991 size_t cch = m_cch;
992 char const *pszTmp = m_psz;
993 while (cch > 0)
994 {
995 char const *pszNext = strstr(pszTmp, a_rstrSep.c_str());
996 if (!pszNext)
997 {
998 strRet.append(RTCString(pszTmp, cch));
999 break;
1000 }
1001 size_t cchNext = pszNext - pszTmp;
1002 if ( cchNext > 0
1003 || mode == KeepEmptyParts)
1004 strRet.append(RTCString(pszTmp, cchNext));
1005 pszTmp += cchNext + a_rstrSep.length();
1006 cch -= cchNext + a_rstrSep.length();
1007 }
1008
1009 return strRet;
1010}
1011
1012/* static */
1013RTCString
1014RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList,
1015 const RTCString &a_rstrPrefix /* = "" */,
1016 const RTCString &a_rstrSep /* = "" */)
1017{
1018 RTCString strRet;
1019 if (a_rList.size() > 1)
1020 {
1021 /* calc the required size */
1022 size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1;
1023 cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1;
1024 for (size_t i = 0; i < a_rList.size(); ++i)
1025 cbNeeded += a_rList.at(i).length();
1026 strRet.reserve(cbNeeded);
1027
1028 /* do the appending. */
1029 for (size_t i = 0; i < a_rList.size() - 1; ++i)
1030 {
1031 if (a_rstrPrefix.isNotEmpty())
1032 strRet.append(a_rstrPrefix);
1033 strRet.append(a_rList.at(i));
1034 strRet.append(a_rstrSep);
1035 }
1036 strRet.append(a_rList.last());
1037 }
1038 /* special case: one list item. */
1039 else if (a_rList.size() > 0)
1040 {
1041 if (a_rstrPrefix.isNotEmpty())
1042 strRet.append(a_rstrPrefix);
1043 strRet.append(a_rList.last());
1044 }
1045
1046 return strRet;
1047}
1048
1049/* static */
1050RTCString
1051RTCString::join(const RTCList<RTCString, RTCString *> &a_rList,
1052 const RTCString &a_rstrSep /* = "" */)
1053{
1054 return RTCString::joinEx(a_rList,
1055 "" /* a_rstrPrefix */, a_rstrSep);
1056}
1057
1058const RTCString operator+(const RTCString &a_rStr1, const RTCString &a_rStr2)
1059{
1060 RTCString strRet(a_rStr1);
1061 strRet += a_rStr2;
1062 return strRet;
1063}
1064
1065const RTCString operator+(const RTCString &a_rStr1, const char *a_pszStr2)
1066{
1067 RTCString strRet(a_rStr1);
1068 strRet += a_pszStr2;
1069 return strRet;
1070}
1071
1072const RTCString operator+(const char *a_psz1, const RTCString &a_rStr2)
1073{
1074 RTCString strRet(a_psz1);
1075 strRet += a_rStr2;
1076 return strRet;
1077}
1078
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