From 0b374cd08f06a182f571e37676d32963db4a6b35 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Tue, 6 Jun 2023 22:16:21 +0200 Subject: [PATCH] maint: better parsing of main arguments Co-authored-by: William Roy --- src/main.cpp | 98 ++++++++++++++------------------------- src/std_include.hpp | 5 +- src/utils/memory.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++ src/utils/memory.hpp | 68 +++++++++++++++++++++++++++ src/utils/string.cpp | 13 ++++++ src/utils/string.hpp | 80 ++++++++++++++++++++++++++++++++ 6 files changed, 305 insertions(+), 65 deletions(-) create mode 100644 src/utils/memory.cpp create mode 100644 src/utils/memory.hpp diff --git a/src/main.cpp b/src/main.cpp index cbafbcb..f10a3ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,116 +6,86 @@ #include "game/cg_client_side_effects_mp.hpp" #include +#include namespace { - bool load_client_effects(const std::string& filename) + void load_client_effects(const std::string& filename) { + assert(!filename.empty()); if (filename.empty()) { - console::error("filename parameter is empty\n"); - return false; + throw std::runtime_error("filename parameter is empty"); } - const auto data = utils::io::read_file(filename); - if (data.empty()) + std::string data; + if (!utils::io::read_file(filename, &data) || data.empty()) { - console::error("'%s' is empty\n", filename.data()); - return false; + throw std::runtime_error(utils::string::va("'%s' is empty", filename.data())); } - return game::parse_client_effects(data.data()); + game::parse_client_effects(data.data()); } - bool load_map_rotation(const std::string& filename) + void load_map_rotation(const std::string& filename) { + assert(!filename.empty()); if (filename.empty()) { - console::error("filename parameter is empty\n"); - return false; + throw std::runtime_error("filename parameter is empty"); } - const auto data = utils::io::read_file(filename); - if (data.empty()) + std::string data; + if (!utils::io::read_file(filename, &data) || data.empty()) { - console::error("'%s' is empty\n", filename.data()); - return false; + throw std::runtime_error(utils::string::va("'%s' is empty", filename.data())); } try { map_rotation::rotation_data rotation_data; rotation_data.parse(data); + + console::info("Successfully parsed map rotation\n"); } catch (const std::exception& ex) { - console::error("%s: '%s' contains invalid data!\n", ex.what(), filename.data()); - return false; + console::error(utils::string::va("%s: '%s' contains invalid data!", ex.what(), filename.data())); } - - console::info("Successfully parsed map rotation\n"); - return true; } -} -int unsafe_main(std::string&& prog, std::vector&& args) -{ - // Parse command-line flags (only increment i for matching flags) - for (auto i = args.begin(); i != args.end();) + void unsafe_main(const std::span s) { - if (*i == "-createfx") + auto p = [&](const std::string& o, const std::function & c) -> void { - ++i; - const auto filename = i != args.end() ? *i++ : std::string(); - console::info("Parsing createfx '%s'\n", filename.data()); - - if (!load_client_effects(filename)) + auto r(s | std::views::transform([](char* v) -> std::string { - return EXIT_FAILURE; - } - } - else if (*i == "-map-rotation") - { - ++i; - const auto filename = i != args.end() ? *i++ : std::string(); - console::info("Parsing map rotation '%s'\n", filename.data()); + return { v }; + })); - if (!load_map_rotation(filename)) + const auto i(std::ranges::find(r, o)); + const auto e(r.end()); + + if (auto n(i != e ? std::ranges::next(i, 1, e) : e); i != e && n != e) { - return EXIT_FAILURE; + c({ *n }); } + }; - } - else - { - console::info("X Labs IW4x validator tool\n" - "Usage: %s OPTIONS\n" - " -createfx \n" - " -fx \n" - " -map-rotation \n", - prog.data() - ); - - return EXIT_FAILURE; - } + p("-createfx", load_client_effects); + p("-map-rotation", load_map_rotation); } - - return EXIT_SUCCESS; } int main(int argc, char* argv[]) { - console::set_title("X Labs IW4x-validator"); - console::log("Starting X Labs IW4x-validator"); + console::set_title("AlterWare IW4x-validator"); + console::log("Starting AlterWare iw4-validator"); try { - std::string prog(argv[0]); - std::vector args; - - args.reserve(argc - 1); - args.assign(argv + 1, argv + argc); - return unsafe_main(std::move(prog), std::move(args)); + unsafe_main(std::span(argv, argc)); + return EXIT_SUCCESS; } catch (const std::exception& ex) { diff --git a/src/std_include.hpp b/src/std_include.hpp index 4132c95..13f30d8 100644 --- a/src/std_include.hpp +++ b/src/std_include.hpp @@ -22,7 +22,7 @@ #undef min #endif -#include + #include #include #include @@ -30,12 +30,15 @@ #include #include #include + +#include #include #include #include #include #include #include +#include #include #include #include diff --git a/src/utils/memory.cpp b/src/utils/memory.cpp new file mode 100644 index 0000000..40fee57 --- /dev/null +++ b/src/utils/memory.cpp @@ -0,0 +1,106 @@ +#include +#include "memory.hpp" + +namespace utils +{ + memory::allocator memory::mem_allocator_; + + memory::allocator::~allocator() + { + this->clear(); + } + + void memory::allocator::clear() + { + std::lock_guard _(this->mutex_); + + for (const auto& data : this->pool_) + { + memory::free(data); + } + + this->pool_.clear(); + } + + void memory::allocator::free(void* data) + { + std::lock_guard _(this->mutex_); + + const auto j = std::find(this->pool_.begin(), this->pool_.end(), data); + if (j != this->pool_.end()) + { + memory::free(data); + this->pool_.erase(j); + } + } + + void memory::allocator::free(const void* data) + { + this->free(const_cast(data)); + } + + void* memory::allocator::allocate(const std::size_t length) + { + std::lock_guard _(this->mutex_); + + const auto data = memory::allocate(length); + this->pool_.push_back(data); + return data; + } + + bool memory::allocator::empty() const + { + return this->pool_.empty(); + } + + char* memory::allocator::duplicate_string(const std::string& string) + { + std::lock_guard _(this->mutex_); + + const auto data = memory::duplicate_string(string); + this->pool_.push_back(data); + return data; + } + + void* memory::allocate(const std::size_t length) + { + return ::calloc(1, length); + } + + char* memory::duplicate_string(const std::string& string) + { + const auto new_string = allocate_array(string.size() + 1); + std::memcpy(new_string, string.data(), string.size()); + return new_string; + } + + void memory::free(void* data) + { + ::free(data); + } + + void memory::free(const void* data) + { + free(const_cast(data)); + } + + bool memory::is_set(const void* mem, const char chr, const std::size_t length) + { + auto* const mem_arr = static_cast(mem); + + for (std::size_t i = 0; i < length; ++i) + { + if (mem_arr[i] != chr) + { + return false; + } + } + + return true; + } + + memory::allocator* memory::get_allocator() + { + return &memory::mem_allocator_; + } +} diff --git a/src/utils/memory.hpp b/src/utils/memory.hpp new file mode 100644 index 0000000..e4402f9 --- /dev/null +++ b/src/utils/memory.hpp @@ -0,0 +1,68 @@ +#pragma once + +namespace utils +{ + class memory final + { + public: + class allocator final + { + public: + ~allocator(); + + void clear(); + + void free(void* data); + + void free(const void* data); + + void* allocate(std::size_t length); + + template + T* allocate() + { + return this->allocate_array(1); + } + + template + T* allocate_array(const std::size_t count = 1) + { + return static_cast(this->allocate(count * sizeof(T))); + } + + [[nodiscard]] bool empty() const; + + char* duplicate_string(const std::string& string); + + private: + std::mutex mutex_; + std::vector pool_; + }; + + static void* allocate(std::size_t length); + + template + static T* allocate() + { + return allocate_array(1); + } + + template + static T* allocate_array(const std::size_t count = 1) + { + return static_cast(allocate(count * sizeof(T))); + } + + static char* duplicate_string(const std::string& string); + + static void free(void* data); + static void free(const void* data); + + static bool is_set(const void* mem, char chr, std::size_t length); + + static allocator* get_allocator(); + + private: + static allocator mem_allocator_; + }; +} diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 8bcd7bb..26da6d8 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -3,6 +3,19 @@ namespace utils::string { + const char* va(const char* fmt, ...) + { + static thread_local va_provider<8, 256> provider; + + va_list ap; + va_start(ap, fmt); + + const auto* result = provider.get(fmt, ap); + + va_end(ap); + return result; + } + std::vector split(const std::string& s, const char delim) { std::stringstream ss(s); diff --git a/src/utils/string.hpp b/src/utils/string.hpp index 56c65e3..8e2f729 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -1,7 +1,87 @@ #pragma once +#include "memory.hpp" namespace utils::string { + template + constexpr auto ARRAY_COUNT(Type(&)[n]) { return n; } + + template + class va_provider final + { + public: + static_assert(buffers != 0 && min_buffer_size != 0, "buffers and min_buffer_size mustn't be 0"); + + va_provider() : current_buffer_(0) + { + } + + char* get(const char* format, va_list ap) + { + ++this->current_buffer_ %= ARRAY_COUNT(this->string_pool_); + auto entry = &this->string_pool_[this->current_buffer_]; + + if (!entry->size_ || !entry->buffer_) + { + throw std::runtime_error("The string pool is not initialized"); + } + + while (true) + { +#ifdef _WIN32 + const auto res = vsnprintf_s(entry->buffer_, entry->size_, _TRUNCATE, format, ap); +#else + const auto res = vsnprintf(entry->buffer_, entry->size_, format, ap); +#endif + + if (res > 0) break; // Success + if (res == 0) return nullptr; // Error + + entry->double_size(); + } + + return entry->buffer_; + } + + private: + class entry final + { + public: + explicit entry(const std::size_t size = min_buffer_size) : size_(size), buffer_(nullptr) + { + if (this->size_ < min_buffer_size) this->size_ = min_buffer_size; + this->allocate(); + } + + ~entry() + { + if (this->buffer_) memory::get_allocator()->free(this->buffer_); + this->size_ = 0; + this->buffer_ = nullptr; + } + + void allocate() + { + if (this->buffer_) memory::get_allocator()->free(this->buffer_); + this->buffer_ = memory::get_allocator()->allocate_array(this->size_ + 1); + } + + void double_size() + { + this->size_ *= 2; + this->allocate(); + } + + std::size_t size_; + char* buffer_; + }; + + std::size_t current_buffer_; + entry string_pool_[buffers]; + }; + + const char* va(const char* fmt, ...); + std::vector split(const std::string& s, char delim); std::string to_lower(const std::string& text);