Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/extra/String.hpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |
4 * | |
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with | |
6 * or without fee is hereby granted, provided that the above copyright notice and this | |
7 * permission notice appear in all copies. | |
8 * | |
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 #ifndef DISTRHO_STRING_HPP_INCLUDED | |
18 #define DISTRHO_STRING_HPP_INCLUDED | |
19 | |
20 #include "../DistrhoUtils.hpp" | |
21 #include "../extra/ScopedSafeLocale.hpp" | |
22 | |
23 #include <algorithm> | |
24 | |
25 #if __cplusplus >= 201703L | |
26 # include <string_view> | |
27 #endif | |
28 | |
29 START_NAMESPACE_DISTRHO | |
30 | |
31 // ----------------------------------------------------------------------- | |
32 // String class | |
33 | |
34 class String | |
35 { | |
36 public: | |
37 // ------------------------------------------------------------------- | |
38 // constructors (no explicit conversions allowed) | |
39 | |
40 /* | |
41 * Empty string. | |
42 */ | |
43 explicit String() noexcept | |
44 : fBuffer(_null()), | |
45 fBufferLen(0), | |
46 fBufferAlloc(false) {} | |
47 | |
48 /* | |
49 * Simple character. | |
50 */ | |
51 explicit String(const char c) noexcept | |
52 : fBuffer(_null()), | |
53 fBufferLen(0), | |
54 fBufferAlloc(false) | |
55 { | |
56 char ch[2]; | |
57 ch[0] = c; | |
58 ch[1] = '\0'; | |
59 | |
60 _dup(ch); | |
61 } | |
62 | |
63 /* | |
64 * Simple char string. | |
65 */ | |
66 explicit String(char* const strBuf, const bool reallocData = true) noexcept | |
67 : fBuffer(_null()), | |
68 fBufferLen(0), | |
69 fBufferAlloc(false) | |
70 { | |
71 if (reallocData || strBuf == nullptr) | |
72 { | |
73 _dup(strBuf); | |
74 } | |
75 else | |
76 { | |
77 fBuffer = strBuf; | |
78 fBufferLen = std::strlen(strBuf); | |
79 fBufferAlloc = true; | |
80 } | |
81 } | |
82 | |
83 /* | |
84 * Simple const char string. | |
85 */ | |
86 explicit String(const char* const strBuf) noexcept | |
87 : fBuffer(_null()), | |
88 fBufferLen(0), | |
89 fBufferAlloc(false) | |
90 { | |
91 _dup(strBuf); | |
92 } | |
93 | |
94 #if __cplusplus >= 201703L | |
95 /* | |
96 * constexpr compatible variant. | |
97 */ | |
98 explicit constexpr String(const std::string_view& strView) noexcept | |
99 : fBuffer(const_cast<char*>(strView.data())), | |
100 fBufferLen(strView.size()), | |
101 fBufferAlloc(false) {} | |
102 #endif | |
103 | |
104 /* | |
105 * Integer. | |
106 */ | |
107 explicit String(const int value) noexcept | |
108 : fBuffer(_null()), | |
109 fBufferLen(0), | |
110 fBufferAlloc(false) | |
111 { | |
112 char strBuf[0xff+1]; | |
113 std::snprintf(strBuf, 0xff, "%d", value); | |
114 strBuf[0xff] = '\0'; | |
115 | |
116 _dup(strBuf); | |
117 } | |
118 | |
119 /* | |
120 * Unsigned integer, possibly in hexadecimal. | |
121 */ | |
122 explicit String(const unsigned int value, const bool hexadecimal = false) noexcept | |
123 : fBuffer(_null()), | |
124 fBufferLen(0), | |
125 fBufferAlloc(false) | |
126 { | |
127 char strBuf[0xff+1]; | |
128 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); | |
129 strBuf[0xff] = '\0'; | |
130 | |
131 _dup(strBuf); | |
132 } | |
133 | |
134 /* | |
135 * Long integer. | |
136 */ | |
137 explicit String(const long value) noexcept | |
138 : fBuffer(_null()), | |
139 fBufferLen(0), | |
140 fBufferAlloc(false) | |
141 { | |
142 char strBuf[0xff+1]; | |
143 std::snprintf(strBuf, 0xff, "%ld", value); | |
144 strBuf[0xff] = '\0'; | |
145 | |
146 _dup(strBuf); | |
147 } | |
148 | |
149 /* | |
150 * Long unsigned integer, possibly hexadecimal. | |
151 */ | |
152 explicit String(const unsigned long value, const bool hexadecimal = false) noexcept | |
153 : fBuffer(_null()), | |
154 fBufferLen(0), | |
155 fBufferAlloc(false) | |
156 { | |
157 char strBuf[0xff+1]; | |
158 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); | |
159 strBuf[0xff] = '\0'; | |
160 | |
161 _dup(strBuf); | |
162 } | |
163 | |
164 /* | |
165 * Long long integer. | |
166 */ | |
167 explicit String(const long long value) noexcept | |
168 : fBuffer(_null()), | |
169 fBufferLen(0), | |
170 fBufferAlloc(false) | |
171 { | |
172 char strBuf[0xff+1]; | |
173 std::snprintf(strBuf, 0xff, "%lld", value); | |
174 strBuf[0xff] = '\0'; | |
175 | |
176 _dup(strBuf); | |
177 } | |
178 | |
179 /* | |
180 * Long long unsigned integer, possibly hexadecimal. | |
181 */ | |
182 explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept | |
183 : fBuffer(_null()), | |
184 fBufferLen(0), | |
185 fBufferAlloc(false) | |
186 { | |
187 char strBuf[0xff+1]; | |
188 std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value); | |
189 strBuf[0xff] = '\0'; | |
190 | |
191 _dup(strBuf); | |
192 } | |
193 | |
194 /* | |
195 * Single-precision floating point number. | |
196 */ | |
197 explicit String(const float value) noexcept | |
198 : fBuffer(_null()), | |
199 fBufferLen(0), | |
200 fBufferAlloc(false) | |
201 { | |
202 char strBuf[0xff+1]; | |
203 | |
204 { | |
205 const ScopedSafeLocale ssl; | |
206 std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value)); | |
207 } | |
208 | |
209 strBuf[0xff] = '\0'; | |
210 | |
211 _dup(strBuf); | |
212 } | |
213 | |
214 /* | |
215 * Double-precision floating point number. | |
216 */ | |
217 explicit String(const double value) noexcept | |
218 : fBuffer(_null()), | |
219 fBufferLen(0), | |
220 fBufferAlloc(false) | |
221 { | |
222 char strBuf[0xff+1]; | |
223 | |
224 { | |
225 const ScopedSafeLocale ssl; | |
226 std::snprintf(strBuf, 0xff, "%.24g", value); | |
227 } | |
228 | |
229 strBuf[0xff] = '\0'; | |
230 | |
231 _dup(strBuf); | |
232 } | |
233 | |
234 // ------------------------------------------------------------------- | |
235 // non-explicit constructor | |
236 | |
237 /* | |
238 * Create string from another string. | |
239 */ | |
240 String(const String& str) noexcept | |
241 : fBuffer(_null()), | |
242 fBufferLen(0), | |
243 fBufferAlloc(false) | |
244 { | |
245 _dup(str.fBuffer); | |
246 } | |
247 | |
248 // ------------------------------------------------------------------- | |
249 // destructor | |
250 | |
251 /* | |
252 * Destructor. | |
253 */ | |
254 ~String() noexcept | |
255 { | |
256 DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,); | |
257 | |
258 if (fBufferAlloc) | |
259 std::free(fBuffer); | |
260 | |
261 fBuffer = nullptr; | |
262 fBufferLen = 0; | |
263 fBufferAlloc = false; | |
264 } | |
265 | |
266 // ------------------------------------------------------------------- | |
267 // public methods | |
268 | |
269 /* | |
270 * Get length of the string. | |
271 */ | |
272 std::size_t length() const noexcept | |
273 { | |
274 return fBufferLen; | |
275 } | |
276 | |
277 /* | |
278 * Check if the string is empty. | |
279 */ | |
280 bool isEmpty() const noexcept | |
281 { | |
282 return (fBufferLen == 0); | |
283 } | |
284 | |
285 /* | |
286 * Check if the string is not empty. | |
287 */ | |
288 bool isNotEmpty() const noexcept | |
289 { | |
290 return (fBufferLen != 0); | |
291 } | |
292 | |
293 /* | |
294 * Check if the string contains a specific character, case-sensitive. | |
295 */ | |
296 bool contains(const char c) const noexcept | |
297 { | |
298 for (std::size_t i=0; i<fBufferLen; ++i) | |
299 { | |
300 if (fBuffer[i] == c) | |
301 return true; | |
302 } | |
303 | |
304 return false; | |
305 } | |
306 | |
307 /* | |
308 * Check if the string contains another string, optionally ignoring case. | |
309 */ | |
310 bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept | |
311 { | |
312 DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false); | |
313 | |
314 if (ignoreCase) | |
315 { | |
316 #ifdef __USE_GNU | |
317 return (strcasestr(fBuffer, strBuf) != nullptr); | |
318 #else | |
319 String tmp1(fBuffer), tmp2(strBuf); | |
320 | |
321 // memory allocation failed or empty string(s) | |
322 if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null()) | |
323 return false; | |
324 | |
325 tmp1.toLower(); | |
326 tmp2.toLower(); | |
327 return (std::strstr(tmp1, tmp2) != nullptr); | |
328 #endif | |
329 } | |
330 | |
331 return (std::strstr(fBuffer, strBuf) != nullptr); | |
332 } | |
333 | |
334 /* | |
335 * Check if character at 'pos' is a digit. | |
336 */ | |
337 bool isDigit(const std::size_t pos) const noexcept | |
338 { | |
339 DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); | |
340 | |
341 return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); | |
342 } | |
343 | |
344 /* | |
345 * Check if the string starts with the character 'c'. | |
346 */ | |
347 bool startsWith(const char c) const noexcept | |
348 { | |
349 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); | |
350 | |
351 return (fBufferLen > 0 && fBuffer[0] == c); | |
352 } | |
353 | |
354 /* | |
355 * Check if the string starts with the string 'prefix'. | |
356 */ | |
357 bool startsWith(const char* const prefix) const noexcept | |
358 { | |
359 DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); | |
360 | |
361 const std::size_t prefixLen(std::strlen(prefix)); | |
362 | |
363 if (fBufferLen < prefixLen) | |
364 return false; | |
365 | |
366 return (std::strncmp(fBuffer, prefix, prefixLen) == 0); | |
367 } | |
368 | |
369 /* | |
370 * Check if the string ends with the character 'c'. | |
371 */ | |
372 bool endsWith(const char c) const noexcept | |
373 { | |
374 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); | |
375 | |
376 return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c); | |
377 } | |
378 | |
379 /* | |
380 * Check if the string ends with the string 'suffix'. | |
381 */ | |
382 bool endsWith(const char* const suffix) const noexcept | |
383 { | |
384 DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); | |
385 | |
386 const std::size_t suffixLen(std::strlen(suffix)); | |
387 | |
388 if (fBufferLen < suffixLen) | |
389 return false; | |
390 | |
391 return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); | |
392 } | |
393 | |
394 /* | |
395 * Find the first occurrence of character 'c' in the string. | |
396 * Returns "length()" if the character is not found. | |
397 */ | |
398 std::size_t find(const char c, bool* const found = nullptr) const noexcept | |
399 { | |
400 if (fBufferLen == 0 || c == '\0') | |
401 { | |
402 if (found != nullptr) | |
403 *found = false; | |
404 return fBufferLen; | |
405 } | |
406 | |
407 for (std::size_t i=0; i < fBufferLen; ++i) | |
408 { | |
409 if (fBuffer[i] == c) | |
410 { | |
411 if (found != nullptr) | |
412 *found = true; | |
413 return i; | |
414 } | |
415 } | |
416 | |
417 if (found != nullptr) | |
418 *found = false; | |
419 return fBufferLen; | |
420 } | |
421 | |
422 /* | |
423 * Find the first occurrence of string 'strBuf' in the string. | |
424 * Returns "length()" if the string is not found. | |
425 */ | |
426 std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |
427 { | |
428 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |
429 { | |
430 if (found != nullptr) | |
431 *found = false; | |
432 return fBufferLen; | |
433 } | |
434 | |
435 if (char* const subStrBuf = std::strstr(fBuffer, strBuf)) | |
436 { | |
437 const ssize_t ret(subStrBuf - fBuffer); | |
438 | |
439 if (ret < 0) | |
440 { | |
441 // should never happen! | |
442 d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret)); | |
443 | |
444 if (found != nullptr) | |
445 *found = false; | |
446 return fBufferLen; | |
447 } | |
448 | |
449 if (found != nullptr) | |
450 *found = true; | |
451 return static_cast<std::size_t>(ret); | |
452 } | |
453 | |
454 if (found != nullptr) | |
455 *found = false; | |
456 return fBufferLen; | |
457 } | |
458 | |
459 /* | |
460 * Find the last occurrence of character 'c' in the string. | |
461 * Returns "length()" if the character is not found. | |
462 */ | |
463 std::size_t rfind(const char c, bool* const found = nullptr) const noexcept | |
464 { | |
465 if (fBufferLen == 0 || c == '\0') | |
466 { | |
467 if (found != nullptr) | |
468 *found = false; | |
469 return fBufferLen; | |
470 } | |
471 | |
472 for (std::size_t i=fBufferLen; i > 0; --i) | |
473 { | |
474 if (fBuffer[i-1] == c) | |
475 { | |
476 if (found != nullptr) | |
477 *found = true; | |
478 return i-1; | |
479 } | |
480 } | |
481 | |
482 if (found != nullptr) | |
483 *found = false; | |
484 return fBufferLen; | |
485 } | |
486 | |
487 /* | |
488 * Find the last occurrence of string 'strBuf' in the string. | |
489 * Returns "length()" if the string is not found. | |
490 */ | |
491 std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |
492 { | |
493 if (found != nullptr) | |
494 *found = false; | |
495 | |
496 if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |
497 return fBufferLen; | |
498 | |
499 const std::size_t strBufLen(std::strlen(strBuf)); | |
500 | |
501 std::size_t ret = fBufferLen; | |
502 const char* tmpBuf = fBuffer; | |
503 | |
504 for (std::size_t i=0; i < fBufferLen; ++i) | |
505 { | |
506 if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) | |
507 { | |
508 if (found != nullptr) | |
509 *found = true; | |
510 break; | |
511 } | |
512 | |
513 --ret; | |
514 ++tmpBuf; | |
515 } | |
516 | |
517 return fBufferLen-ret; | |
518 } | |
519 | |
520 /* | |
521 * Clear the string. | |
522 */ | |
523 void clear() noexcept | |
524 { | |
525 truncate(0); | |
526 } | |
527 | |
528 /* | |
529 * Replace all occurrences of character 'before' with character 'after'. | |
530 */ | |
531 String& replace(const char before, const char after) noexcept | |
532 { | |
533 DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); | |
534 | |
535 for (std::size_t i=0; i < fBufferLen; ++i) | |
536 { | |
537 if (fBuffer[i] == before) | |
538 fBuffer[i] = after; | |
539 } | |
540 | |
541 return *this; | |
542 } | |
543 | |
544 /* | |
545 * Remove all occurrences of character 'c', shifting and truncating the string as necessary. | |
546 */ | |
547 String& remove(const char c) noexcept | |
548 { | |
549 DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this); | |
550 | |
551 if (fBufferLen == 0) | |
552 return *this; | |
553 | |
554 for (std::size_t i=0; i < fBufferLen; ++i) | |
555 { | |
556 if (fBuffer[i] == c) | |
557 { | |
558 --fBufferLen; | |
559 std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i); | |
560 } | |
561 } | |
562 | |
563 fBuffer[fBufferLen] = '\0'; | |
564 return *this; | |
565 } | |
566 | |
567 /* | |
568 * Truncate the string to size 'n'. | |
569 */ | |
570 String& truncate(const std::size_t n) noexcept | |
571 { | |
572 if (n >= fBufferLen) | |
573 return *this; | |
574 | |
575 fBuffer[n] = '\0'; | |
576 fBufferLen = n; | |
577 | |
578 return *this; | |
579 } | |
580 | |
581 /* | |
582 * Convert all non-basic characters to '_'. | |
583 */ | |
584 String& toBasic() noexcept | |
585 { | |
586 for (std::size_t i=0; i < fBufferLen; ++i) | |
587 { | |
588 if (fBuffer[i] >= '0' && fBuffer[i] <= '9') | |
589 continue; | |
590 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |
591 continue; | |
592 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |
593 continue; | |
594 if (fBuffer[i] == '_') | |
595 continue; | |
596 | |
597 fBuffer[i] = '_'; | |
598 } | |
599 | |
600 return *this; | |
601 } | |
602 | |
603 /* | |
604 * Convert all ascii characters to lowercase. | |
605 */ | |
606 String& toLower() noexcept | |
607 { | |
608 static const char kCharDiff('a' - 'A'); | |
609 | |
610 for (std::size_t i=0; i < fBufferLen; ++i) | |
611 { | |
612 if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |
613 fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | |
614 } | |
615 | |
616 return *this; | |
617 } | |
618 | |
619 /* | |
620 * Convert all ascii characters to uppercase. | |
621 */ | |
622 String& toUpper() noexcept | |
623 { | |
624 static const char kCharDiff('a' - 'A'); | |
625 | |
626 for (std::size_t i=0; i < fBufferLen; ++i) | |
627 { | |
628 if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |
629 fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | |
630 } | |
631 | |
632 return *this; | |
633 } | |
634 | |
635 /* | |
636 * Create a new string where all non-basic characters are converted to '_'. | |
637 * @see toBasic() | |
638 */ | |
639 String asBasic() const noexcept | |
640 { | |
641 String s(*this); | |
642 return s.toBasic(); | |
643 } | |
644 | |
645 /* | |
646 * Create a new string where all ascii characters are converted lowercase. | |
647 * @see toLower() | |
648 */ | |
649 String asLower() const noexcept | |
650 { | |
651 String s(*this); | |
652 return s.toLower(); | |
653 } | |
654 | |
655 /* | |
656 * Create a new string where all ascii characters are converted to uppercase. | |
657 * @see toUpper() | |
658 */ | |
659 String asUpper() const noexcept | |
660 { | |
661 String s(*this); | |
662 return s.toUpper(); | |
663 } | |
664 | |
665 /* | |
666 * Direct access to the string buffer (read-only). | |
667 */ | |
668 const char* buffer() const noexcept | |
669 { | |
670 return fBuffer; | |
671 } | |
672 | |
673 /* | |
674 * Get and release the string buffer, while also clearing this string. | |
675 * This allows to keep a pointer to the buffer after this object is deleted. | |
676 * Result must be freed. | |
677 */ | |
678 char* getAndReleaseBuffer() noexcept | |
679 { | |
680 char* ret = fBufferLen > 0 ? fBuffer : nullptr; | |
681 fBuffer = _null(); | |
682 fBufferLen = 0; | |
683 fBufferAlloc = false; | |
684 return ret; | |
685 } | |
686 | |
687 // ------------------------------------------------------------------- | |
688 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |
689 // Copyright (C) 2004-2008 René Nyffenegger | |
690 | |
691 static String asBase64(const void* const data, const std::size_t dataSize) | |
692 { | |
693 static const char* const kBase64Chars = | |
694 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
695 "abcdefghijklmnopqrstuvwxyz" | |
696 "0123456789+/"; | |
697 | |
698 #ifndef _MSC_VER | |
699 const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |
700 #else | |
701 constexpr std::size_t kTmpBufSize = 65536U; | |
702 #endif | |
703 | |
704 const uchar* bytesToEncode((const uchar*)data); | |
705 | |
706 uint i=0, j=0; | |
707 uint charArray3[3], charArray4[4]; | |
708 | |
709 char strBuf[kTmpBufSize + 1]; | |
710 strBuf[kTmpBufSize] = '\0'; | |
711 std::size_t strBufIndex = 0; | |
712 | |
713 String ret; | |
714 | |
715 for (std::size_t s=0; s<dataSize; ++s) | |
716 { | |
717 charArray3[i++] = *(bytesToEncode++); | |
718 | |
719 if (i == 3) | |
720 { | |
721 charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |
722 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); | |
723 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |
724 charArray4[3] = charArray3[2] & 0x3f; | |
725 | |
726 for (i=0; i<4; ++i) | |
727 strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | |
728 | |
729 if (strBufIndex >= kTmpBufSize-7) | |
730 { | |
731 strBuf[strBufIndex] = '\0'; | |
732 strBufIndex = 0; | |
733 ret += strBuf; | |
734 } | |
735 | |
736 i = 0; | |
737 } | |
738 } | |
739 | |
740 if (i != 0) | |
741 { | |
742 for (j=i; j<3; ++j) | |
743 charArray3[j] = '\0'; | |
744 | |
745 charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |
746 charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); | |
747 charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |
748 charArray4[3] = charArray3[2] & 0x3f; | |
749 | |
750 for (j=0; j<4 && i<3 && j<i+1; ++j) | |
751 strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | |
752 | |
753 for (; i++ < 3;) | |
754 strBuf[strBufIndex++] = '='; | |
755 } | |
756 | |
757 if (strBufIndex != 0) | |
758 { | |
759 strBuf[strBufIndex] = '\0'; | |
760 ret += strBuf; | |
761 } | |
762 | |
763 return ret; | |
764 } | |
765 | |
766 // ------------------------------------------------------------------- | |
767 // public operators | |
768 | |
769 operator const char*() const noexcept | |
770 { | |
771 return fBuffer; | |
772 } | |
773 | |
774 char operator[](const std::size_t pos) const noexcept | |
775 { | |
776 if (pos < fBufferLen) | |
777 return fBuffer[pos]; | |
778 | |
779 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | |
780 | |
781 static char fallback; | |
782 fallback = '\0'; | |
783 return fallback; | |
784 } | |
785 | |
786 char& operator[](const std::size_t pos) noexcept | |
787 { | |
788 if (pos < fBufferLen) | |
789 return fBuffer[pos]; | |
790 | |
791 d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | |
792 | |
793 static char fallback; | |
794 fallback = '\0'; | |
795 return fallback; | |
796 } | |
797 | |
798 bool operator==(const char* const strBuf) const noexcept | |
799 { | |
800 return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); | |
801 } | |
802 | |
803 bool operator==(const String& str) const noexcept | |
804 { | |
805 return operator==(str.fBuffer); | |
806 } | |
807 | |
808 bool operator!=(const char* const strBuf) const noexcept | |
809 { | |
810 return !operator==(strBuf); | |
811 } | |
812 | |
813 bool operator!=(const String& str) const noexcept | |
814 { | |
815 return !operator==(str.fBuffer); | |
816 } | |
817 | |
818 String& operator=(const char* const strBuf) noexcept | |
819 { | |
820 _dup(strBuf); | |
821 | |
822 return *this; | |
823 } | |
824 | |
825 String& operator=(const String& str) noexcept | |
826 { | |
827 _dup(str.fBuffer); | |
828 | |
829 return *this; | |
830 } | |
831 | |
832 String& operator+=(const char* const strBuf) noexcept | |
833 { | |
834 if (strBuf == nullptr || strBuf[0] == '\0') | |
835 return *this; | |
836 | |
837 const std::size_t strBufLen = std::strlen(strBuf); | |
838 | |
839 // for empty strings, we can just take the appended string as our entire data | |
840 if (isEmpty()) | |
841 { | |
842 _dup(strBuf, strBufLen); | |
843 return *this; | |
844 } | |
845 | |
846 // we have some data ourselves, reallocate to add the new stuff | |
847 char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1); | |
848 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); | |
849 | |
850 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); | |
851 | |
852 fBuffer = newBuf; | |
853 fBufferLen += strBufLen; | |
854 | |
855 return *this; | |
856 } | |
857 | |
858 String& operator+=(const String& str) noexcept | |
859 { | |
860 return operator+=(str.fBuffer); | |
861 } | |
862 | |
863 String operator+(const char* const strBuf) noexcept | |
864 { | |
865 if (strBuf == nullptr || strBuf[0] == '\0') | |
866 return *this; | |
867 if (isEmpty()) | |
868 return String(strBuf); | |
869 | |
870 const std::size_t strBufLen = std::strlen(strBuf); | |
871 const std::size_t newBufSize = fBufferLen + strBufLen; | |
872 char* const newBuf = (char*)malloc(newBufSize + 1); | |
873 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |
874 | |
875 std::memcpy(newBuf, fBuffer, fBufferLen); | |
876 std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); | |
877 | |
878 return String(newBuf, false); | |
879 } | |
880 | |
881 String operator+(const String& str) noexcept | |
882 { | |
883 return operator+(str.fBuffer); | |
884 } | |
885 | |
886 // needed for std::map compatibility | |
887 bool operator<(const String& str) const noexcept | |
888 { | |
889 return std::strcmp(fBuffer, str.fBuffer) < 0; | |
890 } | |
891 | |
892 // ------------------------------------------------------------------- | |
893 | |
894 private: | |
895 char* fBuffer; // the actual string buffer | |
896 std::size_t fBufferLen; // string length | |
897 bool fBufferAlloc; // wherever the buffer is allocated, not using _null() | |
898 | |
899 /* | |
900 * Static null string. | |
901 * Prevents allocation for new and/or empty strings. | |
902 */ | |
903 static char* _null() noexcept | |
904 { | |
905 static char sNull = '\0'; | |
906 return &sNull; | |
907 } | |
908 | |
909 /* | |
910 * Helper function. | |
911 * Called whenever the string needs to be allocated. | |
912 * | |
913 * Notes: | |
914 * - Allocates string only if 'strBuf' is not null and new string contents are different | |
915 * - If 'strBuf' is null, 'size' must be 0 | |
916 */ | |
917 void _dup(const char* const strBuf, const std::size_t size = 0) noexcept | |
918 { | |
919 if (strBuf != nullptr) | |
920 { | |
921 // don't recreate string if contents match | |
922 if (std::strcmp(fBuffer, strBuf) == 0) | |
923 return; | |
924 | |
925 if (fBufferAlloc) | |
926 std::free(fBuffer); | |
927 | |
928 fBufferLen = (size > 0) ? size : std::strlen(strBuf); | |
929 fBuffer = (char*)std::malloc(fBufferLen+1); | |
930 | |
931 if (fBuffer == nullptr) | |
932 { | |
933 fBuffer = _null(); | |
934 fBufferLen = 0; | |
935 fBufferAlloc = false; | |
936 return; | |
937 } | |
938 | |
939 fBufferAlloc = true; | |
940 | |
941 std::strcpy(fBuffer, strBuf); | |
942 fBuffer[fBufferLen] = '\0'; | |
943 } | |
944 else | |
945 { | |
946 DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size)); | |
947 | |
948 // don't recreate null string | |
949 if (! fBufferAlloc) | |
950 return; | |
951 | |
952 DISTRHO_SAFE_ASSERT(fBuffer != nullptr); | |
953 std::free(fBuffer); | |
954 | |
955 fBuffer = _null(); | |
956 fBufferLen = 0; | |
957 fBufferAlloc = false; | |
958 } | |
959 } | |
960 | |
961 DISTRHO_PREVENT_HEAP_ALLOCATION | |
962 }; | |
963 | |
964 // ----------------------------------------------------------------------- | |
965 | |
966 static inline | |
967 String operator+(const String& strBefore, const char* const strBufAfter) noexcept | |
968 { | |
969 if (strBufAfter == nullptr || strBufAfter[0] == '\0') | |
970 return strBefore; | |
971 if (strBefore.isEmpty()) | |
972 return String(strBufAfter); | |
973 | |
974 const std::size_t strBeforeLen = strBefore.length(); | |
975 const std::size_t strBufAfterLen = std::strlen(strBufAfter); | |
976 const std::size_t newBufSize = strBeforeLen + strBufAfterLen; | |
977 char* const newBuf = (char*)malloc(newBufSize + 1); | |
978 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |
979 | |
980 std::memcpy(newBuf, strBefore.buffer(), strBeforeLen); | |
981 std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1); | |
982 | |
983 return String(newBuf, false); | |
984 } | |
985 | |
986 static inline | |
987 String operator+(const char* const strBufBefore, const String& strAfter) noexcept | |
988 { | |
989 if (strAfter.isEmpty()) | |
990 return String(strBufBefore); | |
991 if (strBufBefore == nullptr || strBufBefore[0] == '\0') | |
992 return strAfter; | |
993 | |
994 const std::size_t strBufBeforeLen = std::strlen(strBufBefore); | |
995 const std::size_t strAfterLen = strAfter.length(); | |
996 const std::size_t newBufSize = strBufBeforeLen + strAfterLen; | |
997 char* const newBuf = (char*)malloc(newBufSize + 1); | |
998 DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |
999 | |
1000 std::memcpy(newBuf, strBufBefore, strBufBeforeLen); | |
1001 std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1); | |
1002 | |
1003 return String(newBuf, false); | |
1004 } | |
1005 | |
1006 // ----------------------------------------------------------------------- | |
1007 | |
1008 END_NAMESPACE_DISTRHO | |
1009 | |
1010 #endif // DISTRHO_STRING_HPP_INCLUDED |