Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
text.h
1#ifndef SOURCEMETA_CORE_TEXT_H_
2#define SOURCEMETA_CORE_TEXT_H_
3
4#ifndef SOURCEMETA_CORE_TEXT_EXPORT
5#include <sourcemeta/core/text_export.h>
6#endif
7
8#include <algorithm> // std::max
9#include <array> // std::array
10#include <charconv> // std::to_chars
11#include <concepts> // std::same_as, std::integral
12#include <cstddef> // std::size_t
13#include <cstdint> // std::int8_t, std::uint64_t
14#include <filesystem> // std::filesystem::path
15#include <ios> // std::streamsize
16#include <limits> // std::numeric_limits
17#include <optional> // std::optional
18#include <ostream> // std::ostream
19#include <string> // std::string
20#include <string_view> // std::string_view
21#include <type_traits> // std::remove_cv_t
22#include <utility> // std::pair
23#include <vector> // std::vector
24
33
34namespace sourcemeta::core {
35
49SOURCEMETA_CORE_TEXT_EXPORT
50auto to_title_case(std::string &value) -> void;
51
65template <typename Character>
66 requires std::same_as<Character, char> ||
67 std::same_as<Character, signed char> ||
68 std::same_as<Character, unsigned char> ||
69 std::same_as<Character, wchar_t>
70inline constexpr auto to_lowercase(const Character character) noexcept
71 -> Character {
72 return (character >= 'A' && character <= 'Z')
73 ? static_cast<Character>(character + ('a' - 'A'))
74 : character;
75}
76
90template <typename Character, typename Traits, typename Allocator>
91 requires requires(Character character) {
92 { to_lowercase(character) } -> std::same_as<Character>;
93 }
94inline auto to_lowercase(std::basic_string<Character, Traits, Allocator> &value)
95 -> void {
96 for (auto &character : value) {
97 character = to_lowercase(character);
98 }
99}
100
114SOURCEMETA_CORE_TEXT_EXPORT
115auto to_lowercase(std::filesystem::path &value) -> void;
116
129template <typename Character>
130 requires std::same_as<Character, char> ||
131 std::same_as<Character, signed char> ||
132 std::same_as<Character, unsigned char> ||
133 std::same_as<Character, wchar_t>
134inline constexpr auto is_lowercase(const Character character) noexcept -> bool {
135 return character < 'A' || character > 'Z';
136}
137
151template <typename String>
152 requires requires(const String &value) {
153 { is_lowercase(*value.begin()) } -> std::same_as<bool>;
154 }
155inline auto is_lowercase(const String &value) noexcept -> bool {
156 for (const auto character : value) {
157 if (!is_lowercase(character)) {
158 return false;
159 }
160 }
161 return true;
162}
163
177SOURCEMETA_CORE_TEXT_EXPORT
178auto is_lowercase(const std::filesystem::path &value) noexcept -> bool;
179
192template <typename Character>
193 requires std::same_as<Character, char> ||
194 std::same_as<Character, signed char> ||
195 std::same_as<Character, unsigned char> ||
196 std::same_as<Character, wchar_t>
197inline constexpr auto is_alpha(const Character character) noexcept -> bool {
198 return (character >= 'a' && character <= 'z') ||
199 (character >= 'A' && character <= 'Z');
200}
201
215inline constexpr auto is_alpha(const std::string_view value) noexcept -> bool {
216 if (value.empty()) {
217 return false;
218 }
219 for (const auto character : value) {
220 if (!is_alpha(character)) {
221 return false;
222 }
223 }
224 return true;
225}
226
238template <typename Character>
239 requires std::same_as<Character, char> ||
240 std::same_as<Character, signed char> ||
241 std::same_as<Character, unsigned char> ||
242 std::same_as<Character, wchar_t>
243inline constexpr auto is_digit(const Character character) noexcept -> bool {
244 return character >= '0' && character <= '9';
245}
246
260inline constexpr auto is_digit(const std::string_view value) noexcept -> bool {
261 if (value.empty()) {
262 return false;
263 }
264 for (const auto character : value) {
265 if (!is_digit(character)) {
266 return false;
267 }
268 }
269 return true;
270}
271
284template <typename Character>
285 requires std::same_as<Character, char> ||
286 std::same_as<Character, signed char> ||
287 std::same_as<Character, unsigned char> ||
288 std::same_as<Character, wchar_t>
289inline constexpr auto is_alphanum(const Character character) noexcept -> bool {
290 return is_alpha(character) || is_digit(character);
291}
292
306inline constexpr auto is_alphanum(const std::string_view value) noexcept
307 -> bool {
308 if (value.empty()) {
309 return false;
310 }
311 for (const auto character : value) {
312 if (!is_alphanum(character)) {
313 return false;
314 }
315 }
316 return true;
317}
318
334SOURCEMETA_CORE_TEXT_EXPORT
335auto truncate(std::string &input, const std::size_t maximum_length,
336 const std::string_view marker) -> void;
337
349SOURCEMETA_CORE_TEXT_EXPORT
350auto replace(const std::string_view input, const std::string_view target,
351 const std::string_view replacement) -> std::string;
352
366SOURCEMETA_CORE_TEXT_EXPORT
367auto trim(const std::string_view input) noexcept -> std::string_view;
368
381SOURCEMETA_CORE_TEXT_EXPORT
382auto strip_left(const std::string_view input, const char character) noexcept
383 -> std::string_view;
384
397SOURCEMETA_CORE_TEXT_EXPORT
398auto strip_right(const std::string_view input, const char character) noexcept
399 -> std::string_view;
400
415inline constexpr auto unquote(const std::string_view input,
416 const char quote) noexcept -> std::string_view {
417 if (input.size() < 2 || input.front() != quote || input.back() != quote) {
418 return input;
419 }
420
421 auto inner{input};
422 inner.remove_prefix(1);
423 inner.remove_suffix(1);
424 return inner;
425}
426
439SOURCEMETA_CORE_TEXT_EXPORT
440auto pad_left(const std::string_view input, const std::size_t width,
441 const char character) -> std::string;
442
456SOURCEMETA_CORE_TEXT_EXPORT
457auto take_until(const std::string_view input, const char marker) noexcept
458 -> std::string_view;
459
476SOURCEMETA_CORE_TEXT_EXPORT
477auto split_once(const std::string_view input, const char delimiter) noexcept
478 -> std::optional<std::pair<std::string_view, std::string_view>>;
479
495SOURCEMETA_CORE_TEXT_EXPORT
496auto split_once(const std::string_view input,
497 const std::string_view delimiter) noexcept
498 -> std::optional<std::pair<std::string_view, std::string_view>>;
499
516SOURCEMETA_CORE_TEXT_EXPORT
517auto rsplit_once(const std::string_view input, const char delimiter) noexcept
518 -> std::optional<std::pair<std::string_view, std::string_view>>;
519
534template <typename Callback>
535auto split(const std::string_view input, const char delimiter,
536 Callback callback) -> void {
537 std::string_view rest{input};
538 while (true) {
539 const auto next{sourcemeta::core::split_once(rest, delimiter)};
540 if (!next.has_value()) {
541 callback(rest);
542 return;
543 }
544 callback(next->first);
545 rest = next->second;
546 }
547}
548
564inline auto split(const std::string_view input, const char delimiter)
565 -> std::vector<std::string_view> {
566 std::vector<std::string_view> parts;
568 input, delimiter,
569 [&parts](const std::string_view part) -> void { parts.push_back(part); });
570 return parts;
571}
572
587template <typename Range>
588auto join(const Range &items, const std::string_view separator) -> std::string {
589 std::string result;
590 bool first{true};
591 for (const auto &item : items) {
592 if (!first) {
593 result.append(separator);
594 }
595
596 result.append(item);
597 first = false;
598 }
599
600 return result;
601}
602
617template <typename Range>
618auto join_to(std::ostream &stream, const Range &items,
619 const std::string_view separator) -> void {
620 bool first{true};
621 for (const auto &item : items) {
622 if (!first) {
623 stream << separator;
624 }
625 stream << item;
626 first = false;
627 }
628}
629
635template <typename Type>
637 std::integral<Type> && !std::same_as<std::remove_cv_t<Type>, bool> &&
638 sizeof(Type) <= sizeof(std::uint64_t);
639
645template <typename Integer>
647inline constexpr std::size_t digits_capacity{
648 static_cast<std::size_t>(std::numeric_limits<Integer>::digits10) + 1 +
649 (std::numeric_limits<Integer>::is_signed ? 1U : 0U)};
650
656using DigitsBuffer = std::array<char, std::max(digits_capacity<std::uint64_t>,
658
681template <typename Integer, std::size_t Capacity>
683 (Capacity >= digits_capacity<Integer>)
684inline auto digits_view(const Integer value,
685 std::array<char, Capacity> &buffer) noexcept
686 -> std::string_view {
687 const auto result{
688 std::to_chars(buffer.data(), buffer.data() + buffer.size(), value)};
689 return {buffer.data(), static_cast<std::size_t>(result.ptr - buffer.data())};
690}
691
706template <typename Output, typename Integer>
707 requires text_spellable_integer<Integer>
708inline auto digits_append(Output &output, const Integer value) -> void {
709 std::array<char, digits_capacity<Integer>> buffer;
710 const auto digits{digits_view(value, buffer)};
711 output.append(digits.data(), digits.size());
712}
713
730template <typename CharT, typename Traits, typename Integer>
731 requires text_spellable_integer<Integer>
732inline auto digits_write(std::basic_ostream<CharT, Traits> &stream,
733 const Integer value) -> void {
734 std::array<char, digits_capacity<Integer>> buffer;
735 const auto digits{digits_view(value, buffer)};
736 stream.write(digits.data(), static_cast<std::streamsize>(digits.size()));
737}
738
752inline auto hex_digit_value(const char character) noexcept -> std::int8_t {
753 // Indexed by byte value: ASCII '0'-'9', 'A'-'F', and 'a'-'f' map to their
754 // hexadecimal value, everything else to -1
755 static constexpr std::array<std::int8_t, 256> table{
756 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
757 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
758 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
759 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
760 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
761 -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1,
762 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
763 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
764 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
765 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
766 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
767 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
768 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
769 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
770 -1, -1, -1, -1}};
771 return table[static_cast<unsigned char>(character)];
772}
773
786inline auto is_hex_digit(const char character) noexcept -> bool {
787 return hex_digit_value(character) >= 0;
788}
789
805SOURCEMETA_CORE_TEXT_EXPORT
806auto hex_to_bytes(const std::string_view input,
807 const bool allow_odd_length = false)
808 -> std::optional<std::string>;
809
820SOURCEMETA_CORE_TEXT_EXPORT
821auto bytes_to_hex(const std::string_view input) -> std::string;
822
838inline auto equals_ignore_case(const std::string_view left,
839 const std::string_view right) noexcept -> bool {
840 if (left.size() != right.size()) {
841 return false;
842 }
843
844 for (std::size_t index{0}; index < left.size(); ++index) {
845 if (to_lowercase(left[index]) != to_lowercase(right[index])) {
846 return false;
847 }
848 }
849
850 return true;
851}
852
867inline auto starts_with_ignore_case(const std::string_view value,
868 const std::string_view prefix) noexcept
869 -> bool {
870 if (value.size() < prefix.size()) {
871 return false;
872 }
873
874 for (std::size_t index{0}; index < prefix.size(); ++index) {
875 if (to_lowercase(value[index]) != to_lowercase(prefix[index])) {
876 return false;
877 }
878 }
879
880 return true;
881}
882
897inline auto less_ignore_case(const std::string_view left,
898 const std::string_view right) noexcept -> bool {
899 const auto length{left.size() < right.size() ? left.size() : right.size()};
900 for (std::size_t index{0}; index < length; ++index) {
901 const auto left_lower{to_lowercase(left[index])};
902 const auto right_lower{to_lowercase(right[index])};
903 if (left_lower != right_lower) {
904 return left_lower < right_lower;
905 }
906 }
907
908 return left.size() < right.size();
909}
910
922SOURCEMETA_CORE_TEXT_EXPORT
923auto squeeze(const std::string_view input, const char character) -> std::string;
924
940template <typename Output>
941auto squeeze(const std::string_view input, const char character, Output &output)
942 -> void {
943 bool in_run{false};
944 for (const auto value : input) {
945 if (value == character) {
946 if (!in_run) {
947 output.push_back(value);
948 }
949
950 in_run = true;
951 } else {
952 output.push_back(value);
953 in_run = false;
954 }
955 }
956}
957
973SOURCEMETA_CORE_TEXT_EXPORT
974auto remove_suffix_ignore_case(const std::string_view input,
975 const std::string_view suffix) noexcept
976 -> std::string_view;
977
978} // namespace sourcemeta::core
979
980#endif
@ Type
The token type does not match the expected media type.
Definition jose_verify.h:202
constexpr auto is_digit(const char character) -> bool
Definition numeric_util.h:51
SOURCEMETA_CORE_TEXT_EXPORT auto truncate(std::string &input, const std::size_t maximum_length, const std::string_view marker) -> void
SOURCEMETA_CORE_TEXT_EXPORT auto to_title_case(std::string &value) -> void
auto join_to(std::ostream &stream, const Range &items, const std::string_view separator) -> void
Definition text.h:618
constexpr std::size_t digits_capacity
Definition text.h:647
SOURCEMETA_CORE_TEXT_EXPORT auto bytes_to_hex(const std::string_view input) -> std::string
std::array< char, std::max(digits_capacity< std::uint64_t >, digits_capacity< std::int64_t >)> DigitsBuffer
Definition text.h:656
constexpr auto is_alphanum(const Character character) noexcept -> bool
Definition text.h:289
auto less_ignore_case(const std::string_view left, const std::string_view right) noexcept -> bool
Definition text.h:897
constexpr auto is_alpha(const Character character) noexcept -> bool
Definition text.h:197
constexpr auto is_lowercase(const Character character) noexcept -> bool
Definition text.h:134
auto is_hex_digit(const char character) noexcept -> bool
Definition text.h:786
auto digits_view(const Integer value, std::array< char, Capacity > &buffer) noexcept -> std::string_view
Definition text.h:684
auto split(const std::string_view input, const char delimiter, Callback callback) -> void
Definition text.h:535
SOURCEMETA_CORE_TEXT_EXPORT auto hex_to_bytes(const std::string_view input, const bool allow_odd_length=false) -> std::optional< std::string >
auto equals_ignore_case(const std::string_view left, const std::string_view right) noexcept -> bool
Definition text.h:838
SOURCEMETA_CORE_TEXT_EXPORT auto strip_left(const std::string_view input, const char character) noexcept -> std::string_view
constexpr auto unquote(const std::string_view input, const char quote) noexcept -> std::string_view
Definition text.h:415
SOURCEMETA_CORE_TEXT_EXPORT auto replace(const std::string_view input, const std::string_view target, const std::string_view replacement) -> std::string
auto hex_digit_value(const char character) noexcept -> std::int8_t
Definition text.h:752
SOURCEMETA_CORE_TEXT_EXPORT auto remove_suffix_ignore_case(const std::string_view input, const std::string_view suffix) noexcept -> std::string_view
auto digits_write(std::basic_ostream< CharT, Traits > &stream, const Integer value) -> void
Definition text.h:732
auto join(const Range &items, const std::string_view separator) -> std::string
Definition text.h:588
SOURCEMETA_CORE_TEXT_EXPORT auto strip_right(const std::string_view input, const char character) noexcept -> std::string_view
SOURCEMETA_CORE_TEXT_EXPORT auto trim(const std::string_view input) noexcept -> std::string_view
SOURCEMETA_CORE_TEXT_EXPORT auto squeeze(const std::string_view input, const char character) -> std::string
SOURCEMETA_CORE_TEXT_EXPORT auto take_until(const std::string_view input, const char marker) noexcept -> std::string_view
constexpr auto to_lowercase(const Character character) noexcept -> Character
Definition text.h:70
SOURCEMETA_CORE_TEXT_EXPORT auto rsplit_once(const std::string_view input, const char delimiter) noexcept -> std::optional< std::pair< std::string_view, std::string_view > >
SOURCEMETA_CORE_TEXT_EXPORT auto pad_left(const std::string_view input, const std::size_t width, const char character) -> std::string
SOURCEMETA_CORE_TEXT_EXPORT auto split_once(const std::string_view input, const char delimiter) noexcept -> std::optional< std::pair< std::string_view, std::string_view > >
auto digits_append(Output &output, const Integer value) -> void
Definition text.h:708
auto starts_with_ignore_case(const std::string_view value, const std::string_view prefix) noexcept -> bool
Definition text.h:867