diff --git a/doc/lua_api.md b/doc/lua_api.md index e3829aba8..ca9338271 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5459,7 +5459,7 @@ Utilities You can use `colorspec_to_bytes` to generate raw RGBA values. Palettes are not supported at the moment. You may use this to procedurally generate textures during server init. -* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a +* `minetest.urlencode(str)`: Encodes reserved URI characters by a percent sign followed by two hex digits. See [RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3). diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua index 30d0804a0..d7620652b 100644 --- a/games/devtest/mods/unittests/misc.lua +++ b/games/devtest/mods/unittests/misc.lua @@ -133,6 +133,12 @@ local function test_compress() end unittests.register("test_compress", test_compress) +local function test_urlencode() + -- checks that API code handles null bytes + assert(core.urlencode("foo\000bar!") == "foo%00bar%21") +end +unittests.register("test_urlencode", test_urlencode) + local function test_game_info() local info = minetest.get_game_info() local game_conf = Settings(info.path .. "/game.conf") diff --git a/src/activeobject.h b/src/activeobject.h index 02c16b557..52f997fdf 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -46,7 +46,7 @@ enum ActiveObjectType { struct ActiveObjectMessage { - ActiveObjectMessage(u16 id_, bool reliable_=true, const std::string &data_ = "") : + ActiveObjectMessage(u16 id_, bool reliable_=true, std::string_view data_ = "") : id(id_), reliable(reliable_), datastring(data_) diff --git a/src/chat.cpp b/src/chat.cpp index 4ba074538..5331a87bd 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -577,6 +577,7 @@ void ChatPrompt::historyNext() void ChatPrompt::nickCompletion(const std::set &names, bool backwards) { + const std::wstring_view line(getLineRef()); // Two cases: // (a) m_nick_completion_start == m_nick_completion_end == 0 // Then no previous nick completion is active. @@ -586,7 +587,6 @@ void ChatPrompt::nickCompletion(const std::set &names, bool backwar // m_nick_completion_start..m_nick_completion_end are the // interval where the originally used prefix was. Cycle // through the list of completions of that prefix. - const std::wstring &line = getLineRef(); u32 prefix_start = m_nick_completion_start; u32 prefix_end = m_nick_completion_end; bool initial = (prefix_end == 0); @@ -601,7 +601,7 @@ void ChatPrompt::nickCompletion(const std::set &names, bool backwar if (prefix_start == prefix_end) return; } - std::wstring prefix = line.substr(prefix_start, prefix_end - prefix_start); + auto prefix = line.substr(prefix_start, prefix_end - prefix_start); // find all names that start with the selected prefix std::vector completions; @@ -624,7 +624,7 @@ void ChatPrompt::nickCompletion(const std::set &names, bool backwar { while (word_end < line.size() && !iswspace(line[word_end])) ++word_end; - std::wstring word = line.substr(prefix_start, word_end - prefix_start); + auto word = line.substr(prefix_start, word_end - prefix_start); // cycle through completions for (u32 i = 0; i < completions.size(); ++i) @@ -640,7 +640,7 @@ void ChatPrompt::nickCompletion(const std::set &names, bool backwar } } } - std::wstring replacement = completions[replacement_index]; + const auto &replacement = completions[replacement_index]; if (word_end < line.size() && iswspace(line[word_end])) ++word_end; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 9d3e9fedc..764fac422 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -541,11 +541,9 @@ bool IClientMediaDownloader::checkAndLoad( // Compute actual checksum of data std::string data_sha1; { - SHA1 data_sha1_calculator; - data_sha1_calculator.addBytes(data.c_str(), data.size()); - unsigned char *data_tmpdigest = data_sha1_calculator.getDigest(); - data_sha1.assign((char*) data_tmpdigest, 20); - free(data_tmpdigest); + SHA1 ctx; + ctx.addBytes(data); + data_sha1 = ctx.getDigest(); } // Check that received file matches announced checksum diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index f3d7bf34a..cb9f5440b 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -67,7 +67,7 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os) return !bad; } -bool FileCache::updateByPath(const std::string &path, const std::string &data) +bool FileCache::updateByPath(const std::string &path, std::string_view data) { createDir(); std::ofstream file(path.c_str(), std::ios_base::binary | @@ -80,13 +80,13 @@ bool FileCache::updateByPath(const std::string &path, const std::string &data) return false; } - file.write(data.c_str(), data.length()); + file << data; file.close(); return !file.fail(); } -bool FileCache::update(const std::string &name, const std::string &data) +bool FileCache::update(const std::string &name, std::string_view data) { std::string path = m_dir + DIR_DELIM + name; return updateByPath(path, data); diff --git a/src/client/filecache.h b/src/client/filecache.h index c8d5a781e..c58e71522 100644 --- a/src/client/filecache.h +++ b/src/client/filecache.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include class FileCache { @@ -31,7 +32,7 @@ public: */ FileCache(const std::string &dir) : m_dir(dir) {} - bool update(const std::string &name, const std::string &data); + bool update(const std::string &name, std::string_view data); bool load(const std::string &name, std::ostream &os); bool exists(const std::string &name); @@ -43,5 +44,5 @@ private: void createDir(); bool loadByPath(const std::string &path, std::ostream &os); - bool updateByPath(const std::string &path, const std::string &data); + bool updateByPath(const std::string &path, std::string_view data); }; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 65f135a82..59852763e 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -804,7 +804,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, } void dumpShaderProgram(std::ostream &output_stream, - const std::string &program_type, const std::string &program) + const std::string &program_type, std::string_view program) { output_stream << program_type << " shader program:" << std::endl << "----------------------------------" << std::endl; diff --git a/src/client/shader.h b/src/client/shader.h index 100ab567f..fabb19922 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -192,4 +192,4 @@ public: IWritableShaderSource *createShaderSource(); void dumpShaderProgram(std::ostream &output_stream, - const std::string &program_type, const std::string &program); + const std::string &program_type, std::string_view program); diff --git a/src/client/tile.cpp b/src/client/tile.cpp index c70821151..624e927d8 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -404,7 +404,7 @@ private: // Generate image based on a string like "stone.png" or "[crack:1:0". // if baseimg is NULL, it is created. Otherwise stuff is made on it. // source_image_names is important to determine when to flush the image from a cache (dynamic media) - bool generateImagePart(std::string part_of_name, video::IImage *& baseimg, std::set &source_image_names); + bool generateImagePart(std::string_view part_of_name, video::IImage *& baseimg, std::set &source_image_names); /*! Generates an image from a full string like * "stone.png^mineral_coal.png^[crack:1:0". @@ -412,7 +412,7 @@ private: * The returned Image should be dropped. * source_image_names is important to determine when to flush the image from a cache (dynamic media) */ - video::IImage* generateImage(const std::string &name, std::set &source_image_names); + video::IImage* generateImage(std::string_view name, std::set &source_image_names); // Thread-safe cache of what source images are known (true = known) MutexedMap m_source_image_existence; @@ -593,7 +593,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst, // Brighten image void brighten(video::IImage *image); // Parse a transform name -u32 parseImageTransform(const std::string& s); +u32 parseImageTransform(std::string_view s); // Apply transform to image dimension core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim); // Apply transform to image data @@ -963,7 +963,8 @@ static video::IImage *createInventoryCubeImage( return result; } -video::IImage* TextureSource::generateImage(const std::string &name, std::set &source_image_names) +video::IImage* TextureSource::generateImage(std::string_view name, + std::set &source_image_names) { // Get the base image @@ -1024,15 +1025,15 @@ video::IImage* TextureSource::generateImage(const std::string &name, std::set &source_image_names) { const char escape = '\\'; // same as in generateImage() @@ -1216,8 +1217,9 @@ bool TextureSource::generateImagePart(std::string part_of_name, // Stuff starting with [ are special commands if (part_of_name.empty() || part_of_name[0] != '[') { - source_image_names.insert(part_of_name); - video::IImage *image = m_sourcecache.getOrLoad(part_of_name); + std::string part_s(part_of_name); + source_image_names.insert(part_s); + video::IImage *image = m_sourcecache.getOrLoad(part_s); if (!image) { // Do not create the dummy texture if (part_of_name.empty()) @@ -1516,8 +1518,10 @@ bool TextureSource::generateImagePart(std::string part_of_name, return false; } - str_replace(part_of_name, '&', '^'); - Strfnd sf(part_of_name); + std::string part_s(part_of_name); + str_replace(part_s, '&', '^'); + + Strfnd sf(part_s); sf.next("{"); std::string imagename_top = sf.next("{"); std::string imagename_left = sf.next("{"); @@ -1873,7 +1877,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, else if (str_starts_with(part_of_name, "[png:")) { std::string png; { - std::string blob = part_of_name.substr(5); + auto blob = part_of_name.substr(5); if (!base64_is_valid(blob)) { errorstream << "generateImagePart(): " << "malformed base64 in [png" << std::endl; @@ -2433,7 +2437,7 @@ void brighten(video::IImage *image) } } -u32 parseImageTransform(const std::string& s) +u32 parseImageTransform(std::string_view s) { int total_transform = 0; diff --git a/src/content/mod_configuration.cpp b/src/content/mod_configuration.cpp index ffb075732..9e0461f56 100644 --- a/src/content/mod_configuration.cpp +++ b/src/content/mod_configuration.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "filesys.h" #include "gettext.h" +#include "exceptions.h" std::string ModConfiguration::getUnsatisfiedModsError() const diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 13d104320..ea13a84a1 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -228,7 +228,7 @@ bool ModStorage::contains(const std::string &name) const return m_database->hasModEntry(m_mod_name, name); } -bool ModStorage::setString(const std::string &name, const std::string &var) +bool ModStorage::setString(const std::string &name, std::string_view var) { if (var.empty()) return m_database->removeModEntry(m_mod_name, name); diff --git a/src/content/mods.h b/src/content/mods.h index ce844397a..228684c6e 100644 --- a/src/content/mods.h +++ b/src/content/mods.h @@ -123,7 +123,7 @@ public: bool contains(const std::string &name) const override; - bool setString(const std::string &name, const std::string &var) override; + bool setString(const std::string &name, std::string_view var) override; const StringMap &getStrings(StringMap *place) const override; diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index c1d8189e6..0ec927189 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -239,9 +239,9 @@ std::set getAvailableGameIds() // Add it to result const char *ends[] = {"_game", NULL}; - std::string shorter = removeStringEnd(dln.name, ends); + auto shorter = removeStringEnd(dln.name, ends); if (!shorter.empty()) - gameids.insert(shorter); + gameids.emplace(shorter); else gameids.insert(dln.name); } diff --git a/src/craftdef.cpp b/src/craftdef.cpp index ac04716b4..72b8e8f9d 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., inline bool isGroupRecipeStr(const std::string &rec_name) { - return str_starts_with(rec_name, std::string("group:")); + return str_starts_with(rec_name, "group:"); } static bool hasGroupItem(const std::vector &recipe) diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp index 19cb6e4fa..300de1792 100644 --- a/src/database/database-dummy.cpp +++ b/src/database/database-dummy.cpp @@ -25,7 +25,7 @@ Dummy database class #include "remoteplayer.h" -bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data) +bool Database_Dummy::saveBlock(const v3s16 &pos, std::string_view data) { m_database[getBlockAsInteger(pos)] = data; return true; @@ -128,11 +128,12 @@ bool Database_Dummy::hasModEntry(const std::string &modname, const std::string & } bool Database_Dummy::setModEntry(const std::string &modname, - const std::string &key, const std::string &value) + const std::string &key, std::string_view value) { auto mod_pair = m_mod_storage_database.find(modname); if (mod_pair == m_mod_storage_database.end()) { - m_mod_storage_database[modname] = StringMap({{key, value}}); + auto &map = m_mod_storage_database[modname]; + map[key] = value; } else { mod_pair->second[key] = value; } diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h index ecfa82c34..05f28317b 100644 --- a/src/database/database-dummy.h +++ b/src/database/database-dummy.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include +#include #include #include "database.h" #include "irrlichttypes.h" @@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Database_Dummy : public MapDatabase, public PlayerDatabase, public ModStorageDatabase { public: - bool saveBlock(const v3s16 &pos, const std::string &data); + bool saveBlock(const v3s16 &pos, std::string_view data); void loadBlock(const v3s16 &pos, std::string *block); bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); @@ -43,7 +44,7 @@ public: const std::string &key, std::string *value); bool hasModEntry(const std::string &modname, const std::string &key); bool setModEntry(const std::string &modname, - const std::string &key, const std::string &value); + const std::string &key,std::string_view value); bool removeModEntry(const std::string &modname, const std::string &key); bool removeModEntries(const std::string &modname); void listMods(std::vector *res); diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index 4357b5ea4..d670b3ef0 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -428,13 +428,14 @@ bool ModStorageDatabaseFiles::hasModEntry(const std::string &modname, const std: } bool ModStorageDatabaseFiles::setModEntry(const std::string &modname, - const std::string &key, const std::string &value) + const std::string &key, std::string_view value) { Json::Value *meta = getOrCreateJson(modname); if (!meta) return false; - (*meta)[key] = Json::Value(value); + Json::Value value_v(value.data(), value.data() + value.size()); + (*meta)[key] = std::move(value_v); m_modified.insert(modname); return true; diff --git a/src/database/database-files.h b/src/database/database-files.h index 61a8fd9a2..04a74aa83 100644 --- a/src/database/database-files.h +++ b/src/database/database-files.h @@ -84,7 +84,7 @@ public: const std::string &key, std::string *value); virtual bool hasModEntry(const std::string &modname, const std::string &key); virtual bool setModEntry(const std::string &modname, - const std::string &key, const std::string &value); + const std::string &key, std::string_view value); virtual bool removeModEntry(const std::string &modname, const std::string &key); virtual bool removeModEntries(const std::string &modname); virtual void listMods(std::vector *res); diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp index 6a1325937..71c56f90a 100644 --- a/src/database/database-leveldb.cpp +++ b/src/database/database-leveldb.cpp @@ -53,10 +53,11 @@ Database_LevelDB::Database_LevelDB(const std::string &savedir) m_database.reset(db); } -bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data) +bool Database_LevelDB::saveBlock(const v3s16 &pos, std::string_view data) { + leveldb::Slice data_s(data.data(), data.size()); leveldb::Status status = m_database->Put(leveldb::WriteOptions(), - i64tos(getBlockAsInteger(pos)), data); + i64tos(getBlockAsInteger(pos)), data_s); if (!status.ok()) { warningstream << "saveBlock: LevelDB error saving block " << pos << ": " << status.ToString() << std::endl; diff --git a/src/database/database-leveldb.h b/src/database/database-leveldb.h index 812752a67..d6a57b66b 100644 --- a/src/database/database-leveldb.h +++ b/src/database/database-leveldb.h @@ -34,7 +34,7 @@ public: Database_LevelDB(const std::string &savedir); ~Database_LevelDB() = default; - bool saveBlock(const v3s16 &pos, const std::string &data); + bool saveBlock(const v3s16 &pos, std::string_view data); void loadBlock(const v3s16 &pos, std::string *block); bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index 7b6ecc934..667d7afbb 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -223,7 +223,7 @@ void MapDatabasePostgreSQL::initStatements() "SELECT posX, posY, posZ FROM blocks"); } -bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data) +bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, std::string_view data) { // Verify if we don't overflow the platform integer with the mapblock size if (data.size() > INT_MAX) { @@ -240,7 +240,7 @@ bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data) y = htonl(pos.Y); z = htonl(pos.Z); - const void *args[] = { &x, &y, &z, data.c_str() }; + const void *args[] = { &x, &y, &z, data.data() }; const int argLen[] = { sizeof(x), sizeof(y), sizeof(z), (int)data.size() }; @@ -940,11 +940,11 @@ bool ModStorageDatabasePostgreSQL::hasModEntry(const std::string &modname, } bool ModStorageDatabasePostgreSQL::setModEntry(const std::string &modname, - const std::string &key, const std::string &value) + const std::string &key, std::string_view value) { verifyDatabase(); - const void *args[] = { modname.c_str(), key.c_str(), value.c_str() }; + const void *args[] = { modname.c_str(), key.c_str(), value.data() }; const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX), diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h index 56b3d6b53..28ff1c35f 100644 --- a/src/database/database-postgresql.h +++ b/src/database/database-postgresql.h @@ -120,7 +120,7 @@ public: MapDatabasePostgreSQL(const std::string &connect_string); virtual ~MapDatabasePostgreSQL() = default; - bool saveBlock(const v3s16 &pos, const std::string &data); + bool saveBlock(const v3s16 &pos, std::string_view data); void loadBlock(const v3s16 &pos, std::string *block); bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); @@ -186,7 +186,7 @@ public: bool getModEntry(const std::string &modname, const std::string &key, std::string *value); bool hasModEntry(const std::string &modname, const std::string &key); bool setModEntry(const std::string &modname, - const std::string &key, const std::string &value); + const std::string &key, std::string_view value); bool removeModEntry(const std::string &modname, const std::string &key); bool removeModEntries(const std::string &modname); void listMods(std::vector *res); diff --git a/src/database/database-redis.cpp b/src/database/database-redis.cpp index 4509bb3c6..a9e549c33 100644 --- a/src/database/database-redis.cpp +++ b/src/database/database-redis.cpp @@ -91,12 +91,12 @@ void Database_Redis::endSave() { freeReplyObject(reply); } -bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data) +bool Database_Redis::saveBlock(const v3s16 &pos, std::string_view data) { std::string tmp = i64tos(getBlockAsInteger(pos)); redisReply *reply = static_cast(redisCommand(ctx, "HSET %s %s %b", - hash.c_str(), tmp.c_str(), data.c_str(), data.size())); + hash.c_str(), tmp.c_str(), data.data(), data.size())); if (!reply) { warningstream << "saveBlock: redis command 'HSET' failed on " "block " << pos << ": " << ctx->errstr << std::endl; diff --git a/src/database/database-redis.h b/src/database/database-redis.h index 6bea563bc..324fcd177 100644 --- a/src/database/database-redis.h +++ b/src/database/database-redis.h @@ -38,7 +38,7 @@ public: void beginSave(); void endSave(); - bool saveBlock(const v3s16 &pos, const std::string &data); + bool saveBlock(const v3s16 &pos, std::string_view data); void loadBlock(const v3s16 &pos, std::string *block); bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 6f6ad341e..32b3be65b 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -258,7 +258,7 @@ bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos) return good; } -bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data) +bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, std::string_view data) { verifyDatabase(); @@ -283,13 +283,8 @@ void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block) return; } - const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0); - size_t len = sqlite3_column_bytes(m_stmt_read, 0); - - if (data) - block->assign(data, len); - else - block->clear(); + auto data = sqlite_to_blob(m_stmt_read, 0); + block->assign(data); sqlite3_step(m_stmt_read); // We should never get more than 1 row, so ok to reset @@ -553,7 +548,7 @@ bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao) int_to_sqlite(m_stmt_player_load_inventory_items, 2, invId); while (sqlite3_step(m_stmt_player_load_inventory_items) == SQLITE_ROW) { const std::string itemStr = sqlite_to_string(m_stmt_player_load_inventory_items, 1); - if (itemStr.length() > 0) { + if (!itemStr.empty()) { ItemStack stack; stack.deSerialize(itemStr); invList->changeItem(sqlite_to_uint(m_stmt_player_load_inventory_items, 0), stack); @@ -567,7 +562,7 @@ bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao) str_to_sqlite(m_stmt_player_metadata_load, 1, sao->getPlayer()->getName()); while (sqlite3_step(m_stmt_player_metadata_load) == SQLITE_ROW) { std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0); - std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1); + auto value = sqlite_to_string_view(m_stmt_player_metadata_load, 1); sao->getMeta().setString(attr, value); } @@ -592,7 +587,7 @@ void PlayerDatabaseSQLite3::listPlayers(std::vector &res) verifyDatabase(); while (sqlite3_step(m_stmt_player_list) == SQLITE_ROW) - res.push_back(sqlite_to_string(m_stmt_player_list, 0)); + res.emplace_back(sqlite_to_string_view(m_stmt_player_list, 0)); sqlite3_reset(m_stmt_player_list); } @@ -669,14 +664,14 @@ bool AuthDatabaseSQLite3::getAuth(const std::string &name, AuthEntry &res) return false; } res.id = sqlite_to_uint(m_stmt_read, 0); - res.name = sqlite_to_string(m_stmt_read, 1); - res.password = sqlite_to_string(m_stmt_read, 2); + res.name = sqlite_to_string_view(m_stmt_read, 1); + res.password = sqlite_to_string_view(m_stmt_read, 2); res.last_login = sqlite_to_int64(m_stmt_read, 3); sqlite3_reset(m_stmt_read); int64_to_sqlite(m_stmt_read_privs, 1, res.id); while (sqlite3_step(m_stmt_read_privs) == SQLITE_ROW) { - res.privileges.emplace_back(sqlite_to_string(m_stmt_read_privs, 0)); + res.privileges.emplace_back(sqlite_to_string_view(m_stmt_read_privs, 0)); } sqlite3_reset(m_stmt_read_privs); @@ -741,7 +736,7 @@ void AuthDatabaseSQLite3::listNames(std::vector &res) verifyDatabase(); while (sqlite3_step(m_stmt_list_names) == SQLITE_ROW) { - res.push_back(sqlite_to_string(m_stmt_list_names, 0)); + res.emplace_back(sqlite_to_string_view(m_stmt_list_names, 0)); } sqlite3_reset(m_stmt_list_names); } @@ -815,11 +810,9 @@ void ModStorageDatabaseSQLite3::getModEntries(const std::string &modname, String str_to_sqlite(m_stmt_get_all, 1, modname); while (sqlite3_step(m_stmt_get_all) == SQLITE_ROW) { - const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get_all, 0); - size_t key_len = sqlite3_column_bytes(m_stmt_get_all, 0); - const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get_all, 1); - size_t value_len = sqlite3_column_bytes(m_stmt_get_all, 1); - (*storage)[std::string(key_data, key_len)] = std::string(value_data, value_len); + auto key = sqlite_to_blob(m_stmt_get_all, 0); + auto value = sqlite_to_blob(m_stmt_get_all, 1); + (*storage)[std::string(key)].assign(value); } sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE); @@ -833,9 +826,8 @@ void ModStorageDatabaseSQLite3::getModKeys(const std::string &modname, str_to_sqlite(m_stmt_get_keys, 1, modname); while (sqlite3_step(m_stmt_get_keys) == SQLITE_ROW) { - const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get_keys, 0); - size_t key_len = sqlite3_column_bytes(m_stmt_get_keys, 0); - storage->emplace_back(key_data, key_len); + auto key = sqlite_to_blob(m_stmt_get_keys, 0); + storage->emplace_back(key); } sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE); @@ -852,9 +844,8 @@ bool ModStorageDatabaseSQLite3::getModEntry(const std::string &modname, "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); bool found = sqlite3_step(m_stmt_get) == SQLITE_ROW; if (found) { - const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get, 0); - size_t value_len = sqlite3_column_bytes(m_stmt_get, 0); - value->assign(value_data, value_len); + auto sv = sqlite_to_blob(m_stmt_get, 0); + value->assign(sv); sqlite3_step(m_stmt_get); } @@ -881,7 +872,7 @@ bool ModStorageDatabaseSQLite3::hasModEntry(const std::string &modname, } bool ModStorageDatabaseSQLite3::setModEntry(const std::string &modname, - const std::string &key, const std::string &value) + const std::string &key, std::string_view value) { verifyDatabase(); diff --git a/src/database/database-sqlite3.h b/src/database/database-sqlite3.h index a400537ff..d008f43e2 100644 --- a/src/database/database-sqlite3.h +++ b/src/database/database-sqlite3.h @@ -44,14 +44,9 @@ protected: void verifyDatabase(); // Convertors - inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const std::string &str) const + inline void str_to_sqlite(sqlite3_stmt *s, int iCol, std::string_view str) const { - sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.c_str(), str.size(), NULL)); - } - - inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const char *str) const - { - sqlite3_vrfy(sqlite3_bind_text(s, iCol, str, strlen(str), NULL)); + sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.data(), str.size(), NULL)); } inline void int_to_sqlite(sqlite3_stmt *s, int iCol, int val) const @@ -69,10 +64,28 @@ protected: sqlite3_vrfy(sqlite3_bind_double(s, iCol, val)); } - inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol) + // Note that the return value is only valid until the statement is stepped or reset. + inline std::string_view sqlite_to_string_view(sqlite3_stmt *s, int iCol) { const char* text = reinterpret_cast(sqlite3_column_text(s, iCol)); - return std::string(text ? text : ""); + return text ? std::string_view(text) : std::string_view(); + } + + // Avoid using this in favor of `sqlite_to_string_view`. + inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol) + { + return std::string(sqlite_to_string_view(s, iCol)); + } + + // Converts a BLOB-type column into a string_view (null byte safe). + // Note that the return value is only valid until the statement is stepped or reset. + inline std::string_view sqlite_to_blob(sqlite3_stmt *s, int iCol) + { + const char *data = reinterpret_cast(sqlite3_column_blob(s, iCol)); + if (!data) + return std::string_view(); + size_t len = sqlite3_column_bytes(s, iCol); + return std::string_view(data, len); } inline s32 sqlite_to_int(sqlite3_stmt *s, int iCol) @@ -107,13 +120,16 @@ protected: } // Query verifiers helpers - inline void sqlite3_vrfy(int s, const std::string &m = "", int r = SQLITE_OK) const + inline void sqlite3_vrfy(int s, std::string_view m = "", int r = SQLITE_OK) const { - if (s != r) - throw DatabaseException(m + ": " + sqlite3_errmsg(m_database)); + if (s != r) { + std::string msg(m); + msg.append(": ").append(sqlite3_errmsg(m_database)); + throw DatabaseException(msg); + } } - inline void sqlite3_vrfy(const int s, const int r, const std::string &m = "") const + inline void sqlite3_vrfy(const int s, const int r, std::string_view m = "") const { sqlite3_vrfy(s, m, r); } @@ -146,7 +162,7 @@ public: MapDatabaseSQLite3(const std::string &savedir); virtual ~MapDatabaseSQLite3(); - bool saveBlock(const v3s16 &pos, const std::string &data); + bool saveBlock(const v3s16 &pos, std::string_view data); void loadBlock(const v3s16 &pos, std::string *block); bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); @@ -245,7 +261,7 @@ public: const std::string &key, std::string *value); virtual bool hasModEntry(const std::string &modname, const std::string &key); virtual bool setModEntry(const std::string &modname, - const std::string &key, const std::string &value); + const std::string &key,std::string_view value); virtual bool removeModEntry(const std::string &modname, const std::string &key); virtual bool removeModEntries(const std::string &modname); virtual void listMods(std::vector *res); diff --git a/src/database/database.h b/src/database/database.h index 84912e795..d4a3f005c 100644 --- a/src/database/database.h +++ b/src/database/database.h @@ -19,12 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include #include +#include #include #include "irr_v3d.h" #include "irrlichttypes.h" -#include "util/basic_macros.h" #include "util/string.h" class Database @@ -40,7 +39,7 @@ class MapDatabase : public Database public: virtual ~MapDatabase() = default; - virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0; + virtual bool saveBlock(const v3s16 &pos, std::string_view data) = 0; virtual void loadBlock(const v3s16 &pos, std::string *block) = 0; virtual bool deleteBlock(const v3s16 &pos) = 0; @@ -97,7 +96,7 @@ public: virtual bool getModEntry(const std::string &modname, const std::string &key, std::string *value) = 0; virtual bool setModEntry(const std::string &modname, - const std::string &key, const std::string &value) = 0; + const std::string &key, std::string_view value) = 0; virtual bool removeModEntry(const std::string &modname, const std::string &key) = 0; virtual bool removeModEntries(const std::string &modname) = 0; virtual void listMods(std::vector *res) = 0; diff --git a/src/filesys.cpp b/src/filesys.cpp index 5ea71b8fd..88d617801 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -828,7 +828,7 @@ const char *GetFilenameFromPath(const char *path) return filename ? filename + 1 : path; } -bool safeWriteToFile(const std::string &path, const std::string &content) +bool safeWriteToFile(const std::string &path, std::string_view content) { std::string tmp_file = path + ".~mt"; @@ -844,7 +844,7 @@ bool safeWriteToFile(const std::string &path, const std::string &content) return false; } DWORD bytes_written; - tmp_success = (WriteFile(tmp_handle, content.c_str(), content.size(), &bytes_written, nullptr) && + tmp_success = (WriteFile(tmp_handle, content.data(), content.size(), &bytes_written, nullptr) && FlushFileBuffers(tmp_handle)); CloseHandle(tmp_handle); #else diff --git a/src/filesys.h b/src/filesys.h index e3b1cce8e..2a6476107 100644 --- a/src/filesys.h +++ b/src/filesys.h @@ -21,8 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include -#include "exceptions.h" #ifdef _WIN32 #define DIR_DELIM "\\" @@ -142,7 +142,7 @@ std::string AbsolutePath(const std::string &path); // delimiter is found. const char *GetFilenameFromPath(const char *path); -bool safeWriteToFile(const std::string &path, const std::string &content); +bool safeWriteToFile(const std::string &path, std::string_view content); #ifndef SERVER bool extractZipFile(irr::io::IFileSystem *fs, const char *filename, const std::string &destination); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index a93f8c505..a1b7519be 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -755,7 +755,7 @@ void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string data->scrollbar_options.thumb_size = value <= 0 ? 1 : value; continue; } else if (options[0] == "arrows") { - std::string value = trim(options[1]); + auto value = trim(options[1]); if (value == "hide") data->scrollbar_options.arrow_visiblity = GUIScrollBar::HIDE; else if (value == "show") @@ -2449,8 +2449,8 @@ bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &eleme if (parts.size() < 2) return false; - std::string type = trim(parts[0]); - std::string description = trim(parts[1]); + auto type = trim(parts[0]); + std::string description(trim(parts[1])); if (type != "size" && type != "invsize") return false; @@ -2473,8 +2473,8 @@ bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &e if (parts.size() != 2) return false; - std::string type = trim(parts[0]); - std::string description = trim(parts[1]); + auto type = trim(parts[0]); + std::string description(trim(parts[1])); if (type != "position") return false; @@ -2512,8 +2512,8 @@ bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &ele if (parts.size() != 2) return false; - std::string type = trim(parts[0]); - std::string description = trim(parts[1]); + auto type = trim(parts[0]); + std::string description(trim(parts[1])); if (type != "anchor") return false; @@ -2552,8 +2552,8 @@ bool GUIFormSpecMenu::parsePaddingDirect(parserData *data, const std::string &el if (parts.size() != 2) return false; - std::string type = trim(parts[0]); - std::string description = trim(parts[1]); + auto type = trim(parts[0]); + std::string description(trim(parts[1])); if (type != "padding") return false; @@ -2624,7 +2624,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b std::vector selectors = split(parts[0], ','); for (size_t sel = 0; sel < selectors.size(); sel++) { - std::string selector = trim(selectors[sel]); + std::string selector(trim(selectors[sel])); // Copy the style properties to a new StyleSpec // This allows a separate state mask per-selector @@ -3216,7 +3216,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.real_coordinates = m_formspec_version >= 2; for (; i < elements.size(); i++) { std::vector parts = split(elements[i], '['); - std::string name = trim(parts[0]); + auto name = trim(parts[0]); if (name != "real_coordinates" || parts.size() != 2) break; // Invalid format diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index bf4a7b2ac..be1715e1a 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -49,10 +49,10 @@ static void sanitize_string(std::string &str) str.erase(std::remove(str.begin(), str.end(), DESERIALIZE_PAIR_DELIM), str.end()); } -bool ItemStackMetadata::setString(const std::string &name, const std::string &var) +bool ItemStackMetadata::setString(const std::string &name, std::string_view var) { std::string clean_name = name; - std::string clean_var = var; + std::string clean_var(var); sanitize_string(clean_name); sanitize_string(clean_var); diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h index e0615916e..db450dc4a 100644 --- a/src/itemstackmetadata.h +++ b/src/itemstackmetadata.h @@ -36,7 +36,7 @@ public: // Overrides void clear() override; - bool setString(const std::string &name, const std::string &var) override; + bool setString(const std::string &name, std::string_view var) override; void serialize(std::ostream &os) const; void deSerialize(std::istream &is); diff --git a/src/map.cpp b/src/map.cpp index 07819c214..38003151e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1798,6 +1798,7 @@ bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_leve o.write((char*) &version, 1); block->serialize(o, version, true, compression_level); + // FIXME: zero copy possible in c++20 or with custom rdbuf bool ret = db->saveBlock(p3d, o.str()); if (ret) { // We just wrote it to the disk so clear modified flag diff --git a/src/mapblock.cpp b/src/mapblock.cpp index c48d3defe..ae7ff31c6 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -382,7 +382,7 @@ void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int Bulk node data */ NameIdMapping nimap; - SharedBuffer buf; + Buffer buf; const u8 content_width = 2; const u8 params_width = 2; if(disk) @@ -653,7 +653,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) // Make a temporary buffer u32 ser_length = MapNode::serializedLength(version); - SharedBuffer databuf_nodelist(nodecount * ser_length); + Buffer databuf_nodelist(nodecount * ser_length); // These have no compression if (version <= 3 || version == 5 || version == 6) { diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index 28a1abf83..b6b78f9e9 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -384,7 +384,7 @@ bool Schematic::serializeToMts(std::ostream *os) const } // compressed bulk node data - SharedBuffer buf = MapNode::serializeBulk(MTSCHEM_MAPNODE_SER_FMT_VER, + auto buf = MapNode::serializeBulk(MTSCHEM_MAPNODE_SER_FMT_VER, schemdata, size.X * size.Y * size.Z, 2, 2); compress(buf, ss, MTSCHEM_MAPNODE_SER_FMT_VER); diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 33354b2d9..d16f6d647 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -690,7 +690,7 @@ void MapNode::deSerialize(u8 *source, u8 version) } } -SharedBuffer MapNode::serializeBulk(int version, +Buffer MapNode::serializeBulk(int version, const MapNode *nodes, u32 nodecount, u8 content_width, u8 params_width) { @@ -706,7 +706,7 @@ SharedBuffer MapNode::serializeBulk(int version, throw SerializationError("MapNode::serializeBulk: serialization to " "version < 24 not possible"); - SharedBuffer databuf(nodecount * (content_width + params_width)); + Buffer databuf(nodecount * (content_width + params_width)); u32 start1 = content_width * nodecount; u32 start2 = (content_width + 1) * nodecount; diff --git a/src/mapnode.h b/src/mapnode.h index c3137bbcb..93dfd01b9 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -316,7 +316,7 @@ struct alignas(u32) MapNode // content_width = the number of bytes of content per node // params_width = the number of bytes of params per node // compressed = true to zlib-compress output - static SharedBuffer serializeBulk(int version, + static Buffer serializeBulk(int version, const MapNode *nodes, u32 nodecount, u8 content_width, u8 params_width); static void deSerializeBulk(std::istream &is, int version, diff --git a/src/metadata.cpp b/src/metadata.cpp index f02495fa4..f8fc02da5 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -70,7 +70,7 @@ bool IMetadata::getStringToRef(const std::string &name, const std::string &IMetadata::resolveString(const std::string &str, std::string *place, u16 recursion, bool deprecated) const { - if (recursion <= 1 && str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { + if (recursion <= 1 && str_starts_with(str, "${") && str.back() == '}') { if (deprecated) { warningstream << "Deprecated use of recursive resolution syntax in metadata: "; safe_print_string(warningstream, str); @@ -128,7 +128,7 @@ const std::string *SimpleMetadata::getStringRaw(const std::string &name, std::st return found != m_stringvars.cend() ? &found->second : nullptr; } -bool SimpleMetadata::setString(const std::string &name, const std::string &var) +bool SimpleMetadata::setString(const std::string &name, std::string_view var) { if (var.empty()) { if (m_stringvars.erase(name) == 0) @@ -137,7 +137,7 @@ bool SimpleMetadata::setString(const std::string &name, const std::string &var) StringMap::iterator it = m_stringvars.find(name); if (it != m_stringvars.end() && it->second == var) return false; - m_stringvars[name] = var; + m_stringvars[name].assign(var); } m_modified = true; return true; diff --git a/src/metadata.h b/src/metadata.h index 1a0350bbf..be053662e 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -53,7 +53,7 @@ public: bool getStringToRef(const std::string &name, std::string &str, u16 recursion = 0) const; // Returns whether the metadata was (potentially) changed. - virtual bool setString(const std::string &name, const std::string &var) = 0; + virtual bool setString(const std::string &name, std::string_view var) = 0; inline bool removeString(const std::string &name) { return setString(name, ""); } @@ -89,7 +89,7 @@ public: size_t size() const; bool contains(const std::string &name) const override; - virtual bool setString(const std::string &name, const std::string &var) override; + virtual bool setString(const std::string &name, std::string_view var) override; const StringMap &getStrings(StringMap *) const override final; const std::vector &getKeys(std::vector *place) const override final; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 6b5e6add3..10ecdd34c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1651,10 +1651,8 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) std::string computed_hash; { SHA1 ctx; - ctx.addBytes(filedata.c_str(), filedata.size()); - unsigned char *buf = ctx.getDigest(); - computed_hash.assign((char*) buf, 20); - free(buf); + ctx.addBytes(filedata); + computed_hash = ctx.getDigest(); } if (raw_hash != computed_hash) { verbosestream << "Hash of file data mismatches, ignoring." << std::endl; diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 5b7b51391..48e8660b6 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -99,7 +99,7 @@ NetworkPacket& NetworkPacket::operator>>(std::string& dst) return *this; } -NetworkPacket& NetworkPacket::operator<<(const std::string &src) +NetworkPacket& NetworkPacket::operator<<(std::string_view src) { if (src.size() > STRING_MAX_LEN) { throw PacketError("String too long"); @@ -109,12 +109,12 @@ NetworkPacket& NetworkPacket::operator<<(const std::string &src) *this << msgsize; - putRawString(src.c_str(), (u32)msgsize); + putRawString(src.data(), (u32)msgsize); return *this; } -void NetworkPacket::putLongString(const std::string &src) +void NetworkPacket::putLongString(std::string_view src) { if (src.size() > LONG_STRING_MAX_LEN) { throw PacketError("String too long"); @@ -124,7 +124,7 @@ void NetworkPacket::putLongString(const std::string &src) *this << msgsize; - putRawString(src.c_str(), msgsize); + putRawString(src.data(), msgsize); } static constexpr bool NEED_SURROGATE_CODING = sizeof(wchar_t) > 2; @@ -160,7 +160,7 @@ NetworkPacket& NetworkPacket::operator>>(std::wstring& dst) return *this; } -NetworkPacket& NetworkPacket::operator<<(const std::wstring &src) +NetworkPacket& NetworkPacket::operator<<(std::wstring_view src) { if (src.size() > WIDE_STRING_MAX_LEN) { throw PacketError("String too long"); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 5f768f51a..ee85b2951 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -55,18 +55,18 @@ public: const char *getString(u32 from_offset) const; // major difference to putCString(): doesn't write len into the buffer void putRawString(const char *src, u32 len); - void putRawString(const std::string &src) + void putRawString(std::string_view src) { - putRawString(src.c_str(), src.size()); + putRawString(src.data(), src.size()); } NetworkPacket &operator>>(std::string &dst); - NetworkPacket &operator<<(const std::string &src); + NetworkPacket &operator<<(std::string_view src); - void putLongString(const std::string &src); + void putLongString(std::string_view src); NetworkPacket &operator>>(std::wstring &dst); - NetworkPacket &operator<<(const std::wstring &src); + NetworkPacket &operator<<(std::wstring_view src); std::string readLongString(); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 7efac7fc0..6fc220145 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1142,17 +1142,16 @@ bool NodeDefManager::getIds(const std::string &name, std::vector &result) const { //TimeTaker t("getIds", NULL, PRECISION_MICRO); - if (name.substr(0,6) != "group:") { + if (!str_starts_with(name, "group:")) { content_t id = CONTENT_IGNORE; bool exists = getId(name, id); if (exists) result.push_back(id); return exists; } - std::string group = name.substr(6); - std::unordered_map>::const_iterator - i = m_group_to_items.find(group); + std::string group = name.substr(6); + auto i = m_group_to_items.find(group); if (i == m_group_to_items.end()) return true; @@ -1847,7 +1846,7 @@ bool NodeResolver::getIdsFromNrBacklog(std::vector *result_out, content_t c; std::string &name = m_nodenames[m_nodenames_idx++]; - if (name.substr(0,6) != "group:") { + if (!str_starts_with(name, "group:")) { if (m_ndef->getId(name, c)) { result_out->push_back(c); } else if (all_required) { diff --git a/src/nodemetadata.h b/src/nodemetadata.h index a791cc5df..da277aabd 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include +#include #include "metadata.h" /* diff --git a/src/porting.cpp b/src/porting.cpp index 76c3ae985..e2cc3dffd 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -832,7 +832,7 @@ static bool open_uri(const std::string &uri) bool open_url(const std::string &url) { - if (url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://") { + if (!str_starts_with(url, "http://") && !str_starts_with(url, "https://")) { errorstream << "Unable to open browser as URL is missing schema: " << url << std::endl; return false; } diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 30a387b84..3bda40ebb 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1664,7 +1664,7 @@ Pointabilities read_pointabilities(lua_State *L, int index) std::string name = luaL_checkstring(L, -2); // handle groups - if(std::string_view(name).substr(0,6)=="group:") { + if (str_starts_with(name, "group:")) { pointabilities.node_groups[name.substr(6)] = read_pointability_type(L, -1); } else { pointabilities.nodes[name] = read_pointability_type(L, -1); @@ -1685,7 +1685,7 @@ Pointabilities read_pointabilities(lua_State *L, int index) std::string name = luaL_checkstring(L, -2); // handle groups - if(std::string_view(name).substr(0,6)=="group:") { + if (str_starts_with(name, "group:")) { pointabilities.object_groups[name.substr(6)] = read_pointability_type(L, -1); } else { pointabilities.objects[name] = read_pointability_type(L, -1); diff --git a/src/script/common/helper.cpp b/src/script/common/helper.cpp index 72de2b14a..a9a05b2cb 100644 --- a/src/script/common/helper.cpp +++ b/src/script/common/helper.cpp @@ -25,6 +25,7 @@ extern "C" { #include #include #include +#include #include "c_converter.h" #include "c_types.h" @@ -78,11 +79,16 @@ v3f LuaHelper::readParam(lua_State *L, int index) } template <> -std::string LuaHelper::readParam(lua_State *L, int index) +std::string_view LuaHelper::readParam(lua_State *L, int index) { size_t length; - std::string result; const char *str = luaL_checklstring(L, index, &length); - result.assign(str, length); - return result; + return std::string_view(str, length); +} + +template <> +std::string LuaHelper::readParam(lua_State *L, int index) +{ + auto sv = readParam(L, index); + return std::string(sv); // a copy } diff --git a/src/script/common/helper.h b/src/script/common/helper.h index fc462b6ef..106966376 100644 --- a/src/script/common/helper.h +++ b/src/script/common/helper.h @@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include + extern "C" { #include } @@ -27,23 +29,22 @@ class LuaHelper { protected: /** - * Read a value using a template type T from Lua State L and index - * + * Read a value using a template type T from Lua state L at index * * @tparam T type to read from Lua * @param L Lua state - * @param index Lua Index to read + * @param index Lua index to read * @return read value from Lua */ template static T readParam(lua_State *L, int index); /** - * Read a value using a template type T from Lua State L and index + * Read a value using a template type T from Lua state L at index * * @tparam T type to read from Lua * @param L Lua state - * @param index Lua Index to read + * @param index Lua index to read * @param default_value default value to apply if nil * @return read value from Lua or default value if nil */ @@ -53,3 +54,18 @@ protected: return lua_isnoneornil(L, index) ? default_value : readParam(L, index); } }; + +// (only declared for documentation purposes:) + +/** + * Read a string from Lua state L at index without copying it. + * + * Note that the returned string view is only valid as long as the value is on + * the stack and has not been modified. Be careful. + * + * @param L Lua state + * @param index Lua index to read + * @return string view + */ +template <> +std::string_view LuaHelper::readParam(lua_State *L, int index); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 875e5dedd..4ac998a8f 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -163,6 +163,9 @@ int LuaItemStack::l_get_metadata(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkObject(L, 1); ItemStack &item = o->m_stack; + + log_deprecated(L, "ItemStack:get_metadata is deprecated", 1, true); + const std::string &value = item.metadata.getString(""); lua_pushlstring(L, value.c_str(), value.size()); return 1; @@ -176,6 +179,8 @@ int LuaItemStack::l_set_metadata(lua_State *L) LuaItemStack *o = checkObject(L, 1); ItemStack &item = o->m_stack; + log_deprecated(L, "ItemStack:set_metadata is deprecated", 1, true); + size_t len = 0; const char *ptr = luaL_checklstring(L, 2, &len); item.metadata.setString("", std::string(ptr, len)); diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index 52d7617d1..58faeb48d 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -115,9 +115,7 @@ int MetaDataRef::l_set_string(lua_State *L) MetaDataRef *ref = checkAnyMetadata(L, 1); std::string name = luaL_checkstring(L, 2); - size_t len = 0; - const char *s = lua_tolstring(L, 3, &len); - std::string str(s, len); + auto str = readParam(L, 3); IMetadata *meta = ref->getmeta(!str.empty()); if (meta != NULL && meta->setString(name, str)) @@ -300,9 +298,8 @@ bool MetaDataRef::handleFromTable(lua_State *L, int table, IMetadata *meta) while (lua_next(L, fieldstable) != 0) { // key at index -2 and value at index -1 std::string name = readParam(L, -2); - size_t cl; - const char *cs = lua_tolstring(L, -1, &cl); - meta->setString(name, std::string(cs, cl)); + auto value = readParam(L, -1); + meta->setString(name, value); lua_pop(L, 1); // Remove value, keep key for next iteration } lua_pop(L, 1); diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 4856ce172..6a1cdc9df 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -318,8 +318,7 @@ int ModApiUtil::l_compress(lua_State *L) { NO_MAP_LOCK_REQUIRED; - size_t size; - const char *data = luaL_checklstring(L, 1, &size); + auto data = readParam(L, 1); LuaCompressMethod method = get_compress_method(L, 2); @@ -330,13 +329,13 @@ int ModApiUtil::l_compress(lua_State *L) if (!lua_isnoneornil(L, 3)) level = readParam(L, 3); - compressZlib(reinterpret_cast(data), size, os, level); + compressZlib(data, os, level); } else if (method == LUA_COMPRESS_METHOD_ZSTD) { int level = ZSTD_CLEVEL_DEFAULT; if (!lua_isnoneornil(L, 3)) level = readParam(L, 3); - compressZstd(reinterpret_cast(data), size, os, level); + compressZstd(data, os, level); } std::string out = os.str(); @@ -350,12 +349,12 @@ int ModApiUtil::l_decompress(lua_State *L) { NO_MAP_LOCK_REQUIRED; - size_t size; - const char *data = luaL_checklstring(L, 1, &size); + auto data = readParam(L, 1); LuaCompressMethod method = get_compress_method(L, 2); - std::istringstream is(std::string(data, size), std::ios_base::binary); + // FIXME: zero copy possible in c++26 or with custom rdbuf + std::istringstream is(std::string(data), std::ios_base::binary); std::ostringstream os(std::ios_base::binary); if (method == LUA_COMPRESS_METHOD_DEFLATE) { @@ -375,10 +374,9 @@ int ModApiUtil::l_encode_base64(lua_State *L) { NO_MAP_LOCK_REQUIRED; - size_t size; - const char *data = luaL_checklstring(L, 1, &size); + auto data = readParam(L, 1); - std::string out = base64_encode((const unsigned char *)(data), size); + std::string out = base64_encode(data); lua_pushlstring(L, out.data(), out.size()); return 1; @@ -389,9 +387,7 @@ int ModApiUtil::l_decode_base64(lua_State *L) { NO_MAP_LOCK_REQUIRED; - size_t size; - const char *d = luaL_checklstring(L, 1, &size); - const std::string data = std::string(d, size); + auto data = readParam(L, 1); if (!base64_is_valid(data)) { lua_pushnil(L); @@ -487,12 +483,11 @@ int ModApiUtil::l_safe_file_write(lua_State *L) { NO_MAP_LOCK_REQUIRED; const char *path = luaL_checkstring(L, 1); - size_t size; - const char *content = luaL_checklstring(L, 2, &size); + auto content = readParam(L, 2); CHECK_SECURE_PATH(L, path, true); - bool ret = fs::safeWriteToFile(path, std::string(content, size)); + bool ret = fs::safeWriteToFile(path, content); lua_pushboolean(L, ret); return 1; @@ -549,18 +544,16 @@ int ModApiUtil::l_get_version(lua_State *L) int ModApiUtil::l_sha1(lua_State *L) { NO_MAP_LOCK_REQUIRED; - size_t size; - const char *data = luaL_checklstring(L, 1, &size); + + auto data = readParam(L, 1); bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); // Compute actual checksum of data std::string data_sha1; { SHA1 ctx; - ctx.addBytes(data, size); - unsigned char *data_tmpdigest = ctx.getDigest(); - data_sha1.assign((char*) data_tmpdigest, 20); - free(data_tmpdigest); + ctx.addBytes(data); + data_sha1 = ctx.getDigest(); } if (hex) { @@ -654,8 +647,8 @@ int ModApiUtil::l_urlencode(lua_State *L) { NO_MAP_LOCK_REQUIRED; - const char *value = luaL_checkstring(L, 1); - lua_pushstring(L, urlencode(value).c_str()); + auto s = readParam(L, 1); + lua_pushstring(L, urlencode(s).c_str()); return 1; } diff --git a/src/serialization.cpp b/src/serialization.cpp index c0a104224..689e4d986 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -106,11 +106,6 @@ void compressZlib(const u8 *data, size_t data_size, std::ostream &os, int level) } } -void compressZlib(const std::string &data, std::ostream &os, int level) -{ - compressZlib((u8*)data.c_str(), data.size(), os, level); -} - void decompressZlib(std::istream &is, std::ostream &os, size_t limit) { z_stream z; @@ -211,7 +206,6 @@ void compressZstd(const u8 *data, size_t data_size, std::ostream &os, int level) // it will be destroyed when the thread ends thread_local std::unique_ptr stream(ZSTD_createCStream()); - ZSTD_initCStream(stream.get(), level); const size_t bufsize = 16384; @@ -247,11 +241,6 @@ void compressZstd(const u8 *data, size_t data_size, std::ostream &os, int level) } -void compressZstd(const std::string &data, std::ostream &os, int level) -{ - compressZstd((u8*)data.c_str(), data.size(), os, level); -} - void decompressZstd(std::istream &is, std::ostream &os) { // reusing the context is recommended for performance @@ -295,7 +284,7 @@ void decompressZstd(std::istream &is, std::ostream &os) } } -void compress(u8 *data, u32 size, std::ostream &os, u8 version, int level) +void compress(const u8 *data, u32 size, std::ostream &os, u8 version, int level) { if(version >= 29) { @@ -345,16 +334,6 @@ void compress(u8 *data, u32 size, std::ostream &os, u8 version, int level) os.write((char*)¤t_byte, 1); } -void compress(const SharedBuffer &data, std::ostream &os, u8 version, int level) -{ - compress(*data, data.getSize(), os, version, level); -} - -void compress(const std::string &data, std::ostream &os, u8 version, int level) -{ - compress((u8*)data.c_str(), data.size(), os, version, level); -} - void decompress(std::istream &is, std::ostream &os, u8 version) { if(version >= 29) diff --git a/src/serialization.h b/src/serialization.h index e83a8c179..d2ffb054f 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "exceptions.h" #include -#include "util/pointer.h" +#include /* Map format serialization version @@ -83,19 +83,27 @@ inline bool ser_ver_supported(s32 v) { } /* - Misc. serialization functions + Compression functions */ void compressZlib(const u8 *data, size_t data_size, std::ostream &os, int level = -1); -void compressZlib(const std::string &data, std::ostream &os, int level = -1); +inline void compressZlib(std::string_view data, std::ostream &os, int level = -1) +{ + compressZlib(reinterpret_cast(data.data()), data.size(), os, level); +} void decompressZlib(std::istream &is, std::ostream &os, size_t limit = 0); void compressZstd(const u8 *data, size_t data_size, std::ostream &os, int level = 0); -void compressZstd(const std::string &data, std::ostream &os, int level = 0); +inline void compressZstd(std::string_view data, std::ostream &os, int level = 0) +{ + compressZstd(reinterpret_cast(data.data()), data.size(), os, level); +} void decompressZstd(std::istream &is, std::ostream &os); -// These choose between zlib and a self-made one according to version -void compress(const SharedBuffer &data, std::ostream &os, u8 version, int level = -1); -void compress(const std::string &data, std::ostream &os, u8 version, int level = -1); -void compress(u8 *data, u32 size, std::ostream &os, u8 version, int level = -1); +// These choose between zstd, zlib and a self-made one according to version +void compress(const u8 *data, u32 size, std::ostream &os, u8 version, int level = -1); +inline void compress(std::string_view data, std::ostream &os, u8 version, int level = -1) +{ + compress(reinterpret_cast(data.data()), data.size(), os, version, level); +} void decompress(std::istream &is, std::ostream &os, u8 version); diff --git a/src/server.cpp b/src/server.cpp index 7a776a246..5973ab555 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2534,14 +2534,13 @@ bool Server::addMediaFile(const std::string &filename, } SHA1 sha1; - sha1.addBytes(filedata.c_str(), filedata.length()); + sha1.addBytes(filedata); - unsigned char *digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest, 20); - std::string sha1_hex = hex_encode((char*) digest, 20); + std::string digest = sha1.getDigest(); + std::string sha1_base64 = base64_encode(digest); + std::string sha1_hex = hex_encode(digest); if (digest_to) - *digest_to = std::string((char*) digest, 20); - free(digest); + *digest_to = digest; // Put in list m_media[filename] = MediaInfo(filepath, sha1_base64); diff --git a/src/server.h b/src/server.h index 9448ea6d4..de7f9931c 100644 --- a/src/server.h +++ b/src/server.h @@ -93,8 +93,8 @@ struct MediaInfo // does what it says. used by some cases of dynamic media. bool delete_at_shutdown; - MediaInfo(const std::string &path_="", - const std::string &sha1_digest_=""): + MediaInfo(std::string_view path_ = "", + std::string_view sha1_digest_ = ""): path(path_), sha1_digest(sha1_digest_), no_announce(false), diff --git a/src/settings.cpp b/src/settings.cpp index 8b89bf489..74d00762d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -97,7 +97,7 @@ void SettingsHierarchy::onLayerRemoved(int layer) /* Settings implementation */ -Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag) +Settings *Settings::createLayer(SettingsLayer sl, std::string_view end_tag) { return new Settings(end_tag, &g_hierarchy, (int)sl); } @@ -109,7 +109,7 @@ Settings *Settings::getLayer(SettingsLayer sl) } -Settings::Settings(const std::string &end_tag, SettingsHierarchy *h, +Settings::Settings(std::string_view end_tag, SettingsHierarchy *h, int settings_layer) : m_end_tag(end_tag), m_hierarchy(h), @@ -131,7 +131,7 @@ Settings::~Settings() } -Settings & Settings::operator = (const Settings &other) +Settings & Settings::operator=(const Settings &other) { if (&other == this) return *this; @@ -151,7 +151,7 @@ Settings & Settings::operator = (const Settings &other) } -bool Settings::checkNameValid(const std::string &name) +bool Settings::checkNameValid(std::string_view name) { bool valid = name.find_first_of("=\"{}#") == std::string::npos; if (valid) @@ -166,7 +166,7 @@ bool Settings::checkNameValid(const std::string &name) } -bool Settings::checkValueValid(const std::string &value) +bool Settings::checkValueValid(std::string_view value) { if (value.substr(0, 3) == "\"\"\"" || value.find("\n\"\"\"") != std::string::npos) { @@ -407,13 +407,13 @@ bool Settings::parseCommandLine(int argc, char *argv[], { int nonopt_index = 0; for (int i = 1; i < argc; i++) { - std::string arg_name = argv[i]; + std::string_view arg_name(argv[i]); if (arg_name.substr(0, 2) != "--") { // If option doesn't start with -, read it in as nonoptX - if (arg_name[0] != '-'){ + if (arg_name[0] != '-') { std::string name = "nonopt"; name += itos(nonopt_index); - set(name, arg_name); + set(name, std::string(arg_name)); nonopt_index++; continue; } @@ -422,7 +422,7 @@ bool Settings::parseCommandLine(int argc, char *argv[], return false; } - std::string name = arg_name.substr(2); + std::string name(arg_name.substr(2)); auto n = allowed_options.find(name); if (n == allowed_options.end()) { @@ -997,7 +997,7 @@ bool Settings::remove(const std::string &name) SettingsParseEvent Settings::parseConfigObject(const std::string &line, std::string &name, std::string &value) { - std::string trimmed_line = trim(line); + auto trimmed_line = trim(line); if (trimmed_line.empty()) return SPE_NONE; diff --git a/src/settings.h b/src/settings.h index 1aeec0255..692001c59 100644 --- a/src/settings.h +++ b/src/settings.h @@ -23,8 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/basic_macros.h" #include -#include #include +#include #include class Settings; @@ -124,18 +124,17 @@ typedef std::unordered_map SettingEntries; class Settings { public: /* These functions operate on the global hierarchy! */ - static Settings *createLayer(SettingsLayer sl, const std::string &end_tag = ""); + static Settings *createLayer(SettingsLayer sl, std::string_view end_tag = ""); static Settings *getLayer(SettingsLayer sl); /**/ - Settings(const std::string &end_tag = "") : + Settings(std::string_view end_tag = "") : m_end_tag(end_tag) {} - Settings(const std::string &end_tag, SettingsHierarchy *h, int settings_layer); + Settings(std::string_view end_tag, SettingsHierarchy *h, int settings_layer); ~Settings(); - Settings & operator += (const Settings &other); - Settings & operator = (const Settings &other); + Settings & operator=(const Settings &other); /*********************** * Reading and writing * @@ -258,8 +257,8 @@ private: bool updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth=0); - static bool checkNameValid(const std::string &name); - static bool checkValueValid(const std::string &value); + static bool checkNameValid(std::string_view name); + static bool checkValueValid(std::string_view value); static std::string getMultiline(std::istream &is, size_t *num_lines=NULL); static void printEntry(std::ostream &os, const std::string &name, const SettingsEntry &entry, u32 tab_depth=0); @@ -276,9 +275,7 @@ private: // For sane mutex locking when iterating friend class LuaSettings; - void updateNoLock(const Settings &other); void clearNoLock(); - void clearDefaultsNoLock(); void doCallbacks(const std::string &name) const; diff --git a/src/sound.h b/src/sound.h index 5a593e6d0..afb9ac9ef 100644 --- a/src/sound.h +++ b/src/sound.h @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ struct SoundSpec { - SoundSpec(const std::string &name = "", float gain = 1.0f, + SoundSpec(std::string_view name = "", float gain = 1.0f, bool loop = false, float fade = 0.0f, float pitch = 1.0f, float start_time = 0.0f) : name(name), gain(gain), fade(fade), pitch(pitch), start_time(start_time), diff --git a/src/unittest/test_compression.cpp b/src/unittest/test_compression.cpp index a96282f58..3ada3fa00 100644 --- a/src/unittest/test_compression.cpp +++ b/src/unittest/test_compression.cpp @@ -57,7 +57,7 @@ void TestCompression::runTests(IGameDef *gamedef) void TestCompression::testRLECompression() { - SharedBuffer fromdata(4); + Buffer fromdata(4); fromdata[0]=1; fromdata[1]=5; fromdata[2]=5; @@ -106,7 +106,7 @@ void TestCompression::testRLECompression() void TestCompression::testZlibCompression() { - SharedBuffer fromdata(4); + Buffer fromdata(4); fromdata[0]=1; fromdata[1]=5; fromdata[2]=5; diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 84548a596..acada024b 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -189,10 +189,9 @@ void TestMapSettingsManager::testMapSettingsManager() SHA1 ctx; std::string metafile_contents; UASSERT(fs::ReadFile(test_mapmeta_path, metafile_contents)); - ctx.addBytes(&metafile_contents[0], metafile_contents.size()); - unsigned char *sha1_result = ctx.getDigest(); - int resultdiff = memcmp(sha1_result, expected_contents_hash, 20); - free(sha1_result); + ctx.addBytes(metafile_contents); + std::string sha1_result = ctx.getDigest(); + int resultdiff = memcmp(sha1_result.data(), expected_contents_hash, 20); UASSERT(!resultdiff); #endif diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 0a19f1466..9ebf3b873 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -240,20 +240,26 @@ void TestUtilities::testPadString() void TestUtilities::testStartsWith() { - UASSERT(str_starts_with(std::string(), std::string()) == true); + std::string the("the"); + UASSERT(str_starts_with(std::string(), "") == true); UASSERT(str_starts_with(std::string("the sharp pickaxe"), std::string()) == true); UASSERT(str_starts_with(std::string("the sharp pickaxe"), - std::string("the")) == true); + std::string_view(the)) == true); UASSERT(str_starts_with(std::string("the sharp pickaxe"), std::string("The")) == false); UASSERT(str_starts_with(std::string("the sharp pickaxe"), std::string("The"), true) == true); - UASSERT(str_starts_with(std::string("T"), std::string("The")) == false); + UASSERT(str_starts_with(std::string("T"), "The") == false); } void TestUtilities::testStrEqual() { + std::string foo("foo"); + UASSERT(str_equal(foo, std::string_view(foo))); + UASSERT(!str_equal(foo, std::string("bar"))); + UASSERT(str_equal(std::string_view(foo), std::string_view(foo))); + UASSERT(str_equal(std::wstring(L"FOO"), std::wstring(L"foo"), true)); UASSERT(str_equal(utf8_to_wide("abc"), utf8_to_wide("abc"))); UASSERT(str_equal(utf8_to_wide("ABC"), utf8_to_wide("abc"), true)); } @@ -629,14 +635,14 @@ void TestUtilities::testBase64() void TestUtilities::testSanitizeDirName() { - UASSERT(sanitizeDirName("a", "~") == "a"); - UASSERT(sanitizeDirName(" ", "~") == "__"); - UASSERT(sanitizeDirName(" a ", "~") == "_a_"); - UASSERT(sanitizeDirName("COM1", "~") == "~COM1"); - UASSERT(sanitizeDirName("COM1", ":") == "_COM1"); - UASSERT(sanitizeDirName("cOm\u00B2", "~") == "~cOm\u00B2"); - UASSERT(sanitizeDirName("cOnIn$", "~") == "~cOnIn$"); - UASSERT(sanitizeDirName(" cOnIn$ ", "~") == "_cOnIn$_"); + UASSERTEQ(auto, sanitizeDirName("a", "~"), "a"); + UASSERTEQ(auto, sanitizeDirName(" ", "~"), "__"); + UASSERTEQ(auto, sanitizeDirName(" a ", "~"), "_a_"); + UASSERTEQ(auto, sanitizeDirName("COM1", "~"), "~COM1"); + UASSERTEQ(auto, sanitizeDirName("COM1", ":"), "_COM1"); + UASSERTEQ(auto, sanitizeDirName("cOm\u00B2", "~"), "~cOm\u00B2"); + UASSERTEQ(auto, sanitizeDirName("cOnIn$", "~"), "~cOnIn$"); + UASSERTEQ(auto, sanitizeDirName(" cOnIn$ ", "~"), "_cOnIn$_"); } template diff --git a/src/util/auth.cpp b/src/util/auth.cpp index 3dd5a9afa..d7d1da280 100644 --- a/src/util/auth.cpp +++ b/src/util/auth.cpp @@ -39,10 +39,9 @@ std::string translate_password(const std::string &name, std::string slt = name + password; SHA1 sha1; - sha1.addBytes(slt.c_str(), slt.length()); - unsigned char *digest = sha1.getDigest(); - std::string pwd = base64_encode(digest, 20); - free(digest); + sha1.addBytes(slt); + std::string digest = sha1.getDigest(); + std::string pwd = base64_encode(digest); return pwd; } @@ -112,8 +111,8 @@ std::string encode_srp_verifier(const std::string &verifier, { std::ostringstream ret_str; ret_str << "#1#" - << base64_encode((unsigned char *)salt.c_str(), salt.size()) << "#" - << base64_encode((unsigned char *)verifier.c_str(), verifier.size()); + << base64_encode(salt) << "#" + << base64_encode(verifier); return ret_str.str(); } diff --git a/src/util/base64.cpp b/src/util/base64.cpp index ac4421f51..2acf78167 100644 --- a/src/util/base64.cpp +++ b/src/util/base64.cpp @@ -45,15 +45,15 @@ static inline bool is_base64(unsigned char c) || c == '+' || c == '/'; } -bool base64_is_valid(std::string const& s) +bool base64_is_valid(std::string_view s) { size_t i = 0; for (; i < s.size(); ++i) if (!is_base64(s[i])) break; unsigned char padding = 3 - ((i + 3) % 4); - if ((padding == 1 && base64_chars_padding_1.find(s[i - 1]) == std::string::npos) - || (padding == 2 && base64_chars_padding_2.find(s[i - 1]) == std::string::npos) + if ((padding == 1 && base64_chars_padding_1.find(s[i - 1]) == s.npos) + || (padding == 2 && base64_chars_padding_2.find(s[i - 1]) == s.npos) || padding == 3) return false; int actual_padding = s.size() - i; @@ -69,8 +69,14 @@ bool base64_is_valid(std::string const& s) return padding == actual_padding; } -std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { +std::string base64_encode(std::string_view s) +{ + const unsigned char *bytes_to_encode = reinterpret_cast(s.data()); + size_t in_len = s.size(); + std::string ret; + ret.reserve(in_len + in_len / 3); + int i = 0; int j = 0; unsigned char char_array_3[3]; @@ -110,16 +116,17 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_ } return ret; - } -std::string base64_decode(std::string const& encoded_string) { +std::string base64_decode(std::string_view encoded_string) +{ int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; + ret.reserve(in_len / 4 * 3); while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; diff --git a/src/util/base64.h b/src/util/base64.h index 68fa5b743..3846d3a25 100644 --- a/src/util/base64.h +++ b/src/util/base64.h @@ -29,7 +29,8 @@ René Nyffenegger rene.nyffenegger@adp-gmbh.ch #pragma once #include +#include -bool base64_is_valid(std::string const& s); -std::string base64_encode(unsigned char const* , unsigned int len); -std::string base64_decode(std::string const& s); +bool base64_is_valid(std::string_view s); +std::string base64_encode(std::string_view s); +std::string base64_decode(std::string_view s); diff --git a/src/util/hex.h b/src/util/hex.h index 708f33024..30e02994f 100644 --- a/src/util/hex.h +++ b/src/util/hex.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include +#include static const char hex_chars[] = "0123456789abcdef"; @@ -41,9 +42,9 @@ static inline std::string hex_encode(const char *data, unsigned int data_size) return ret; } -static inline std::string hex_encode(const std::string &data) +static inline std::string hex_encode(std::string_view data) { - return hex_encode(data.c_str(), data.size()); + return hex_encode(data.data(), data.size()); } static inline bool hex_digit_decode(char hexdigit, unsigned char &value) diff --git a/src/util/pointer.h b/src/util/pointer.h index f4b70f822..528897a1c 100644 --- a/src/util/pointer.h +++ b/src/util/pointer.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" // For assert() #include #include // std::shared_ptr +#include template class ConstSharedPtr { @@ -133,6 +134,13 @@ public: return m_size; } + operator std::string_view() const + { + if (!data) + return std::string_view(); + return std::string_view(reinterpret_cast(data), m_size); + } + private: void drop() { diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index ee46fd941..e1552f602 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -34,7 +34,7 @@ FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN; //// String //// -std::string serializeString16(const std::string &plain) +std::string serializeString16(std::string_view plain) { std::string s; char buf[2]; @@ -76,7 +76,7 @@ std::string deSerializeString16(std::istream &is) //// Long String //// -std::string serializeString32(const std::string &plain) +std::string serializeString32(std::string_view plain) { std::string s; char buf[4]; @@ -122,7 +122,7 @@ std::string deSerializeString32(std::istream &is) //// JSON-like strings //// -std::string serializeJsonString(const std::string &plain) +std::string serializeJsonString(std::string_view plain) { std::string tmp; @@ -263,13 +263,13 @@ std::string deSerializeJsonString(std::istream &is) return tmp; } -std::string serializeJsonStringIfNeeded(const std::string &s) +std::string serializeJsonStringIfNeeded(std::string_view s) { for (size_t i = 0; i < s.size(); ++i) { if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"') return serializeJsonString(s); } - return s; + return std::string(s); } std::string deSerializeJsonStringIfNeeded(std::istream &is) diff --git a/src/util/serialize.h b/src/util/serialize.h index 4dc1a54c6..00250ece5 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -41,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include // for memcpy #include #include -#include +#include #define FIXEDPOINT_FACTOR 1000.0f @@ -450,26 +450,26 @@ inline v3f clampToF1000(v3f v) } // Creates a string with the length as the first two bytes -std::string serializeString16(const std::string &plain); +std::string serializeString16(std::string_view plain); // Reads a string with the length as the first two bytes std::string deSerializeString16(std::istream &is); // Creates a string with the length as the first four bytes -std::string serializeString32(const std::string &plain); +std::string serializeString32(std::string_view plain); // Reads a string with the length as the first four bytes std::string deSerializeString32(std::istream &is); // Creates a string encoded in JSON format (almost equivalent to a C string literal) -std::string serializeJsonString(const std::string &plain); +std::string serializeJsonString(std::string_view plain); // Reads a string encoded in JSON format std::string deSerializeJsonString(std::istream &is); // If the string contains spaces, quotes or control characters, encodes as JSON. // Else returns the string unmodified. -std::string serializeJsonStringIfNeeded(const std::string &s); +std::string serializeJsonStringIfNeeded(std::string_view s); // Parses a string serialized by serializeJsonStringIfNeeded. std::string deSerializeJsonStringIfNeeded(std::istream &is); diff --git a/src/util/sha1.cpp b/src/util/sha1.cpp index ba95909ff..5d8b92598 100644 --- a/src/util/sha1.cpp +++ b/src/util/sha1.cpp @@ -65,7 +65,7 @@ void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num ) SHA1::SHA1() { // make sure that the data type is the right size - assert( sizeof( Uint32 ) * 5 == 20 ); + static_assert( sizeof( Uint32 ) * 5 == 20 ); } // Destructor ******************************************************** @@ -134,20 +134,19 @@ void SHA1::process() } // addBytes ********************************************************** -void SHA1::addBytes( const char* data, int num ) +void SHA1::addBytes( const char* data, Uint32 num ) { assert( data ); - assert( num >= 0 ); // add these bytes to the running total size += num; // repeat until all data is processed while( num > 0 ) { // number of bytes required to complete block - int needed = 64 - unprocessedBytes; - assert( needed > 0 ); + Uint32 needed = 64 - unprocessedBytes; + assert( needed <= 64 ); // number of bytes to copy (use smaller of two) - int toCopy = (num < needed) ? num : needed; + Uint32 toCopy = (num < needed) ? num : needed; // Copy the bytes memcpy( bytes + unprocessedBytes, data, toCopy ); // Bytes have been copied @@ -161,7 +160,7 @@ void SHA1::addBytes( const char* data, int num ) } // digest ************************************************************ -unsigned char* SHA1::getDigest() +void SHA1::getDigest(unsigned char *digest) { // save the message size Uint32 totalBitsL = size << 3; @@ -179,20 +178,16 @@ unsigned char* SHA1::getDigest() addBytes( (char*)footer, 64 - unprocessedBytes); assert( unprocessedBytes <= 56 ); // how many zeros do we need - int neededZeros = 56 - unprocessedBytes; + Uint32 neededZeros = 56 - unprocessedBytes; // store file size (in bits) in big-endian format storeBigEndianUint32( footer + neededZeros , totalBitsH ); storeBigEndianUint32( footer + neededZeros + 4, totalBitsL ); // finish the final block addBytes( (char*)footer, neededZeros + 8 ); - // allocate memory for the digest bytes - unsigned char* digest = (unsigned char*)malloc( 20 ); // copy the digest bytes storeBigEndianUint32( digest, H0 ); storeBigEndianUint32( digest + 4, H1 ); storeBigEndianUint32( digest + 8, H2 ); storeBigEndianUint32( digest + 12, H3 ); storeBigEndianUint32( digest + 16, H4 ); - // return the digest - return digest; } diff --git a/src/util/sha1.h b/src/util/sha1.h index 20f89eae6..81eeeca97 100644 --- a/src/util/sha1.h +++ b/src/util/sha1.h @@ -26,7 +26,11 @@ SOFTWARE. #pragma once -typedef unsigned int Uint32; +#include +#include +#include + +typedef uint32_t Uint32; class SHA1 { @@ -38,15 +42,24 @@ private: Uint32 H3 = 0x10325476; Uint32 H4 = 0xc3d2e1f0; unsigned char bytes[64]; - int unprocessedBytes = 0; + Uint32 unprocessedBytes = 0; Uint32 size = 0; void process(); public: SHA1(); ~SHA1(); - void addBytes(const char *data, int num); - unsigned char *getDigest(); + void addBytes(const char *data, Uint32 num); + inline void addBytes(std::string_view data) { + addBytes(data.data(), data.size()); + } + void getDigest(unsigned char *to); + inline std::string getDigest() { + std::string ret(20, '\000'); + getDigest(reinterpret_cast(ret.data())); + return ret; + } + // utility methods static Uint32 lrot(Uint32 x, int bits); static void storeBigEndianUint32(unsigned char *byte, Uint32 num); diff --git a/src/util/strfnd.h b/src/util/strfnd.h index 4ba599437..296e37a82 100644 --- a/src/util/strfnd.h +++ b/src/util/strfnd.h @@ -20,14 +20,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include +#include +// FIXME: convert this class to string_view template class BasicStrfnd { typedef std::basic_string String; String str; size_t pos; public: - BasicStrfnd(const String &s) : str(s), pos(0) {} + BasicStrfnd(const String &s) { start(s); } + BasicStrfnd(const T *ptr) { str = ptr; pos = 0; } + BasicStrfnd(std::basic_string_view sv) { str = sv; pos = 0; } + void start(const String &s) { str = s; pos = 0; } size_t where() { return pos; } void to(size_t i) { pos = i; } diff --git a/src/util/string.cpp b/src/util/string.cpp index efb07c1d1..534459e2a 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -82,14 +82,14 @@ const char *DEFAULT_ENCODING = "UTF-32LE"; const char *DEFAULT_ENCODING = "WCHAR_T"; #endif -std::wstring utf8_to_wide(const std::string &input) +std::wstring utf8_to_wide(std::string_view input) { const size_t inbuf_size = input.length(); // maximum possible size, every character is sizeof(wchar_t) bytes size_t outbuf_size = input.length() * sizeof(wchar_t); char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated - memcpy(inbuf, input.c_str(), inbuf_size); + memcpy(inbuf, input.data(), inbuf_size); std::wstring out; out.resize(outbuf_size / sizeof(wchar_t)); @@ -110,14 +110,14 @@ std::wstring utf8_to_wide(const std::string &input) return out; } -std::string wide_to_utf8(const std::wstring &input) +std::string wide_to_utf8(std::wstring_view input) { const size_t inbuf_size = input.length() * sizeof(wchar_t); // maximum possible size: utf-8 encodes codepoints using 1 up to 4 bytes size_t outbuf_size = input.length() * 4; char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated - memcpy(inbuf, input.c_str(), inbuf_size); + memcpy(inbuf, input.data(), inbuf_size); std::string out; out.resize(outbuf_size); @@ -135,24 +135,24 @@ std::string wide_to_utf8(const std::wstring &input) #else // _WIN32 -std::wstring utf8_to_wide(const std::string &input) +std::wstring utf8_to_wide(std::string_view input) { size_t outbuf_size = input.size() + 1; wchar_t *outbuf = new wchar_t[outbuf_size]; memset(outbuf, 0, outbuf_size * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(), + MultiByteToWideChar(CP_UTF8, 0, input.data(), input.size(), outbuf, outbuf_size); std::wstring out(outbuf); delete[] outbuf; return out; } -std::string wide_to_utf8(const std::wstring &input) +std::string wide_to_utf8(std::wstring_view input) { size_t outbuf_size = (input.size() + 1) * 6; char *outbuf = new char[outbuf_size]; memset(outbuf, 0, outbuf_size); - WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(), + WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), outbuf, outbuf_size, NULL, NULL); std::string out(outbuf); delete[] outbuf; @@ -162,9 +162,9 @@ std::string wide_to_utf8(const std::wstring &input) #endif // _WIN32 -std::string urlencode(const std::string &str) +std::string urlencode(std::string_view str) { - // Encodes non-unreserved URI characters by a percent sign + // Encodes reserved URI characters by a percent sign // followed by two hex digits. See RFC 3986, section 2.3. static const char url_hex_chars[] = "0123456789ABCDEF"; std::ostringstream oss(std::ios::binary); @@ -180,7 +180,7 @@ std::string urlencode(const std::string &str) return oss.str(); } -std::string urldecode(const std::string &str) +std::string urldecode(std::string_view str) { // Inverse of urlencode std::ostringstream oss(std::ios::binary); @@ -615,10 +615,10 @@ void str_replace(std::string &str, char from, char to) * before filling it again. */ -void translate_all(const std::wstring &s, size_t &i, +static void translate_all(const std::wstring &s, size_t &i, Translations *translations, std::wstring &res); -void translate_string(const std::wstring &s, Translations *translations, +static void translate_string(const std::wstring &s, Translations *translations, const std::wstring &textdomain, size_t &i, std::wstring &res) { std::wostringstream output; @@ -732,14 +732,15 @@ void translate_string(const std::wstring &s, Translations *translations, res = result.str(); } -void translate_all(const std::wstring &s, size_t &i, +static void translate_all(const std::wstring &s, size_t &i, Translations *translations, std::wstring &res) { - std::wostringstream output; + res.clear(); + res.reserve(s.length()); while (i < s.length()) { // Not an escape sequence: just add the character. if (s[i] != '\x1b') { - output.put(s[i]); + res.append(1, s[i]); ++i; continue; } @@ -747,7 +748,7 @@ void translate_all(const std::wstring &s, size_t &i, // We have an escape sequence: locate it and its data // It is either a single character, or it begins with '(' // and extends up to the following ')', with '\' as an escape character. - size_t escape_start = i; + const size_t escape_start = i; ++i; size_t start_index = i; size_t length; @@ -784,14 +785,12 @@ void translate_all(const std::wstring &s, size_t &i, textdomain = parts[1]; std::wstring translated; translate_string(s, translations, textdomain, i, translated); - output << translated; + res.append(translated); } else { // Another escape sequence, such as colors. Preserve it. - output << std::wstring(s, escape_start, i - escape_start); + res.append(&s[escape_start], i - escape_start); } } - - res = output.str(); } // Translate string server side @@ -813,7 +812,7 @@ std::wstring translate_string(const std::wstring &s) #endif } -static const std::array disallowed_dir_names = { +static const std::array disallowed_dir_names = { // Problematic filenames from here: // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names // Plus undocumented values from here: @@ -853,10 +852,10 @@ static const std::array disallowed_dir_names = { /** * List of characters that are blacklisted from created directories */ -static const std::wstring disallowed_path_chars = L"<>:\"/\\|?*."; +static const std::wstring_view disallowed_path_chars = L"<>:\"/\\|?*."; -std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix) +std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix) { std::wstring safe_name = utf8_to_wide(str); @@ -897,7 +896,7 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_ } -void safe_print_string(std::ostream &os, const std::string &str) +void safe_print_string(std::ostream &os, std::string_view str) { std::ostream::fmtflags flags = os.flags(); os << std::hex; @@ -913,7 +912,7 @@ void safe_print_string(std::ostream &os, const std::string &str) } -v3f str_to_v3f(const std::string &str) +v3f str_to_v3f(std::string_view str) { v3f value; Strfnd f(str); diff --git a/src/util/string.h b/src/util/string.h index f5382200d..01ec73481 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -23,10 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrString.h" #include #include +#include #include #include #include -#include #include #include #include @@ -76,15 +76,18 @@ struct FlagDesc { // Try to avoid converting between wide and UTF-8 unless you need to // input/output stuff via Irrlicht -std::wstring utf8_to_wide(const std::string &input); -std::string wide_to_utf8(const std::wstring &input); +std::wstring utf8_to_wide(std::string_view input); +std::string wide_to_utf8(std::wstring_view input); + +std::string urlencode(std::string_view str); +std::string urldecode(std::string_view str); -std::string urlencode(const std::string &str); -std::string urldecode(const std::string &str); u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask); std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask); + size_t mystrlcpy(char *dst, const char *src, size_t size) noexcept; char *mystrtok_r(char *s, const char *sep, char **lasts) noexcept; + u64 read_seed(const char *str); bool parseColorString(const std::string &value, video::SColor &color, bool quiet, unsigned char default_alpha = 0xff); @@ -115,23 +118,32 @@ inline std::string padStringRight(std::string str, size_t len) * * @return If no end could be removed then "" is returned. */ -inline std::string removeStringEnd(const std::string &str, +inline std::string_view removeStringEnd(std::string_view str, const char *ends[]) { const char **p = ends; for (; *p && (*p)[0] != '\0'; p++) { - std::string end = *p; + std::string_view end(*p); if (str.size() < end.size()) continue; if (str.compare(str.size() - end.size(), end.size(), end) == 0) return str.substr(0, str.size() - end.size()); } - return ""; + return std::string_view(); } +#define MAKE_VARIANT(_name, _t0, _t1) \ + template \ + inline auto _name(_t0 arg1, _t1 arg2, Args&&... args) \ + { \ + return (_name)(std::basic_string_view(arg1), std::basic_string_view(arg2), \ + std::forward(args)...); \ + } + + /** * Check two strings for equivalence. If \p case_insensitive is true * then the case of the strings is ignored (default is false). @@ -142,8 +154,8 @@ inline std::string removeStringEnd(const std::string &str, * @return true if the strings match */ template -inline bool str_equal(const std::basic_string &s1, - const std::basic_string &s2, +inline bool str_equal(std::basic_string_view s1, + std::basic_string_view s2, bool case_insensitive = false) { if (!case_insensitive) @@ -159,6 +171,16 @@ inline bool str_equal(const std::basic_string &s1, return true; } +// For some reason an std::string will not implicitly get converted +// to an std::basic_string_view in the template case above, so we need +// these three wrappers. It works if you take out the template parameters. +// see also +MAKE_VARIANT(str_equal, const std::basic_string &, const std::basic_string &) + +MAKE_VARIANT(str_equal, std::basic_string_view, const std::basic_string &) + +MAKE_VARIANT(str_equal, const std::basic_string &, std::basic_string_view) + /** * Check whether \p str begins with the string prefix. If \p case_insensitive @@ -171,8 +193,8 @@ inline bool str_equal(const std::basic_string &s1, * @return true if the str begins with prefix */ template -inline bool str_starts_with(const std::basic_string &str, - const std::basic_string &prefix, +inline bool str_starts_with(std::basic_string_view str, + std::basic_string_view prefix, bool case_insensitive = false) { if (str.size() < prefix.size()) @@ -187,24 +209,17 @@ inline bool str_starts_with(const std::basic_string &str, return true; } -/** - * Check whether \p str begins with the string prefix. If \p case_insensitive - * is true then the check is case insensitve (default is false; i.e. case is - * significant). - * - * @param str - * @param prefix - * @param case_insensitive - * @return true if the str begins with prefix - */ -template -inline bool str_starts_with(const std::basic_string &str, - const T *prefix, - bool case_insensitive = false) -{ - return str_starts_with(str, std::basic_string(prefix), - case_insensitive); -} +// (same conversion issue here) +MAKE_VARIANT(str_starts_with, const std::basic_string &, const std::basic_string &) + +MAKE_VARIANT(str_starts_with, std::basic_string_view, const std::basic_string &) + +MAKE_VARIANT(str_starts_with, const std::basic_string &, std::basic_string_view) + +// (the same but with char pointers, only for the prefix argument) +MAKE_VARIANT(str_starts_with, const std::basic_string &, const T*) + +MAKE_VARIANT(str_starts_with, std::basic_string_view, const T*) /** @@ -218,8 +233,8 @@ inline bool str_starts_with(const std::basic_string &str, * @return true if the str begins with suffix */ template -inline bool str_ends_with(const std::basic_string &str, - const std::basic_string &suffix, +inline bool str_ends_with(std::basic_string_view str, + std::basic_string_view suffix, bool case_insensitive = false) { if (str.size() < suffix.size()) @@ -235,25 +250,20 @@ inline bool str_ends_with(const std::basic_string &str, return true; } +// (same conversion issue here) +MAKE_VARIANT(str_ends_with, const std::basic_string &, const std::basic_string &) -/** - * Check whether \p str ends with the string suffix. If \p case_insensitive - * is true then the check is case insensitve (default is false; i.e. case is - * significant). - * - * @param str - * @param suffix - * @param case_insensitive - * @return true if the str begins with suffix - */ -template -inline bool str_ends_with(const std::basic_string &str, - const T *suffix, - bool case_insensitive = false) -{ - return str_ends_with(str, std::basic_string(suffix), - case_insensitive); -} +MAKE_VARIANT(str_ends_with, std::basic_string_view, const std::basic_string &) + +MAKE_VARIANT(str_ends_with, const std::basic_string &, std::basic_string_view) + +// (the same but with char pointers, only for the suffix argument) +MAKE_VARIANT(str_ends_with, const std::basic_string &, const T*) + +MAKE_VARIANT(str_ends_with, std::basic_string_view, const T*) + + +#undef MAKE_VARIANT /** @@ -282,24 +292,21 @@ inline std::vector > str_split( * @param str * @return A copy of \p str converted to all lowercase characters. */ -inline std::string lowercase(const std::string &str) +inline std::string lowercase(std::string_view str) { std::string s2; - - s2.reserve(str.size()); - - for (char i : str) - s2 += tolower(i); - + s2.resize(str.size()); + for (size_t i = 0; i < str.size(); i++) + s2[i] = tolower(str[i]); return s2; } /** * @param str - * @return A copy of \p str with leading and trailing whitespace removed. + * @return A view of \p str with leading and trailing whitespace removed. */ -inline std::string trim(const std::string &str) +inline std::string_view trim(std::string_view str) { size_t front = 0; size_t back = str.size(); @@ -313,6 +320,26 @@ inline std::string trim(const std::string &str) return str.substr(front, back - front); } +// If input was a temporary string keep it one to make sure patterns like +// trim(func_that_returns_str()) are predictable regarding memory allocation +// and don't lead to UAF. ↓ ↓ ↓ + +/** + * @param str + * @return A copy of \p str with leading and trailing whitespace removed. + */ +inline std::string trim(std::string &&str) +{ + std::string ret(trim(std::string_view(str))); + return ret; +} + +// The above declaration causes ambiguity with char pointers so we have to fix that: +inline std::string_view trim(const char *str) +{ + return trim(std::string_view(str)); +} + /** * Returns whether \p str should be regarded as (bool) true. Case and leading @@ -320,7 +347,7 @@ inline std::string trim(const std::string &str) * true are "y", "yes", "true" and any number that is not 0. * @param str */ -inline bool is_yes(const std::string &str) +inline bool is_yes(std::string_view str) { std::string s2 = lowercase(trim(str)); @@ -377,7 +404,7 @@ inline float mystof(const std::string &str) template inline T from_string(const std::string &str) { - std::stringstream tmp(str); + std::istringstream tmp(str); T t; tmp >> t; return t; @@ -386,42 +413,6 @@ inline T from_string(const std::string &str) /// Returns a 64-bit signed value represented by the string \p str (decimal). inline s64 stoi64(const std::string &str) { return from_string(str); } -#if __cplusplus < 201103L -namespace std { - -/// Returns a string representing the value \p val. -template -inline string to_string(T val) -{ - ostringstream oss; - oss << val; - return oss.str(); -} -#define DEFINE_STD_TOSTRING_FLOATINGPOINT(T) \ - template <> \ - inline string to_string(T val) \ - { \ - ostringstream oss; \ - oss << std::fixed \ - << std::setprecision(6) \ - << val; \ - return oss.str(); \ - } -DEFINE_STD_TOSTRING_FLOATINGPOINT(float) -DEFINE_STD_TOSTRING_FLOATINGPOINT(double) -DEFINE_STD_TOSTRING_FLOATINGPOINT(long double) - -#undef DEFINE_STD_TOSTRING_FLOATINGPOINT - -/// Returns a wide string representing the value \p val -template -inline wstring to_wstring(T val) -{ - return utf8_to_wide(to_string(val)); -} -} -#endif - /// Returns a string representing the decimal value of the 32-bit value \p i. inline std::string itos(s32 i) { return std::to_string(i); } /// Returns a string representing the decimal value of the 64-bit value \p i. @@ -443,8 +434,8 @@ inline std::string ftos(float f) * @param pattern The pattern to replace. * @param replacement What to replace the pattern with. */ -inline void str_replace(std::string &str, const std::string &pattern, - const std::string &replacement) +inline void str_replace(std::string &str, std::string_view pattern, + std::string_view replacement) { std::string::size_type start = str.find(pattern, 0); while (start != str.npos) { @@ -454,7 +445,7 @@ inline void str_replace(std::string &str, const std::string &pattern, } /** - * Escapes characters [ ] \ , ; that cannot be used in formspecs + * Escapes characters that cannot be used in formspecs */ inline void str_formspec_escape(std::string &str) { @@ -486,7 +477,7 @@ void str_replace(std::string &str, char from, char to); * * @see string_allowed_blacklist() */ -inline bool string_allowed(const std::string &str, const std::string &allowed_chars) +inline bool string_allowed(std::string_view str, std::string_view allowed_chars) { return str.find_first_not_of(allowed_chars) == str.npos; } @@ -502,8 +493,8 @@ inline bool string_allowed(const std::string &str, const std::string &allowed_ch * @see string_allowed() */ -inline bool string_allowed_blacklist(const std::string &str, - const std::string &blacklisted_chars) +inline bool string_allowed_blacklist(std::string_view str, + std::string_view blacklisted_chars) { return str.find_first_of(blacklisted_chars) == str.npos; } @@ -524,12 +515,12 @@ inline bool string_allowed_blacklist(const std::string &str, * @param row_len The row length (in characters). * @return A new string with the wrapping applied. */ -inline std::string wrap_rows(const std::string &from, - unsigned row_len) +inline std::string wrap_rows(std::string_view from, unsigned row_len) { std::string to; + to.reserve(from.size()); - size_t character_idx = 0; + unsigned character_idx = 0; for (size_t i = 0; i < from.size(); i++) { if (!IS_UTF8_MULTB_INNER(from[i])) { // Wrap string after last inner byte of char @@ -551,6 +542,7 @@ template inline std::basic_string unescape_string(const std::basic_string &s) { std::basic_string res; + res.reserve(s.size()); for (size_t i = 0; i < s.length(); i++) { if (s[i] == '\\') { @@ -574,6 +566,7 @@ template std::basic_string unescape_enriched(const std::basic_string &s) { std::basic_string output; + output.reserve(s.size()); size_t i = 0; while (i < s.length()) { if (s[i] == '\x1b') { @@ -646,7 +639,7 @@ inline std::wstring unescape_translate(const std::wstring &s) { * @return true if to_check is not empty and all characters in to_check are * decimal digits, otherwise false */ -inline bool is_number(const std::string &to_check) +inline bool is_number(std::string_view to_check) { for (char i : to_check) if (!std::isdigit(i)) @@ -720,7 +713,7 @@ inline const std::string duration_to_string(int sec) * @return A std::string */ inline std::string str_join(const std::vector &list, - const std::string &delimiter) + std::string_view delimiter) { std::ostringstream oss; bool first = true; @@ -738,17 +731,17 @@ inline std::string str_join(const std::vector &list, */ inline std::string stringw_to_utf8(const irr::core::stringw &input) { - std::wstring str(input.c_str()); - return wide_to_utf8(str); + std::wstring_view sv(input.c_str(), input.size()); + return wide_to_utf8(sv); } /** * Create an irr::core:stringw from a UTF8 std::string. */ -inline irr::core::stringw utf8_to_stringw(const std::string &input) +inline irr::core::stringw utf8_to_stringw(std::string_view input) { std::wstring str = utf8_to_wide(input); - return irr::core::stringw(str.c_str()); + return irr::core::stringw(str.c_str(), str.size()); } /** @@ -757,7 +750,7 @@ inline irr::core::stringw utf8_to_stringw(const std::string &input) * and add a prefix to them * 2. Remove 'unsafe' characters from the name by replacing them with '_' */ -std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix); +std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix); /** * Prints a sanitized version of a string without control characters. @@ -765,12 +758,12 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_ * ASCII control characters are replaced with their hex encoding in angle * brackets (e.g. "a\x1eb" -> "a<1e>b"). */ -void safe_print_string(std::ostream &os, const std::string &str); +void safe_print_string(std::ostream &os, std::string_view str); /** * Parses a string of form `(1, 2, 3)` to a v3f * - * @param str String - * @return + * @param str string + * @return float vector */ -v3f str_to_v3f(const std::string &str); +v3f str_to_v3f(std::string_view str);