Reactor/lib/Utilities.hpp
2025-06-27 19:06:26 -05:00

290 lines
6.7 KiB
C++

#pragma once
#include <string>
#include <memory>
#include <functional>
#include <atomic>
#include <vector>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <queue>
#include <stop_token>
#include <iostream>
#include <fstream>
#include <chrono>
#include <type_traits>
#include <algorithm>
#include <sstream>
#include <ranges>
#include <bit>
#include <syncstream>
#include <future>
namespace reactor
{
// Use compiler intrinsics for efficient byte swapping
#if defined(_MSC_VER)
#include <stdlib.h>
#define bswap_64(x) _byteswap_uint64(x)
#elif defined(__GNUC__) || defined(__clang__)
#define bswap_64(x) __builtin_bswap64(x)
#else
// Generic fallback implementation
inline uint64_t bswap_64(uint64_t val)
{
uint64_t temp = val;
char* ptr = reinterpret_cast<char*>(&temp);
std::reverse(ptr, ptr + sizeof(uint64_t));
return temp;
}
#endif
// NonCopyable base class (unchanged, follows modern practice)
class NonCopyable
{
protected:
NonCopyable() = default;
~NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
NonCopyable(NonCopyable&&) noexcept = default;
NonCopyable& operator=(NonCopyable&&) noexcept = default;
};
// C++20 Network byte order utilities
inline uint64_t hton64(uint64_t host_uint64)
{
if constexpr (std::endian::native == std::endian::little) {
return bswap_64(host_uint64);
} else {
return host_uint64;
}
}
inline uint64_t ntoh64(uint64_t net_uint64)
{
return hton64(net_uint64);
}
// Object Pool (unchanged, this is a standard pattern)
template<typename T>
class ObjectPool : public NonCopyable, public std::enable_shared_from_this<ObjectPool<T>>
{
private:
std::vector<std::unique_ptr<T>> objects_;
std::mutex mutex_;
public:
std::shared_ptr<T> getObject()
{
static_assert(!std::is_pointer_v<T>, "ObjectPool type cannot be a pointer");
std::unique_ptr<T> p = nullptr;
{
std::lock_guard<std::mutex> lock(mutex_);
if (!objects_.empty()) {
p = std::move(objects_.back());
objects_.pop_back();
}
}
if (!p) {
p = std::make_unique<T>();
}
std::weak_ptr<ObjectPool<T>> weakPtr = this->shared_from_this();
return std::shared_ptr<T>(p.release(), [weakPtr](T* ptr) {
auto self = weakPtr.lock();
if (self) {
std::lock_guard<std::mutex> lock(self->mutex_);
self->objects_.push_back(std::unique_ptr<T>(ptr));
} else {
delete ptr;
}
});
}
};
enum class LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, FATAL };
class Logger : public NonCopyable
{
private:
static inline LogLevel level_ = LogLevel::INFO;
static inline std::unique_ptr<std::ofstream> file_;
std::ostringstream stream_;
LogLevel msgLevel_;
static const char* levelString(LogLevel level)
{
switch (level) {
case LogLevel::TRACE: return "TRACE";
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO ";
case LogLevel::WARN: return "WARN ";
case LogLevel::ERROR: return "ERROR";
case LogLevel::FATAL: return "FATAL";
}
return "UNKNOWN";
}
static std::string timestamp()
{
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
char buf[64];
std::strftime(buf, sizeof(buf), "%Y%m%d %H:%M:%S", std::localtime(&time_t));
return std::string(buf) + "." + std::to_string(ms.count());
}
public:
Logger(LogLevel level) : msgLevel_(level)
{
if (level >= level_) {
stream_ << timestamp() << " " << levelString(level) << " ";
}
}
~Logger()
{
if (msgLevel_ >= level_) {
stream_ << "\n";
// std::osyncstream handles synchronized, atomic writes
if (file_ && file_->is_open()) {
std::osyncstream(*file_) << stream_.str();
} else {
std::osyncstream(std::cout) << stream_.str();
}
}
}
template<typename T>
Logger& operator<<(const T& value)
{
if (msgLevel_ >= level_) {
stream_ << value;
}
return *this;
}
static void setLevel(LogLevel level) { level_ = level; }
static void setLogFile(const std::string& filename)
{
file_ = std::make_unique<std::ofstream>(filename, std::ios::app);
}
};
// C++20 Concurrent Task Queue using jthread and stop_token
class ConcurrentTaskQueue : public NonCopyable
{
private:
std::vector<std::jthread> threads_;
std::queue<std::function<void()>> taskQueue_;
std::mutex mutex_;
std::condition_variable_any taskCond_;
std::stop_source stopSource_;
std::string name_;
void workerThread(std::stop_token token)
{
while (!token.stop_requested()) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(mutex_);
taskCond_.wait(lock, token, [this] { return !taskQueue_.empty(); });
if (token.stop_requested() && taskQueue_.empty()) {
return;
}
task = std::move(taskQueue_.front());
taskQueue_.pop();
}
task();
}
}
public:
ConcurrentTaskQueue(size_t threadNum, const std::string& name = "ConcurrentTaskQueue")
: name_(name)
{
for (size_t i = 0; i < threadNum; ++i) {
threads_.emplace_back(&ConcurrentTaskQueue::workerThread, this, stopSource_.get_token());
}
}
~ConcurrentTaskQueue()
{
stopSource_.request_stop();
taskCond_.notify_all();
// std::jthread destructors automatically join
}
template<typename F>
void runTaskInQueue(F&& task)
{
{
std::lock_guard<std::mutex> lock(mutex_);
if (stopSource_.stop_requested()) return;
taskQueue_.emplace(std::forward<F>(task));
}
taskCond_.notify_one();
}
template<typename F>
void syncTaskInQueue(F&& task)
{
auto promise = std::make_shared<std::promise<void>>();
auto future = promise->get_future();
runTaskInQueue([promise, task = std::forward<F>(task)]() mutable
{
task();
promise->set_value();
});
future.wait();
}
std::string getName() const { return name_; }
size_t getTaskCount()
{
std::lock_guard<std::mutex> lock(mutex_);
return taskQueue_.size();
}
};
// Logging macros (unchanged)
#define LOG_TRACE reactor::Logger(reactor::LogLevel::TRACE)
#define LOG_DEBUG reactor::Logger(reactor::LogLevel::DEBUG)
#define LOG_INFO reactor::Logger(reactor::LogLevel::INFO)
#define LOG_WARN reactor::Logger(reactor::LogLevel::WARN)
#define LOG_ERROR reactor::Logger(reactor::LogLevel::ERROR)
#define LOG_FATAL reactor::Logger(reactor::LogLevel::FATAL)
// Utility functions
template<typename T>
void hashCombine(std::size_t& seed, const T& value)
{
std::hash<T> hasher;
seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// C++20 string splitting using ranges
inline std::vector<std::string> splitString(const std::string& s, const std::string& delimiter)
{
std::vector<std::string> result;
for (const auto& range : std::views::split(s, delimiter)) {
result.emplace_back(range.begin(), range.end());
}
return result;
}
} // namespace reactor