commit ff8d45a: [Minor] Use a separate error class instead of std::string

Vsevolod Stakhov vsevolod at rspamd.com
Mon Oct 17 21:42:04 UTC 2022


Author: Vsevolod Stakhov
Date: 2022-10-17 11:39:38 +0100
URL: https://github.com/rspamd/rspamd/commit/ff8d45abef65ae9552a5c089317f96208474bbe6 (HEAD -> master)

[Minor] Use a separate error class instead of std::string

---
 src/libserver/hyperscan_tools.cxx        |  29 ++++-----
 src/libserver/symcache/symcache_impl.cxx |   9 ++-
 src/libutil/cxx/error.hxx                | 100 +++++++++++++++++++++++++++++++
 src/libutil/cxx/file_util.cxx            |  52 ++++++++--------
 src/libutil/cxx/file_util.hxx            |  25 ++++----
 5 files changed, 162 insertions(+), 53 deletions(-)

diff --git a/src/libserver/hyperscan_tools.cxx b/src/libserver/hyperscan_tools.cxx
index a2acc0c18..1272aa452 100644
--- a/src/libserver/hyperscan_tools.cxx
+++ b/src/libserver/hyperscan_tools.cxx
@@ -21,6 +21,7 @@
 #include "contrib/ankerl/svector.h"
 #include "fmt/core.h"
 #include "libutil/cxx/file_util.hxx"
+#include "libutil/cxx/error.hxx"
 #include "hs.h"
 #include "logger.h"
 #include "worker_util.h"
@@ -150,35 +151,35 @@ struct hs_shared_database {
 };
 
 static auto
-hs_shared_from_unserialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, std::string>
+hs_shared_from_unserialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, error>
 {
 	auto ptr = map.get_map();
-	return tl::expected<hs_shared_database, std::string>{tl::in_place, std::move(map), (hs_database_t *)ptr};
+	return tl::expected<hs_shared_database, error>{tl::in_place, std::move(map), (hs_database_t *)ptr};
 }
 
 static auto
-hs_shared_from_serialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, std::string>
+hs_shared_from_serialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, error>
 {
 	hs_database_t *target = nullptr;
 
 	if (auto ret = hs_deserialize_database((const char *)map.get_map(), map.get_size(), &target); ret != HS_SUCCESS) {
-		return tl::make_unexpected("cannot deserialize database");
+		return tl::make_unexpected(error {"cannot deserialize database", ret});
 	}
 
-	return tl::expected<hs_shared_database, std::string>{tl::in_place, target};
+	return tl::expected<hs_shared_database, error>{tl::in_place, target};
 }
 
-auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database, std::string>
+auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database, error>
 {
 	auto &hs_cache = hs_known_files_cache::get();
 
 	return raii_mmaped_file::mmap_shared(fname, O_RDONLY, PROT_READ)
-		.and_then([&]<class T>(T &&cached_serialized) -> tl::expected<hs_shared_database, std::string> {
+		.and_then([&]<class T>(T &&cached_serialized) -> tl::expected<hs_shared_database, error> {
 #if defined(HS_MAJOR) && defined(HS_MINOR) && HS_MAJOR >= 5 && HS_MINOR >= 4
 			auto unserialized_fname = fmt::format("{}.unser", fname);
 			auto unserialized_file = raii_locked_file::create(unserialized_fname.c_str(), O_CREAT | O_RDWR | O_EXCL,
 				00644)
-				.and_then([&](auto &&new_file_locked) -> tl::expected<raii_file, std::string> {
+				.and_then([&](auto &&new_file_locked) -> tl::expected<raii_file, error> {
 					auto tmpfile_pattern = fmt::format("{}{}hsmp-XXXXXXXXXXXXXXXXXX",
 						cached_serialized.get_file().get_dir(), G_DIR_SEPARATOR);
 					auto tmpfile = raii_locked_file::mkstemp(tmpfile_pattern.data(), O_CREAT | O_RDWR | O_EXCL,
@@ -199,7 +200,7 @@ auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database,
 						void *buf;
 						posix_memalign(&buf, 16, unserialized_size);
 						if (buf == NULL) {
-							return tl::make_unexpected("Cannot allocate memory");
+							return tl::make_unexpected(error {"Cannot allocate memory", errno, error_category::CRITICAL });
 						}
 
 						// Store owned string
@@ -207,14 +208,14 @@ auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database,
 
 						if (auto ret = hs_deserialize_database_at((const char *)cached_serialized.get_map(),
 								cached_serialized.get_size(), (hs_database_t *) buf); ret != HS_SUCCESS) {
-							return tl::make_unexpected(
-								fmt::format("cannot deserialize hyperscan database: {}", ret));
+							return tl::make_unexpected(error {
+								fmt::format("cannot deserialize hyperscan database: {}", ret), ret });
 						}
 						else {
 							if (write(tmpfile_checked.get_fd(), buf, unserialized_size) == -1) {
 								free(buf);
-								return tl::make_unexpected(fmt::format("cannot write to {}: {}",
-									tmpfile_name, ::strerror(errno)));
+								return tl::make_unexpected(error { fmt::format("cannot write to {}: {}",
+									tmpfile_name, ::strerror(errno)), errno, error_category::CRITICAL });
 							}
 							else {
 								free(buf);
@@ -247,7 +248,7 @@ auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database,
 						return raii_file::open(unserialized_fname.c_str(), O_RDONLY);
 					};
 				})
-				.or_else([&](auto unused) -> tl::expected<raii_file, std::string> {
+				.or_else([&](auto unused) -> tl::expected<raii_file, error> {
 					// Cannot create file, so try to open it in RO mode
 					return raii_file::open(unserialized_fname.c_str(), O_RDONLY);
 				});
diff --git a/src/libserver/symcache/symcache_impl.cxx b/src/libserver/symcache/symcache_impl.cxx
index c29b9d6d4..4208d4882 100644
--- a/src/libserver/symcache/symcache_impl.cxx
+++ b/src/libserver/symcache/symcache_impl.cxx
@@ -232,7 +232,12 @@ auto symcache::load_items() -> bool
 			O_RDONLY, PROT_READ);
 
 	if (!cached_map.has_value()) {
-		msg_info_cache("%s", cached_map.error().c_str());
+		if (cached_map.error().category == util::error_category::CRITICAL) {
+			msg_err_cache("%s", cached_map.error().error_message.data());
+		}
+		else {
+			msg_info_cache("%s", cached_map.error().error_message.data());
+		}
 		return false;
 	}
 
@@ -369,7 +374,7 @@ bool symcache::save_items() const
 			return false;
 		}
 
-		msg_err_cache("%s", file_sink.error().c_str());
+		msg_err_cache("%s", file_sink.error().error_message.data());
 
 		return false;
 	}
diff --git a/src/libutil/cxx/error.hxx b/src/libutil/cxx/error.hxx
new file mode 100644
index 000000000..65fa76f3b
--- /dev/null
+++ b/src/libutil/cxx/error.hxx
@@ -0,0 +1,100 @@
+/*-
+ * Copyright 2022 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RSPAMD_ERROR_HXX
+#define RSPAMD_ERROR_HXX
+#pragma once
+
+#include "config.h"
+#include <string>
+#include <string_view>
+#include <cstdint>
+#include <optional>
+
+/***
+ * This unit is used to represent Rspamd C++ errors in a way to interoperate
+ * with C code if needed and avoid allocations for static strings
+ */
+namespace rspamd::util {
+
+enum class error_category : std::uint8_t {
+	INFORMAL,
+	IMPORTANT,
+	CRITICAL
+};
+
+struct error {
+public:
+	/**
+	 * Construct from a static string, this string must live long enough to outlive this object
+	 * @param msg
+	 * @param code
+	 * @param category
+	 */
+	error(const char *msg, int code, error_category category = error_category::INFORMAL) :
+		error_message(msg), error_code(code), category(category) {}
+	/**
+	 * Construct error from a temporary string taking membership
+	 * @param msg
+	 * @param code
+	 * @param category
+	 */
+	error(std::string &&msg, int code, error_category category = error_category::INFORMAL)
+		: error_code(code), category(category) {
+		static_storage = std::move(msg);
+		error_message = static_storage.value();
+	}
+	/**
+	 * Construct error from another string copying it into own storage
+	 * @param msg
+	 * @param code
+	 * @param category
+	 */
+	error(const std::string &msg, int code, error_category category = error_category::INFORMAL)
+		: error_code(code), category(category) {
+		static_storage = msg;
+		error_message = static_storage.value();
+	}
+
+	/**
+	 * Convert into GError
+	 * @return
+	 */
+	auto into_g_error() const -> GError * {
+		return g_error_new(g_quark_from_static_string("rspamd"), error_code, "%s",
+			error_message.data());
+	}
+
+	/**
+	 * Convenience alias for the `into_g_error`
+	 * @param err
+	 */
+	auto into_g_error_set(GError **err) const -> void {
+		if (err && *err == nullptr) {
+			*err = into_g_error();
+		}
+	}
+public:
+	std::string_view error_message;
+	int error_code;
+	error_category category;
+private:
+	std::optional<std::string> static_storage;
+};
+
+} // namespace rspamd::util
+
+#endif //RSPAMD_ERROR_HXX
diff --git a/src/libutil/cxx/file_util.cxx b/src/libutil/cxx/file_util.cxx
index 10a91a251..d0860b77b 100644
--- a/src/libutil/cxx/file_util.cxx
+++ b/src/libutil/cxx/file_util.cxx
@@ -24,7 +24,7 @@
 
 namespace rspamd::util {
 
-auto raii_file::open(const char *fname, int flags) -> tl::expected<raii_file, std::string>
+auto raii_file::open(const char *fname, int flags) -> tl::expected<raii_file, error>
 {
 	int oflags = flags;
 #ifdef O_CLOEXEC
@@ -32,25 +32,25 @@ auto raii_file::open(const char *fname, int flags) -> tl::expected<raii_file, st
 #endif
 
 	if (fname == nullptr) {
-		return tl::make_unexpected("cannot open file; filename is nullptr");
+		return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
 	}
 
 	auto fd = ::open(fname, oflags);
 
 	if (fd == -1) {
-		return tl::make_unexpected(fmt::format("cannot open file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error{fmt::format("cannot open file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	auto ret = raii_file{fname, fd, false};
 
 	if (fstat(ret.fd, &ret.st) == -1) {
-		return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error {fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	return ret;
 }
 
-auto raii_file::create(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>
 {
 	int oflags = flags;
 #ifdef O_CLOEXEC
@@ -58,57 +58,58 @@ auto raii_file::create(const char *fname, int flags, int perms) -> tl::expected<
 #endif
 
 	if (fname == nullptr) {
-		return tl::make_unexpected("cannot open file; filename is nullptr");
+		return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
 	}
 
 	auto fd = ::open(fname, oflags, perms);
 
 	if (fd == -1) {
-		return tl::make_unexpected(fmt::format("cannot create file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error{fmt::format("cannot create file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	auto ret = raii_file{fname, fd, false};
 
 	if (fstat(ret.fd, &ret.st) == -1) {
-		return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error{fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	return ret;
 }
 
-auto raii_file::create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>
 {
 	int oflags = flags;
 #ifdef O_CLOEXEC
 	oflags |= O_CLOEXEC | O_CREAT | O_EXCL;
 #endif
 	if (fname == nullptr) {
-		return tl::make_unexpected("cannot open file; filename is nullptr");
+		return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
 	}
 
 	auto fd = ::open(fname, oflags, perms);
 
 	if (fd == -1) {
-		return tl::make_unexpected(fmt::format("cannot create file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error {fmt::format("cannot create file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	auto ret = raii_file{fname, fd, true};
 
 	if (fstat(ret.fd, &ret.st) == -1) {
-		return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+		return tl::make_unexpected(error {fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
 	}
 
 	return ret;
 }
 
-auto raii_file::mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>
 {
 	int oflags = flags;
 #ifdef O_CLOEXEC
 	oflags |= O_CLOEXEC | O_CREAT | O_EXCL;
 #endif
 	if (pattern == nullptr) {
-		return tl::make_unexpected("cannot open file; pattern is nullptr");
+		return tl::make_unexpected(error {"cannot open file; pattern is nullptr", EINVAL, error_category::CRITICAL});
+
 	}
 
 	std::string mutable_pattern = pattern;
@@ -116,14 +117,14 @@ auto raii_file::mkstemp(const char *pattern, int flags, int perms) -> tl::expect
 	auto fd = g_mkstemp_full(mutable_pattern.data(), oflags, perms);
 
 	if (fd == -1) {
-		return tl::make_unexpected(fmt::format("cannot create file {}: {}", pattern, ::strerror(errno)));
+		return tl::make_unexpected(error {fmt::format("cannot create file {}: {}", pattern, ::strerror(errno)), errno});
 	}
 
 	auto ret = raii_file{mutable_pattern.c_str(), fd, true};
 
 	if (fstat(ret.fd, &ret.st) == -1) {
-		return tl::make_unexpected(fmt::format("cannot stat file {}: {}",
-				mutable_pattern.c_str(), ::strerror(errno)));
+		return tl::make_unexpected(error { fmt::format("cannot stat file {}: {}",
+				mutable_pattern, ::strerror(errno)), errno} );
 	}
 
 	return ret;
@@ -152,10 +153,11 @@ raii_locked_file::~raii_locked_file() noexcept
 	}
 }
 
-auto raii_locked_file::lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, std::string>
+auto raii_locked_file::lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>
 {
 	if (!rspamd_file_lock(unlocked.get_fd(), TRUE)) {
-		return tl::make_unexpected(fmt::format("cannot lock file {}: {}", unlocked.get_name(), ::strerror(errno)));
+		return tl::make_unexpected(
+			error { fmt::format("cannot lock file {}: {}", unlocked.get_name(), ::strerror(errno)), errno});
 	}
 
 	return raii_locked_file{std::move(unlocked)};
@@ -175,7 +177,7 @@ raii_mmaped_file::raii_mmaped_file(raii_file &&_file, void *_map)
 }
 
 auto raii_mmaped_file::mmap_shared(raii_file &&file,
-								   int flags) -> tl::expected<raii_mmaped_file, std::string>
+								   int flags) -> tl::expected<raii_mmaped_file, error>
 {
 	void *map;
 
@@ -184,8 +186,8 @@ auto raii_mmaped_file::mmap_shared(raii_file &&file,
 	map = mmap(NULL, file.get_stat().st_size, flags, MAP_SHARED, file.get_fd(), 0);
 
 	if (map == MAP_FAILED) {
-		return tl::make_unexpected(fmt::format("cannot mmap file at fd: {}: {}",
-				file.get_fd(), ::strerror(errno)));
+		return tl::make_unexpected(error { fmt::format("cannot mmap file at fd: {}: {}",
+				file.get_fd(), ::strerror(errno)), errno });
 
 	}
 
@@ -193,7 +195,7 @@ auto raii_mmaped_file::mmap_shared(raii_file &&file,
 }
 
 auto raii_mmaped_file::mmap_shared(const char *fname, int open_flags,
-								   int mmap_flags) -> tl::expected<raii_mmaped_file, std::string>
+								   int mmap_flags) -> tl::expected<raii_mmaped_file, error>
 {
 	auto file = raii_file::open(fname, open_flags);
 
@@ -218,10 +220,10 @@ raii_mmaped_file::raii_mmaped_file(raii_mmaped_file &&other) noexcept
 }
 
 auto raii_file_sink::create(const char *fname, int flags, int perms,
-							const char *suffix) -> tl::expected<raii_file_sink, std::string>
+							const char *suffix) -> tl::expected<raii_file_sink, error>
 {
 	if (!fname || !suffix) {
-		return tl::make_unexpected("cannot create file sink: bad input arguments");
+		return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
 	}
 
 	auto tmp_fname = fmt::format("{}.{}", fname, suffix);
diff --git a/src/libutil/cxx/file_util.hxx b/src/libutil/cxx/file_util.hxx
index c66fd17ef..097ce03a7 100644
--- a/src/libutil/cxx/file_util.hxx
+++ b/src/libutil/cxx/file_util.hxx
@@ -19,6 +19,7 @@
 
 #include "config.h"
 #include "contrib/expected/expected.hpp"
+#include "libutil/cxx/error.hxx"
 #include <string>
 #include <sys/stat.h>
 
@@ -31,10 +32,10 @@ struct raii_file {
 public:
 	virtual ~raii_file() noexcept;
 
-	static auto open(const char *fname, int flags) -> tl::expected<raii_file, std::string>;
-	static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>;
-	static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>;
-	static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, std::string>;
+	static auto open(const char *fname, int flags) -> tl::expected<raii_file, error>;
+	static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+	static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+	static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>;
 
 	auto get_fd() const -> int {
 		return fd;
@@ -135,28 +136,28 @@ struct raii_locked_file final : public raii_file {
 public:
 	~raii_locked_file() noexcept override;
 
-	static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, std::string> {
+	static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, error> {
 		auto locked = raii_file::open(fname, flags).and_then([]<class T>(T &&file) {
 			return lock_raii_file(std::forward<T>(file));
 		});
 
 		return locked;
 	}
-	static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+	static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error> {
 		auto locked = raii_file::create(fname, flags, perms).and_then([]<class T>(T &&file) {
 			return lock_raii_file(std::forward<T>(file));
 		});
 
 		return locked;
 	}
-	static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+	static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error> {
 		auto locked = raii_file::create_temp(fname, flags, perms).and_then([]<class T>(T &&file) {
 			return lock_raii_file(std::forward<T>(file));
 		});
 
 		return locked;
 	}
-	static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+	static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, error> {
 		auto locked = raii_file::mkstemp(pattern, flags, perms).and_then([]<class T>(T &&file) {
 			return lock_raii_file(std::forward<T>(file));
 		});
@@ -185,7 +186,7 @@ public:
 	raii_locked_file() = delete;
 	raii_locked_file(const raii_locked_file &other) = delete;
 private:
-	static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, std::string>;
+	static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>;
 	raii_locked_file(raii_file &&other) noexcept : raii_file(std::move(other)) {}
 	explicit raii_locked_file(const char *fname, int fd, bool temp) : raii_file(fname, fd, temp) {}
 };
@@ -195,8 +196,8 @@ private:
  */
 struct raii_mmaped_file final {
 	~raii_mmaped_file();
-	static auto mmap_shared(raii_file &&file, int flags) -> tl::expected<raii_mmaped_file, std::string>;
-	static auto mmap_shared(const char *fname, int open_flags, int mmap_flags) -> tl::expected<raii_mmaped_file, std::string>;
+	static auto mmap_shared(raii_file &&file, int flags) -> tl::expected<raii_mmaped_file, error>;
+	static auto mmap_shared(const char *fname, int open_flags, int mmap_flags) -> tl::expected<raii_mmaped_file, error>;
 	// Returns a constant pointer to the underlying map
 	auto get_map() const -> void* {return map;}
 	auto get_file() const -> const raii_file& { return file; }
@@ -235,7 +236,7 @@ private:
  */
 struct raii_file_sink final {
 	static auto create(const char *fname, int flags, int perms, const char *suffix = "new")
-		-> tl::expected<raii_file_sink, std::string>;
+		-> tl::expected<raii_file_sink, error>;
 	auto write_output() -> bool;
 	~raii_file_sink();
 	auto get_fd() const -> int


More information about the Commits mailing list