From 7c2870bbeeb638864644a1cffb6cfa4f859f9557 Mon Sep 17 00:00:00 2001 From: FutureRave Date: Fri, 25 Nov 2022 19:44:46 +0000 Subject: [PATCH] maint: downgrade to c++14 to expand portability --- premake5.lua | 4 +- src/network/address.hpp | 14 +- src/utils/cryptography.cpp | 1107 ++++++++++++++++++------------------ src/utils/cryptography.hpp | 193 +++---- src/utils/io.cpp | 156 ++--- src/utils/io.hpp | 25 +- src/utils/memory.cpp | 8 +- src/utils/memory.hpp | 8 +- src/utils/string.cpp | 147 ++--- src/utils/string.hpp | 145 ++--- 10 files changed, 887 insertions(+), 920 deletions(-) diff --git a/premake5.lua b/premake5.lua index 0bec753..807fe11 100644 --- a/premake5.lua +++ b/premake5.lua @@ -57,11 +57,11 @@ filter "platforms:arm64" filter {} filter {"language:C++", "toolset:not msc*"} - buildoptions "-std=c++17" + buildoptions "-std=c++14" filter {} filter "toolset:msc*" - buildoptions "/std:c++17" + buildoptions "/std:c++14" filter {} filter {"system:windows"} diff --git a/src/network/address.hpp b/src/network/address.hpp index d3ef065..d922180 100644 --- a/src/network/address.hpp +++ b/src/network/address.hpp @@ -11,15 +11,15 @@ namespace network void set_ipv4(in_addr addr); void set_port(unsigned short port); - [[nodiscard]] unsigned short get_port() const; + GSL_NODISCARD unsigned short get_port() const; - [[nodiscard]] sockaddr& get_addr(); - [[nodiscard]] const sockaddr& get_addr() const; - [[nodiscard]] sockaddr_in& get_in_addr(); - [[nodiscard]] const sockaddr_in& get_in_addr() const; + GSL_NODISCARD sockaddr& get_addr(); + GSL_NODISCARD const sockaddr& get_addr() const; + GSL_NODISCARD sockaddr_in& get_in_addr(); + GSL_NODISCARD const sockaddr_in& get_in_addr() const; - [[nodiscard]] bool is_local() const; - [[nodiscard]] std::string to_string(bool with_port = true) const; + GSL_NODISCARD bool is_local() const; + GSL_NODISCARD std::string to_string(bool with_port = true) const; bool operator==(const address& obj) const; diff --git a/src/utils/cryptography.cpp b/src/utils/cryptography.cpp index e164f97..8364d6e 100644 --- a/src/utils/cryptography.cpp +++ b/src/utils/cryptography.cpp @@ -7,633 +7,630 @@ using namespace std::literals; /// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf -namespace utils::cryptography +namespace utils { - namespace + namespace cryptography { - struct __ + namespace { - __() + struct __ { - ltc_mp = ltm_desc; - - register_cipher(&aes_desc); - register_cipher(&des3_desc); - - register_prng(&sprng_desc); - register_prng(&fortuna_desc); - register_prng(&yarrow_desc); - - register_hash(&sha1_desc); - register_hash(&sha256_desc); - register_hash(&sha512_desc); - } - } ___; - - [[maybe_unused]] const char* cs(const std::uint8_t* data) - { - return reinterpret_cast(data); - } - - [[maybe_unused]] char* cs(std::uint8_t* data) - { - return reinterpret_cast(data); - } - - [[maybe_unused]] const std::uint8_t* cs(const char* data) - { - return reinterpret_cast(data); - } - - [[maybe_unused]] std::uint8_t* cs(char* data) - { - return reinterpret_cast(data); - } - - [[maybe_unused]] unsigned long ul(const std::size_t value) - { - return static_cast(value); - } - - class prng - { - public: - prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true) - : state_(std::make_unique()) - , descriptor_(descriptor) - { - this->id_ = register_prng(&descriptor); - if (this->id_ == -1) + __() { - throw std::runtime_error("PRNG "s + this->descriptor_.name + " could not be registered!"); + ltc_mp = ltm_desc; + + register_cipher(&aes_desc); + register_cipher(&des3_desc); + + register_prng(&sprng_desc); + register_prng(&fortuna_desc); + register_prng(&yarrow_desc); + + register_hash(&sha1_desc); + register_hash(&sha256_desc); + register_hash(&sha512_desc); + } + } ___; + + GSL_NODISCARD const char* cs(const std::uint8_t* data) + { + return reinterpret_cast(data); + } + + GSL_NODISCARD char* cs(std::uint8_t* data) + { + return reinterpret_cast(data); + } + + GSL_NODISCARD const std::uint8_t* cs(const char* data) + { + return reinterpret_cast(data); + } + + GSL_NODISCARD std::uint8_t* cs(char* data) + { + return reinterpret_cast(data); + } + + GSL_NODISCARD unsigned long ul(const std::size_t value) + { + return static_cast(value); + } + + class prng + { + public: + prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true) + : state_(std::make_unique()) + , descriptor_(descriptor) + { + this->id_ = register_prng(&descriptor); + if (this->id_ == -1) + { + throw std::runtime_error("PRNG "s + this->descriptor_.name + " could not be registered!"); + } + + if (autoseed) + { + this->auto_seed(); + } + else + { + this->descriptor_.start(this->state_.get()); + } } - if (autoseed) + ~prng() { - this->auto_seed(); + this->descriptor_.done(this->state_.get()); } - else + + prng_state* get_state() const { - this->descriptor_.start(this->state_.get()); + this->descriptor_.ready(this->state_.get()); + return this->state_.get(); } - } - ~prng() - { - this->descriptor_.done(this->state_.get()); - } + int get_id() const + { + return this->id_; + } - prng_state* get_state() const - { - this->descriptor_.ready(this->state_.get()); - return this->state_.get(); - } + void add_entropy(const void* data, const std::size_t length) const + { + this->descriptor_.add_entropy(static_cast(data), ul(length), this->state_.get()); + } - int get_id() const - { - return this->id_; - } + void read(void* data, const std::size_t length) const + { + this->descriptor_.read(static_cast(data), ul(length), this->get_state()); + } - void add_entropy(const void* data, const std::size_t length) const - { - this->descriptor_.add_entropy(static_cast(data), ul(length), this->state_.get()); - } + private: + int id_; + std::unique_ptr state_; + const ltc_prng_descriptor& descriptor_; - void read(void* data, const std::size_t length) const - { - this->descriptor_.read(static_cast(data), ul(length), this->get_state()); - } + void auto_seed() const + { + rng_make_prng(128, this->id_, this->state_.get(), nullptr); - private: - int id_; - std::unique_ptr state_; - const ltc_prng_descriptor& descriptor_; + int i[4]; // uninitialized data + auto* i_ptr = &i; + this->add_entropy(reinterpret_cast(&i), sizeof(i)); + this->add_entropy(reinterpret_cast(&i_ptr), sizeof(i_ptr)); - void auto_seed() const - { - rng_make_prng(128, this->id_, this->state_.get(), nullptr); + auto t = time(nullptr); + this->add_entropy(reinterpret_cast(&t), sizeof(t)); + } + }; - int i[4]; // uninitialized data - auto* i_ptr = &i; - this->add_entropy(reinterpret_cast(&i), sizeof(i)); - this->add_entropy(reinterpret_cast(&i_ptr), sizeof(i_ptr)); - - auto t = time(nullptr); - this->add_entropy(reinterpret_cast(&t), sizeof(t)); - } - }; - - const prng prng_(fortuna_desc); - } - - ecc::key::key() - { - std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); - } - - ecc::key::~key() - { - this->free(); - } - - ecc::key::key(key&& obj) noexcept - : key() - { - this->operator=(std::move(obj)); - } - - ecc::key::key(const key& obj) - : key() - { - this->operator=(obj); - } - - ecc::key& ecc::key::operator=(key&& obj) noexcept - { - if (this != &obj) - { - std::memmove(&this->key_storage_, &obj.key_storage_, sizeof(this->key_storage_)); - std::memset(&obj.key_storage_, 0, sizeof(obj.key_storage_)); + const prng prng_(fortuna_desc); } - return *this; - } - - ecc::key& ecc::key::operator=(const key& obj) - { - if (this != &obj && obj.is_valid()) - { - this->deserialize(obj.serialize(obj.key_storage_.type)); - } - - return *this; - } - - bool ecc::key::is_valid() const - { - return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_))); - } - - ecc_key& ecc::key::get() - { - return this->key_storage_; - } - - const ecc_key& ecc::key::get() const - { - return this->key_storage_; - } - - std::string ecc::key::get_public_key() const - { - std::uint8_t buffer[512]{}; - unsigned long length = sizeof(buffer); - - if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK) - { - return std::string(cs(buffer), length); - } - - return {}; - } - - void ecc::key::set(const std::string& pub_key_buffer) - { - this->free(); - - if (ecc_ansi_x963_import(cs(pub_key_buffer.data()), - ul(pub_key_buffer.size()), - &this->key_storage_) != CRYPT_OK) + ecc::key::key() { std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); } - } - void ecc::key::deserialize(const std::string& key) - { - this->free(); - - if (ecc_import(cs(key.data()), ul(key.size()), - &this->key_storage_) != CRYPT_OK - ) + ecc::key::~key() { + this->free(); + } + + ecc::key::key(key&& obj) noexcept + : key() + { + this->operator=(std::move(obj)); + } + + ecc::key::key(const key& obj) + : key() + { + this->operator=(obj); + } + + ecc::key& ecc::key::operator=(key&& obj) noexcept + { + if (this != &obj) + { + std::memmove(&this->key_storage_, &obj.key_storage_, sizeof(this->key_storage_)); + std::memset(&obj.key_storage_, 0, sizeof(obj.key_storage_)); + } + + return *this; + } + + ecc::key& ecc::key::operator=(const key& obj) + { + if (this != &obj && obj.is_valid()) + { + this->deserialize(obj.serialize(obj.key_storage_.type)); + } + + return *this; + } + + bool ecc::key::is_valid() const + { + return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_))); + } + + ecc_key& ecc::key::get() + { + return this->key_storage_; + } + + const ecc_key& ecc::key::get() const + { + return this->key_storage_; + } + + std::string ecc::key::get_public_key() const + { + std::uint8_t buffer[512]{}; + unsigned long length = sizeof(buffer); + + if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK) + { + return std::string(cs(buffer), length); + } + + return {}; + } + + void ecc::key::set(const std::string& pub_key_buffer) + { + this->free(); + + if (ecc_ansi_x963_import(cs(pub_key_buffer.data()), + ul(pub_key_buffer.size()), + &this->key_storage_) != CRYPT_OK) + { + std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); + } + } + + void ecc::key::deserialize(const std::string& key) + { + this->free(); + + if (ecc_import(cs(key.data()), ul(key.size()), + &this->key_storage_) != CRYPT_OK + ) + { + std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); + } + } + + std::string ecc::key::serialize(const int type) const + { + std::uint8_t buffer[4096]{}; + unsigned long length = sizeof(buffer); + + if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK) + { + return std::string(cs(buffer), length); + } + + return ""; + } + + void ecc::key::free() + { + if (this->is_valid()) + { + ecc_free(&this->key_storage_); + } + std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); } - } - std::string ecc::key::serialize(const int type) const - { - std::uint8_t buffer[4096]{}; - unsigned long length = sizeof(buffer); - - if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK) + bool ecc::key::operator==(key& key) const { + return (this->is_valid() && key.is_valid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC)); + } + + std::uint64_t ecc::key::get_hash() const + { + const auto hash = sha1::compute(this->get_public_key()); + if (hash.size() >= 8) + { + return *reinterpret_cast(hash.data()); + } + + return 0; + } + + ecc::key ecc::generate_key(const int bits) + { + key key; + ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get()); + + return key; + } + + ecc::key ecc::generate_key(const int bits, const std::string& entropy) + { + key key{}; + const prng yarrow(yarrow_desc, false); + yarrow.add_entropy(entropy.data(), entropy.size()); + + ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get()); + + return key; + } + + std::string ecc::sign_message(const key& key, const std::string& message) + { + if (!key.is_valid()) return ""; + + std::uint8_t buffer[512]{}; + unsigned long length = sizeof(buffer); + + ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length, prng_.get_state(), prng_.get_id(), + &key.get()); + return std::string(cs(buffer), length); } - return ""; - } - - void ecc::key::free() - { - if (this->is_valid()) + bool ecc::verify_message(const key& key, const std::string& message, const std::string& signature) { - ecc_free(&this->key_storage_); + if (!key.is_valid()) return false; + + auto result = 0; + return (ecc_verify_hash(cs(signature.data()), + ul(signature.size()), + cs(message.data()), + ul(message.size()), &result, + &key.get()) == CRYPT_OK && result != 0); } - std::memset(&this->key_storage_, 0, sizeof(this->key_storage_)); - } - - bool ecc::key::operator==(key& key) const - { - return (this->is_valid() && key.is_valid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC)); - } - - std::uint64_t ecc::key::get_hash() const - { - const auto hash = sha1::compute(this->get_public_key()); - if (hash.size() >= 8) + bool ecc::encrypt(const key& key, std::string& data) { - return *reinterpret_cast(hash.data()); - } + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); - return 0; - } + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return ecc_encrypt_key(cs(data.data()), ul(data.size()), const_cast(cs(out_data.data())), &out_len, + prng_.get_state(), prng_.get_id(), find_hash("sha512"), &key.get()); + }; - ecc::key ecc::generate_key(const int bits) - { - key key; - ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get()); + auto res = crypt(); - return key; - } + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } - ecc::key ecc::generate_key(const int bits, const std::string& entropy) - { - key key{}; - const prng yarrow(yarrow_desc, false); - yarrow.add_entropy(entropy.data(), entropy.size()); + if (res != CRYPT_OK) + { + return false; + } - ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get()); - - return key; - } - - std::string ecc::sign_message(const key& key, const std::string& message) - { - if (!key.is_valid()) return ""; - - std::uint8_t buffer[512]{}; - unsigned long length = sizeof(buffer); - - ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length, prng_.get_state(), prng_.get_id(), - &key.get()); - - return std::string(cs(buffer), length); - } - - bool ecc::verify_message(const key& key, const std::string& message, const std::string& signature) - { - if (!key.is_valid()) return false; - - auto result = 0; - return (ecc_verify_hash(cs(signature.data()), - ul(signature.size()), - cs(message.data()), - ul(message.size()), &result, - &key.get()) == CRYPT_OK && result != 0); - } - - bool ecc::encrypt(const key& key, std::string& data) - { - std::string out_data{}; - out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); - - auto out_len = ul(out_data.size()); - auto crypt = [&]() - { - return ecc_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, - prng_.get_state(), prng_.get_id(), find_hash("sha512"), &key.get()); - }; - - auto res = crypt(); - - if (res == CRYPT_BUFFER_OVERFLOW) - { out_data.resize(out_len); - res = crypt(); + data = std::move(out_data); + return true; } - if (res != CRYPT_OK) + bool ecc::decrypt(const key& key, std::string& data) { - return false; - } + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); - out_data.resize(out_len); - data = std::move(out_data); - return true; - } + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return ecc_decrypt_key(cs(data.data()), ul(data.size()), const_cast(cs(out_data.data())), &out_len, &key.get()); + }; - bool ecc::decrypt(const key& key, std::string& data) - { - std::string out_data{}; - out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); + auto res = crypt(); - auto out_len = ul(out_data.size()); - auto crypt = [&]() - { - return ecc_decrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, &key.get()); - }; + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } - auto res = crypt(); + if (res != CRYPT_OK) + { + return false; + } - if (res == CRYPT_BUFFER_OVERFLOW) - { out_data.resize(out_len); - res = crypt(); + data = std::move(out_data); + return true; } - if (res != CRYPT_OK) + std::string rsa::encrypt(const std::string& data, const std::string& hash, const std::string& key) { - return false; - } - - out_data.resize(out_len); - data = std::move(out_data); - return true; - } - - std::string rsa::encrypt(const std::string& data, const std::string& hash, const std::string& key) - { - rsa_key new_key; - rsa_import(cs(key.data()), ul(key.size()), &new_key); - const auto _ = gsl::finally([&]() - { - rsa_free(&new_key); - }); + rsa_key new_key; + rsa_import(cs(key.data()), ul(key.size()), &new_key); + const auto _ = gsl::finally([&]() + { + rsa_free(&new_key); + }); - std::string out_data{}; - out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); - auto out_len = ul(out_data.size()); - auto crypt = [&]() - { - return rsa_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, cs(hash.data()), - ul(hash.size()), prng_.get_state(), prng_.get_id(), find_hash("sha512"), &new_key); - }; + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return rsa_encrypt_key(cs(data.data()), ul(data.size()), const_cast(cs(out_data.data())), &out_len, cs(hash.data()), + ul(hash.size()), prng_.get_state(), prng_.get_id(), find_hash("sha512"), &new_key); + }; - auto res = crypt(); + auto res = crypt(); - if (res == CRYPT_BUFFER_OVERFLOW) - { - out_data.resize(out_len); - res = crypt(); - } + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } - if (res == CRYPT_OK) - { - out_data.resize(out_len); - return out_data; - } + if (res == CRYPT_OK) + { + out_data.resize(out_len); + return out_data; + } - return {}; - } - - std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key) - { - std::string enc_data; - enc_data.resize(data.size()); - - symmetric_CBC cbc; - const auto des3 = find_cipher("3des"); - - cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); - cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(data.size()), &cbc); - cbc_done(&cbc); - - return enc_data; - } - - std::string des3::decrypt(const std::string& data, const std::string& iv, const std::string& key) - { - std::string dec_data; - dec_data.resize(data.size()); - - symmetric_CBC cbc; - const auto des3 = find_cipher("3des"); - - cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); - cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc); - cbc_done(&cbc); - - return dec_data; - } - - std::string tiger::compute(const std::string& data, const bool hex) - { - return compute(cs(data.data()), data.size(), hex); - } - - std::string tiger::compute(const std::uint8_t* data, const std::size_t length, const bool hex) - { - std::uint8_t buffer[24]{}; - - hash_state state; - tiger_init(&state); - tiger_process(&state, data, ul(length)); - tiger_done(&state, buffer); - - std::string hash(cs(buffer), sizeof(buffer)); - if (!hex) return hash; - - return string::dump_hex(hash, ""); - } - - std::string aes::encrypt(const std::string& data, const std::string& iv, const std::string& key) - { - std::string enc_data; - enc_data.resize(data.size()); - - symmetric_CBC cbc; - const auto aes = find_cipher("aes"); - - cbc_start(aes, cs(iv.data()), cs(key.data()), - static_cast(key.size()), 0, &cbc); - cbc_encrypt(cs(data.data()), - cs(enc_data.data()), - ul(data.size()), &cbc); - cbc_done(&cbc); - - return enc_data; - } - - std::string aes::decrypt(const std::string& data, const std::string& iv, const std::string& key) - { - std::string dec_data; - dec_data.resize(data.size()); - - symmetric_CBC cbc; - const auto aes = find_cipher("aes"); - - cbc_start(aes, cs(iv.data()), cs(key.data()), - static_cast(key.size()), 0, &cbc); - cbc_decrypt(cs(data.data()), - cs(dec_data.data()), - ul(data.size()), &cbc); - cbc_done(&cbc); - - return dec_data; - } - - std::string hmac_sha1::compute(const std::string& data, const std::string& key) - { - std::string buffer; - buffer.resize(20); - - hmac_state state; - hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size())); - hmac_process(&state, cs(data.data()), static_cast(data.size())); - - auto out_len = ul(buffer.size()); - hmac_done(&state, cs(buffer.data()), &out_len); - - buffer.resize(out_len); - return buffer; - } - - std::string sha1::compute(const std::string& data, const bool hex) - { - return compute(cs(data.data()), data.size(), hex); - } - - std::string sha1::compute(const std::uint8_t* data, const size_t length, const bool hex) - { - std::uint8_t buffer[20]{}; - - hash_state state; - sha1_init(&state); - sha1_process(&state, data, ul(length)); - sha1_done(&state, buffer); - - std::string hash(cs(buffer), sizeof(buffer)); - if (!hex) return hash; - - return string::dump_hex(hash, ""); - } - - std::string sha256::compute(const std::string& data, const bool hex) - { - return compute(cs(data.data()), data.size(), hex); - } - - std::string sha256::compute(const std::uint8_t* data, const std::size_t length, const bool hex) - { - std::uint8_t buffer[32]{}; - - hash_state state; - sha256_init(&state); - sha256_process(&state, data, ul(length)); - sha256_done(&state, buffer); - - std::string hash(cs(buffer), sizeof(buffer)); - if (!hex) return hash; - - return string::dump_hex(hash, ""); - } - - std::string sha512::compute(const std::string& data, const bool hex) - { - return compute(cs(data.data()), data.size(), hex); - } - - std::string sha512::compute(const std::uint8_t* data, const std::size_t length, const bool hex) - { - std::uint8_t buffer[64]{}; - - hash_state state; - sha512_init(&state); - sha512_process(&state, data, ul(length)); - sha512_done(&state, buffer); - - std::string hash(cs(buffer), sizeof(buffer)); - if (!hex) return hash; - - return string::dump_hex(hash, ""); - } - - std::string base64::encode(const std::uint8_t* data, const std::size_t len) - { - std::string result; - result.resize((len + 2) * 2); - - auto out_len = ul(result.size()); - if (base64_encode(data, ul(len), result.data(), &out_len) != CRYPT_OK) - { return {}; } - result.resize(out_len); - return result; - } - - std::string base64::encode(const std::string& data) - { - return base64::encode(cs(data.data()), static_cast(data.size())); - } - - std::string base64::decode(const std::string& data) - { - std::string result; - result.resize((data.size() + 2) * 2); - - auto out_len = ul(result.size()); - if (base64_decode(data.data(), ul(data.size()), cs(result.data()), &out_len) != CRYPT_OK) + std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key) { - return {}; + std::string enc_data; + enc_data.resize(data.size()); + + symmetric_CBC cbc; + const auto des3 = find_cipher("3des"); + + cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_encrypt(cs(data.data()), const_cast(cs(enc_data.data())), ul(data.size()), &cbc); + cbc_done(&cbc); + + return enc_data; } - result.resize(out_len); - return result; - } - - unsigned int jenkins_one_at_a_time::compute(const std::string& data) - { - return compute(data.data(), data.size()); - } - - unsigned int jenkins_one_at_a_time::compute(const char* key, const std::size_t len) - { - unsigned int hash, i; - for (hash = i = 0; i < len; ++i) + std::string des3::decrypt(const std::string& data, const std::string& iv, const std::string& key) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); + std::string dec_data; + dec_data.resize(data.size()); + + symmetric_CBC cbc; + const auto des3 = find_cipher("3des"); + + cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_decrypt(cs(data.data()), const_cast(cs(dec_data.data())), ul(data.size()), &cbc); + cbc_done(&cbc); + + return dec_data; } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; - } - std::uint32_t random::get_integer() - { - std::uint32_t result; - get_data(&result, sizeof(result)); - return result; - } + std::string tiger::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } - std::string random::get_challenge() - { - std::string result; - result.resize(sizeof(std::uint32_t)); - get_data(result.data(), result.size()); - return string::dump_hex(result, ""); - } + std::string tiger::compute(const std::uint8_t* data, const std::size_t length, const bool hex) + { + std::uint8_t buffer[24]{}; - void random::get_data(void* data, const std::size_t size) - { - prng_.read(data, size); + hash_state state; + tiger_init(&state); + tiger_process(&state, data, ul(length)); + tiger_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string aes::encrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string enc_data; + enc_data.resize(data.size()); + + symmetric_CBC cbc; + const auto aes = find_cipher("aes"); + + cbc_start(aes, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_encrypt(cs(data.data()), const_cast(cs(enc_data.data())), ul(data.size()), &cbc); + cbc_done(&cbc); + + return enc_data; + } + + std::string aes::decrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string dec_data; + dec_data.resize(data.size()); + + symmetric_CBC cbc; + const auto aes = find_cipher("aes"); + + cbc_start(aes, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_decrypt(cs(data.data()), const_cast(cs(dec_data.data())), ul(data.size()), &cbc); + cbc_done(&cbc); + + return dec_data; + } + + std::string hmac_sha1::compute(const std::string& data, const std::string& key) + { + std::string buffer; + buffer.resize(20); + + hmac_state state; + hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size())); + hmac_process(&state, cs(data.data()), static_cast(data.size())); + + auto out_len = ul(buffer.size()); + hmac_done(&state, const_cast(cs(buffer.data())), &out_len); + + buffer.resize(out_len); + return buffer; + } + + std::string sha1::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha1::compute(const std::uint8_t* data, const size_t length, const bool hex) + { + std::uint8_t buffer[20]{}; + + hash_state state; + sha1_init(&state); + sha1_process(&state, data, ul(length)); + sha1_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string sha256::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha256::compute(const std::uint8_t* data, const std::size_t length, const bool hex) + { + std::uint8_t buffer[32]{}; + + hash_state state; + sha256_init(&state); + sha256_process(&state, data, ul(length)); + sha256_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string sha512::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha512::compute(const std::uint8_t* data, const std::size_t length, const bool hex) + { + std::uint8_t buffer[64]{}; + + hash_state state; + sha512_init(&state); + sha512_process(&state, data, ul(length)); + sha512_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string base64::encode(const std::uint8_t* data, const std::size_t len) + { + std::string result; + result.resize((len + 2) * 2); + + auto out_len = ul(result.size()); + if (base64_encode(data, ul(len), const_cast(result.data()), &out_len) != CRYPT_OK) + { + return {}; + } + + result.resize(out_len); + return result; + } + + std::string base64::encode(const std::string& data) + { + return base64::encode(cs(data.data()), static_cast(data.size())); + } + + std::string base64::decode(const std::string& data) + { + std::string result; + result.resize((data.size() + 2) * 2); + + auto out_len = ul(result.size()); + if (base64_decode(data.data(), ul(data.size()), const_cast(cs(result.data())), &out_len) != CRYPT_OK) + { + return {}; + } + + result.resize(out_len); + return result; + } + + unsigned int jenkins_one_at_a_time::compute(const std::string& data) + { + return compute(data.data(), data.size()); + } + + unsigned int jenkins_one_at_a_time::compute(const char* key, const std::size_t len) + { + unsigned int hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + + std::uint32_t random::get_integer() + { + std::uint32_t result; + get_data(&result, sizeof(result)); + return result; + } + + std::string random::get_challenge() + { + std::string result; + result.resize(sizeof(std::uint32_t)); + get_data(const_cast(result.data()), result.size()); + return string::dump_hex(result, ""); + } + + void random::get_data(void* data, const std::size_t size) + { + prng_.read(data, size); + } } } diff --git a/src/utils/cryptography.hpp b/src/utils/cryptography.hpp index 25aa4dc..67b6c34 100644 --- a/src/utils/cryptography.hpp +++ b/src/utils/cryptography.hpp @@ -3,116 +3,119 @@ #include #include -namespace utils::cryptography +namespace utils { - namespace ecc + namespace cryptography { - class key final + namespace ecc { - public: - key(); - ~key(); + class key final + { + public: + key(); + ~key(); - key(key&& obj) noexcept; - key(const key& obj); - key& operator=(key&& obj) noexcept; - key& operator=(const key& obj); + key(key&& obj) noexcept; + key(const key& obj); + key& operator=(key&& obj) noexcept; + key& operator=(const key& obj); - [[nodiscard]] bool is_valid() const; + GSL_NODISCARD bool is_valid() const; - ecc_key& get(); - [[nodiscard]] const ecc_key& get() const; + ecc_key& get(); + GSL_NODISCARD const ecc_key& get() const; - [[nodiscard]] std::string get_public_key() const; + GSL_NODISCARD std::string get_public_key() const; - void set(const std::string& pub_key_buffer); + void set(const std::string& pub_key_buffer); - void deserialize(const std::string& key); + void deserialize(const std::string& key); - [[nodiscard]] std::string serialize(int type = PK_PRIVATE) const; + GSL_NODISCARD std::string serialize(int type = PK_PRIVATE) const; - void free(); + void free(); - bool operator==(key& key) const; + bool operator==(key& key) const; - [[nodiscard]] std::uint64_t get_hash() const; + GSL_NODISCARD std::uint64_t get_hash() const; - private: - ecc_key key_storage_{}; + private: + ecc_key key_storage_{}; + }; + + key generate_key(int bits); + key generate_key(int bits, const std::string& entropy); + std::string sign_message(const key& key, const std::string& message); + bool verify_message(const key& key, const std::string& message, const std::string& signature); + + bool encrypt(const key& key, std::string& data); + bool decrypt(const key& key, std::string& data); + } + + namespace rsa + { + std::string encrypt(const std::string& data, const std::string& hash, const std::string& key); + } + + namespace des3 + { + std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); + std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); + } + + namespace tiger + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); + } + + namespace aes + { + std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); + std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); + } + + namespace hmac_sha1 + { + std::string compute(const std::string& data, const std::string& key); + } + + namespace sha1 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); + } + + namespace sha256 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); + } + + namespace sha512 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); + } + + namespace base64 + { + std::string encode(const std::uint8_t* data, std::size_t len); + std::string encode(const std::string& data); + std::string decode(const std::string& data); + } + + namespace jenkins_one_at_a_time + { + std::uint32_t compute(const std::string& data); + std::uint32_t compute(const char* key, std::size_t len); }; - key generate_key(int bits); - key generate_key(int bits, const std::string& entropy); - std::string sign_message(const key& key, const std::string& message); - bool verify_message(const key& key, const std::string& message, const std::string& signature); - - bool encrypt(const key& key, std::string& data); - bool decrypt(const key& key, std::string& data); - } - - namespace rsa - { - std::string encrypt(const std::string& data, const std::string& hash, const std::string& key); - } - - namespace des3 - { - std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); - std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); - } - - namespace tiger - { - std::string compute(const std::string& data, bool hex = false); - std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); - } - - namespace aes - { - std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); - std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); - } - - namespace hmac_sha1 - { - std::string compute(const std::string& data, const std::string& key); - } - - namespace sha1 - { - std::string compute(const std::string& data, bool hex = false); - std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); - } - - namespace sha256 - { - std::string compute(const std::string& data, bool hex = false); - std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); - } - - namespace sha512 - { - std::string compute(const std::string& data, bool hex = false); - std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false); - } - - namespace base64 - { - std::string encode(const std::uint8_t* data, std::size_t len); - std::string encode(const std::string& data); - std::string decode(const std::string& data); - } - - namespace jenkins_one_at_a_time - { - std::uint32_t compute(const std::string& data); - std::uint32_t compute(const char* key, std::size_t len); - }; - - namespace random - { - std::uint32_t get_integer(); - std::string get_challenge(); - void get_data(void* data, std::size_t size); + namespace random + { + std::uint32_t get_integer(); + std::string get_challenge(); + void get_data(void* data, std::size_t size); + } } } diff --git a/src/utils/io.cpp b/src/utils/io.cpp index c8f6317..c1f4f22 100644 --- a/src/utils/io.cpp +++ b/src/utils/io.cpp @@ -1,128 +1,92 @@ #include #include "io.hpp" -namespace utils::io +namespace utils { - bool remove_file(const std::string& file) + namespace io { - return remove(file.data()) == 0; - } - - bool move_file(const std::string& src, const std::string& target) - { - return rename(src.data(), target.data()) == 0; - } - - bool file_exists(const std::string& file) - { - return std::ifstream(file).good(); - } - - bool write_file(const std::string& file, const std::string& data, const bool append) - { - const auto pos = file.find_last_of("/\\"); - if (pos != std::string::npos) + bool remove_file(const std::string& file) { - create_directory(file.substr(0, pos)); + return remove(file.data()) == 0; } - auto mode = std::ios::binary | std::ofstream::out; - if (append) + bool move_file(const std::string& src, const std::string& target) { - mode |= std::ofstream::app; + return rename(src.data(), target.data()) == 0; } - std::ofstream stream(file, mode); - - if (stream.is_open()) + bool file_exists(const std::string& file) { - stream.write(data.data(), static_cast(data.size())); - stream.close(); - return true; + return std::ifstream(file).good(); } - return false; - } - - std::string read_file(const std::string& file) - { - std::string data; - read_file(file, &data); - return data; - } - - bool read_file(const std::string& file, std::string* data) - { - if (!data) return false; - data->clear(); - - if (file_exists(file)) + bool write_file(const std::string& file, const std::string& data, const bool append) { - std::ifstream stream(file, std::ios::binary); - if (!stream.is_open()) return false; - - stream.seekg(0, std::ios::end); - const std::streamsize size = stream.tellg(); - stream.seekg(0, std::ios::beg); - - if (size > -1) + auto mode = std::ios::binary | std::ofstream::out; + if (append) { - data->resize(static_cast(size)); - stream.read(const_cast(data->data()), size); + mode |= std::ofstream::app; + } + + std::ofstream stream(file, mode); + + if (stream.is_open()) + { + stream.write(data.data(), static_cast(data.size())); stream.close(); return true; } + + return false; } - return false; - } - - std::size_t file_size(const std::string& file) - { - if (file_exists(file)) + std::string read_file(const std::string& file) { - std::ifstream stream(file, std::ios::binary); + std::string data; + read_file(file, &data); + return data; + } - if (stream.good()) + bool read_file(const std::string& file, std::string* data) + { + if (!data) return false; + data->clear(); + + if (file_exists(file)) { + std::ifstream stream(file, std::ios::binary); + if (!stream.is_open()) return false; + stream.seekg(0, std::ios::end); - return static_cast(stream.tellg()); + const std::streamsize size = stream.tellg(); + stream.seekg(0, std::ios::beg); + + if (size > -1) + { + data->resize(static_cast(size)); + stream.read(const_cast(data->data()), size); + stream.close(); + return true; + } } + + return false; } - return 0; - } - - bool create_directory(const std::string& directory) - { - return std::filesystem::create_directories(directory); - } - - bool directory_exists(const std::string& directory) - { - return std::filesystem::is_directory(directory); - } - - bool directory_is_empty(const std::string& directory) - { - return std::filesystem::is_empty(directory); - } - - std::vector list_files(const std::string& directory) - { - std::vector files; - - for (auto& file : std::filesystem::directory_iterator(directory)) + std::size_t file_size(const std::string& file) { - files.push_back(file.path().generic_string()); + if (file_exists(file)) + { + std::ifstream stream(file, std::ios::binary); + + if (stream.good()) + { + stream.seekg(0, std::ios::end); + return static_cast(stream.tellg()); + } + } + + return 0; } - - return files; - } - - void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target) - { - std::filesystem::copy(src, target, std::filesystem::copy_options::overwrite_existing | - std::filesystem::copy_options::recursive); } } diff --git a/src/utils/io.hpp b/src/utils/io.hpp index 5dec1a5..529fd29 100644 --- a/src/utils/io.hpp +++ b/src/utils/io.hpp @@ -1,20 +1,17 @@ #pragma once #include -#include -namespace utils::io +namespace utils { - bool remove_file(const std::string& file); - bool move_file(const std::string& src, const std::string& target); - bool file_exists(const std::string& file); - bool write_file(const std::string& file, const std::string& data, bool append = false); - bool read_file(const std::string& file, std::string* data); - std::string read_file(const std::string& file); - std::size_t file_size(const std::string& file); - bool create_directory(const std::string& directory); - bool directory_exists(const std::string& directory); - bool directory_is_empty(const std::string& directory); - std::vector list_files(const std::string& directory); - void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); + namespace io + { + bool remove_file(const std::string& file); + bool move_file(const std::string& src, const std::string& target); + bool file_exists(const std::string& file); + bool write_file(const std::string& file, const std::string& data, bool append = false); + bool read_file(const std::string& file, std::string* data); + std::string read_file(const std::string& file); + std::size_t file_size(const std::string& file); + } } diff --git a/src/utils/memory.cpp b/src/utils/memory.cpp index 2d8832d..880d4b5 100644 --- a/src/utils/memory.cpp +++ b/src/utils/memory.cpp @@ -13,7 +13,7 @@ namespace utils void memory::allocator::clear() { - std::lock_guard _(this->mutex_); + std::lock_guard _(this->mutex_); for (auto& data : this->pool_) { @@ -25,7 +25,7 @@ namespace utils void memory::allocator::free(void* data) { - std::lock_guard _(this->mutex_); + std::lock_guard _(this->mutex_); const auto j = std::find(this->pool_.begin(), this->pool_.end(), data); if (j != this->pool_.end()) @@ -42,7 +42,7 @@ namespace utils void* memory::allocator::allocate(const size_t length) { - std::lock_guard _(this->mutex_); + std::lock_guard _(this->mutex_); const auto data = memory::allocate(length); this->pool_.push_back(data); @@ -56,7 +56,7 @@ namespace utils char* memory::allocator::duplicate_string(const std::string& string) { - std::lock_guard _(this->mutex_); + std::lock_guard _(this->mutex_); const auto data = memory::duplicate_string(string); this->pool_.push_back(data); diff --git a/src/utils/memory.hpp b/src/utils/memory.hpp index a6f29e1..09c0d4b 100644 --- a/src/utils/memory.hpp +++ b/src/utils/memory.hpp @@ -22,13 +22,13 @@ namespace utils void* allocate(size_t length); template - inline T* allocate() + T* allocate() { return this->allocate_array(1); } template - inline T* allocate_array(const size_t count = 1) + T* allocate_array(const size_t count = 1) { return static_cast(this->allocate(count * sizeof(T))); } @@ -45,13 +45,13 @@ namespace utils static void* allocate(size_t length); template - static inline T* allocate() + static T* allocate() { return allocate_array(1); } template - static inline T* allocate_array(const size_t count = 1) + static T* allocate_array(const size_t count = 1) { return static_cast(allocate(count * sizeof(T))); } diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 626d373..ff08cad 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -5,98 +5,101 @@ #include #include -namespace utils::string +namespace utils { - const char* va(const char* fmt, ...) + namespace string { - static thread_local va_provider<8, 256> provider; - - va_list ap; - va_start(ap, fmt); - - const char* result = provider.get(fmt, ap); - - va_end(ap); - return result; - } - - std::vector split(const std::string& s, const char delim) - { - std::stringstream ss(s); - std::string item; - std::vector elems; - - while (std::getline(ss, item, delim)) + const char* va(const char* fmt, ...) { - elems.push_back(item); - item = std::string(); + static thread_local va_provider<8, 256> provider; + + va_list ap; + va_start(ap, fmt); + + const char* result = provider.get(fmt, ap); + + va_end(ap); + return result; } - return elems; - } - - std::string to_lower(std::string text) - { - std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + std::vector split(const std::string& s, const char delim) { - return static_cast(std::tolower(input)); - }); + std::stringstream ss(s); + std::string item; + std::vector elems; - return text; - } + while (std::getline(ss, item, delim)) + { + elems.push_back(item); + item = std::string(); + } - std::string to_upper(std::string text) - { - std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + return elems; + } + + std::string to_lower(std::string text) { - return static_cast(std::toupper(input)); - }); + std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + { + return static_cast(std::tolower(input)); + }); - return text; - } + return text; + } - bool starts_with(const std::string& text, const std::string& substring) - { - return text.find(substring) == 0; - } - - bool ends_with(const std::string& text, const std::string& substring) - { - if (substring.size() > text.size()) return false; - return std::equal(substring.rbegin(), substring.rend(), text.rbegin()); - } - - std::string replace(std::string str, const std::string& from, const std::string& to) - { - if (from.empty()) + std::string to_upper(std::string text) { + std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + { + return static_cast(std::toupper(input)); + }); + + return text; + } + + bool starts_with(const std::string& text, const std::string& substring) + { + return text.find(substring) == 0; + } + + bool ends_with(const std::string& text, const std::string& substring) + { + if (substring.size() > text.size()) return false; + return std::equal(substring.rbegin(), substring.rend(), text.rbegin()); + } + + std::string replace(std::string str, const std::string& from, const std::string& to) + { + if (from.empty()) + { + return str; + } + + std::size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + return str; } - std::size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) + std::string dump_hex(const std::string& data, const std::string& separator) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } + std::string result; - return str; - } - - std::string dump_hex(const std::string& data, const std::string& separator) - { - std::string result; - - for (unsigned int i = 0; i < data.size(); ++i) - { - if (i > 0) + for (unsigned int i = 0; i < data.size(); ++i) { - result.append(separator); + if (i > 0) + { + result.append(separator); + } + + result.append(va("%02X", data[i] & 0xFF)); } - result.append(va("%02X", data[i] & 0xFF)); + return result; } - - return result; } } diff --git a/src/utils/string.hpp b/src/utils/string.hpp index 676214b..42a4e9b 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -2,96 +2,99 @@ #include "memory.hpp" #ifndef ARRAYSIZE -template -size_t ARRAYSIZE(Type (&)[n]) { return n; } +template +std::size_t ARRAYSIZE(Type (&)[n]) { return n; } #endif -namespace utils::string +namespace utils { - template - class va_provider final + namespace string { - public: - static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0"); - - va_provider() : current_buffer_(0) - { - } - - char* get(const char* format, va_list ap) - { - ++this->current_buffer_ %= ARRAYSIZE(this->string_pool_); - auto entry = &this->string_pool_[this->current_buffer_]; - - if (!entry->size || !entry->buffer) - { - throw std::runtime_error("String pool not initialized"); - } - - while (true) - { -#ifdef _WIN32 - const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); -#else - const int 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 + template + class va_provider final { public: - explicit entry(const std::size_t _size = MinBufferSize) : size(_size), buffer(nullptr) + static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0"); + + va_provider() : current_buffer_(0) { - if (this->size < MinBufferSize) this->size = MinBufferSize; - this->allocate(); } - ~entry() + char* get(const char* format, va_list ap) { - if (this->buffer) memory::get_allocator()->free(this->buffer); - this->size = 0; - this->buffer = nullptr; + ++this->current_buffer_ %= ARRAYSIZE(this->string_pool_); + auto entry = &this->string_pool_[this->current_buffer_]; + + if (!entry->size || !entry->buffer) + { + throw std::runtime_error("String pool not initialized"); + } + + while (true) + { +#ifdef _WIN32 + const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); +#else + const int 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; } - void allocate() + private: + class entry final { - if (this->buffer) memory::get_allocator()->free(this->buffer); - this->buffer = memory::get_allocator()->allocate_array(this->size + 1); - } + public: + explicit entry(const std::size_t _size = MinBufferSize) : size(_size), buffer(nullptr) + { + if (this->size < MinBufferSize) this->size = MinBufferSize; + this->allocate(); + } - void double_size() - { - this->size *= 2; - this->allocate(); - } + ~entry() + { + if (this->buffer) memory::get_allocator()->free(this->buffer); + this->size = 0; + this->buffer = nullptr; + } - std::size_t size; - char* buffer; + 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]; }; - std::size_t current_buffer_; - entry string_pool_[Buffers]; - }; + const char* va(const char* fmt, ...); - const char* va(const char* fmt, ...); + std::vector split(const std::string& s, char delim); - std::vector split(const std::string& s, char delim); + std::string to_lower(std::string text); + std::string to_upper(std::string text); + bool starts_with(const std::string& text, const std::string& substring); + bool ends_with(const std::string& text, const std::string& substring); - std::string to_lower(std::string text); - std::string to_upper(std::string text); - bool starts_with(const std::string& text, const std::string& substring); - bool ends_with(const std::string& text, const std::string& substring); + std::string replace(std::string str, const std::string& from, const std::string& to); - std::string replace(std::string str, const std::string& from, const std::string& to); - - std::string dump_hex(const std::string& data, const std::string& separator = " "); + std::string dump_hex(const std::string& data, const std::string& separator = " "); + } }