diff --git a/src/about.cpp b/src/about.cpp index 5ab83a54612..add2081fc8b 100644 --- a/src/about.cpp +++ b/src/about.cpp @@ -38,7 +38,7 @@ std::vector images_general; void gather_images(const config& from, std::vector& 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()); } diff --git a/src/image_modifications.cpp b/src/image_modifications.cpp index 26186749284..c5533d1ca9f 100644 --- a/src/image_modifications.cpp +++ b/src/image_modifications.cpp @@ -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 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::string_view)>; +using mod_parser = std::function(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> mod_parsers; +std::map mod_parsers; /** Decodes a single modification using an appropriate mod_parser * @@ -94,22 +96,22 @@ std::map> mod_parsers; */ std::unique_ptr decode_modification(const std::string& encoded_mod) { - const std::vector split = utils::parenthetical_split(encoded_mod); + std::vector 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 parse_##type##_mod(std::string_view); \ + static std::unique_ptr parse_##type##_mod(const std::string&); \ static parse_mod_registration parse_##type##_mod_registration_aux(#type, &parse_##type##_mod); \ - static std::unique_ptr parse_##type##_mod(std::string_view args_var) \ + static std::unique_ptr 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 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(params[0]).value_or(-1); + const int side_n = lexical_cast_default(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 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 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& 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(); + break; case 1: return std::make_unique( - utils::from_chars(slice_params[0]).value_or(0)); + lexical_cast_default(slice_params[0])); + break; case 2: return std::make_unique( - utils::from_chars(slice_params[0]).value_or(0), - utils::from_chars(slice_params[1]).value_or(0)); + lexical_cast_default(slice_params[0]), + lexical_cast_default(slice_params[1])); + break; case 3: return std::make_unique( - utils::from_chars(slice_params[0]).value_or(0), - utils::from_chars(slice_params[1]).value_or(0), - utils::from_chars(slice_params[2]).value_or(0)); + lexical_cast_default(slice_params[0]), + lexical_cast_default(slice_params[1]), + lexical_cast_default(slice_params[2])); + break; } return nullptr; } @@ -756,35 +763,28 @@ REGISTER_MOD_PARSER(CROP_TRANSPARENCY, ) return std::make_unique(); } -// 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& 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(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(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(*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& 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(-1, -1, -1); - - case 1: { - const auto threshold = utils::from_chars(params[0]); - - if(threshold && in_range(*threshold, -1, 255)) { - return std::make_unique(*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(-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(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(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(params[0]); - const auto thG = utils::from_chars(params[1]); - const auto thB = utils::from_chars(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(*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 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(factors[0]).value_or(0); + r = lexical_cast_default(factors[0]); if(s > 1 ) { - g = utils::from_chars(factors[0]).value_or(0); + g = lexical_cast_default(factors[1]); } if(s > 2 ) { - b = utils::from_chars(factors[0]).value_or(0); + b = lexical_cast_default(factors[2]); } return std::make_unique(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& 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(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(parsed_field); opacity /= 100.0f; } return std::make_unique( - utils::from_chars(params[0]).value_or(0), - utils::from_chars(params[1]).value_or(0), - utils::from_chars(params[2]).value_or(0), + lexical_cast_default(params[0]), + lexical_cast_default(params[1]), + lexical_cast_default(params[2]), opacity); } // Crop/slice REGISTER_MOD_PARSER(CROP, args) { - const auto slice_params = utils::split_view(args, ',', utils::STRIP_SPACES); + const std::vector& 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(slice_params[0]).value_or(0); + slice_rect.x = lexical_cast_default(slice_params[0]); if(s > 1) { - slice_rect.y = utils::from_chars(slice_params[1]).value_or(0); + slice_rect.y = lexical_cast_default(slice_params[1]); } if(s > 2) { - slice_rect.w = utils::from_chars(slice_params[2]).value_or(0); + slice_rect.w = lexical_cast_default(slice_params[2]); } if(s > 3) { - slice_rect.h = utils::from_chars(slice_params[3]).value_or(0); + slice_rect.h = lexical_cast_default(slice_params[3]); } return std::make_unique(slice_rect); @@ -986,8 +995,8 @@ REGISTER_MOD_PARSER(BLIT, args) int x = 0, y = 0; if(s == 3) { - x = utils::from_chars(param[1]).value_or(0); - y = utils::from_chars(param[2]).value_or(0); + x = lexical_cast_default(param[1]); + y = lexical_cast_default(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(param[1]).value_or(0); - y = utils::from_chars(param[2]).value_or(0); + x = lexical_cast_default(param[1]); + y = lexical_cast_default(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(surf); } namespace { /** Common helper function to parse scaling IPF inputs. */ -std::optional parse_scale_args(std::string_view args) +std::optional parse_scale_args(const std::string& args) { - const auto scale_params = utils::split_view(args, ',', utils::STRIP_SPACES); + const std::vector& 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 parse_scale_args(std::string_view args) } point size{0, 0}; - size.x = utils::from_chars(scale_params[0]).value_or(0); + size.x = lexical_cast_default(scale_params[0]); if(s > 1) { - size.y = utils::from_chars(scale_params[1]).value_or(0); + size.y = lexical_cast_default(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(args).value_or(0); + int z = lexical_cast_default(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(0, utils::from_chars(args).value_or(0)); + const int depth = std::max(0, lexical_cast_default(args)); return std::make_unique(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(args); + num = lexical_cast_default(args); } else { // make multiplier - const std::string_view parsed_field = args.substr(0, p100_pos); - num = lexical_cast_default(parsed_field); + const std::string parsed_field = args.substr(0, p100_pos); + num = lexical_cast_default(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(args).value_or(0); - return std::make_unique(r, 0, 0); + const int r = lexical_cast_default(args); + return std::make_unique(r,0,0); } // Green component color-shift REGISTER_MOD_PARSER(G, args) { - const int g = utils::from_chars(args).value_or(0); - return std::make_unique(0, g, 0); + const int g = lexical_cast_default(args); + return std::make_unique(0,g,0); } // Blue component color-shift REGISTER_MOD_PARSER(B, args) { - const int b = utils::from_chars(args).value_or(0); - return std::make_unique(0, 0, b); + const int b = lexical_cast_default(args); + return std::make_unique(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 factors = utils::split(args, ','); for(int i = 0; i < std::min(factors.size(), 4); ++i) { - c[i] = utils::from_chars(factors[i]).value_or(0); + c[i] = lexical_cast_default(factors[i]); } return std::make_unique(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 params = utils::split(args, ',', utils::STRIP_SPACES); // accept 3 arguments (rgb) or 4 (rgba) if(params.size() != 3 && params.size() != 4) { diff --git a/src/scripting/lua_kernel_base.cpp b/src/scripting/lua_kernel_base.cpp index 29b22ddb55b..495abd42699 100644 --- a/src/scripting/lua_kernel_base.cpp +++ b/src/scripting/lua_kernel_base.cpp @@ -355,7 +355,7 @@ static int intf_name_generator(lua_State *L) if(lua_istable(L, 2)) { input = lua_check>(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); diff --git a/src/serialization/string_utils.cpp b/src/serialization/string_utils.cpp index 2ba2d5460ad..3e41a573774 100644 --- a/src/serialization/string_utils.cpp +++ b/src/serialization/string_utils.cpp @@ -99,15 +99,6 @@ std::set split_set(std::string_view s, char sep, const int flags) return res; } -std::vector split_view(std::string_view s, const char sep, const int flags) -{ - std::vector res; - split_foreach(s, sep, flags, [&](std::string_view item) { - res.push_back(item); - }); - return res; -} - std::vector 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 map_split( return res; } -std::vector parenthetical_split(std::string_view val, - const char separator, std::string_view left, - std::string_view right,const int flags) +std::vector 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 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 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 parenthetical_split(std::string_view val, }else{ ++i2; } - part.push_back(right[i]); + part.push_back(rp[i]); found=true; break; } diff --git a/src/serialization/string_utils.hpp b/src/serialization/string_utils.hpp index 76b45cc0c61..21df7044283 100644 --- a/src/serialization/string_utils.hpp +++ b/src/serialization/string_utils.hpp @@ -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 split(std::string_view val, const char c = ',', const int flags = REMOVE_EMPTY | STRIP_SPACES); std::set split_set(std::string_view val, const char c = ',', const int flags = REMOVE_EMPTY | STRIP_SPACES); -std::vector 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 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); /** diff --git a/src/utils/from_chars.hpp b/src/utils/from_chars.hpp deleted file mode 100644 index 0e7e666c7ce..00000000000 --- a/src/utils/from_chars.hpp +++ /dev/null @@ -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 -#include -#include - -namespace utils -{ -template -std::optional from_chars(std::string_view str, int base = 10) -{ - static_assert(std::is_integral_v, "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