VirtualBox

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

Last change on this file since 95685 was 95336, checked in by vboxsync, 2 years ago

IPRT/RTCString: Added a truncate() method that takes UTF-8 encoding into account when truncating the string. bugref:10185

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.9 KB
Line 
1/* $Id: ministring.cpp 95336 2022-06-21 20:48:58Z 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
776RTCString &RTCString::truncate(size_t cchMax) RT_NOEXCEPT
777{
778 if (cchMax < m_cch)
779 {
780 /*
781 * Make sure the truncated string ends with a correctly encoded
782 * codepoint and is not missing a few bytes.
783 */
784 if (cchMax > 0)
785 {
786 char chTail = m_psz[cchMax];
787 if ( (chTail & 0x80) == 0 /* single byte codepoint */
788 || (chTail & 0xc0) == 0xc0) /* first byte of codepoint sequence. */
789 { /* likely */ }
790 else
791 {
792 /* We need to find the start of the codepoint sequence: */
793 do
794 cchMax -= 1;
795 while ( cchMax > 0
796 && (m_psz[cchMax] & 0xc0) != 0xc0);
797 }
798 }
799
800 /*
801 * Do the truncating.
802 */
803 m_psz[cchMax] = '\0';
804 m_cch = cchMax;
805 }
806 return *this;
807}
808
809
810size_t RTCString::find(const char *pszNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
811{
812 if (offStart < length())
813 {
814 const char *pszThis = c_str();
815 if (pszThis)
816 {
817 if (pszNeedle && *pszNeedle != '\0')
818 {
819 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
820 if (pszHit)
821 return pszHit - pszThis;
822 }
823 }
824 }
825
826 return npos;
827}
828
829size_t RTCString::find(const RTCString *pStrNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
830{
831 if (offStart < length())
832 {
833 const char *pszThis = c_str();
834 if (pszThis)
835 {
836 if (pStrNeedle)
837 {
838 const char *pszNeedle = pStrNeedle->c_str();
839 if (pszNeedle && *pszNeedle != '\0')
840 {
841 const char *pszHit = strstr(pszThis + offStart, pszNeedle);
842 if (pszHit)
843 return pszHit - pszThis;
844 }
845 }
846 }
847 }
848
849 return npos;
850}
851
852
853size_t RTCString::find(const RTCString &rStrNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
854{
855 return find(&rStrNeedle, offStart);
856}
857
858
859size_t RTCString::find(const char chNeedle, size_t offStart /*= 0*/) const RT_NOEXCEPT
860{
861 Assert((unsigned int)chNeedle < 128U);
862 if (offStart < length())
863 {
864 const char *pszThis = c_str();
865 if (pszThis)
866 {
867 const char *pszHit = (const char *)memchr(&pszThis[offStart], chNeedle, length() - offStart);
868 if (pszHit)
869 return pszHit - pszThis;
870 }
871 }
872 return npos;
873}
874
875
876void RTCString::findReplace(char chFind, char chReplace) RT_NOEXCEPT
877{
878 Assert((unsigned int)chFind < 128U);
879 Assert((unsigned int)chReplace < 128U);
880
881 for (size_t i = 0; i < length(); ++i)
882 {
883 char *p = &m_psz[i];
884 if (*p == chFind)
885 *p = chReplace;
886 }
887}
888
889size_t RTCString::count(char ch) const RT_NOEXCEPT
890{
891 Assert((unsigned int)ch < 128U);
892
893 size_t c = 0;
894 const char *psz = m_psz;
895 if (psz)
896 {
897 char chCur;
898 while ((chCur = *psz++) != '\0')
899 if (chCur == ch)
900 c++;
901 }
902 return c;
903}
904
905#if 0 /** @todo implement these when needed. */
906size_t RTCString::count(const char *psz, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
907{
908}
909
910size_t RTCString::count(const RTCString *pStr, CaseSensitivity cs = CaseSensitive) const RT_NOEXCEPT
911{
912
913}
914#endif
915
916
917RTCString &RTCString::strip() RT_NOEXCEPT
918{
919 stripRight();
920 return stripLeft();
921}
922
923
924RTCString &RTCString::stripLeft() RT_NOEXCEPT
925{
926 char *psz = m_psz;
927 size_t const cch = m_cch;
928 size_t off = 0;
929 while (off < cch && RT_C_IS_SPACE(psz[off]))
930 off++;
931 if (off > 0)
932 {
933 if (off != cch)
934 {
935 memmove(psz, &psz[off], cch - off + 1);
936 m_cch = cch - off;
937 }
938 else
939 setNull();
940 }
941 return *this;
942}
943
944
945RTCString &RTCString::stripRight() RT_NOEXCEPT
946{
947 char *psz = m_psz;
948 size_t cch = m_cch;
949 while (cch > 0 && RT_C_IS_SPACE(psz[cch - 1]))
950 cch--;
951 if (m_cch != cch)
952 {
953 m_cch = cch;
954 psz[cch] = '\0';
955 }
956 return *this;
957}
958
959
960
961RTCString RTCString::substrCP(size_t pos /*= 0*/, size_t n /*= npos*/) const
962{
963 RTCString ret;
964
965 if (n)
966 {
967 const char *psz;
968
969 if ((psz = c_str()))
970 {
971 RTUNICP cp;
972
973 // walk the UTF-8 characters until where the caller wants to start
974 size_t i = pos;
975 while (*psz && i--)
976 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
977 return ret; // return empty string on bad encoding
978
979 const char *pFirst = psz;
980
981 if (n == npos)
982 // all the rest:
983 ret = pFirst;
984 else
985 {
986 i = n;
987 while (*psz && i--)
988 if (RT_FAILURE(RTStrGetCpEx(&psz, &cp)))
989 return ret; // return empty string on bad encoding
990
991 size_t cbCopy = psz - pFirst;
992 if (cbCopy)
993 {
994 ret.reserve(cbCopy + 1); // may throw bad_alloc
995#ifndef RT_EXCEPTIONS_ENABLED
996 AssertRelease(capacity() >= cbCopy + 1);
997#endif
998 memcpy(ret.m_psz, pFirst, cbCopy);
999 ret.m_cch = cbCopy;
1000 ret.m_psz[cbCopy] = '\0';
1001 }
1002 }
1003 }
1004 }
1005
1006 return ret;
1007}
1008
1009bool RTCString::endsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1010{
1011 size_t l1 = length();
1012 if (l1 == 0)
1013 return false;
1014
1015 size_t l2 = that.length();
1016 if (l1 < l2)
1017 return false;
1018
1019 if (!m_psz) /* Don't crash when running against an empty string. */
1020 return false;
1021
1022 /** @todo r=bird: See handling of l2 == in startsWith; inconsistent output (if l2 == 0, it matches anything). */
1023
1024 size_t l = l1 - l2;
1025 if (cs == CaseSensitive)
1026 return ::RTStrCmp(&m_psz[l], that.m_psz) == 0;
1027 return ::RTStrICmp(&m_psz[l], that.m_psz) == 0;
1028}
1029
1030bool RTCString::startsWith(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1031{
1032 size_t l1 = length();
1033 size_t l2 = that.length();
1034 if (l1 == 0 || l2 == 0) /** @todo r=bird: this differs from endsWith, and I think other IPRT code. If l2 == 0, it matches anything. */
1035 return false;
1036
1037 if (l1 < l2)
1038 return false;
1039
1040 if (cs == CaseSensitive)
1041 return ::RTStrNCmp(m_psz, that.m_psz, l2) == 0;
1042 return ::RTStrNICmp(m_psz, that.m_psz, l2) == 0;
1043}
1044
1045bool RTCString::startsWithWord(const char *pszWord, CaseSensitivity enmCase /*= CaseSensitive*/) const RT_NOEXCEPT
1046{
1047 const char *pszSrc = RTStrStripL(c_str()); /** @todo RTStrStripL doesn't use RTUniCpIsSpace (nbsp) */
1048 size_t cchWord = strlen(pszWord);
1049 if ( enmCase == CaseSensitive
1050 ? RTStrNCmp(pszSrc, pszWord, cchWord) == 0
1051 : RTStrNICmp(pszSrc, pszWord, cchWord) == 0)
1052 {
1053 if ( pszSrc[cchWord] == '\0'
1054 || RT_C_IS_SPACE(pszSrc[cchWord])
1055 || RT_C_IS_PUNCT(pszSrc[cchWord]) )
1056 return true;
1057 RTUNICP uc = RTStrGetCp(&pszSrc[cchWord]);
1058 if (RTUniCpIsSpace(uc))
1059 return true;
1060 }
1061 return false;
1062}
1063
1064bool RTCString::startsWithWord(const RTCString &rThat, CaseSensitivity enmCase /*= CaseSensitive*/) const RT_NOEXCEPT
1065{
1066 return startsWithWord(rThat.c_str(), enmCase);
1067}
1068
1069bool RTCString::contains(const RTCString &that, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1070{
1071 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
1072 * endsWith only does half way). */
1073 if (cs == CaseSensitive)
1074 return ::RTStrStr(m_psz, that.m_psz) != NULL;
1075 return ::RTStrIStr(m_psz, that.m_psz) != NULL;
1076}
1077
1078bool RTCString::contains(const char *pszNeedle, CaseSensitivity cs /*= CaseSensitive*/) const RT_NOEXCEPT
1079{
1080 /** @todo r-bird: Not checking for NULL strings like startsWith does (and
1081 * endsWith only does half way). */
1082 if (cs == CaseSensitive)
1083 return ::RTStrStr(m_psz, pszNeedle) != NULL;
1084 return ::RTStrIStr(m_psz, pszNeedle) != NULL;
1085}
1086
1087int RTCString::toInt(uint64_t &i) const RT_NOEXCEPT
1088{
1089 if (!m_psz)
1090 return VERR_NO_DIGITS;
1091 return RTStrToUInt64Ex(m_psz, NULL, 0, &i);
1092}
1093
1094int RTCString::toInt(uint32_t &i) const RT_NOEXCEPT
1095{
1096 if (!m_psz)
1097 return VERR_NO_DIGITS;
1098 return RTStrToUInt32Ex(m_psz, NULL, 0, &i);
1099}
1100
1101RTCList<RTCString, RTCString *>
1102RTCString::split(const RTCString &a_rstrSep, SplitMode mode /* = RemoveEmptyParts */) const
1103{
1104 RTCList<RTCString> strRet;
1105 if (!m_psz)
1106 return strRet;
1107 if (a_rstrSep.isEmpty())
1108 {
1109 strRet.append(RTCString(m_psz));
1110 return strRet;
1111 }
1112
1113 size_t cch = m_cch;
1114 char const *pszTmp = m_psz;
1115 while (cch > 0)
1116 {
1117 char const *pszNext = strstr(pszTmp, a_rstrSep.c_str());
1118 if (!pszNext)
1119 {
1120 strRet.append(RTCString(pszTmp, cch));
1121 break;
1122 }
1123 size_t cchNext = pszNext - pszTmp;
1124 if ( cchNext > 0
1125 || mode == KeepEmptyParts)
1126 strRet.append(RTCString(pszTmp, cchNext));
1127 pszTmp += cchNext + a_rstrSep.length();
1128 cch -= cchNext + a_rstrSep.length();
1129 }
1130
1131 return strRet;
1132}
1133
1134/* static */
1135RTCString
1136RTCString::joinEx(const RTCList<RTCString, RTCString *> &a_rList,
1137 const RTCString &a_rstrPrefix /* = "" */,
1138 const RTCString &a_rstrSep /* = "" */)
1139{
1140 RTCString strRet;
1141 if (a_rList.size() > 1)
1142 {
1143 /* calc the required size */
1144 size_t cbNeeded = a_rstrSep.length() * (a_rList.size() - 1) + 1;
1145 cbNeeded += a_rstrPrefix.length() * (a_rList.size() - 1) + 1;
1146 for (size_t i = 0; i < a_rList.size(); ++i)
1147 cbNeeded += a_rList.at(i).length();
1148 strRet.reserve(cbNeeded);
1149
1150 /* do the appending. */
1151 for (size_t i = 0; i < a_rList.size() - 1; ++i)
1152 {
1153 if (a_rstrPrefix.isNotEmpty())
1154 strRet.append(a_rstrPrefix);
1155 strRet.append(a_rList.at(i));
1156 strRet.append(a_rstrSep);
1157 }
1158 strRet.append(a_rList.last());
1159 }
1160 /* special case: one list item. */
1161 else if (a_rList.size() > 0)
1162 {
1163 if (a_rstrPrefix.isNotEmpty())
1164 strRet.append(a_rstrPrefix);
1165 strRet.append(a_rList.last());
1166 }
1167
1168 return strRet;
1169}
1170
1171/* static */
1172RTCString
1173RTCString::join(const RTCList<RTCString, RTCString *> &a_rList,
1174 const RTCString &a_rstrSep /* = "" */)
1175{
1176 return RTCString::joinEx(a_rList,
1177 "" /* a_rstrPrefix */, a_rstrSep);
1178}
1179
1180RTDECL(const RTCString) operator+(const RTCString &a_rStr1, const RTCString &a_rStr2)
1181{
1182 RTCString strRet(a_rStr1);
1183 strRet += a_rStr2;
1184 return strRet;
1185}
1186
1187RTDECL(const RTCString) operator+(const RTCString &a_rStr1, const char *a_pszStr2)
1188{
1189 RTCString strRet(a_rStr1);
1190 strRet += a_pszStr2;
1191 return strRet;
1192}
1193
1194RTDECL(const RTCString) operator+(const char *a_psz1, const RTCString &a_rStr2)
1195{
1196 RTCString strRet(a_psz1);
1197 strRet += a_rStr2;
1198 return strRet;
1199}
1200
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