LibSWOC++ 1.5.14
Solid Wall of C++
Loading...
Searching...
No Matches
TextView.h
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright Apache Software Foundation 2019
13
14#pragma once
15#include <bitset>
16#include <cstdint>
17#include <iosfwd>
18#include <memory.h>
19#include <string>
20#include <string_view>
21#include <limits>
22#include <type_traits>
23
24#include "swoc/swoc_version.h"
26
27// For no apparent reason, g++ 11 complains about array bound violations with either suffix_at or
28// assign, the error message is too vague for me to be sure - it doesn't even provide the location of
29// the method invocation. I've been using g++ 12 for development and I don't see that error.
30#if __GNUC__ == 11
31#pragma GCC diagnostic push
32#pragma GCC diagnostic ignored "-Warray-bounds"
33#endif
34
35namespace swoc { inline namespace SWOC_VERSION_NS {
36
37class TextView;
38
42class CharSet {
43 using self_type = CharSet;
44
45public:
52 constexpr CharSet(TextView const &chars);
53
59 bool operator()(unsigned char c) const;
60
66 bool operator()(char c) const;
67
68protected:
69 std::bitset<std::numeric_limits<unsigned char>::max() + 1> _chars;
70};
71
94class TextView : public std::string_view {
95 using self_type = TextView;
96 using super_type = std::string_view;
97
98public:
100 constexpr TextView() noexcept = default;
101
105 constexpr TextView(super_type const &that) noexcept;
106
115 constexpr TextView(char const *ptr, size_t n) noexcept;
116
122 constexpr TextView(char const *ptr, unsigned n) noexcept;
123
132 constexpr TextView(char const *ptr, ssize_t n) noexcept;
133
142 constexpr TextView(char const *ptr, int n) noexcept;
143
166 template <typename T>
167 explicit TextView(
168 T first,
169 std::enable_if_t<!std::is_array_v<T> && std::is_pointer_v<T> && std::is_convertible_v<T, char const *>, T> last) noexcept
170 : super_type(first, last - first) {}
171
180 template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> &&
181 std::is_convertible_v<decltype(std::declval<C>().size()), size_t>,
182 void>>
183 constexpr TextView(C const &c);
184
195 template <size_t N> constexpr TextView(const char (&s)[N]) noexcept;
196
205 TextView(char *&src) : super_type(src, src ? strlen(src) : 0) {}
206
215 TextView(char const *&src) : super_type(src, src ? strlen(src) : 0) {}
216
220 constexpr TextView(std::nullptr_t) noexcept;
221
225 TextView(std::string const &str) noexcept;
226
228 self_type &operator=(super_type const &that);
229
232 template <size_t N> self_type &operator=(const char (&s)[N]);
233
235 self_type &operator=(char *&s);
237 self_type &operator=(char const *&s);
238
240 self_type &operator=(const std::string &s);
241
249 self_type &assign(char *&c_str);
250
258 self_type &assign(char const *&c_str);
259
268 self_type &assign(char const *ptr, size_t n);
269
276 self_type &assign(char const *b, char const *e);
277
279 self_type &assign(std::string const &s);
280
291 template <size_t N> self_type &assign(const char (&s)[N]) noexcept;
292
301 template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), char const *> &&
302 std::is_convertible_v<decltype(std::declval<C>().size()), size_t>,
303 void>>
304 constexpr self_type &
305 assign(C const &c) {
306 return this->assign(c.data(), c.size());
307 }
308
317 constexpr char operator*() const;
318
323 self_type &operator++();
324
329 self_type operator++(int);
330
336 self_type &operator+=(size_t n);
337
340 constexpr bool operator!() const noexcept;
341
344 explicit constexpr operator bool() const noexcept;
345
347 self_type &clear();
348
350 template <typename F> size_t find_if(F const &pred) const;
352 template <typename F> size_t rfind_if(F const &pred) const;
353
358 self_type &ltrim(char c);
359
364 self_type &ltrim(CharSet const &delimiters);
365
370 self_type &ltrim(std::string_view const &delimiters);
371
378 self_type &ltrim(const char *delimiters);
379
384 template <typename F> self_type &ltrim_if(F const &pred);
385
390 self_type &rtrim(char c);
391
396 self_type &rtrim(CharSet const &delimiters);
397
401 self_type &rtrim(std::string_view const &delimiters);
402
409 template <typename F> self_type &rtrim_if(F const &pred);
410
415 self_type &trim(char c);
416
420 self_type &trim(CharSet const &delimiters);
421
425 self_type &trim(std::string_view const &delimiters);
426
431 self_type &trim(const char *delimiters);
432
437 template <typename F> self_type &trim_if(F const &pred);
438
444 constexpr self_type prefix(size_t n) const noexcept;
445
452 self_type prefix_at(char c) const;
453
463 self_type prefix_at(std::string_view const &delimiters) const;
464
478 template <typename F> self_type prefix_if(F const &pred) const;
479
485 self_type &remove_prefix(size_t n);
486
492 self_type &remove_suffix(size_t n);
493
501 self_type &remove_prefix_at(char c);
502
510 self_type &remove_prefix_at(std::string_view const &delimiters);
511
520 template <typename F> self_type &remove_prefix_if(F const &pred);
521
534 self_type split_prefix(size_t n);
535
547 self_type split_prefix_at(char c);
548
560 self_type split_prefix_at(std::string_view const &delimiters);
561
575 template <typename F> self_type split_prefix_if(F const &pred);
576
589 self_type take_prefix(size_t n);
590
603 self_type take_prefix_at(char c);
604
617 self_type take_prefix_at(std::string_view const &delimiters);
618
633 template <typename F> self_type take_prefix_if(F const &pred);
634
647 template <typename F> self_type clip_prefix_of(F const &pred);
648
654 constexpr self_type suffix(size_t n) const noexcept;
655
662 self_type suffix_at(char c) const;
663
673 self_type suffix_at(std::string_view const &delimiters) const;
674
688 template <typename F> self_type suffix_if(F const &pred) const;
689
698 self_type &remove_suffix_at(char c);
699
707 self_type &remove_suffix_at(std::string_view const &delimiters);
708
717 template <typename F> self_type &remove_suffix_if(F const &pred);
718
731 self_type split_suffix(size_t n);
732
744 self_type split_suffix_at(char c);
745
757 self_type split_suffix_at(std::string_view const &delimiters);
758
773 template <typename F> self_type split_suffix_if(F const &pred);
774
784 self_type take_suffix(size_t n);
785
795 self_type take_suffix_at(char c);
796
806 self_type take_suffix_at(std::string_view const &delimiters);
807
818 template <typename F> self_type take_suffix_if(F const &pred);
819
832 template <typename F> self_type clip_suffix_of(F const &pred);
833
846 constexpr self_type substr(size_type pos = 0, size_type count = npos) const noexcept;
847
854 bool starts_with(std::string_view const &prefix) const noexcept;
855
862 bool starts_with(char const *prefix) const;
863
870 bool starts_with(char c) const noexcept;
871
878 bool starts_with_nocase(std::string_view const &prefix) const noexcept;
879
886 bool starts_with_nocase(char const *prefix) const;
887
894 bool starts_with_nocase(char c) const noexcept;
895
902 bool ends_with(std::string_view const &suffix) const noexcept;
903
910 bool ends_with(char const *suffix) const;
911
918 bool ends_with(char c) const noexcept;
919
926 bool ends_with_nocase(std::string_view const &suffix) const noexcept;
927
934 bool ends_with_nocase(char const *suffix) const;
935
942 bool ends_with_nocase(char c) const noexcept;
943
944 // Functors for using this class in STL containers.
946 struct LessThan {
948 bool
949 operator()(self_type const &lhs, self_type const &rhs) const noexcept {
950 return -1 == strcmp(lhs, rhs);
951 }
952 };
953
957 bool
958 operator()(self_type const &lhs, self_type const &rhs) const noexcept {
959 return -1 == strcasecmp(lhs, rhs);
960 }
961 };
962
966 bool
967 operator()(self_type const &lhs, self_type const &rhs) const noexcept {
968 return lhs.size() == rhs.size() && 0 == strcasecmp(lhs, rhs);
969 }
970 };
971
981 constexpr value_type const *data() const noexcept;
982
990 constexpr value_type const *data_end() const noexcept;
991
997 template <typename Stream> Stream &stream_write(Stream &os, const TextView &b) const;
998
1000 // These methods are all overloads of other methods, defined in order to make the API more
1001 // convenient to use. Mostly these overload @c int for @c size_t so naked numbers work as expected.
1002 constexpr self_type prefix(int n) const noexcept;
1003 self_type take_suffix(int n);
1004 self_type split_prefix(int n);
1005 constexpr self_type suffix(int n) const noexcept;
1006 self_type split_suffix(int n);
1008
1009protected:
1011 static void init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set);
1012};
1013
1016extern const int8_t svtoi_convert[256];
1017
1028intmax_t svtoi(TextView src, TextView *parsed = nullptr, int base = 0);
1029
1040uintmax_t svtou(TextView src, TextView *parsed = nullptr, int base = 0);
1041
1057template <int RADIX>
1058uintmax_t
1060 static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36");
1061 static constexpr auto MAX = std::numeric_limits<uintmax_t>::max();
1062 static constexpr auto OVERFLOW_LIMIT = MAX / RADIX;
1063 uintmax_t zret = 0;
1064 uintmax_t v;
1065 while (src.size() && ((v = swoc::svtoi_convert[uint8_t(*src)]) < RADIX)) {
1066 // Tweaked for performance - need to check range after @a RADIX multiply.
1067 ++src; // Update view iff the character is parsed.
1068 if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) {
1069 zret += v;
1070 } else {
1071 zret = MAX; // clamp to max - once set will always hit this case for subsequent input.
1072 }
1073 }
1074 return zret;
1075}
1076
1079template <int N>
1080uintmax_t
1082 return svto_radix<N>(src);
1083}
1084
1098double svtod(TextView text, TextView *parsed = nullptr);
1099// ----------------------------------------------------------
1100// Inline implementations.
1101// Note: Why, you may ask, do I use @c TextView::self_type for return type instead of the
1102// simpler plain @c TextView ? Because otherwise Doxygen can't match up the declaration and
1103// definition and the reference documentation is messed up. Sigh.
1104
1105inline constexpr CharSet::CharSet(TextView const &chars) {
1106 for (auto c : chars) {
1107 _chars[uint8_t(c)] = true;
1108 }
1109}
1110
1111inline bool
1112CharSet::operator()(unsigned char c) const {
1113 return _chars[c];
1114}
1115
1116inline bool
1117CharSet::operator()(char c) const {
1118 return _chars[uint8_t(c)];
1119}
1120
1121// === TextView Implementation ===
1123// Doxygen doesn't match these up well due to various type and template issues.
1124// @internal If there is more than one overload for numeric types, it's easy to get ambiguity. The only
1125// fix, unfortunately, is lots of overloads to cover the ambiguous cases.
1126inline constexpr TextView::TextView(const char *ptr, size_t n) noexcept
1127 : super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n) {}
1128inline constexpr TextView::TextView(const char *ptr, unsigned n) noexcept : super_type(ptr, size_t(n)) {}
1129inline constexpr TextView::TextView(const char *ptr, ssize_t n) noexcept
1130 : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {}
1131inline constexpr TextView::TextView(const char *ptr, int n) noexcept
1132 : super_type(ptr, n < 0 ? (ptr ? ::strlen(ptr) : 0) : size_t(n)) {}
1133inline constexpr TextView::TextView(std::nullptr_t) noexcept : super_type(nullptr, 0) {}
1134inline TextView::TextView(std::string const &str) noexcept : super_type(str) {}
1135inline constexpr TextView::TextView(super_type const &that) noexcept : super_type(that) {}
1136template <size_t N> constexpr TextView::TextView(const char (&s)[N]) noexcept : super_type(s, s[N - 1] ? N : N - 1) {}
1137template <typename C, typename> constexpr TextView::TextView(C const &c) : super_type(c.data(), c.size()) {}
1138
1139inline void
1140TextView::init_delimiter_set(std::string_view const &delimiters, std::bitset<256> &set) {
1141 set.reset();
1142 for (char c : delimiters)
1143 set[static_cast<uint8_t>(c)] = true;
1144}
1145
1146inline auto
1147TextView::clear() -> self_type & {
1148 new (this) self_type();
1149 return *this;
1150}
1151
1152inline constexpr char
1153TextView::operator*() const {
1154 return this->empty() ? char(0) : *(this->data());
1155}
1156
1157inline constexpr bool
1158TextView::operator!() const noexcept {
1159 return this->empty();
1160}
1161
1162inline constexpr TextView::operator bool() const noexcept {
1163 return !this->empty();
1164}
1165
1166inline auto
1167TextView::operator++() -> self_type & {
1168 this->remove_prefix(1);
1169 return *this;
1170}
1171
1172inline auto
1173TextView::operator++(int) -> self_type {
1174 self_type zret{*this};
1175 this->remove_prefix(1);
1176 return zret;
1177}
1178
1179inline auto
1180TextView::operator+=(size_t n) -> self_type & {
1181 this->remove_prefix(n);
1182 return *this;
1183}
1184
1185template <size_t N>
1186inline auto
1187TextView::operator=(const char (&s)[N]) -> self_type & {
1188 return *this = self_type{s, s[N - 1] ? N : N - 1};
1189}
1190
1191inline auto
1192TextView::operator=(super_type const &that) -> self_type & {
1193 this->super_type::operator=(that);
1194 return *this;
1195}
1196
1197inline auto
1198TextView::operator=(char *&s) -> self_type & {
1199 this->super_type::operator=(s);
1200 return *this;
1201}
1202
1203inline auto
1204TextView::operator=(char const *&s) -> self_type & {
1205 this->super_type::operator=(s);
1206 return *this;
1207}
1208
1209inline auto
1210TextView::operator=(const std::string &s) -> self_type & {
1211 this->super_type::operator=(s);
1212 return *this;
1213}
1214
1215inline auto
1216TextView::assign(char *&c_str) -> self_type & {
1217 return this->assign(c_str, strlen(c_str));
1218}
1219
1220inline auto
1221TextView::assign(char const *&c_str) -> self_type & {
1222 return this->assign(c_str, strlen(c_str));
1223}
1224
1225inline auto
1226TextView::assign(const std::string &s) -> self_type & {
1227 *this = super_type(s);
1228 return *this;
1229}
1230
1231inline TextView &
1232TextView::assign(char const *ptr, size_t n) {
1233 *this = super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n);
1234 return *this;
1235}
1236
1237inline TextView &
1238TextView::assign(char const *b, char const *e) {
1239 *this = super_type(b, e - b);
1240 return *this;
1241}
1242
1243template <size_t N>
1244inline auto
1245TextView::assign(char const (&s)[N]) noexcept -> self_type & {
1246 return *this = self_type{s, s[N - 1] ? N : N - 1};
1247}
1248
1249inline constexpr auto
1250TextView::prefix(size_t n) const noexcept -> self_type {
1251 return {this->data(), std::min(n, this->size())};
1252}
1253
1254inline constexpr TextView
1255TextView::prefix(int n) const noexcept {
1256 return {this->data(), std::min<size_t>(n, this->size())};
1257}
1258
1259inline auto
1260TextView::prefix_at(char c) const -> self_type {
1261 self_type zret; // default to empty return.
1262 if (auto n = this->find(c); n != npos) {
1263 zret.assign(this->data(), n);
1264 }
1265 return zret;
1266}
1267
1268inline TextView
1269TextView::prefix_at(std::string_view const &delimiters) const {
1270 self_type zret; // default to empty return.
1271 if (auto n = this->find_first_of(delimiters); n != npos) {
1272 zret.assign(this->data(), n);
1273 }
1274 return zret;
1275}
1276
1277template <typename F>
1278auto
1279TextView::prefix_if(F const &pred) const -> self_type {
1280 self_type zret; // default to empty return.
1281 if (auto n = this->find_if(pred); n != npos) {
1282 zret.assign(this->data(), n);
1283 }
1284 return zret;
1285}
1286
1287inline auto
1288TextView::remove_prefix(size_t n) -> self_type & {
1289 this->super_type::remove_prefix(std::min(n, this->size()));
1290 return *this;
1291}
1292
1293inline TextView &
1295 if (auto n = this->find(c); n != npos) {
1296 this->super_type::remove_prefix(n + 1);
1297 }
1298 return *this;
1299}
1300
1301inline auto
1302TextView::remove_prefix_at(std::string_view const &delimiters) -> self_type & {
1303 if (auto n = this->find_first_of(delimiters); n != npos) {
1304 this->super_type::remove_prefix(n + 1);
1305 }
1306 return *this;
1307}
1308
1309template <typename F>
1310auto
1311TextView::remove_prefix_if(F const &pred) -> self_type & {
1312 if (auto n = this->find_if(pred); n != npos) {
1313 this->super_type::remove_prefix(n + 1);
1314 }
1315 return *this;
1316}
1317
1318inline TextView
1319TextView::split_prefix(size_t n) {
1320 self_type zret; // default to empty return.
1321 if (n < this->size()) {
1322 zret = this->prefix(n);
1323 this->remove_prefix(std::min(n + 1, this->size()));
1324 }
1325 return zret;
1326}
1327
1328inline TextView
1330 return this->split_prefix(size_t(n));
1331}
1332
1333inline TextView
1335 return this->split_prefix(this->find(c));
1336}
1337
1338inline TextView
1339TextView::split_prefix_at(std::string_view const &delimiters) {
1340 return this->split_prefix(this->find_first_of(delimiters));
1341}
1342
1343template <typename F>
1344TextView::self_type
1345TextView::split_prefix_if(F const &pred) {
1346 return this->split_prefix(this->find_if(pred));
1347}
1348
1349inline TextView
1350TextView::take_prefix(size_t n) {
1351 n = std::min(n, this->size());
1352 self_type zret = this->prefix(n);
1353 this->remove_prefix(std::min(n + 1, this->size()));
1354 return zret;
1355}
1356
1357inline TextView
1359 return this->take_prefix(this->find(c));
1360}
1361
1362inline TextView
1363TextView::take_prefix_at(std::string_view const &delimiters) {
1364 return this->take_prefix(this->find_first_of(delimiters));
1365}
1366
1367template <typename F>
1368auto
1369TextView::take_prefix_if(F const &pred) -> self_type {
1370 return this->take_prefix(this->find_if(pred));
1371}
1372
1373inline constexpr TextView
1374TextView::suffix(size_t n) const noexcept {
1375 n = std::min(n, this->size());
1376 return {this->data_end() - n, n};
1377}
1378
1379inline constexpr TextView
1380TextView::suffix(int n) const noexcept {
1381 return this->suffix(size_t(n));
1382}
1383
1384inline TextView
1385TextView::suffix_at(char c) const {
1386 self_type zret;
1387 if (auto n = this->rfind(c); n != npos && n + 1 < this->size()) {
1388 ++n;
1389 zret.assign(this->data() + n, this->size() - n);
1390 }
1391 return zret;
1392}
1393
1394inline TextView
1395TextView::suffix_at(std::string_view const &delimiters) const {
1396 self_type zret;
1397 if (auto n = this->find_last_of(delimiters); n != npos) {
1398 ++n;
1399 zret.assign(this->data() + n, this->size() - n);
1400 }
1401 return zret;
1402}
1403
1404template <typename F>
1405auto
1406TextView::suffix_if(F const &pred) const -> self_type {
1407 self_type zret;
1408 if (auto n = this->rfind_if(pred); n != npos) {
1409 ++n;
1410 zret.assign(this->data() + n, this->size() - n);
1411 }
1412 return zret;
1413}
1414
1415inline auto
1416TextView::remove_suffix(size_t n) -> self_type & {
1417 this->super_type::remove_suffix(std::min(n, this->size()));
1418 return *this;
1419}
1420
1421inline TextView &
1423 if (auto n = this->rfind(c); n != npos) {
1424 return this->remove_suffix(this->size() - n);
1425 }
1426 return this->clear();
1427}
1428
1429inline TextView &
1430TextView::remove_suffix_at(std::string_view const &delimiters) {
1431 if (auto n = this->find_last_of(delimiters); n != npos) {
1432 return this->remove_suffix(this->size() - n);
1433 }
1434 return this->clear();
1435}
1436
1437template <typename F>
1438TextView::self_type &
1439TextView::remove_suffix_if(F const &pred) {
1440 if (auto n = this->rfind_if(pred); n != npos) {
1441 return this->remove_suffix(this->size() - n);
1442 }
1443 return this->clear();
1444}
1445
1446inline TextView
1447TextView::split_suffix(size_t n) {
1448 self_type zret;
1449 n = std::min(n, this->size());
1450 zret = this->suffix(n);
1451 this->remove_suffix(n + 1);
1452 return zret;
1453}
1454
1455inline auto
1456TextView::split_suffix(int n) -> self_type {
1457 return this->split_suffix(size_t(n));
1458}
1459
1460inline TextView
1462 auto idx = this->rfind(c);
1463 return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1));
1464}
1465
1466inline auto
1467TextView::split_suffix_at(std::string_view const &delimiters) -> self_type {
1468 auto idx = this->find_last_of(delimiters);
1469 return npos == idx ? self_type{} : this->split_suffix(this->size() - (idx + 1));
1470}
1471
1472template <typename F>
1473TextView::self_type
1474TextView::split_suffix_if(F const &pred) {
1475 return this->split_suffix(this->rfind_if(pred));
1476}
1477
1478inline TextView
1479TextView::take_suffix(size_t n) {
1480 self_type zret{*this};
1481 *this = zret.split_prefix(n);
1482 return zret;
1483}
1484
1485inline TextView
1486TextView::take_suffix(int n) {
1487 return this->take_suffix(size_t(n));
1488}
1489
1490inline TextView
1492 return this->take_suffix(this->rfind(c));
1493}
1494
1495inline TextView
1496TextView::take_suffix_at(std::string_view const &delimiters) {
1497 return this->take_suffix(this->find_last_of(delimiters));
1498}
1499
1500template <typename F>
1501TextView::self_type
1502TextView::take_suffix_if(F const &pred) {
1503 return this->take_suffix_at(this->rfind_if(pred));
1504}
1505
1506template <typename F>
1507inline size_t
1508TextView::find_if(F const &pred) const {
1509 for (const char *spot = this->data(), *limit = this->data_end(); spot < limit; ++spot)
1510 if (pred(*spot))
1511 return spot - this->data();
1512 return npos;
1513}
1514
1515template <typename F>
1516inline size_t
1517TextView::rfind_if(F const &pred) const {
1518 for (const char *spot = this->data_end(), *limit = this->data(); spot > limit;)
1519 if (pred(*--spot))
1520 return spot - this->data();
1521 return npos;
1522}
1523
1524inline TextView &
1525TextView::ltrim(char c) {
1526 this->remove_prefix(this->find_first_not_of(c));
1527 return *this;
1528}
1529
1530inline TextView &
1531TextView::rtrim(char c) {
1532 auto n = this->find_last_not_of(c);
1533 this->remove_suffix(this->size() - (n == npos ? 0 : n + 1));
1534 return *this;
1535}
1536
1537inline TextView &
1538TextView::trim(char c) {
1539 return this->ltrim(c).rtrim(c);
1540}
1541
1542inline TextView &
1543TextView::ltrim(CharSet const &delimiters) {
1544 const char *spot = this->data();
1545 const char *limit = this->data_end();
1546
1547 while (spot < limit && delimiters(*spot)) {
1548 ++spot;
1549 }
1550 this->remove_prefix(spot - this->data());
1551
1552 return *this;
1553}
1554
1555inline TextView &
1556TextView::ltrim(std::string_view const &delimiters) {
1557 return this->ltrim(CharSet(delimiters));
1558}
1559
1560inline TextView &
1561TextView::ltrim(const char *delimiters) {
1562 return this->ltrim(CharSet(delimiters));
1563}
1564
1565inline TextView &
1566TextView::rtrim(CharSet const &delimiters) {
1567 const char *spot = this->data_end();
1568 const char *limit = this->data();
1569 while (limit < spot-- && delimiters(*spot)) {
1570 }
1571
1572 this->remove_suffix(this->data_end() - (spot + 1));
1573 return *this;
1574}
1575
1576inline TextView &
1577TextView::rtrim(std::string_view const &delimiters) {
1578 return this->rtrim(CharSet(delimiters));
1579}
1580
1581inline TextView &
1582TextView::trim(CharSet const &delimiters) {
1583 const char *spot;
1584 const char *limit;
1585
1586 // Do this explicitly, so we don't have to initialize the character set twice.
1587 for (spot = this->data(), limit = this->data_end(); spot < limit && delimiters(*spot); ++spot)
1588 ;
1589 this->remove_prefix(spot - this->data());
1590
1591 spot = this->data_end();
1592 limit = this->data();
1593 while (limit < spot-- && delimiters(*spot)) {
1594 }
1595 this->remove_suffix(this->data_end() - (spot + 1));
1596
1597 return *this;
1598}
1599
1600inline TextView &
1601TextView::trim(std::string_view const &delimiters) {
1602 return this->trim(CharSet(delimiters));
1603}
1604
1605inline TextView &
1606TextView::trim(const char *delimiters) {
1607 return this->trim(CharSet(delimiters));
1608}
1609
1610template <typename F>
1611TextView::self_type &
1612TextView::ltrim_if(F const &pred) {
1613 const char *spot;
1614 const char *limit;
1615 for (spot = this->data(), limit = this->data_end(); spot < limit && pred(*spot); ++spot)
1616 ;
1617 this->remove_prefix(spot - this->data());
1618 return *this;
1619}
1620
1621template <typename F>
1622TextView::self_type &
1623TextView::rtrim_if(F const &pred) {
1624 const char *spot = this->data_end();
1625 const char *limit = this->data();
1626 while (limit < spot-- && pred(*spot))
1627 ;
1628 this->remove_suffix(this->data_end() - (spot + 1));
1629 return *this;
1630}
1631
1632template <typename F>
1633TextView::self_type &
1634TextView::trim_if(F const &pred) {
1635 return this->ltrim_if(pred).rtrim_if(pred);
1636}
1637
1638constexpr inline auto
1639TextView::data() const noexcept -> value_type const * {
1640 return super_type::data();
1641}
1642
1643constexpr inline auto
1644TextView::data_end() const noexcept -> value_type const * {
1645 return this->data() + this->size();
1646}
1647
1648inline constexpr TextView
1649TextView::substr(size_type pos, size_type count) const noexcept {
1650 if (pos >= this->size()) {
1651 return {};
1652 }
1653 count = std::min(this->size() - pos, count);
1654 return {this->data() + pos, count};
1655}
1656
1657inline bool
1658TextView::starts_with(std::string_view const &prefix) const noexcept {
1659 return this->size() >= prefix.size() && 0 == ::memcmp(this->data(), prefix.data(), prefix.size());
1660}
1661
1662inline bool
1663TextView::starts_with(char const *prefix) const {
1664 return this->starts_with(super_type(prefix));
1665}
1666inline bool
1667TextView::starts_with_nocase(char const *prefix) const {
1668 return this->starts_with_nocase(super_type{prefix});
1669}
1670
1671inline bool
1672TextView::starts_with(char c) const noexcept {
1673 return !this->empty() && c == this->front();
1674}
1675inline bool
1676TextView::starts_with_nocase(char c) const noexcept {
1677 return !this->empty() && tolower(c) == tolower(this->front());
1678}
1679
1680inline bool
1681TextView::starts_with_nocase(std::string_view const &prefix) const noexcept {
1682 return this->size() >= prefix.size() && 0 == ::strncasecmp(this->data(), prefix.data(), prefix.size());
1683}
1684
1685inline bool
1686TextView::ends_with(std::string_view const &suffix) const noexcept {
1687 return this->size() >= suffix.size() && 0 == ::memcmp(this->data_end() - suffix.size(), suffix.data(), suffix.size());
1688}
1689
1690inline bool
1691TextView::ends_with_nocase(std::string_view const &suffix) const noexcept {
1692 return this->size() >= suffix.size() && 0 == ::strncasecmp(this->data_end() - suffix.size(), suffix.data(), suffix.size());
1693}
1694
1695inline bool
1696TextView::ends_with(char const *suffix) const {
1697 return this->ends_with(super_type(suffix));
1698}
1699
1700inline bool
1701TextView::ends_with_nocase(char const *suffix) const {
1702 return this->ends_with_nocase(super_type(suffix));
1703}
1704
1705inline bool
1706TextView::ends_with(char c) const noexcept {
1707 return !this->empty() && c == this->back();
1708}
1709inline bool
1710TextView::ends_with_nocase(char c) const noexcept {
1711 return !this->empty() && tolower(c) == tolower(this->back());
1712}
1713
1714template <typename Stream>
1715Stream &
1716TextView::stream_write(Stream &os, const TextView &b) const {
1717 // Local function, avoids extra template work.
1718 static const auto stream_fill = [](Stream &ostream, size_t n) -> Stream & {
1719 static constexpr size_t pad_size = 8;
1720 typename Stream::char_type padding[pad_size];
1721
1722 std::fill_n(padding, pad_size, ostream.fill());
1723 for (; n >= pad_size && ostream.good(); n -= pad_size)
1724 ostream.write(padding, pad_size);
1725 if (n > 0 && ostream.good())
1726 ostream.write(padding, n);
1727 return ostream;
1728 };
1729
1730 const std::size_t w = os.width();
1731 if (w <= b.size()) {
1732 os.write(b.data(), b.size());
1733 } else {
1734 const std::size_t pad_size = w - b.size();
1735 const bool align_left = (os.flags() & Stream::adjustfield) == Stream::left;
1736 if (!align_left && os.good())
1737 stream_fill(os, pad_size);
1738 if (os.good())
1739 os.write(b.data(), b.size());
1740 if (align_left && os.good())
1741 stream_fill(os, pad_size);
1742 }
1743 return os;
1744}
1745
1746template <typename F>
1747TextView::self_type
1748TextView::clip_prefix_of(F const &pred) {
1749 size_t idx = 0;
1750 for (auto spot = this->data(), limit = spot + this->size(); spot < limit && pred(*spot); ++spot, ++idx)
1751 ; // empty
1752 TextView token = this->prefix(idx);
1753 this->remove_prefix(idx);
1754 return token;
1755}
1756
1757template <typename F>
1758TextView::self_type
1759TextView::clip_suffix_of(F const &pred) {
1760 size_t idx = this->size() - 1;
1761 for (auto spot = this->data() + idx, limit = this->data(); spot >= limit && pred(*spot); --spot, --idx)
1762 ; // empty
1763 TextView token = this->suffix(idx);
1764 this->remove_suffix(idx);
1765 return token;
1766}
1768
1769// Provide an instantiation for @c std::ostream as it's likely this is the only one ever used.
1770extern template std::ostream &TextView::stream_write(std::ostream &, const TextView &) const;
1771
1799template <typename X, typename V> class TransformView {
1800 using self_type = TransformView;
1801 using source_iterator = decltype(V{}.begin());
1802
1803public:
1804 using transform_type = X;
1806 using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>;
1808 using value_type = std::invoke_result_t<transform_type, source_value_type>;
1810 using iterator = self_type;
1811
1818
1825
1827 TransformView(self_type const &that) = default;
1829 TransformView(self_type &&that) = default;
1830
1832 self_type &operator=(self_type const &that) = default;
1834 self_type &operator=(self_type &&that) = default;
1835
1837 bool operator==(self_type const &that) const;
1839 bool operator!=(self_type const &that) const;
1840
1844 self_type &operator++();
1846 self_type operator++(int);
1847
1849 bool empty() const;
1851 explicit operator bool() const;
1852
1854 iterator
1855 begin() const {
1856 return *this;
1857 }
1858
1859 iterator
1860 end() const {
1861 return self_type{_xf, _limit};
1862 }
1863
1864protected:
1866 source_iterator _spot;
1867 source_iterator _limit;
1868
1870 TransformView(transform_type &&xf, source_iterator &&limit) : _xf(xf), _spot(limit), _limit(limit) {}
1871};
1872
1873template <typename X, typename V>
1875
1876template <typename X, typename V>
1879
1880template <typename X, typename V>
1881auto
1883 return _xf(*_spot);
1884}
1885
1886template <typename X, typename V>
1887auto
1889 ++_spot;
1890 return *this;
1891}
1892
1893template <typename X, typename V>
1894auto
1896 self_type zret{*this};
1897 ++_spot;
1898 return zret;
1899}
1900
1901template <typename X, typename V>
1902bool
1904 return _spot == _limit;
1905}
1906
1907template <typename X, typename V> TransformView<X, V>::operator bool() const {
1908 return _spot != _limit;
1909}
1910
1911template <typename X, typename V>
1912bool
1913TransformView<X, V>::operator==(self_type const &that) const {
1914 return _spot == that._spot && _limit == that._limit;
1915}
1916
1917template <typename X, typename V>
1918bool
1919TransformView<X, V>::operator!=(self_type const &that) const {
1920 return _spot != that._spot || _limit != that._limit;
1921}
1922
1931template <typename X, typename V>
1933transform_view_of(X const &xf, V const &src) {
1934 return TransformView<X, V>(xf, src);
1935}
1936
1944template <typename V> class TransformView<void, V> {
1945 using self_type = TransformView;
1947 using source_iterator = decltype(V{}.begin());
1948
1949public:
1951 using source_value_type = std::remove_reference_t<decltype(*source_iterator{})>;
1953 using value_type = source_value_type;
1955 using iterator = self_type;
1956
1962
1964 TransformView(self_type const &that) = default;
1966 TransformView(self_type &&that) = default;
1967
1969 self_type &operator=(self_type const &that) = default;
1971 self_type &operator=(self_type &&that) = default;
1972
1974 bool operator==(self_type const &that) const;
1976 bool operator!=(self_type const &that) const;
1977
1979 value_type operator*() const;
1980
1982 self_type &operator++();
1983
1985 self_type operator++(int);
1986
1988 bool empty() const;
1989
1991 explicit operator bool() const;
1992
1994 iterator begin() const;
1996 iterator end() const;
1997
1998protected:
1999 source_iterator _spot;
2000 source_iterator _limit;
2001
2003 explicit TransformView(source_iterator &&limit) : _spot(limit), _limit(limit) {}
2004};
2005
2006template <typename V>
2007auto
2009 return *_spot;
2010}
2011
2012template <typename V>
2013auto
2015 ++_spot;
2016 return *this;
2017}
2018
2019template <typename V>
2020auto
2022 auto zret{*this};
2023 ++*this;
2024 return zret;
2025}
2026
2027template <typename V>
2028bool
2029TransformView<void, V>::operator==(const self_type &that) const {
2030 return _spot == that._spot && _limit == that._limit;
2031}
2032
2033template <typename V>
2034bool
2035TransformView<void, V>::operator!=(const self_type &that) const {
2036 return _spot != that._spot || _limit != that._limit;
2037}
2038
2039template <typename V>
2040bool
2042 return _spot == _limit;
2043}
2044
2045template <typename V> TransformView<void, V>::operator bool() const {
2046 return _spot != _limit;
2047}
2048
2049template <typename V>
2050auto
2051TransformView<void, V>::begin() const -> self_type {
2052 return *this;
2053}
2054
2055template <typename V>
2056auto
2057TransformView<void, V>::end() const -> self_type {
2058 return self_type{_limit};
2059}
2060
2062// Capture @c void transforms and make them identity transforms.
2063template <typename V>
2065transform_view_of(V const &v) {
2066 return TransformView<void, V>(v);
2067}
2068
2070
2076namespace literals {
2087constexpr std::string_view
2088operator"" _sv(const char *s, size_t n) {
2089 return {s, n};
2090}
2091
2102constexpr swoc::TextView
2103operator"" _tv(const char *s, size_t n) {
2104 return {s, n};
2105}
2106} // namespace literals
2107
2108}} // namespace swoc::SWOC_VERSION_NS
2109
2110namespace std {
2112ostream &operator<<(ostream &os, const swoc::TextView &view);
2113
2115/* For interaction with specific STL interfaces, primarily std::filesystem. Along with the
2116 * dereference operator, this enables a @c TextView to act as a character iterator to a C string
2117 * even if the internal view is not nul terminated.
2118 * @note Putting these directly in the class doesn't seem to work.
2119 */
2120template <> struct iterator_traits<swoc::TextView> {
2121 using value_type = char;
2122 using pointer_type = const char *;
2123 using reference_type = const char &;
2124 using difference_type = ssize_t;
2125 using iterator_category = forward_iterator_tag;
2126};
2127
2128template <typename X, typename V> struct iterator_traits<swoc::TransformView<X, V>> {
2129 using value_type = typename swoc::TransformView<X, V>::value_type;
2130 using pointer_type = const value_type *;
2131 using reference_type = const value_type &;
2132 using difference_type = ssize_t;
2133 using iterator_category = forward_iterator_tag;
2134};
2135
2136template <> struct hash<swoc::TextView> {
2137 static constexpr hash<string_view> super_hash{};
2138 size_t
2139 operator()(swoc::TextView const &s) const {
2140 return super_hash(s);
2141 }
2142};
2144
2145} // namespace std
2146
2147#if __GNUC__ == 11
2148#pragma GCC diagnostic pop
2149#endif
uintmax_t svto_radix(TextView &src)
Definition TextView.h:1059
constexpr TextView() noexcept=default
Default constructor (empty buffer).
constexpr value_type const * data() const noexcept
constexpr CharSet(TextView const &chars)
Definition TextView.h:1105
bool operator()(unsigned char c) const
Definition TextView.h:1112
self_type & operator=(char *&s)
Assign from C-string s.
self_type take_suffix_at(char c)
self_type prefix_at(char c) const
constexpr bool operator!() const noexcept
self_type & trim_if(F const &pred)
self_type & operator++()
self_type & remove_suffix_if(F const &pred)
self_type take_suffix_if(F const &pred)
bool starts_with_nocase(std::string_view const &prefix) const noexcept
self_type & clear()
Clear the view (become an empty view).
self_type & assign(char *&c_str)
self_type clip_prefix_of(F const &pred)
constexpr self_type & assign(C const &c)
Definition TextView.h:305
self_type & remove_prefix_at(char c)
self_type & rtrim_if(F const &pred)
constexpr TextView() noexcept=default
Default constructor (empty buffer).
self_type take_suffix(size_t n)
Stream & stream_write(Stream &os, const TextView &b) const
static void init_delimiter_set(std::string_view const &delimiters, std::bitset< 256 > &set)
Initialize a bit mask to mark which characters are in this view.
self_type & remove_prefix_if(F const &pred)
self_type operator++(int)
constexpr char operator*() const
self_type & remove_prefix(size_t n)
TextView(char const *&src)
Definition TextView.h:215
self_type & operator=(char const *&s)
Assign from C-string s.
self_type split_prefix_if(F const &pred)
self_type & ltrim_if(F const &pred)
constexpr value_type const * data_end() const noexcept
bool ends_with_nocase(std::string_view const &suffix) const noexcept
self_type & rtrim(char c)
self_type & assign(std::string const &s)
Explicitly set the view from a std::string.
TextView(std::string const &str) noexcept
self_type & assign(char const *&c_str)
self_type split_prefix(size_t n)
self_type & operator+=(size_t n)
bool ends_with(std::string_view const &suffix) const noexcept
constexpr self_type prefix(size_t n) const noexcept
self_type & remove_suffix_at(char c)
bool starts_with(std::string_view const &prefix) const noexcept
self_type take_prefix_at(char c)
self_type & remove_suffix(size_t n)
self_type split_suffix_if(F const &pred)
self_type split_prefix_at(char c)
self_type & assign(const char(&s)[N]) noexcept
self_type split_suffix_at(char c)
self_type take_prefix(size_t n)
self_type & assign(char const *ptr, size_t n)
constexpr TextView(const char(&s)[N]) noexcept
self_type suffix_at(char c) const
self_type & operator=(const char(&s)[N])
self_type & trim(char c)
self_type & operator=(super_type const &that)
Assign a super class instance, std::string_view to this.
constexpr value_type const * data() const noexcept
constexpr self_type suffix(size_t n) const noexcept
self_type & ltrim(char c)
self_type suffix_if(F const &pred) const
constexpr TextView(std::nullptr_t) noexcept
self_type take_prefix_if(F const &pred)
constexpr self_type substr(size_type pos=0, size_type count=npos) const noexcept
self_type clip_suffix_of(F const &pred)
size_t rfind_if(F const &pred) const
Get the offset of the last character for which pred is true.
TextView(char *&src)
Definition TextView.h:205
self_type & assign(char const *b, char const *e)
self_type prefix_if(F const &pred) const
size_t find_if(F const &pred) const
Get the offset of the first character for which pred is true.
self_type & operator=(const std::string &s)
Assign from a std::string.
self_type split_suffix(size_t n)
constexpr TextView(C const &c)
self_type iterator
This class serves as its own iterator.
Definition TextView.h:1955
TransformView(source_view_type const &v)
Definition TextView.h:1961
TransformView(self_type &&that)=default
Move constructor.
source_value_type value_type
Result type of calling the transform on an element of the source view.
Definition TextView.h:1953
V source_view_type
Export source view type.
Definition TextView.h:1950
value_type operator*() const
Get the current element.
Definition TextView.h:2008
TransformView(source_iterator &&limit)
Special constructor for making an empty instance to serve as the end iterator.
Definition TextView.h:2003
TransformView(self_type const &that)=default
Copy constructor.
self_type & operator=(self_type &&that)=default
Move assignment.
self_type & operator++()
Move to next element.
Definition TextView.h:2014
iterator end() const
Iterator past last transformed character.
Definition TextView.h:2057
self_type & operator=(self_type const &that)=default
Copy assignment.
iterator begin() const
Iterator to first transformed character.
Definition TextView.h:2051
source_iterator _spot
Current location.
Definition TextView.h:1999
source_iterator _limit
End marker.
Definition TextView.h:2000
bool empty() const
Check if view is empty.
Definition TextView.h:2041
source_iterator _spot
Current location in the source view.
Definition TextView.h:1866
value_type operator*() const
Get the current element.
Definition TextView.h:1882
iterator begin() const
Iterator to first transformed character.
Definition TextView.h:1855
self_type & operator=(self_type const &that)=default
Copy assignment.
source_iterator _limit
End of source view.
Definition TextView.h:1867
self_type iterator
This class serves as its own iterator.
Definition TextView.h:1810
TransformView(transform_type &&xf, source_view_type const &v)
Definition TextView.h:1874
TransformView(transform_type &&xf, source_iterator &&limit)
Special constructor for making an empty instance to serve as the end iterator.
Definition TextView.h:1870
X transform_type
Export transform functor type.
Definition TextView.h:1804
bool operator==(self_type const &that) const
Equality.
Definition TextView.h:1913
TransformView(self_type const &that)=default
Copy constructor.
TransformView(transform_type const &xf, source_view_type const &v)
Definition TextView.h:1877
bool operator!=(self_type const &that) const
Inequality.
Definition TextView.h:1919
bool empty() const
Check if view is empty.
Definition TextView.h:1903
self_type & operator++()
Move to next element.
Definition TextView.h:1888
self_type operator++(int)
Move to next element.
Definition TextView.h:1895
iterator end() const
Iterator past last transformed character.
Definition TextView.h:1860
TransformView(self_type &&that)=default
Move constructor.
self_type & operator=(self_type &&that)=default
Move assignment.
transform_type _xf
Per character transform functor.
Definition TextView.h:1865
V source_view_type
Export source view type.
Definition TextView.h:1805
std::invoke_result_t< transform_type, source_value_type > value_type
Result type of calling the transform on an element of the source view.
Definition TextView.h:1808
STL namespace.
For template deduction guides.
Definition ArenaWriter.cc:9
uintmax_t svtou(TextView src, TextView *out, int base)
Definition TextView.cc:81
uintmax_t svto_radix(TextView &src)
Definition TextView.h:1059
const int8_t svtoi_convert[256]
intmax_t svtoi(TextView src, TextView *out, int base)
Definition TextView.cc:45
TransformView< X, V > transform_view_of(X const &xf, V const &src)
Definition TextView.h:1933
bool operator==(IPAddr const &lhs, sockaddr const *sa)
Equality.
Definition swoc_ip.cc:650
bool operator!=(IP4Addr const &lhs, IP4Addr const &rhs)
Definition IPAddr.h:845
int strcasecmp(const std::string_view &lhs, const std::string_view &rhs)
int strcmp(const std::string_view &lhs, const std::string_view &rhs)
Support for containers that need case insensitive comparisons between views.
Definition TextView.h:964
bool operator()(self_type const &lhs, self_type const &rhs) const noexcept
Definition TextView.h:967
Ordering functor, case ignoring lexicographic comparison.
Definition TextView.h:955
bool operator()(self_type const &lhs, self_type const &rhs) const noexcept
Definition TextView.h:958
Ordering functor, lexicographic comparison.
Definition TextView.h:946
bool operator()(self_type const &lhs, self_type const &rhs) const noexcept
Definition TextView.h:949