StormByte-String 1.0.0
C++26 string module of the StormByte suite
 
Loading...
Searching...
No Matches
string.hxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024-2026 David C. Manuelda (StormBytePP)
3 *
4 * This file is part of StormByte-String.
5 *
6 * StormByte-String original source is dual-licensed:
7 *
8 * 1. GNU Lesser General Public License v3.0 (or later)
9 * You may redistribute and/or modify this file under the terms of the
10 * GNU Lesser General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * 2. Commercial license
15 * Alternatively, this file may be used under the terms of a commercial
16 * license agreement with the copyright holder
17 * (David C. Manuelda <StormByte@gmail.com>).
18 *
19 * Both licenses apply only to original StormByte-String source in this
20 * repository. They do not cover other StormByte modules or any third-party
21 * material shipped with this repository (including everything under
22 * thirdparty/, and in particular the bundled StormByte Base tree),
23 * which remains under its own license.
24 *
25 * Neither license grants any patent rights. Any patent licenses required
26 * to use this software or third-party components must be obtained separately
27 * from the patent holders.
28 *
29 * StormByte-String is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU Lesser General Public License for more details.
33 *
34 * You should have received a copy of the GNU Lesser General Public License
35 * version 3 along with StormByte-String. If not, see
36 * <https://www.gnu.org/licenses/lgpl-3.0.html>.
37 *
38 * SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-StormByte-Commercial
39 */
40
41#pragma once
42
43#include <StormByte/cstring.hxx>
45
46#include <cctype>
47#include <compare>
48#include <cstddef>
49#include <functional>
50#include <iterator>
51#include <ostream>
52#include <queue>
53#include <string>
54#include <string_view>
55#include <vector>
56
61namespace StormByte {
66 namespace String {
71 class WString;
72
98 public:
99 using value_type = char;
100 using const_iterator = const char*;
101 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
102
111 String() noexcept;
112
117 explicit String(const char* str) noexcept;
118
123 explicit String(std::string_view str) noexcept;
124
129 explicit String(CString text) noexcept;
130
135 explicit String(const WString& other) noexcept;
136
141 String(const String& other) noexcept;
142
147 String(String&& other) noexcept;
148
152 ~String() noexcept = default;
153
159 String& operator=(const String& other) noexcept;
160
166 String& operator=(String&& other) noexcept;
167
179 inline const_iterator begin() const noexcept {
180 return data();
181 }
182
187 inline const_iterator end() const noexcept {
188 const char* text = data();
189 return text ? text + size() : nullptr;
190 }
191
196 inline const_iterator cbegin() const noexcept {
197 return begin();
198 }
199
204 inline const_iterator cend() const noexcept {
205 return end();
206 }
207
212 inline const_reverse_iterator rbegin() const noexcept {
213 return const_reverse_iterator(end());
214 }
215
220 inline const_reverse_iterator rend() const noexcept {
221 return const_reverse_iterator(begin());
222 }
223
228 inline const_reverse_iterator crbegin() const noexcept {
229 return rbegin();
230 }
231
236 inline const_reverse_iterator crend() const noexcept {
237 return rend();
238 }
239
244 inline const char* data() const noexcept {
245 return static_cast<const char*>(m_text);
246 }
247
252 inline Size size() const noexcept {
253 return m_text.Length();
254 }
255
260 inline Size length() const noexcept {
261 return size();
262 }
263
268 inline bool empty() const noexcept {
269 return size() == 0;
270 }
271
278 inline char operator[](const Size& index) const noexcept {
279 return m_text[index];
280 }
281
286 inline explicit operator bool() const noexcept {
287 return static_cast<bool>(m_text);
288 }
289
302 inline operator std::string_view() const noexcept {
303 return static_cast<std::string_view>(m_text);
304 }
305
310 STORMBYTE_FORCE_INLINE explicit operator std::string() const {
311 return static_cast<std::string>(m_text);
312 }
313
319 inline explicit operator const char*() const noexcept {
320 return static_cast<const char*>(m_text);
321 }
322
327 explicit operator WString() const noexcept;
328
333 inline const CString& Bytes() const noexcept {
334 return m_text;
335 }
336
344 static constexpr Size npos{~0ull};
345
351 inline bool starts_with(std::string_view text) const noexcept {
352 return static_cast<std::string_view>(*this).starts_with(text);
353 }
354
360 inline bool starts_with(char ch) const noexcept {
361 return static_cast<std::string_view>(*this).starts_with(ch);
362 }
363
369 inline bool ends_with(std::string_view text) const noexcept {
370 return static_cast<std::string_view>(*this).ends_with(text);
371 }
372
378 inline bool ends_with(char ch) const noexcept {
379 return static_cast<std::string_view>(*this).ends_with(ch);
380 }
381
387 inline bool contains(std::string_view text) const noexcept {
388 return static_cast<std::string_view>(*this).contains(text);
389 }
390
396 inline bool contains(char ch) const noexcept {
397 return static_cast<std::string_view>(*this).contains(ch);
398 }
399
406 inline Size find(std::string_view text, Size pos = {}) const noexcept {
407 return FindAt(static_cast<std::string_view>(*this), text, pos);
408 }
409
416 inline Size find(char ch, Size pos = {}) const noexcept {
417 return FindAt(static_cast<std::string_view>(*this), ch, pos);
418 }
419
427 inline Size find(const char* text, Size pos, Size count) const noexcept {
428 return find(std::string_view(text, static_cast<std::size_t>(count)), pos);
429 }
430
437 inline Size rfind(std::string_view text, Size pos = npos) const noexcept {
438 return RFindAt(static_cast<std::string_view>(*this), text, pos);
439 }
440
447 inline Size rfind(char ch, Size pos = npos) const noexcept {
448 return RFindAt(static_cast<std::string_view>(*this), ch, pos);
449 }
450
458 inline Size rfind(const char* text, Size pos, Size count) const noexcept {
459 return rfind(std::string_view(text, static_cast<std::size_t>(count)), pos);
460 }
461
468 inline Size find_first_of(std::string_view text, Size pos = {}) const noexcept {
469 const std::string_view self = *this;
470 if (pos > Size{self.size()})
471 return npos;
472 return FromIndex(self.find_first_of(text, static_cast<std::size_t>(pos)));
473 }
474
481 inline Size find_first_of(char ch, Size pos = {}) const noexcept {
482 return find(ch, pos);
483 }
484
491 inline Size find_last_of(std::string_view text, Size pos = npos) const noexcept {
492 const std::string_view self = *this;
493 const std::size_t start = pos == npos ? std::string_view::npos : static_cast<std::size_t>(pos);
494 return FromIndex(self.find_last_of(text, start));
495 }
496
503 inline Size find_last_of(char ch, Size pos = npos) const noexcept {
504 return rfind(ch, pos);
505 }
506
513 inline Size find_first_not_of(std::string_view text, Size pos = {}) const noexcept {
514 const std::string_view self = *this;
515 if (pos > Size{self.size()})
516 return npos;
517 return FromIndex(self.find_first_not_of(text, static_cast<std::size_t>(pos)));
518 }
519
526 inline Size find_first_not_of(char ch, Size pos = {}) const noexcept {
527 const std::string_view self = *this;
528 if (pos > Size{self.size()})
529 return npos;
530 return FromIndex(self.find_first_not_of(ch, static_cast<std::size_t>(pos)));
531 }
532
539 inline Size find_last_not_of(std::string_view text, Size pos = npos) const noexcept {
540 const std::string_view self = *this;
541 const std::size_t start = pos == npos ? std::string_view::npos : static_cast<std::size_t>(pos);
542 return FromIndex(self.find_last_not_of(text, start));
543 }
544
551 inline Size find_last_not_of(char ch, Size pos = npos) const noexcept {
552 const std::string_view self = *this;
553 const std::size_t start = pos == npos ? std::string_view::npos : static_cast<std::size_t>(pos);
554 return FromIndex(self.find_last_not_of(ch, start));
555 }
556
563 inline String substr(Size pos = {}, Size count = npos) const noexcept {
564 const std::string_view text = *this;
565 if (pos > Size{text.size()})
566 return String();
567 const std::size_t n = count == npos ? std::string_view::npos : static_cast<std::size_t>(count);
568 return String(text.substr(static_cast<std::size_t>(pos), n));
569 }
570
576 inline int compare(std::string_view text) const noexcept {
577 return static_cast<std::string_view>(*this).compare(text);
578 }
579
585 inline char front() const noexcept {
586 return (*this)[Size{0}];
587 }
588
594 inline char back() const noexcept {
595 return (*this)[size() - Size{1}];
596 }
597
610 static String ToLower(std::string_view str) noexcept;
611
617 static String ToUpper(std::string_view str) noexcept;
618
624 static String SanitizeNewlines(std::string_view str) noexcept;
625
631 static String RemoveWhitespace(std::string_view str) noexcept;
632
638 static bool IsInteger(std::string_view str) noexcept;
639
646 static STORMBYTE_FORCE_INLINE void Split(std::string_view str, std::vector<String>& out) noexcept {
647 out.clear();
648 std::size_t i = 0;
649 while (i < str.size()) {
650 while (i < str.size() && std::isspace(static_cast<unsigned char>(str[i])) != 0)
651 ++i;
652 if (i >= str.size())
653 break;
654 std::size_t j = i;
655 while (j < str.size() && std::isspace(static_cast<unsigned char>(str[j])) == 0)
656 ++j;
657 out.emplace_back(str.substr(i, j - i));
658 i = j;
659 }
660 }
661
669 static STORMBYTE_FORCE_INLINE void Explode(std::string_view str, char delimiter, std::queue<String>& out) noexcept {
670 while (!out.empty())
671 out.pop();
672 std::size_t start = 0;
673 for (std::size_t i = 0; i <= str.size(); ++i) {
674 if (i == str.size() || str[i] == delimiter) {
675 out.emplace(str.substr(start, i - start));
676 start = i + 1;
677 }
678 }
679 }
680
685 inline String ToLower() const noexcept {
686 return ToLower(static_cast<std::string_view>(*this));
687 }
688
693 inline String ToUpper() const noexcept {
694 return ToUpper(static_cast<std::string_view>(*this));
695 }
696
701 inline String SanitizeNewlines() const noexcept {
702 return SanitizeNewlines(static_cast<std::string_view>(*this));
703 }
704
709 inline String RemoveWhitespace() const noexcept {
710 return RemoveWhitespace(static_cast<std::string_view>(*this));
711 }
712
717 inline bool IsInteger() const noexcept {
718 return IsInteger(static_cast<std::string_view>(*this));
719 }
720
725 STORMBYTE_FORCE_INLINE std::vector<String> Split() const noexcept {
726 std::vector<String> out;
727 Split(static_cast<std::string_view>(*this), out);
728 return out;
729 }
730
736 STORMBYTE_FORCE_INLINE std::queue<String> Explode(char delimiter) const noexcept {
737 std::queue<String> out;
738 Explode(static_cast<std::string_view>(*this), delimiter, out);
739 return out;
740 }
741
754 inline bool operator==(const String& other) const noexcept {
755 return m_text == other.m_text;
756 }
757
763 inline bool operator!=(const String& other) const noexcept {
764 return !(*this == other);
765 }
766
772 inline bool operator==(const char* str) const noexcept {
773 return m_text == str;
774 }
775
781 inline bool operator!=(const char* str) const noexcept {
782 return !(*this == str);
783 }
784
790 inline std::strong_ordering operator<=>(const String& other) const noexcept {
791 return m_text <=> other.m_text;
792 }
793
799 inline std::strong_ordering operator<=>(const char* str) const noexcept {
800 return m_text <=> str;
801 }
802
809 void swap(String& other) noexcept;
810
811 private:
812 static Size FromIndex(std::size_t index) noexcept {
813 return index == std::string_view::npos ? npos : Size{index};
814 }
815
816 template<typename Needle>
817 static Size FindAt(std::string_view self, Needle needle, Size pos) noexcept {
818 if (pos > Size{self.size()})
819 return npos;
820 return FromIndex(self.find(needle, static_cast<std::size_t>(pos)));
821 }
822
823 template<typename Needle>
824 static Size RFindAt(std::string_view self, Needle needle, Size pos) noexcept {
825 const std::size_t start = pos == npos ? std::string_view::npos : static_cast<std::size_t>(pos);
826 return FromIndex(self.rfind(needle, start));
827 }
828
829 CString m_text;
830 };
831
838 inline std::ostream& operator<<(std::ostream& stream, const String& text) {
839 return stream << text.Bytes();
840 }
841
848 inline bool operator==(const char* str, const String& text) noexcept {
849 return text == str;
850 }
851
858 inline bool operator!=(const char* str, const String& text) noexcept {
859 return text != str;
860 }
861
867 inline void swap(String& left, String& right) noexcept {
868 left.swap(right);
869 }
870 }
871}
872
876template<>
877struct std::hash<StormByte::String::String> {
883 std::size_t operator()(const StormByte::String::String& text) const noexcept {
884 return std::hash<std::string_view>{}(static_cast<std::string_view>(text));
885 }
886};
Owned UTF-8 text composed of StormByte::CString.
Definition string.hxx:97
std::strong_ordering operator<=>(const String &other) const noexcept
Content order.
Definition string.hxx:790
Size find_last_not_of(std::string_view text, Size pos=npos) const noexcept
Last byte that is not in text, at or before pos.
Definition string.hxx:539
char value_type
Byte type.
Definition string.hxx:99
char operator[](const Size &index) const noexcept
Character at index.
Definition string.hxx:278
String substr(Size pos={}, Size count=npos) const noexcept
Copy of a slice.
Definition string.hxx:563
Size size() const noexcept
Character count; 0 when null or empty.
Definition string.hxx:252
bool ends_with(char ch) const noexcept
Whether the text ends with ch.
Definition string.hxx:378
Size length() const noexcept
Same as size.
Definition string.hxx:260
const_iterator cbegin() const noexcept
First character, or null.
Definition string.hxx:196
String ToUpper() const noexcept
ASCII-letter upper case of this text.
Definition string.hxx:693
Size find_first_not_of(char ch, Size pos={}) const noexcept
First byte other than ch, at or after pos.
Definition string.hxx:526
bool operator!=(const String &other) const noexcept
Content inequality.
Definition string.hxx:763
Size find(const char *text, Size pos, Size count) const noexcept
First occurrence of count bytes of text.
Definition string.hxx:427
static String RemoveWhitespace(std::string_view str) noexcept
Drops isspace bytes.
std::reverse_iterator< const_iterator > const_reverse_iterator
Reverse observer.
Definition string.hxx:101
bool contains(char ch) const noexcept
Whether ch occurs.
Definition string.hxx:396
bool empty() const noexcept
Whether size is zero.
Definition string.hxx:268
char back() const noexcept
Last byte.
Definition string.hxx:594
bool starts_with(char ch) const noexcept
Whether the text begins with ch.
Definition string.hxx:360
static String ToLower(std::string_view str) noexcept
ASCII-letter lower case; UTF-8 otherwise copied.
Size find(std::string_view text, Size pos={}) const noexcept
First occurrence of text at or after pos.
Definition string.hxx:406
void swap(String &other) noexcept
Swaps buffers with other.
int compare(std::string_view text) const noexcept
Same order as std::string_view::compare.
Definition string.hxx:576
static String ToUpper(std::string_view str) noexcept
ASCII-letter upper case; UTF-8 otherwise copied.
bool operator==(const String &other) const noexcept
Content equality.
Definition string.hxx:754
const_reverse_iterator rbegin() const noexcept
Reverse begin.
Definition string.hxx:212
Size find_last_not_of(char ch, Size pos=npos) const noexcept
Last byte other than ch, at or before pos.
Definition string.hxx:551
String ToLower() const noexcept
ASCII-letter lower case of this text.
Definition string.hxx:685
Size find(char ch, Size pos={}) const noexcept
First occurrence of ch at or after pos.
Definition string.hxx:416
const_iterator end() const noexcept
One past the last character, or null.
Definition string.hxx:187
char front() const noexcept
First byte.
Definition string.hxx:585
static STORMBYTE_FORCE_INLINE void Explode(std::string_view str, char delimiter, std::queue< String > &out) noexcept
Tokens on delimiter.
Definition string.hxx:669
static bool IsInteger(std::string_view str) noexcept
Optional sign plus ASCII digits.
bool operator==(const char *str) const noexcept
Content equality with a C string.
Definition string.hxx:772
String() noexcept
Null text.
Size rfind(char ch, Size pos=npos) const noexcept
Last occurrence of ch at or before pos.
Definition string.hxx:447
String RemoveWhitespace() const noexcept
Drops isspace bytes from this text.
Definition string.hxx:709
bool starts_with(std::string_view text) const noexcept
Whether the text begins with text.
Definition string.hxx:351
STORMBYTE_FORCE_INLINE std::queue< String > Explode(char delimiter) const noexcept
Tokens on delimiter.
Definition string.hxx:736
STORMBYTE_FORCE_INLINE std::vector< String > Split() const noexcept
Whitespace-separated tokens.
Definition string.hxx:725
bool ends_with(std::string_view text) const noexcept
Whether the text ends with text.
Definition string.hxx:369
String SanitizeNewlines() const noexcept
CR LF to LF on this text.
Definition string.hxx:701
Size rfind(std::string_view text, Size pos=npos) const noexcept
Last occurrence of text at or before pos.
Definition string.hxx:437
Size find_last_of(std::string_view text, Size pos=npos) const noexcept
Last byte that is in text, at or before pos.
Definition string.hxx:491
Size find_first_not_of(std::string_view text, Size pos={}) const noexcept
First byte that is not in text, at or after pos.
Definition string.hxx:513
const_iterator cend() const noexcept
One past the last character, or null.
Definition string.hxx:204
std::strong_ordering operator<=>(const char *str) const noexcept
Content order against a C string.
Definition string.hxx:799
const char * const_iterator
Contiguous observer.
Definition string.hxx:100
static String SanitizeNewlines(std::string_view str) noexcept
Turns CR LF into LF.
Size find_first_of(char ch, Size pos={}) const noexcept
First ch at or after pos.
Definition string.hxx:481
bool contains(std::string_view text) const noexcept
Whether text occurs.
Definition string.hxx:387
Size rfind(const char *text, Size pos, Size count) const noexcept
Last occurrence of count bytes of text.
Definition string.hxx:458
const char * data() const noexcept
Contiguous pointer; null when the buffer is null.
Definition string.hxx:244
Size find_last_of(char ch, Size pos=npos) const noexcept
Last ch at or before pos.
Definition string.hxx:503
static STORMBYTE_FORCE_INLINE void Split(std::string_view str, std::vector< String > &out) noexcept
Whitespace-separated tokens.
Definition string.hxx:646
const_reverse_iterator rend() const noexcept
Reverse end.
Definition string.hxx:220
bool operator!=(const char *str) const noexcept
Content inequality with a C string.
Definition string.hxx:781
bool IsInteger() const noexcept
Whether this text is an integer token.
Definition string.hxx:717
Size find_first_of(std::string_view text, Size pos={}) const noexcept
First byte that is in text, at or after pos.
Definition string.hxx:468
const_reverse_iterator crend() const noexcept
Reverse end.
Definition string.hxx:236
const_reverse_iterator crbegin() const noexcept
Reverse begin.
Definition string.hxx:228
Wide counterpart of String.
Definition wstring.hxx:96
void swap(String &left, String &right) noexcept
Swaps two texts.
Definition string.hxx:867
std::ostream & operator<<(std::ostream &stream, const String &text)
Writes text to stream.
Definition string.hxx:838
bool operator==(const char *str, const String &text) noexcept
Content equality.
Definition string.hxx:848
bool operator!=(const char *str, const String &text) noexcept
Content inequality.
Definition string.hxx:858
Root namespace of the StormByte suite.
String module: owned UTF-8 and wide text on top of StormByte::CString / StormByte::WCString.
STL namespace.
#define STORMBYTE_FORCE_INLINE
std::size_t operator()(const StormByte::String::String &text) const noexcept
Hashes text.
Definition string.hxx:883
#define STORMBYTE_STRING_PUBLIC
Definition visibility.h:53