1
0
Fork 0

Codechange: Use StringBuilder to create encoded strings.

pull/13983/head
frosch 2025-04-04 20:08:25 +02:00 committed by frosch
parent 128e0fcde2
commit dd073eb38d
4 changed files with 44 additions and 48 deletions

View File

@ -29,6 +29,7 @@
#include "../window_func.h"
#include "../strings_func.h"
#include "../core/endian_func.hpp"
#include "../core/string_builder.hpp"
#include "../vehicle_base.h"
#include "../company_func.h"
#include "../timer/timer_game_economy.h"
@ -928,7 +929,7 @@ void FixSCCEncoded(std::string &str, bool fix_code)
* `:"<STRING>"` becomes `<RS><SCC_ENCODED_STRING><STRING>`
*/
std::string result;
auto output = std::back_inserter(result);
StringBuilder builder(result);
bool is_encoded = false; // Set if we determine by the presence of SCC_ENCODED that the string is an encoded string.
bool in_string = false; // Set if we in a string, between double-quotes.
@ -940,11 +941,11 @@ void FixSCCEncoded(std::string &str, bool fix_code)
char32_t c;
Utf8Decode(&c, &*it);
it += len;
if (c == SCC_ENCODED || (fix_code && (c == 0xE028 || c == 0xE02A))) {
Utf8Encode(output, SCC_ENCODED);
builder.PutUtf8(SCC_ENCODED);
need_type = false;
is_encoded = true;
it += len;
continue;
}
@ -955,27 +956,24 @@ void FixSCCEncoded(std::string &str, bool fix_code)
in_string = !in_string;
if (in_string && need_type) {
/* Started a new string parameter. */
Utf8Encode(output, SCC_ENCODED_STRING);
builder.PutUtf8(SCC_ENCODED_STRING);
need_type = false;
}
it += len;
continue;
}
if (!in_string && c == ':') {
*output = SCC_RECORD_SEPARATOR;
builder.PutUtf8(SCC_RECORD_SEPARATOR);
need_type = true;
it += len;
continue;
}
if (need_type) {
/* Started a new numeric parameter. */
Utf8Encode(output, SCC_ENCODED_NUMERIC);
builder.PutUtf8(SCC_ENCODED_NUMERIC);
need_type = false;
}
Utf8Encode(output, c);
it += len;
builder.PutUtf8(c);
}
str = std::move(result);

View File

@ -163,9 +163,9 @@ EncodedString ScriptText::GetEncodedText()
ParamList params;
int param_count = 0;
std::string result;
auto output = std::back_inserter(result);
StringBuilder builder(result);
this->_FillParamList(params, seen_texts);
this->_GetEncodedText(output, param_count, params, true);
this->_GetEncodedText(builder, param_count, params, true);
if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string)));
return ::EncodedString{std::move(result)};
}
@ -193,46 +193,46 @@ void ScriptText::_FillParamList(ParamList &params, ScriptTextList &seen_texts)
}
}
void ScriptText::ParamCheck::Encode(std::back_insert_iterator<std::string> &output, std::string_view cmd)
void ScriptText::ParamCheck::Encode(StringBuilder &builder, std::string_view cmd)
{
if (this->cmd.empty()) this->cmd = cmd;
if (this->used) return;
struct visitor {
std::back_insert_iterator<std::string> &output;
StringBuilder &builder;
void operator()(std::string value)
{
Utf8Encode(this->output, SCC_ENCODED_STRING);
this->builder.PutUtf8(SCC_ENCODED_STRING);
StrMakeValidInPlace(value, {StringValidationSetting::ReplaceWithQuestionMark, StringValidationSetting::AllowNewline, StringValidationSetting::ReplaceTabCrNlWithSpace});
fmt::format_to(this->output, "{}", value);
this->builder.Put(value);
}
void operator()(const SQInteger &value)
{
Utf8Encode(this->output, SCC_ENCODED_NUMERIC);
fmt::format_to(this->output, "{:X}", value);
this->builder.PutUtf8(SCC_ENCODED_NUMERIC);
this->builder.PutIntegerBase(value, 16);
}
void operator()(const ScriptTextRef &value)
{
Utf8Encode(this->output, SCC_ENCODED);
fmt::format_to(this->output, "{:X}", value->string);
this->builder.PutUtf8(SCC_ENCODED);
this->builder.PutIntegerBase(value->string.base(), 16);
}
};
*output = SCC_RECORD_SEPARATOR;
std::visit(visitor{output}, *this->param);
builder.PutUtf8(SCC_RECORD_SEPARATOR);
std::visit(visitor{builder}, *this->param);
this->used = true;
}
void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output, int &param_count, ParamSpan args, bool first)
void ScriptText::_GetEncodedText(StringBuilder &builder, int &param_count, ParamSpan args, bool first)
{
const std::string &name = GetGameStringName(this->string);
if (first) {
Utf8Encode(output, SCC_ENCODED);
fmt::format_to(output, "{:X}", this->string);
builder.PutUtf8(SCC_ENCODED);
builder.PutIntegerBase(this->string.base(), 16);
}
const StringParams &params = GetGameStringParams(this->string);
@ -256,7 +256,7 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output,
case StringParam::RAW_STRING:
{
ParamCheck &p = *get_next_arg();
p.Encode(output, cur_param.cmd);
p.Encode(builder, cur_param.cmd);
if (p.cmd != cur_param.cmd) throw 1;
if (!std::holds_alternative<std::string>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a raw string", name, param_count + 1, cur_param.cmd));
break;
@ -265,7 +265,7 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output,
case StringParam::STRING:
{
ParamCheck &p = *get_next_arg();
p.Encode(output, cur_param.cmd);
p.Encode(builder, cur_param.cmd);
if (p.cmd != cur_param.cmd) throw 1;
if (!std::holds_alternative<ScriptTextRef>(*p.param)) {
ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a GSText", name, param_count + 1, cur_param.cmd));
@ -274,12 +274,12 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output,
}
int count = 0;
ScriptTextRef &ref = std::get<ScriptTextRef>(*p.param);
ref->_GetEncodedText(output, count, args.subspan(idx), false);
ref->_GetEncodedText(builder, count, args.subspan(idx), false);
if (++count != cur_param.consumes) {
ScriptLog::Warning(fmt::format("{}({}): {{{}}} expects {} to be consumed, but {} consumes {}", name, param_count + 1, cur_param.cmd, cur_param.consumes - 1, GetGameStringName(ref->string), count - 1));
/* Fill missing params if needed. */
for (int i = count; i < cur_param.consumes; i++) {
Utf8Encode(output, SCC_RECORD_SEPARATOR);
builder.PutUtf8(SCC_RECORD_SEPARATOR);
}
}
skip_args(cur_param.consumes - 1);
@ -289,7 +289,7 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output,
default:
for (int i = 0; i < cur_param.consumes; i++) {
ParamCheck &p = *get_next_arg();
p.Encode(output, i == 0 ? cur_param.cmd : "");
p.Encode(builder, i == 0 ? cur_param.cmd : "");
if (i == 0 && p.cmd != cur_param.cmd) throw 1;
if (!std::holds_alternative<SQInteger>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects an integer", name, param_count + i + 1, cur_param.cmd));
}

View File

@ -12,6 +12,7 @@
#include "script_object.hpp"
#include "../../strings_func.h"
#include "../../core/string_builder.hpp"
#include <variant>
@ -141,7 +142,7 @@ private:
ParamCheck(StringIndexInTab owner, int idx, Param *param) : owner(owner), idx(idx), param(param) {}
void Encode(std::back_insert_iterator<std::string> &output, std::string_view cmd);
void Encode(StringBuilder &output, std::string_view cmd);
};
using ParamList = std::vector<ParamCheck>;
@ -168,7 +169,7 @@ private:
* @param args The parameters to be consumed.
* @param first Whether it's the first call in the recursion.
*/
void _GetEncodedText(std::back_insert_iterator<std::string> &output, int &param_count, ParamSpan args, bool first);
void _GetEncodedText(StringBuilder &output, int &param_count, ParamSpan args, bool first);
/**
* Set a parameter, where the value is the first item on the stack.

View File

@ -103,41 +103,38 @@ EncodedString GetEncodedString(StringID str)
EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params)
{
std::string result;
auto output = std::back_inserter(result);
Utf8Encode(output, SCC_ENCODED_INTERNAL);
fmt::format_to(output, "{:X}", str);
StringBuilder builder(result);
builder.PutUtf8(SCC_ENCODED_INTERNAL);
builder.PutIntegerBase(str, 16);
struct visitor {
std::back_insert_iterator<std::string> &output;
StringBuilder &builder;
void operator()(const std::monostate &) {}
void operator()(const uint64_t &arg)
{
Utf8Encode(output, SCC_ENCODED_NUMERIC);
fmt::format_to(this->output, "{:X}", arg);
this->builder.PutUtf8(SCC_ENCODED_NUMERIC);
this->builder.PutIntegerBase(arg, 16);
}
void operator()(const std::string &value)
{
#ifdef WITH_ASSERT
/* Don't allow an encoded string to contain another encoded string. */
if (!value.empty()) {
char32_t c;
const char *p = value.data();
if (Utf8Decode(&c, p)) {
assert(c != SCC_ENCODED && c != SCC_ENCODED_INTERNAL && c != SCC_RECORD_SEPARATOR);
}
{
auto [len, c] = DecodeUtf8(value);
assert(len == 0 || (c != SCC_ENCODED && c != SCC_ENCODED_INTERNAL && c != SCC_RECORD_SEPARATOR));
}
#endif /* WITH_ASSERT */
Utf8Encode(output, SCC_ENCODED_STRING);
fmt::format_to(this->output, "{}", value);
this->builder.PutUtf8(SCC_ENCODED_STRING);
this->builder += value;
}
};
visitor v{output};
visitor v{builder};
for (const auto &param : params) {
*output = SCC_RECORD_SEPARATOR;
builder.PutUtf8(SCC_RECORD_SEPARATOR);
std::visit(v, param.data);
}