audited all the library using gemini, openssl support is still pending
This commit is contained in:
@@ -1,42 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <ckitty/memory/primitives.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ckitty {
|
||||
|
||||
namespace chrono {
|
||||
|
||||
using namespace std::chrono;
|
||||
namespace detail {
|
||||
/**
|
||||
* @brief Thread-safe cross-platform wrapper for localtime.
|
||||
*/
|
||||
inline bool safe_localtime(const std::time_t& time, std::tm& result) noexcept {
|
||||
#if defined(_WIN32)
|
||||
return localtime_s(&result, &time) == 0;
|
||||
#else
|
||||
return localtime_r(&time, &result) != nullptr;
|
||||
#endif
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline u64 millis() {
|
||||
auto now = high_resolution_clock::now().time_since_epoch();
|
||||
return duration_cast<milliseconds>(now).count();
|
||||
// --- Monotonic & System Timestamps ---
|
||||
|
||||
/**
|
||||
* @brief Returns system wall-clock epoch time in milliseconds.
|
||||
*/
|
||||
[[nodiscard]] inline u64 millis() noexcept {
|
||||
const auto now = std::chrono::system_clock::now().time_since_epoch();
|
||||
return static_cast<u64>(std::chrono::duration_cast<std::chrono::milliseconds>(now).count());
|
||||
}
|
||||
|
||||
inline u64 micros() {
|
||||
auto now = high_resolution_clock::now().time_since_epoch();
|
||||
return duration_cast<microseconds>(now).count();
|
||||
/**
|
||||
* @brief Returns system wall-clock epoch time in microseconds.
|
||||
*/
|
||||
[[nodiscard]] inline u64 micros() noexcept {
|
||||
const auto now = std::chrono::system_clock::now().time_since_epoch();
|
||||
return static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>(now).count());
|
||||
}
|
||||
|
||||
// Date Time String // Could be done better but this is neat too
|
||||
/**
|
||||
* @brief Returns monotonic time in nanoseconds (ideal for benchmarks).
|
||||
*/
|
||||
[[nodiscard]] inline u64 steady_nanos() noexcept {
|
||||
const auto now = std::chrono::steady_clock::now().time_since_epoch();
|
||||
return static_cast<u64>(std::chrono::duration_cast<std::chrono::nanoseconds>(now).count());
|
||||
}
|
||||
|
||||
inline std::string dateTimeString(const std::string& format, const std::tm time) {
|
||||
// --- Date & Time Formatting ---
|
||||
|
||||
/**
|
||||
* @brief Formats a std::tm structure according to a format string.
|
||||
*/
|
||||
[[nodiscard]] inline std::string date_time_string(std::string_view format, const std::tm& time) {
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&time, format.c_str());
|
||||
oss << std::put_time(&time, std::string(format).c_str());
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
inline std::string dateTimeString(const std::string& format = "%d-%m-%Y %H-%M-%S") {
|
||||
/**
|
||||
* @brief Formats current local time according to a strftime format string.
|
||||
*/
|
||||
[[nodiscard]] inline std::string date_time_string(std::string_view format = "%Y-%m-%d %H:%M:%S") {
|
||||
const std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
return dateTimeString(format, tm);
|
||||
std::tm tm_buf{};
|
||||
if (!detail::safe_localtime(t, tm_buf)) {
|
||||
return "";
|
||||
}
|
||||
return date_time_string(format, tm_buf);
|
||||
}
|
||||
|
||||
}
|
||||
// Backward-compatible camelCase alias
|
||||
[[nodiscard]] inline std::string dateTimeString(std::string_view format = "%Y-%m-%d %H:%M:%S") {
|
||||
return date_time_string(format);
|
||||
}
|
||||
|
||||
}
|
||||
// --- Stopwatch Utility ---
|
||||
|
||||
/**
|
||||
* @brief High-precision monotonic timer for benchmarking and measuring elapsed time.
|
||||
*/
|
||||
class Stopwatch {
|
||||
private:
|
||||
std::chrono::steady_clock::time_point _start{std::chrono::steady_clock::now()};
|
||||
|
||||
public:
|
||||
constexpr Stopwatch() noexcept = default;
|
||||
|
||||
/// Resets the starting time point to now.
|
||||
void reset() noexcept {
|
||||
_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
/// Returns elapsed time in milliseconds.
|
||||
[[nodiscard]] double elapsed_ms() const noexcept {
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration<double, std::milli>(end - _start).count();
|
||||
}
|
||||
|
||||
/// Returns elapsed time in microseconds.
|
||||
[[nodiscard]] double elapsed_us() const noexcept {
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration<double, std::micro>(end - _start).count();
|
||||
}
|
||||
|
||||
/// Returns elapsed time in seconds.
|
||||
[[nodiscard]] double elapsed_sec() const noexcept {
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration<double>(end - _start).count();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chrono
|
||||
|
||||
} // namespace ckitty
|
||||
@@ -2,67 +2,161 @@
|
||||
|
||||
#include <ckitty/memory/primitives.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
// --- OS & Platform Preprocessor Detection ---
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
#define CKITTY_OS_WINDOWS 1
|
||||
#define CKITTY_DEVICE_DESKTOP 1
|
||||
#elif defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
||||
#define CKITTY_OS_IOS 1
|
||||
#define CKITTY_DEVICE_MOBILE 1
|
||||
#elif defined(TARGET_OS_MAC) && TARGET_OS_MAC
|
||||
#define CKITTY_OS_MACOS 1
|
||||
#define CKITTY_DEVICE_DESKTOP 1
|
||||
#else
|
||||
#error "Unknown Apple platform"
|
||||
#endif
|
||||
#elif defined(__ANDROID__)
|
||||
#define CKITTY_OS_ANDROID 1
|
||||
#define CKITTY_DEVICE_MOBILE 1
|
||||
#elif defined(__linux__) || defined(__unix__)
|
||||
#define CKITTY_OS_LINUX 1
|
||||
#define CKITTY_DEVICE_DESKTOP 1
|
||||
#elif defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm)
|
||||
#define CKITTY_OS_WASM 1
|
||||
#define CKITTY_DEVICE_BROWSER 1
|
||||
#else
|
||||
#error "Unknown operating system or target environment."
|
||||
#endif
|
||||
|
||||
// --- CPU Architecture Detection ---
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define CKITTY_ARCH_X86_64 1
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
#define CKITTY_ARCH_X86 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define CKITTY_ARCH_ARM64 1
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
#define CKITTY_ARCH_ARM 1
|
||||
#elif defined(__wasm32__) || defined(__wasm64__)
|
||||
#define CKITTY_ARCH_WASM 1
|
||||
#else
|
||||
#define CKITTY_ARCH_UNKNOWN 1
|
||||
#endif
|
||||
|
||||
// --- Build Mode Detection ---
|
||||
|
||||
#if defined(NDEBUG) && !defined(_DEBUG)
|
||||
#define CKITTY_BUILD_RELEASE 1
|
||||
#else
|
||||
#define CKITTY_BUILD_DEBUG 1
|
||||
#endif
|
||||
|
||||
namespace ckitty {
|
||||
|
||||
namespace environment {
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
||||
// Do not bother to differentiate 32 bits from 64 bits.
|
||||
inline const std::string OS_NAME = "Windows";
|
||||
inline const std::string DEVICE = "Desktop";
|
||||
inline const std::string CODE = "desktop/windows";
|
||||
#define CKITTY_OS_WINDOWS
|
||||
#define CKITTY_DEVICE_DESKTOP
|
||||
#elif __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
// iOS, tvOS, or watchOS Simulator
|
||||
// #elif TARGET_OS_MACCATALYST
|
||||
// Mac's Catalyst (ports iOS API into Mac, like UIKit).
|
||||
#if defined(TARGET_IPHONE_SIMULATOR) || defined(TARGET_OS_IPHONE)
|
||||
// iOS, tvOS, or watchOS device
|
||||
inline const std::string OS_NAME = "iOS";
|
||||
inline const std::string DEVICE = "Mobile";
|
||||
inline const std::string CODE = "mobile/ios";
|
||||
#define CKITTY_OS_IOS
|
||||
#define CKITTY_DEVICE_MOBILE
|
||||
#elif TARGET_OS_MAC
|
||||
// Other kinds of Apple platforms
|
||||
inline const std::string OS_NAME = "MacOS";
|
||||
inline const std::string DEVICE = "Desktop";
|
||||
inline const std::string CODE = "desktop/macos";
|
||||
#define CKITTY_OS_MACOS
|
||||
#define CKITTY_DEVICE_DESKTOP
|
||||
#else
|
||||
# error "Unknown Apple platform"
|
||||
#endif
|
||||
#elif __ANDROID__
|
||||
// Below __linux__ check should be enough to handle Android,
|
||||
// but something may be unique to Android.
|
||||
// all unices not caught above
|
||||
inline const std::string OS_NAME = "Android";
|
||||
inline const std::string DEVICE = "Mobile";
|
||||
inline const std::string CODE = "mobile/android";
|
||||
#define CKITTY_OS_ANDROID
|
||||
#define CKITTY_DEVICE_MOBILE
|
||||
#elif defined(__linux__) || defined(__unix__)
|
||||
// linux / unix
|
||||
inline const std::string OS_NAME = "Linux";
|
||||
inline const std::string DEVICE = "Desktop";
|
||||
inline const std::string CODE = "desktop/linux";
|
||||
#define CKITTY_OS_LINUX
|
||||
#define CKITTY_DEVICE_DESKTOP
|
||||
#elif defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm)
|
||||
// WASM
|
||||
inline const std::string OS_NAME = "WASM";
|
||||
inline const std::string DEVICE = "Browser";
|
||||
inline const std::string CODE = "browser/wasm";
|
||||
#define CKITTY_OS_WASM
|
||||
#define CKITTY_DEVICE_BROWSER
|
||||
#else
|
||||
# error "Unknown compiler/system."
|
||||
// --- OS String Constants ---
|
||||
|
||||
#if defined(CKITTY_OS_WINDOWS)
|
||||
constexpr std::string_view OS_NAME = "Windows";
|
||||
constexpr std::string_view DEVICE = "Desktop";
|
||||
constexpr std::string_view CODE = "desktop/windows";
|
||||
#elif defined(CKITTY_OS_IOS)
|
||||
constexpr std::string_view OS_NAME = "iOS";
|
||||
constexpr std::string_view DEVICE = "Mobile";
|
||||
constexpr std::string_view CODE = "mobile/ios";
|
||||
#elif defined(CKITTY_OS_MACOS)
|
||||
constexpr std::string_view OS_NAME = "MacOS";
|
||||
constexpr std::string_view DEVICE = "Desktop";
|
||||
constexpr std::string_view CODE = "desktop/macos";
|
||||
#elif defined(CKITTY_OS_ANDROID)
|
||||
constexpr std::string_view OS_NAME = "Android";
|
||||
constexpr std::string_view DEVICE = "Mobile";
|
||||
constexpr std::string_view CODE = "mobile/android";
|
||||
#elif defined(CKITTY_OS_LINUX)
|
||||
constexpr std::string_view OS_NAME = "Linux";
|
||||
constexpr std::string_view DEVICE = "Desktop";
|
||||
constexpr std::string_view CODE = "desktop/linux";
|
||||
#elif defined(CKITTY_OS_WASM)
|
||||
constexpr std::string_view OS_NAME = "WASM";
|
||||
constexpr std::string_view DEVICE = "Browser";
|
||||
constexpr std::string_view CODE = "browser/wasm";
|
||||
#endif
|
||||
|
||||
}
|
||||
// --- Compile-Time Boolean Flags (C++17 if constexpr ready) ---
|
||||
|
||||
}
|
||||
#if defined(CKITTY_OS_WINDOWS)
|
||||
constexpr bool is_windows = true;
|
||||
#else
|
||||
constexpr bool is_windows = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_OS_MACOS)
|
||||
constexpr bool is_macos = true;
|
||||
#else
|
||||
constexpr bool is_macos = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_OS_IOS)
|
||||
constexpr bool is_ios = true;
|
||||
#else
|
||||
constexpr bool is_ios = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_OS_LINUX)
|
||||
constexpr bool is_linux = true;
|
||||
#else
|
||||
constexpr bool is_linux = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_OS_ANDROID)
|
||||
constexpr bool is_android = true;
|
||||
#else
|
||||
constexpr bool is_android = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_OS_WASM)
|
||||
constexpr bool is_wasm = true;
|
||||
#else
|
||||
constexpr bool is_wasm = false;
|
||||
#endif
|
||||
|
||||
// Device Type Flags
|
||||
constexpr bool is_desktop = (is_windows || is_macos || is_linux);
|
||||
constexpr bool is_mobile = (is_ios || is_android);
|
||||
constexpr bool is_browser = is_wasm;
|
||||
|
||||
// Architecture Flags
|
||||
#if defined(CKITTY_ARCH_X86_64)
|
||||
constexpr bool is_x86_64 = true;
|
||||
constexpr std::string_view ARCH_NAME = "x86_64";
|
||||
#else
|
||||
constexpr bool is_x86_64 = false;
|
||||
#endif
|
||||
|
||||
#if defined(CKITTY_ARCH_ARM64)
|
||||
constexpr bool is_arm64 = true;
|
||||
constexpr std::string_view ARCH_NAME = "arm64";
|
||||
#else
|
||||
constexpr bool is_arm64 = false;
|
||||
#endif
|
||||
|
||||
// Build Mode Flags
|
||||
#if defined(CKITTY_BUILD_DEBUG)
|
||||
constexpr bool is_debug = true;
|
||||
constexpr bool is_release = false;
|
||||
#else
|
||||
constexpr bool is_debug = false;
|
||||
constexpr bool is_release = true;
|
||||
#endif
|
||||
|
||||
} // namespace environment
|
||||
|
||||
} // namespace ckitty
|
||||
+131
-39
@@ -1,62 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <ckitty/memory/primitives.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <source_location>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace ckitty {
|
||||
|
||||
struct error {
|
||||
// --- Single Stack Frame ---
|
||||
|
||||
i32 code;
|
||||
struct error_frame {
|
||||
i32 code{0};
|
||||
std::string message;
|
||||
std::source_location location;
|
||||
|
||||
error(i32 code = 0, const std::string& msg = "")
|
||||
: code(code), message(msg) {
|
||||
error_frame(i32 code, std::string msg, std::source_location loc = std::source_location::current())
|
||||
: code(code), message(std::move(msg)), location(loc) {}
|
||||
};
|
||||
|
||||
// --- Stacked Error Representation ---
|
||||
|
||||
class error {
|
||||
private:
|
||||
std::vector<error_frame> _frames;
|
||||
|
||||
public:
|
||||
error() = default;
|
||||
|
||||
/// Construct a single error frame with automatic source location capture
|
||||
error(i32 code, std::string message, std::source_location loc = std::source_location::current()) {
|
||||
_frames.emplace_back(code, std::move(message), loc);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return code;
|
||||
[[nodiscard]] bool empty() const noexcept { return _frames.empty(); }
|
||||
[[nodiscard]] explicit operator bool() const noexcept { return !_frames.empty(); }
|
||||
|
||||
[[nodiscard]] i32 code() const noexcept {
|
||||
return _frames.empty() ? 0 : _frames.front().code;
|
||||
}
|
||||
|
||||
operator std::string() const {
|
||||
std::ostringstream oss;
|
||||
oss << "[" << std::hex << std::showbase << std::uppercase << std::setw(2) << std::setfill('0') << code << "] ";
|
||||
oss << (message.empty() ? "(no info)" : message);
|
||||
return oss.str();
|
||||
}
|
||||
[[nodiscard]] const std::vector<error_frame>& frames() const noexcept { return _frames; }
|
||||
|
||||
error& operator+=(const error& err) {
|
||||
auto err1 = (*this) + err;
|
||||
code = err1.code;
|
||||
message = err1.message;
|
||||
/// Pushes a new contextual frame to the top of the stack
|
||||
error& context(std::string_view message, std::source_location loc = std::source_location::current()) {
|
||||
_frames.emplace_back(code(), std::string(message), loc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
error operator+(const error& err) const {
|
||||
// If this error has no code (no error), return the other error
|
||||
if (code == 0) {
|
||||
return err;
|
||||
/// Formats the error stack into a multi-line visual trace
|
||||
[[nodiscard]] std::string to_string() const {
|
||||
if (_frames.empty()) return "[OK] No Error";
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Error Stack Trace (Root Code: 0x" << std::hex << code() << std::dec << "):\n";
|
||||
|
||||
for (size_t i = 0; i < _frames.size(); ++i) {
|
||||
const auto& f = _frames[i];
|
||||
oss << " [" << i << "] " << f.message << "\n";
|
||||
oss << " at " << f.location.file_name() << ":"
|
||||
<< f.location.line() << " in " << f.location.function_name() << "\n";
|
||||
}
|
||||
|
||||
// If the other error has no code, return this error
|
||||
if (err.code == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Both errors have codes, combine them
|
||||
error result = *this;
|
||||
|
||||
// Combine messages
|
||||
if (!result.message.empty()) {
|
||||
result.message += "\n ";
|
||||
}
|
||||
|
||||
// add next error to the message
|
||||
result.message += err.operator std::string();
|
||||
return result;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
explicit operator std::string() const { return to_string(); }
|
||||
};
|
||||
|
||||
}
|
||||
// --- Failable (Expected) Template ---
|
||||
|
||||
struct unexpected {
|
||||
error err;
|
||||
explicit unexpected(error e) : err(std::move(e)) {}
|
||||
explicit unexpected(i32 code, std::string message) : err(code, message) {}
|
||||
};
|
||||
|
||||
template<typename T = void>
|
||||
class failable {
|
||||
private:
|
||||
std::variant<T, error> _storage;
|
||||
|
||||
public:
|
||||
// Success constructors
|
||||
constexpr failable(const T& val) : _storage(val) {}
|
||||
constexpr failable(T&& val) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
: _storage(std::move(val)) {}
|
||||
|
||||
// Failure constructors
|
||||
constexpr failable(error err) : _storage(std::move(err)) {}
|
||||
constexpr failable(unexpected unex) : _storage(std::move(unex.err)) {}
|
||||
|
||||
// Status queries
|
||||
[[nodiscard]] bool has_value() const noexcept { return std::holds_alternative<T>(_storage); }
|
||||
[[nodiscard]] explicit operator bool() const noexcept { return has_value(); }
|
||||
|
||||
// Value Access
|
||||
[[nodiscard]] T& value() & { return std::get<T>(_storage); }
|
||||
[[nodiscard]] const T& value() const& { return std::get<T>(_storage); }
|
||||
[[nodiscard]] T&& value() && { return std::get<T>(std::move(_storage)); }
|
||||
|
||||
[[nodiscard]] T value_or(T&& fallback) const& {
|
||||
return has_value() ? std::get<T>(_storage) : std::forward<T>(fallback);
|
||||
}
|
||||
|
||||
// Error Access
|
||||
[[nodiscard]] error& error_cause() & { return std::get<error>(_storage); }
|
||||
[[nodiscard]] const error& error_cause() const& { return std::get<error>(_storage); }
|
||||
|
||||
/// Attaches context to the contained error if in a failed state
|
||||
failable& context(std::string_view msg, std::source_location loc = std::source_location::current()) {
|
||||
if (!has_value()) {
|
||||
std::get<error>(_storage).context(msg, loc);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Void Specialization ---
|
||||
|
||||
template<>
|
||||
class failable<void> {
|
||||
private:
|
||||
std::optional<error> _err;
|
||||
|
||||
public:
|
||||
constexpr failable() noexcept = default;
|
||||
constexpr failable(error err) : _err(std::move(err)) {}
|
||||
failable(unexpected unex) : _err(std::move(unex.err)) {}
|
||||
|
||||
[[nodiscard]] bool has_value() const noexcept { return !_err.has_value(); }
|
||||
[[nodiscard]] explicit operator bool() const noexcept { return has_value(); }
|
||||
|
||||
[[nodiscard]] const error& error_cause() const { return *_err; }
|
||||
|
||||
failable& context(std::string_view msg, std::source_location loc = std::source_location::current()) {
|
||||
if (_err) {
|
||||
_err->context(msg, loc);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ckitty
|
||||
|
||||
|
||||
Reference in New Issue
Block a user