Simplify rgb_color_t::try_parse_named

This came out of an investigation into making strings immutable.
This code did "lazy" lowercasing but we can simplify it by just
providing our own case-insensitive compare routine, which is good
enough for colors.
This commit is contained in:
ridiculousfish
2021-04-18 12:06:05 -07:00
parent 0111eafbbc
commit 2fb0a703de

View File

@@ -13,6 +13,25 @@
#include "common.h" #include "common.h"
#include "fallback.h" // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep
/// Compare wide strings with simple ASCII canonicalization.
/// \return -1, 0, or 1 if s1 is less than, equal to, or greater than s2, respectively.
static int simple_icase_compare(const wchar_t *s1, const wchar_t *s2) {
for (size_t idx = 0; s1[idx] || s2[idx]; idx++) {
wchar_t c1 = s1[idx];
wchar_t c2 = s2[idx];
// "Canonicalize" to lower case.
if (L'A' <= c1 && c1 <= L'Z') c1 = L'a' + (c1 - L'A');
if (L'A' <= c2 && c2 <= L'Z') c2 = L'a' + (c2 - L'A');
if (c1 != c2) {
return c1 < c2 ? -1 : 1;
}
}
// We must have equal lengths and equal values.
return 0;
}
bool rgb_color_t::try_parse_special(const wcstring &special) { bool rgb_color_t::try_parse_special(const wcstring &special) {
std::memset(&data, 0, sizeof data); std::memset(&data, 0, sizeof data);
const wchar_t *name = special.c_str(); const wchar_t *name = special.c_str();
@@ -26,11 +45,11 @@ bool rgb_color_t::try_parse_special(const wcstring &special) {
// what amounts to a simple memcmp before needing to access the invariant case lookup tables. // what amounts to a simple memcmp before needing to access the invariant case lookup tables.
this->type = type_none; this->type = type_none;
if (special.size() == const_strlen(L"normal")) { if (special.size() == const_strlen(L"normal")) {
if (!wcscmp(name, L"normal") || !wcscasecmp(name, L"normal")) { if (!simple_icase_compare(name, L"normal")) {
this->type = type_normal; this->type = type_normal;
} }
} else if (special.size() == const_strlen(L"reset")) { } else if (special.size() == const_strlen(L"reset")) {
if (!wcscmp(name, L"reset") || !wcscasecmp(name, L"reset")) { if (!simple_icase_compare(name, L"reset")) {
this->type = type_reset; this->type = type_reset;
} }
} }
@@ -203,47 +222,23 @@ wcstring_list_t rgb_color_t::named_color_names() {
} }
bool rgb_color_t::try_parse_named(const wcstring &str) { bool rgb_color_t::try_parse_named(const wcstring &str) {
constexpr size_t colors_count = sizeof(named_colors) / sizeof(named_colors[0]);
constexpr const named_color_t *named_colors_begin = &named_colors[0];
constexpr const named_color_t *named_colors_end = &named_colors[0] + colors_count;
std::memset(&data, 0, sizeof data); std::memset(&data, 0, sizeof data);
if (str.empty()) {
if (str.size() == 0) {
return false; return false;
} }
// Binary search // Binary search with simple case-insensitive compares.
named_color_t search; auto is_less = [](const named_color_t &s1, const wchar_t *s2) -> bool {
search.name = str.c_str(); return simple_icase_compare(s1.name, s2) < 0;
};
// Optimized conversion to lowercase with early abort auto start = std::begin(named_colors);
maybe_t<wcstring> lowercase; auto end = std::end(named_colors);
for (auto &c : str) { auto where = std::lower_bound(start, end, str.c_str(), is_less);
if (c >= L'a' && c <= L'z') { if (where != end && simple_icase_compare(where->name, str.c_str()) == 0) {
continue; data.name_idx = where->idx;
}
if (c >= L'A' && c <= L'Z') {
lowercase = str;
std::transform(lowercase.value().begin(), lowercase.value().end(),
lowercase.value().begin(), std::towlower);
search.name = lowercase.value().c_str();
break;
}
// Cannot be a named color
return false;
}
auto result = std::lower_bound(named_colors_begin, named_colors_end, search,
[&](const named_color_t &c1, const named_color_t &c2) {
return wcscmp(c1.name, c2.name) < 0;
});
if (result != named_colors_end && !(wcscmp(search.name, result->name) < 0)) {
data.name_idx = result->idx;
this->type = type_named; this->type = type_named;
return true; return true;
} }
return false; return false;
} }