From a372c59483a36d361d9e043dbee2a90ec24fcf69 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 13 May 2023 08:58:24 +0200 Subject: [PATCH] Codechange: replace C-style (stredup) chat completion with std::string_view --- src/network/network_chat_gui.cpp | 73 +++++++++++++++----------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 20fd7cedf9..cf04ebcef9 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -323,18 +323,16 @@ struct NetworkChatWindow : public Window { * Find the next item of the list of things that can be auto-completed. * @param item The current indexed item to return. This function can, and most * likely will, alter item, to skip empty items in the arrays. - * @return Returns the char that matched to the index. + * @return Returns the view that matched to the index. */ - const char *ChatTabCompletionNextItem(uint *item) + std::optional ChatTabCompletionNextItem(uint *item) { - static char chat_tab_temp_buffer[64]; - /* First, try clients */ if (*item < MAX_CLIENT_SLOTS) { /* Skip inactive clients */ for (NetworkClientInfo *ci : NetworkClientInfo::Iterate(*item)) { *item = ci->index; - return ci->client_name.c_str(); + return ci->client_name; } *item = MAX_CLIENT_SLOTS; } @@ -346,12 +344,11 @@ struct NetworkChatWindow : public Window { for (const Town *t : Town::Iterate(*item - MAX_CLIENT_SLOTS)) { /* Get the town-name via the string-system */ SetDParam(0, t->index); - GetString(chat_tab_temp_buffer, STR_TOWN_NAME, lastof(chat_tab_temp_buffer)); - return &chat_tab_temp_buffer[0]; + return GetString(STR_TOWN_NAME); } } - return nullptr; + return std::nullopt; } /** @@ -359,13 +356,14 @@ struct NetworkChatWindow : public Window { * the word right from that as to complete. It also writes a \0 at the * position of the space (if any). If nothing found, buf is returned. */ - static char *ChatTabCompletionFindText(char *buf) + static std::string_view ChatTabCompletionFindText(std::string_view &buf) { - char *p = strrchr(buf, ' '); - if (p == nullptr) return buf; + auto it = buf.find_last_of(' '); + if (it == std::string_view::npos) return buf; - *p = '\0'; - return p + 1; + std::string_view res = buf.substr(it + 1); + buf.remove_suffix(res.size() + 1); + return res; } /** @@ -373,46 +371,44 @@ struct NetworkChatWindow : public Window { */ void ChatTabCompletion() { - static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH]; - assert(this->message_editbox.text.max_bytes == lengthof(_chat_tab_completion_buf)); + static std::string _chat_tab_completion_buf; Textbuf *tb = &this->message_editbox.text; - size_t len, tb_len; - uint item; - char *tb_buf, *pre_buf; - const char *cur_name; + uint item = 0; bool second_scan = false; - item = 0; + /* Create views, so we do not need to copy the data for now. */ + std::string_view pre_buf = _chat_tab_completion_active ? std::string_view(_chat_tab_completion_buf) : std::string_view(tb->buf); + std::string_view tb_buf = ChatTabCompletionFindText(pre_buf); - /* Copy the buffer so we can modify it without damaging the real data */ - pre_buf = (_chat_tab_completion_active) ? stredup(_chat_tab_completion_buf) : stredup(tb->buf); + /* + * Comparing pointers of the data, as both "Hi:" and "Hi: Hi:" will result in + * tb_buf and pre_buf being "Hi:", which would be equal in content but not in context. + */ + bool begin_of_line = tb_buf.data() == pre_buf.data(); - tb_buf = ChatTabCompletionFindText(pre_buf); - tb_len = strlen(tb_buf); - - while ((cur_name = ChatTabCompletionNextItem(&item)) != nullptr) { + std::optional cur_item; + while ((cur_item = ChatTabCompletionNextItem(&item)).has_value()) { + std::string_view cur_name = cur_item.value(); item++; if (_chat_tab_completion_active) { /* We are pressing TAB again on the same name, is there another name * that starts with this? */ if (!second_scan) { - size_t offset; - size_t length; + std::string_view view; /* If we are completing at the begin of the line, skip the ': ' we added */ - if (tb_buf == pre_buf) { - offset = 0; - length = (tb->bytes - 1) - 2; + if (begin_of_line) { + view = std::string_view(tb->buf, (tb->bytes - 1) - 2); } else { /* Else, find the place we are completing at */ - offset = strlen(pre_buf) + 1; - length = (tb->bytes - 1) - offset; + size_t offset = pre_buf.size() + 1; + view = std::string_view(tb->buf + offset, (tb->bytes - 1) - offset); } /* Compare if we have a match */ - if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; + if (cur_name == view) second_scan = true; continue; } @@ -420,21 +416,19 @@ struct NetworkChatWindow : public Window { /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ } - len = strlen(cur_name); - if (tb_len < len && StrStartsWith(cur_name, tb_buf)) { + if (tb_buf.size() < cur_name.size() && StrStartsWith(cur_name, tb_buf)) { /* Save the data it was before completion */ - if (!second_scan) seprintf(_chat_tab_completion_buf, lastof(_chat_tab_completion_buf), "%s", tb->buf); + if (!second_scan) _chat_tab_completion_buf = tb->buf; _chat_tab_completion_active = true; /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ - if (pre_buf == tb_buf) { + if (begin_of_line) { this->message_editbox.text.Assign(fmt::format("{}: ", cur_name)); } else { this->message_editbox.text.Assign(fmt::format("{} {}", pre_buf, cur_name)); } this->SetDirty(); - free(pre_buf); return; } } @@ -446,7 +440,6 @@ struct NetworkChatWindow : public Window { this->SetDirty(); } - free(pre_buf); } Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override