1
0
Fork 0

Codechange: use std::string_view to parse setting values

pull/8505/merge
Rubidium 2025-04-21 22:21:52 +02:00 committed by rubidium42
parent 12118b201c
commit fdb8defa64
4 changed files with 57 additions and 61 deletions

View File

@ -23,6 +23,7 @@
#include "stdafx.h"
#include <charconv>
#include "core/string_consumer.hpp"
#include "settings_table.h"
#include "debug.h"
#include "currency.h"
@ -178,24 +179,21 @@ enum IniFileVersion : uint32_t {
const uint16_t INIFILE_VERSION = (IniFileVersion)(IFV_MAX_VERSION - 1); ///< Current ini-file version of OpenTTD.
/**
* Find the index value of a ONEofMANY type in a string separated by |
* Find the index value of a ONEofMANY type in a string
* @param str the current value of the setting for which a value needs found
* @param len length of the string
* @param many full domain of values the ONEofMANY setting can have
* @return the integer index of the full-list, or SIZE_MAX if not found
* @return the integer index of the full-list, or std::nullopt if not found
*/
size_t OneOfManySettingDesc::ParseSingleValue(const char *str, size_t len, const std::vector<std::string> &many)
std::optional<uint32_t> OneOfManySettingDesc::ParseSingleValue(std::string_view str, const std::vector<std::string> &many)
{
StringConsumer consumer{str};
auto digit = consumer.TryReadIntegerBase<uint32_t>(10);
/* check if it's an integer */
if (isdigit(*str)) return std::strtoul(str, nullptr, 0);
if (digit.has_value()) return digit;
size_t idx = 0;
for (const auto &one : many) {
if (one.size() == len && strncmp(one.c_str(), str, len) == 0) return idx;
idx++;
}
return SIZE_MAX;
auto it = std::ranges::find(many, str);
if (it == many.end()) return std::nullopt;
return static_cast<uint32_t>(it - many.begin());
}
/**
@ -204,10 +202,10 @@ size_t OneOfManySettingDesc::ParseSingleValue(const char *str, size_t len, const
* @param str the current value of the setting for which a value needs found.
* @return Either true/false, or nullopt if no boolean value found.
*/
std::optional<bool> BoolSettingDesc::ParseSingleValue(const char *str)
std::optional<bool> BoolSettingDesc::ParseSingleValue(std::string_view str)
{
if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return true;
if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return false;
if (str == "true" || str == "on" || str == "1") return true;
if (str == "false" || str == "off" || str == "0") return false;
return std::nullopt;
}
@ -216,29 +214,26 @@ std::optional<bool> BoolSettingDesc::ParseSingleValue(const char *str)
* Find the set-integer value MANYofMANY type in a string
* @param many full domain of values the MANYofMANY setting can have
* @param str the current string value of the setting, each individual
* of separated by a whitespace,tab or | character
* @return the 'fully' set integer, or SIZE_MAX if a set is not found
* of separated by a whitespace, tab or | character
* @return the 'fully' set integer, or std::nullopt if a set is not found
*/
static size_t LookupManyOfMany(const std::vector<std::string> &many, const char *str)
static std::optional<uint32_t> LookupManyOfMany(const std::vector<std::string> &many, std::string_view str)
{
const char *s;
size_t r;
size_t res = 0;
static const std::string_view separators{" \t|"};
for (;;) {
uint32_t res = 0;
StringConsumer consumer{str};
while (consumer.AnyBytesLeft()) {
/* skip "whitespace" */
while (*str == ' ' || *str == '\t' || *str == '|') str++;
if (*str == 0) break;
consumer.SkipUntilCharNotIn(separators);
s = str;
while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++;
std::string_view value = consumer.ReadUntilCharIn(separators);
if (value.empty()) break;
r = OneOfManySettingDesc::ParseSingleValue(str, s - str, many);
if (r == SIZE_MAX) return r;
auto r = OneOfManySettingDesc::ParseSingleValue(value, many);
if (!r.has_value()) return r;
SetBit(res, (uint8_t)r); // value found, set it
if (*s == 0) break;
str = s + 1;
SetBit(res, *r); // value found, set it
}
return res;
}
@ -378,31 +373,32 @@ std::string ManyOfManySettingDesc::FormatValue(const void *object) const
* @param str Input string that will be parsed based on the type of desc.
* @return The value from the parse string, or the default value of the setting.
*/
size_t IntSettingDesc::ParseValue(const char *str) const
int32_t IntSettingDesc::ParseValue(std::string_view str) const
{
char *end;
size_t val = std::strtoul(str, &end, 0);
if (end == str) {
StringConsumer consumer{str};
/* The actual settings value might be int32 or uint32. Read as int64 and just cast away the high bits. */
auto value = consumer.TryReadIntegerBase<int64_t>(10);
if (!value.has_value()) {
_settings_error_list.emplace_back(
GetEncodedString(STR_CONFIG_ERROR),
GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName()));
return this->GetDefaultValue();
}
if (*end != '\0') {
if (consumer.AnyBytesLeft()) {
_settings_error_list.emplace_back(
GetEncodedString(STR_CONFIG_ERROR),
GetEncodedString(STR_CONFIG_ERROR_TRAILING_CHARACTERS, this->GetName()));
}
return val;
return static_cast<int32_t>(*value);
}
size_t OneOfManySettingDesc::ParseValue(const char *str) const
int32_t OneOfManySettingDesc::ParseValue(std::string_view str) const
{
size_t r = OneOfManySettingDesc::ParseSingleValue(str, strlen(str), this->many);
auto r = OneOfManySettingDesc::ParseSingleValue(str, this->many);
/* if the first attempt of conversion from string to the appropriate value fails,
* look if we have defined a converter from old value to new value. */
if (r == SIZE_MAX && this->many_cnvt != nullptr) r = this->many_cnvt(str);
if (r != SIZE_MAX) return r; // and here goes converted value
if (!r.has_value() && this->many_cnvt != nullptr) r = this->many_cnvt(str);
if (r.has_value()) return *r; // and here goes converted value
_settings_error_list.emplace_back(
GetEncodedString(STR_CONFIG_ERROR),
@ -410,10 +406,10 @@ size_t OneOfManySettingDesc::ParseValue(const char *str) const
return this->GetDefaultValue();
}
size_t ManyOfManySettingDesc::ParseValue(const char *str) const
int32_t ManyOfManySettingDesc::ParseValue(std::string_view str) const
{
size_t r = LookupManyOfMany(this->many, str);
if (r != SIZE_MAX) return r;
auto r = LookupManyOfMany(this->many, str);
if (r.has_value()) return *r;
_settings_error_list.emplace_back(
GetEncodedString(STR_CONFIG_ERROR),
@ -421,7 +417,7 @@ size_t ManyOfManySettingDesc::ParseValue(const char *str) const
return this->GetDefaultValue();
}
size_t BoolSettingDesc::ParseValue(const char *str) const
int32_t BoolSettingDesc::ParseValue(std::string_view str) const
{
auto r = BoolSettingDesc::ParseSingleValue(str);
if (r.has_value()) return *r;
@ -673,8 +669,8 @@ static void IniLoadSettings(IniFile &ini, const SettingTable &settings_table, co
void IntSettingDesc::ParseValue(const IniItem *item, void *object) const
{
size_t val = (item == nullptr) ? this->GetDefaultValue() : this->ParseValue(item->value.has_value() ? item->value->c_str() : "");
this->MakeValueValidAndWrite(object, (int32_t)val);
int32_t val = (item != nullptr && item->value.has_value()) ? this->ParseValue(*item->value) : this->GetDefaultValue();
this->MakeValueValidAndWrite(object, val);
}
void StringSettingDesc::ParseValue(const IniItem *item, void *object) const
@ -760,7 +756,7 @@ std::string BoolSettingDesc::FormatValue(const void *object) const
bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const
{
int32_t item_value = (int32_t)this->ParseValue(item->value->c_str());
int32_t item_value = static_cast<int32_t>(this->ParseValue(*item->value));
int32_t object_value = this->Read(object);
return item_value == object_value;
}
@ -1413,13 +1409,13 @@ void LoadFromConfig(bool startup)
const IniItem *old_item;
if (generic_version < IFV_GAME_TYPE && IsConversionNeeded(generic_ini, "network", "server_advertise", "server_game_type", &old_item)) {
auto old_value = BoolSettingDesc::ParseSingleValue(old_item->value->c_str());
auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value);
_settings_client.network.server_game_type = old_value.value_or(false) ? SERVER_GAME_TYPE_PUBLIC : SERVER_GAME_TYPE_LOCAL;
}
if (generic_version < IFV_AUTOSAVE_RENAME && IsConversionNeeded(generic_ini, "gui", "autosave", "autosave_interval", &old_item)) {
static std::vector<std::string> _old_autosave_interval{"off", "monthly", "quarterly", "half year", "yearly"};
auto old_value = OneOfManySettingDesc::ParseSingleValue(old_item->value->c_str(), old_item->value->size(), _old_autosave_interval);
auto old_value = OneOfManySettingDesc::ParseSingleValue(*old_item->value, _old_autosave_interval).value_or(-1);
switch (old_value) {
case 0: _settings_client.gui.autosave_interval = 0; break;
@ -1433,7 +1429,7 @@ void LoadFromConfig(bool startup)
/* Persist the right click close option from older versions. */
if (generic_version < IFV_RIGHT_CLICK_CLOSE && IsConversionNeeded(generic_ini, "gui", "right_mouse_wnd_close", "right_click_wnd_close", &old_item)) {
auto old_value = BoolSettingDesc::ParseSingleValue(old_item->value->c_str());
auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value);
_settings_client.gui.right_click_wnd_close = old_value.value_or(false) ? RCC_YES : RCC_NO;
}

View File

@ -235,7 +235,7 @@ struct IntSettingDesc : SettingDesc {
void ChangeValue(const void *object, int32_t newvalue) const;
void MakeValueValidAndWrite(const void *object, int32_t value) const;
virtual size_t ParseValue(const char *str) const;
virtual int32_t ParseValue(std::string_view str) const;
std::string FormatValue(const void *object) const override;
void ParseValue(const IniItem *item, void *object) const override;
bool IsSameValue(const IniItem *item, void *object) const override;
@ -258,16 +258,16 @@ struct BoolSettingDesc : IntSettingDesc {
IntSettingDesc(save, flags, startup, def ? 1 : 0, 0, 1, 0, str, str_help, str_val, cat,
pre_check, post_callback, get_title_cb, get_help_cb, get_value_params_cb, get_def_cb, nullptr) {}
static std::optional<bool> ParseSingleValue(const char *str);
static std::optional<bool> ParseSingleValue(std::string_view str);
bool IsBoolSetting() const override { return true; }
size_t ParseValue(const char *str) const override;
int32_t ParseValue(std::string_view str) const override;
std::string FormatValue(const void *object) const override;
};
/** One of many setting. */
struct OneOfManySettingDesc : IntSettingDesc {
typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error
typedef std::optional<uint32_t> OnConvert(std::string_view value); ///< callback prototype for conversion error
template <ConvertibleThroughBaseOrTo<int32_t> Tdef, ConvertibleThroughBaseOrTo<uint32_t> Tmax>
OneOfManySettingDesc(const SaveLoad &save, SettingFlags flags, bool startup, Tdef def,
@ -284,10 +284,10 @@ struct OneOfManySettingDesc : IntSettingDesc {
std::vector<std::string> many; ///< possible values for this type
OnConvert *many_cnvt; ///< callback procedure when loading value mechanism fails
static size_t ParseSingleValue(const char *str, size_t len, const std::vector<std::string> &many);
static std::optional<uint32_t> ParseSingleValue(std::string_view str, const std::vector<std::string> &many);
std::string FormatSingleValue(uint id) const;
size_t ParseValue(const char *str) const override;
int32_t ParseValue(std::string_view str) const override;
std::string FormatValue(const void *object) const override;
};
@ -302,7 +302,7 @@ struct ManyOfManySettingDesc : OneOfManySettingDesc {
OneOfManySettingDesc(save, flags, startup, def, (1 << many.size()) - 1, str, str_help,
str_val, cat, pre_check, post_callback, get_title_cb, get_help_cb, get_value_params_cb, get_def_cb, many, many_cnvt) {}
size_t ParseValue(const char *str) const override;
int32_t ParseValue(std::string_view str) const override;
std::string FormatValue(const void *object) const override;
};

View File

@ -445,11 +445,11 @@ static bool CheckRoadSide(int32_t &)
* @param value that was read from config file
* @return the "hopefully" converted value
*/
static size_t ConvertLandscape(const char *value)
static std::optional<uint32_t> ConvertLandscape(std::string_view value)
{
/* try with the old values */
static std::vector<std::string> _old_landscape_values{"normal", "hilly", "desert", "candy"};
return OneOfManySettingDesc::ParseSingleValue(value, strlen(value), _old_landscape_values);
return OneOfManySettingDesc::ParseSingleValue(value, _old_landscape_values);
}
static bool CheckFreeformEdges(int32_t &new_value)

View File

@ -10,7 +10,7 @@
#include "table/strings.h"
/* Callback function used in _settings[] as well as _company_settings[] */
static size_t ConvertLandscape(const char *value);
static std::optional<uint32_t> ConvertLandscape(std::string_view value);
static StringID SettingTitleWallclock(const IntSettingDesc &sd);
static StringID SettingHelpWallclock(const IntSettingDesc &sd);