VirtualBox

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

Last change on this file was 101346, checked in by vboxsync, 8 months ago

IPRT/ministring: More complete set of endsWith[I] and startsWith[I] methods.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.1 KB
Line 
1/* $Id: ministring.cpp 101346 2023-10-04 23:33:39Z 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-2023 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * The contents of this file may alternatively be used under the terms
29 * of the Common Development and Distribution License Version 1.0
30 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31 * in the VirtualBox distribution, in which case the provisions of the
32 * CDDL are applicable instead of those of the GPL.
33 *
34 * You may elect to license modified versions of this file under the
35 * terms and conditions of either the GPL or the CDDL or both.
36 *
37 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38 */
39
40
41/*********************************************************************************************************************************
42* Header Files *
43*********************************************************************************************************************************/
44#include <iprt/cpp/ministring.h>
45#include "internal/iprt.h"
46
47#include <iprt/ctype.h>
48#include <iprt/uni.h>
49#include <iprt/err.h>
50
51
52
53/*********************************************************************************************************************************
54* Global Variables *
55*********************************************************************************************************************************/
56const size_t RTCString::npos = ~(size_t)0;
57
58
59/*********************************************************************************************************************************
60* Defined Constants And Macros *
61*********************************************************************************************************************************/
62/** Allocation block alignment used when appending bytes to a string. */
63#define IPRT_MINISTRING_APPEND_ALIGNMENT 64
64
65
66RTCString &RTCString::assign(const RTCString &a_rSrc)
67{
68 Assert(&a_rSrc != this);
69 size_t const cchSrc = a_rSrc.length();
70 if (cchSrc > 0)
71 {
72 reserve(cchSrc + 1);
73 memcpy(m_psz, a_rSrc.c_str(), cchSrc);
74 m_psz[cchSrc] = '\0';
75 m_cch = cchSrc;
76 return *this;
77 }
78 setNull();
79 return *this;
80
81}
82
83int RTCString::assignNoThrow(const RTCString &a_rSrc) RT_NOEXCEPT
84{
85 AssertReturn(&a_rSrc != this, VINF_SUCCESS);
86 size_t const cchSrc = a_rSrc.length();
87 if (cchSrc > 0)
88 {
89 int rc = reserveNoThrow(cchSrc + 1);
90 if (RT_SUCCESS(rc))
91 {
92 memcpy(m_psz, a_rSrc.c_str(), cchSrc);
93 m_psz[cchSrc] = '\0';
94 m_cch = cchSrc;
95 return VINF_SUCCESS;
96 }
97 return rc;
98 }
99 setNull();
100 return VINF_SUCCESS;
101
102}
103
104RTCString &RTCString::assign(const char *a_pszSrc)
105{
106 if (a_pszSrc)
107 {
108 size_t cchSrc = strlen(a_pszSrc);
109 if (cchSrc)
110 {
111 Assert((uintptr_t)&a_pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
112
113 reserve(cchSrc + 1);
114 memcpy(m_psz, a_pszSrc, cchSrc);
115 m_psz[cchSrc] = '\0';
116 m_cch = cchSrc;
117 return *this;
118 }
119 }
120 setNull();
121 return *this;
122}
123
124int RTCString::assignNoThrow(const char *a_pszSrc) RT_NOEXCEPT
125{
126 if (a_pszSrc)
127 {
128 size_t cchSrc = strlen(a_pszSrc);
129 if (cchSrc)
130 {
131 Assert((uintptr_t)&a_pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
132
133 int rc = reserveNoThrow(cchSrc + 1);
134 if (RT_SUCCESS(rc))
135 {
136 memcpy(m_psz, a_pszSrc, cchSrc);
137 m_psz[cchSrc] = '\0';
138 m_cch = cchSrc;
139 return VINF_SUCCESS;
140 }
141 return rc;
142 }
143 }
144 setNull();
145 return VINF_SUCCESS;
146}
147
148RTCString &RTCString::assign(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/)
149{
150 AssertReturn(&a_rSrc != this, *this);
151 if (a_offSrc < a_rSrc.length())
152 {
153 size_t cchMax = a_rSrc.length() - a_offSrc;
154 if (a_cchSrc > cchMax)
155 a_cchSrc = cchMax;
156 reserve(a_cchSrc + 1);
157 memcpy(m_psz, a_rSrc.c_str() + a_offSrc, a_cchSrc);
158 m_psz[a_cchSrc] = '\0';
159 m_cch = a_cchSrc;
160 }
161 else
162 setNull();
163 return *this;
164}
165
166int RTCString::assignNoThrow(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/) RT_NOEXCEPT
167{
168 AssertReturn(&a_rSrc != this, VINF_SUCCESS);
169 if (a_offSrc < a_rSrc.length())
170 {
171 size_t cchMax = a_rSrc.length() - a_offSrc;
172 if (a_cchSrc > cchMax)
173 a_cchSrc = cchMax;
174 int rc = reserveNoThrow(a_cchSrc + 1);
175 if (RT_SUCCESS(rc))
176 {
177 memcpy(m_psz, a_rSrc.c_str() + a_offSrc, a_cchSrc);
178 m_psz[a_cchSrc] = '\0';
179 m_cch = a_cchSrc;
180 return VINF_SUCCESS;
181 }
182 return rc;
183 }
184 setNull();
185 return VINF_SUCCESS;
186}
187
188RTCString &RTCString::assign(const char *a_pszSrc, size_t a_cchSrc)
189{
190 if (a_cchSrc)
191 {
192 a_cchSrc = RTStrNLen(a_pszSrc, a_cchSrc);
193 if (a_cchSrc)
194 {
195 Assert((uintptr_t)&a_pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
196
197 reserve(a_cchSrc + 1);
198 memcpy(m_psz, a_pszSrc, a_cchSrc);
199 m_psz[a_cchSrc] = '\0';
200 m_cch = a_cchSrc;
201 return *this;
202 }
203 }
204 setNull();
205 return *this;
206}
207
208int RTCString::assignNoThrow(const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT
209{
210 if (a_cchSrc)
211 {
212 a_cchSrc = RTStrNLen(a_pszSrc, a_cchSrc);
213 if (a_cchSrc)
214 {
215 Assert((uintptr_t)&a_pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
216
217 int rc = reserveNoThrow(a_cchSrc + 1);
218 if (RT_SUCCESS(rc))
219 {
220 memcpy(m_psz, a_pszSrc, a_cchSrc);
221 m_psz[a_cchSrc] = '\0';
222 m_cch = a_cchSrc;
223 return VINF_SUCCESS;
224 }
225 return rc;
226 }
227 }
228 setNull();
229 return VINF_SUCCESS;
230}
231
232RTCString &RTCString::assign(size_t a_cTimes, char a_ch)
233{
234 reserve(a_cTimes + 1);
235 memset(m_psz, a_ch, a_cTimes);
236 m_psz[a_cTimes] = '\0';
237 m_cch = a_cTimes;
238 return *this;
239}
240
241
242int RTCString::assignNoThrow(size_t a_cTimes, char a_ch) RT_NOEXCEPT
243{
244 int rc = reserveNoThrow(a_cTimes + 1);
245 if (RT_SUCCESS(rc))
246 {
247 memset(m_psz, a_ch, a_cTimes);
248 m_psz[a_cTimes] = '\0';
249 m_cch = a_cTimes;
250 return VINF_SUCCESS;
251 }
252 return rc;
253}
254
255
256RTCString &RTCString::printf(const char *pszFormat, ...)
257{
258 va_list va;
259 va_start(va, pszFormat);
260 printfV(pszFormat, va);
261 va_end(va);
262 return *this;
263}
264
265int RTCString::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
266{
267 va_list va;
268 va_start(va, pszFormat);
269 int rc = printfVNoThrow(pszFormat, va);
270 va_end(va);
271 return rc;
272}
273
274/**
275 * Callback used with RTStrFormatV by RTCString::printfV.
276 *
277 * @returns The number of bytes added (not used).
278 *
279 * @param pvArg The string object.
280 * @param pachChars The characters to append.
281 * @param cbChars The number of characters. 0 on the final callback.
282 */
283/*static*/ DECLCALLBACK(size_t)
284RTCString::printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars)
285{
286 RTCString *pThis = (RTCString *)pvArg;
287 if (cbChars)
288 {
289 size_t const cchBoth = pThis->m_cch + cbChars;
290 if (cchBoth >= pThis->m_cbAllocated)
291 {
292 /* Double the buffer size, if it's less that _4M. Align sizes like
293 for append. */
294 size_t cbAlloc = RT_ALIGN_Z(pThis->m_cbAllocated, IPRT_MINISTRING_APPEND_ALIGNMENT);
295 cbAlloc += RT_MIN(cbAlloc, _4M);
296 if (cbAlloc <= cchBoth)
297 cbAlloc = RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT);
298 pThis->reserve(cbAlloc);
299#ifndef RT_EXCEPTIONS_ENABLED
300 AssertReleaseReturn(pThis->capacity() > cchBoth, 0);
301#endif
302 }
303
304 memcpy(&pThis->m_psz[pThis->m_cch], pachChars, cbChars);
305 pThis->m_cch = cchBoth;
306 pThis->m_psz[cchBoth] = '\0';
307 }
308 return cbChars;
309}
310
311RTCString &RTCString::printfV(const char *pszFormat, va_list va)
312{
313 cleanup();
314 RTStrFormatV(printfOutputCallback, this, NULL, NULL, pszFormat, va);
315 return *this;
316}
317
318RTCString &RTCString::appendPrintfV(const char *pszFormat, va_list va)
319{
320 RTStrFormatV(printfOutputCallback, this, NULL, NULL, pszFormat, va);
321 return *this;
322}
323
324struct RTCSTRINGOTHROW
325{
326 RTCString *pThis;
327 int rc;
328};
329
330/**
331 * Callback used with RTStrFormatV by RTCString::printfVNoThrow.
332 *
333 * @returns The number of bytes added (not used).
334 *
335 * @param pvArg Pointer to a RTCSTRINGOTHROW structure.
336 * @param pachChars The characters to append.
337 * @param cbChars The number of characters. 0 on the final callback.
338 */
339/*static*/ DECLCALLBACK(size_t)
340RTCString::printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT
341{
342 RTCString *pThis = ((RTCSTRINGOTHROW *)pvArg)->pThis;
343 if (cbChars)
344 {
345 size_t const cchBoth = pThis->m_cch + cbChars;
346 if (cchBoth >= pThis->m_cbAllocated)
347 {
348 /* Double the buffer size, if it's less that _4M. Align sizes like
349 for append. */
350 size_t cbAlloc = RT_ALIGN_Z(pThis->m_cbAllocated, IPRT_MINISTRING_APPEND_ALIGNMENT);
351 cbAlloc += RT_MIN(cbAlloc, _4M);
352 if (cbAlloc <= cchBoth)
353 cbAlloc = RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT);
354 int rc = pThis->reserveNoThrow(cbAlloc);
355 if (RT_SUCCESS(rc))
356 { /* likely */ }
357 else
358 {
359 ((RTCSTRINGOTHROW *)pvArg)->rc = rc;
360 return cbChars;
361 }
362 }
363
364 memcpy(&pThis->m_psz[pThis->m_cch], pachChars, cbChars);
365 pThis->m_cch = cchBoth;
366 pThis->m_psz[cchBoth] = '\0';
367 }
368 return cbChars;
369}
370
371int RTCString::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
372{
373 cleanup();
374 RTCSTRINGOTHROW Args = { this, VINF_SUCCESS };
375 RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va);
376 return Args.rc;
377}
378
379int RTCString::appendPrintfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
380{
381 RTCSTRINGOTHROW Args = { this, VINF_SUCCESS };
382 RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va);
383 return Args.rc;
384}
385
386RTCString &RTCString::appendPrintf(const char *pszFormat, ...)
387{
388 va_list va;
389 va_start(va, pszFormat);
390 appendPrintfV(pszFormat, va);
391 va_end(va);
392 return *this;
393}
394
395int RTCString::appendPrintfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
396{
397 va_list va;
398 va_start(va, pszFormat);
399 int rc = appendPrintfVNoThrow(pszFormat, va);
400 va_end(va);
401 return rc;
402}
403
404RTCString &RTCString::append(const RTCString &that)
405{
406 Assert(&that != this);
407 return appendWorker(that.c_str(), that.length());
408}
409
410int RTCString::appendNoThrow(const RTCString &that) RT_NOEXCEPT
411{
412 Assert(&that != this);
413 return appendWorkerNoThrow(that.c_str(), that.length());
414}
415
416RTCString &RTCString::append(const char *pszThat)
417{
418 return appendWorker(pszThat, strlen(pszThat));
419}
420
421int RTCString::appendNoThrow(const char *pszThat) RT_NOEXCEPT
422{
423 return appendWorkerNoThrow(pszThat, strlen(pszThat));
424}
425
426RTCString &RTCString::append(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/)
427{
428 if (offStart < rThat.length())
429 {
430 size_t cchLeft = rThat.length() - offStart;
431 return appendWorker(rThat.c_str() + offStart, RT_MIN(cchLeft, cchMax));
432 }
433 return *this;
434}
435
436int RTCString::appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/) RT_NOEXCEPT
437{
438 if (offStart < rThat.length())
439 {
440 size_t cchLeft = rThat.length() - offStart;
441 return appendWorkerNoThrow(rThat.c_str() + offStart, RT_MIN(cchLeft, cchMax));
442 }
443 return VINF_SUCCESS;
444}
445
446RTCString &RTCString::append(const char *pszThat, size_t cchMax)
447{
448 return appendWorker(pszThat, RTStrNLen(pszThat, cchMax));
449}
450
451int RTCString::appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT
452{
453 return appendWorkerNoThrow(pszThat, RTStrNLen(pszThat, cchMax));
454}
455
456RTCString &RTCString::appendWorker(const char *pszSrc, size_t cchSrc)
457{
458 if (cchSrc)
459 {
460 Assert((uintptr_t)&pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
461
462 size_t cchThis = length();
463 size_t cchBoth = cchThis + cchSrc;
464
465 if (cchBoth >= m_cbAllocated)
466 {
467 reserve(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
468 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
469#ifndef RT_EXCEPTIONS_ENABLED
470 AssertRelease(capacity() > cchBoth);
471#endif
472 }
473
474 memcpy(&m_psz[cchThis], pszSrc, cchSrc);
475 m_psz[cchBoth] = '\0';
476 m_cch = cchBoth;
477 }
478 return *this;
479}
480
481int RTCString::appendWorkerNoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT
482{
483 if (cchSrc)
484 {
485 Assert((uintptr_t)&pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
486
487 size_t cchThis = length();
488 size_t cchBoth = cchThis + cchSrc;
489
490 if (cchBoth >= m_cbAllocated)
491 {
492 int rc = reserveNoThrow(RT_ALIGN_Z(cchBoth + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
493 if (RT_SUCCESS(rc))
494 { /* likely */ }
495 else
496 return rc;
497 }
498
499 memcpy(&m_psz[cchThis], pszSrc, cchSrc);
500 m_psz[cchBoth] = '\0';
501 m_cch = cchBoth;
502 }
503 return VINF_SUCCESS;
504}
505
506RTCString &RTCString::append(char ch)
507{
508 Assert((unsigned char)ch < 0x80); /* Don't create invalid UTF-8. */
509 if (ch)
510 {
511 // allocate in chunks of 20 in case this gets called several times
512 if (m_cch + 1 >= m_cbAllocated)
513 {
514 reserve(RT_ALIGN_Z(m_cch + 2, IPRT_MINISTRING_APPEND_ALIGNMENT));
515 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
516#ifndef RT_EXCEPTIONS_ENABLED
517 AssertRelease(capacity() > m_cch + 1);
518#endif
519 }
520
521 m_psz[m_cch] = ch;
522 m_psz[++m_cch] = '\0';
523 }
524 return *this;
525}
526
527int RTCString::appendNoThrow(char ch) RT_NOEXCEPT
528{
529 Assert((unsigned char)ch < 0x80); /* Don't create invalid UTF-8. */
530 if (ch)
531 {
532 // allocate in chunks of 20 in case this gets called several times
533 if (m_cch + 1 >= m_cbAllocated)
534 {
535 int rc = reserveNoThrow(RT_ALIGN_Z(m_cch + 2, IPRT_MINISTRING_APPEND_ALIGNMENT));
536 if (RT_SUCCESS(rc))
537 { /* likely */ }
538 else
539 return rc;
540 }
541
542 m_psz[m_cch] = ch;
543 m_psz[++m_cch] = '\0';
544 }
545 return VINF_SUCCESS;
546}
547
548RTCString &RTCString::appendCodePoint(RTUNICP uc)
549{
550 /*
551 * Single byte encoding.
552 */
553 if (uc < 0x80)
554 return RTCString::append((char)uc);
555
556 /*
557 * Multibyte encoding.
558 * Assume max encoding length when resizing the string, that's simpler.
559 */
560 AssertReturn(uc <= UINT32_C(0x7fffffff), *this);
561
562 if (m_cch + 6 >= m_cbAllocated)
563 {
564 reserve(RT_ALIGN_Z(m_cch + 6 + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
565 // calls realloc(cbBoth) and sets m_cbAllocated; may throw bad_alloc.
566#ifndef RT_EXCEPTIONS_ENABLED
567 AssertRelease(capacity() > m_cch + 6);
568#endif
569 }
570
571 char *pszNext = RTStrPutCp(&m_psz[m_cch], uc);
572 m_cch = pszNext - m_psz;
573 *pszNext = '\0';
574
575 return *this;
576}
577
578int RTCString::appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT
579{
580 /*
581 * Single byte encoding.
582 */
583 if (uc < 0x80)
584 return RTCString::appendNoThrow((char)uc);
585
586 /*
587 * Multibyte encoding.
588 * Assume max encoding length when resizing the string, that's simpler.
589 */
590 AssertReturn(uc <= UINT32_C(0x7fffffff), VERR_INVALID_UTF8_ENCODING);
591
592 if (m_cch + 6 >= m_cbAllocated)
593 {
594 int rc = reserveNoThrow(RT_ALIGN_Z(m_cch + 6 + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
595 if (RT_SUCCESS(rc))
596 { /* likely */ }
597 else
598 return rc;
599 }
600
601 char *pszNext = RTStrPutCp(&m_psz[m_cch], uc);
602 m_cch = pszNext - m_psz;
603 *pszNext = '\0';
604
605 return VINF_SUCCESS;
606}
607
608RTCString &RTCString::erase(size_t offStart /*= 0*/, size_t cchLength /*= npos*/) RT_NOEXCEPT
609{
610 size_t cch = length();
611 if (offStart < cch)
612 {
613 if (cchLength >= cch - offStart)
614 {
615 /* Trail removal, nothing to move. */
616 m_cch = offStart;
617 m_psz[offStart] = '\0';
618 }
619 else if (cchLength > 0)
620 {
621 /* Pull up the tail to offStart. */
622 size_t cchAfter = cch - offStart - cchLength;
623 memmove(&m_psz[offStart], &m_psz[offStart + cchLength], cchAfter);
624 m_cch = cch -= cchLength;
625 m_psz[cch] = '\0';
626 }
627 }
628 return *this;
629}
630
631RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement)
632{
633 return replaceWorker(offStart, cchLength, rStrReplacement.c_str(), rStrReplacement.length());
634}
635
636int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement) RT_NOEXCEPT
637{
638 return replaceWorkerNoThrow(offStart, cchLength, rStrReplacement.c_str(), rStrReplacement.length());
639}
640
641RTCString &RTCString::replace(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
642 size_t offReplacement, size_t cchReplacement)
643{
644 Assert(this != &rStrReplacement);
645 if (cchReplacement > 0)
646 {
647 if (offReplacement < rStrReplacement.length())
648 {
649 size_t cchMaxReplacement = rStrReplacement.length() - offReplacement;
650 return replaceWorker(offStart, cchLength, rStrReplacement.c_str() + offReplacement,
651 RT_MIN(cchReplacement, cchMaxReplacement));
652 }
653 /* Our non-standard handling of out_of_range situations. */
654 AssertMsgFailed(("offReplacement=%zu (cchReplacement=%zu) rStrReplacement.length()=%zu\n",
655 offReplacement, cchReplacement, rStrReplacement.length()));
656 }
657 return replaceWorker(offStart, cchLength, "", 0);
658}
659
660int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const RTCString &rStrReplacement,
661 size_t offReplacement, size_t cchReplacement) RT_NOEXCEPT
662{
663 Assert(this != &rStrReplacement);
664 if (cchReplacement > 0)
665 {
666 if (offReplacement < rStrReplacement.length())
667 {
668 size_t cchMaxReplacement = rStrReplacement.length() - offReplacement;
669 return replaceWorkerNoThrow(offStart, cchLength, rStrReplacement.c_str() + offReplacement,
670 RT_MIN(cchReplacement, cchMaxReplacement));
671 }
672 return VERR_OUT_OF_RANGE;
673 }
674 return replaceWorkerNoThrow(offStart, cchLength, "", 0);
675}
676
677RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement)
678{
679 return replaceWorker(offStart, cchLength, pszReplacement, strlen(pszReplacement));
680}
681
682int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement) RT_NOEXCEPT
683{
684 return replaceWorkerNoThrow(offStart, cchLength, pszReplacement, strlen(pszReplacement));
685}
686
687RTCString &RTCString::replace(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement)
688{
689 return replaceWorker(offStart, cchLength, pszReplacement, RTStrNLen(pszReplacement, cchReplacement));
690}
691
692int RTCString::replaceNoThrow(size_t offStart, size_t cchLength, const char *pszReplacement, size_t cchReplacement) RT_NOEXCEPT
693{
694 return replaceWorkerNoThrow(offStart, cchLength, pszReplacement, RTStrNLen(pszReplacement, cchReplacement));
695}
696
697RTCString &RTCString::replaceWorker(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc)
698{
699 Assert((uintptr_t)&pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated || !cchSrc);
700
701 /*
702 * Our non-standard handling of out_of_range situations.
703 */
704 size_t const cchOldLength = length();
705 AssertMsgReturn(offStart < cchOldLength, ("offStart=%zu (cchLength=%zu); length()=%zu\n", offStart, cchLength, cchOldLength),
706 *this);
707
708 /*
709 * Correct the length parameter.
710 */
711 size_t cchMaxLength = cchOldLength - offStart;
712 if (cchMaxLength < cchLength)
713 cchLength = cchMaxLength;
714
715 /*
716 * Adjust string allocation if necessary.
717 */
718 size_t cchNew = cchOldLength - cchLength + cchSrc;
719 if (cchNew >= m_cbAllocated)
720 {
721 reserve(RT_ALIGN_Z(cchNew + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
722 // calls realloc(cchBoth + 1) and sets m_cbAllocated; may throw bad_alloc.
723#ifndef RT_EXCEPTIONS_ENABLED
724 AssertRelease(capacity() > cchNew);
725#endif
726 }
727
728 /*
729 * Make the change.
730 */
731 size_t cchAfter = cchOldLength - offStart - cchLength;
732 if (cchAfter > 0)
733 memmove(&m_psz[offStart + cchSrc], &m_psz[offStart + cchLength], cchAfter);
734 memcpy(&m_psz[offStart], pszSrc, cchSrc);
735 m_psz[cchNew] = '\0';
736 m_cch = cchNew;
737
738 return *this;
739}
740
741int RTCString::replaceWorkerNoThrow(size_t offStart, size_t cchLength, const char *pszSrc, size_t cchSrc) RT_NOEXCEPT
742{
743 Assert((uintptr_t)&pszSrc - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated || !cchSrc);
744
745 /*
746 * Our non-standard handling of out_of_range situations.
747 */
748 size_t const cchOldLength = length();
749 AssertMsgReturn(offStart < cchOldLength, ("offStart=%zu (cchLength=%zu); length()=%zu\n", offStart, cchLength, cchOldLength),
750 VERR_OUT_OF_RANGE);
751
752 /*
753 * Correct the length parameter.
754 */
755 size_t cchMaxLength = cchOldLength - offStart;
756 if (cchMaxLength < cchLength)
757 cchLength = cchMaxLength;
758
759 /*
760 * Adjust string allocation if necessary.
761 */
762 size_t cchNew = cchOldLength - cchLength + cchSrc;
763 if (cchNew >= m_cbAllocated)
764 {
765 int rc = reserveNoThrow(RT_ALIGN_Z(cchNew + 1, IPRT_MINISTRING_APPEND_ALIGNMENT));
766 if (RT_SUCCESS(rc))
767 { /* likely */ }
768 else
769 return rc;
770 }
771
772 /*
773 * Make the change.
774 */
775 size_t cchAfter = cchOldLength - offStart - cchLength;
776 if (cchAfter > 0)
777 memmove(&m_psz[offStart + cchSrc], &m_psz[offStart + cchLength], cchAfter);
778 memcpy(&m_psz[offStart], pszSrc, cchSrc);
779 m_psz[cchNew] = '\0';
780 m_cch = cchNew;
781
782 return VINF_SUCCESS;
783}
784
785
786RTCString &RTCString::truncate(size_t cchMax) RT_NOEXCEPT
787{
788 if (cchMax < m_cch)
789 {
790 /*
791 * Make sure the truncated string ends with a correctly encoded
792 * codepoint and is not missing a few bytes.
793 */
794 if (cchMax > 0)
795 {
796 char chTail = m_psz[cchMax];
797 if ( (chTail & 0x80) == 0 /* single byte codepoint */
798 || (chTail & 0xc0) == 0xc0) /* first byte of codepoint sequence. */
799 { /* likely */ }
800 else
801 {
802 /* We need to find the start of the codepoint sequence: */
803 do
804 cchMax -= 1;
805 while ( cchMax > 0
806 && (m_psz[cchMax] & 0xc0) != 0xc0);
807 }
808 }
809
810 /*
811 * Do the truncating.
812 */
813 m_psz[cchMax] = '\0';
814 m_cch = cchMax;
815 }
816 return *this;
817}
818
819
820size_t RTCString::find(const char *pszNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
821{
822 if (offStart < length())
823 {
824 const char *pszThis = c_str();
825 if (pszThis)
826 {
827 if (pszNeedle && *pszNeedle != '\0')
828 {
829 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
830 if (pszHit)
831 return pszHit - pszThis;
832 }
833 }
834 }
835
836 return npos;
837}
838
839size_t RTCString::find(const RTCString *pStrNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
840{
841 if (offStart < length())
842 {
843 const char *pszThis = c_str();
844 if (pszThis)
845 {
846 if (pStrNeedle)
847 {
848 const char *pszNeedle = pStrNeedle->c_str();
849 if (pszNeedle && *pszNeedle != '\0')
850 {
851 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
852 if (pszHit)
853 return pszHit - pszThis;
854 }
855 }
856 }
857 }
858
859 return npos;
860}
861
862
863size_t RTCString::find(const RTCString &rStrNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
864{
865 return find(&rStrNeedle, offStart);
866}
867
868
869size_t RTCString::find(const char chNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
870{
871 Assert((unsigned int)chNeedle < 128U);
872 if (offStart < length())
873 {
874 const char *pszThis = c_str();
875 if (pszThis)
876 {
877 const char *pszHit = (const char *)memchr(&pszThis[offStart], chNeedle, length() - offStart);
878 if (pszHit)
879 return pszHit - pszThis;
880 }
881 }
882 return npos;
883}
884
885
886void RTCString::findReplace(char chFind, char chReplace) RT_NOEXCEPT
887{
888 Assert((unsigned int)chFind < 128U);
889 Assert((unsigned int)chReplace < 128U);
890
891 for (size_t i = 0; i < length(); ++i)
892 {
893 char *p = &m_psz[i];
894 if (*p == chFind)
895 *p = chReplace;
896 }
897}
898
899size_t RTCString::count(char ch) const RT_NOEXCEPT
900{
901 Assert((unsigned int)ch < 128U);
902
903 size_t c = 0;
904 const char *psz = m_psz;
905 if (psz)
906 {
907 char chCur;
908 while ((chCur = *psz++) != '\0')
909 if (chCur == ch)
910 c++;
911 }
912 return c;
913}
914
915#if 0 /** @todo implement these when needed. */
916size_t RTCString::count(const char *psz, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
917{
918}
919
920size_t RTCString::count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
921{
922
923}
924#endif
925
926
927RTCString &RTCString::strip() RT_NOEXCEPT
928{
929 stripRight();
930 return stripLeft();
931}
932
933
934RTCString &RTCString::stripLeft() RT_NOEXCEPT
935{
936 char *psz = m_psz;
937 size_t const cch = m_cch;
938 size_t off = 0;
939 while (off < cch && RT_C_IS_SPACE(psz[off]))
940 off++;
941 if (off > 0)
942 {
943 if (off != cch)
944 {
945 memmove(psz, &psz[off], cch - off + 1);
946 m_cch = cch - off;
947 }
948 else
949 setNull();
950 }
951 return *this;
952}
953
954
955RTCString &RTCString::stripRight() RT_NOEXCEPT
956{
957 char *psz = m_psz;
958 size_t cch = m_cch;
959 while (cch > 0 && RT_C_IS_SPACE(psz[cch - 1]))
960 cch--;
961 if (m_cch != cch)
962 {
963 m_cch = cch;
964 psz[cch] = '\0';
965 }
966 return *this;
967}
968
969
970
971RTCString RTCString::substrCP(size_t pos /*= 0*/, size_t n /*= npos*/) const
972{
973 RTCString ret;
974
975 if (n)
976 {
977 const char *psz;
978
979 if ((psz = c_str()))
980 {
981 RTUNICP cp;
982
983 // walk the UTF-8 characters until where the caller wants to start
984 size_t i = pos;
985 while (*psz && i--)
986 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
987 return ret; // return empty string on bad encoding
988
989 const char *pFirst = psz;
990
991 if (n == npos)
992 // all the rest:
993 ret = pFirst;
994 else
995 {
996 i = n;
997 while (*psz && i--)
998 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
999 return ret; // return empty string on bad encoding
1000
1001 size_t cbCopy = psz - pFirst;
1002 if (cbCopy)
1003 {
1004 ret.reserve(cbCopy + 1); // may throw bad_alloc
1005#ifndef RT_EXCEPTIONS_ENABLED
1006 AssertRelease(capacity() >= cbCopy + 1);
1007#endif
1008 memcpy(ret.m_psz, pFirst, cbCopy);
1009 ret.m_cch = cbCopy;
1010 ret.m_psz[cbCopy] = '\0';
1011 }
1012 }
1013 }
1014 }
1015
1016 return ret;
1017}
1018
1019bool RTCString::endsWith(const RTCString &a_rThat) const RT_NOEXCEPT
1020{
1021 size_t const cchThis = length();
1022 size_t const cchThat = a_rThat.length();
1023 if ( cchThat > 0
1024 && cchThat <= cchThis)
1025 return ::memcmp(&m_psz[cchThis - cchThat], a_rThat.m_psz, cchThat) == 0;
1026 return false;
1027}
1028
1029bool RTCString::endsWithI(const RTCString &a_rThat) const RT_NOEXCEPT
1030{
1031 size_t const cchThis = length();
1032 size_t const cchThat = a_rThat.length();
1033 if ( cchThat > 0
1034 && cchThat <= cchThis)
1035 return ::RTStrICmp(&m_psz[cchThis - cchThat], a_rThat.m_psz) == 0;
1036 return false;
1037}
1038
1039bool RTCString::endsWith(const char *a_pszSuffix, size_t a_cchSuffix) const RT_NOEXCEPT
1040{
1041 Assert(RTStrNLen(a_pszSuffix, a_cchSuffix) == a_cchSuffix);
1042 return a_cchSuffix > 0
1043 && a_cchSuffix <= length()
1044 && ::memcmp(&m_psz[length() - a_cchSuffix], a_pszSuffix, a_cchSuffix) == 0;
1045}
1046
1047bool RTCString::endsWith(const char *a_pszSuffix) const RT_NOEXCEPT
1048{
1049 if (a_pszSuffix)
1050 return endsWith(a_pszSuffix, strlen(a_pszSuffix));
1051 return false;
1052}
1053
1054bool RTCString::endsWithI(const char *a_pszSuffix, size_t a_cchSuffix) const RT_NOEXCEPT
1055{
1056 Assert(RTStrNLen(a_pszSuffix, a_cchSuffix) == a_cchSuffix);
1057 return a_cchSuffix > 0
1058 && a_cchSuffix <= length()
1059 && ::RTStrNICmp(&m_psz[length() - a_cchSuffix], a_pszSuffix, a_cchSuffix) == 0;
1060}
1061
1062bool RTCString::endsWithI(const char *a_pszSuffix) const RT_NOEXCEPT
1063{
1064 if (a_pszSuffix)
1065 return endsWithI(a_pszSuffix, strlen(a_pszSuffix));
1066 return false;
1067}
1068
1069bool RTCString::startsWith(const RTCString &a_rThat) const RT_NOEXCEPT
1070{
1071 size_t const cchThis = length();
1072 size_t const cchThat = a_rThat.length();
1073 if ( cchThat > 0
1074 && cchThat <= cchThis)
1075 return ::memcmp(m_psz, a_rThat.m_psz, cchThat) == 0;
1076 return false;
1077}
1078
1079bool RTCString::startsWithI(const RTCString &a_rThat) const RT_NOEXCEPT
1080{
1081 size_t const cchThis = length();
1082 size_t const cchThat = a_rThat.length();
1083 if ( cchThat > 0
1084 && cchThat <= cchThis)
1085 return ::RTStrNICmp(m_psz, a_rThat.m_psz, cchThat) == 0;
1086 return false;
1087}
1088
1089bool RTCString::startsWith(const char *a_pszPrefix, size_t a_cchPrefix) const RT_NOEXCEPT
1090{
1091 Assert(RTStrNLen(a_pszPrefix, a_cchPrefix) == a_cchPrefix);
1092 return a_cchPrefix > 0
1093 && a_cchPrefix <= length()
1094 && ::memcmp(m_psz, a_pszPrefix, a_cchPrefix) == 0;
1095}
1096
1097bool RTCString::startsWith(const char *a_pszPrefix) const RT_NOEXCEPT
1098{
1099 if (a_pszPrefix)
1100 return startsWith(a_pszPrefix, strlen(a_pszPrefix));
1101 return false;
1102}
1103
1104bool RTCString::startsWithI(const char *a_pszPrefix, size_t a_cchPrefix) const RT_NOEXCEPT
1105{
1106 Assert(RTStrNLen(a_pszPrefix, a_cchPrefix) == a_cchPrefix);
1107 return a_cchPrefix > 0
1108 && a_cchPrefix <= length()
1109 && ::RTStrNICmp(m_psz, a_pszPrefix, a_cchPrefix) == 0;
1110}
1111
1112bool RTCString::startsWithI(const char *a_pszPrefix) const RT_NOEXCEPT
1113{
1114 if (a_pszPrefix)
1115 return startsWithI(a_pszPrefix, strlen(a_pszPrefix));
1116 return false;
1117}
1118
1119bool RTCString::startsWithWord(const char *pszWord, CaseSensitivity enmCase /*= CaseSensitive*/) const RT_NOEXCEPT
1120{
1121 const char *pszSrc = RTStrStripL(c_str()); /** @todo RTStrStripL doesn't use RTUniCpIsSpace (nbsp) */
1122 size_t cchWord = strlen(pszWord);
1123 if ( enmCase == CaseSensitive
1124 ? RTStrNCmp(pszSrc, pszWord, cchWord) == 0
1125 : RTStrNICmp(pszSrc, pszWord, cchWord) == 0)
1126 {
1127 if ( pszSrc[cchWord] == '\0'
1128 || RT_C_IS_SPACE(pszSrc[cchWord])
1129 || RT_C_IS_PUNCT(pszSrc[cchWord]) )
1130 return true;
1131 RTUNICP uc = RTStrGetCp(&pszSrc[cchWord]);
1132 if (RTUniCpIsSpace(uc))
1133 return true;
1134 }
1135 return false;
1136}
1137
1138bool RTCString::startsWithWord(const RTCString &rThat, CaseSensitivity enmCase /*= CaseSensitive*/) const RT_NOEXCEPT
1139{
1140 return startsWithWord(rThat.c_str(), enmCase);
1141}
1142
1143bool RTCString::contains(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1144{
1145 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
1146 * endsWith only does half way). */
1147 if (cs == CaseSensitive)
1148 return ::RTStrStr(m_psz, that.m_psz) != NULL;
1149 return ::RTStrIStr(m_psz, that.m_psz) != NULL;
1150}
1151
1152bool RTCString::contains(const char *pszNeedle, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1153{
1154 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
1155 * endsWith only does half way). */
1156 if (cs == CaseSensitive)
1157 return ::RTStrStr(m_psz, pszNeedle) != NULL;
1158 return ::RTStrIStr(m_psz, pszNeedle) != NULL;
1159}
1160
1161int RTCString::toInt(uint64_t &i) const RT_NOEXCEPT
1162{
1163 if (!m_psz)
1164 return VERR_NO_DIGITS;
1165 return RTStrToUInt64Ex(m_psz, NULL, 0, &i);
1166}
1167
1168int RTCString::toInt(uint32_t &i) const RT_NOEXCEPT
1169{
1170 if (!m_psz)
1171 return VERR_NO_DIGITS;
1172 return RTStrToUInt32Ex(m_psz, NULL, 0, &i);
1173}
1174
1175RTCList<RTCString, RTCString *>
1176RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const
1177{
1178 RTCList<RTCString> strRet;
1179 if (!m_psz)
1180 return strRet;
1181 if (a_rstrSep.isEmpty())
1182 {
1183 strRet.append(RTCString(m_psz));
1184 return strRet;
1185 }
1186
1187 size_t cch = m_cch;
1188 char const *pszTmp = m_psz;
1189 while (cch > 0)
1190 {
1191 char const *pszNext = strstr(pszTmp, a_rstrSep.c_str());
1192 if (!pszNext)
1193 {
1194 strRet.append(RTCString(pszTmp, cch));
1195 break;
1196 }
1197 size_t cchNext = pszNext - pszTmp;
1198 if ( cchNext > 0
1199 || mode == KeepEmptyParts)
1200 strRet.append(RTCString(pszTmp, cchNext));
1201 pszTmp += cchNext + a_rstrSep.length();
1202 cch -= cchNext + a_rstrSep.length();
1203 }
1204
1205 return strRet;
1206}
1207
1208/* static */
1209RTCString
1210RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList,
1211 const RTCString &a_rstrPrefix /* = "" */,
1212 const RTCString &a_rstrSep /* = "" */)
1213{
1214 RTCString strRet;
1215 if (a_rList.size() > 1)
1216 {
1217 /* calc the required size */
1218 size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1;
1219 cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1;
1220 for (size_t i = 0; i < a_rList.size(); ++i)
1221 cbNeeded += a_rList.at(i).length();
1222 strRet.reserve(cbNeeded);
1223
1224 /* do the appending. */
1225 for (size_t i = 0; i < a_rList.size() - 1; ++i)
1226 {
1227 if (a_rstrPrefix.isNotEmpty())
1228 strRet.append(a_rstrPrefix);
1229 strRet.append(a_rList.at(i));
1230 strRet.append(a_rstrSep);
1231 }
1232 strRet.append(a_rList.last());
1233 }
1234 /* special case: one list item. */
1235 else if (a_rList.size() > 0)
1236 {
1237 if (a_rstrPrefix.isNotEmpty())
1238 strRet.append(a_rstrPrefix);
1239 strRet.append(a_rList.last());
1240 }
1241
1242 return strRet;
1243}
1244
1245/* static */
1246RTCString
1247RTCString::join(const RTCList<RTCString, RTCString *> &a_rList,
1248 const RTCString &a_rstrSep /* = "" */)
1249{
1250 return RTCString::joinEx(a_rList,
1251 "" /* a_rstrPrefix */, a_rstrSep);
1252}
1253
1254RTDECL(const RTCString) operator+(const RTCString &a_rStr1, const RTCString &a_rStr2)
1255{
1256 RTCString strRet(a_rStr1);
1257 strRet += a_rStr2;
1258 return strRet;
1259}
1260
1261RTDECL(const RTCString) operator+(const RTCString &a_rStr1, const char *a_pszStr2)
1262{
1263 RTCString strRet(a_rStr1);
1264 strRet += a_pszStr2;
1265 return strRet;
1266}
1267
1268RTDECL(const RTCString) operator+(const char *a_psz1, const RTCString &a_rStr2)
1269{
1270 RTCString strRet(a_psz1);
1271 strRet += a_rStr2;
1272 return strRet;
1273}
1274
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use