diff --git a/src/string_func.h b/src/string_func.h index c7c9478dcb..00f1d3fcfd 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -76,7 +76,9 @@ inline size_t ttd_strnlen(const char *str, size_t maxlen) bool IsValidChar(char32_t key, CharSetFilter afilter); size_t Utf8Decode(char32_t *c, const char *s); -inline size_t Utf8Decode(char32_t *c, std::string::iterator &s) { return Utf8Decode(c, &*s); } +/* std::string_view::iterator might be char *, in which case we do not want this templated variant to be taken. */ +template requires (!std::is_same_v && (std::is_same_v || std::is_same_v)) +inline size_t Utf8Decode(char32_t *c, T &s) { return Utf8Decode(c, &*s); } size_t Utf8Encode(char *buf, char32_t c); size_t Utf8Encode(std::ostreambuf_iterator &buf, char32_t c); size_t Utf8Encode(std::back_insert_iterator &buf, char32_t c); diff --git a/src/stringfilter.cpp b/src/stringfilter.cpp index 16800c2440..9ec17a7267 100644 --- a/src/stringfilter.cpp +++ b/src/stringfilter.cpp @@ -25,34 +25,34 @@ static const char32_t STATE_QUOTE2 = '"'; * Set the term to filter on. * @param str Filter term */ -void StringFilter::SetFilterTerm(const char *str) +void StringFilter::SetFilterTerm(std::string_view str) { this->word_index.clear(); this->word_index.shrink_to_fit(); this->word_matches = 0; - free(this->filter_buffer); - - assert(str != nullptr); - - char *dest = MallocT(strlen(str) + 1); - this->filter_buffer = dest; char32_t state = STATE_WHITESPACE; - const char *pos = str; - WordState *word = nullptr; - size_t len; - for (;; pos += len) { + auto pos = str.begin(); + auto word_begin = str.end(); + auto word_end = pos; + + /* Helper to prevent duplicating code. */ + auto add_word = [&] () { + if (word_begin != str.end()) { + this->word_index.emplace_back(std::string(word_begin, word_end + 1), false); + word_begin = str.end(); + } + }; + + for (size_t len; pos < str.end(); pos += len) { char32_t c; len = Utf8Decode(&c, pos); - if (c == 0 || (state == STATE_WORD && IsWhitespace(c))) { + if (state == STATE_WORD && IsWhitespace(c)) { /* Finish word */ - if (word != nullptr) { - *(dest++) = '\0'; - word = nullptr; - } + add_word(); state = STATE_WHITESPACE; - if (c != 0) continue; else break; + continue; } if (state == STATE_WHITESPACE) { @@ -74,22 +74,14 @@ void StringFilter::SetFilterTerm(const char *str) } /* Add to word */ - if (word == nullptr) { - word = &this->word_index.emplace_back(WordState{ dest, false }); + if (word_begin == str.end()) { + word_begin = pos; } - - memcpy(dest, pos, len); - dest += len; + word_end = pos; } -} -/** - * Set the term to filter on. - * @param str Filter term - */ -void StringFilter::SetFilterTerm(const std::string &str) -{ - this->SetFilterTerm(str.c_str()); + /* Add the last word of the string. */ + add_word(); } /** @@ -119,12 +111,12 @@ void StringFilter::AddLine(const char *str) for (WordState &ws : this->word_index) { if (!ws.match) { if (this->locale_aware) { - if (match_case ? StrNaturalContains(str, ws.start) : StrNaturalContainsIgnoreCase(str, ws.start)) { + if (match_case ? StrNaturalContains(str, ws.word) : StrNaturalContainsIgnoreCase(str, ws.word)) { ws.match = true; this->word_matches++; } } else { - if ((match_case ? strstr(str, ws.start) : strcasestr(str, ws.start)) != nullptr) { + if ((match_case ? strstr(str, ws.word.c_str()) : strcasestr(str, ws.word.c_str())) != nullptr) { ws.match = true; this->word_matches++; } diff --git a/src/stringfilter_type.h b/src/stringfilter_type.h index ec7d545fd8..14283addd2 100644 --- a/src/stringfilter_type.h +++ b/src/stringfilter_type.h @@ -31,13 +31,12 @@ struct StringFilter { private: /** State of a single filter word */ struct WordState { - const char *start; ///< Word to filter for. + std::string word; ///< Word to filter for. bool match; ///< Already matched? }; - const char *filter_buffer; ///< Parsed filter string. Words separated by 0. std::vector word_index; ///< Word index and filter state. - uint word_matches; ///< Summary of filter state: Number of words matched. + uint word_matches = 0; ///< Summary of filter state: Number of words matched. const bool *case_sensitive; ///< Match case-sensitively (usually a static variable). bool locale_aware; ///< Match words using the current locale. @@ -47,11 +46,9 @@ public: * Constructor for filter. * @param case_sensitive Pointer to a (usually static) variable controlling the case-sensitivity. nullptr means always case-insensitive. */ - StringFilter(const bool *case_sensitive = nullptr, bool locale_aware = true) : filter_buffer(nullptr), word_matches(0), case_sensitive(case_sensitive), locale_aware(locale_aware) {} - ~StringFilter() { free(this->filter_buffer); } + StringFilter(const bool *case_sensitive = nullptr, bool locale_aware = true) : case_sensitive(case_sensitive), locale_aware(locale_aware) {} - void SetFilterTerm(const char *str); - void SetFilterTerm(const std::string &str); + void SetFilterTerm(std::string_view str); /** * Check whether any filter words were entered.