util/text.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/util/text.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <cctype> | ||
| 6 | #include <cstdint> | ||
| 7 | |||
| 8 | #include <fmt/format.h> | ||
| 9 | |||
| 10 | #include "chrono_batch_render/util/error.h" | ||
| 11 | |||
| 12 | namespace chrono::batch_render { | ||
| 13 | |||
| 14 | 373 | std::string Lowercase(std::string_view text) { | |
| 15 | 373 | std::string lower(text); | |
| 16 | 373 | std::ranges::transform(lower, lower.begin(), | |
| 17 | 1420 | [](unsigned char c) { return char(std::tolower(c)); }); | |
| 18 | 373 | return lower; | |
| 19 | } | ||
| 20 | |||
| 21 | 1431 | std::string Join(const std::vector<std::string>& parts, std::string_view separator) { | |
| 22 | 1431 | std::string joined; | |
| 23 | 3622 | for (const std::string& part : parts) { | |
| 24 | 2191 | if (!joined.empty()) { | |
| 25 | 763 | joined += separator; | |
| 26 | } | ||
| 27 | 4382 | joined += part; | |
| 28 | } | ||
| 29 | 1431 | return joined; | |
| 30 | ✗ | } | |
| 31 | |||
| 32 | 118707 | std::vector<std::string_view> Split(std::string_view text, char separator) { | |
| 33 | 118707 | std::vector<std::string_view> parts; | |
| 34 | 118707 | size_t start = 0; | |
| 35 | 152956 | for (size_t end = text.find(separator); end != std::string_view::npos; | |
| 36 | 34249 | end = text.find(separator, start)) { | |
| 37 | 34249 | parts.push_back(text.substr(start, end - start)); | |
| 38 | 34249 | start = end + 1; | |
| 39 | } | ||
| 40 | 118707 | parts.push_back(text.substr(start)); | |
| 41 | 118707 | return parts; | |
| 42 | ✗ | } | |
| 43 | |||
| 44 | 83278 | std::pair<std::string_view, std::string_view> SplitLast(std::string_view text, char separator) { | |
| 45 | 83278 | const size_t at = text.rfind(separator); | |
| 46 | 83278 | if (at == std::string_view::npos) { | |
| 47 | 1 | return {std::string_view(), text}; | |
| 48 | } | ||
| 49 | 83277 | return {text.substr(0, at), text.substr(at + 1)}; | |
| 50 | } | ||
| 51 | |||
| 52 | 59223 | bool IsIdentifier(std::string_view text) { | |
| 53 | 651238 | const auto letter = [](char c) { | |
| 54 | 592015 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'; | |
| 55 | }; | ||
| 56 | 532793 | const auto letterOrDigit = [&](char c) { return letter(c) || (c >= '0' && c <= '9'); }; | |
| 57 | 59223 | return !text.empty() && letter(text[0]) && | |
| 58 | 59219 | std::all_of(text.begin() + 1, text.end(), letterOrDigit); | |
| 59 | } | ||
| 60 | |||
| 61 | 3532 | bool Glob(std::string_view pattern, std::string_view text) { | |
| 62 | 3532 | size_t p = 0; | |
| 63 | 3532 | size_t t = 0; | |
| 64 | 3532 | size_t star = std::string_view::npos; | |
| 65 | 3532 | size_t resume = 0; | |
| 66 | 12987 | while (t < text.size()) { | |
| 67 | 11801 | if (p < pattern.size() && (pattern[p] == '?' || pattern[p] == text[t])) { | |
| 68 | 6075 | ++p; | |
| 69 | 6075 | ++t; | |
| 70 | 5726 | } else if (p < pattern.size() && pattern[p] == '*') { | |
| 71 | 1019 | star = p++; | |
| 72 | 1019 | resume = t; | |
| 73 | 4707 | } else if (star != std::string_view::npos) { | |
| 74 | 2361 | p = star + 1; | |
| 75 | 2361 | t = ++resume; | |
| 76 | } else { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | 1187 | while (p < pattern.size() && pattern[p] == '*') { | |
| 82 | 1 | ++p; | |
| 83 | } | ||
| 84 | 1186 | return p == pattern.size(); | |
| 85 | } | ||
| 86 | |||
| 87 | 47678 | uint64_t Fnv1a(std::string_view bytes, uint64_t hash) { | |
| 88 | 581310104 | for (const char byte : bytes) { | |
| 89 | 581262426 | hash = (hash ^ uint8_t(byte)) * 0x100000001b3ull; | |
| 90 | } | ||
| 91 | 47678 | return hash; | |
| 92 | } | ||
| 93 | |||
| 94 | 41740 | std::string Digest(std::string_view bytes) { | |
| 95 | 41740 | return fmt::format("{:016x}", Fnv1a(bytes)); | |
| 96 | } | ||
| 97 | |||
| 98 | std::string | ||
| 99 | 515 | FillTemplate(std::string_view text, | |
| 100 | std::initializer_list<std::pair<std::string_view, std::string_view>> values) { | ||
| 101 | 515 | std::string filled; | |
| 102 | 515 | size_t at = 0; | |
| 103 | 6187 | while (true) { | |
| 104 | 3351 | const size_t open = text.find("{{", at); | |
| 105 | 3351 | if (open == std::string_view::npos) { | |
| 106 | 513 | filled += text.substr(at); | |
| 107 | 513 | return filled; | |
| 108 | } | ||
| 109 | 2838 | const size_t close = text.find("}}", open + 2); | |
| 110 | 2838 | if (close == std::string_view::npos) { | |
| 111 | 1 | throw Error("A template opens a placeholder at byte {} and never closes it", open); | |
| 112 | } | ||
| 113 | 2837 | const std::string_view name = text.substr(open + 2, close - open - 2); | |
| 114 | 2837 | const auto found = | |
| 115 | 2837 | std::ranges::find(values, name, &std::pair<std::string_view, std::string_view>::first); | |
| 116 | 2837 | if (found == values.end()) { | |
| 117 | 1 | throw Error("A template uses {{{{{}}}}}, which nothing fills", name); | |
| 118 | } | ||
| 119 | 2838 | filled += text.substr(at, open - at); | |
| 120 | 2836 | filled += found->second; | |
| 121 | 2836 | at = close + 2; | |
| 122 | 2836 | } | |
| 123 | 2 | } | |
| 124 | |||
| 125 | 42 | std::string Replaced(std::string text, std::string_view from, std::string_view to) { | |
| 126 | 42 | if (from.empty()) { | |
| 127 | ✗ | return text; | |
| 128 | } | ||
| 129 | 50 | for (size_t at = text.find(from); at != std::string::npos; at = text.find(from, at)) { | |
| 130 | 8 | text.replace(at, from.size(), to); | |
| 131 | 8 | at += to.size(); | |
| 132 | } | ||
| 133 | 42 | return text; | |
| 134 | } | ||
| 135 | |||
| 136 | 510 | std::string ModulePath(std::string_view module) { | |
| 137 | 510 | std::string path(module); | |
| 138 | 510 | std::ranges::replace(path, '.', '/'); | |
| 139 | 510 | return path + ".slang"; | |
| 140 | 510 | } | |
| 141 | |||
| 142 | 11 | std::string MapOrigins(std::string message, | |
| 143 | std::span<const std::pair<std::string, std::string>> origins) { | ||
| 144 | 32 | for (const auto& [generated, origin] : origins) { | |
| 145 | 21 | message = | |
| 146 | 21 | Replaced(Replaced(std::move(message), "./" + generated, origin), generated, origin); | |
| 147 | } | ||
| 148 | 11 | return message; | |
| 149 | } | ||
| 150 | |||
| 151 | } | ||
| 152 |