From 3c90b6c57d1613ae2011a9459fed4535785b65a5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 20 Jun 2020 16:03:49 -0500 Subject: [PATCH] Re-implement optimizations to color lookup by name Hopefully without the null-termination issues this time around. Fixed by simply cloning the str object as-is then transforming it. --- src/color.cpp | 95 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/src/color.cpp b/src/color.cpp index aea715738..2838a1e39 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -7,6 +7,8 @@ #include #include #include // IWYU pragma: keep +#include +#include #include "common.h" #include "fallback.h" // IWYU pragma: keep @@ -150,29 +152,40 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) { struct named_color_t { const wchar_t *name; - unsigned char idx; + uint8_t idx; unsigned char rgb[3]; bool hidden; }; -static const named_color_t named_colors[] = { - {L"black", 0, {0x00, 0x00, 0x00}, false}, {L"red", 1, {0x80, 0x00, 0x00}, false}, - {L"green", 2, {0x00, 0x80, 0x00}, false}, {L"brown", 3, {0x72, 0x50, 0x00}, true}, - {L"yellow", 3, {0x80, 0x80, 0x00}, false}, {L"blue", 4, {0x00, 0x00, 0x80}, false}, - {L"magenta", 5, {0x80, 0x00, 0x80}, false}, {L"purple", 5, {0x80, 0x00, 0x80}, true}, - {L"cyan", 6, {0x00, 0x80, 0x80}, false}, {L"white", 7, {0xC0, 0xC0, 0xC0}, false}, - {L"grey", 7, {0xE5, 0xE5, 0xE5}, true}, {L"brblack", 8, {0x80, 0x80, 0x80}, false}, - {L"brgrey", 8, {0x55, 0x55, 0x55}, true}, {L"brred", 9, {0xFF, 0x00, 0x00}, false}, - {L"brgreen", 10, {0x00, 0xFF, 0x00}, false}, {L"brbrown", 11, {0xFF, 0xFF, 0x00}, true}, - {L"bryellow", 11, {0xFF, 0xFF, 0x00}, false}, {L"brblue", 12, {0x00, 0x00, 0xFF}, false}, - {L"brmagenta", 13, {0xFF, 0x00, 0xFF}, false}, {L"brpurple", 13, {0xFF, 0x00, 0xFF}, true}, - {L"brcyan", 14, {0x00, 0xFF, 0xFF}, false}, {L"brwhite", 15, {0xFF, 0xFF, 0xFF}, false}, +// Keep this sorted alphabetically +static const std::vector named_colors { + {L"black", 0, {0x00, 0x00, 0x00}, false}, + {L"blue", 4, {0x00, 0x00, 0x80}, false}, + {L"brblack", 8, {0x80, 0x80, 0x80}, false}, + {L"brblue", 12, {0x00, 0x00, 0xFF}, false}, + {L"brbrown", 11, {0xFF, 0xFF, 0x00}, true}, + {L"brcyan", 14, {0x00, 0xFF, 0xFF}, false}, + {L"brgreen", 10, {0x00, 0xFF, 0x00}, false}, + {L"brgrey", 8, {0x55, 0x55, 0x55}, true}, + {L"brmagenta", 13, {0xFF, 0x00, 0xFF}, false}, + {L"brown", 3, {0x72, 0x50, 0x00}, true}, + {L"brpurple", 13, {0xFF, 0x00, 0xFF}, true}, + {L"brred", 9, {0xFF, 0x00, 0x00}, false}, + {L"brwhite", 15, {0xFF, 0xFF, 0xFF}, false}, + {L"bryellow", 11, {0xFF, 0xFF, 0x00}, false}, + {L"cyan", 6, {0x00, 0x80, 0x80}, false}, + {L"green", 2, {0x00, 0x80, 0x00}, false}, + {L"grey", 7, {0xE5, 0xE5, 0xE5}, true}, + {L"magenta", 5, {0x80, 0x00, 0x80}, false}, + {L"purple", 5, {0x80, 0x00, 0x80}, true}, + {L"red", 1, {0x80, 0x00, 0x00}, false}, + {L"white", 7, {0xC0, 0xC0, 0xC0}, false}, + {L"yellow", 3, {0x80, 0x80, 0x00}, false}, }; wcstring_list_t rgb_color_t::named_color_names() { - const size_t count = sizeof named_colors / sizeof *named_colors; wcstring_list_t result; - result.reserve(1 + count); + result.reserve(1 + named_colors.size()); for (const auto &named_color : named_colors) { if (!named_color.hidden) { result.push_back(named_color.name); @@ -188,23 +201,53 @@ wcstring_list_t rgb_color_t::named_color_names() { } bool rgb_color_t::try_parse_named(const wcstring &str) { + static auto named_colors_begin = named_colors.begin(); + static auto named_colors_end = named_colors.end(); std::memset(&data, 0, sizeof data); - size_t max = sizeof named_colors / sizeof *named_colors; - for (size_t idx = 0; idx < max; idx++) { - if (0 == wcscasecmp(str.c_str(), named_colors[idx].name)) { - data.name_idx = named_colors[idx].idx; - this->type = type_named; - return true; - } + + if (str.size() == 0) { + return false; } + + // Binary search + named_color_t search; + search.name = str.c_str(); + + // Optimized conversion to lowercase with early abort + maybe_t lowercase; + for (auto &c : str) { + if (c >= L'a' && c <= L'z') { + continue; + } + 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; + return true; + } + return false; } static const wchar_t *name_for_color_idx(unsigned char idx) { - size_t max = sizeof named_colors / sizeof *named_colors; - for (size_t i = 0; i < max; i++) { - if (named_colors[i].idx == idx) { - return named_colors[i].name; + if (idx < named_colors.size()) { + for (auto &color : named_colors) { + if (idx == color.idx) { + return color.name; + } } } return L"unknown";