VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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