Revert IPF string handling optimisations

When reverting this revert (after 1.18 has branched), the
new .hpp file should be added to XCode and CodeBlocks.

This reverts PR #8396, PR #8407 and the "Small fixup" commit;
they bumped the minimum compiler version from GCC 7 to 8 or 11.

This reverts commit 870eeff8e6.
This reverts commit 029136b19a.
This reverts commit d74ff6f31b.
This reverts commit 8a6de95d48.
This reverts commit d14d233d79.
This reverts commit 698f5c115c.
This commit is contained in:
Steve Cotton 2024-02-19 09:14:05 +01:00 committed by Steve Cotton
parent e0df67656a
commit e14d7ddee9
6 changed files with 141 additions and 167 deletions

View file

@ -38,7 +38,7 @@ std::vector<std::string> images_general;
void gather_images(const config& from, std::vector<std::string>& to)
{
const auto& im = utils::parenthetical_split(from["images"].str(), ',');
const auto& im = utils::parenthetical_split(from["images"], ',');
to.insert(to.end(), im.begin(), im.end());
}

View file

@ -23,7 +23,6 @@
#include "log.hpp"
#include "serialization/string_utils.hpp"
#include "team.hpp"
#include "utils/from_chars.hpp"
#include "formula/formula.hpp"
#include "formula/callable.hpp"
@ -38,7 +37,10 @@ namespace image {
/** Adds @a mod to the queue (unless mod is nullptr). */
void modification_queue::push(std::unique_ptr<modification> mod)
{
priorities_[mod->priority()].push_back(std::move(mod));
// Null pointers do not get stored. (Shouldn't happen, but just in case.)
if(mod != nullptr) {
priorities_[mod->priority()].push_back(std::move(mod));
}
}
/** Removes the top element from the queue */
@ -76,14 +78,14 @@ modification * modification_queue::top() const
namespace {
/** A function used to parse modification arguments */
using mod_parser = std::function<std::unique_ptr<modification>(std::string_view)>;
using mod_parser = std::function<std::unique_ptr<modification>(const std::string&)>;
/** A map of all registered mod parsers
*
* The mapping is between the modification name and the parser function pointer
* An example of an entry would be "TC" -> &parse_TC_mod
*/
std::map<std::string, mod_parser, std::less<>> mod_parsers;
std::map<std::string, mod_parser> mod_parsers;
/** Decodes a single modification using an appropriate mod_parser
*
@ -94,22 +96,22 @@ std::map<std::string, mod_parser, std::less<>> mod_parsers;
*/
std::unique_ptr<modification> decode_modification(const std::string& encoded_mod)
{
const std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
if(split.size() != 2) {
ERR_DP << "error parsing image modifications: " << encoded_mod;
return nullptr;
}
const std::string& mod_type = split[0];
const std::string& args = split[1];
std::string mod_type = split[0];
std::string args = split[1];
if(const auto parser = mod_parsers.find(mod_type); parser != mod_parsers.end()) {
return std::invoke(parser->second, args);
} else {
if(mod_parsers.find(mod_type) == mod_parsers.end()) {
ERR_DP << "unknown image function in path: " << mod_type;
return nullptr;
}
return mod_parsers[mod_type](args);
}
} // end anon namespace
@ -603,14 +605,14 @@ struct parse_mod_registration
* @param args_var The name for the string argument provided
*/
#define REGISTER_MOD_PARSER(type, args_var) \
static std::unique_ptr<modification> parse_##type##_mod(std::string_view); \
static std::unique_ptr<modification> parse_##type##_mod(const std::string&); \
static parse_mod_registration parse_##type##_mod_registration_aux(#type, &parse_##type##_mod); \
static std::unique_ptr<modification> parse_##type##_mod(std::string_view args_var) \
static std::unique_ptr<modification> parse_##type##_mod(const std::string& args_var) \
// Color-range-based recoloring
REGISTER_MOD_PARSER(TC, args)
{
const auto params = utils::split_view(args,',');
std::vector<std::string> params = utils::split(args,',');
if(params.size() < 2) {
ERR_DP << "too few arguments passed to the ~TC() function";
@ -618,9 +620,9 @@ REGISTER_MOD_PARSER(TC, args)
return nullptr;
}
const int side_n = utils::from_chars<int>(params[0]).value_or(-1);
const int side_n = lexical_cast_default<int>(params[0], -1);
if(side_n < 1) {
ERR_DP << "Invalid side (" << params[0] << ") passed to the ~TC() function";
ERR_DP << "Invalid side (" << side_n << ") passed to the ~TC() function";
return nullptr;
}
@ -653,7 +655,7 @@ REGISTER_MOD_PARSER(TC, args)
// Team-color-based color range selection and recoloring
REGISTER_MOD_PARSER(RC, args)
{
const auto recolor_params = utils::split_view(args,'>');
const std::vector<std::string> recolor_params = utils::split(args,'>');
if(recolor_params.size() <= 1) {
return nullptr;
@ -682,7 +684,7 @@ REGISTER_MOD_PARSER(RC, args)
// Palette switch
REGISTER_MOD_PARSER(PAL, args)
{
const auto remap_params = utils::split_view(args,'>');
const std::vector<std::string> remap_params = utils::split(args,'>');
if(remap_params.size() < 2) {
ERR_DP << "not enough arguments passed to the ~PAL() function: " << args;
@ -723,23 +725,28 @@ REGISTER_MOD_PARSER(FL, args)
// Rotations
REGISTER_MOD_PARSER(ROTATE, args)
{
const auto slice_params = utils::split_view(args, ',', utils::STRIP_SPACES);
const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
const std::size_t s = slice_params.size();
switch(slice_params.size()) {
switch(s) {
case 0:
return std::make_unique<rotate_modification>();
break;
case 1:
return std::make_unique<rotate_modification>(
utils::from_chars<int>(slice_params[0]).value_or(0));
lexical_cast_default<int>(slice_params[0]));
break;
case 2:
return std::make_unique<rotate_modification>(
utils::from_chars<int>(slice_params[0]).value_or(0),
utils::from_chars<int>(slice_params[1]).value_or(0));
lexical_cast_default<int>(slice_params[0]),
lexical_cast_default<int>(slice_params[1]));
break;
case 3:
return std::make_unique<rotate_modification>(
utils::from_chars<int>(slice_params[0]).value_or(0),
utils::from_chars<int>(slice_params[1]).value_or(0),
utils::from_chars<int>(slice_params[2]).value_or(0));
lexical_cast_default<int>(slice_params[0]),
lexical_cast_default<int>(slice_params[1]),
lexical_cast_default<int>(slice_params[2]));
break;
}
return nullptr;
}
@ -756,35 +763,28 @@ REGISTER_MOD_PARSER(CROP_TRANSPARENCY, )
return std::make_unique<crop_transparency_modification>();
}
// TODO: should this be made a more general util function?
bool in_range(int val, int min, int max)
{
return min <= val && val <= max;
}
// Black and white
REGISTER_MOD_PARSER(BW, args)
{
const auto params = utils::split_view(args, ',');
const std::vector<std::string>& params = utils::split(args, ',');
if(params.size() != 1) {
ERR_DP << "~BW() requires exactly one argument";
return nullptr;
}
// TODO: maybe get this directly as uint8_t?
const auto threshold = utils::from_chars<int>(params[0]);
if(!threshold) {
try {
int threshold = std::stoi(params[0]);
if(threshold < 0 || threshold > 255) {
ERR_DP << "~BW() argument out of range 0 - 255";
return nullptr;
} else {
return std::make_unique<bw_modification>(threshold);
}
} catch (const std::invalid_argument&) {
ERR_DP << "unsupported argument in ~BW() function";
return nullptr;
}
if(!in_range(*threshold, 0, 255)) {
ERR_DP << "~BW() argument out of range 0 - 255";
return nullptr;
}
return std::make_unique<bw_modification>(*threshold);
}
// Sepia
@ -796,42 +796,51 @@ REGISTER_MOD_PARSER(SEPIA, )
// Negative
REGISTER_MOD_PARSER(NEG, args)
{
const auto params = utils::split_view(args, ',');
const std::vector<std::string>& params = utils::split(args, ',');
switch(params.size()) {
case 0:
// apparently -1 may be a magic number but this is the threshold
// value required to fully invert a channel
return std::make_unique<negative_modification>(-1, -1, -1);
case 1: {
const auto threshold = utils::from_chars<int>(params[0]);
if(threshold && in_range(*threshold, -1, 255)) {
return std::make_unique<negative_modification>(*threshold, *threshold, *threshold);
} else {
ERR_DP << "unsupported argument value in ~NEG() function";
switch (params.size()) {
case 0:
// apparently -1 may be a magic number
// but this is the threshold value required
// to fully invert a channel
return std::make_unique<negative_modification>(-1, -1, -1);
break;
case 1:
try {
int threshold = std::stoi(params[0]);
if(threshold < -1 || threshold > 255) {
ERR_DP << "unsupported argument value in ~NEG() function";
return nullptr;
} else {
return std::make_unique<negative_modification>(threshold, threshold, threshold);
}
} catch (const std::invalid_argument&) {
ERR_DP << "unsupported argument value in ~NEG() function";
return nullptr;
}
break;
case 3:
try {
int thresholdRed = std::stoi(params[0]);
int thresholdGreen = std::stoi(params[1]);
int thresholdBlue = std::stoi(params[2]);
if(thresholdRed < -1 || thresholdRed > 255 || thresholdGreen < -1 || thresholdGreen > 255 || thresholdBlue < -1 || thresholdBlue > 255) {
ERR_DP << "unsupported argument value in ~NEG() function";
return nullptr;
} else {
return std::make_unique<negative_modification>(thresholdRed, thresholdGreen, thresholdBlue);
}
} catch (const std::invalid_argument&) {
ERR_DP << "unsupported argument value in ~NEG() function";
return nullptr;
}
break;
default:
ERR_DP << "~NEG() requires 0, 1 or 3 arguments";
return nullptr;
}
}
case 3: {
const auto thR = utils::from_chars<int>(params[0]);
const auto thG = utils::from_chars<int>(params[1]);
const auto thB = utils::from_chars<int>(params[2]);
if(thR && thG && thB && in_range(*thR, -1, 255) && in_range(*thG, -1, 255) && in_range(*thB, -1, 255)) {
return std::make_unique<negative_modification>(*thR, *thG, *thB);
} else {
ERR_DP << "unsupported argument value in ~NEG() function";
return nullptr;
}
}
default:
ERR_DP << "~NEG() requires 0, 1 or 3 arguments";
return nullptr;
}
return nullptr;
}
// Plot Alpha
@ -879,7 +888,7 @@ REGISTER_MOD_PARSER(CHAN, args)
// Color-shift
REGISTER_MOD_PARSER(CS, args)
{
const auto factors = utils::split_view(args, ',');
std::vector<std::string> const factors = utils::split(args, ',');
const std::size_t s = factors.size();
if(s == 0) {
@ -889,13 +898,13 @@ REGISTER_MOD_PARSER(CS, args)
int r = 0, g = 0, b = 0;
r = utils::from_chars<int>(factors[0]).value_or(0);
r = lexical_cast_default<int>(factors[0]);
if(s > 1 ) {
g = utils::from_chars<int>(factors[0]).value_or(0);
g = lexical_cast_default<int>(factors[1]);
}
if(s > 2 ) {
b = utils::from_chars<int>(factors[0]).value_or(0);
b = lexical_cast_default<int>(factors[2]);
}
return std::make_unique<cs_modification>(r, g , b);
@ -904,7 +913,7 @@ REGISTER_MOD_PARSER(CS, args)
// Color blending
REGISTER_MOD_PARSER(BLEND, args)
{
const auto params = utils::split_view(args, ',');
const std::vector<std::string>& params = utils::split(args, ',');
if(params.size() != 4) {
ERR_DP << "~BLEND() requires exactly 4 arguments";
@ -912,29 +921,29 @@ REGISTER_MOD_PARSER(BLEND, args)
}
float opacity = 0.0f;
const std::string_view& opacity_str = params[3];
const std::string_view::size_type p100_pos = opacity_str.find('%');
const std::string& opacity_str = params[3];
const std::string::size_type p100_pos = opacity_str.find('%');
if(p100_pos == std::string::npos)
opacity = lexical_cast_default<float>(opacity_str);
else {
// make multiplier
const std::string_view parsed_field = opacity_str.substr(0, p100_pos);
const std::string& parsed_field = opacity_str.substr(0, p100_pos);
opacity = lexical_cast_default<float>(parsed_field);
opacity /= 100.0f;
}
return std::make_unique<blend_modification>(
utils::from_chars<int>(params[0]).value_or(0),
utils::from_chars<int>(params[1]).value_or(0),
utils::from_chars<int>(params[2]).value_or(0),
lexical_cast_default<int>(params[0]),
lexical_cast_default<int>(params[1]),
lexical_cast_default<int>(params[2]),
opacity);
}
// Crop/slice
REGISTER_MOD_PARSER(CROP, args)
{
const auto slice_params = utils::split_view(args, ',', utils::STRIP_SPACES);
const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
const std::size_t s = slice_params.size();
if(s == 0 || (s == 1 && slice_params[0].empty())) {
@ -944,16 +953,16 @@ REGISTER_MOD_PARSER(CROP, args)
SDL_Rect slice_rect { 0, 0, 0, 0 };
slice_rect.x = utils::from_chars<int16_t>(slice_params[0]).value_or(0);
slice_rect.x = lexical_cast_default<int16_t, const std::string&>(slice_params[0]);
if(s > 1) {
slice_rect.y = utils::from_chars<int16_t>(slice_params[1]).value_or(0);
slice_rect.y = lexical_cast_default<int16_t, const std::string&>(slice_params[1]);
}
if(s > 2) {
slice_rect.w = utils::from_chars<uint16_t>(slice_params[2]).value_or(0);
slice_rect.w = lexical_cast_default<uint16_t, const std::string&>(slice_params[2]);
}
if(s > 3) {
slice_rect.h = utils::from_chars<uint16_t>(slice_params[3]).value_or(0);
slice_rect.h = lexical_cast_default<uint16_t, const std::string&>(slice_params[3]);
}
return std::make_unique<crop_modification>(slice_rect);
@ -986,8 +995,8 @@ REGISTER_MOD_PARSER(BLIT, args)
int x = 0, y = 0;
if(s == 3) {
x = utils::from_chars<int>(param[1]).value_or(0);
y = utils::from_chars<int>(param[2]).value_or(0);
x = lexical_cast_default<int>(param[1]);
y = lexical_cast_default<int>(param[2]);
}
const image::locator img(param[0]);
@ -1014,8 +1023,8 @@ REGISTER_MOD_PARSER(MASK, args)
int x = 0, y = 0;
if(s == 3) {
x = utils::from_chars<int>(param[1]).value_or(0);
y = utils::from_chars<int>(param[2]).value_or(0);
x = lexical_cast_default<int>(param[1]);
y = lexical_cast_default<int>(param[2]);
}
if(x < 0 || y < 0) {
@ -1041,16 +1050,17 @@ REGISTER_MOD_PARSER(L, args)
return nullptr;
}
surface surf = get_surface(std::string{args}); // FIXME: string_view for image::locator::value
surface surf = get_surface(args);
return std::make_unique<light_modification>(surf);
}
namespace
{
/** Common helper function to parse scaling IPF inputs. */
std::optional<point> parse_scale_args(std::string_view args)
std::optional<point> parse_scale_args(const std::string& args)
{
const auto scale_params = utils::split_view(args, ',', utils::STRIP_SPACES);
const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
const std::size_t s = scale_params.size();
if(s == 0 || (s == 1 && scale_params[0].empty())) {
@ -1058,10 +1068,10 @@ std::optional<point> parse_scale_args(std::string_view args)
}
point size{0, 0};
size.x = utils::from_chars<int>(scale_params[0]).value_or(0);
size.x = lexical_cast_default<int, const std::string&>(scale_params[0]);
if(s > 1) {
size.y = utils::from_chars<int>(scale_params[1]).value_or(0);
size.y = lexical_cast_default<int, const std::string&>(scale_params[1]);
}
return size;
@ -1117,7 +1127,7 @@ REGISTER_MOD_PARSER(SCALE_INTO_SHARP, args)
// xBRZ
REGISTER_MOD_PARSER(XBRZ, args)
{
int z = utils::from_chars<int>(args).value_or(0);
int z = lexical_cast_default<int, const std::string &>(args);
if(z < 1 || z > 5) {
z = 5; //only values 2 - 5 are permitted for xbrz scaling factors.
}
@ -1130,7 +1140,7 @@ REGISTER_MOD_PARSER(XBRZ, args)
// Gaussian-like blur
REGISTER_MOD_PARSER(BL, args)
{
const int depth = std::max<int>(0, utils::from_chars<int>(args).value_or(0));
const int depth = std::max<int>(0, lexical_cast_default<int>(args));
return std::make_unique<bl_modification>(depth);
}
@ -1140,11 +1150,11 @@ REGISTER_MOD_PARSER(O, args)
const std::string::size_type p100_pos = args.find('%');
float num = 0.0f;
if(p100_pos == std::string::npos) {
num = lexical_cast_default<float, std::string_view>(args);
num = lexical_cast_default<float,const std::string&>(args);
} else {
// make multiplier
const std::string_view parsed_field = args.substr(0, p100_pos);
num = lexical_cast_default<float, std::string_view>(parsed_field);
const std::string parsed_field = args.substr(0, p100_pos);
num = lexical_cast_default<float,const std::string&>(parsed_field);
num /= 100.0f;
}
@ -1158,22 +1168,22 @@ REGISTER_MOD_PARSER(O, args)
// Red component color-shift
REGISTER_MOD_PARSER(R, args)
{
const int r = utils::from_chars<int>(args).value_or(0);
return std::make_unique<cs_modification>(r, 0, 0);
const int r = lexical_cast_default<int>(args);
return std::make_unique<cs_modification>(r,0,0);
}
// Green component color-shift
REGISTER_MOD_PARSER(G, args)
{
const int g = utils::from_chars<int>(args).value_or(0);
return std::make_unique<cs_modification>(0, g, 0);
const int g = lexical_cast_default<int>(args);
return std::make_unique<cs_modification>(0,g,0);
}
// Blue component color-shift
REGISTER_MOD_PARSER(B, args)
{
const int b = utils::from_chars<int>(args).value_or(0);
return std::make_unique<cs_modification>(0, 0, b);
const int b = lexical_cast_default<int>(args);
return std::make_unique<cs_modification>(0,0,b);
}
REGISTER_MOD_PARSER(NOP, )
@ -1198,10 +1208,10 @@ REGISTER_MOD_PARSER(RIGHT, )
REGISTER_MOD_PARSER(BG, args)
{
int c[4] { 0, 0, 0, SDL_ALPHA_OPAQUE };
const auto factors = utils::split_view(args, ',');
std::vector<std::string> factors = utils::split(args, ',');
for(int i = 0; i < std::min<int>(factors.size(), 4); ++i) {
c[i] = utils::from_chars<int>(factors[i]).value_or(0);
c[i] = lexical_cast_default<int>(factors[i]);
}
return std::make_unique<background_modification>(color_t(c[0], c[1], c[2], c[3]));
@ -1210,7 +1220,7 @@ REGISTER_MOD_PARSER(BG, args)
// Channel swap
REGISTER_MOD_PARSER(SWAP, args)
{
const auto params = utils::split_view(args, ',', utils::STRIP_SPACES);
std::vector<std::string> params = utils::split(args, ',', utils::STRIP_SPACES);
// accept 3 arguments (rgb) or 4 (rgba)
if(params.size() != 3 && params.size() != 4) {

View file

@ -355,7 +355,7 @@ static int intf_name_generator(lua_State *L)
if(lua_istable(L, 2)) {
input = lua_check<std::vector<std::string>>(L, 2);
} else {
input = utils::parenthetical_split(luaW_checktstring(L, 2).str(), ',');
input = utils::parenthetical_split(luaW_checktstring(L, 2), ',');
}
int chain_sz = luaL_optinteger(L, 3, 2);
int max_len = luaL_optinteger(L, 4, 12);

View file

@ -99,15 +99,6 @@ std::set<std::string> split_set(std::string_view s, char sep, const int flags)
return res;
}
std::vector<std::string_view> split_view(std::string_view s, const char sep, const int flags)
{
std::vector<std::string_view> res;
split_foreach(s, sep, flags, [&](std::string_view item) {
res.push_back(item);
});
return res;
}
std::vector<std::string> square_parenthetical_split(const std::string& val,
const char separator, const std::string& left,
const std::string& right,const int flags)
@ -308,16 +299,19 @@ std::map<std::string, std::string> map_split(
return res;
}
std::vector<std::string> parenthetical_split(std::string_view val,
const char separator, std::string_view left,
std::string_view right,const int flags)
std::vector<std::string> parenthetical_split(const std::string& val,
const char separator, const std::string& left,
const std::string& right,const int flags)
{
std::vector< std::string > res;
std::vector<char> part;
bool in_parenthesis = false;
std::string_view::const_iterator i1 = val.begin();
std::string_view::const_iterator i2;
std::string lp=left;
std::string rp=right;
std::string::const_iterator i1 = val.begin();
std::string::const_iterator i2;
if (flags & STRIP_SPACES) {
while (i1 != val.end() && portable_isspace(*i1))
++i1;
@ -361,8 +355,8 @@ std::vector<std::string> parenthetical_split(std::string_view val,
continue;
}
bool found=false;
for(std::size_t i=0; i < left.size(); i++){
if (*i2 == left[i]){
for(std::size_t i=0; i < lp.size(); i++){
if (*i2 == lp[i]){
if (!separator && part.empty()){
std::string new_val(i1, i2);
if (flags & STRIP_SPACES)
@ -373,7 +367,7 @@ std::vector<std::string> parenthetical_split(std::string_view val,
}else{
++i2;
}
part.push_back(right[i]);
part.push_back(rp[i]);
found=true;
break;
}

View file

@ -94,12 +94,12 @@ void split_foreach(std::string_view s, char sep, const int flags, const F& f)
});
}
/** Splits a (comma-)separated string into a vector of pieces. */
std::vector<std::string> split(std::string_view val, const char c = ',', const int flags = REMOVE_EMPTY | STRIP_SPACES);
std::set<std::string> split_set(std::string_view val, const char c = ',', const int flags = REMOVE_EMPTY | STRIP_SPACES);
std::vector<std::string_view> split_view(std::string_view val, const char c = ',', const int flags = REMOVE_EMPTY | STRIP_SPACES);
/**
* This function is identical to split(), except it does not split when it otherwise would if the
* previous character was identical to the parameter 'quote' (i.e. it does not split quoted commas).
@ -149,10 +149,10 @@ std::map<std::string, std::string> map_split(
* RETURNS: {"a", "b", "c", "d", "e", "f{g}", "h"}
*/
std::vector< std::string > parenthetical_split(
std::string_view val,
const std::string& val,
const char separator = 0,
std::string_view left = "(",
std::string_view right = ")",
const std::string& left = "(",
const std::string& right = ")",
const int flags = REMOVE_EMPTY | STRIP_SPACES);
/**

View file

@ -1,30 +0,0 @@
/*
Copyright (C) 2024
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include <charconv>
#include <optional>
#include <string_view>
namespace utils
{
template<typename T>
std::optional<T> from_chars(std::string_view str, int base = 10)
{
static_assert(std::is_integral_v<T>, "Float support for charconv incomplete on current build requirements");
T result {};
const auto [_, ec] = std::from_chars(str.data(), str.data() + str.size(), result, base);
return ec == std::errc{} ? std::make_optional(result) : std::nullopt;
}
} // namespace utils