diff options
-rw-r--r-- | deimos/core/BUILD | 2 | ||||
-rw-r--r-- | deimos/core/base.h | 7 | ||||
-rw-r--r-- | deimos/core/format.cpp | 83 | ||||
-rw-r--r-- | deimos/core/format.h | 58 | ||||
-rw-r--r-- | deimos/core/std.h | 26 | ||||
-rw-r--r-- | main/main.cpp | 11 |
6 files changed, 178 insertions, 9 deletions
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<T> list) :
+ m_begin{list.begin()},
+ m_size{(int64_t)list.size()}
+ {}
+
template<typename U>
requires std::convertible_to<U*, T*>
constexpr Span(const Span<U>& 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 <stdio.h> // NOLINT
+#include <inttypes.h> // 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<char>(&c, 1)));
+ }
+
+ void PushString(Span<const char> 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<const FormatArg> 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<const char> 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<typename T>
+concept Formattable = requires (T value) { FormatArg(value); };
+
+void FormatVa(IWriter*, gsl::czstring fmt, Span<const FormatArg>);
+
+template<Formattable... Args>
+void Format(IWriter* writer, gsl::czstring fmt, Args&&... args)
+{
+ FormatVa(writer, fmt, { FormatArg(std::forward<Args>(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<typename T> concept integral = __is_integral(T);
+template<typename T> concept signed_integral = integral<T> && __is_signed(T);
+template<typename T> concept unsigned_integral = integral<T> && __is_unsigned(T);
+
template<typename T> constexpr bool is_trivially_destructible_v = __is_trivially_destructible(T);
template<typename U, typename V> 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<typename T>
+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<int, int>, "");
diff --git a/main/main.cpp b/main/main.cpp index 4d3e829..10a7dc4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,13 +1,16 @@ #include <deimos/core/api_registry.h>
#include <deimos/core/os.h>
+#include <deimos/core/format.h>
+
+using namespace deimos;
int main(int /* argc */, char* /* argv */[])
{
- auto* api_registry = deimos::InitializeGlobalApiRegistry();
- auto* os_api = api_registry->Get<deimos::OsApi>();
+ auto* api_registry = InitializeGlobalApiRegistry();
+ auto* os_api = api_registry->Get<OsApi>();
- deimos::OsConsoleWriter writer(os_api->console, deimos::OsConsoleType::kStdOut);
- writer.Write(deimos::AsBytes(deimos::Span<const char>({"Hello, world!\n", 14})));
+ OsConsoleWriter writer(os_api->console, OsConsoleType::kStdOut);
+ Format(&writer, "Hello, $!", "world");
return 0;
}
|