From 3320960992afe36f4b6306130c6327e084c381b2 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Thu, 4 Apr 2024 18:37:13 +0200 Subject: Add format --- deimos/core/BUILD | 2 ++ deimos/core/base.h | 7 ++++- deimos/core/format.cpp | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ deimos/core/format.h | 58 +++++++++++++++++++++++++++++++++++ deimos/core/std.h | 26 +++++++++++++--- 5 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 deimos/core/format.cpp create mode 100644 deimos/core/format.h (limited to 'deimos') diff --git a/deimos/core/BUILD b/deimos/core/BUILD index fd20796..fcedb22 100644 --- a/deimos/core/BUILD +++ b/deimos/core/BUILD @@ -10,11 +10,13 @@ cc_library( "io.h", "os.h", "std.h", + "format.h", ], srcs = [ "allocator.cpp", "api_registry.cpp", "os_win32.cpp", + "format.cpp", ], visibility = ["//:__subpackages__"], ) diff --git a/deimos/core/base.h b/deimos/core/base.h index 8295053..21cad3d 100644 --- a/deimos/core/base.h +++ b/deimos/core/base.h @@ -72,6 +72,11 @@ public: Expects(size >= 0); } + constexpr Span(std::initializer_list list) : + m_begin{list.begin()}, + m_size{(int64_t)list.size()} + {} + template requires std::convertible_to constexpr Span(const Span& other) : // NOLINT @@ -81,7 +86,7 @@ public: constexpr T* data() const { return m_begin; } constexpr T* begin() const { return m_begin; } - constexpr T* end() const { return m_begin + m_size; } + constexpr T* end() const { return m_begin + m_size; } // NOLINT constexpr int64_t size() const { return m_size; } }; diff --git a/deimos/core/format.cpp b/deimos/core/format.cpp new file mode 100644 index 0000000..f4f4e83 --- /dev/null +++ b/deimos/core/format.cpp @@ -0,0 +1,83 @@ +#include "deimos/core/format.h" +#include "deimos/core/io.h" + +#include // NOLINT +#include // NOLINT + +namespace deimos +{ + +class FormatContext +{ + IWriter* m_writer; + +public: + explicit FormatContext(IWriter* writer) : m_writer{writer} {} + + void PushChar(char c) + { + m_writer->Write(AsBytes(Span(&c, 1))); + } + + void PushString(Span str) + { + m_writer->Write(AsBytes(str)); + } + + void PushUnsignedInteger(int64_t value) + { + char buffer[64]; + const int size = snprintf(&buffer[0], 64, "%" PRIu64, value); + PushString({ &buffer[0], size }); + } + + void PushInteger(int64_t value) + { + char buffer[64]; + const int size = snprintf(&buffer[0], 64, "%" PRIi64, value); + PushString({ &buffer[0], size }); + } + + void PushArg(const FormatArg& arg) + { + switch (arg.type) + { + case FormatArg::kString: + PushString(arg.string); + break; + case FormatArg::kInteger: + PushInteger(arg.integer); + break; + case FormatArg::kUnsignedInteger: + PushUnsignedInteger(arg.integer); + break; + } + } +}; + +void FormatVa(IWriter* writer, gsl::czstring fmt, Span args) +{ + if (fmt == nullptr) { return; } + + const FormatArg* arg = args.begin(); + const FormatArg* const end_arg = args.end(); + + FormatContext ctx(writer); + + while (*fmt != '\0') + { + if (*fmt == '$' && arg < end_arg) + { + ctx.PushArg(*arg++); // NOLINT + } + else + { + ctx.PushChar(*fmt); + } + + fmt += 1; + } +} + +} // namespace deimos + diff --git a/deimos/core/format.h b/deimos/core/format.h new file mode 100644 index 0000000..98d9001 --- /dev/null +++ b/deimos/core/format.h @@ -0,0 +1,58 @@ +#pragma once + +#include "deimos/core/base.h" + +namespace deimos +{ + +class IWriter; + +struct FormatArg +{ + enum Type : uint8_t + { + kInteger, + kUnsignedInteger, + kString, + }; + + Type type; + + union + { + int64_t integer; + uint64_t unsigned_integer; + + // @Todo Use string views + Span string; + }; + + explicit FormatArg(std::signed_integral auto value) : + type{kInteger}, + integer{value} + {} + + explicit FormatArg(std::unsigned_integral auto value) : + type{kUnsignedInteger}, + unsigned_integer{value} + {} + + explicit FormatArg(gsl::czstring value) : + type{kString}, + string{value, (int64_t)__builtin_strlen(value)} + {} +}; + +template +concept Formattable = requires (T value) { FormatArg(value); }; + +void FormatVa(IWriter*, gsl::czstring fmt, Span); + +template +void Format(IWriter* writer, gsl::czstring fmt, Args&&... args) +{ + FormatVa(writer, fmt, { FormatArg(std::forward(args))... }); +} + +} // namespace deimos + diff --git a/deimos/core/std.h b/deimos/core/std.h index c789d95..89d7da5 100644 --- a/deimos/core/std.h +++ b/deimos/core/std.h @@ -5,10 +5,10 @@ using uint16_t = unsigned short; using uint32_t = unsigned int; using uint64_t = unsigned long long; -using int8_t = char; -using int16_t = short; -using int32_t = int; -using int64_t = long long; +using int8_t = signed char; +using int16_t = signed short; +using int32_t = signed int; +using int64_t = signed long long; using size_t = uint64_t; @@ -17,6 +17,10 @@ static_assert(sizeof(size_t) == sizeof(void*), "size_t should be pointer size"); namespace std { +template concept integral = __is_integral(T); +template concept signed_integral = integral && __is_signed(T); +template concept unsigned_integral = integral && __is_unsigned(T); + template constexpr bool is_trivially_destructible_v = __is_trivially_destructible(T); template constexpr bool _is_same_helper = false; @@ -74,6 +78,20 @@ constexpr T exchange(T& obj, U&& new_value) enum __attribute__((__may_alias__)) byte : uint8_t {}; static_assert(sizeof(byte) == 1, ""); +template +class initializer_list +{ + const T* m_begin = nullptr; + size_t m_size = 0; + +public: + constexpr initializer_list() noexcept = default; + + constexpr size_t size() const noexcept { return m_size; } + constexpr const T* begin() const noexcept { return m_begin; } + constexpr const T* end() const noexcept { return m_begin + m_size; } +}; + } // namespace std static_assert(std::same_as, ""); -- cgit