VirtualBox

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

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

IPRT/ministring: Fixed bug in new no-throw printf methods. bugref:9167

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