diff --git a/deps/ox/src/ox/std/CMakeLists.txt b/deps/ox/src/ox/std/CMakeLists.txt index 0abcc9c8..f1640968 100644 --- a/deps/ox/src/ox/std/CMakeLists.txt +++ b/deps/ox/src/ox/std/CMakeLists.txt @@ -60,6 +60,7 @@ install( byteswap.hpp defines.hpp error.hpp + fmt.hpp hardware.hpp hashmap.hpp heapmgr.hpp diff --git a/deps/ox/src/ox/std/bstring.hpp b/deps/ox/src/ox/std/bstring.hpp index 86ac71b0..4713508b 100644 --- a/deps/ox/src/ox/std/bstring.hpp +++ b/deps/ox/src/ox/std/bstring.hpp @@ -45,6 +45,8 @@ class BString { constexpr char &operator[](std::size_t i) noexcept; + void append(const char *str, std::size_t sz) noexcept; + constexpr char *data() noexcept; constexpr const char *c_str() const noexcept; @@ -153,6 +155,17 @@ constexpr char &BString::operator[](std::size_t i) noexcept { return m_buff[i]; } +template +void BString::append(const char *str, std::size_t strLen) noexcept { + auto currentLen = len(); + if (cap() < currentLen + strLen + 1) { + strLen = cap() - currentLen; + } + ox_memcpy(m_buff + currentLen, str, strLen); + // make sure last element is a null terminator + m_buff[currentLen + strLen] = 0; +} + template constexpr char *BString::data() noexcept { return static_cast(m_buff); diff --git a/deps/ox/src/ox/std/byteswap.hpp b/deps/ox/src/ox/std/byteswap.hpp index 274f1ee0..f987e811 100644 --- a/deps/ox/src/ox/std/byteswap.hpp +++ b/deps/ox/src/ox/std/byteswap.hpp @@ -175,12 +175,16 @@ class OX_PACKED ByteSwapInteger { return newVal; } + [[nodiscard]] constexpr auto get() const noexcept -> T { + return static_cast(*this); + } + /** * Returns the integer as it is stored. If it is stored as little endian, * a little endian integer is returned regardless of the endianness of * the system. */ - constexpr auto raw() noexcept -> T { + [[nodiscard]] constexpr auto raw() const noexcept -> T { return m_value; } diff --git a/deps/ox/src/ox/std/fmt.hpp b/deps/ox/src/ox/std/fmt.hpp new file mode 100644 index 00000000..d1f38916 --- /dev/null +++ b/deps/ox/src/ox/std/fmt.hpp @@ -0,0 +1,184 @@ +/* + * Copyright 2015 - 2021 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include + +namespace ox::detail { + +class FmtArg { + + private: + char dataStr[10] = {}; + + public: + const char *out = nullptr; + + template + constexpr FmtArg(T v) noexcept { + if constexpr(is_bool_v) { + out = v ? "true" : "false"; + } else if constexpr(is_integral_v) { + out = ox_itoa(v, dataStr); + } else { + out = v; + } + } + +}; + +[[nodiscard]] +constexpr uint64_t argCount(const char *str) noexcept { + uint64_t cnt = 0; + const auto prev = [str](std::size_t i) -> char { + if (i > 0) { + return str[i - 1]; + } else { + return '\0'; + } + }; + const auto next = [str](std::size_t i) -> char { + if (i < ox_strlen(str) - 1) { + return str[i + 1]; + } else { + return '\0'; + } + }; + for (std::size_t i = 0; i < ox_strlen(str); ++i) { + if (str[i] == '{' && prev(i) != '\\' && next(i) == '}') { + ++cnt; + } + } + return cnt; +} + +static_assert(argCount("{}") == 1); +static_assert(argCount("{}{}") == 2); +static_assert(argCount("thing1: {}, thing2: {}") == 2); +static_assert(argCount("thing1: {}, thing2: {}{}") == 3); + +struct FmtSegment { + const char *str = nullptr; + unsigned length = 0; + + constexpr bool operator==(const FmtSegment &o) const noexcept { + return length == o.length && ox_strncmp(str, o.str, length) == 0; + } + + constexpr bool operator!=(const FmtSegment &o) const noexcept { + return length != o.length || ox_strncmp(str, o.str, length) != 0; + } +}; + +template +struct Fmt { + static constexpr std::size_t size = sz; + FmtSegment segments[sz]; + + constexpr bool operator==(const Fmt &o) const noexcept { + for (std::size_t i = 0; i < sz; ++i) { + if (segments[i] != o.segments[i]) { + return false; + } + } + return true; + } +}; + +template +[[nodiscard]] +constexpr Fmt fmtSegments(const char *fmt) noexcept { + Fmt out; + const auto prev = [fmt](std::size_t i) -> char { + if (i > 0) { + return fmt[i - 1]; + } else { + return '\0'; + } + }; + const auto next = [fmt](std::size_t i) -> char { + if (i < ox_strlen(fmt) - 1) { + return fmt[i + 1]; + } else { + return '\0'; + } + }; + auto current = &out.segments[0]; + current->str = fmt; + for (std::size_t i = 0; i < ox_strlen(fmt); ++i) { + if (fmt[i] == '{' && prev(i) != '\\' && next(i) == '}') { + ++current; + current->str = fmt + i + 2; + current->length = 0; + i += 1; + } else { + ++current->length; + } + } + return out; +} + +static_assert([] { + constexpr auto fmt = "{}"; + return fmtSegments(fmt) == Fmt<2>{ + { + {"", 0}, + {"", 0}, + } + }; + }() +); +static_assert([] { + constexpr auto fmt = "thing: {}"; + return fmtSegments(fmt) == Fmt<2>{ + { + {"thing: ", 7}, + {"", 0}, + } + }; + }() +); +static_assert([] { + constexpr auto fmt = "thing: {}, thing2: {}"; + return fmtSegments(fmt) == Fmt<3>{ + { + {"thing: ", 7}, + {", thing2: ", 10}, + {"", 0}, + } + }; + }() +); +static_assert([] { + constexpr auto fmt = "thing: {}, thing2: {}s"; + return fmtSegments(fmt) == Fmt<3>{ + { + {"thing: ", 7}, + {", thing2: ", 10}, + {"s", 1}, + } + }; + }() +); +static_assert([] { + constexpr auto fmt = "loadTexture: section: {}, w: {}, h: {}"; + return fmtSegments(fmt) == Fmt<4>{ + { + {"loadTexture: section: ", 22}, + {", w: ", 5}, + {", h: ", 5}, + } + }; + }() +); + + +} diff --git a/deps/ox/src/ox/std/std.hpp b/deps/ox/src/ox/std/std.hpp index 722193b0..873aae8b 100644 --- a/deps/ox/src/ox/std/std.hpp +++ b/deps/ox/src/ox/std/std.hpp @@ -14,6 +14,7 @@ #include "byteswap.hpp" #include "defines.hpp" #include "error.hpp" +#include "fmt.hpp" #include "hardware.hpp" #include "hashmap.hpp" #include "heapmgr.hpp" diff --git a/deps/ox/src/ox/std/trace.cpp b/deps/ox/src/ox/std/trace.cpp index 77153c4f..d049b1af 100644 --- a/deps/ox/src/ox/std/trace.cpp +++ b/deps/ox/src/ox/std/trace.cpp @@ -6,60 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#if defined(OX_USE_STDLIB) -#include -#include -#endif - #include "trace.hpp" -extern "C" { - -void oxTraceInitHook(); - -void oxTraceHook(const char *file, int line, const char *ch, const char *msg); - -} - namespace ox::trace { -#if defined(OX_USE_STDLIB) -static const auto OxPrintTrace = std::getenv("OXTRACE"); -#else -constexpr auto OxPrintTrace = false; -#endif - -OutStream::OutStream(const char *file, int line, const char *ch, const char *msg) { - m_msg.file = file; - m_msg.line = line; - m_msg.ch = ch; - m_msg.msg = msg; -} - -OutStream::~OutStream() { - oxTraceHook(m_msg.file.c_str(), m_msg.line, m_msg.ch.c_str(), m_msg.msg.c_str()); -} - - -StdOutStream::StdOutStream(const char *file, int line, const char *ch, const char *msg) { - m_msg.file = file; - m_msg.line = line; - m_msg.ch = ch; - m_msg.msg = msg; -} - -StdOutStream::~StdOutStream() { - oxTraceHook(m_msg.file.c_str(), m_msg.line, m_msg.ch.c_str(), m_msg.msg.c_str()); -#if defined(OX_USE_STDLIB) - if (OxPrintTrace) { - std::cout << std::setw(53) << std::left << m_msg.ch.c_str() << '|'; - std::cout << std::setw(65) << std::left << m_msg.msg.c_str() << '|'; - std::cout << " " << m_msg.file.c_str() << ':' << m_msg.line << "\n"; - } -#endif -} - - void logError(const char *file, int line, Error err) { if (err) { TraceStream trc(file, line, "ox::error"); diff --git a/deps/ox/src/ox/std/trace.hpp b/deps/ox/src/ox/std/trace.hpp index 8b53db57..88fe9a84 100644 --- a/deps/ox/src/ox/std/trace.hpp +++ b/deps/ox/src/ox/std/trace.hpp @@ -8,15 +8,25 @@ #pragma once -#include +#include "bstring.hpp" +#include "fmt.hpp" +#include "hashmap.hpp" + +extern "C" { + +void oxTraceInitHook(); + +void oxTraceHook(const char *file, int line, const char *ch, const char *msg); + +} namespace ox::trace { struct TraceMsg { - ox::BString<255> file = ""; + const char *file = ""; int line = 0; uint64_t time = 0; - ox::BString<75> ch = ""; + const char *ch = ""; ox::BString<100> msg; }; @@ -33,26 +43,51 @@ Error model(T *io, ox::trace::TraceMsg *obj) { class OutStream { - private: + protected: const char *m_delimiter = " "; TraceMsg m_msg; public: - OutStream() = default; + constexpr OutStream(const char *file, int line, const char *ch, const char *msg = "") { + m_msg.file = file; + m_msg.line = line; + m_msg.ch = ch; + m_msg.msg = msg; + } - OutStream(const char *file, int line, const char *ch, const char *msg = ""); + template + constexpr OutStream(const char *file, int line, const char *ch, detail::Fmt fmtSegments, Args... args) { + static_assert(sizeof...(args) == fmtSegmentCnt - 1, "Wrong number of trace arguments for format."); + m_msg.file = file; + m_msg.line = line; + m_msg.ch = ch; + const auto &firstSegment = fmtSegments.segments[0]; + m_msg.msg.append(firstSegment.str, firstSegment.length); + const detail::FmtArg elements[sizeof...(args)] = {args...}; + for (auto i = 0u; i < fmtSegments.size - 1; ++i) { + m_msg.msg += elements[i].out; + const auto &s = fmtSegments.segments[i + 1]; + m_msg.msg.append(s.str, s.length); + } + } - ~OutStream(); + inline ~OutStream() { + oxTraceHook(m_msg.file, m_msg.line, m_msg.ch, m_msg.msg.c_str()); + } template - inline OutStream &operator<<(const T &v) { - m_msg.msg += m_delimiter; + constexpr OutStream &operator<<(const T &v) { + if (m_msg.msg.len()) { + m_msg.msg += m_delimiter; + } m_msg.msg += v; return *this; } - inline OutStream &operator<<(Error err) { - m_msg.msg += m_delimiter; + constexpr OutStream &operator<<(Error err) { + if (m_msg.msg.len()) { + m_msg.msg += m_delimiter; + } m_msg.msg += static_cast(err); return *this; } @@ -60,38 +95,7 @@ class OutStream { /** * del sets the delimiter between log segments. */ - inline OutStream &del(const char *delimiter) { - m_delimiter = delimiter; - return *this; - } - -}; - - -class StdOutStream { - - private: - const char *m_delimiter = " "; - TraceMsg m_msg; - - public: - StdOutStream() = default; - - StdOutStream(const char *file, int line, const char *ch, const char *msg = ""); - - ~StdOutStream(); - - template - constexpr inline StdOutStream &operator<<(const T &v) { - m_msg.msg += m_delimiter; - m_msg.msg += v; - return *this; - } - - /** - * del sets the delimiter between log segments. - */ - inline StdOutStream &del(const char *delimiter) { + constexpr OutStream &del(const char *delimiter) { m_delimiter = delimiter; return *this; } @@ -102,19 +106,19 @@ class StdOutStream { class NullStream { public: - constexpr NullStream() = default; - constexpr NullStream(const char*, int, const char*, const char* = "") { } - ~NullStream() = default; + template + constexpr NullStream(const char*, int, const char*, detail::Fmt, Args...) { + } template - constexpr inline NullStream &operator<<(const T&) { + constexpr NullStream &operator<<(const T&) { return *this; } - inline NullStream &del(const char*) { + constexpr NullStream &del(const char*) { return *this; } @@ -135,3 +139,5 @@ void init(); #define oxLogError(err) ox::trace::logError(__FILE__, __LINE__, err) #define oxTrace(ch) ox::trace::TraceStream(__FILE__, __LINE__, ch) + +#define oxTracef(ch, fmt, ...) ox::trace::TraceStream(__FILE__, __LINE__, ch, ox::detail::fmtSegments(fmt), ##__VA_ARGS__) diff --git a/deps/ox/src/ox/std/tracehook.cpp b/deps/ox/src/ox/std/tracehook.cpp index 593cfa39..c580aae9 100644 --- a/deps/ox/src/ox/std/tracehook.cpp +++ b/deps/ox/src/ox/std/tracehook.cpp @@ -6,6 +6,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#if defined(OX_USE_STDLIB) +#include +#include + +static const auto OxPrintTrace = std::getenv("OXTRACE") != nullptr; +#else +constexpr auto OxPrintTrace = false; +#endif + extern "C" { void oxTraceInitHook() { @@ -13,6 +22,13 @@ void oxTraceInitHook() { void oxTraceHook([[maybe_unused]] const char *file, [[maybe_unused]] int line, [[maybe_unused]] const char *ch, [[maybe_unused]] const char *msg) { +#if defined(OX_USE_STDLIB) + if (OxPrintTrace) { + std::cout << std::setw(53) << std::left << ch << "| "; + std::cout << std::setw(65) << std::left << msg << '|'; + std::cout << " " << file << ':' << line << "\n"; + } +#endif } } diff --git a/deps/ox/src/ox/std/typetraits.hpp b/deps/ox/src/ox/std/typetraits.hpp index 30176393..0c67e2ee 100644 --- a/deps/ox/src/ox/std/typetraits.hpp +++ b/deps/ox/src/ox/std/typetraits.hpp @@ -63,6 +63,15 @@ template<> struct is_integral: ox::true_type {}; template<> struct is_integral : ox::true_type {}; template<> struct is_integral: ox::true_type {}; +template +constexpr bool is_integral_v = ox::is_integral::value; + +template struct is_bool: ox::false_type {}; +template<> struct is_bool : ox::true_type {}; + +template +constexpr bool is_bool_v = ox::is_bool::value; + template struct is_union: ox::integral_constant> {}; template