diff --git a/.github/workflows/check_copyright_license_headers.yaml b/.github/workflows/check_copyright_license_headers.yaml index fabc664..912fc1f 100644 --- a/.github/workflows/check_copyright_license_headers.yaml +++ b/.github/workflows/check_copyright_license_headers.yaml @@ -22,4 +22,4 @@ jobs: env: GITHUB_BASE_REF: ${{github.base_ref}} GITHUB_HEAD_REF: ${{github.head_ref}} - GITHUB_TREE_URL: ${{github.server_url}}/${{github.repository}}/tree/${{github.head_ref}} \ No newline at end of file + GITHUB_HEAD_URL: ${{github.server_url}}/${{github.repository}}/tree/${{github.head_ref}} \ No newline at end of file diff --git a/Base b/Base index 367e49e..9133e93 160000 --- a/Base +++ b/Base @@ -1 +1 @@ -Subproject commit 367e49e1c9c47505e82250ee72a61a1154e3a48e +Subproject commit 9133e933321726778890ef16b83eda7e00021f7f diff --git a/scripts/check_copyright_license_headers.sh b/scripts/check_copyright_license_headers.sh index 1a2037e..c5a3a48 100644 --- a/scripts/check_copyright_license_headers.sh +++ b/scripts/check_copyright_license_headers.sh @@ -1,6 +1,6 @@ #!/bin/bash -HEADER=$'// Copyright 2025 Atalante.\n// Licensed under MIT.' +HEADER=$'// Copyright 2025 Atalante Studio.\n// Distributed under the MIT License.' files=$(git diff --diff-filter=d --name-only origin/$GITHUB_BASE_REF origin/$GITHUB_HEAD_REF -- *.{cpp,hpp,ipp}) unlicensed_files=() @@ -17,7 +17,7 @@ if [[ ${#unlicensed_files[@]} -gt 0 ]]; then echo "The following file(s) don't have a valid copyright license header." >> $GITHUB_STEP_SUMMARY for file in "${unlicensed_files[@]}"; do - echo "- [$file]($GITHUB_TREE_URL/$file)" >> $GITHUB_STEP_SUMMARY + echo "- [$file]($GITHUB_HEAD_URL/$file)" >> $GITHUB_STEP_SUMMARY done exit 1 diff --git a/scroll/ConsoleEscapeCode.hpp b/scroll/ConsoleEscapeCode.hpp index 7f15861..e7a9cef 100644 --- a/scroll/ConsoleEscapeCode.hpp +++ b/scroll/ConsoleEscapeCode.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once diff --git a/scroll/ConsoleLogger/ConsoleLogger.hpp b/scroll/ConsoleLogger/ConsoleLogger.hpp index 58925e5..dafcdef 100644 --- a/scroll/ConsoleLogger/ConsoleLogger.hpp +++ b/scroll/ConsoleLogger/ConsoleLogger.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once @@ -9,14 +9,9 @@ namespace scroll { class ConsoleLogger : public Logger { - private: - static void writeLogHeader(std::ostream& stream, view source, view levelName, ConsoleEscapeCode levelBackgroundColor, ConsoleEscapeCode levelForegroundColor); - public: explicit ConsoleLogger(std::ostream& stream, LogLevel minLogLevel, view source); - ~ConsoleLogger(); - std::ostream& getOutputStream(); void setOutputStream(std::ostream& stream); @@ -29,10 +24,10 @@ namespace scroll { ConsoleLogger& padRight(uint64 padding); template - void trace(view file, uint32 line, Argument&& argument); + void trace(view file, uint64 line, Argument&& argument); template - void trace(view file, uint32 line, view pattern, Argument&&... arguments); + void trace(view file, uint64 line, view pattern, Argument&&... arguments); template void debug(Argument&& argument); @@ -58,8 +53,51 @@ namespace scroll { template void error(view function, view file, uint64 line, view pattern, Argument&&... arguments); + private: + #if ATL_OPERATING_SYSTEM == ATL_OPERATING_SYSTEM_WINDOWS + void setConsoleMode(DWORD nStdHandle) { + const HANDLE hConsoleHandle = GetStdHandle(nStdHandle); + + ATL_ASSERT(hConsoleHandle != NULL && hConsoleHandle != INVALID_HANDLE_VALUE); + + BOOL result = FALSE; + DWORD mode; + + result = GetConsoleMode(hConsoleHandle, &mode); + + if (result == 0) { + redirected = true; + + return; + } + + result = SetConsoleMode(hConsoleHandle, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + ATL_ASSERT(result > 0); + } + #endif + + template + void writeConsoleLog(view level, uint64 levelSize, view pattern, Argument&&... arguments); + + template + void writeTraceLog(view file, uint64 line, view pattern, Argument&&... arguments); + + template + void writeDebugLog(view pattern, Argument&&... arguments); + + template + void writeInfoLog(view pattern, Argument&&... arguments); + + template + void writeWarningLog(view pattern, Argument&&... arguments); + + template + void writeErrorLog(view function, view file, uint64 line, view pattern, Argument&&... arguments); + private: std::ostream* stream; + bool redirected = false; bool writingEscapeCodes = false; }; } diff --git a/scroll/ConsoleLogger/ConsoleLogger.ipp b/scroll/ConsoleLogger/ConsoleLogger.ipp index c4ddc9e..2c3fe99 100644 --- a/scroll/ConsoleLogger/ConsoleLogger.ipp +++ b/scroll/ConsoleLogger/ConsoleLogger.ipp @@ -1,28 +1,16 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. namespace scroll { - inline void ConsoleLogger::writeLogHeader(std::ostream& stream, view source, view levelName, ConsoleEscapeCode levelBackgroundColor, ConsoleEscapeCode levelForegroundColor) { - stream << "\033[" << ConsoleEscapeCode::DIMMED << "m[" << timestamp() << "]\033[" << ConsoleEscapeCode::RESET_BRIGHT << "m "; - - if (source.count() > 0) { - stream << source << ' '; - } - - stream << "\033[" << levelBackgroundColor << ';' << levelForegroundColor << "m " << levelName << ' '; - stream << "\033[" << ConsoleEscapeCode::RESET_BACKGROUND_COLOR << ';' << ConsoleEscapeCode::RESET_FOREGROUND_COLOR << "m "; - } - - inline ConsoleLogger::ConsoleLogger(std::ostream& stream, LogLevel minimumLogLevel, view source) : - Logger(minimumLogLevel, source) + inline ConsoleLogger::ConsoleLogger(std::ostream& stream, LogLevel minLogLevel, view source) : + Logger(minLogLevel, source), + stream(&stream) { - setOutputStream(stream); - } + #if ATL_OPERATING_SYSTEM == ATL_OPERATING_SYSTEM_WINDOWS + SetConsoleOutputCP(CP_UTF8); - inline ConsoleLogger::~ConsoleLogger() { - if (writingEscapeCodes) { - stream->write("m", 1); - } + setConsoleMode(STD_OUTPUT_HANDLE); + #endif } inline std::ostream& ConsoleLogger::getOutputStream() { @@ -31,13 +19,6 @@ namespace scroll { inline void ConsoleLogger::setOutputStream(std::ostream& stream) { this->stream = &stream; - - #if ATL_OPERATING_SYSTEM == ATL_OPERATING_SYSTEM_WINDOWS - const HANDLE hConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); - - ATL_ASSERT(hConsoleHandle != NULL && hConsoleHandle != INVALID_HANDLE_VALUE); - ATL_ASSERT(SetConsoleMode(hConsoleHandle, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING) > 0); - #endif } template @@ -48,7 +29,11 @@ namespace scroll { stream->write("m", 1); } - *stream << argument; + buffer << argument; + + *stream << buffer.getText(); + + buffer.clear(); return *this; } @@ -64,7 +49,9 @@ namespace scroll { stream->write(";", 1); } - *stream << static_cast(argument); + const std::string string = toString(argument); + + stream->write(&string[0], string.size()); return *this; } @@ -82,142 +69,124 @@ namespace scroll { } template - void ConsoleLogger::trace(view file, uint32 line, Argument&& argument) { - if (LogLevel::TRACE < minLogLevel) { - return; - } - - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "TRACE", ConsoleEscapeCode::BACKGROUND_COLOR_WHITE, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - writeIndented(stream, format("[] (at []:[])", format(std::forward(argument)), file, line), getLogIndentation() - 2); - - stream << '\n'; + inline void ConsoleLogger::trace(view file, uint64 line, Argument&& argument) { + writeTraceLog(file, line, argumentInjectionPattern, std::forward(argument)); } template - void ConsoleLogger::trace(view file, uint32 line, view pattern, Argument&&... arguments) { - if (LogLevel::TRACE < minLogLevel) { - return; - } - - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "TRACE", ConsoleEscapeCode::BACKGROUND_COLOR_WHITE, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - writeIndented(stream, format("[] (at []:[])", format(pattern, std::forward(arguments)...), file, line), getLogIndentation() - 2); - - stream << '\n'; + inline void ConsoleLogger::trace(view file, uint64 line, view pattern, Argument&&... arguments) { + writeTraceLog(file, line, pattern, std::forward(arguments)...); } template - void ConsoleLogger::debug(Argument&& argument) { - if (LogLevel::DEBUG < minLogLevel) { - return; - } - - std::ostream& stream = std::cerr; + inline void ConsoleLogger::debug(Argument&& argument) { + writeDebugLog(argumentInjectionPattern, std::forward(argument)); + } - writeLogHeader(stream, source, "DEBUG", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_GRAY, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - writeIndented(stream, format(std::forward(argument)), getLogIndentation() - 2); + template + inline void ConsoleLogger::debug(view pattern, Argument&&... arguments) { + writeDebugLog(pattern, std::forward(arguments)...); + } - stream << '\n'; + template + inline void ConsoleLogger::info(Argument&& argument) { + writeInfoLog(argumentInjectionPattern, std::forward(argument)); } template - void ConsoleLogger::debug(view pattern, Argument&&... arguments) { - if (LogLevel::DEBUG < minLogLevel) { - return; - } - - std::ostream& stream = std::cerr; + inline void ConsoleLogger::info(view pattern, Argument&&... arguments) { + writeInfoLog(pattern, std::forward(arguments)...); + } - writeLogHeader(stream, source, "DEBUG", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_GRAY, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation() - 2); + template + inline void ConsoleLogger::warning(Argument&& argument) { + writeWarningLog(argumentInjectionPattern, std::forward(argument)); + } - stream << '\n'; + template + inline void ConsoleLogger::warning(view pattern, Argument&&... arguments) { + writeWarningLog(pattern, std::forward(arguments)...); } template - void ConsoleLogger::info(Argument&& argument) { - if (LogLevel::INFO < minLogLevel) { - return; - } + inline void ConsoleLogger::error(view function, view file, uint64 line, Argument&& argument) { + writeErrorLog(function, file, line, argumentInjectionPattern, std::forward(argument)); + } - std::ostream& stream = std::cout; + template + inline void ConsoleLogger::error(view function, view file, uint64 line, view pattern, Argument&&... arguments) { + writeErrorLog(function, file, line, pattern, std::forward(arguments)...); + } - writeLogHeader(stream, source, "INFO", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_BLUE, ConsoleEscapeCode::FOREGROUND_COLOR_WHITE); - writeIndented(stream, format(std::forward(argument)), getLogIndentation() - 3); + template + inline void ConsoleLogger::writeConsoleLog(view level, uint64 levelSize, view pattern, Argument&&... arguments) { + static constexpr view LOG_BUFFER_TEMPLATE = "\033[2m[00:00:00.000000]\033[22m "; - stream << '\n'; + writeLog(5, LOG_BUFFER_TEMPLATE, level, levelSize, pattern, std::forward(arguments)...); } template - void ConsoleLogger::info(view pattern, Argument&&... arguments) { - if (LogLevel::INFO < minLogLevel) { + inline void ConsoleLogger::writeTraceLog(view file, uint64 line, view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "\033[107;30m TRACE \033[49;39m "; + + if (LogLevel::TRACE < minLogLevel || redirected) { return; } - std::ostream& stream = std::cout; - - writeLogHeader(stream, source, "INFO", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_BLUE, ConsoleEscapeCode::FOREGROUND_COLOR_WHITE); - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation() - 3); - - stream << '\n'; + writeConsoleLog(LOG_LEVEL, 8, pattern, std::forward(arguments)...); + buffer << " (at " << file << ':' << line << ')' << '\n'; + buffer.flush(std::clog); } - template - void ConsoleLogger::warning(Argument&& argument) { - if (LogLevel::WARNING < minLogLevel) { + template + inline void ConsoleLogger::writeDebugLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "\033[47;30m DEBUG \033[49;39m "; + + if (LogLevel::DEBUG < minLogLevel || redirected) { return; } - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "WARNING", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_YELLOW, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - stream << "\033[" << ConsoleEscapeCode::FOREGROUND_COLOR_LIGHT_YELLOW << 'm'; - writeIndented(stream, format(std::forward(argument)), getLogIndentation()); - stream << "\033[" << ConsoleEscapeCode::RESET_FOREGROUND_COLOR << "m\n"; + writeConsoleLog(LOG_LEVEL, 8, pattern, std::forward(arguments)...); + buffer << '\n'; + buffer.flush(std::clog); } template - void ConsoleLogger::warning(view pattern, Argument&&... arguments) { - if (LogLevel::WARNING < minLogLevel) { + inline void ConsoleLogger::writeInfoLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "\033[104;97m INFO \033[49;39m "; + + if (LogLevel::INFO < minLogLevel || redirected) { return; } - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "WARNING", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_YELLOW, ConsoleEscapeCode::FOREGROUND_COLOR_BLACK); - stream << "\033[" << ConsoleEscapeCode::FOREGROUND_COLOR_LIGHT_YELLOW << 'm'; - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation()); - stream << "\033[" << ConsoleEscapeCode::RESET_FOREGROUND_COLOR << "m\n"; + writeConsoleLog(LOG_LEVEL, 7, pattern, std::forward(arguments)...); + buffer << '\n'; + buffer.flush(std::clog); } - template - void ConsoleLogger::error(view function, view file, uint64 line, Argument&& argument) { - if (LogLevel::ERROR < minLogLevel) { + template + inline void ConsoleLogger::writeWarningLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "\033[103;30m WARNING \033[49;39m \033[93m"; + + if (LogLevel::WARNING < minLogLevel || redirected) { return; } - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "ERROR", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_RED, ConsoleEscapeCode::FOREGROUND_COLOR_WHITE); - stream << "\033[" << ConsoleEscapeCode::FOREGROUND_COLOR_LIGHT_RED << 'm'; - writeIndented(stream, format("[] (in [], at []:[])", format(std::forward(argument)), function, file, line), getLogIndentation() - 2); - stream << "\033[" << ConsoleEscapeCode::RESET_FOREGROUND_COLOR << "m\n"; + writeConsoleLog(LOG_LEVEL, 9, pattern, std::forward(arguments)...); + buffer << "\033[39m\n"; + buffer.flush(std::clog); } template - void ConsoleLogger::error(view function, view file, uint64 line, view pattern, Argument&&... arguments) { - if (LogLevel::ERROR < minLogLevel) { + inline void ConsoleLogger::writeErrorLog(view function, view file, uint64 line, view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "\033[101;97m ERROR \033[49;39m \033[91m"; + + if (LogLevel::ERROR < minLogLevel || redirected) { return; } - std::ostream& stream = std::cerr; - - writeLogHeader(stream, source, "ERROR", ConsoleEscapeCode::BACKGROUND_COLOR_LIGHT_RED, ConsoleEscapeCode::FOREGROUND_COLOR_WHITE); - stream << "\033[" << ConsoleEscapeCode::FOREGROUND_COLOR_LIGHT_RED << 'm'; - writeIndented(stream, format("[] (in [], at []:[])", format(pattern, std::forward(arguments)...), function, file, line), getLogIndentation() - 2); - stream << "\033[" << ConsoleEscapeCode::RESET_FOREGROUND_COLOR << "m\n"; + writeConsoleLog(LOG_LEVEL, 8, pattern, std::forward(arguments)...); + buffer << " (in " << function << ", at " << file << ':' << line << ")\033[39m\n"; + buffer.flush(std::clog); } } \ No newline at end of file diff --git a/scroll/Conversions.hpp b/scroll/Conversions.hpp index cf8565c..ac838b2 100644 --- a/scroll/Conversions.hpp +++ b/scroll/Conversions.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once diff --git a/scroll/Conversions.ipp b/scroll/Conversions.ipp index b47c45a..cd8bfa7 100644 --- a/scroll/Conversions.ipp +++ b/scroll/Conversions.ipp @@ -1,11 +1,15 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. namespace scroll { inline std::ostream& operator<<(std::ostream& stream, view view) { return stream.write(&view[0], view.count()); } + inline std::ostream& operator<<(std::ostream& stream, const sequence& sequence) { + return stream << &sequence[0]; + } + inline std::ostream& operator<<(std::ostream& stream, ConsoleEscapeCode code) { return stream << static_cast(code); } diff --git a/scroll/FileLogger/FileLogger.hpp b/scroll/FileLogger/FileLogger.hpp index 2daef70..f6029bd 100644 --- a/scroll/FileLogger/FileLogger.hpp +++ b/scroll/FileLogger/FileLogger.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once @@ -12,10 +12,10 @@ namespace scroll { explicit FileLogger(std::ofstream& stream, LogLevel minLogLevel, view source); template - void trace(view file, uint32 line, Argument&& argument); + void trace(view file, uint64 line, Argument&& argument); template - void trace(view file, uint32 line, view pattern, Argument&&... arguments); + void trace(view file, uint64 line, view pattern, Argument&&... arguments); template void debug(Argument&& argument); @@ -42,7 +42,23 @@ namespace scroll { void error(view function, view file, uint64 line, view pattern, Argument&&... arguments); private: - void writeLogHeader(view levelName); + template + void writeFileLog(view level, view pattern, Argument&&... arguments); + + template + void writeTraceLog(view file, uint64 line, view pattern, Argument&&... arguments); + + template + void writeDebugLog(view pattern, Argument&&... arguments); + + template + void writeInfoLog(view pattern, Argument&&... arguments); + + template + void writeWarningLog(view pattern, Argument&&... arguments); + + template + void writeErrorLog(view function, view file, uint64 line, view pattern, Argument&&... arguments); public: std::ofstream& stream; diff --git a/scroll/FileLogger/FileLogger.ipp b/scroll/FileLogger/FileLogger.ipp index 1d5d309..974c0fd 100644 --- a/scroll/FileLogger/FileLogger.ipp +++ b/scroll/FileLogger/FileLogger.ipp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. namespace scroll { inline FileLogger::FileLogger(std::ofstream& stream, LogLevel minLogLevel, view source) : @@ -10,132 +10,124 @@ namespace scroll { } template - void FileLogger::trace(view file, uint32 line, Argument&& argument) { - if (LogLevel::TRACE < minLogLevel) { - return; - } - - writeLogHeader("TRACE"); - writeIndented(stream, format("[] (at []:[])", format(std::forward(argument)), file, line), getLogIndentation() - 4); - - stream << '\n'; + inline void FileLogger::trace(view file, uint64 line, Argument&& argument) { + writeTraceLog(file, line, argumentInjectionPattern, std::forward(argument)); } template - void FileLogger::trace(view file, uint32 line, view pattern, Argument&&... arguments) { - if (LogLevel::TRACE < minLogLevel) { - return; - } - - writeLogHeader("TRACE"); - writeIndented(stream, format("[] (at []:[])", format(pattern, std::forward(arguments)...), file, line), getLogIndentation() - 4); - - stream << '\n'; + inline void FileLogger::trace(view file, uint64 line, view pattern, Argument&&... arguments) { + writeTraceLog(file, line, pattern, std::forward(arguments)...); } template - void FileLogger::debug(Argument&& argument) { - if (LogLevel::DEBUG < minLogLevel) { - return; - } - - writeLogHeader("DEBUG"); - writeIndented(stream, format(std::forward(argument)), getLogIndentation() - 4); - - stream << '\n'; + inline void FileLogger::debug(Argument&& argument) { + writeDebugLog(argumentInjectionPattern, std::forward(argument)); } template - void FileLogger::debug(view pattern, Argument&&... arguments) { - if (LogLevel::DEBUG < minLogLevel) { - return; - } + inline void FileLogger::debug(view pattern, Argument&&... arguments) { + writeDebugLog(pattern, std::forward(arguments)...); + } - writeLogHeader("DEBUG"); - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation() - 4); + template + inline void FileLogger::info(Argument&& argument) { + writeInfoLog(argumentInjectionPattern, std::forward(argument)); + } - stream << '\n'; + template + inline void FileLogger::info(view pattern, Argument&&... arguments) { + writeInfoLog(pattern, std::forward(arguments)...); } template - void FileLogger::info(Argument&& argument) { - if (LogLevel::INFO < minLogLevel) { - return; - } + inline void FileLogger::warning(Argument&& argument) { + writeWarningLog(argumentInjectionPattern, std::forward(argument)); + } - writeLogHeader("INFO"); - writeIndented(stream, format(std::forward(argument)), getLogIndentation() - 5); + template + inline void FileLogger::warning(view pattern, Argument&&... arguments) { + writeWarningLog(pattern, std::forward(arguments)...); + } - stream << '\n'; + template + inline void FileLogger::error(view function, view file, uint64 line, Argument&& argument) { + writeErrorLog(function, file, line, argumentInjectionPattern, std::forward(argument)); } template - void FileLogger::info(view pattern, Argument&&... arguments) { - if (LogLevel::INFO < minLogLevel) { - return; - } + inline void FileLogger::error(view function, view file, uint64 line, view pattern, Argument&&... arguments) { + writeErrorLog(function, file, line, pattern, std::forward(arguments)...); + } - writeLogHeader("INFO"); - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation() - 5); + template + inline void FileLogger::writeFileLog(view level, view pattern, Argument&&... arguments) { + static constexpr view LOG_BUFFER_TEMPLATE = "[00:00:00.000000] "; - stream << '\n'; + writeLog(1, LOG_BUFFER_TEMPLATE, level, level.count(), pattern, std::forward(arguments)...); } - template - void FileLogger::warning(Argument&& argument) { - if (LogLevel::WARNING < minLogLevel) { + template + inline void FileLogger::writeTraceLog(view file, uint64 line, view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "TRACE "; + + if (LogLevel::TRACE < minLogLevel) { return; } - writeLogHeader("WARNING"); - writeIndented(stream, format(std::forward(argument)), getLogIndentation() - 2); - - stream << '\n'; + writeFileLog(LOG_LEVEL, pattern, std::forward(arguments)...); + buffer << " (at " << file << ':' << line << ')' << '\n'; + buffer.flush(stream); } template - void FileLogger::warning(view pattern, Argument&&... arguments) { - if (LogLevel::WARNING < minLogLevel) { + inline void FileLogger::writeDebugLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "DEBUG "; + + if (LogLevel::DEBUG < minLogLevel) { return; } - writeLogHeader("WARNING"); - writeIndented(stream, format(pattern, std::forward(arguments)...), getLogIndentation() - 2); - - stream << '\n'; + writeFileLog(LOG_LEVEL, pattern, std::forward(arguments)...); + buffer << '\n'; + buffer.flush(stream); } - template - void FileLogger::error(view function, view file, uint64 line, Argument&& argument) { - if (LogLevel::ERROR < minLogLevel) { + template + inline void FileLogger::writeInfoLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "INFO "; + + if (LogLevel::INFO < minLogLevel) { return; } - writeLogHeader("ERROR"); - writeIndented(stream, format("[] (in [], at []:[])", format(std::forward(argument)), function, file, line), getLogIndentation() - 4); - - stream << '\n'; + writeFileLog(LOG_LEVEL, pattern, std::forward(arguments)...); + buffer << '\n'; + buffer.flush(stream); } template - void FileLogger::error(view function, view file, uint64 line, view pattern, Argument&&... arguments) { - if (LogLevel::ERROR < minLogLevel) { + inline void FileLogger::writeWarningLog(view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "WARNING "; + + if (LogLevel::WARNING < minLogLevel) { return; } - writeLogHeader("ERROR"); - writeIndented(stream, format("[] (in [], at []:[])", format(pattern, std::forward(arguments)...), function, file, line), getLogIndentation() - 4); - - stream << '\n'; + writeFileLog(LOG_LEVEL, pattern, std::forward(arguments)...); + buffer << '\n'; + buffer.flush(stream); } - inline void FileLogger::writeLogHeader(view levelName) { - stream << '[' << timestamp() << "] "; + template + inline void FileLogger::writeErrorLog(view function, view file, uint64 line, view pattern, Argument&&... arguments) { + static constexpr view LOG_LEVEL = "ERROR "; - if (source.count() > 0) { - stream << source << ' '; + if (LogLevel::ERROR < minLogLevel) { + return; } - stream << levelName << ' '; + writeFileLog(LOG_LEVEL, pattern, std::forward(arguments)...); + buffer << " (in " << function << ", at " << file << ':' << line << ')' << '\n'; + buffer.flush(stream); } } \ No newline at end of file diff --git a/scroll/LogLevel.hpp b/scroll/LogLevel.hpp index 693c299..2f46c5e 100644 --- a/scroll/LogLevel.hpp +++ b/scroll/LogLevel.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once diff --git a/scroll/Logger/Logger.hpp b/scroll/Logger/Logger.hpp index 1855e86..f86aac8 100644 --- a/scroll/Logger/Logger.hpp +++ b/scroll/Logger/Logger.hpp @@ -1,17 +1,20 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once #include "scroll/LogLevel.hpp" +#include "scroll/TextBuffer/TextBuffer.hpp" namespace scroll { template - struct ArgumentInjectionPattern { - static sequence argumentInjectionPattern; + class BaseLogger { + protected: + static sequence argumentInjectionPattern; + static TextBuffer formatBuffer; }; - class Logger : public ArgumentInjectionPattern<> { + class Logger : public BaseLogger<> { public: // Returns the current argument injection pattern. static view getArgumentInjectionPattern(); @@ -19,21 +22,51 @@ namespace scroll { // Replaces the current argument injection pattern. static void setArgumentInjectionPattern(view pattern); - template - static sequence format(Argument&& argument); + static sequence format(view pattern); - template - static sequence format(view pattern, HeadArgument&& headArgument, Argument&&... arguments); + template + static sequence format(view pattern, CurrentArgument&& argument, Argument&&... arguments) { + const uint64 argumentInjectionPatternOffset = view(pattern).find(argumentInjectionPattern, 0); - static sequence timestamp(); + if (argumentInjectionPatternOffset > 0) { + formatBuffer << view(&pattern[0], argumentInjectionPatternOffset); + } - protected: - // NOTE: `indentation` must **not** include the size of `text`. - static void writeIndented(std::ostream& stream, view text, uint64 indentation); + if (argumentInjectionPatternOffset >= pattern.count()) { + const sequence text = formatBuffer.getText(); + + formatBuffer.clear(); + + return text; + } + + formatBuffer << argument; + + const uint64 patternOffset = argumentInjectionPatternOffset + argumentInjectionPattern.count(); + + if (patternOffset >= pattern.count()) { + const sequence text = formatBuffer.getText(); + + formatBuffer.clear(); + + return text; + } + + pattern = view(&pattern[patternOffset], pattern.count() - patternOffset); + + return format(pattern, std::forward(arguments)...); + } private: - static constexpr uint64 TIMESTAMP_SIZE = 15; - static constexpr uint64 MAX_LOG_LEVEL_NAME_SIZE = 7; + static sequence createIndentationString(uint64 indentation) { + sequence indentationString(indentation); + + for (char8& character : indentationString) { + character = ' '; + } + + return indentationString; + } public: LogLevel getMinLogLevel() const; @@ -43,11 +76,133 @@ namespace scroll { protected: explicit Logger(LogLevel minLogLevel, view source); - uint16 getLogIndentation() const; + template + void writeLog(uint64 offset, view bufferTemplate, view level, uint64 levelSize, view pattern, Argument&&... arguments) { + #ifdef _MSC_VER + const errno_t error = strncpy_s(&buffer.buffer[0], bufferTemplate.count() + 1, &bufferTemplate[0], bufferTemplate.count()); + + ATL_ASSERT(error == 0); + #else + std::strncpy(&buffer.buffer[0], &bufferTemplate[0], bufferTemplate.count()); + #endif + + buffer.seek(offset); + + writeTimestamp(); + + buffer.seek(bufferTemplate.count()); + + uint64 indentation = 18; + + if (source.count() > 0) { + buffer << source << ' '; + + indentation += source.count() + 1; + } + + buffer << level; + + indentation += levelSize; + + const sequence indentationString = createIndentationString(indentation); + + writeFormattedArguments(pattern, indentationString, std::forward(arguments)...); + } + + private: + void writeIndented(view pattern, const sequence& indentationString) { + uint64 previousLineOffset = 0; + uint64 lineOffset = 0; + + while (true) { + lineOffset = pattern.find('\n', previousLineOffset); + + if (lineOffset > previousLineOffset) { + buffer << view(&pattern[previousLineOffset], lineOffset - previousLineOffset); + } + + if (lineOffset >= pattern.count()) { + break; + } + + buffer << '\n' << indentationString; + + previousLineOffset = lineOffset + 1; + } + } + + void writeFormattedArguments(view pattern, const sequence& indentationString); + + template + void writeFormattedArguments(view pattern, const sequence& indentationString, Argument&& argument, PackedArgument&&... arguments) { + const uint64 argumentInjectionPatternOffset = pattern.find(argumentInjectionPattern, 0); + + if (argumentInjectionPatternOffset > 0) { + writeIndented(view(&pattern[0], argumentInjectionPatternOffset), indentationString); + } + + if (argumentInjectionPatternOffset >= pattern.count()) { + return; + } + + const std::string formattedArgument = toString(argument); + + writeIndented(&formattedArgument[0], indentationString); + + const uint64 patternOffset = argumentInjectionPatternOffset + argumentInjectionPattern.count(); + + if (patternOffset >= pattern.count()) { + return; + } + + pattern = view(&pattern[patternOffset], pattern.count() - patternOffset); + + writeFormattedArguments(pattern, indentationString, std::forward(arguments)...); + } + + void writeTimestamp() { + const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + const uint64 us = std::chrono::duration_cast(now - std::chrono::time_point_cast(now)).count(); + const std::time_t time = std::chrono::system_clock::to_time_t(now); + + std::tm* dateTime; + + #ifdef _MSC_VER + std::tm _dateTime{}; + + const errno_t error = localtime_s(&_dateTime, &time); + + ATL_ASSERT(error == 0); + + dateTime = &_dateTime; + #else + dateTime = std::localtime(&time); + #endif + + std::string formatted = toString(dateTime->tm_hour); + + buffer.padLeft(&formatted[0], 2); + buffer.jump(1); + + formatted = toString(dateTime->tm_min); + + buffer.padLeft(&formatted[0], 2); + buffer.jump(1); + + formatted = toString(dateTime->tm_sec); + + buffer.padLeft(&formatted[0], 2); + buffer.jump(1); + + formatted = toString(us); + + buffer.padLeft(&formatted[0], 6); + } protected: LogLevel minLogLevel; view source; + TextBuffer buffer; }; } diff --git a/scroll/Logger/Logger.ipp b/scroll/Logger/Logger.ipp index 11be602..7f51a35 100644 --- a/scroll/Logger/Logger.ipp +++ b/scroll/Logger/Logger.ipp @@ -1,44 +1,12 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. namespace scroll { template - sequence ArgumentInjectionPattern::argumentInjectionPattern = "[]"; + sequence BaseLogger::argumentInjectionPattern = "[]"; - template - sequence Logger::format(Argument&& argument) { - static std::ostringstream stream; - - stream << argument; - - const sequence formattedArgument = stream.str().c_str(); - - stream.str({}); - - return formattedArgument; - } - - template - sequence Logger::format(view pattern, HeadArgument&& headArgument, Argument&&... arguments) { - const uint64 argumentInjectionPatternOffset = pattern.find(argumentInjectionPattern, 0); - - if (argumentInjectionPatternOffset >= pattern.count()) { - return pattern; - } - - const sequence formattedArgument = format(headArgument); - - sequence updatedPattern(pattern.count() - argumentInjectionPattern.count() + formattedArgument.count()); - - copy(&pattern[0], &pattern[argumentInjectionPatternOffset], &updatedPattern[0]); - copy(formattedArgument.begin(), formattedArgument.end(), &updatedPattern[argumentInjectionPatternOffset]); - - if (argumentInjectionPatternOffset + argumentInjectionPattern.count() < pattern.count()) { - copy(&pattern[argumentInjectionPatternOffset + argumentInjectionPattern.count()], pattern.end(), &updatedPattern[argumentInjectionPatternOffset + formattedArgument.count()]); - } - - return format(updatedPattern, std::forward(arguments)...); - } + template + TextBuffer BaseLogger::formatBuffer; inline view Logger::getArgumentInjectionPattern() { return argumentInjectionPattern; @@ -47,44 +15,17 @@ namespace scroll { inline void Logger::setArgumentInjectionPattern(view pattern) { ATL_ASSERT(pattern.count() > 0); - argumentInjectionPattern = &pattern[0]; + argumentInjectionPattern = pattern; } - inline sequence Logger::timestamp() { - static std::ostringstream usStream; - - const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - const uint64 us = std::chrono::duration_cast(now - std::chrono::time_point_cast(now)).count(); - const std::time_t time = std::chrono::system_clock::to_time_t(now); - - std::tm* dateTime; - - #ifdef _MSC_VER - std::tm _dateTime{}; - - const errno_t error = localtime_s(&_dateTime, &time); + inline sequence Logger::format(view pattern) { + formatBuffer << pattern; - ATL_ASSERT(error == 0); + const sequence text = formatBuffer.getText(); - dateTime = &_dateTime; - #else - dateTime = std::localtime(&time); - #endif + formatBuffer.clear(); - // Include null-terminating character. - sequence hmsString(9); - - const uint64 hmsCharacterCount = std::strftime(&hmsString[0], hmsString.count(), "%H:%M:%S", dateTime); - - ATL_ASSERT(hmsCharacterCount > 0); - - usStream << std::setw(6) << std::setfill('0') << us; - - const sequence timestamp = format("[].[]", hmsString, usStream.str()); - - usStream.str({}); - - return timestamp; + return text; } inline LogLevel Logger::getMinLogLevel() const { @@ -100,41 +41,7 @@ namespace scroll { source(source) {} - inline uint16 Logger::getLogIndentation() const { - uint16 indentation = static_cast(TIMESTAMP_SIZE + MAX_LOG_LEVEL_NAME_SIZE + 6); - - if (source.count() > 0) { - indentation += static_cast(source.count()) + 1; - } - - return indentation; - } - - inline void Logger::writeIndented(std::ostream& stream, view text, uint64 indentation) { - // TODO: Create string with fixed indentation and write parts of it. - sequence indentationString(indentation); - - std::fill_n(&indentationString[0], indentation, ' '); - - uint64 previousOffset = 0; - uint64 offset = 0; - - while (true) { - offset = text.find('\n', previousOffset); - - if (offset == previousOffset) { - break; - } - - stream.write(&text[previousOffset], offset - previousOffset); - - if (offset >= text.count()) { - break; - } - - previousOffset = offset + 1; - - stream << '\n' << indentationString; - } + inline void Logger::writeFormattedArguments(view pattern, const sequence& indentationString) { + writeIndented(pattern, indentationString); } } \ No newline at end of file diff --git a/scroll/TextBuffer/TextBuffer.hpp b/scroll/TextBuffer/TextBuffer.hpp new file mode 100644 index 0000000..3447153 --- /dev/null +++ b/scroll/TextBuffer/TextBuffer.hpp @@ -0,0 +1,50 @@ +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. + +#pragma once + +namespace scroll { + template + std::string toString(Argument argument) { + // NOTE: Being here means that a toString() specialization for `argument` was not provided. + return "\xEF\xBF\xBD"; + } + + class TextBuffer { + protected: + static constexpr uint64 MAX_LOG_BUFFER_SIZE = 4096; + + public: + sequence getText() const; + + void jump(uint64 offset); + void seek(uint64 offset); + + template + TextBuffer& operator<<(Argument argument); + + TextBuffer& operator<<(char8 argument); + TextBuffer& operator<<(const sequence& argument); + TextBuffer& operator<<(const std::string& argument); + TextBuffer& operator<<(char8* argument); + TextBuffer& operator<<(const char8* argument); + TextBuffer& operator<<(view argument); + + #if ATL_STANDARD >= ATL_STANDARD_CPP17 + TextBuffer& operator<<(std::string_view argument); + #endif + + TextBuffer& padLeft(view text, uint64 padding); + TextBuffer& padRight(view text, uint64 padding); + + void flush(std::ostream& stream); + + void clear(); + + public: + char8 buffer[MAX_LOG_BUFFER_SIZE]{}; + uint64 offset = 0; + }; +} + +#include "scroll/TextBuffer/TextBuffer.ipp" \ No newline at end of file diff --git a/scroll/TextBuffer/TextBuffer.ipp b/scroll/TextBuffer/TextBuffer.ipp new file mode 100644 index 0000000..2aea00e --- /dev/null +++ b/scroll/TextBuffer/TextBuffer.ipp @@ -0,0 +1,173 @@ +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. + +namespace scroll { + template<> + inline std::string toString(bool argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(uint8 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(uint16 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(uint32 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(uint64 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(sint8 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(sint16 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(sint32 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(sint64 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(char8 argument) { + return std::string(1, argument); + } + + template<> + inline std::string toString(float32 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(float64 argument) { + return std::to_string(argument); + } + + template<> + inline std::string toString(const char8* argument) { + return argument; + } + + template<> + inline std::string toString(const sequence& argument) { + return std::string(&argument[0], argument.count()); + } + + template<> + inline std::string toString(view argument) { + return std::string(&argument[0], argument.count()); + } + + template<> + inline std::string toString(const std::string& argument) { + return argument; + } + + template<> + inline std::string toString(ConsoleEscapeCode argument) { + return std::to_string(static_cast(argument)); + } + + inline sequence TextBuffer::getText() const { + return sequence(&buffer[0], offset); + } + + inline void TextBuffer::jump(uint64 offset) { + ATL_ASSERT(this->offset + offset < MAX_LOG_BUFFER_SIZE); + + this->offset += offset; + } + + inline void TextBuffer::seek(uint64 offset) { + ATL_ASSERT(offset < MAX_LOG_BUFFER_SIZE); + + this->offset = offset; + } + + template + inline TextBuffer& TextBuffer::operator<<(Argument argument) { + const std::string string = toString(argument); + + return operator<<(view(&string[0], string.size())); + } + + inline TextBuffer& TextBuffer::operator<<(char8 argument) { + buffer[offset] = argument; + + jump(1); + + return *this; + } + + inline TextBuffer& TextBuffer::operator<<(const sequence& argument) { + return operator<<(view(&argument[0], argument.count())); + } + + inline TextBuffer& TextBuffer::operator<<(const std::string& argument) { + return operator<<(view(&argument[0], argument.size())); + } + + inline TextBuffer& TextBuffer::operator<<(char8* argument) { + return operator<<(view(argument)); + } + + inline TextBuffer& TextBuffer::operator<<(const char8* argument) { + return operator<<(view(argument)); + } + + inline TextBuffer& TextBuffer::operator<<(view argument) { + copy(argument.begin(), argument.end(), &buffer[offset]); + jump(argument.count()); + + return *this; + } + + #if ATL_STANDARD >= ATL_STANDARD_CPP17 + inline TextBuffer& TextBuffer::operator<<(std::string_view argument) { + return operator<<(view(&argument[0], argument.size())); + } + #endif + + inline TextBuffer& TextBuffer::padLeft(view text, uint64 padding) { + offset += ATL_MAX(0, padding - text.count()); + + return operator<<(text); + } + + inline TextBuffer& TextBuffer::padRight(view text, uint64 padding) { + TextBuffer& textBuffer = operator<<(text); + + offset += ATL_MAX(0, padding - text.count()); + + return textBuffer; + } + + inline void TextBuffer::flush(std::ostream& stream) { + stream.write(&buffer[0], offset); + + clear(); + } + + inline void TextBuffer::clear() { + offset = 0; + } +} \ No newline at end of file diff --git a/scroll/base.hpp b/scroll/base.hpp index 31607de..91a4134 100644 --- a/scroll/base.hpp +++ b/scroll/base.hpp @@ -1,15 +1,15 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once #include +#include #include #include #include #include #include -#include #ifndef ATL_MODULE_BASE #include "Base/Base/Base.hpp" @@ -19,7 +19,7 @@ #include #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING - #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 + #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 #endif #endif @@ -29,10 +29,19 @@ namespace scroll { using atl::uint32; using atl::uint64; + using atl::sint8; + using atl::sint16; + using atl::sint32; + using atl::sint64; + using atl::char8; + using atl::float32; + using atl::float64; + using atl::sequence; using atl::view; using atl::copy; + using atl::pointer_cast; } \ No newline at end of file diff --git a/scroll/scroll.hpp b/scroll/scroll.hpp index 3e8933a..6490257 100644 --- a/scroll/scroll.hpp +++ b/scroll/scroll.hpp @@ -1,5 +1,5 @@ -// Copyright 2025 Atalante. -// Licensed under MIT. +// Copyright 2025 Atalante Studio. +// Distributed under the MIT License. #pragma once