diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 4763321ba9..585c267f9f 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -16,6 +16,7 @@ #include "querystring_gui.h" #include "video/video_driver.hpp" #include "zoom_func.h" +#include "core/string_consumer.hpp" #include "widgets/osk_widget.h" @@ -358,17 +359,10 @@ void GetKeyboardLayout() keyboard[1] = _keyboard_opt[1].empty() ? GetString(STR_OSK_KEYBOARD_LAYOUT_CAPS) : _keyboard_opt[1]; for (uint j = 0; j < 2; j++) { - auto kbd = keyboard[j].begin(); - bool ended = false; + StringConsumer consumer(keyboard[j]); for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { - _keyboard[j][i] = Utf8Consume(kbd); - /* Be lenient when the last characters are missing (is quite normal) */ - if (_keyboard[j][i] == '\0' || ended) { - ended = true; - _keyboard[j][i] = ' '; - continue; - } + _keyboard[j][i] = consumer.AnyBytesLeft() ? consumer.ReadUtf8() : ' '; if (IsPrintable(_keyboard[j][i])) { errormark[j] += ' '; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 5b743ba20d..9c7bcc32c6 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -30,6 +30,7 @@ #include "../strings_func.h" #include "../core/endian_func.hpp" #include "../core/string_builder.hpp" +#include "../core/string_consumer.hpp" #include "../vehicle_base.h" #include "../company_func.h" #include "../timer/timer_game_economy.h" @@ -935,13 +936,14 @@ void FixSCCEncoded(std::string &str, bool fix_code) bool in_string = false; // Set if we in a string, between double-quotes. bool need_type = true; // Set if a parameter type needs to be emitted. - for (auto it = std::begin(str); it != std::end(str); /* nothing */) { - size_t len = Utf8EncodedCharLen(*it); - if (len == 0 || it + len > std::end(str)) break; - + StringConsumer consumer(str); + while (consumer.AnyBytesLeft()) { char32_t c; - Utf8Decode(&c, &*it); - it += len; + if (auto r = consumer.TryReadUtf8(); r.has_value()) { + c = *r; + } else { + break; + } if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) { builder.PutUtf8(SCC_ENCODED); need_type = false; diff --git a/src/strings.cpp b/src/strings.cpp index 68a59c0c9b..438af68e8a 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -39,8 +39,8 @@ #include "core/backup_type.hpp" #include "gfx_layout.h" #include "core/utf8.hpp" +#include "core/string_consumer.hpp" #include -#include #include "table/strings.h" #include "table/control_codes.h" @@ -153,48 +153,39 @@ EncodedString EncodedString::ReplaceParam(size_t param, StringParameter &&data) if (this->empty()) return {}; std::vector params; + StringConsumer consumer(this->string); - /* We need char * for std::from_chars. Iterate the underlying data, as string's own iterators may interfere. */ - const char *p = this->string.data(); - const char *e = this->string.data() + this->string.length(); - - char32_t c = Utf8Consume(p); - if (c != SCC_ENCODED_INTERNAL) return {}; + if (!consumer.ReadUtf8If(SCC_ENCODED_INTERNAL)) return {}; StringID str; - auto result = std::from_chars(p, e, str, 16); - if (result.ec != std::errc()) return {}; - if (result.ptr != e && *result.ptr != SCC_RECORD_SEPARATOR) return {}; - p = result.ptr; + if (auto r = consumer.TryReadIntegerBase(16); r.has_value()) { + str = *r; + } else { + return {}; + } + if (consumer.AnyBytesLeft() && !consumer.ReadUtf8If(SCC_RECORD_SEPARATOR)) return {}; - while (p != e) { - auto s = ++p; + while (consumer.AnyBytesLeft()) { + StringConsumer record(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::SKIP_ONE_SEPARATOR)); - /* Find end of the parameter. */ - for (; p != e && *p != SCC_RECORD_SEPARATOR; ++p) {} - - if (s == p) { + if (!record.AnyBytesLeft()) { /* This is an empty parameter. */ params.emplace_back(std::monostate{}); continue; } /* Get the parameter type. */ - char32_t parameter_type; - size_t len = Utf8Decode(¶meter_type, s); - s += len; - + char32_t parameter_type = record.ReadUtf8(); switch (parameter_type) { case SCC_ENCODED_NUMERIC: { - uint64_t value; - result = std::from_chars(s, p, value, 16); - if (result.ec != std::errc() || result.ptr != p) return {}; + uint64_t value = record.ReadIntegerBase(16); + assert(!record.AnyBytesLeft()); params.emplace_back(value); break; } case SCC_ENCODED_STRING: { - params.emplace_back(std::string(s, p)); + params.emplace_back(std::string(record.Read(StringConsumer::npos))); break; }