usearch 2.9.2 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +39 -38
  2. package/javascript/{types → dist/cjs}/usearch.d.ts +90 -115
  3. package/javascript/dist/cjs/usearch.js +531 -0
  4. package/javascript/dist/esm/usearch.d.ts +212 -0
  5. package/javascript/{usearch.js → dist/esm/usearch.js} +199 -153
  6. package/package.json +25 -23
  7. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  8. package/prebuilds/linux-arm64/usearch.node +0 -0
  9. package/prebuilds/linux-x64/usearch.node +0 -0
  10. package/prebuilds/win32-ia32/usearch.node +0 -0
  11. package/prebuilds/win32-x64/usearch.node +0 -0
  12. package/.clang-format +0 -10
  13. package/.clang-tidy +0 -60
  14. package/.cmake-format.py +0 -19
  15. package/.dockerignore +0 -93
  16. package/.gitattributes +0 -9
  17. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  18. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  19. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -64
  20. package/.github/workflows/Dockerfile.libs +0 -36
  21. package/.github/workflows/prerelease.yml +0 -331
  22. package/.github/workflows/release.yml +0 -722
  23. package/.github/workflows/update_version.sh +0 -15
  24. package/.gitmodules +0 -9
  25. package/.releaserc +0 -90
  26. package/CITATION.cff +0 -11
  27. package/CMakeLists.txt +0 -324
  28. package/CODE_OF_CONDUCT.md +0 -128
  29. package/CONTRIBUTING.md +0 -361
  30. package/Cargo.toml +0 -19
  31. package/Dockerfile +0 -16
  32. package/VERSION +0 -1
  33. package/assets/unum.png +0 -0
  34. package/assets/usearch-approaches-transparent.png +0 -0
  35. package/assets/usearch-approaches-white.png +0 -0
  36. package/assets/usearch-neighbor-types.png +0 -0
  37. package/binding.gyp +0 -87
  38. package/build.gradle +0 -204
  39. package/build.rs +0 -18
  40. package/c/CMakeLists.txt +0 -25
  41. package/c/README.md +0 -34
  42. package/c/lib.cpp +0 -358
  43. package/c/test.c +0 -382
  44. package/c/usearch.h +0 -311
  45. package/cmake/clipp.cmake +0 -7
  46. package/cmake/config.cmake.in +0 -15
  47. package/cmake/emscripten.cmake +0 -8
  48. package/cmake/jemalloc.cmake +0 -62
  49. package/cmake/pkg-config.pc.in +0 -4
  50. package/cmake/usearchConfigVersion.cmake.in +0 -20
  51. package/conanfile.py +0 -26
  52. package/cpp/CMakeLists.txt +0 -14
  53. package/cpp/README.md +0 -161
  54. package/cpp/bench.cpp +0 -618
  55. package/cpp/test.cpp +0 -323
  56. package/fp16/CMakeLists.txt +0 -176
  57. package/fp16/LICENSE +0 -11
  58. package/fp16/README.md +0 -20
  59. package/fp16/bench/alt-element.cc +0 -89
  60. package/fp16/bench/from-alt-array.cc +0 -175
  61. package/fp16/bench/from-ieee-array.cc +0 -399
  62. package/fp16/bench/ieee-element.cc +0 -272
  63. package/fp16/bench/to-alt-array.cc +0 -91
  64. package/fp16/bench/to-ieee-array.cc +0 -303
  65. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  66. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  67. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  68. package/fp16/configure.py +0 -65
  69. package/fp16/confu.yaml +0 -8
  70. package/fp16/include/fp16/__init__.py +0 -0
  71. package/fp16/include/fp16/avx.py +0 -50
  72. package/fp16/include/fp16/avx2.py +0 -53
  73. package/fp16/include/fp16/bitcasts.h +0 -92
  74. package/fp16/include/fp16/fp16.h +0 -451
  75. package/fp16/include/fp16/psimd.h +0 -131
  76. package/fp16/include/fp16.h +0 -11
  77. package/fp16/jni/Android.mk +0 -6
  78. package/fp16/jni/Application.mk +0 -4
  79. package/fp16/test/alt-from-fp32-value.cc +0 -498
  80. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  81. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  82. package/fp16/test/alt-to-fp32-value.cc +0 -348
  83. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  84. package/fp16/test/bitcasts.cc +0 -56
  85. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  86. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  87. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  88. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  89. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  90. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  91. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  92. package/fp16/test/peachpy/stubs.py +0 -38
  93. package/fp16/test/tables.cc +0 -530
  94. package/fp16/test/tables.h +0 -13
  95. package/fp16/third-party/THHalf.h +0 -92
  96. package/fp16/third-party/eigen-half.h +0 -170
  97. package/fp16/third-party/float16-compressor.h +0 -80
  98. package/fp16/third-party/half.hpp +0 -2916
  99. package/fp16/third-party/npy-halffloat.h +0 -328
  100. package/include/usearch/index.hpp +0 -3836
  101. package/include/usearch/index_dense.hpp +0 -2004
  102. package/include/usearch/index_plugins.hpp +0 -2213
  103. package/javascript/lib.cpp +0 -341
  104. package/javascript/types/usearch.d.ts.map +0 -1
  105. package/javascript/usearch.test.js +0 -74
  106. package/package-ci.json +0 -10
  107. package/pyproject.toml +0 -51
  108. package/setup.py +0 -201
  109. package/simsimd/.releaserc +0 -86
  110. package/simsimd/CONTRIBUTING.md +0 -88
  111. package/simsimd/Cargo.lock +0 -709
  112. package/simsimd/Cargo.toml +0 -44
  113. package/simsimd/LICENSE +0 -201
  114. package/simsimd/README.md +0 -317
  115. package/simsimd/binding.gyp +0 -36
  116. package/simsimd/build.rs +0 -20
  117. package/simsimd/include/simsimd/binary.h +0 -245
  118. package/simsimd/include/simsimd/complex.h +0 -174
  119. package/simsimd/include/simsimd/probability.h +0 -548
  120. package/simsimd/include/simsimd/simsimd.h +0 -465
  121. package/simsimd/include/simsimd/spatial.h +0 -1267
  122. package/simsimd/include/simsimd/types.h +0 -208
  123. package/simsimd/javascript/bench.js +0 -127
  124. package/simsimd/javascript/fallback.ts +0 -193
  125. package/simsimd/javascript/lib.c +0 -102
  126. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  127. package/simsimd/javascript/simsimd.ts +0 -124
  128. package/simsimd/javascript/test.mjs +0 -153
  129. package/simsimd/javascript/tsconfig-base.json +0 -25
  130. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  131. package/simsimd/javascript/tsconfig-esm.json +0 -8
  132. package/simsimd/package-lock.json +0 -1688
  133. package/simsimd/package.json +0 -61
  134. package/simsimd/rust/benches/cosine.rs +0 -27
  135. package/simsimd/rust/benches/native.rs +0 -30
  136. package/simsimd/rust/benches/sqeuclidean.rs +0 -27
  137. package/simsimd/rust/lib.c +0 -45
  138. package/simsimd/rust/lib.rs +0 -550
  139. package/sqlite/README.md +0 -178
  140. package/sqlite/lib.cpp +0 -325
  141. package/stringzilla/CONTRIBUTING.md +0 -446
  142. package/stringzilla/Cargo.lock +0 -25
  143. package/stringzilla/Cargo.toml +0 -26
  144. package/stringzilla/LICENSE +0 -201
  145. package/stringzilla/README.md +0 -1422
  146. package/stringzilla/VERSION +0 -1
  147. package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
  148. package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
  149. package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
  150. package/stringzilla/binding.gyp +0 -16
  151. package/stringzilla/build.rs +0 -87
  152. package/stringzilla/c/lib.c +0 -337
  153. package/stringzilla/cli/README.md +0 -65
  154. package/stringzilla/cli/__init__.py +0 -0
  155. package/stringzilla/cli/split.py +0 -105
  156. package/stringzilla/cli/wc.py +0 -117
  157. package/stringzilla/include/stringzilla/experimental.h +0 -845
  158. package/stringzilla/include/stringzilla/module.modulemap +0 -9
  159. package/stringzilla/include/stringzilla/stringzilla.h +0 -5370
  160. package/stringzilla/include/stringzilla/stringzilla.hpp +0 -3838
  161. package/stringzilla/javascript/lib.c +0 -125
  162. package/stringzilla/javascript/stringzilla.js +0 -22
  163. package/stringzilla/package-ci.json +0 -10
  164. package/stringzilla/package.json +0 -32
  165. package/stringzilla/rust/lib.rs +0 -873
  166. package/stringzilla/swift/StringProtocol+StringZilla.swift +0 -280
  167. package/stringzilla/swift/Test.swift +0 -60
  168. package/tsconfig.json +0 -21
  169. package/wasmer.toml +0 -12
  170. package/winlibinstaller.bat +0 -42
  171. /package/{simsimd/javascript/dist-package-cjs.json → javascript/dist/cjs/package.json} +0 -0
  172. /package/{simsimd/javascript/dist-package-esm.json → javascript/dist/esm/package.json} +0 -0
@@ -1,3838 +0,0 @@
1
- /**
2
- * @brief StringZilla C++ wrapper improving over the performance of `std::string_view` and `std::string`,
3
- * mostly for substring search, adding approximate matching functionality, and C++23 functionality
4
- * to a C++11 compatible implementation.
5
- *
6
- * This implementation is aiming to be compatible with C++11, while implementing the C++23 functionality.
7
- * By default, it includes C++ STL headers, but that can be avoided to minimize compilation overhead.
8
- * https://artificial-mind.net/projects/compile-health/
9
- *
10
- * @see StringZilla: https://github.com/ashvardanian/StringZilla/blob/main/README.md
11
- * @see C++ Standard String: https://en.cppreference.com/w/cpp/header/string
12
- *
13
- * @file stringzilla.hpp
14
- * @author Ash Vardanian
15
- */
16
- #ifndef STRINGZILLA_HPP_
17
- #define STRINGZILLA_HPP_
18
-
19
- /**
20
- * @brief When set to 1, the library will include the C++ STL headers and implement
21
- * automatic conversion from and to `std::stirng_view` and `std::basic_string<any_allocator>`.
22
- */
23
- #ifndef SZ_AVOID_STL
24
- #define SZ_AVOID_STL (0) // true or false
25
- #endif
26
-
27
- /* We need to detect the version of the C++ language we are compiled with.
28
- * This will affect recent features like `operator<=>` and tests against STL.
29
- */
30
- #define SZ_DETECT_CPP_23 (__cplusplus >= 202101L)
31
- #define SZ_DETECT_CPP20 (__cplusplus >= 202002L)
32
- #define SZ_DETECT_CPP_17 (__cplusplus >= 201703L)
33
- #define SZ_DETECT_CPP14 (__cplusplus >= 201402L)
34
- #define SZ_DETECT_CPP_11 (__cplusplus >= 201103L)
35
- #define SZ_DETECT_CPP_98 (__cplusplus >= 199711L)
36
-
37
- /**
38
- * @brief The `constexpr` keyword has different applicability scope in different C++ versions.
39
- * Useful for STL conversion operators, as several `std::string` members are `constexpr` in C++20.
40
- */
41
- #if SZ_DETECT_CPP20
42
- #define sz_constexpr_if_cpp20 constexpr
43
- #else
44
- #define sz_constexpr_if_cpp20
45
- #endif
46
-
47
- #if !SZ_AVOID_STL
48
- #include <array>
49
- #include <bitset>
50
- #include <string>
51
- #include <vector>
52
- #if SZ_DETECT_CPP_17 && __cpp_lib_string_view
53
- #include <string_view>
54
- #endif
55
- #endif
56
-
57
- #include <cassert> // `assert`
58
- #include <cstddef> // `std::size_t`
59
- #include <cstdint> // `std::int8_t`
60
- #include <iosfwd> // `std::basic_ostream`
61
- #include <stdexcept> // `std::out_of_range`
62
- #include <utility> // `std::swap`
63
-
64
- #include <stringzilla/stringzilla.h>
65
-
66
- namespace ashvardanian {
67
- namespace stringzilla {
68
-
69
- template <typename>
70
- class basic_charset;
71
- template <typename>
72
- class basic_string_slice;
73
- template <typename, typename>
74
- class basic_string;
75
-
76
- using string_span = basic_string_slice<char>;
77
- using string_view = basic_string_slice<char const>;
78
-
79
- template <std::size_t count_characters>
80
- using carray = char[count_characters];
81
-
82
- #pragma region Character Sets
83
-
84
- /**
85
- * @brief The concatenation of the `ascii_lowercase` and `ascii_uppercase`. This value is not locale-dependent.
86
- * https://docs.python.org/3/library/string.html#string.ascii_letters
87
- */
88
- inline carray<52> const &ascii_letters() noexcept {
89
- static carray<52> const all = {
90
- //
91
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
92
- 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
93
- 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
94
- };
95
- return all;
96
- }
97
-
98
- /**
99
- * @brief The lowercase letters "abcdefghijklmnopqrstuvwxyz". This value is not locale-dependent.
100
- * https://docs.python.org/3/library/string.html#string.ascii_lowercase
101
- */
102
- inline carray<26> const &ascii_lowercase() noexcept {
103
- static carray<26> const all = {
104
- //
105
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
106
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
107
- };
108
- return all;
109
- }
110
-
111
- /**
112
- * @brief The uppercase letters "ABCDEFGHIJKLMNOPQRSTUVWXYZ". This value is not locale-dependent.
113
- * https://docs.python.org/3/library/string.html#string.ascii_uppercase
114
- */
115
- inline carray<26> const &ascii_uppercase() noexcept {
116
- static carray<26> const all = {
117
- //
118
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
119
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
120
- };
121
- return all;
122
- }
123
-
124
- /**
125
- * @brief ASCII characters which are considered printable.
126
- * A combination of `digits`, `ascii_letters`, `punctuation`, and `whitespace`.
127
- * https://docs.python.org/3/library/string.html#string.printable
128
- */
129
- inline carray<100> const &ascii_printables() noexcept {
130
- static carray<100> const all = {
131
- //
132
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
133
- 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
134
- 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
135
- 'Y', 'Z', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<',
136
- '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', ' ', '\t', '\n', '\r', '\f', '\v',
137
- };
138
- return all;
139
- }
140
-
141
- /**
142
- * @brief Non-printable ASCII control characters.
143
- * Includes all codes from 0 to 31 and 127.
144
- */
145
- inline carray<33> const &ascii_controls() noexcept {
146
- static carray<33> const all = {
147
- //
148
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
149
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127,
150
- };
151
- return all;
152
- }
153
-
154
- /**
155
- * @brief The digits "0123456789".
156
- * https://docs.python.org/3/library/string.html#string.digits
157
- */
158
- inline carray<10> const &digits() noexcept {
159
- static carray<10> const all = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
160
- return all;
161
- }
162
-
163
- /**
164
- * @brief The letters "0123456789abcdefABCDEF".
165
- * https://docs.python.org/3/library/string.html#string.hexdigits
166
- */
167
- inline carray<22> const &hexdigits() noexcept {
168
- static carray<22> const all = {
169
- //
170
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
171
- 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F',
172
- };
173
- return all;
174
- }
175
-
176
- /**
177
- * @brief The letters "01234567".
178
- * https://docs.python.org/3/library/string.html#string.octdigits
179
- */
180
- inline carray<8> const &octdigits() noexcept {
181
- static carray<8> const all = {'0', '1', '2', '3', '4', '5', '6', '7'};
182
- return all;
183
- }
184
-
185
- /**
186
- * @brief ASCII characters considered punctuation characters in the C locale:
187
- * !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~.
188
- * https://docs.python.org/3/library/string.html#string.punctuation
189
- */
190
- inline carray<32> const &punctuation() noexcept {
191
- static carray<32> const all = {
192
- //
193
- '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':',
194
- ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~',
195
- };
196
- return all;
197
- }
198
-
199
- /**
200
- * @brief ASCII characters that are considered whitespace.
201
- * This includes space, tab, linefeed, return, formfeed, and vertical tab.
202
- * https://docs.python.org/3/library/string.html#string.whitespace
203
- */
204
- inline carray<6> const &whitespaces() noexcept {
205
- static carray<6> const all = {' ', '\t', '\n', '\r', '\f', '\v'};
206
- return all;
207
- }
208
-
209
- /**
210
- * @brief ASCII characters that are considered line delimiters.
211
- * https://docs.python.org/3/library/stdtypes.html#str.splitlines
212
- */
213
- inline carray<8> const &newlines() noexcept {
214
- static carray<8> const all = {'\n', '\r', '\f', '\v', '\x1C', '\x1D', '\x1E', '\x85'};
215
- return all;
216
- }
217
-
218
- /**
219
- * @brief ASCII characters forming the BASE64 encoding alphabet.
220
- */
221
- inline carray<64> const &base64() noexcept {
222
- static carray<64> const all = {
223
- //
224
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
225
- 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
226
- 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
227
- };
228
- return all;
229
- }
230
-
231
- /**
232
- * @brief A set of characters represented as a bitset with 256 slots.
233
- */
234
- template <typename char_type_ = char>
235
- class basic_charset {
236
- sz_charset_t bitset_;
237
-
238
- public:
239
- using char_type = char_type_;
240
-
241
- basic_charset() noexcept {
242
- // ! Instead of relying on the `sz_charset_init`, we have to reimplement it to support `constexpr`.
243
- bitset_._u64s[0] = 0, bitset_._u64s[1] = 0, bitset_._u64s[2] = 0, bitset_._u64s[3] = 0;
244
- }
245
- explicit basic_charset(std::initializer_list<char_type> chars) noexcept : basic_charset() {
246
- // ! Instead of relying on the `sz_charset_add(&bitset_, c)`, we have to reimplement it to support `constexpr`.
247
- for (auto c : chars) bitset_._u64s[sz_bitcast(sz_u8_t, c) >> 6] |= (1ull << (sz_bitcast(sz_u8_t, c) & 63u));
248
- }
249
- template <std::size_t count_characters>
250
- explicit basic_charset(char_type const (&chars)[count_characters]) noexcept : basic_charset() {
251
- static_assert(count_characters > 0, "Character array cannot be empty");
252
- for (std::size_t i = 0; i < count_characters - 1; ++i) { // count_characters - 1 to exclude the null terminator
253
- char_type c = chars[i];
254
- bitset_._u64s[sz_bitcast(sz_u8_t, c) >> 6] |= (1ull << (sz_bitcast(sz_u8_t, c) & 63u));
255
- }
256
- }
257
-
258
- template <std::size_t count_characters>
259
- explicit basic_charset(std::array<char_type, count_characters> const &chars) noexcept : basic_charset() {
260
- static_assert(count_characters > 0, "Character array cannot be empty");
261
- for (std::size_t i = 0; i < count_characters - 1; ++i) { // count_characters - 1 to exclude the null terminator
262
- char_type c = chars[i];
263
- bitset_._u64s[sz_bitcast(sz_u8_t, c) >> 6] |= (1ull << (sz_bitcast(sz_u8_t, c) & 63u));
264
- }
265
- }
266
-
267
- basic_charset(basic_charset const &other) noexcept : bitset_(other.bitset_) {}
268
- basic_charset &operator=(basic_charset const &other) noexcept {
269
- bitset_ = other.bitset_;
270
- return *this;
271
- }
272
-
273
- basic_charset operator|(basic_charset other) const noexcept {
274
- basic_charset result = *this;
275
- result.bitset_._u64s[0] |= other.bitset_._u64s[0], result.bitset_._u64s[1] |= other.bitset_._u64s[1],
276
- result.bitset_._u64s[2] |= other.bitset_._u64s[2], result.bitset_._u64s[3] |= other.bitset_._u64s[3];
277
- return *this;
278
- }
279
-
280
- inline basic_charset &add(char_type c) noexcept {
281
- sz_charset_add(&bitset_, sz_bitcast(sz_u8_t, c));
282
- return *this;
283
- }
284
- inline sz_charset_t &raw() noexcept { return bitset_; }
285
- inline sz_charset_t const &raw() const noexcept { return bitset_; }
286
- inline bool contains(char_type c) const noexcept { return sz_charset_contains(&bitset_, sz_bitcast(sz_u8_t, c)); }
287
- inline basic_charset inverted() const noexcept {
288
- basic_charset result = *this;
289
- sz_charset_invert(&result.bitset_);
290
- return result;
291
- }
292
- };
293
-
294
- using char_set = basic_charset<char>;
295
-
296
- inline char_set ascii_letters_set() { return char_set {ascii_letters()}; }
297
- inline char_set ascii_lowercase_set() { return char_set {ascii_lowercase()}; }
298
- inline char_set ascii_uppercase_set() { return char_set {ascii_uppercase()}; }
299
- inline char_set ascii_printables_set() { return char_set {ascii_printables()}; }
300
- inline char_set ascii_controls_set() { return char_set {ascii_controls()}; }
301
- inline char_set digits_set() { return char_set {digits()}; }
302
- inline char_set hexdigits_set() { return char_set {hexdigits()}; }
303
- inline char_set octdigits_set() { return char_set {octdigits()}; }
304
- inline char_set punctuation_set() { return char_set {punctuation()}; }
305
- inline char_set whitespaces_set() { return char_set {whitespaces()}; }
306
- inline char_set newlines_set() { return char_set {newlines()}; }
307
- inline char_set base64_set() { return char_set {base64()}; }
308
-
309
- #pragma endregion
310
-
311
- #pragma region Ranges of Search Matches
312
-
313
- struct end_sentinel_type {};
314
- struct include_overlaps_type {};
315
- struct exclude_overlaps_type {};
316
-
317
- #if SZ_DETECT_CPP_17
318
- inline static constexpr end_sentinel_type end_sentinel;
319
- inline static constexpr include_overlaps_type include_overlaps;
320
- inline static constexpr exclude_overlaps_type exclude_overlaps;
321
- #endif
322
-
323
- /**
324
- * @brief Zero-cost wrapper around the `.find` member function of string-like classes.
325
- */
326
- template <typename string_type_, typename overlaps_type = include_overlaps_type>
327
- struct matcher_find {
328
- using size_type = typename string_type_::size_type;
329
- string_type_ needle_;
330
-
331
- matcher_find(string_type_ needle = {}) noexcept : needle_(needle) {}
332
- size_type needle_length() const noexcept { return needle_.length(); }
333
- size_type operator()(string_type_ haystack) const noexcept { return haystack.find(needle_); }
334
- size_type skip_length() const noexcept {
335
- // TODO: Apply Galil rule to match repetitive patterns in strictly linear time.
336
- return std::is_same<overlaps_type, include_overlaps_type>() ? 1 : needle_.length();
337
- }
338
- };
339
-
340
- /**
341
- * @brief Zero-cost wrapper around the `.rfind` member function of string-like classes.
342
- */
343
- template <typename string_type_, typename overlaps_type = include_overlaps_type>
344
- struct matcher_rfind {
345
- using size_type = typename string_type_::size_type;
346
- string_type_ needle_;
347
-
348
- matcher_rfind(string_type_ needle = {}) noexcept : needle_(needle) {}
349
- size_type needle_length() const noexcept { return needle_.length(); }
350
- size_type operator()(string_type_ haystack) const noexcept { return haystack.rfind(needle_); }
351
- size_type skip_length() const noexcept {
352
- // TODO: Apply Galil rule to match repetitive patterns in strictly linear time.
353
- return std::is_same<overlaps_type, include_overlaps_type>() ? 1 : needle_.length();
354
- }
355
- };
356
-
357
- /**
358
- * @brief Zero-cost wrapper around the `.find_first_of` member function of string-like classes.
359
- */
360
- template <typename haystack_type, typename needles_type = haystack_type>
361
- struct matcher_find_first_of {
362
- using size_type = typename haystack_type::size_type;
363
- needles_type needles_;
364
- constexpr size_type needle_length() const noexcept { return 1; }
365
- constexpr size_type skip_length() const noexcept { return 1; }
366
- size_type operator()(haystack_type haystack) const noexcept { return haystack.find_first_of(needles_); }
367
- };
368
-
369
- /**
370
- * @brief Zero-cost wrapper around the `.find_last_of` member function of string-like classes.
371
- */
372
- template <typename haystack_type, typename needles_type = haystack_type>
373
- struct matcher_find_last_of {
374
- using size_type = typename haystack_type::size_type;
375
- needles_type needles_;
376
- constexpr size_type needle_length() const noexcept { return 1; }
377
- constexpr size_type skip_length() const noexcept { return 1; }
378
- size_type operator()(haystack_type haystack) const noexcept { return haystack.find_last_of(needles_); }
379
- };
380
-
381
- /**
382
- * @brief Zero-cost wrapper around the `.find_first_not_of` member function of string-like classes.
383
- */
384
- template <typename haystack_type, typename needles_type = haystack_type>
385
- struct matcher_find_first_not_of {
386
- using size_type = typename haystack_type::size_type;
387
- needles_type needles_;
388
- constexpr size_type needle_length() const noexcept { return 1; }
389
- constexpr size_type skip_length() const noexcept { return 1; }
390
- size_type operator()(haystack_type haystack) const noexcept { return haystack.find_first_not_of(needles_); }
391
- };
392
-
393
- /**
394
- * @brief Zero-cost wrapper around the `.find_last_not_of` member function of string-like classes.
395
- */
396
- template <typename haystack_type, typename needles_type = haystack_type>
397
- struct matcher_find_last_not_of {
398
- using size_type = typename haystack_type::size_type;
399
- needles_type needles_;
400
- constexpr size_type needle_length() const noexcept { return 1; }
401
- constexpr size_type skip_length() const noexcept { return 1; }
402
- size_type operator()(haystack_type haystack) const noexcept { return haystack.find_last_not_of(needles_); }
403
- };
404
-
405
- /**
406
- * @brief A range of string slices representing the matches of a substring search.
407
- * Compatible with C++23 ranges, C++11 string views, and of course, StringZilla.
408
- * Similar to a pair of `boost::algorithm::find_iterator`.
409
- */
410
- template <typename string_type_, typename matcher_type_>
411
- class range_matches {
412
- public:
413
- using string_type = string_type_;
414
- using matcher_type = matcher_type_;
415
-
416
- private:
417
- matcher_type matcher_;
418
- string_type haystack_;
419
-
420
- public:
421
- using size_type = std::size_t;
422
- using difference_type = std::ptrdiff_t;
423
- using value_type = string_type;
424
- using pointer = string_type; // Needed for compatibility with STL container constructors.
425
- using reference = string_type; // Needed for compatibility with STL container constructors.
426
-
427
- range_matches(string_type haystack, matcher_type needle) noexcept : matcher_(needle), haystack_(haystack) {}
428
-
429
- class iterator {
430
- matcher_type matcher_;
431
- string_type remaining_;
432
-
433
- public:
434
- using iterator_category = std::forward_iterator_tag;
435
- using difference_type = std::ptrdiff_t;
436
- using value_type = string_type;
437
- using pointer = string_type; // Needed for compatibility with STL container constructors.
438
- using reference = string_type; // Needed for compatibility with STL container constructors.
439
-
440
- iterator(string_type haystack, matcher_type matcher) noexcept : matcher_(matcher), remaining_(haystack) {
441
- auto position = matcher_(remaining_);
442
- remaining_.remove_prefix(position != string_type::npos ? position : remaining_.size());
443
- }
444
-
445
- pointer operator->() const noexcept = delete;
446
- value_type operator*() const noexcept { return remaining_.substr(0, matcher_.needle_length()); }
447
-
448
- iterator &operator++() noexcept {
449
- remaining_.remove_prefix(matcher_.skip_length());
450
- auto position = matcher_(remaining_);
451
- remaining_.remove_prefix(position != string_type::npos ? position : remaining_.size());
452
- return *this;
453
- }
454
-
455
- iterator operator++(int) noexcept {
456
- iterator temp = *this;
457
- ++(*this);
458
- return temp;
459
- }
460
-
461
- // Assumes both iterators point to the same underlying string.
462
- bool operator!=(iterator const &other) const noexcept { return remaining_.data() != other.remaining_.data(); }
463
- bool operator==(iterator const &other) const noexcept { return remaining_.data() == other.remaining_.data(); }
464
- bool operator!=(end_sentinel_type) const noexcept { return !remaining_.empty(); }
465
- bool operator==(end_sentinel_type) const noexcept { return remaining_.empty(); }
466
- };
467
-
468
- iterator begin() const noexcept { return {haystack_, matcher_}; }
469
- iterator end() const noexcept { return {string_type {haystack_.data() + haystack_.size(), 0ull}, matcher_}; }
470
- size_type size() const noexcept { return static_cast<size_type>(ssize()); }
471
- difference_type ssize() const noexcept { return std::distance(begin(), end()); }
472
- bool empty() const noexcept { return begin() == end_sentinel_type {}; }
473
- bool include_overlaps() const noexcept { return matcher_.skip_length() < matcher_.needle_length(); }
474
-
475
- /**
476
- * @brief Copies the matches into a container.
477
- */
478
- template <typename container_>
479
- void to(container_ &container) {
480
- for (auto match : *this) { container.push_back(match); }
481
- }
482
-
483
- /**
484
- * @brief Copies the matches into a consumed container, returning it at the end.
485
- */
486
- template <typename container_>
487
- container_ to() {
488
- return container_ {begin(), end()};
489
- }
490
- };
491
-
492
- /**
493
- * @brief A range of string slices representing the matches of a @b reverse-order substring search.
494
- * Compatible with C++23 ranges, C++11 string views, and of course, StringZilla.
495
- * Similar to a pair of `boost::algorithm::find_iterator`.
496
- */
497
- template <typename string_type_, typename matcher_type_>
498
- class range_rmatches {
499
- public:
500
- using string_type = string_type_;
501
- using matcher_type = matcher_type_;
502
-
503
- using size_type = std::size_t;
504
- using difference_type = std::ptrdiff_t;
505
- using value_type = string_type;
506
- using pointer = string_type; // Needed for compatibility with STL container constructors.
507
- using reference = string_type; // Needed for compatibility with STL container constructors.
508
-
509
- private:
510
- matcher_type matcher_;
511
- string_type haystack_;
512
-
513
- public:
514
- range_rmatches(string_type haystack, matcher_type needle) : matcher_(needle), haystack_(haystack) {}
515
-
516
- class iterator {
517
- matcher_type matcher_;
518
- string_type remaining_;
519
-
520
- public:
521
- using iterator_category = std::forward_iterator_tag;
522
- using difference_type = std::ptrdiff_t;
523
- using value_type = string_type;
524
- using pointer = string_type; // Needed for compatibility with STL container constructors.
525
- using reference = string_type; // Needed for compatibility with STL container constructors.
526
-
527
- iterator(string_type haystack, matcher_type matcher) noexcept : matcher_(matcher), remaining_(haystack) {
528
- auto position = matcher_(remaining_);
529
- remaining_.remove_suffix(position != string_type::npos
530
- ? remaining_.size() - position - matcher_.needle_length()
531
- : remaining_.size());
532
- }
533
-
534
- pointer operator->() const noexcept = delete;
535
- value_type operator*() const noexcept {
536
- return remaining_.substr(remaining_.size() - matcher_.needle_length());
537
- }
538
-
539
- iterator &operator++() noexcept {
540
- remaining_.remove_suffix(matcher_.skip_length());
541
- auto position = matcher_(remaining_);
542
- remaining_.remove_suffix(position != string_type::npos
543
- ? remaining_.size() - position - matcher_.needle_length()
544
- : remaining_.size());
545
- return *this;
546
- }
547
-
548
- iterator operator++(int) noexcept {
549
- iterator temp = *this;
550
- ++(*this);
551
- return temp;
552
- }
553
-
554
- // Assumes both iterators point to the same underlying string.
555
- // This has to be `.data() + .size()`, to be compatible with `std::string_view` on MSVC.
556
- bool operator!=(iterator const &other) const noexcept {
557
- return remaining_.data() + remaining_.size() != other.remaining_.data() + other.remaining_.size();
558
- }
559
- bool operator==(iterator const &other) const noexcept {
560
- return remaining_.data() + remaining_.size() == other.remaining_.data() + other.remaining_.size();
561
- }
562
- bool operator!=(end_sentinel_type) const noexcept { return !remaining_.empty(); }
563
- bool operator==(end_sentinel_type) const noexcept { return remaining_.empty(); }
564
- };
565
-
566
- iterator begin() const noexcept { return {haystack_, matcher_}; }
567
- iterator end() const noexcept { return {string_type {haystack_.data(), 0ull}, matcher_}; }
568
- size_type size() const noexcept { return static_cast<size_type>(ssize()); }
569
- difference_type ssize() const noexcept { return std::distance(begin(), end()); }
570
- bool empty() const noexcept { return begin() == end_sentinel_type {}; }
571
- bool include_overlaps() const noexcept { return matcher_.skip_length() < matcher_.needle_length(); }
572
-
573
- /**
574
- * @brief Copies the matches into a container.
575
- */
576
- template <typename container_>
577
- void to(container_ &container) {
578
- for (auto match : *this) { container.push_back(match); }
579
- }
580
-
581
- /**
582
- * @brief Copies the matches into a consumed container, returning it at the end.
583
- */
584
- template <typename container_>
585
- container_ to() {
586
- return container_ {begin(), end()};
587
- }
588
- };
589
-
590
- /**
591
- * @brief A range of string slices for different splits of the data.
592
- * Compatible with C++23 ranges, C++11 string views, and of course, StringZilla.
593
- * Similar to a pair of `boost::algorithm::split_iterator`.
594
- *
595
- * In some sense, represents the inverse operation to `range_matches`, as it reports not the search matches
596
- * but the data between them. Meaning that for `N` search matches, there will be `N+1` elements in the range.
597
- * Unlike ::range_matches, this range can't be empty. It also can't report overlapping intervals.
598
- */
599
- template <typename string_type_, typename matcher_type_>
600
- class range_splits {
601
- public:
602
- using string_type = string_type_;
603
- using matcher_type = matcher_type_;
604
-
605
- using size_type = std::size_t;
606
- using difference_type = std::ptrdiff_t;
607
- using value_type = string_type;
608
- using pointer = string_type; // Needed for compatibility with STL container constructors.
609
- using reference = string_type; // Needed for compatibility with STL container constructors.
610
-
611
- private:
612
- matcher_type matcher_;
613
- string_type haystack_;
614
-
615
- public:
616
- range_splits(string_type haystack, matcher_type needle) noexcept : matcher_(needle), haystack_(haystack) {}
617
-
618
- class iterator {
619
- matcher_type matcher_;
620
- string_type remaining_;
621
- std::size_t length_within_remaining_;
622
- bool reached_tail_;
623
-
624
- public:
625
- using iterator_category = std::forward_iterator_tag;
626
- using difference_type = std::ptrdiff_t;
627
- using value_type = string_type;
628
- using pointer = string_type; // Needed for compatibility with STL container constructors.
629
- using reference = string_type; // Needed for compatibility with STL container constructors.
630
-
631
- iterator(string_type haystack, matcher_type matcher) noexcept : matcher_(matcher), remaining_(haystack) {
632
- auto position = matcher_(remaining_);
633
- length_within_remaining_ = position != string_type::npos ? position : remaining_.size();
634
- reached_tail_ = false;
635
- }
636
-
637
- iterator(string_type haystack, matcher_type matcher, end_sentinel_type) noexcept
638
- : matcher_(matcher), remaining_(haystack), length_within_remaining_(0), reached_tail_(true) {}
639
-
640
- pointer operator->() const noexcept = delete;
641
- value_type operator*() const noexcept { return remaining_.substr(0, length_within_remaining_); }
642
-
643
- iterator &operator++() noexcept {
644
- remaining_.remove_prefix(length_within_remaining_);
645
- reached_tail_ = remaining_.empty();
646
- remaining_.remove_prefix(matcher_.needle_length() * !reached_tail_);
647
- auto position = matcher_(remaining_);
648
- length_within_remaining_ = position != string_type::npos ? position : remaining_.size();
649
- return *this;
650
- }
651
-
652
- iterator operator++(int) noexcept {
653
- iterator temp = *this;
654
- ++(*this);
655
- return temp;
656
- }
657
-
658
- bool operator!=(iterator const &other) const noexcept {
659
- return (remaining_.begin() != other.remaining_.begin()) || (reached_tail_ != other.reached_tail_);
660
- }
661
- bool operator==(iterator const &other) const noexcept {
662
- return (remaining_.begin() == other.remaining_.begin()) && (reached_tail_ == other.reached_tail_);
663
- }
664
- bool operator!=(end_sentinel_type) const noexcept { return !remaining_.empty() || !reached_tail_; }
665
- bool operator==(end_sentinel_type) const noexcept { return remaining_.empty() && reached_tail_; }
666
- bool is_last() const noexcept { return remaining_.size() == length_within_remaining_; }
667
- };
668
-
669
- iterator begin() const noexcept { return {haystack_, matcher_}; }
670
- iterator end() const noexcept { return {string_type {haystack_.end(), 0}, matcher_, end_sentinel_type {}}; }
671
- size_type size() const noexcept { return static_cast<size_type>(ssize()); }
672
- difference_type ssize() const noexcept { return std::distance(begin(), end()); }
673
- constexpr bool empty() const noexcept { return false; }
674
-
675
- /**
676
- * @brief Copies the matches into a container.
677
- */
678
- template <typename container_>
679
- void to(container_ &container) {
680
- for (auto match : *this) { container.push_back(match); }
681
- }
682
-
683
- /**
684
- * @brief Copies the matches into a consumed container, returning it at the end.
685
- */
686
- template <typename container_>
687
- container_ to(container_ &&container = {}) {
688
- for (auto match : *this) { container.push_back(match); }
689
- return std::move(container);
690
- }
691
- };
692
-
693
- /**
694
- * @brief A range of string slices for different splits of the data in @b reverse-order.
695
- * Compatible with C++23 ranges, C++11 string views, and of course, StringZilla.
696
- * Similar to a pair of `boost::algorithm::split_iterator`.
697
- *
698
- * In some sense, represents the inverse operation to `range_matches`, as it reports not the search matches
699
- * but the data between them. Meaning that for `N` search matches, there will be `N+1` elements in the range.
700
- * Unlike ::range_matches, this range can't be empty. It also can't report overlapping intervals.
701
- */
702
- template <typename string_type_, typename matcher_type_>
703
- class range_rsplits {
704
- public:
705
- using string_type = string_type_;
706
- using matcher_type = matcher_type_;
707
-
708
- using size_type = std::size_t;
709
- using difference_type = std::ptrdiff_t;
710
- using value_type = string_type;
711
- using pointer = string_type; // Needed for compatibility with STL container constructors.
712
- using reference = string_type; // Needed for compatibility with STL container constructors.
713
-
714
- private:
715
- matcher_type matcher_;
716
- string_type haystack_;
717
-
718
- public:
719
- range_rsplits(string_type haystack, matcher_type needle) noexcept : matcher_(needle), haystack_(haystack) {}
720
-
721
- class iterator {
722
- matcher_type matcher_;
723
- string_type remaining_;
724
- std::size_t length_within_remaining_;
725
- bool reached_tail_;
726
-
727
- public:
728
- using iterator_category = std::forward_iterator_tag;
729
- using difference_type = std::ptrdiff_t;
730
- using value_type = string_type;
731
- using pointer = string_type; // Needed for compatibility with STL container constructors.
732
- using reference = string_type; // Needed for compatibility with STL container constructors.
733
-
734
- iterator(string_type haystack, matcher_type matcher) noexcept : matcher_(matcher), remaining_(haystack) {
735
- auto position = matcher_(remaining_);
736
- length_within_remaining_ = position != string_type::npos
737
- ? remaining_.size() - position - matcher_.needle_length()
738
- : remaining_.size();
739
- reached_tail_ = false;
740
- }
741
-
742
- iterator(string_type haystack, matcher_type matcher, end_sentinel_type) noexcept
743
- : matcher_(matcher), remaining_(haystack), length_within_remaining_(0), reached_tail_(true) {}
744
-
745
- pointer operator->() const noexcept = delete;
746
- value_type operator*() const noexcept {
747
- return remaining_.substr(remaining_.size() - length_within_remaining_);
748
- }
749
-
750
- iterator &operator++() noexcept {
751
- remaining_.remove_suffix(length_within_remaining_);
752
- reached_tail_ = remaining_.empty();
753
- remaining_.remove_suffix(matcher_.needle_length() * !reached_tail_);
754
- auto position = matcher_(remaining_);
755
- length_within_remaining_ = position != string_type::npos
756
- ? remaining_.size() - position - matcher_.needle_length()
757
- : remaining_.size();
758
- return *this;
759
- }
760
-
761
- iterator operator++(int) noexcept {
762
- iterator temp = *this;
763
- ++(*this);
764
- return temp;
765
- }
766
-
767
- bool operator!=(iterator const &other) const noexcept {
768
- return (remaining_.end() != other.remaining_.end()) || (reached_tail_ != other.reached_tail_);
769
- }
770
- bool operator==(iterator const &other) const noexcept {
771
- return (remaining_.end() == other.remaining_.end()) && (reached_tail_ == other.reached_tail_);
772
- }
773
- bool operator!=(end_sentinel_type) const noexcept { return !remaining_.empty() || !reached_tail_; }
774
- bool operator==(end_sentinel_type) const noexcept { return remaining_.empty() && reached_tail_; }
775
- bool is_last() const noexcept { return remaining_.size() == length_within_remaining_; }
776
- };
777
-
778
- iterator begin() const noexcept { return {haystack_, matcher_}; }
779
- iterator end() const noexcept { return {{haystack_.data(), 0ull}, matcher_, end_sentinel_type {}}; }
780
- size_type size() const noexcept { return static_cast<size_type>(ssize()); }
781
- difference_type ssize() const noexcept { return std::distance(begin(), end()); }
782
- constexpr bool empty() const noexcept { return false; }
783
-
784
- /**
785
- * @brief Copies the matches into a container.
786
- */
787
- template <typename container_>
788
- void to(container_ &container) {
789
- for (auto match : *this) { container.push_back(match); }
790
- }
791
-
792
- /**
793
- * @brief Copies the matches into a consumed container, returning it at the end.
794
- */
795
- template <typename container_>
796
- container_ to(container_ &&container = {}) {
797
- for (auto match : *this) { container.push_back(match); }
798
- return std::move(container);
799
- }
800
- };
801
-
802
- /**
803
- * @brief Find all potentially @b overlapping inclusions of a needle substring.
804
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
805
- */
806
- template <typename string>
807
- range_matches<string, matcher_find<string, include_overlaps_type>> find_all(string const &h, string const &n,
808
- include_overlaps_type = {}) noexcept {
809
- return {h, n};
810
- }
811
-
812
- /**
813
- * @brief Find all potentially @b overlapping inclusions of a needle substring in @b reverse order.
814
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
815
- */
816
- template <typename string>
817
- range_rmatches<string, matcher_rfind<string, include_overlaps_type>> rfind_all(string const &h, string const &n,
818
- include_overlaps_type = {}) noexcept {
819
- return {h, n};
820
- }
821
-
822
- /**
823
- * @brief Find all @b non-overlapping inclusions of a needle substring.
824
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
825
- */
826
- template <typename string>
827
- range_matches<string, matcher_find<string, exclude_overlaps_type>> find_all(string const &h, string const &n,
828
- exclude_overlaps_type) noexcept {
829
- return {h, n};
830
- }
831
-
832
- /**
833
- * @brief Find all @b non-overlapping inclusions of a needle substring in @b reverse order.
834
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
835
- */
836
- template <typename string>
837
- range_rmatches<string, matcher_rfind<string, exclude_overlaps_type>> rfind_all(string const &h, string const &n,
838
- exclude_overlaps_type) noexcept {
839
- return {h, n};
840
- }
841
-
842
- /**
843
- * @brief Find all inclusions of characters from the second string.
844
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
845
- */
846
- template <typename string>
847
- range_matches<string, matcher_find_first_of<string>> find_all_characters(string const &h, string const &n) noexcept {
848
- return {h, n};
849
- }
850
-
851
- /**
852
- * @brief Find all inclusions of characters from the second string in @b reverse order.
853
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
854
- */
855
- template <typename string>
856
- range_rmatches<string, matcher_find_last_of<string>> rfind_all_characters(string const &h, string const &n) noexcept {
857
- return {h, n};
858
- }
859
-
860
- /**
861
- * @brief Find all characters except the ones in the second string.
862
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
863
- */
864
- template <typename string>
865
- range_matches<string, matcher_find_first_not_of<string>> find_all_other_characters(string const &h,
866
- string const &n) noexcept {
867
- return {h, n};
868
- }
869
-
870
- /**
871
- * @brief Find all characters except the ones in the second string in @b reverse order.
872
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
873
- */
874
- template <typename string>
875
- range_rmatches<string, matcher_find_last_not_of<string>> rfind_all_other_characters(string const &h,
876
- string const &n) noexcept {
877
- return {h, n};
878
- }
879
-
880
- /**
881
- * @brief Splits a string around every @b non-overlapping inclusion of the second string.
882
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
883
- */
884
- template <typename string>
885
- range_splits<string, matcher_find<string, exclude_overlaps_type>> split(string const &h, string const &n) noexcept {
886
- return {h, n};
887
- }
888
-
889
- /**
890
- * @brief Splits a string around every @b non-overlapping inclusion of the second string in @b reverse order.
891
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
892
- */
893
- template <typename string>
894
- range_rsplits<string, matcher_rfind<string, exclude_overlaps_type>> rsplit(string const &h, string const &n) noexcept {
895
- return {h, n};
896
- }
897
-
898
- /**
899
- * @brief Splits a string around every character from the second string.
900
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
901
- */
902
- template <typename string>
903
- range_splits<string, matcher_find_first_of<string>> split_characters(string const &h, string const &n) noexcept {
904
- return {h, n};
905
- }
906
-
907
- /**
908
- * @brief Splits a string around every character from the second string in @b reverse order.
909
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
910
- */
911
- template <typename string>
912
- range_rsplits<string, matcher_find_last_of<string>> rsplit_characters(string const &h, string const &n) noexcept {
913
- return {h, n};
914
- }
915
-
916
- /**
917
- * @brief Splits a string around every character except the ones from the second string.
918
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
919
- */
920
- template <typename string>
921
- range_splits<string, matcher_find_first_not_of<string>> split_other_characters(string const &h,
922
- string const &n) noexcept {
923
- return {h, n};
924
- }
925
-
926
- /**
927
- * @brief Splits a string around every character except the ones from the second string in @b reverse order.
928
- * @tparam string A string-like type, ideally a view, like StringZilla or STL `string_view`.
929
- */
930
- template <typename string>
931
- range_rsplits<string, matcher_find_last_not_of<string>> rsplit_other_characters(string const &h,
932
- string const &n) noexcept {
933
- return {h, n};
934
- }
935
-
936
- /** @brief Helper function using `std::advance` iterator and return it back. */
937
- template <typename iterator_type, typename distance_type>
938
- iterator_type advanced(iterator_type &&it, distance_type n) {
939
- std::advance(it, n);
940
- return it;
941
- }
942
-
943
- /** @brief Helper function using `range_length` to compute the unsigned distance. */
944
- template <typename iterator_type>
945
- std::size_t range_length(iterator_type first, iterator_type last) {
946
- return static_cast<std::size_t>(std::distance(first, last));
947
- }
948
-
949
- #pragma endregion
950
-
951
- #pragma region Global Operations with Dynamic Memory
952
-
953
- template <typename allocator_type_>
954
- static void *_call_allocate(sz_size_t n, void *allocator_state) noexcept {
955
- return reinterpret_cast<allocator_type_ *>(allocator_state)->allocate(n);
956
- }
957
-
958
- template <typename allocator_type_>
959
- static void _call_free(void *ptr, sz_size_t n, void *allocator_state) noexcept {
960
- return reinterpret_cast<allocator_type_ *>(allocator_state)->deallocate(reinterpret_cast<char *>(ptr), n);
961
- }
962
-
963
- template <typename generator_type_>
964
- static sz_u64_t _call_random_generator(void *state) noexcept {
965
- generator_type_ &generator = *reinterpret_cast<generator_type_ *>(state);
966
- return generator();
967
- }
968
-
969
- template <typename allocator_type_, typename allocator_callback_>
970
- static bool _with_alloc(allocator_type_ &allocator, allocator_callback_ &&callback) noexcept {
971
- sz_memory_allocator_t alloc;
972
- alloc.allocate = &_call_allocate<allocator_type_>;
973
- alloc.free = &_call_free<allocator_type_>;
974
- alloc.handle = &allocator;
975
- return callback(alloc);
976
- }
977
-
978
- template <typename allocator_type_, typename allocator_callback_>
979
- static bool _with_alloc(allocator_callback_ &&callback) noexcept {
980
- allocator_type_ allocator;
981
- return _with_alloc(allocator, std::forward<allocator_callback_>(callback));
982
- }
983
-
984
- #pragma endregion
985
-
986
- #pragma region Helper Template Classes
987
-
988
- /**
989
- * @brief A result of split a string once, containing the string slice ::before,
990
- * the ::match itself, and the slice ::after.
991
- */
992
- template <typename string_>
993
- struct string_partition_result {
994
- string_ before;
995
- string_ match;
996
- string_ after;
997
- };
998
-
999
- /**
1000
- * @brief A reverse iterator for mutable and immutable character buffers.
1001
- * Replaces `std::reverse_iterator` to avoid including `<iterator>`.
1002
- */
1003
- template <typename value_type_>
1004
- class reversed_iterator_for {
1005
- public:
1006
- using iterator_category = std::random_access_iterator_tag;
1007
- using value_type = value_type_;
1008
- using difference_type = std::ptrdiff_t;
1009
- using pointer = value_type_ *;
1010
- using reference = value_type_ &;
1011
-
1012
- reversed_iterator_for(pointer ptr) noexcept : ptr_(ptr) {}
1013
- reference operator*() const noexcept { return *ptr_; }
1014
-
1015
- bool operator==(reversed_iterator_for const &other) const noexcept { return ptr_ == other.ptr_; }
1016
- bool operator!=(reversed_iterator_for const &other) const noexcept { return ptr_ != other.ptr_; }
1017
- reference operator[](difference_type n) const noexcept { return *(*this + n); }
1018
- reversed_iterator_for operator+(difference_type n) const noexcept { return reversed_iterator_for(ptr_ - n); }
1019
- reversed_iterator_for operator-(difference_type n) const noexcept { return reversed_iterator_for(ptr_ + n); }
1020
- difference_type operator-(reversed_iterator_for const &other) const noexcept { return other.ptr_ - ptr_; }
1021
-
1022
- reversed_iterator_for &operator++() noexcept {
1023
- --ptr_;
1024
- return *this;
1025
- }
1026
-
1027
- reversed_iterator_for operator++(int) const noexcept {
1028
- reversed_iterator_for temp = *this;
1029
- --ptr_;
1030
- return temp;
1031
- }
1032
-
1033
- reversed_iterator_for &operator--() const noexcept {
1034
- ++ptr_;
1035
- return *this;
1036
- }
1037
-
1038
- reversed_iterator_for operator--(int) const noexcept {
1039
- reversed_iterator_for temp = *this;
1040
- ++ptr_;
1041
- return temp;
1042
- }
1043
-
1044
- private:
1045
- value_type_ *ptr_;
1046
- };
1047
-
1048
- /**
1049
- * @brief An "expression template" for lazy concatenation of strings using the `operator|`.
1050
- */
1051
- template <typename first_type, typename second_type>
1052
- struct concatenation {
1053
-
1054
- using value_type = typename first_type::value_type;
1055
- using pointer = value_type *;
1056
- using const_pointer = value_type const *;
1057
- using size_type = typename first_type::size_type;
1058
- using difference_type = typename first_type::difference_type;
1059
-
1060
- first_type first;
1061
- second_type second;
1062
-
1063
- std::size_t size() const noexcept { return first.size() + second.size(); }
1064
- std::size_t length() const noexcept { return first.size() + second.size(); }
1065
-
1066
- size_type copy(pointer destination) const noexcept {
1067
- first.copy(destination);
1068
- second.copy(destination + first.length());
1069
- return first.length() + second.length();
1070
- }
1071
-
1072
- size_type copy(pointer destination, size_type length) const noexcept {
1073
- auto first_length = std::min(first.length(), length);
1074
- auto second_length = std::min(second.length(), length - first_length);
1075
- first.copy(destination, first_length);
1076
- second.copy(destination + first_length, second_length);
1077
- return first_length + second_length;
1078
- }
1079
-
1080
- template <typename last_type>
1081
- concatenation<concatenation<first_type, second_type>, last_type> operator|(last_type &&last) const {
1082
- return {*this, last};
1083
- }
1084
- };
1085
-
1086
- #pragma endregion
1087
-
1088
- #pragma region String Views/Spans
1089
-
1090
- /**
1091
- * @brief A string slice (view/span) class implementing a superset of C++23 functionality
1092
- * with much faster SIMD-accelerated substring search and approximate matching.
1093
- * Constructors are `constexpr` enabling `_sz` literals.
1094
- *
1095
- * @tparam char_type_ The character type, usually `char const` or `char`. Must be a single byte long.
1096
- */
1097
- template <typename char_type_>
1098
- class basic_string_slice {
1099
-
1100
- static_assert(sizeof(char_type_) == 1, "Characters must be a single byte long");
1101
- static_assert(std::is_reference<char_type_>::value == false, "Characters can't be references");
1102
-
1103
- using char_type = char_type_;
1104
- using mutable_char_type = typename std::remove_const<char_type_>::type;
1105
- using immutable_char_type = typename std::add_const<char_type_>::type;
1106
-
1107
- char_type *start_;
1108
- std::size_t length_;
1109
-
1110
- public:
1111
- // STL compatibility
1112
- using traits_type = std::char_traits<char_type_>;
1113
- using value_type = mutable_char_type;
1114
- using pointer = char_type *;
1115
- using const_pointer = immutable_char_type *;
1116
- using reference = char_type &;
1117
- using const_reference = immutable_char_type &;
1118
- using const_iterator = const_pointer;
1119
- using iterator = pointer;
1120
- using reverse_iterator = reversed_iterator_for<char_type>;
1121
- using const_reverse_iterator = reversed_iterator_for<immutable_char_type>;
1122
- using size_type = std::size_t;
1123
- using difference_type = std::ptrdiff_t;
1124
-
1125
- // Non-STL type definitions
1126
- using string_slice = basic_string_slice<char_type>;
1127
- using string_span = basic_string_slice<mutable_char_type>;
1128
- using string_view = basic_string_slice<immutable_char_type>;
1129
- using partition_type = string_partition_result<string_slice>;
1130
-
1131
- /** @brief Special value for missing matches.
1132
- *
1133
- * We take the largest 63-bit unsigned integer on 64-bit machines.
1134
- * We take the largest 31-bit unsigned integer on 32-bit machines.
1135
- */
1136
- static constexpr size_type npos = SZ_SSIZE_MAX;
1137
-
1138
- #pragma region Constructors and STL Utilities
1139
-
1140
- constexpr basic_string_slice() noexcept : start_(nullptr), length_(0) {}
1141
- constexpr basic_string_slice(pointer c_string) noexcept
1142
- : start_(c_string), length_(null_terminated_length(c_string)) {}
1143
- constexpr basic_string_slice(pointer c_string, size_type length) noexcept : start_(c_string), length_(length) {}
1144
-
1145
- sz_constexpr_if_cpp20 basic_string_slice(basic_string_slice const &other) noexcept = default;
1146
- sz_constexpr_if_cpp20 basic_string_slice &operator=(basic_string_slice const &other) noexcept = default;
1147
- basic_string_slice(std::nullptr_t) = delete;
1148
-
1149
- /** @brief Exchanges the view with that of the `other`. */
1150
- void swap(string_slice &other) noexcept { std::swap(start_, other.start_), std::swap(length_, other.length_); }
1151
-
1152
- #if !SZ_AVOID_STL
1153
-
1154
- template <typename sfinae_ = char_type, typename std::enable_if<std::is_const<sfinae_>::value, int>::type = 0>
1155
- sz_constexpr_if_cpp20 basic_string_slice(std::string const &other) noexcept
1156
- : basic_string_slice(other.data(), other.size()) {}
1157
-
1158
- template <typename sfinae_ = char_type, typename std::enable_if<!std::is_const<sfinae_>::value, int>::type = 0>
1159
- sz_constexpr_if_cpp20 basic_string_slice(std::string &other) noexcept
1160
- : basic_string_slice(&other[0], other.size()) {} // The `.data()` has mutable variant only since C++17
1161
-
1162
- template <typename sfinae_ = char_type, typename std::enable_if<std::is_const<sfinae_>::value, int>::type = 0>
1163
- sz_constexpr_if_cpp20 string_slice &operator=(std::string const &other) noexcept {
1164
- return assign({other.data(), other.size()});
1165
- }
1166
-
1167
- template <typename sfinae_ = char_type, typename std::enable_if<!std::is_const<sfinae_>::value, int>::type = 0>
1168
- sz_constexpr_if_cpp20 string_slice &operator=(std::string &other) noexcept {
1169
- return assign({other.data(), other.size()});
1170
- }
1171
-
1172
- operator std::string() const { return {data(), size()}; }
1173
-
1174
- /**
1175
- * @brief Formatted output function for compatibility with STL's `std::basic_ostream`.
1176
- * @throw `std::ios_base::failure` if an exception occurred during output.
1177
- */
1178
- template <typename stream_traits>
1179
- friend std::basic_ostream<value_type, stream_traits> &operator<<(std::basic_ostream<value_type, stream_traits> &os,
1180
- string_slice const &str) noexcept(false) {
1181
- return os.write(str.data(), str.size());
1182
- }
1183
-
1184
- #if SZ_DETECT_CPP_17 && __cpp_lib_string_view
1185
-
1186
- template <typename sfinae_ = char_type, typename std::enable_if<std::is_const<sfinae_>::value, int>::type = 0>
1187
- sz_constexpr_if_cpp20 basic_string_slice(std::string_view const &other) noexcept
1188
- : basic_string_slice(other.data(), other.size()) {}
1189
-
1190
- template <typename sfinae_ = char_type, typename std::enable_if<std::is_const<sfinae_>::value, int>::type = 0>
1191
- sz_constexpr_if_cpp20 string_slice &operator=(std::string_view const &other) noexcept {
1192
- return assign({other.data(), other.size()});
1193
- }
1194
- operator std::string_view() const noexcept { return {data(), size()}; }
1195
-
1196
- #endif
1197
-
1198
- #endif
1199
-
1200
- #pragma endregion
1201
-
1202
- #pragma region Iterators and Element Access
1203
-
1204
- iterator begin() const noexcept { return iterator(start_); }
1205
- iterator end() const noexcept { return iterator(start_ + length_); }
1206
- const_iterator cbegin() const noexcept { return const_iterator(start_); }
1207
- const_iterator cend() const noexcept { return const_iterator(start_ + length_); }
1208
- reverse_iterator rbegin() const noexcept { return reverse_iterator(start_ + length_ - 1); }
1209
- reverse_iterator rend() const noexcept { return reverse_iterator(start_ - 1); }
1210
- const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(start_ + length_ - 1); }
1211
- const_reverse_iterator crend() const noexcept { return const_reverse_iterator(start_ - 1); }
1212
-
1213
- reference operator[](size_type pos) const noexcept { return start_[pos]; }
1214
- reference at(size_type pos) const noexcept { return start_[pos]; }
1215
- reference front() const noexcept { return start_[0]; }
1216
- reference back() const noexcept { return start_[length_ - 1]; }
1217
- pointer data() const noexcept { return start_; }
1218
-
1219
- difference_type ssize() const noexcept { return static_cast<difference_type>(length_); }
1220
- size_type size() const noexcept { return length_; }
1221
- size_type length() const noexcept { return length_; }
1222
- size_type max_size() const noexcept { return npos - 1; }
1223
- bool empty() const noexcept { return length_ == 0; }
1224
-
1225
- #pragma endregion
1226
-
1227
- #pragma region Slicing
1228
-
1229
- #pragma region Safe and Signed Extensions
1230
-
1231
- /**
1232
- * @brief Equivalent to Python's `"abc"[-3:-1]`. Exception-safe, unlike STL's `substr`.
1233
- * Supports signed and unsigned intervals.
1234
- */
1235
- string_slice operator[](std::initializer_list<difference_type> signed_offsets) const noexcept {
1236
- assert(signed_offsets.size() == 2 && "operator[] can't take more than 2 offsets");
1237
- return sub(signed_offsets.begin()[0], signed_offsets.begin()[1]);
1238
- }
1239
-
1240
- /**
1241
- * @brief Signed alternative to `at()`. Handy if you often write `str[str.size() - 2]`.
1242
- * @warning The behavior is @b undefined if the position is beyond bounds.
1243
- */
1244
- reference sat(difference_type signed_offset) const noexcept {
1245
- size_type pos = (signed_offset < 0) ? size() + signed_offset : signed_offset;
1246
- assert(pos < size() && "string_slice::sat(i) out of bounds");
1247
- return start_[pos];
1248
- }
1249
-
1250
- /**
1251
- * @brief The opposite operation to `remove_prefix`, that does no bounds checking.
1252
- * @warning The behavior is @b undefined if `n > size()`.
1253
- */
1254
- string_slice front(size_type n) const noexcept {
1255
- assert(n <= size() && "string_slice::front(n) out of bounds");
1256
- return {start_, n};
1257
- }
1258
-
1259
- /**
1260
- * @brief The opposite operation to `remove_prefix`, that does no bounds checking.
1261
- * @warning The behavior is @b undefined if `n > size()`.
1262
- */
1263
- string_slice back(size_type n) const noexcept {
1264
- assert(n <= size() && "string_slice::back(n) out of bounds");
1265
- return {start_ + length_ - n, n};
1266
- }
1267
-
1268
- /**
1269
- * @brief Equivalent to Python's `"abc"[-3:-1]`. Exception-safe, unlike STL's `substr`.
1270
- * Supports signed and unsigned intervals.
1271
- */
1272
- string_slice sub(difference_type signed_start_offset, difference_type signed_end_offset = npos) const noexcept {
1273
- sz_size_t normalized_offset, normalized_length;
1274
- sz_ssize_clamp_interval(length_, signed_start_offset, signed_end_offset, &normalized_offset,
1275
- &normalized_length);
1276
- return string_slice(start_ + normalized_offset, normalized_length);
1277
- }
1278
-
1279
- /**
1280
- * @brief Exports this entire view. Not an STL function, but useful for concatenations.
1281
- * The STL variant expects at least two arguments.
1282
- */
1283
- size_type copy(value_type *destination) const noexcept {
1284
- sz_copy((sz_ptr_t)destination, start_, length_);
1285
- return length_;
1286
- }
1287
-
1288
- #pragma endregion
1289
-
1290
- #pragma region STL Style
1291
-
1292
- /**
1293
- * @brief Removes the first `n` characters from the view.
1294
- * @warning The behavior is @b undefined if `n > size()`.
1295
- */
1296
- void remove_prefix(size_type n) noexcept { assert(n <= size()), start_ += n, length_ -= n; }
1297
-
1298
- /**
1299
- * @brief Removes the last `n` characters from the view.
1300
- * @warning The behavior is @b undefined if `n > size()`.
1301
- */
1302
- void remove_suffix(size_type n) noexcept { assert(n <= size()), length_ -= n; }
1303
-
1304
- /** @brief Added for STL compatibility. */
1305
- string_slice substr() const noexcept { return *this; }
1306
-
1307
- /**
1308
- * @brief Return a slice of this view after first `skip` bytes.
1309
- * @throws `std::out_of_range` if `skip > size()`.
1310
- * @see `sub` for a cleaner exception-less alternative.
1311
- */
1312
- string_slice substr(size_type skip) const noexcept(false) {
1313
- if (skip > size()) throw std::out_of_range("string_slice::substr");
1314
- return string_slice(start_ + skip, length_ - skip);
1315
- }
1316
-
1317
- /**
1318
- * @brief Return a slice of this view after first `skip` bytes, taking at most `count` bytes.
1319
- * @throws `std::out_of_range` if `skip > size()`.
1320
- * @see `sub` for a cleaner exception-less alternative.
1321
- */
1322
- string_slice substr(size_type skip, size_type count) const noexcept(false) {
1323
- if (skip > size()) throw std::out_of_range("string_slice::substr");
1324
- return string_slice(start_ + skip, sz_min_of_two(count, length_ - skip));
1325
- }
1326
-
1327
- /**
1328
- * @brief Exports a slice of this view after first `skip` bytes, taking at most `count` bytes.
1329
- * @throws `std::out_of_range` if `skip > size()`.
1330
- * @see `sub` for a cleaner exception-less alternative.
1331
- */
1332
- size_type copy(value_type *destination, size_type count, size_type skip = 0) const noexcept(false) {
1333
- if (skip > size()) throw std::out_of_range("string_slice::copy");
1334
- count = sz_min_of_two(count, length_ - skip);
1335
- sz_copy((sz_ptr_t)destination, start_ + skip, count);
1336
- return count;
1337
- }
1338
-
1339
- #pragma endregion
1340
-
1341
- #pragma endregion
1342
-
1343
- #pragma region Comparisons
1344
-
1345
- #pragma region Whole String Comparisons
1346
-
1347
- /**
1348
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1349
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1350
- */
1351
- int compare(string_view other) const noexcept {
1352
- return (int)sz_order(start_, length_, other.start_, other.length_);
1353
- }
1354
-
1355
- /**
1356
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1357
- * Equivalent to `substr(pos1, count1).compare(other)`.
1358
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1359
- * @throw `std::out_of_range` if `pos1 > size()`.
1360
- */
1361
- int compare(size_type pos1, size_type count1, string_view other) const noexcept(false) {
1362
- return substr(pos1, count1).compare(other);
1363
- }
1364
-
1365
- /**
1366
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1367
- * Equivalent to `substr(pos1, count1).compare(other.substr(pos2, count2))`.
1368
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1369
- * @throw `std::out_of_range` if `pos1 > size()` or if `pos2 > other.size()`.
1370
- */
1371
- int compare(size_type pos1, size_type count1, string_view other, size_type pos2, size_type count2) const
1372
- noexcept(false) {
1373
- return substr(pos1, count1).compare(other.substr(pos2, count2));
1374
- }
1375
-
1376
- /**
1377
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1378
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1379
- */
1380
- int compare(const_pointer other) const noexcept { return compare(string_view(other)); }
1381
-
1382
- /**
1383
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1384
- * Equivalent to substr(pos1, count1).compare(other).
1385
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1386
- * @throw `std::out_of_range` if `pos1 > size()`.
1387
- */
1388
- int compare(size_type pos1, size_type count1, const_pointer other) const noexcept(false) {
1389
- return substr(pos1, count1).compare(string_view(other));
1390
- }
1391
-
1392
- /**
1393
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
1394
- * Equivalent to `substr(pos1, count1).compare({s, count2})`.
1395
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
1396
- * @throw `std::out_of_range` if `pos1 > size()`.
1397
- */
1398
- int compare(size_type pos1, size_type count1, const_pointer other, size_type count2) const noexcept(false) {
1399
- return substr(pos1, count1).compare(string_view(other, count2));
1400
- }
1401
-
1402
- /** @brief Checks if the string is equal to the other string. */
1403
- bool operator==(string_view other) const noexcept {
1404
- return size() == other.size() && sz_equal(data(), other.data(), other.size()) == sz_true_k;
1405
- }
1406
-
1407
- /** @brief Checks if the string is equal to a concatenation of two strings. */
1408
- bool operator==(concatenation<string_view, string_view> const &other) const noexcept {
1409
- return size() == other.size() && sz_equal(data(), other.first.data(), other.first.size()) == sz_true_k &&
1410
- sz_equal(data() + other.first.size(), other.second.data(), other.second.size()) == sz_true_k;
1411
- }
1412
-
1413
- #if SZ_DETECT_CPP20
1414
-
1415
- /** @brief Computes the lexicographic ordering between this and the ::other string. */
1416
- std::strong_ordering operator<=>(string_view other) const noexcept {
1417
- std::strong_ordering orders[3] {std::strong_ordering::less, std::strong_ordering::equal,
1418
- std::strong_ordering::greater};
1419
- return orders[compare(other) + 1];
1420
- }
1421
-
1422
- #else
1423
-
1424
- /** @brief Checks if the string is not equal to the other string. */
1425
- bool operator!=(string_view other) const noexcept { return !operator==(other); }
1426
-
1427
- /** @brief Checks if the string is lexicographically smaller than the other string. */
1428
- bool operator<(string_view other) const noexcept { return compare(other) == sz_less_k; }
1429
-
1430
- /** @brief Checks if the string is lexicographically equal or smaller than the other string. */
1431
- bool operator<=(string_view other) const noexcept { return compare(other) != sz_greater_k; }
1432
-
1433
- /** @brief Checks if the string is lexicographically greater than the other string. */
1434
- bool operator>(string_view other) const noexcept { return compare(other) == sz_greater_k; }
1435
-
1436
- /** @brief Checks if the string is lexicographically equal or greater than the other string. */
1437
- bool operator>=(string_view other) const noexcept { return compare(other) != sz_less_k; }
1438
-
1439
- #endif
1440
-
1441
- #pragma endregion
1442
- #pragma region Prefix and Suffix Comparisons
1443
-
1444
- /** @brief Checks if the string starts with the other string. */
1445
- bool starts_with(string_view other) const noexcept {
1446
- return length_ >= other.length_ && sz_equal(start_, other.start_, other.length_) == sz_true_k;
1447
- }
1448
-
1449
- /** @brief Checks if the string starts with the other string. */
1450
- bool starts_with(const_pointer other) const noexcept {
1451
- auto other_length = null_terminated_length(other);
1452
- return length_ >= other_length && sz_equal(start_, other, other_length) == sz_true_k;
1453
- }
1454
-
1455
- /** @brief Checks if the string starts with the other character. */
1456
- bool starts_with(value_type other) const noexcept { return length_ && start_[0] == other; }
1457
-
1458
- /** @brief Checks if the string ends with the other string. */
1459
- bool ends_with(string_view other) const noexcept {
1460
- return length_ >= other.length_ &&
1461
- sz_equal(start_ + length_ - other.length_, other.start_, other.length_) == sz_true_k;
1462
- }
1463
-
1464
- /** @brief Checks if the string ends with the other string. */
1465
- bool ends_with(const_pointer other) const noexcept {
1466
- auto other_length = null_terminated_length(other);
1467
- return length_ >= other_length && sz_equal(start_ + length_ - other_length, other, other_length) == sz_true_k;
1468
- }
1469
-
1470
- /** @brief Checks if the string ends with the other character. */
1471
- bool ends_with(value_type other) const noexcept { return length_ && start_[length_ - 1] == other; }
1472
-
1473
- /** @brief Python-like convenience function, dropping the matching prefix. */
1474
- string_slice remove_prefix(string_view other) const noexcept {
1475
- return starts_with(other) ? string_slice {start_ + other.length_, length_ - other.length_} : *this;
1476
- }
1477
-
1478
- /** @brief Python-like convenience function, dropping the matching suffix. */
1479
- string_slice remove_suffix(string_view other) const noexcept {
1480
- return ends_with(other) ? string_slice {start_, length_ - other.length_} : *this;
1481
- }
1482
-
1483
- #pragma endregion
1484
- #pragma endregion
1485
-
1486
- #pragma region Matching Substrings
1487
-
1488
- bool contains(string_view other) const noexcept { return find(other) != npos; }
1489
- bool contains(value_type character) const noexcept { return find(character) != npos; }
1490
- bool contains(const_pointer other) const noexcept { return find(other) != npos; }
1491
-
1492
- #pragma region Returning offsets
1493
-
1494
- /**
1495
- * @brief Find the first occurrence of a substring, skipping the first `skip` characters.
1496
- * The behavior is @b undefined if `skip > size()`.
1497
- * @return The offset of the first character of the match, or `npos` if not found.
1498
- */
1499
- size_type find(string_view other, size_type skip = 0) const noexcept {
1500
- auto ptr = sz_find(start_ + skip, length_ - skip, other.start_, other.length_);
1501
- return ptr ? ptr - start_ : npos;
1502
- }
1503
-
1504
- /**
1505
- * @brief Find the first occurrence of a character, skipping the first `skip` characters.
1506
- * The behavior is @b undefined if `skip > size()`.
1507
- * @return The offset of the match, or `npos` if not found.
1508
- */
1509
- size_type find(value_type character, size_type skip = 0) const noexcept {
1510
- auto ptr = sz_find_byte(start_ + skip, length_ - skip, &character);
1511
- return ptr ? ptr - start_ : npos;
1512
- }
1513
-
1514
- /**
1515
- * @brief Find the first occurrence of a substring, skipping the first `skip` characters.
1516
- * The behavior is @b undefined if `skip > size()`.
1517
- * @return The offset of the first character of the match, or `npos` if not found.
1518
- */
1519
- size_type find(const_pointer other, size_type pos, size_type count) const noexcept {
1520
- return find(string_view(other, count), pos);
1521
- }
1522
-
1523
- /**
1524
- * @brief Find the last occurrence of a substring.
1525
- * @return The offset of the first character of the match, or `npos` if not found.
1526
- */
1527
- size_type rfind(string_view other) const noexcept {
1528
- auto ptr = sz_rfind(start_, length_, other.start_, other.length_);
1529
- return ptr ? ptr - start_ : npos;
1530
- }
1531
-
1532
- /**
1533
- * @brief Find the last occurrence of a substring, within first `until` characters.
1534
- * @return The offset of the first character of the match, or `npos` if not found.
1535
- */
1536
- size_type rfind(string_view other, size_type until) const noexcept(false) {
1537
- return until + other.size() < length_ ? substr(0, until + other.size()).rfind(other) : rfind(other);
1538
- }
1539
-
1540
- /**
1541
- * @brief Find the last occurrence of a character.
1542
- * @return The offset of the match, or `npos` if not found.
1543
- */
1544
- size_type rfind(value_type character) const noexcept {
1545
- auto ptr = sz_rfind_byte(start_, length_, &character);
1546
- return ptr ? ptr - start_ : npos;
1547
- }
1548
-
1549
- /**
1550
- * @brief Find the last occurrence of a character, within first `until` characters.
1551
- * @return The offset of the match, or `npos` if not found.
1552
- */
1553
- size_type rfind(value_type character, size_type until) const noexcept {
1554
- return until < length_ ? substr(0, until + 1).rfind(character) : rfind(character);
1555
- }
1556
-
1557
- /**
1558
- * @brief Find the last occurrence of a substring, within first `until` characters.
1559
- * @return The offset of the first character of the match, or `npos` if not found.
1560
- */
1561
- size_type rfind(const_pointer other, size_type until, size_type count) const noexcept {
1562
- return rfind(string_view(other, count), until);
1563
- }
1564
-
1565
- /** @brief Find the first occurrence of a character from a set. */
1566
- size_type find(char_set set) const noexcept { return find_first_of(set); }
1567
-
1568
- /** @brief Find the last occurrence of a character from a set. */
1569
- size_type rfind(char_set set) const noexcept { return find_last_of(set); }
1570
-
1571
- #pragma endregion
1572
- #pragma region Returning Partitions
1573
-
1574
- /** @brief Split the string into three parts, before the match, the match itself, and after it. */
1575
- partition_type partition(string_view pattern) const noexcept { return partition_(pattern, pattern.length()); }
1576
-
1577
- /** @brief Split the string into three parts, before the match, the match itself, and after it. */
1578
- partition_type partition(char_set pattern) const noexcept { return partition_(pattern, 1); }
1579
-
1580
- /** @brief Split the string into three parts, before the @b last match, the last match itself, and after it. */
1581
- partition_type rpartition(string_view pattern) const noexcept { return rpartition_(pattern, pattern.length()); }
1582
-
1583
- /** @brief Split the string into three parts, before the @b last match, the last match itself, and after it. */
1584
- partition_type rpartition(char_set pattern) const noexcept { return rpartition_(pattern, 1); }
1585
-
1586
- #pragma endregion
1587
- #pragma endregion
1588
-
1589
- #pragma region Matching Character Sets
1590
-
1591
- // `isascii` is a macro in MSVC headers
1592
- bool contains_only(char_set set) const noexcept { return find_first_not_of(set) == npos; }
1593
- bool is_alpha() const noexcept { return !empty() && contains_only(ascii_letters_set()); }
1594
- bool is_alnum() const noexcept { return !empty() && contains_only(ascii_letters_set() | digits_set()); }
1595
- bool is_ascii() const noexcept { return empty() || contains_only(ascii_controls_set() | ascii_printables_set()); }
1596
- bool is_digit() const noexcept { return !empty() && contains_only(digits_set()); }
1597
- bool is_lower() const noexcept { return !empty() && contains_only(ascii_lowercase_set()); }
1598
- bool is_space() const noexcept { return !empty() && contains_only(whitespaces_set()); }
1599
- bool is_upper() const noexcept { return !empty() && contains_only(ascii_uppercase_set()); }
1600
- bool is_printable() const noexcept { return empty() || contains_only(ascii_printables_set()); }
1601
-
1602
- #pragma region Character Set Arguments
1603
- /**
1604
- * @brief Find the first occurrence of a character from a set.
1605
- * @param skip Number of characters to skip before the search.
1606
- * @warning The behavior is @b undefined if `skip > size()`.
1607
- */
1608
- size_type find_first_of(char_set set, size_type skip = 0) const noexcept {
1609
- auto ptr = sz_find_charset(start_ + skip, length_ - skip, &set.raw());
1610
- return ptr ? ptr - start_ : npos;
1611
- }
1612
-
1613
- /**
1614
- * @brief Find the first occurrence of a character outside a set.
1615
- * @param skip The number of first characters to be skipped.
1616
- * @warning The behavior is @b undefined if `skip > size()`.
1617
- */
1618
- size_type find_first_not_of(char_set set, size_type skip = 0) const noexcept {
1619
- return find_first_of(set.inverted(), skip);
1620
- }
1621
-
1622
- /**
1623
- * @brief Find the last occurrence of a character from a set.
1624
- */
1625
- size_type find_last_of(char_set set) const noexcept {
1626
- auto ptr = sz_rfind_charset(start_, length_, &set.raw());
1627
- return ptr ? ptr - start_ : npos;
1628
- }
1629
-
1630
- /**
1631
- * @brief Find the last occurrence of a character outside a set.
1632
- */
1633
- size_type find_last_not_of(char_set set) const noexcept { return find_last_of(set.inverted()); }
1634
-
1635
- /**
1636
- * @brief Find the last occurrence of a character from a set.
1637
- * @param until The offset of the last character to be considered.
1638
- */
1639
- size_type find_last_of(char_set set, size_type until) const noexcept {
1640
- auto len = sz_min_of_two(until + 1, length_);
1641
- auto ptr = sz_rfind_charset(start_, len, &set.raw());
1642
- return ptr ? ptr - start_ : npos;
1643
- }
1644
-
1645
- /**
1646
- * @brief Find the last occurrence of a character outside a set.
1647
- * @param until The offset of the last character to be considered.
1648
- */
1649
- size_type find_last_not_of(char_set set, size_type until) const noexcept {
1650
- return find_last_of(set.inverted(), until);
1651
- }
1652
-
1653
- #pragma endregion
1654
- #pragma region String Arguments
1655
-
1656
- /**
1657
- * @brief Find the first occurrence of a character from a ::set.
1658
- * @param skip The number of first characters to be skipped.
1659
- */
1660
- size_type find_first_of(string_view other, size_type skip = 0) const noexcept {
1661
- return find_first_of(other.as_set(), skip);
1662
- }
1663
-
1664
- /**
1665
- * @brief Find the first occurrence of a character outside a ::set.
1666
- * @param skip The number of first characters to be skipped.
1667
- */
1668
- size_type find_first_not_of(string_view other, size_type skip = 0) const noexcept {
1669
- return find_first_not_of(other.as_set(), skip);
1670
- }
1671
-
1672
- /**
1673
- * @brief Find the last occurrence of a character from a ::set.
1674
- * @param until The offset of the last character to be considered.
1675
- */
1676
- size_type find_last_of(string_view other, size_type until = npos) const noexcept {
1677
- return find_last_of(other.as_set(), until);
1678
- }
1679
-
1680
- /**
1681
- * @brief Find the last occurrence of a character outside a ::set.
1682
- * @param until The offset of the last character to be considered.
1683
- */
1684
- size_type find_last_not_of(string_view other, size_type until = npos) const noexcept {
1685
- return find_last_not_of(other.as_set(), until);
1686
- }
1687
-
1688
- #pragma endregion
1689
- #pragma region C-Style Arguments
1690
-
1691
- /**
1692
- * @brief Find the first occurrence of a character from a set.
1693
- * @param skip The number of first characters to be skipped.
1694
- * @warning The behavior is @b undefined if `skip > size()`.
1695
- */
1696
- size_type find_first_of(const_pointer other, size_type skip, size_type count) const noexcept {
1697
- return find_first_of(string_view(other, count), skip);
1698
- }
1699
-
1700
- /**
1701
- * @brief Find the first occurrence of a character outside a set.
1702
- * @param skip The number of first characters to be skipped.
1703
- * @warning The behavior is @b undefined if `skip > size()`.
1704
- */
1705
- size_type find_first_not_of(const_pointer other, size_type skip, size_type count) const noexcept {
1706
- return find_first_not_of(string_view(other, count), skip);
1707
- }
1708
-
1709
- /**
1710
- * @brief Find the last occurrence of a character from a set.
1711
- * @param until The number of first characters to be considered.
1712
- */
1713
- size_type find_last_of(const_pointer other, size_type until, size_type count) const noexcept {
1714
- return find_last_of(string_view(other, count), until);
1715
- }
1716
-
1717
- /**
1718
- * @brief Find the last occurrence of a character outside a set.
1719
- * @param until The number of first characters to be considered.
1720
- */
1721
- size_type find_last_not_of(const_pointer other, size_type until, size_type count) const noexcept {
1722
- return find_last_not_of(string_view(other, count), until);
1723
- }
1724
-
1725
- #pragma endregion
1726
- #pragma region Slicing
1727
-
1728
- /**
1729
- * @brief Python-like convenience function, dropping prefix formed of given characters.
1730
- * Similar to `boost::algorithm::trim_left_if(str, is_any_of(set))`.
1731
- */
1732
- string_slice lstrip(char_set set) const noexcept {
1733
- set = set.inverted();
1734
- auto new_start = sz_find_charset(start_, length_, &set.raw());
1735
- return new_start ? string_slice {new_start, length_ - static_cast<size_type>(new_start - start_)}
1736
- : string_slice();
1737
- }
1738
-
1739
- /**
1740
- * @brief Python-like convenience function, dropping suffix formed of given characters.
1741
- * Similar to `boost::algorithm::trim_right_if(str, is_any_of(set))`.
1742
- */
1743
- string_slice rstrip(char_set set) const noexcept {
1744
- set = set.inverted();
1745
- auto new_end = sz_rfind_charset(start_, length_, &set.raw());
1746
- return new_end ? string_slice {start_, static_cast<size_type>(new_end - start_ + 1)} : string_slice();
1747
- }
1748
-
1749
- /**
1750
- * @brief Python-like convenience function, dropping both the prefix & the suffix formed of given characters.
1751
- * Similar to `boost::algorithm::trim_if(str, is_any_of(set))`.
1752
- */
1753
- string_slice strip(char_set set) const noexcept {
1754
- set = set.inverted();
1755
- auto new_start = sz_find_charset(start_, length_, &set.raw());
1756
- return new_start ? string_slice {new_start,
1757
- static_cast<size_type>(
1758
- sz_rfind_charset(new_start, length_ - (new_start - start_), &set.raw()) -
1759
- new_start + 1)}
1760
- : string_slice();
1761
- }
1762
-
1763
- #pragma endregion
1764
- #pragma endregion
1765
-
1766
- #pragma region Search Ranges
1767
-
1768
- using find_all_type = range_matches<string_slice, matcher_find<string_view, include_overlaps_type>>;
1769
- using rfind_all_type = range_rmatches<string_slice, matcher_rfind<string_view, include_overlaps_type>>;
1770
-
1771
- using find_disjoint_type = range_matches<string_slice, matcher_find<string_view, exclude_overlaps_type>>;
1772
- using rfind_disjoint_type = range_rmatches<string_slice, matcher_rfind<string_view, exclude_overlaps_type>>;
1773
-
1774
- using find_all_chars_type = range_matches<string_slice, matcher_find_first_of<string_view, char_set>>;
1775
- using rfind_all_chars_type = range_rmatches<string_slice, matcher_find_last_of<string_view, char_set>>;
1776
-
1777
- /** @brief Find all potentially @b overlapping occurrences of a given string. */
1778
- find_all_type find_all(string_view needle, include_overlaps_type = {}) const noexcept { return {*this, needle}; }
1779
-
1780
- /** @brief Find all potentially @b overlapping occurrences of a given string in @b reverse order. */
1781
- rfind_all_type rfind_all(string_view needle, include_overlaps_type = {}) const noexcept { return {*this, needle}; }
1782
-
1783
- /** @brief Find all @b non-overlapping occurrences of a given string. */
1784
- find_disjoint_type find_all(string_view needle, exclude_overlaps_type) const noexcept { return {*this, needle}; }
1785
-
1786
- /** @brief Find all @b non-overlapping occurrences of a given string in @b reverse order. */
1787
- rfind_disjoint_type rfind_all(string_view needle, exclude_overlaps_type) const noexcept { return {*this, needle}; }
1788
-
1789
- /** @brief Find all occurrences of given characters. */
1790
- find_all_chars_type find_all(char_set set) const noexcept { return {*this, {set}}; }
1791
-
1792
- /** @brief Find all occurrences of given characters in @b reverse order. */
1793
- rfind_all_chars_type rfind_all(char_set set) const noexcept { return {*this, {set}}; }
1794
-
1795
- using split_type = range_splits<string_slice, matcher_find<string_view, exclude_overlaps_type>>;
1796
- using rsplit_type = range_rsplits<string_slice, matcher_rfind<string_view, exclude_overlaps_type>>;
1797
-
1798
- using split_chars_type = range_splits<string_slice, matcher_find_first_of<string_view, char_set>>;
1799
- using rsplit_chars_type = range_rsplits<string_slice, matcher_find_last_of<string_view, char_set>>;
1800
-
1801
- /** @brief Split around occurrences of a given string. */
1802
- split_type split(string_view delimiter) const noexcept { return {*this, delimiter}; }
1803
-
1804
- /** @brief Split around occurrences of a given string in @b reverse order. */
1805
- rsplit_type rsplit(string_view delimiter) const noexcept { return {*this, delimiter}; }
1806
-
1807
- /** @brief Split around occurrences of given characters. */
1808
- split_chars_type split(char_set set = whitespaces_set()) const noexcept { return {*this, {set}}; }
1809
-
1810
- /** @brief Split around occurrences of given characters in @b reverse order. */
1811
- rsplit_chars_type rsplit(char_set set = whitespaces_set()) const noexcept { return {*this, {set}}; }
1812
-
1813
- /** @brief Split around the occurrences of all newline characters. */
1814
- split_chars_type splitlines() const noexcept { return split(newlines_set); }
1815
-
1816
- #pragma endregion
1817
-
1818
- /** @brief Hashes the string, equivalent to `std::hash<string_view>{}(str)`. */
1819
- size_type hash() const noexcept { return static_cast<size_type>(sz_hash(start_, length_)); }
1820
-
1821
- /** @brief Populate a character set with characters present in this string. */
1822
- char_set as_set() const noexcept {
1823
- char_set set;
1824
- for (auto c : *this) set.add(c);
1825
- return set;
1826
- }
1827
-
1828
- private:
1829
- sz_constexpr_if_cpp20 string_view &assign(string_view const &other) noexcept {
1830
- start_ = other.start_;
1831
- length_ = other.length_;
1832
- return *this;
1833
- }
1834
-
1835
- sz_constexpr_if_cpp20 static size_type null_terminated_length(const_pointer s) noexcept {
1836
- const_pointer p = s;
1837
- while (*p) ++p;
1838
- return p - s;
1839
- }
1840
-
1841
- template <typename pattern_>
1842
- partition_type partition_(pattern_ &&pattern, std::size_t pattern_length) const noexcept {
1843
- size_type pos = find(pattern);
1844
- if (pos == npos) return {*this, string_view(), string_view()};
1845
- return {string_view(start_, pos), string_view(start_ + pos, pattern_length),
1846
- string_view(start_ + pos + pattern_length, length_ - pos - pattern_length)};
1847
- }
1848
-
1849
- template <typename pattern_>
1850
- partition_type rpartition_(pattern_ &&pattern, std::size_t pattern_length) const noexcept {
1851
- size_type pos = rfind(pattern);
1852
- if (pos == npos) return {*this, string_view(), string_view()};
1853
- return {string_view(start_, pos), string_view(start_ + pos, pattern_length),
1854
- string_view(start_ + pos + pattern_length, length_ - pos - pattern_length)};
1855
- }
1856
- };
1857
-
1858
- #pragma endregion
1859
-
1860
- /**
1861
- * @brief Memory-owning string class with a Small String Optimization.
1862
- *
1863
- * @section API
1864
- *
1865
- * Some APIs are different from `basic_string_slice`:
1866
- * * `lstrip`, `rstrip`, `strip` modify the string in-place, instead of returning a new view.
1867
- * * `sat`, `sub`, and element access has non-const overloads returning references to mutable objects.
1868
- *
1869
- * Functions defined for `basic_string`, but not present in `basic_string_slice`:
1870
- * * `replace`, `insert`, `erase`, `append`, `push_back`, `pop_back`, `resize`, `shrink_to_fit`... from STL,
1871
- * * `try_` exception-free "try" operations that returning non-zero values on success,
1872
- * * `replace_all` and `erase_all` similar to Boost,
1873
- * * `edit_distance` - Levenshtein distance computation reusing the allocator,
1874
- * * `randomize`, `random` - for fast random string generation.
1875
- *
1876
- * Functions defined for `basic_string_slice`, but not present in `basic_string`:
1877
- * * `[r]partition`, `[r]split`, `[r]find_all` missing to enforce lifetime on long operations.
1878
- * * `remove_prefix`, `remove_suffix` for now.
1879
- *
1880
- * @section Exceptions
1881
- *
1882
- * Default constructor is `constexpr`. Move constructor and move assignment operator are `noexcept`.
1883
- * Copy constructor and copy assignment operator are not! They may throw `std::bad_alloc` if the memory
1884
- * allocation fails. Similar to STL `std::out_of_range` if the position argument to some of the functions
1885
- * is out of bounds. Same as with STL, the bound checks are often asymmetric, so pay attention to docs.
1886
- * If exceptions are disabled, on failure, `std::terminate` is called.
1887
- */
1888
- template <typename char_type_, typename allocator_type_ = std::allocator<char_type_>>
1889
- class basic_string {
1890
-
1891
- static_assert(sizeof(char_type_) == 1, "Characters must be a single byte long");
1892
- static_assert(std::is_reference<char_type_>::value == false, "Characters can't be references");
1893
- static_assert(std::is_const<char_type_>::value == false, "Characters must be mutable");
1894
-
1895
- using char_type = char_type_;
1896
- using sz_alloc_type = sz_memory_allocator_t;
1897
-
1898
- sz_string_t string_;
1899
-
1900
- /**
1901
- * Stateful allocators and their support in C++ strings is extremely error-prone by design.
1902
- * Depending on traits like `propagate_on_container_copy_assignment` and `propagate_on_container_move_assignment`,
1903
- * its state will be copied from one string to another. It goes against the design of most string constructors,
1904
- * as they also receive allocator as the last argument!
1905
- */
1906
- static_assert(std::is_empty<allocator_type_>::value, "We currently only support stateless allocators");
1907
-
1908
- template <typename allocator_callback>
1909
- static bool _with_alloc(allocator_callback &&callback) noexcept {
1910
- return ashvardanian::stringzilla::_with_alloc<allocator_type_>(callback);
1911
- }
1912
-
1913
- bool is_internal() const noexcept { return sz_string_is_on_stack(&string_); }
1914
-
1915
- void init(std::size_t length, char_type value) noexcept(false) {
1916
- sz_ptr_t start;
1917
- if (!_with_alloc(
1918
- [&](sz_alloc_type &alloc) { return (start = sz_string_init_length(&string_, length, &alloc)); }))
1919
- throw std::bad_alloc();
1920
- sz_fill(start, length, *(sz_u8_t *)&value);
1921
- }
1922
-
1923
- void init(string_view other) noexcept(false) {
1924
- sz_ptr_t start;
1925
- if (!_with_alloc(
1926
- [&](sz_alloc_type &alloc) { return (start = sz_string_init_length(&string_, other.size(), &alloc)); }))
1927
- throw std::bad_alloc();
1928
- sz_copy(start, (sz_cptr_t)other.data(), other.size());
1929
- }
1930
-
1931
- void move(basic_string &other) noexcept {
1932
- // We can't just assign the other string state, as its start address may be somewhere else on the stack.
1933
- sz_ptr_t string_start;
1934
- sz_size_t string_length;
1935
- sz_size_t string_space;
1936
- sz_bool_t string_is_external;
1937
- sz_string_unpack(&other.string_, &string_start, &string_length, &string_space, &string_is_external);
1938
-
1939
- // Acquire the old string's value bitwise
1940
- *(&string_) = *(&other.string_);
1941
- // Reposition the string start pointer to the stack if it fits.
1942
- // Ternary condition may be optimized to a branchless version.
1943
- string_.internal.start = string_is_external ? string_.internal.start : &string_.internal.chars[0];
1944
- sz_string_init(&other.string_); // Discard the other string.
1945
- }
1946
-
1947
- public:
1948
- // STL compatibility
1949
- using traits_type = std::char_traits<char_type>;
1950
- using value_type = char_type;
1951
- using pointer = char_type *;
1952
- using const_pointer = char_type const *;
1953
- using reference = char_type &;
1954
- using const_reference = char_type const &;
1955
- using const_iterator = const_pointer;
1956
- using iterator = pointer;
1957
- using const_reverse_iterator = reversed_iterator_for<char_type const>;
1958
- using reverse_iterator = reversed_iterator_for<char_type>;
1959
- using size_type = std::size_t;
1960
- using difference_type = std::ptrdiff_t;
1961
-
1962
- // Non-STL type definitions
1963
- using allocator_type = allocator_type_;
1964
- using string_span = basic_string_slice<char_type>;
1965
- using string_view = basic_string_slice<typename std::add_const<char_type>::type>;
1966
- using partition_type = string_partition_result<string_view>;
1967
-
1968
- /** @brief Special value for missing matches.
1969
- *
1970
- * We take the largest 63-bit unsigned integer on 64-bit machines.
1971
- * We take the largest 31-bit unsigned integer on 32-bit machines.
1972
- */
1973
- static constexpr size_type npos = SZ_SSIZE_MAX;
1974
-
1975
- #pragma region Constructors and STL Utilities
1976
-
1977
- sz_constexpr_if_cpp20 basic_string() noexcept {
1978
- // ! Instead of relying on the `sz_string_init`, we have to reimplement it to support `constexpr`.
1979
- string_.internal.start = &string_.internal.chars[0];
1980
- string_.words[1] = 0;
1981
- string_.words[2] = 0;
1982
- string_.words[3] = 0;
1983
- }
1984
-
1985
- ~basic_string() noexcept {
1986
- _with_alloc([&](sz_alloc_type &alloc) {
1987
- sz_string_free(&string_, &alloc);
1988
- return true;
1989
- });
1990
- }
1991
-
1992
- basic_string(basic_string &&other) noexcept { move(other); }
1993
- basic_string &operator=(basic_string &&other) noexcept {
1994
- if (!is_internal()) {
1995
- _with_alloc([&](sz_alloc_type &alloc) {
1996
- sz_string_free(&string_, &alloc);
1997
- return true;
1998
- });
1999
- }
2000
- move(other);
2001
- return *this;
2002
- }
2003
-
2004
- basic_string(basic_string const &other) noexcept(false) { init(other); }
2005
- basic_string &operator=(basic_string const &other) noexcept(false) { return assign(other); }
2006
- basic_string(string_view view) noexcept(false) { init(view); }
2007
- basic_string &operator=(string_view view) noexcept(false) { return assign(view); }
2008
-
2009
- basic_string(const_pointer c_string) noexcept(false) : basic_string(string_view(c_string)) {}
2010
- basic_string(const_pointer c_string, size_type length) noexcept(false)
2011
- : basic_string(string_view(c_string, length)) {}
2012
- basic_string &operator=(const_pointer other) noexcept(false) { return assign(string_view(other)); }
2013
-
2014
- basic_string(std::nullptr_t) = delete;
2015
-
2016
- /** @brief Construct a string by repeating a certain ::character ::count times. */
2017
- basic_string(size_type count, value_type character) noexcept(false) { init(count, character); }
2018
-
2019
- basic_string(basic_string const &other, size_type pos) noexcept(false) { init(string_view(other).substr(pos)); }
2020
- basic_string(basic_string const &other, size_type pos, size_type count) noexcept(false) {
2021
- init(string_view(other).substr(pos, count));
2022
- }
2023
-
2024
- basic_string(std::initializer_list<value_type> list) noexcept(false) {
2025
- init(string_view(list.begin(), list.size()));
2026
- }
2027
-
2028
- operator string_view() const noexcept { return view(); }
2029
- string_view view() const noexcept {
2030
- sz_ptr_t string_start;
2031
- sz_size_t string_length;
2032
- sz_string_range(&string_, &string_start, &string_length);
2033
- return {string_start, string_length};
2034
- }
2035
-
2036
- operator string_span() noexcept { return span(); }
2037
- string_span span() noexcept {
2038
- sz_ptr_t string_start;
2039
- sz_size_t string_length;
2040
- sz_string_range(&string_, &string_start, &string_length);
2041
- return {string_start, string_length};
2042
- }
2043
-
2044
- /** @brief Exchanges the string contents witt the `other` string. */
2045
- void swap(basic_string &other) noexcept {
2046
- // If at least one of the strings is on the stack, a basic `std::swap(string_, other.string_)` won't work,
2047
- // as the pointer to the stack-allocated memory will be swapped, instead of the contents.
2048
- sz_ptr_t first_start, second_start;
2049
- sz_size_t first_length, second_length;
2050
- sz_size_t first_space, second_space;
2051
- sz_bool_t first_is_external, second_is_external;
2052
- sz_string_unpack(&string_, &first_start, &first_length, &first_space, &first_is_external);
2053
- sz_string_unpack(&other.string_, &second_start, &second_length, &second_space, &second_is_external);
2054
- std::swap(string_, other.string_);
2055
- if (!first_is_external) other.string_.internal.start = &other.string_.internal.chars[0];
2056
- if (!second_is_external) string_.internal.start = &string_.internal.chars[0];
2057
- }
2058
-
2059
- #if !SZ_AVOID_STL
2060
-
2061
- basic_string(std::string const &other) noexcept(false) : basic_string(other.data(), other.size()) {}
2062
- basic_string &operator=(std::string const &other) noexcept(false) { return assign({other.data(), other.size()}); }
2063
-
2064
- // As we are need both `data()` and `size()`, going through `operator string_view()`
2065
- // and `sz_string_unpack` is faster than separate invocations.
2066
- operator std::string() const { return view(); }
2067
-
2068
- /**
2069
- * @brief Formatted output function for compatibility with STL's `std::basic_ostream`.
2070
- * @throw `std::ios_base::failure` if an exception occurred during output.
2071
- */
2072
- template <typename stream_traits>
2073
- friend std::basic_ostream<value_type, stream_traits> &operator<<(std::basic_ostream<value_type, stream_traits> &os,
2074
- basic_string const &str) noexcept(false) {
2075
- return os.write(str.data(), str.size());
2076
- }
2077
-
2078
- #if SZ_DETECT_CPP_17 && __cpp_lib_string_view
2079
-
2080
- basic_string(std::string_view other) noexcept(false) : basic_string(other.data(), other.size()) {}
2081
- basic_string &operator=(std::string_view other) noexcept(false) { return assign({other.data(), other.size()}); }
2082
- operator std::string_view() const noexcept { return view(); }
2083
-
2084
- #endif
2085
-
2086
- #endif
2087
-
2088
- template <typename first_type, typename second_type>
2089
- explicit basic_string(concatenation<first_type, second_type> const &expression) noexcept(false) {
2090
- _with_alloc([&](sz_alloc_type &alloc) {
2091
- sz_ptr_t ptr = sz_string_init_length(&string_, expression.length(), &alloc);
2092
- if (!ptr) return false;
2093
- expression.copy(ptr);
2094
- return true;
2095
- });
2096
- }
2097
-
2098
- template <typename first_type, typename second_type>
2099
- basic_string &operator=(concatenation<first_type, second_type> const &expression) noexcept(false) {
2100
- if (!try_assign(expression)) throw std::bad_alloc();
2101
- return *this;
2102
- }
2103
-
2104
- #pragma endregion
2105
-
2106
- #pragma region Iterators and Accessors
2107
-
2108
- iterator begin() noexcept { return iterator(data()); }
2109
- const_iterator begin() const noexcept { return const_iterator(data()); }
2110
- const_iterator cbegin() const noexcept { return const_iterator(data()); }
2111
-
2112
- // As we are need both `data()` and `size()`, going through `operator string_view()`
2113
- // and `sz_string_unpack` is faster than separate invocations.
2114
- iterator end() noexcept { return span().end(); }
2115
- const_iterator end() const noexcept { return view().end(); }
2116
- const_iterator cend() const noexcept { return view().end(); }
2117
-
2118
- reverse_iterator rbegin() noexcept { return span().rbegin(); }
2119
- const_reverse_iterator rbegin() const noexcept { return view().rbegin(); }
2120
- const_reverse_iterator crbegin() const noexcept { return view().crbegin(); }
2121
-
2122
- reverse_iterator rend() noexcept { return span().rend(); }
2123
- const_reverse_iterator rend() const noexcept { return view().rend(); }
2124
- const_reverse_iterator crend() const noexcept { return view().crend(); }
2125
-
2126
- reference operator[](size_type pos) noexcept { return string_.internal.start[pos]; }
2127
- const_reference operator[](size_type pos) const noexcept { return string_.internal.start[pos]; }
2128
-
2129
- reference front() noexcept { return string_.internal.start[0]; }
2130
- const_reference front() const noexcept { return string_.internal.start[0]; }
2131
- reference back() noexcept { return string_.internal.start[size() - 1]; }
2132
- const_reference back() const noexcept { return string_.internal.start[size() - 1]; }
2133
- pointer data() noexcept { return string_.internal.start; }
2134
- const_pointer data() const noexcept { return string_.internal.start; }
2135
- pointer c_str() noexcept { return string_.internal.start; }
2136
- const_pointer c_str() const noexcept { return string_.internal.start; }
2137
-
2138
- reference at(size_type pos) noexcept(false) {
2139
- if (pos >= size()) throw std::out_of_range("sz::basic_string::at");
2140
- return string_.internal.start[pos];
2141
- }
2142
- const_reference at(size_type pos) const noexcept(false) {
2143
- if (pos >= size()) throw std::out_of_range("sz::basic_string::at");
2144
- return string_.internal.start[pos];
2145
- }
2146
-
2147
- difference_type ssize() const noexcept { return static_cast<difference_type>(size()); }
2148
- size_type size() const noexcept { return view().size(); }
2149
- size_type length() const noexcept { return size(); }
2150
- size_type max_size() const noexcept { return npos - 1; }
2151
- bool empty() const noexcept { return string_.external.length == 0; }
2152
- size_type capacity() const noexcept {
2153
- sz_ptr_t string_start;
2154
- sz_size_t string_length;
2155
- sz_size_t string_space;
2156
- sz_bool_t string_is_external;
2157
- sz_string_unpack(&string_, &string_start, &string_length, &string_space, &string_is_external);
2158
- return string_space - 1;
2159
- }
2160
-
2161
- allocator_type get_allocator() const noexcept { return {}; }
2162
-
2163
- #pragma endregion
2164
-
2165
- #pragma region Slicing
2166
-
2167
- #pragma region Safe and Signed Extensions
2168
-
2169
- /**
2170
- * @brief Equivalent to Python's `"abc"[-3:-1]`. Exception-safe, unlike STL's `substr`.
2171
- * Supports signed and unsigned intervals.
2172
- */
2173
- string_view operator[](std::initializer_list<difference_type> offsets) const noexcept { return view()[offsets]; }
2174
- string_span operator[](std::initializer_list<difference_type> offsets) noexcept { return span()[offsets]; }
2175
-
2176
- /**
2177
- * @brief Signed alternative to `at()`. Handy if you often write `str[str.size() - 2]`.
2178
- * @warning The behavior is @b undefined if the position is beyond bounds.
2179
- */
2180
- value_type sat(difference_type offset) const noexcept { return view().sat(offset); }
2181
- reference sat(difference_type offset) noexcept { return span().sat(offset); }
2182
-
2183
- /**
2184
- * @brief The opposite operation to `remove_prefix`, that does no bounds checking.
2185
- * @warning The behavior is @b undefined if `n > size()`.
2186
- */
2187
- string_view front(size_type n) const noexcept { return view().front(n); }
2188
- string_span front(size_type n) noexcept { return span().front(n); }
2189
-
2190
- /**
2191
- * @brief The opposite operation to `remove_prefix`, that does no bounds checking.
2192
- * @warning The behavior is @b undefined if `n > size()`.
2193
- */
2194
- string_view back(size_type n) const noexcept { return view().back(n); }
2195
- string_span back(size_type n) noexcept { return span().back(n); }
2196
-
2197
- /**
2198
- * @brief Equivalent to Python's `"abc"[-3:-1]`. Exception-safe, unlike STL's `substr`.
2199
- * Supports signed and unsigned intervals. @b Doesn't copy or allocate memory!
2200
- */
2201
- string_view sub(difference_type start, difference_type end = npos) const noexcept { return view().sub(start, end); }
2202
- string_span sub(difference_type start, difference_type end = npos) noexcept { return span().sub(start, end); }
2203
-
2204
- /**
2205
- * @brief Exports this entire view. Not an STL function, but useful for concatenations.
2206
- * The STL variant expects at least two arguments.
2207
- */
2208
- size_type copy(value_type *destination) const noexcept { return view().copy(destination); }
2209
-
2210
- #pragma endregion
2211
-
2212
- #pragma region STL Style
2213
-
2214
- /**
2215
- * @brief Removes the first `n` characters from the view.
2216
- * @warning The behavior is @b undefined if `n > size()`.
2217
- */
2218
- void remove_prefix(size_type n) noexcept {
2219
- assert(n <= size());
2220
- sz_string_erase(&string_, 0, n);
2221
- }
2222
-
2223
- /**
2224
- * @brief Removes the last `n` characters from the view.
2225
- * @warning The behavior is @b undefined if `n > size()`.
2226
- */
2227
- void remove_suffix(size_type n) noexcept {
2228
- assert(n <= size());
2229
- sz_string_erase(&string_, size() - n, n);
2230
- }
2231
-
2232
- /** @brief Added for STL compatibility. */
2233
- basic_string substr() const noexcept { return *this; }
2234
-
2235
- /**
2236
- * @brief Return a slice of this view after first `skip` bytes.
2237
- * @throws `std::out_of_range` if `skip > size()`.
2238
- * @see `sub` for a cleaner exception-less alternative.
2239
- */
2240
- basic_string substr(size_type skip) const noexcept(false) { return view().substr(skip); }
2241
-
2242
- /**
2243
- * @brief Return a slice of this view after first `skip` bytes, taking at most `count` bytes.
2244
- * @throws `std::out_of_range` if `skip > size()`.
2245
- * @see `sub` for a cleaner exception-less alternative.
2246
- */
2247
- basic_string substr(size_type skip, size_type count) const noexcept(false) { return view().substr(skip, count); }
2248
-
2249
- /**
2250
- * @brief Exports a slice of this view after first `skip` bytes, taking at most `count` bytes.
2251
- * @throws `std::out_of_range` if `skip > size()`.
2252
- * @see `sub` for a cleaner exception-less alternative.
2253
- */
2254
- size_type copy(value_type *destination, size_type count, size_type skip = 0) const noexcept(false) {
2255
- return view().copy(destination, count, skip);
2256
- }
2257
-
2258
- #pragma endregion
2259
-
2260
- #pragma endregion
2261
-
2262
- #pragma region Comparisons
2263
-
2264
- #pragma region Whole String Comparisons
2265
-
2266
- /**
2267
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2268
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2269
- */
2270
- int compare(string_view other) const noexcept { return view().compare(other); }
2271
-
2272
- /**
2273
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2274
- * Equivalent to `substr(pos1, count1).compare(other)`.
2275
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2276
- * @throw `std::out_of_range` if `pos1 > size()`.
2277
- */
2278
- int compare(size_type pos1, size_type count1, string_view other) const noexcept(false) {
2279
- return view().compare(pos1, count1, other);
2280
- }
2281
-
2282
- /**
2283
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2284
- * Equivalent to `substr(pos1, count1).compare(other.substr(pos2, count2))`.
2285
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2286
- * @throw `std::out_of_range` if `pos1 > size()` or if `pos2 > other.size()`.
2287
- */
2288
- int compare(size_type pos1, size_type count1, string_view other, size_type pos2, size_type count2) const
2289
- noexcept(false) {
2290
- return view().compare(pos1, count1, other, pos2, count2);
2291
- }
2292
-
2293
- /**
2294
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2295
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2296
- */
2297
- int compare(const_pointer other) const noexcept { return view().compare(other); }
2298
-
2299
- /**
2300
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2301
- * Equivalent to substr(pos1, count1).compare(other).
2302
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2303
- * @throw `std::out_of_range` if `pos1 > size()`.
2304
- */
2305
- int compare(size_type pos1, size_type count1, const_pointer other) const noexcept(false) {
2306
- return view().compare(pos1, count1, other);
2307
- }
2308
-
2309
- /**
2310
- * @brief Compares two strings lexicographically. If prefix matches, lengths are compared.
2311
- * Equivalent to `substr(pos1, count1).compare({s, count2})`.
2312
- * @return 0 if equal, negative if `*this` is less than `other`, positive if `*this` is greater than `other`.
2313
- * @throw `std::out_of_range` if `pos1 > size()`.
2314
- */
2315
- int compare(size_type pos1, size_type count1, const_pointer other, size_type count2) const noexcept(false) {
2316
- return view().compare(pos1, count1, other, count2);
2317
- }
2318
-
2319
- /** @brief Checks if the string is equal to the other string. */
2320
- bool operator==(basic_string const &other) const noexcept { return view() == other.view(); }
2321
- bool operator==(string_view other) const noexcept { return view() == other; }
2322
- bool operator==(const_pointer other) const noexcept { return view() == string_view(other); }
2323
-
2324
- #if SZ_DETECT_CPP20
2325
-
2326
- /** @brief Computes the lexicographic ordering between this and the ::other string. */
2327
- std::strong_ordering operator<=>(basic_string const &other) const noexcept { return view() <=> other.view(); }
2328
- std::strong_ordering operator<=>(string_view other) const noexcept { return view() <=> other; }
2329
- std::strong_ordering operator<=>(const_pointer other) const noexcept { return view() <=> string_view(other); }
2330
-
2331
- #else
2332
-
2333
- /** @brief Checks if the string is not equal to the other string. */
2334
- bool operator!=(string_view other) const noexcept { return !operator==(other); }
2335
-
2336
- /** @brief Checks if the string is lexicographically smaller than the other string. */
2337
- bool operator<(string_view other) const noexcept { return compare(other) == sz_less_k; }
2338
-
2339
- /** @brief Checks if the string is lexicographically equal or smaller than the other string. */
2340
- bool operator<=(string_view other) const noexcept { return compare(other) != sz_greater_k; }
2341
-
2342
- /** @brief Checks if the string is lexicographically greater than the other string. */
2343
- bool operator>(string_view other) const noexcept { return compare(other) == sz_greater_k; }
2344
-
2345
- /** @brief Checks if the string is lexicographically equal or greater than the other string. */
2346
- bool operator>=(string_view other) const noexcept { return compare(other) != sz_less_k; }
2347
-
2348
- #endif
2349
-
2350
- #pragma endregion
2351
- #pragma region Prefix and Suffix Comparisons
2352
-
2353
- /** @brief Checks if the string starts with the other string. */
2354
- bool starts_with(string_view other) const noexcept { return view().starts_with(other); }
2355
-
2356
- /** @brief Checks if the string starts with the other string. */
2357
- bool starts_with(const_pointer other) const noexcept { return view().starts_with(other); }
2358
-
2359
- /** @brief Checks if the string starts with the other character. */
2360
- bool starts_with(value_type other) const noexcept { return view().starts_with(other); }
2361
-
2362
- /** @brief Checks if the string ends with the other string. */
2363
- bool ends_with(string_view other) const noexcept { return view().ends_with(other); }
2364
-
2365
- /** @brief Checks if the string ends with the other string. */
2366
- bool ends_with(const_pointer other) const noexcept { return view().ends_with(other); }
2367
-
2368
- /** @brief Checks if the string ends with the other character. */
2369
- bool ends_with(value_type other) const noexcept { return view().ends_with(other); }
2370
-
2371
- #pragma endregion
2372
- #pragma endregion
2373
-
2374
- #pragma region Matching Substrings
2375
-
2376
- bool contains(string_view other) const noexcept { return view().contains(other); }
2377
- bool contains(value_type character) const noexcept { return view().contains(character); }
2378
- bool contains(const_pointer other) const noexcept { return view().contains(other); }
2379
-
2380
- #pragma region Returning offsets
2381
-
2382
- /**
2383
- * @brief Find the first occurrence of a substring, skipping the first `skip` characters.
2384
- * The behavior is @b undefined if `skip > size()`.
2385
- * @return The offset of the first character of the match, or `npos` if not found.
2386
- */
2387
- size_type find(string_view other, size_type skip = 0) const noexcept { return view().find(other, skip); }
2388
-
2389
- /**
2390
- * @brief Find the first occurrence of a character, skipping the first `skip` characters.
2391
- * The behavior is @b undefined if `skip > size()`.
2392
- * @return The offset of the match, or `npos` if not found.
2393
- */
2394
- size_type find(value_type character, size_type skip = 0) const noexcept { return view().find(character, skip); }
2395
-
2396
- /**
2397
- * @brief Find the first occurrence of a substring, skipping the first `skip` characters.
2398
- * The behavior is @b undefined if `skip > size()`.
2399
- * @return The offset of the first character of the match, or `npos` if not found.
2400
- */
2401
- size_type find(const_pointer other, size_type pos, size_type count) const noexcept {
2402
- return view().find(other, pos, count);
2403
- }
2404
-
2405
- /**
2406
- * @brief Find the last occurrence of a substring.
2407
- * @return The offset of the first character of the match, or `npos` if not found.
2408
- */
2409
- size_type rfind(string_view other) const noexcept { return view().rfind(other); }
2410
-
2411
- /**
2412
- * @brief Find the last occurrence of a substring, within first `until` characters.
2413
- * @return The offset of the first character of the match, or `npos` if not found.
2414
- */
2415
- size_type rfind(string_view other, size_type until) const noexcept { return view().rfind(other, until); }
2416
-
2417
- /**
2418
- * @brief Find the last occurrence of a character.
2419
- * @return The offset of the match, or `npos` if not found.
2420
- */
2421
- size_type rfind(value_type character) const noexcept { return view().rfind(character); }
2422
-
2423
- /**
2424
- * @brief Find the last occurrence of a character, within first `until` characters.
2425
- * @return The offset of the match, or `npos` if not found.
2426
- */
2427
- size_type rfind(value_type character, size_type until) const noexcept { return view().rfind(character, until); }
2428
-
2429
- /**
2430
- * @brief Find the last occurrence of a substring, within first `until` characters.
2431
- * @return The offset of the first character of the match, or `npos` if not found.
2432
- */
2433
- size_type rfind(const_pointer other, size_type until, size_type count) const noexcept {
2434
- return view().rfind(other, until, count);
2435
- }
2436
-
2437
- /** @brief Find the first occurrence of a character from a set. */
2438
- size_type find(char_set set) const noexcept { return view().find(set); }
2439
-
2440
- /** @brief Find the last occurrence of a character from a set. */
2441
- size_type rfind(char_set set) const noexcept { return view().rfind(set); }
2442
-
2443
- #pragma endregion
2444
- #pragma endregion
2445
-
2446
- #pragma region Matching Character Sets
2447
-
2448
- bool contains_only(char_set set) const noexcept { return find_first_not_of(set) == npos; }
2449
- bool is_alpha() const noexcept { return !empty() && contains_only(ascii_letters_set()); }
2450
- bool is_alnum() const noexcept { return !empty() && contains_only(ascii_letters_set() | digits_set()); }
2451
- bool is_ascii() const noexcept { return empty() || contains_only(ascii_controls_set() | ascii_printables_set()); }
2452
- bool is_digit() const noexcept { return !empty() && contains_only(digits_set()); }
2453
- bool is_lower() const noexcept { return !empty() && contains_only(ascii_lowercase_set()); }
2454
- bool is_space() const noexcept { return !empty() && contains_only(whitespaces_set()); }
2455
- bool is_upper() const noexcept { return !empty() && contains_only(ascii_uppercase_set()); }
2456
- bool is_printable() const noexcept { return empty() || contains_only(ascii_printables_set()); }
2457
-
2458
- #pragma region Character Set Arguments
2459
-
2460
- /**
2461
- * @brief Find the first occurrence of a character from a set.
2462
- * @param skip Number of characters to skip before the search.
2463
- * @warning The behavior is @b undefined if `skip > size()`.
2464
- */
2465
- size_type find_first_of(char_set set, size_type skip = 0) const noexcept { return view().find_first_of(set, skip); }
2466
-
2467
- /**
2468
- * @brief Find the first occurrence of a character outside a set.
2469
- * @param skip The number of first characters to be skipped.
2470
- * @warning The behavior is @b undefined if `skip > size()`.
2471
- */
2472
- size_type find_first_not_of(char_set set, size_type skip = 0) const noexcept {
2473
- return view().find_first_not_of(set, skip);
2474
- }
2475
-
2476
- /**
2477
- * @brief Find the last occurrence of a character from a set.
2478
- */
2479
- size_type find_last_of(char_set set) const noexcept { return view().find_last_of(set); }
2480
-
2481
- /**
2482
- * @brief Find the last occurrence of a character outside a set.
2483
- */
2484
- size_type find_last_not_of(char_set set) const noexcept { return view().find_last_not_of(set); }
2485
-
2486
- /**
2487
- * @brief Find the last occurrence of a character from a set.
2488
- * @param until The offset of the last character to be considered.
2489
- */
2490
- size_type find_last_of(char_set set, size_type until) const noexcept { return view().find_last_of(set, until); }
2491
-
2492
- /**
2493
- * @brief Find the last occurrence of a character outside a set.
2494
- * @param until The offset of the last character to be considered.
2495
- */
2496
- size_type find_last_not_of(char_set set, size_type until) const noexcept {
2497
- return view().find_last_not_of(set, until);
2498
- }
2499
-
2500
- #pragma endregion
2501
- #pragma region String Arguments
2502
-
2503
- /**
2504
- * @brief Find the first occurrence of a character from a ::set.
2505
- * @param skip The number of first characters to be skipped.
2506
- */
2507
- size_type find_first_of(string_view other, size_type skip = 0) const noexcept {
2508
- return view().find_first_of(other, skip);
2509
- }
2510
-
2511
- /**
2512
- * @brief Find the first occurrence of a character outside a ::set.
2513
- * @param skip The number of first characters to be skipped.
2514
- */
2515
- size_type find_first_not_of(string_view other, size_type skip = 0) const noexcept {
2516
- return view().find_first_not_of(other, skip);
2517
- }
2518
-
2519
- /**
2520
- * @brief Find the last occurrence of a character from a ::set.
2521
- * @param until The offset of the last character to be considered.
2522
- */
2523
- size_type find_last_of(string_view other, size_type until = npos) const noexcept {
2524
- return view().find_last_of(other, until);
2525
- }
2526
-
2527
- /**
2528
- * @brief Find the last occurrence of a character outside a ::set.
2529
- * @param until The offset of the last character to be considered.
2530
- */
2531
- size_type find_last_not_of(string_view other, size_type until = npos) const noexcept {
2532
- return view().find_last_not_of(other, until);
2533
- }
2534
-
2535
- #pragma endregion
2536
- #pragma region C-Style Arguments
2537
-
2538
- /**
2539
- * @brief Find the first occurrence of a character from a set.
2540
- * @param skip The number of first characters to be skipped.
2541
- * @warning The behavior is @b undefined if `skip > size()`.
2542
- */
2543
- size_type find_first_of(const_pointer other, size_type skip, size_type count) const noexcept {
2544
- return view().find_first_of(other, skip, count);
2545
- }
2546
-
2547
- /**
2548
- * @brief Find the first occurrence of a character outside a set.
2549
- * @param skip The number of first characters to be skipped.
2550
- * @warning The behavior is @b undefined if `skip > size()`.
2551
- */
2552
- size_type find_first_not_of(const_pointer other, size_type skip, size_type count) const noexcept {
2553
- return view().find_first_not_of(other, skip, count);
2554
- }
2555
-
2556
- /**
2557
- * @brief Find the last occurrence of a character from a set.
2558
- * @param until The number of first characters to be considered.
2559
- */
2560
- size_type find_last_of(const_pointer other, size_type until, size_type count) const noexcept {
2561
- return view().find_last_of(other, until, count);
2562
- }
2563
-
2564
- /**
2565
- * @brief Find the last occurrence of a character outside a set.
2566
- * @param until The number of first characters to be considered.
2567
- */
2568
- size_type find_last_not_of(const_pointer other, size_type until, size_type count) const noexcept {
2569
- return view().find_last_not_of(other, until, count);
2570
- }
2571
-
2572
- #pragma endregion
2573
- #pragma region Slicing
2574
-
2575
- /**
2576
- * @brief Python-like convenience function, dropping prefix formed of given characters.
2577
- * Similar to `boost::algorithm::trim_left_if(str, is_any_of(set))`.
2578
- */
2579
- basic_string &lstrip(char_set set) noexcept {
2580
- auto remaining = view().lstrip(set);
2581
- remove_prefix(size() - remaining.size());
2582
- return *this;
2583
- }
2584
-
2585
- /**
2586
- * @brief Python-like convenience function, dropping suffix formed of given characters.
2587
- * Similar to `boost::algorithm::trim_right_if(str, is_any_of(set))`.
2588
- */
2589
- basic_string &rstrip(char_set set) noexcept {
2590
- auto remaining = view().rstrip(set);
2591
- remove_suffix(size() - remaining.size());
2592
- return *this;
2593
- }
2594
-
2595
- /**
2596
- * @brief Python-like convenience function, dropping both the prefix & the suffix formed of given characters.
2597
- * Similar to `boost::algorithm::trim_if(str, is_any_of(set))`.
2598
- */
2599
- basic_string &strip(char_set set) noexcept { return lstrip(set).rstrip(set); }
2600
-
2601
- #pragma endregion
2602
- #pragma endregion
2603
-
2604
- #pragma region Modifiers
2605
- #pragma region Non-STL API
2606
-
2607
- bool try_resize(size_type count, value_type character = '\0') noexcept;
2608
-
2609
- bool try_reserve(size_type capacity) noexcept {
2610
- return _with_alloc([&](sz_alloc_type &alloc) { return sz_string_reserve(&string_, capacity, &alloc); });
2611
- }
2612
-
2613
- bool try_assign(string_view other) noexcept;
2614
-
2615
- template <typename first_type, typename second_type>
2616
- bool try_assign(concatenation<first_type, second_type> const &other) noexcept;
2617
-
2618
- bool try_push_back(char_type c) noexcept;
2619
-
2620
- bool try_append(const_pointer str, size_type length) noexcept;
2621
-
2622
- bool try_append(string_view str) noexcept { return try_append(str.data(), str.size()); }
2623
-
2624
- /**
2625
- * @brief Erases ( @b in-place ) a range of characters defined with signed offsets.
2626
- * @return Number of characters removed.
2627
- */
2628
- size_type try_erase(difference_type signed_start_offset = 0, difference_type signed_end_offset = npos) noexcept {
2629
- sz_size_t normalized_offset, normalized_length;
2630
- sz_ssize_clamp_interval(size(), signed_start_offset, signed_end_offset, &normalized_offset, &normalized_length);
2631
- if (!normalized_length) return false;
2632
- sz_string_erase(&string_, normalized_offset, normalized_length);
2633
- return normalized_length;
2634
- }
2635
-
2636
- /**
2637
- * @brief Inserts ( @b in-place ) a range of characters at a given signed offset.
2638
- * @return `true` if the insertion was successful, `false` otherwise.
2639
- */
2640
- bool try_insert(difference_type signed_offset, string_view string) noexcept {
2641
- sz_size_t normalized_offset, normalized_length;
2642
- sz_ssize_clamp_interval(size(), signed_offset, 0, &normalized_offset, &normalized_length);
2643
- if (!_with_alloc([&](sz_alloc_type &alloc) {
2644
- return sz_string_expand(&string_, normalized_offset, string.size(), &alloc);
2645
- }))
2646
- return false;
2647
-
2648
- sz_copy(data() + normalized_offset, string.data(), string.size());
2649
- return true;
2650
- }
2651
-
2652
- /**
2653
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2654
- * @return `true` if the replacement was successful, `false` otherwise.
2655
- */
2656
- bool try_replace(difference_type signed_start_offset, difference_type signed_end_offset,
2657
- string_view replacement) noexcept {
2658
-
2659
- sz_size_t normalized_offset, normalized_length;
2660
- sz_ssize_clamp_interval(size(), signed_start_offset, signed_end_offset, &normalized_offset, &normalized_length);
2661
- if (!try_preparing_replacement(normalized_offset, normalized_length, replacement)) return false;
2662
- sz_copy(data() + normalized_offset, replacement.data(), replacement.size());
2663
- return true;
2664
- }
2665
-
2666
- #pragma endregion
2667
-
2668
- #pragma region STL Interfaces
2669
-
2670
- /**
2671
- * @brief Clears the string contents, but @b no deallocations happen.
2672
- */
2673
- void clear() noexcept { sz_string_erase(&string_, 0, SZ_SIZE_MAX); }
2674
-
2675
- /**
2676
- * @brief Resizes the string to the given size, filling the new space with the given character,
2677
- * or NULL-character if nothing is provided.
2678
- * @throw `std::length_error` if the string is too long.
2679
- * @throw `std::bad_alloc` if the allocation fails.
2680
- */
2681
- void resize(size_type count, value_type character = '\0') noexcept(false) {
2682
- if (count > max_size()) throw std::length_error("sz::basic_string::resize");
2683
- if (!try_resize(count, character)) throw std::bad_alloc();
2684
- }
2685
-
2686
- /**
2687
- * @brief Informs the string object of a planned change in size, so that it pre-allocate once.
2688
- * @throw `std::length_error` if the string is too long.
2689
- */
2690
- void reserve(size_type capacity) noexcept(false) {
2691
- if (capacity > max_size()) throw std::length_error("sz::basic_string::reserve");
2692
- if (!try_reserve(capacity)) throw std::bad_alloc();
2693
- }
2694
-
2695
- /**
2696
- * @brief Inserts ( @b in-place ) a ::character multiple times at the given offset.
2697
- * @throw `std::out_of_range` if `offset > size()`.
2698
- * @throw `std::length_error` if the string is too long.
2699
- * @throw `std::bad_alloc` if the allocation fails.
2700
- */
2701
- basic_string &insert(size_type offset, size_type repeats, char_type character) noexcept(false) {
2702
- if (offset > size()) throw std::out_of_range("sz::basic_string::insert");
2703
- if (size() + repeats > max_size()) throw std::length_error("sz::basic_string::insert");
2704
- if (!_with_alloc([&](sz_alloc_type &alloc) { return sz_string_expand(&string_, offset, repeats, &alloc); }))
2705
- throw std::bad_alloc();
2706
-
2707
- sz_fill(data() + offset, repeats, character);
2708
- return *this;
2709
- }
2710
-
2711
- /**
2712
- * @brief Inserts ( @b in-place ) a range of characters at the given offset.
2713
- * @throw `std::out_of_range` if `offset > size()`.
2714
- * @throw `std::length_error` if the string is too long.
2715
- * @throw `std::bad_alloc` if the allocation fails.
2716
- */
2717
- basic_string &insert(size_type offset, string_view other) noexcept(false) {
2718
- if (offset > size()) throw std::out_of_range("sz::basic_string::insert");
2719
- if (size() + other.size() > max_size()) throw std::length_error("sz::basic_string::insert");
2720
- if (!_with_alloc(
2721
- [&](sz_alloc_type &alloc) { return sz_string_expand(&string_, offset, other.size(), &alloc); }))
2722
- throw std::bad_alloc();
2723
-
2724
- sz_copy(data() + offset, other.data(), other.size());
2725
- return *this;
2726
- }
2727
-
2728
- /**
2729
- * @brief Inserts ( @b in-place ) a range of characters at the given offset.
2730
- * @throw `std::out_of_range` if `offset > size()`.
2731
- * @throw `std::length_error` if the string is too long.
2732
- * @throw `std::bad_alloc` if the allocation fails.
2733
- */
2734
- basic_string &insert(size_type offset, const_pointer start, size_type length) noexcept(false) {
2735
- return insert(offset, string_view(start, length));
2736
- }
2737
-
2738
- /**
2739
- * @brief Inserts ( @b in-place ) a slice of another string at the given offset.
2740
- * @throw `std::out_of_range` if `offset > size()` or `other_index > other.size()`.
2741
- * @throw `std::length_error` if the string is too long.
2742
- * @throw `std::bad_alloc` if the allocation fails.
2743
- */
2744
- basic_string &insert(size_type offset, string_view other, size_type other_index,
2745
- size_type count = npos) noexcept(false) {
2746
- return insert(offset, other.substr(other_index, count));
2747
- }
2748
-
2749
- /**
2750
- * @brief Inserts ( @b in-place ) one ::character at the given iterator position.
2751
- * @throw `std::out_of_range` if `pos > size()` or `other_index > other.size()`.
2752
- * @throw `std::length_error` if the string is too long.
2753
- * @throw `std::bad_alloc` if the allocation fails.
2754
- */
2755
- iterator insert(const_iterator it, char_type character) noexcept(false) {
2756
- auto pos = range_length(cbegin(), it);
2757
- insert(pos, string_view(&character, 1));
2758
- return begin() + pos;
2759
- }
2760
-
2761
- /**
2762
- * @brief Inserts ( @b in-place ) a ::character multiple times at the given iterator position.
2763
- * @throw `std::out_of_range` if `pos > size()` or `other_index > other.size()`.
2764
- * @throw `std::length_error` if the string is too long.
2765
- * @throw `std::bad_alloc` if the allocation fails.
2766
- */
2767
- iterator insert(const_iterator it, size_type repeats, char_type character) noexcept(false) {
2768
- auto pos = range_length(cbegin(), it);
2769
- insert(pos, repeats, character);
2770
- return begin() + pos;
2771
- }
2772
-
2773
- /**
2774
- * @brief Inserts ( @b in-place ) a range at the given iterator position.
2775
- * @throw `std::out_of_range` if `pos > size()` or `other_index > other.size()`.
2776
- * @throw `std::length_error` if the string is too long.
2777
- * @throw `std::bad_alloc` if the allocation fails.
2778
- */
2779
- template <typename input_iterator>
2780
- iterator insert(const_iterator it, input_iterator first, input_iterator last) noexcept(false) {
2781
-
2782
- auto pos = range_length(cbegin(), it);
2783
- if (pos > size()) throw std::out_of_range("sz::basic_string::insert");
2784
-
2785
- auto added_length = range_length(first, last);
2786
- if (size() + added_length > max_size()) throw std::length_error("sz::basic_string::insert");
2787
-
2788
- if (!_with_alloc([&](sz_alloc_type &alloc) { return sz_string_expand(&string_, pos, added_length, &alloc); }))
2789
- throw std::bad_alloc();
2790
-
2791
- iterator result = begin() + pos;
2792
- for (iterator output = result; first != last; ++first, ++output) *output = *first;
2793
- return result;
2794
- }
2795
-
2796
- /**
2797
- * @brief Inserts ( @b in-place ) an initializer list of characters.
2798
- * @throw `std::out_of_range` if `pos > size()` or `other_index > other.size()`.
2799
- * @throw `std::length_error` if the string is too long.
2800
- * @throw `std::bad_alloc` if the allocation fails.
2801
- */
2802
- iterator insert(const_iterator it, std::initializer_list<char_type> list) noexcept(false) {
2803
- return insert(it, list.begin(), list.end());
2804
- }
2805
-
2806
- /**
2807
- * @brief Erases ( @b in-place ) the given range of characters.
2808
- * @throws `std::out_of_range` if `pos > size()`.
2809
- * @see `try_erase_slice` for a cleaner exception-less alternative.
2810
- */
2811
- basic_string &erase(size_type pos = 0, size_type count = npos) noexcept(false) {
2812
- if (!count || empty()) return *this;
2813
- if (pos >= size()) throw std::out_of_range("sz::basic_string::erase");
2814
- sz_string_erase(&string_, pos, count);
2815
- return *this;
2816
- }
2817
-
2818
- /**
2819
- * @brief Erases ( @b in-place ) the given range of characters.
2820
- * @return Iterator pointing following the erased character, or end() if no such character exists.
2821
- */
2822
- iterator erase(const_iterator first, const_iterator last) noexcept {
2823
- auto start = begin();
2824
- auto offset = first - start;
2825
- sz_string_erase(&string_, offset, last - first);
2826
- return start + offset;
2827
- }
2828
-
2829
- /**
2830
- * @brief Erases ( @b in-place ) the one character at a given postion.
2831
- * @return Iterator pointing following the erased character, or end() if no such character exists.
2832
- */
2833
- iterator erase(const_iterator pos) noexcept { return erase(pos, pos + 1); }
2834
-
2835
- /**
2836
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2837
- * @throws `std::out_of_range` if `pos > size()`.
2838
- * @throws `std::length_error` if the string is too long.
2839
- * @see `try_replace` for a cleaner exception-less alternative.
2840
- */
2841
- basic_string &replace(size_type pos, size_type count, string_view const &str) noexcept(false) {
2842
- if (pos > size()) throw std::out_of_range("sz::basic_string::replace");
2843
- if (size() - count + str.size() > max_size()) throw std::length_error("sz::basic_string::replace");
2844
- if (!try_preparing_replacement(pos, count, str.size())) throw std::bad_alloc();
2845
- sz_copy(data() + pos, str.data(), str.size());
2846
- return *this;
2847
- }
2848
-
2849
- /**
2850
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2851
- * @throws `std::out_of_range` if `pos > size()`.
2852
- * @throws `std::length_error` if the string is too long.
2853
- * @see `try_replace` for a cleaner exception-less alternative.
2854
- */
2855
- basic_string &replace(const_iterator first, const_iterator last, string_view const &str) noexcept(false) {
2856
- return replace(range_length(cbegin(), first), last - first, str);
2857
- }
2858
-
2859
- /**
2860
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2861
- * @throws `std::out_of_range` if `pos > size()` or `pos2 > str.size()`.
2862
- * @throws `std::length_error` if the string is too long.
2863
- * @see `try_replace` for a cleaner exception-less alternative.
2864
- */
2865
- basic_string &replace(size_type pos, size_type count, string_view const &str, size_type pos2,
2866
- size_type count2 = npos) noexcept(false) {
2867
- return replace(pos, count, str.substr(pos2, count2));
2868
- }
2869
-
2870
- /**
2871
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2872
- * @throws `std::out_of_range` if `pos > size()`.
2873
- * @throws `std::length_error` if the string is too long.
2874
- * @see `try_replace` for a cleaner exception-less alternative.
2875
- */
2876
- basic_string &replace(size_type pos, size_type count, const_pointer cstr, size_type count2) noexcept(false) {
2877
- return replace(pos, count, string_view(cstr, count2));
2878
- }
2879
-
2880
- /**
2881
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2882
- * @throws `std::out_of_range` if `pos > size()`.
2883
- * @throws `std::length_error` if the string is too long.
2884
- * @see `try_replace` for a cleaner exception-less alternative.
2885
- */
2886
- basic_string &replace(const_iterator first, const_iterator last, const_pointer cstr,
2887
- size_type count2) noexcept(false) {
2888
- return replace(range_length(cbegin(), first), last - first, string_view(cstr, count2));
2889
- }
2890
-
2891
- /**
2892
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2893
- * @throws `std::out_of_range` if `pos > size()`.
2894
- * @throws `std::length_error` if the string is too long.
2895
- * @see `try_replace` for a cleaner exception-less alternative.
2896
- */
2897
- basic_string &replace(size_type pos, size_type count, const_pointer cstr) noexcept(false) {
2898
- return replace(pos, count, string_view(cstr));
2899
- }
2900
-
2901
- /**
2902
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2903
- * @throws `std::out_of_range` if `pos > size()`.
2904
- * @throws `std::length_error` if the string is too long.
2905
- * @see `try_replace` for a cleaner exception-less alternative.
2906
- */
2907
- basic_string &replace(const_iterator first, const_iterator last, const_pointer cstr) noexcept(false) {
2908
- return replace(range_length(cbegin(), first), last - first, string_view(cstr));
2909
- }
2910
-
2911
- /**
2912
- * @brief Replaces ( @b in-place ) a range of characters with a repetition of given characters.
2913
- * @throws `std::out_of_range` if `pos > size()`.
2914
- * @throws `std::length_error` if the string is too long.
2915
- * @see `try_replace` for a cleaner exception-less alternative.
2916
- */
2917
- basic_string &replace(size_type pos, size_type count, size_type count2, char_type character) noexcept(false) {
2918
- if (pos > size()) throw std::out_of_range("sz::basic_string::replace");
2919
- if (size() - count + count2 > max_size()) throw std::length_error("sz::basic_string::replace");
2920
- if (!try_preparing_replacement(pos, count, count2)) throw std::bad_alloc();
2921
- sz_fill(data() + pos, count2, character);
2922
- return *this;
2923
- }
2924
-
2925
- /**
2926
- * @brief Replaces ( @b in-place ) a range of characters with a repetition of given characters.
2927
- * @throws `std::out_of_range` if `pos > size()`.
2928
- * @throws `std::length_error` if the string is too long.
2929
- * @see `try_replace` for a cleaner exception-less alternative.
2930
- */
2931
- basic_string &replace(const_iterator first, const_iterator last, size_type count2,
2932
- char_type character) noexcept(false) {
2933
- return replace(range_length(cbegin(), first), last - first, count2, character);
2934
- }
2935
-
2936
- /**
2937
- * @brief Replaces ( @b in-place ) a range of characters with a given string.
2938
- * @throws `std::out_of_range` if `pos > size()`.
2939
- * @throws `std::length_error` if the string is too long.
2940
- * @see `try_replace` for a cleaner exception-less alternative.
2941
- */
2942
- template <typename input_iterator>
2943
- basic_string &replace(const_iterator first, const_iterator last, input_iterator first2,
2944
- input_iterator last2) noexcept(false) {
2945
- auto pos = range_length(cbegin(), first);
2946
- auto count = range_length(first, last);
2947
- auto count2 = range_length(first2, last2);
2948
- if (pos > size()) throw std::out_of_range("sz::basic_string::replace");
2949
- if (size() - count + count2 > max_size()) throw std::length_error("sz::basic_string::replace");
2950
- if (!try_preparing_replacement(pos, count, count2)) throw std::bad_alloc();
2951
- for (iterator output = begin() + pos; first2 != last2; ++first2, ++output) *output = *first2;
2952
- return *this;
2953
- }
2954
-
2955
- /**
2956
- * @brief Replaces ( @b in-place ) a range of characters with a given initializer list.
2957
- * @throws `std::out_of_range` if `pos > size()`.
2958
- * @throws `std::length_error` if the string is too long.
2959
- * @see `try_replace` for a cleaner exception-less alternative.
2960
- */
2961
- basic_string &replace(const_iterator first, const_iterator last,
2962
- std::initializer_list<char_type> list) noexcept(false) {
2963
- return replace(first, last, list.begin(), list.end());
2964
- }
2965
-
2966
- /**
2967
- * @brief Appends the given character at the end.
2968
- * @throw `std::length_error` if the string is too long.
2969
- * @throw `std::bad_alloc` if the allocation fails.
2970
- */
2971
- void push_back(char_type ch) noexcept(false) {
2972
- if (size() == max_size()) throw std::length_error("string::push_back");
2973
- if (!try_push_back(ch)) throw std::bad_alloc();
2974
- }
2975
-
2976
- /**
2977
- * @brief Removes the last character from the string.
2978
- * @warning The behavior is @b undefined if the string is empty.
2979
- */
2980
- void pop_back() noexcept { sz_string_erase(&string_, size() - 1, 1); }
2981
-
2982
- /**
2983
- * @brief Overwrites the string with the given string.
2984
- * @throw `std::length_error` if the string is too long.
2985
- * @throw `std::bad_alloc` if the allocation fails.
2986
- * @see `try_assign` for a cleaner exception-less alternative.
2987
- */
2988
- basic_string &assign(string_view other) noexcept(false) {
2989
- if (!try_assign(other)) throw std::bad_alloc();
2990
- return *this;
2991
- }
2992
-
2993
- /**
2994
- * @brief Overwrites the string with the given repeated character.
2995
- * @throw `std::length_error` if the string is too long.
2996
- * @throw `std::bad_alloc` if the allocation fails.
2997
- * @see `try_assign` for a cleaner exception-less alternative.
2998
- */
2999
- basic_string &assign(size_type repeats, char_type character) noexcept(false) {
3000
- resize(repeats, character);
3001
- sz_fill(data(), repeats, *(sz_u8_t *)&character);
3002
- return *this;
3003
- }
3004
-
3005
- /**
3006
- * @brief Overwrites the string with the given string.
3007
- * @throw `std::length_error` if the string is too long.
3008
- * @throw `std::bad_alloc` if the allocation fails.
3009
- * @see `try_assign` for a cleaner exception-less alternative.
3010
- */
3011
- basic_string &assign(const_pointer other, size_type length) noexcept(false) { return assign({other, length}); }
3012
-
3013
- /**
3014
- * @brief Overwrites the string with the given string.
3015
- * @throw `std::length_error` if the string is too long or `pos > str.size()`.
3016
- * @throw `std::bad_alloc` if the allocation fails.
3017
- * @see `try_assign` for a cleaner exception-less alternative.
3018
- */
3019
- basic_string &assign(string_view str, size_type pos, size_type count = npos) noexcept(false) {
3020
- return assign(str.substr(pos, count));
3021
- }
3022
-
3023
- /**
3024
- * @brief Overwrites the string with the given iterator range.
3025
- * @throw `std::length_error` if the string is too long.
3026
- * @throw `std::bad_alloc` if the allocation fails.
3027
- * @see `try_assign` for a cleaner exception-less alternative.
3028
- */
3029
- template <typename input_iterator>
3030
- basic_string &assign(input_iterator first, input_iterator last) noexcept(false) {
3031
- resize(range_length(first, last));
3032
- for (iterator output = begin(); first != last; ++first, ++output) *output = *first;
3033
- return *this;
3034
- }
3035
-
3036
- /**
3037
- * @brief Overwrites the string with the given initializer list.
3038
- * @throw `std::length_error` if the string is too long.
3039
- * @throw `std::bad_alloc` if the allocation fails.
3040
- * @see `try_assign` for a cleaner exception-less alternative.
3041
- */
3042
- basic_string &assign(std::initializer_list<char_type> list) noexcept(false) {
3043
- return assign(list.begin(), list.end());
3044
- }
3045
-
3046
- /**
3047
- * @brief Appends to the end of the current string.
3048
- * @throw `std::length_error` if the string is too long.
3049
- * @throw `std::bad_alloc` if the allocation fails.
3050
- * @see `try_append` for a cleaner exception-less alternative.
3051
- */
3052
- basic_string &append(string_view str) noexcept(false) {
3053
- if (!try_append(str)) throw std::bad_alloc();
3054
- return *this;
3055
- }
3056
-
3057
- /**
3058
- * @brief Appends to the end of the current string.
3059
- * @throw `std::length_error` if the string is too long or `pos > str.size()`.
3060
- * @throw `std::bad_alloc` if the allocation fails.
3061
- * @see `try_append` for a cleaner exception-less alternative.
3062
- */
3063
- basic_string &append(string_view str, size_type pos, size_type length = npos) noexcept(false) {
3064
- return append(str.substr(pos, length));
3065
- }
3066
-
3067
- /**
3068
- * @brief Appends to the end of the current string.
3069
- * @throw `std::length_error` if the string is too long.
3070
- * @throw `std::bad_alloc` if the allocation fails.
3071
- * @see `try_append` for a cleaner exception-less alternative.
3072
- */
3073
- basic_string &append(const_pointer str, size_type length) noexcept(false) { return append({str, length}); }
3074
-
3075
- /**
3076
- * @brief Appends to the end of the current string.
3077
- * @throw `std::length_error` if the string is too long.
3078
- * @throw `std::bad_alloc` if the allocation fails.
3079
- * @see `try_append` for a cleaner exception-less alternative.
3080
- */
3081
- basic_string &append(const_pointer str) noexcept(false) { return append(string_view(str)); }
3082
-
3083
- /**
3084
- * @brief Appends a repeated character to the end of the current string.
3085
- * @throw `std::length_error` if the string is too long.
3086
- * @throw `std::bad_alloc` if the allocation fails.
3087
- * @see `try_append` for a cleaner exception-less alternative.
3088
- */
3089
- basic_string &append(size_type repeats, char_type ch) noexcept(false) {
3090
- resize(size() + repeats, ch);
3091
- return *this;
3092
- }
3093
-
3094
- /**
3095
- * @brief Appends to the end of the current string.
3096
- * @throw `std::length_error` if the string is too long.
3097
- * @throw `std::bad_alloc` if the allocation fails.
3098
- * @see `try_append` for a cleaner exception-less alternative.
3099
- */
3100
- basic_string &append(std::initializer_list<char_type> other) noexcept(false) {
3101
- return append(other.begin(), other.end());
3102
- }
3103
-
3104
- /**
3105
- * @brief Appends to the end of the current string.
3106
- * @throw `std::length_error` if the string is too long.
3107
- * @throw `std::bad_alloc` if the allocation fails.
3108
- * @see `try_append` for a cleaner exception-less alternative.
3109
- */
3110
- template <typename input_iterator>
3111
- basic_string &append(input_iterator first, input_iterator last) noexcept(false) {
3112
- insert<input_iterator>(cend(), first, last);
3113
- return *this;
3114
- }
3115
-
3116
- basic_string &operator+=(string_view other) noexcept(false) { return append(other); }
3117
- basic_string &operator+=(std::initializer_list<char_type> other) noexcept(false) { return append(other); }
3118
- basic_string &operator+=(char_type character) noexcept(false) { return operator+=(string_view(&character, 1)); }
3119
- basic_string &operator+=(const_pointer other) noexcept(false) { return operator+=(string_view(other)); }
3120
-
3121
- basic_string operator+(char_type character) const noexcept(false) { return operator+(string_view(&character, 1)); }
3122
- basic_string operator+(const_pointer other) const noexcept(false) { return operator+(string_view(other)); }
3123
- basic_string operator+(string_view other) const noexcept(false) {
3124
- return basic_string {concatenation<string_view, string_view> {view(), other}};
3125
- }
3126
- basic_string operator+(std::initializer_list<char_type> other) const noexcept(false) {
3127
- return basic_string {concatenation<string_view, string_view> {view(), other}};
3128
- }
3129
-
3130
- #pragma endregion
3131
- #pragma endregion
3132
-
3133
- concatenation<string_view, string_view> operator|(string_view other) const noexcept { return {view(), other}; }
3134
-
3135
- size_type edit_distance(string_view other, size_type bound = 0) const noexcept {
3136
- size_type distance;
3137
- _with_alloc([&](sz_alloc_type &alloc) {
3138
- distance = sz_edit_distance(data(), size(), other.data(), other.size(), bound, &alloc);
3139
- return true;
3140
- });
3141
- return distance;
3142
- }
3143
-
3144
- /** @brief Hashes the string, equivalent to `std::hash<string_view>{}(str)`. */
3145
- size_type hash() const noexcept { return view().hash(); }
3146
-
3147
- /**
3148
- * @brief Overwrites the string with random characters from the given alphabet using the random generator.
3149
- *
3150
- * @param generator A random generator function object that returns a random number in the range [0, 2^64).
3151
- * @param alphabet A string of characters to choose from.
3152
- */
3153
- template <typename generator_type>
3154
- basic_string &randomize(generator_type &generator, string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept {
3155
- sz_ptr_t start;
3156
- sz_size_t length;
3157
- sz_string_range(&string_, &start, &length);
3158
- sz_random_generator_t generator_callback = &_call_random_generator<generator_type>;
3159
- sz_generate(alphabet.data(), alphabet.size(), start, length, generator_callback, &generator);
3160
- return *this;
3161
- }
3162
-
3163
- /**
3164
- * @brief Overwrites the string with random characters from the given alphabet
3165
- * using `std::rand` as the random generator.
3166
- *
3167
- * @param alphabet A string of characters to choose from.
3168
- */
3169
- basic_string &randomize(string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept {
3170
- auto generator = []() { return static_cast<sz_u64_t>(std::rand()); };
3171
- return randomize(generator, alphabet);
3172
- }
3173
-
3174
- /**
3175
- * @brief Generate a new random string of given length using `std::rand` as the random generator.
3176
- * May throw exceptions if the memory allocation fails.
3177
- *
3178
- * @param length The length of the generated string.
3179
- * @param alphabet A string of characters to choose from.
3180
- */
3181
- static basic_string random(size_type length, string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept(false) {
3182
- return basic_string(length, '\0').randomize(alphabet);
3183
- }
3184
-
3185
- /**
3186
- * @brief Generate a new random string of given length using the provided random number generator.
3187
- * May throw exceptions if the memory allocation fails.
3188
- *
3189
- * @param generator A random generator function object that returns a random number in the range [0, 2^64).
3190
- * @param length The length of the generated string.
3191
- * @param alphabet A string of characters to choose from.
3192
- */
3193
- template <typename generator_type>
3194
- static basic_string random(generator_type &generator, size_type length,
3195
- string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept(false) {
3196
- return basic_string(length, '\0').randomize(generator, alphabet);
3197
- }
3198
-
3199
- /**
3200
- * @brief Replaces ( @b in-place ) all occurrences of a given string with the ::replacement string.
3201
- * Similar to `boost::algorithm::replace_all` and Python's `str.replace`.
3202
- *
3203
- * The implementation is not as composable, as using search ranges combined with a replacing mapping for matches,
3204
- * and might be suboptimal, if you are exporting the cleaned-up string to another buffer.
3205
- * The algorithm is suboptimal when this string is made exclusively of the pattern.
3206
- */
3207
- basic_string &replace_all(string_view pattern, string_view replacement) noexcept(false) {
3208
- if (!try_replace_all(pattern, replacement)) throw std::bad_alloc();
3209
- return *this;
3210
- }
3211
-
3212
- /**
3213
- * @brief Replaces ( @b in-place ) all occurrences of a given character set with the ::replacement string.
3214
- * Similar to `boost::algorithm::replace_all` and Python's `str.replace`.
3215
- *
3216
- * The implementation is not as composable, as using search ranges combined with a replacing mapping for matches,
3217
- * and might be suboptimal, if you are exporting the cleaned-up string to another buffer.
3218
- * The algorithm is suboptimal when this string is made exclusively of the pattern.
3219
- */
3220
- basic_string &replace_all(char_set pattern, string_view replacement) noexcept(false) {
3221
- if (!try_replace_all(pattern, replacement)) throw std::bad_alloc();
3222
- return *this;
3223
- }
3224
-
3225
- /**
3226
- * @brief Replaces ( @b in-place ) all occurrences of a given string with the ::replacement string.
3227
- * Similar to `boost::algorithm::replace_all` and Python's `str.replace`.
3228
- *
3229
- * The implementation is not as composable, as using search ranges combined with a replacing mapping for matches,
3230
- * and might be suboptimal, if you are exporting the cleaned-up string to another buffer.
3231
- * The algorithm is suboptimal when this string is made exclusively of the pattern.
3232
- */
3233
- bool try_replace_all(string_view pattern, string_view replacement) noexcept {
3234
- return try_replace_all_<string_view>(pattern, replacement);
3235
- }
3236
-
3237
- /**
3238
- * @brief Replaces ( @b in-place ) all occurrences of a given character set with the ::replacement string.
3239
- * Similar to `boost::algorithm::replace_all` and Python's `str.replace`.
3240
- *
3241
- * The implementation is not as composable, as using search ranges combined with a replacing mapping for matches,
3242
- * and might be suboptimal, if you are exporting the cleaned-up string to another buffer.
3243
- * The algorithm is suboptimal when this string is made exclusively of the pattern.
3244
- */
3245
- bool try_replace_all(char_set pattern, string_view replacement) noexcept {
3246
- return try_replace_all_<char_set>(pattern, replacement);
3247
- }
3248
-
3249
- private:
3250
- template <typename pattern_type>
3251
- bool try_replace_all_(pattern_type pattern, string_view replacement) noexcept;
3252
-
3253
- /**
3254
- * @brief Tries to prepare the string for a replacement of a given range with a new string.
3255
- * The allocation may occur, if the replacement is longer than the replaced range.
3256
- */
3257
- bool try_preparing_replacement(size_type offset, size_type length, size_type new_length) noexcept;
3258
- };
3259
-
3260
- using string = basic_string<char, std::allocator<char>>;
3261
-
3262
- static_assert(sizeof(string) == 4 * sizeof(void *), "String size must be 4 pointers.");
3263
-
3264
- namespace literals {
3265
- constexpr string_view operator""_sz(char const *str, std::size_t length) noexcept { return {str, length}; }
3266
- } // namespace literals
3267
-
3268
- template <typename char_type_, typename allocator_>
3269
- bool basic_string<char_type_, allocator_>::try_resize(size_type count, value_type character) noexcept {
3270
- sz_ptr_t string_start;
3271
- sz_size_t string_length;
3272
- sz_size_t string_space;
3273
- sz_bool_t string_is_external;
3274
- sz_string_unpack(&string_, &string_start, &string_length, &string_space, &string_is_external);
3275
-
3276
- // Allocate more space if needed.
3277
- if (count >= string_space) {
3278
- if (!_with_alloc(
3279
- [&](sz_alloc_type &alloc) { return sz_string_expand(&string_, SZ_SIZE_MAX, count, &alloc) != NULL; }))
3280
- return false;
3281
- sz_string_unpack(&string_, &string_start, &string_length, &string_space, &string_is_external);
3282
- }
3283
-
3284
- // Fill the trailing characters.
3285
- if (count > string_length) {
3286
- sz_fill(string_start + string_length, count - string_length, character);
3287
- string_start[count] = '\0';
3288
- // Knowing the layout of the string, we can perform this operation safely,
3289
- // even if its located on stack.
3290
- string_.external.length += count - string_length;
3291
- }
3292
- else { sz_string_erase(&string_, count, SZ_SIZE_MAX); }
3293
- return true;
3294
- }
3295
-
3296
- template <typename char_type_, typename allocator_>
3297
- bool basic_string<char_type_, allocator_>::try_assign(string_view other) noexcept {
3298
- // We can't just assign the other string state, as its start address may be somewhere else on the stack.
3299
- sz_ptr_t string_start;
3300
- sz_size_t string_length;
3301
- sz_string_range(&string_, &string_start, &string_length);
3302
-
3303
- if (string_length >= other.length()) {
3304
- other.copy(string_start, other.length());
3305
- sz_string_erase(&string_, other.length(), SZ_SIZE_MAX);
3306
- }
3307
- else {
3308
- if (!_with_alloc([&](sz_alloc_type &alloc) {
3309
- string_start = sz_string_expand(&string_, SZ_SIZE_MAX, other.length(), &alloc);
3310
- if (!string_start) return false;
3311
- other.copy(string_start, other.length());
3312
- return true;
3313
- }))
3314
- return false;
3315
- }
3316
- return true;
3317
- }
3318
-
3319
- template <typename char_type_, typename allocator_>
3320
- bool basic_string<char_type_, allocator_>::try_push_back(char_type c) noexcept {
3321
- return _with_alloc([&](sz_alloc_type &alloc) {
3322
- auto old_size = size();
3323
- sz_ptr_t start = sz_string_expand(&string_, SZ_SIZE_MAX, 1, &alloc);
3324
- if (!start) return false;
3325
- start[old_size] = c;
3326
- return true;
3327
- });
3328
- }
3329
-
3330
- template <typename char_type_, typename allocator_>
3331
- bool basic_string<char_type_, allocator_>::try_append(const_pointer str, size_type length) noexcept {
3332
- return _with_alloc([&](sz_alloc_type &alloc) {
3333
- auto old_size = size();
3334
- sz_ptr_t start = sz_string_expand(&string_, SZ_SIZE_MAX, length, &alloc);
3335
- if (!start) return false;
3336
- sz_copy(start + old_size, str, length);
3337
- return true;
3338
- });
3339
- }
3340
-
3341
- template <typename char_type_, typename allocator_>
3342
- template <typename pattern_type>
3343
- bool basic_string<char_type_, allocator_>::try_replace_all_(pattern_type pattern, string_view replacement) noexcept {
3344
- // Depending on the size of the pattern and the replacement, we may need to allocate more space.
3345
- // There are 3 cases to consider:
3346
- // 1. The pattern and the replacement are of the same length. Piece of cake!
3347
- // 2. The pattern is longer than the replacement. We need to compact the strings.
3348
- // 3. The pattern is shorter than the replacement. We may have to allocate more memory.
3349
- using matcher_type = typename std::conditional<std::is_same<pattern_type, char_set>::value,
3350
- matcher_find_first_of<string_view, pattern_type>,
3351
- matcher_find<string_view, exclude_overlaps_type>>::type;
3352
- matcher_type matcher({pattern});
3353
- string_view this_view = view();
3354
-
3355
- // 1. The pattern and the replacement are of the same length.
3356
- if (matcher.needle_length() == replacement.length()) {
3357
- using matches_type = range_matches<string_view, matcher_type>;
3358
- // Instead of iterating with `begin()` and `end()`, we could use the cheaper sentinel-based approach.
3359
- // for (string_view match : matches) { ... }
3360
- matches_type matches = matches_type(this_view, {pattern});
3361
- for (auto matches_iterator = matches.begin(); matches_iterator != end_sentinel_type {}; ++matches_iterator) {
3362
- replacement.copy(const_cast<pointer>((*matches_iterator).data()));
3363
- }
3364
- return true;
3365
- }
3366
-
3367
- // 2. The pattern is longer than the replacement. We need to compact the strings.
3368
- else if (matcher.needle_length() > replacement.length()) {
3369
- // Dealing with shorter replacements, we will avoid memory allocations, but we can also minimize the number
3370
- // of `memmove`-s, by keeping one more iterator, pointing to the end of the last compacted area.
3371
- // Having the split-ranges, however, we reuse their logic.
3372
- using splits_type = range_splits<string_view, matcher_type>;
3373
- splits_type splits = splits_type(this_view, {pattern});
3374
- auto matches_iterator = splits.begin();
3375
- auto compacted_end = (*matches_iterator).end();
3376
- if (compacted_end == end()) return true; // No matches.
3377
-
3378
- ++matches_iterator; // Skip the first match.
3379
- do {
3380
- string_view match_view = *matches_iterator;
3381
- replacement.copy(const_cast<pointer>(compacted_end));
3382
- compacted_end += replacement.length();
3383
- sz_move((sz_ptr_t)compacted_end, match_view.begin(), match_view.length());
3384
- compacted_end += match_view.length();
3385
- ++matches_iterator;
3386
- } while (matches_iterator != end_sentinel_type {});
3387
-
3388
- // Can't fail, so let's just return true :)
3389
- try_resize(compacted_end - begin());
3390
- return true;
3391
- }
3392
-
3393
- // 3. The pattern is shorter than the replacement. We may have to allocate more memory.
3394
- else {
3395
- using rmatcher_type = typename std::conditional<std::is_same<pattern_type, char_set>::value,
3396
- matcher_find_last_of<string_view, pattern_type>,
3397
- matcher_rfind<string_view, exclude_overlaps_type>>::type;
3398
- using rmatches_type = range_rmatches<string_view, rmatcher_type>;
3399
- rmatches_type rmatches = rmatches_type(this_view, {pattern});
3400
-
3401
- // It's cheaper to iterate through the whole string once, counting the number of matches,
3402
- // reserving memory once, than re-allocating and copying the string multiple times.
3403
- auto matches_count = rmatches.size();
3404
- if (matches_count == 0) return true; // No matches.
3405
-
3406
- // TODO: Resize without initializing the memory.
3407
- auto replacement_delta_length = replacement.length() - matcher.needle_length();
3408
- auto added_length = matches_count * replacement_delta_length;
3409
- auto old_length = size();
3410
- auto new_length = old_length + added_length;
3411
- if (!try_resize(new_length)) return false;
3412
- this_view = view().front(old_length);
3413
-
3414
- // Now iterate through splits similarly to the 2nd case, but in reverse order.
3415
- using rsplits_type = range_rsplits<string_view, rmatcher_type>;
3416
- rsplits_type splits = rsplits_type(this_view, {pattern});
3417
- auto splits_iterator = splits.begin();
3418
-
3419
- // Put the compacted pointer to the end of the new string, and walk left.
3420
- auto compacted_begin = this_view.data() + new_length;
3421
-
3422
- // By now we know that at least one match exists, which means the splits .
3423
- do {
3424
- string_view slice_view = *splits_iterator;
3425
- compacted_begin -= slice_view.length();
3426
- sz_move((sz_ptr_t)compacted_begin, slice_view.begin(), slice_view.length());
3427
- compacted_begin -= replacement.length();
3428
- replacement.copy(const_cast<pointer>(compacted_begin));
3429
- ++splits_iterator;
3430
- } while (!splits_iterator.is_last());
3431
-
3432
- return true;
3433
- }
3434
- }
3435
-
3436
- template <typename char_type_, typename allocator_>
3437
- template <typename first_type, typename second_type>
3438
- bool basic_string<char_type_, allocator_>::try_assign(concatenation<first_type, second_type> const &other) noexcept {
3439
- // We can't just assign the other string state, as its start address may be somewhere else on the stack.
3440
- sz_ptr_t string_start;
3441
- sz_size_t string_length;
3442
- sz_string_range(&string_, &string_start, &string_length);
3443
-
3444
- if (string_length >= other.length()) {
3445
- sz_string_erase(&string_, other.length(), SZ_SIZE_MAX);
3446
- other.copy(string_start, other.length());
3447
- }
3448
- else {
3449
- if (!_with_alloc([&](sz_alloc_type &alloc) {
3450
- string_start = sz_string_expand(&string_, SZ_SIZE_MAX, other.length(), &alloc);
3451
- if (!string_start) return false;
3452
- other.copy(string_start, other.length());
3453
- return true;
3454
- }))
3455
- return false;
3456
- }
3457
- return true;
3458
- }
3459
-
3460
- template <typename char_type_, typename allocator_>
3461
- bool basic_string<char_type_, allocator_>::try_preparing_replacement(size_type offset, size_type length,
3462
- size_type replacement_length) noexcept {
3463
- // There are three cases:
3464
- // 1. The replacement is the same length as the replaced range.
3465
- // 2. The replacement is shorter than the replaced range.
3466
- // 3. The replacement is longer than the replaced range. An allocation may occur.
3467
- assert(offset + length <= size());
3468
-
3469
- // 1. The replacement is the same length as the replaced range.
3470
- if (replacement_length == length) { return true; }
3471
-
3472
- // 2. The replacement is shorter than the replaced range.
3473
- else if (replacement_length < length) {
3474
- sz_string_erase(&string_, offset + replacement_length, length - replacement_length);
3475
- return true;
3476
- }
3477
- // 3. The replacement is longer than the replaced range. An allocation may occur.
3478
- else {
3479
- return _with_alloc([&](sz_alloc_type &alloc) {
3480
- return sz_string_expand(&string_, offset + length, replacement_length - length, &alloc);
3481
- });
3482
- }
3483
- }
3484
-
3485
- /** @brief SFINAE-type used to infer the resulting type of concatenating multiple string together. */
3486
- template <typename... args_types>
3487
- struct concatenation_result {};
3488
-
3489
- template <typename first_type, typename second_type>
3490
- struct concatenation_result<first_type, second_type> {
3491
- using type = concatenation<first_type, second_type>;
3492
- };
3493
-
3494
- template <typename first_type, typename... following_types>
3495
- struct concatenation_result<first_type, following_types...> {
3496
- using type = concatenation<first_type, typename concatenation_result<following_types...>::type>;
3497
- };
3498
-
3499
- /**
3500
- * @brief Concatenates two strings into a template expression.
3501
- * @see `concatenation` class for more details.
3502
- */
3503
- template <typename first_type, typename second_type>
3504
- concatenation<first_type, second_type> concatenate(first_type &&first, second_type &&second) noexcept(false) {
3505
- return {first, second};
3506
- }
3507
-
3508
- /**
3509
- * @brief Concatenates two or more strings into a template expression.
3510
- * @see `concatenation` class for more details.
3511
- */
3512
- template <typename first_type, typename second_type, typename... following_types>
3513
- typename concatenation_result<first_type, second_type, following_types...>::type concatenate(
3514
- first_type &&first, second_type &&second, following_types &&...following) noexcept(false) {
3515
- // Fold expression like the one below would result in faster compile times,
3516
- // but would incur the penalty of additional `if`-statements in every `append` call.
3517
- // Moreover, those are only supported in C++17 and later.
3518
- // std::size_t total_size = (strings.size() + ... + 0);
3519
- // std::string result;
3520
- // result.reserve(total_size);
3521
- // (result.append(strings), ...);
3522
- return ashvardanian::stringzilla::concatenate(
3523
- std::forward<first_type>(first),
3524
- ashvardanian::stringzilla::concatenate(std::forward<second_type>(second),
3525
- std::forward<following_types>(following)...));
3526
- }
3527
-
3528
- /**
3529
- * @brief Calculates the Hamming edit distance in @b bytes between two strings.
3530
- * @see sz_edit_distance
3531
- */
3532
- template <typename char_type_>
3533
- std::size_t hamming_distance(basic_string_slice<char_type_> const &a, basic_string_slice<char_type_> const &b,
3534
- std::size_t bound = 0) noexcept {
3535
- return sz_hamming_distance(a.data(), a.size(), b.data(), b.size(), bound);
3536
- }
3537
-
3538
- /**
3539
- * @brief Calculates the Hamming edit distance in @b bytes between two strings.
3540
- * @see sz_edit_distance
3541
- */
3542
- template <typename char_type_, typename allocator_type_ = std::allocator<typename std::remove_const<char_type_>::type>>
3543
- std::size_t hamming_distance(basic_string<char_type_, allocator_type_> const &a,
3544
- basic_string<char_type_, allocator_type_> const &b, std::size_t bound = 0) noexcept {
3545
- return ashvardanian::stringzilla::hamming_distance(a.view(), b.view(), bound);
3546
- }
3547
-
3548
- /**
3549
- * @brief Calculates the Hamming edit distance in @b unicode codepoints between two strings.
3550
- * @see sz_hamming_distance_utf8
3551
- */
3552
- template <typename char_type_>
3553
- std::size_t hamming_distance_utf8(basic_string_slice<char_type_> const &a, basic_string_slice<char_type_> const &b,
3554
- std::size_t bound = 0) noexcept {
3555
- return sz_hamming_distance_utf8(a.data(), a.size(), b.data(), b.size(), bound);
3556
- }
3557
-
3558
- /**
3559
- * @brief Calculates the Hamming edit distance in @b unicode codepoints between two strings.
3560
- * @see sz_edit_distance
3561
- */
3562
- template <typename char_type_, typename allocator_type_ = std::allocator<typename std::remove_const<char_type_>::type>>
3563
- std::size_t hamming_distance_utf8(basic_string<char_type_, allocator_type_> const &a,
3564
- basic_string<char_type_, allocator_type_> const &b, std::size_t bound = 0) noexcept {
3565
- return ashvardanian::stringzilla::hamming_distance_utf8(a.view(), b.view(), bound);
3566
- }
3567
-
3568
- /**
3569
- * @brief Calculates the Levenshtein edit distance in @b bytes between two strings.
3570
- * @see sz_edit_distance
3571
- */
3572
- template <typename char_type_, typename allocator_type_ = std::allocator<typename std::remove_const<char_type_>::type>>
3573
- std::size_t edit_distance(basic_string_slice<char_type_> const &a, basic_string_slice<char_type_> const &b,
3574
- std::size_t bound = 0, allocator_type_ &&allocator = allocator_type_ {}) noexcept(false) {
3575
- std::size_t result;
3576
- if (!_with_alloc(allocator, [&](sz_memory_allocator_t &alloc) {
3577
- result = sz_edit_distance(a.data(), a.size(), b.data(), b.size(), bound, &alloc);
3578
- return result != SZ_SIZE_MAX;
3579
- }))
3580
- throw std::bad_alloc();
3581
- return result;
3582
- }
3583
-
3584
- /**
3585
- * @brief Calculates the Levenshtein edit distance in @b bytes between two strings.
3586
- * @see sz_edit_distance
3587
- */
3588
- template <typename char_type_, typename allocator_type_ = std::allocator<char_type_>>
3589
- std::size_t edit_distance(basic_string<char_type_, allocator_type_> const &a,
3590
- basic_string<char_type_, allocator_type_> const &b, std::size_t bound = 0) noexcept(false) {
3591
- return ashvardanian::stringzilla::edit_distance(a.view(), b.view(), bound, a.get_allocator());
3592
- }
3593
-
3594
- /**
3595
- * @brief Calculates the Levenshtein edit distance in @b unicode codepoints between two strings.
3596
- * @see sz_edit_distance_utf8
3597
- */
3598
- template <typename char_type_, typename allocator_type_ = std::allocator<typename std::remove_const<char_type_>::type>>
3599
- std::size_t edit_distance_utf8(basic_string_slice<char_type_> const &a, basic_string_slice<char_type_> const &b,
3600
- std::size_t bound = 0,
3601
- allocator_type_ &&allocator = allocator_type_ {}) noexcept(false) {
3602
- std::size_t result;
3603
- if (!_with_alloc(allocator, [&](sz_memory_allocator_t &alloc) {
3604
- result = sz_edit_distance_utf8(a.data(), a.size(), b.data(), b.size(), bound, &alloc);
3605
- return result != SZ_SIZE_MAX;
3606
- }))
3607
- throw std::bad_alloc();
3608
- return result;
3609
- }
3610
-
3611
- /**
3612
- * @brief Calculates the Levenshtein edit distance in @b unicode codepoints between two strings.
3613
- * @see sz_edit_distance_utf8
3614
- */
3615
- template <typename char_type_, typename allocator_type_ = std::allocator<char_type_>>
3616
- std::size_t edit_distance_utf8(basic_string<char_type_, allocator_type_> const &a,
3617
- basic_string<char_type_, allocator_type_> const &b,
3618
- std::size_t bound = 0) noexcept(false) {
3619
- return ashvardanian::stringzilla::edit_distance_utf8(a.view(), b.view(), bound, a.get_allocator());
3620
- }
3621
-
3622
- /**
3623
- * @brief Calculates the Needleman-Wunsch alignment score between two strings.
3624
- * @see sz_alignment_score
3625
- */
3626
- template <typename char_type_, typename allocator_type_ = std::allocator<typename std::remove_const<char_type_>::type>>
3627
- std::ptrdiff_t alignment_score(basic_string_slice<char_type_> const &a, basic_string_slice<char_type_> const &b,
3628
- std::int8_t const (&subs)[256][256], std::int8_t gap = -1,
3629
- allocator_type_ &&allocator = allocator_type_ {}) noexcept(false) {
3630
-
3631
- static_assert(sizeof(sz_error_cost_t) == sizeof(std::int8_t), "sz_error_cost_t must be 8-bit.");
3632
- static_assert(std::is_signed<sz_error_cost_t>() == std::is_signed<std::int8_t>(),
3633
- "sz_error_cost_t must be signed.");
3634
-
3635
- std::ptrdiff_t result;
3636
- if (!_with_alloc(allocator, [&](sz_memory_allocator_t &alloc) {
3637
- result = sz_alignment_score(a.data(), a.size(), b.data(), b.size(), &subs[0][0], gap, &alloc);
3638
- return result != SZ_SSIZE_MAX;
3639
- }))
3640
- throw std::bad_alloc();
3641
- return result;
3642
- }
3643
-
3644
- /**
3645
- * @brief Calculates the Needleman-Wunsch alignment score between two strings.
3646
- * @see sz_alignment_score
3647
- */
3648
- template <typename char_type_, typename allocator_type_ = std::allocator<char_type_>>
3649
- std::ptrdiff_t alignment_score(basic_string<char_type_, allocator_type_> const &a,
3650
- basic_string<char_type_, allocator_type_> const &b, //
3651
- std::int8_t const (&subs)[256][256], std::int8_t gap = -1) noexcept(false) {
3652
- return ashvardanian::stringzilla::alignment_score(a.view(), b.view(), subs, gap, a.get_allocator());
3653
- }
3654
-
3655
- /**
3656
- * @brief Overwrites the string slice with random characters from the given alphabet using the random generator.
3657
- *
3658
- * @param string The string to overwrite.
3659
- * @param generator A random generator function object that returns a random number in the range [0, 2^64).
3660
- * @param alphabet A string of characters to choose from.
3661
- */
3662
- template <typename char_type_, typename generator_type_>
3663
- void randomize(basic_string_slice<char_type_> string, generator_type_ &generator,
3664
- string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept {
3665
- static_assert(!std::is_const<char_type_>::value, "The string must be mutable.");
3666
- sz_random_generator_t generator_callback = &_call_random_generator<generator_type_>;
3667
- sz_generate(alphabet.data(), alphabet.size(), string.data(), string.size(), generator_callback, &generator);
3668
- }
3669
-
3670
- /**
3671
- * @brief Overwrites the string slice with random characters from the given alphabet
3672
- * using `std::rand` as the random generator.
3673
- *
3674
- * @param string The string to overwrite.
3675
- * @param alphabet A string of characters to choose from.
3676
- */
3677
- template <typename char_type_>
3678
- void randomize(basic_string_slice<char_type_> string, string_view alphabet = "abcdefghijklmnopqrstuvwxyz") noexcept {
3679
- randomize(string, &std::rand, alphabet);
3680
- }
3681
-
3682
- using sorted_idx_t = sz_sorted_idx_t;
3683
-
3684
- /**
3685
- * @brief Internal data-structure used to forward the arguments to the `sz_sort` function.
3686
- * @see sorted_order
3687
- */
3688
- template <typename objects_type_, typename string_extractor_>
3689
- struct _sequence_args {
3690
- objects_type_ const *begin;
3691
- std::size_t count;
3692
- sorted_idx_t *order;
3693
- string_extractor_ extractor;
3694
- };
3695
-
3696
- template <typename objects_type_, typename string_extractor_>
3697
- sz_cptr_t _call_sequence_member_start(struct sz_sequence_t const *sequence, sz_size_t i) {
3698
- using handle_type = _sequence_args<objects_type_, string_extractor_>;
3699
- handle_type const *args = reinterpret_cast<handle_type const *>(sequence->handle);
3700
- string_view member = args->extractor(args->begin[i]);
3701
- return member.data();
3702
- }
3703
-
3704
- template <typename objects_type_, typename string_extractor_>
3705
- sz_size_t _call_sequence_member_length(struct sz_sequence_t const *sequence, sz_size_t i) {
3706
- using handle_type = _sequence_args<objects_type_, string_extractor_>;
3707
- handle_type const *args = reinterpret_cast<handle_type const *>(sequence->handle);
3708
- string_view member = args->extractor(args->begin[i]);
3709
- return static_cast<sz_size_t>(member.size());
3710
- }
3711
-
3712
- /**
3713
- * @brief Computes the permutation of an array, that would lead to sorted order.
3714
- * The elements of the array must be convertible to a `string_view` with the given extractor.
3715
- * Unlike the `sz_sort` C interface, overwrites the output array.
3716
- *
3717
- * @param[in] begin The pointer to the first element of the array.
3718
- * @param[in] end The pointer to the element after the last element of the array.
3719
- * @param[out] order The pointer to the output array of indices, that will be populated with the permutation.
3720
- * @param[in] extractor The function object that extracts the string from the object.
3721
- *
3722
- * @see sz_sort
3723
- */
3724
- template <typename objects_type_, typename string_extractor_>
3725
- void sorted_order(objects_type_ const *begin, objects_type_ const *end, sorted_idx_t *order,
3726
- string_extractor_ &&extractor) noexcept {
3727
-
3728
- // Pack the arguments into a single structure to reference it from the callback.
3729
- _sequence_args<objects_type_, string_extractor_> args = {begin, static_cast<std::size_t>(end - begin), order,
3730
- std::forward<string_extractor_>(extractor)};
3731
- // Populate the array with `iota`-style order.
3732
- for (std::size_t i = 0; i != args.count; ++i) order[i] = static_cast<sorted_idx_t>(i);
3733
-
3734
- sz_sequence_t array;
3735
- array.order = reinterpret_cast<sorted_idx_t *>(order);
3736
- array.count = args.count;
3737
- array.handle = &args;
3738
- array.get_start = _call_sequence_member_start<objects_type_, string_extractor_>;
3739
- array.get_length = _call_sequence_member_length<objects_type_, string_extractor_>;
3740
- sz_sort(&array);
3741
- }
3742
-
3743
- #if !SZ_AVOID_STL
3744
-
3745
- /**
3746
- * @brief Computes the Rabin-Karp-like rolling binary fingerprint of a string.
3747
- * @see sz_hashes
3748
- */
3749
- template <std::size_t bitset_bits_, typename char_type_>
3750
- void hashes_fingerprint(basic_string_slice<char_type_> const &str, std::size_t window_length,
3751
- std::bitset<bitset_bits_> &fingerprint) noexcept {
3752
- constexpr std::size_t fingerprint_bytes = sizeof(std::bitset<bitset_bits_>);
3753
- return sz_hashes_fingerprint(str.data(), str.size(), window_length, (sz_ptr_t)&fingerprint, fingerprint_bytes);
3754
- }
3755
-
3756
- /**
3757
- * @brief Computes the Rabin-Karp-like rolling binary fingerprint of a string.
3758
- * @see sz_hashes
3759
- */
3760
- template <std::size_t bitset_bits_, typename char_type_>
3761
- std::bitset<bitset_bits_> hashes_fingerprint(basic_string_slice<char_type_> const &str,
3762
- std::size_t window_length) noexcept {
3763
- std::bitset<bitset_bits_> fingerprint;
3764
- ashvardanian::stringzilla::hashes_fingerprint(str, window_length, fingerprint);
3765
- return fingerprint;
3766
- }
3767
-
3768
- /**
3769
- * @brief Computes the Rabin-Karp-like rolling binary fingerprint of a string.
3770
- * @see sz_hashes
3771
- */
3772
- template <std::size_t bitset_bits_, typename char_type_>
3773
- std::bitset<bitset_bits_> hashes_fingerprint(basic_string<char_type_> const &str, std::size_t window_length) noexcept {
3774
- return ashvardanian::stringzilla::hashes_fingerprint<bitset_bits_>(str.view(), window_length);
3775
- }
3776
-
3777
- /**
3778
- * @brief Computes the permutation of an array, that would lead to sorted order.
3779
- * @return The array of indices, that will be populated with the permutation.
3780
- * @throw `std::bad_alloc` if the allocation fails.
3781
- */
3782
- template <typename objects_type_, typename string_extractor_>
3783
- std::vector<sorted_idx_t> sorted_order(objects_type_ const *begin, objects_type_ const *end,
3784
- string_extractor_ &&extractor) noexcept(false) {
3785
- std::vector<sorted_idx_t> order(end - begin);
3786
- sorted_order(begin, end, order.data(), std::forward<string_extractor_>(extractor));
3787
- return order;
3788
- }
3789
-
3790
- /**
3791
- * @brief Computes the permutation of an array, that would lead to sorted order.
3792
- * @return The array of indices, that will be populated with the permutation.
3793
- * @throw `std::bad_alloc` if the allocation fails.
3794
- */
3795
- template <typename string_like_type_>
3796
- std::vector<sorted_idx_t> sorted_order(string_like_type_ const *begin, string_like_type_ const *end) noexcept(false) {
3797
- static_assert(std::is_convertible<string_like_type_, string_view>::value,
3798
- "The type must be convertible to string_view.");
3799
- return sorted_order(begin, end, [](string_like_type_ const &s) -> string_view { return s; });
3800
- }
3801
-
3802
- /**
3803
- * @brief Computes the permutation of an array, that would lead to sorted order.
3804
- * @return The array of indices, that will be populated with the permutation.
3805
- * @throw `std::bad_alloc` if the allocation fails.
3806
- */
3807
- template <typename string_like_type_>
3808
- std::vector<sorted_idx_t> sorted_order(std::vector<string_like_type_> const &array) noexcept(false) {
3809
- static_assert(std::is_convertible<string_like_type_, string_view>::value,
3810
- "The type must be convertible to string_view.");
3811
- return sorted_order(array.data(), array.data() + array.size(),
3812
- [](string_like_type_ const &s) -> string_view { return s; });
3813
- }
3814
-
3815
- #endif
3816
-
3817
- } // namespace stringzilla
3818
- } // namespace ashvardanian
3819
-
3820
- #pragma region STL Specializations
3821
-
3822
- namespace std {
3823
-
3824
- template <>
3825
- struct hash<ashvardanian::stringzilla::string_view> {
3826
- size_t operator()(ashvardanian::stringzilla::string_view str) const noexcept { return str.hash(); }
3827
- };
3828
-
3829
- template <>
3830
- struct hash<ashvardanian::stringzilla::string> {
3831
- size_t operator()(ashvardanian::stringzilla::string const &str) const noexcept { return str.hash(); }
3832
- };
3833
-
3834
- } // namespace std
3835
-
3836
- #pragma endregion
3837
-
3838
- #endif // STRINGZILLA_HPP_