From f5ffd4789bca560dc7910b94a287c9dfd3513eb7 Mon Sep 17 00:00:00 2001 From: frosch Date: Fri, 28 Mar 2025 17:38:11 +0100 Subject: [PATCH] Codechange: Use StringConsumer in FormatString. --- src/newgrf_text.cpp | 6 +- src/strings.cpp | 146 +++++++++++++++++++---------------------- src/strings_internal.h | 3 +- 3 files changed, 73 insertions(+), 82 deletions(-) diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index a23d94a988..7d17af6b13 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -861,10 +861,10 @@ static void ProcessNewGRFStringControlCode(char32_t scc, StringConsumer &consume /** * Emit OpenTTD's internal string code for the different NewGRF string codes. * @param scc NewGRF string code. - * @param[in,out] str String iterator, moved forward if SCC_NEWGRF_PUSH_WORD is found. + * @param consumer String consumer, moved forward if SCC_NEWGRF_PUSH_WORD is found. * @returns String code to use. */ -char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str) +char32_t RemapNewGRFStringControlCode(char32_t scc, StringConsumer &consumer) { switch (scc) { default: @@ -932,7 +932,7 @@ char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str) /* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */ case SCC_NEWGRF_PUSH_WORD: - Utf8Consume(str); + consumer.SkipUtf8(); return 0; case SCC_NEWGRF_DISCARD_WORD: diff --git a/src/strings.cpp b/src/strings.cpp index 438af68e8a..d0d65f9d40 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -644,7 +644,7 @@ static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *sp * @param plural_form The plural form we want an index for. * @return The plural index for the given form. */ -static int DeterminePluralForm(int64_t count, int plural_form) +static int DeterminePluralForm(int64_t count, uint plural_form) { /* The absolute value determines plurality */ uint64_t n = abs(count); @@ -765,22 +765,25 @@ static int DeterminePluralForm(int64_t count, int plural_form) } } -static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder) +static void ParseStringChoice(StringConsumer &consumer, uint form, StringBuilder &builder) { /* {Length of each string} {each string} */ - uint n = (uint8_t)*b++; - size_t form_offset = 0, form_len = 0, total_len = 0; + uint n = consumer.ReadUint8(); + size_t form_pre = 0, form_len = 0, form_post = 0; for (uint i = 0; i != n; i++) { - uint len = (uint8_t)*b++; - if (i == form) { - form_offset = total_len; + uint len = consumer.ReadUint8(); + if (i < form) { + form_pre += len; + } else if (i > form) { + form_post += len; + } else { form_len = len; } - total_len += len; } - builder += std::string_view(b + form_offset, form_len); - return b + total_len; + consumer.Skip(form_pre); + builder += consumer.Read(form_len); + consumer.Skip(form_post); } /** Helper for unit conversion. */ @@ -982,67 +985,59 @@ uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type) /** * Decodes an encoded string during FormatString. - * @param str The buffer of the encoded string. + * @param consumer The encoded string. * @param game_script Set if decoding a GameScript-encoded string. This affects how string IDs are handled. * @param builder The string builder to write the string to. - * @returns Updated position position in input buffer. */ -static const char *DecodeEncodedString(const char *str, bool game_script, StringBuilder &builder) +static void DecodeEncodedString(StringConsumer &consumer, bool game_script, StringBuilder &builder) { std::vector sub_args; - char *p; - StringIndexInTab id(std::strtoul(str, &p, 16)); - if (*p != SCC_RECORD_SEPARATOR && *p != '\0') { - while (*p != '\0') p++; + StringIndexInTab id(consumer.ReadIntegerBase(16)); + if (consumer.AnyBytesLeft() && !consumer.ReadUtf8If(SCC_RECORD_SEPARATOR)) { + consumer.SkipAll(); builder += "(invalid SCC_ENCODED)"; - return p; + return; } if (game_script && id >= TAB_SIZE_GAMESCRIPT) { - while (*p != '\0') p++; + consumer.SkipAll(); builder += "(invalid StringID)"; - return p; + return; } - while (*p != '\0') { - /* The start of parameter. */ - const char *s = ++p; + while (consumer.AnyBytesLeft()) { + StringConsumer record(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::SKIP_ONE_SEPARATOR)); - /* Find end of the parameter. */ - for (; *p != '\0' && *p != SCC_RECORD_SEPARATOR; ++p) {} - - if (s == p) { + if (!record.AnyBytesLeft()) { /* This is an empty parameter. */ sub_args.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: { - uint64_t param = std::strtoull(s, &p, 16); + uint64_t param = record.ReadIntegerBase(16); if (param >= TAB_SIZE_GAMESCRIPT) { - while (*p != '\0') p++; builder += "(invalid sub-StringID)"; - return p; + return; } + assert(!record.AnyBytesLeft()); param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, StringIndexInTab(param)); sub_args.emplace_back(param); break; } case SCC_ENCODED_NUMERIC: { - uint64_t param = std::strtoull(s, &p, 16); + uint64_t param = record.ReadIntegerBase(16); + assert(!record.AnyBytesLeft()); sub_args.emplace_back(param); break; } case SCC_ENCODED_STRING: { - sub_args.emplace_back(std::string(s, p - s)); + sub_args.emplace_back(std::string(record.Read(StringConsumer::npos))); break; } @@ -1055,8 +1050,6 @@ static const char *DecodeEncodedString(const char *str, bool game_script, String StringID stringid = game_script ? MakeStringID(TEXT_TAB_GAMESCRIPT_START, id) : StringID{id.base()}; GetStringWithArgs(builder, stringid, sub_args, true); - - return p; } /** @@ -1087,13 +1080,12 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin } uint next_substr_case_index = 0; struct StrStackItem { - const char *str; - const char *end; + StringConsumer consumer; size_t first_param_offset; uint case_index; StrStackItem(std::string_view view, size_t first_param_offset, uint case_index) - : str(view.data()), end(view.data() + view.size()), first_param_offset(first_param_offset), case_index(case_index) + : consumer(view), first_param_offset(first_param_offset), case_index(case_index) {} }; std::stack> str_stack; @@ -1101,19 +1093,19 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin for (;;) { try { - while (!str_stack.empty() && str_stack.top().str >= str_stack.top().end) { + while (!str_stack.empty() && !str_stack.top().consumer.AnyBytesLeft()) { str_stack.pop(); } if (str_stack.empty()) break; - const char *&str = str_stack.top().str; + StringConsumer &consumer = str_stack.top().consumer; const size_t ref_param_offset = str_stack.top().first_param_offset; const uint case_index = str_stack.top().case_index; - char32_t b = Utf8Consume(&str); + char32_t b = consumer.ReadUtf8(); assert(b != 0); if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) { /* We need to pass some stuff as it might be modified. */ - b = RemapNewGRFStringControlCode(b, &str); + b = RemapNewGRFStringControlCode(b, consumer); if (b == 0) continue; } @@ -1126,13 +1118,13 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin switch (b) { case SCC_ENCODED: case SCC_ENCODED_INTERNAL: - str = DecodeEncodedString(str, b == SCC_ENCODED, builder); + DecodeEncodedString(consumer, b == SCC_ENCODED, builder); break; case SCC_NEWGRF_STRINL: { - StringID substr = Utf8Consume(&str); + StringID substr = consumer.ReadUtf8(STR_NULL); std::string_view ptr = GetStringPtr(substr); - str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str" + str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "consumer" next_substr_case_index = 0; break; } @@ -1140,15 +1132,15 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin case SCC_NEWGRF_PRINT_WORD_STRING_ID: { StringID substr = args.GetNextParameter(); std::string_view ptr = GetStringPtr(substr); - str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str" + str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "consumer" next_substr_case_index = 0; break; } case SCC_GENDER_LIST: { // {G 0 Der Die Das} /* First read the meta data from the language file. */ - size_t offset = ref_param_offset + (uint8_t)*str++; - int gender = 0; + size_t offset = ref_param_offset + consumer.ReadUint8(); + uint8_t gender = 0; if (offset >= args.GetNumParameters()) { /* The offset may come from an external NewGRF, and be invalid. */ builder += "(invalid GENDER parameter)"; @@ -1170,37 +1162,38 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin FormatString(tmp_builder, input, tmp_params); } - /* The gender is stored at the start of the formatted string. */ - const char *s = buffer.c_str(); - char32_t c = Utf8Consume(&s); - /* Does this string have a gender, if so, set it */ - if (c == SCC_GENDER_INDEX) gender = (uint8_t)s[0]; + /* The gender is stored at the start of the formatted string. + * Does this string have a gender, if so, set it. */ + StringConsumer gender_consumer(buffer); + if (gender_consumer.ReadUtf8If(SCC_GENDER_INDEX)) { + gender = gender_consumer.ReadUint8(); + } } - str = ParseStringChoice(str, gender, builder); + ParseStringChoice(consumer, gender, builder); break; } /* This sets up the gender for the string. * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */ - case SCC_GENDER_INDEX: // {GENDER 0} + case SCC_GENDER_INDEX: { // {GENDER 0} + uint8_t gender = consumer.ReadUint8(); if (_scan_for_gender_data) { builder.PutUtf8(SCC_GENDER_INDEX); - builder.PutUint8(*str++); - } else { - str++; + builder.PutUint8(gender); } break; + } case SCC_PLURAL_LIST: { // {P} - int plural_form = *str++; // contains the plural form for this string - size_t offset = ref_param_offset + (uint8_t)*str++; + uint8_t plural_form = consumer.ReadUint8(); // contains the plural form for this string + size_t offset = ref_param_offset + consumer.ReadUint8(); const uint64_t *v = nullptr; /* The offset may come from an external NewGRF, and be invalid. */ if (offset < args.GetNumParameters()) { v = std::get_if(&args.GetParam(offset)); // contains the number that determines plural } if (v != nullptr) { - str = ParseStringChoice(str, DeterminePluralForm(static_cast(*v), plural_form), builder); + ParseStringChoice(consumer, DeterminePluralForm(static_cast(*v), plural_form), builder); } else { builder += "(invalid PLURAL parameter)"; } @@ -1208,38 +1201,35 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin } case SCC_ARG_INDEX: { // Move argument pointer - args.SetOffset(ref_param_offset + (uint8_t)*str++); + args.SetOffset(ref_param_offset + consumer.ReadUint8()); break; } case SCC_SET_CASE: { // {SET_CASE} /* This is a pseudo command, it's outputted when someone does {STRING.ack} * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */ - next_substr_case_index = (uint8_t)*str++; + next_substr_case_index = consumer.ReadUint8(); break; } case SCC_SWITCH_CASE: { // {Used to implement case switching} /* <0x9E> * Each LEN is printed using 2 bytes in little endian order. */ - uint num = (uint8_t)*str++; + uint num = consumer.ReadUint8(); std::optional found; for (; num > 0; --num) { - uint8_t index = static_cast(str[0]); - uint16_t len = static_cast(str[1]) + (static_cast(str[2]) << 8); - str += 3; + uint8_t index = consumer.ReadUint8(); + uint16_t len = consumer.ReadUint16LE(); + auto case_str = consumer.Read(len); if (index == case_index) { /* Found the case */ - found.emplace(str, len); + found = case_str; } - str += len; } - uint16_t default_len = static_cast(str[0]) + (static_cast(str[1]) << 8); - str += 2; - if (!found.has_value()) found.emplace(str, default_len); - str += default_len; - assert(str <= str_stack.top().end); - str_stack.emplace(*found, ref_param_offset, case_index); // this may invalidate "str" + uint16_t default_len = consumer.ReadUint16LE(); + auto default_str = consumer.Read(default_len); + if (!found.has_value()) found = default_str; + str_stack.emplace(*found, ref_param_offset, case_index); // this may invalidate "consumer" break; } diff --git a/src/strings_internal.h b/src/strings_internal.h index 47e4329bad..c15a09a0c3 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -13,6 +13,7 @@ #include "strings_func.h" #include "string_func.h" #include "core/string_builder.hpp" +#include "core/string_consumer.hpp" class StringParameters { protected: @@ -214,6 +215,6 @@ void GenerateTownNameString(StringBuilder &builder, size_t lang, uint32_t seed); void GetTownName(StringBuilder &builder, const struct Town *t); void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed); -char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str); +char32_t RemapNewGRFStringControlCode(char32_t scc, StringConsumer &consumer); #endif /* STRINGS_INTERNAL_H */