VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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