Merge remote-tracking branch 'origin'
commit
685753b217
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
include(cmake/color.cmake)
|
||||
set(BLT_VERSION 3.0.6)
|
||||
set(BLT_VERSION 5.2.34)
|
||||
|
||||
set(BLT_TARGET BLT)
|
||||
|
||||
|
@ -19,6 +19,7 @@ option(BUILD_PROFILING "Build the BLT profiler extension" ON)
|
|||
option(BUILD_FS "Build the BLT FS utilities including the NBT + eNBT extension" ON)
|
||||
option(BUILD_PARSE "Build the BLT parsers" ON)
|
||||
option(BUILD_FORMAT "Build the BLT formatters" ON)
|
||||
option(BUILD_LOGGING "Build the BLT logging utilities" ON)
|
||||
|
||||
option(BUILD_TESTS "Build the BLT test set" OFF)
|
||||
|
||||
|
@ -88,8 +89,14 @@ if (${BUILD_FORMAT})
|
|||
message(STATUS "Building ${Yellow}format${ColourReset} cxx files")
|
||||
file(GLOB_RECURSE FORMAT_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/format/*.cpp")
|
||||
else ()
|
||||
set(FORMAT_FILES ""
|
||||
include/blt/std/iterator.h)
|
||||
set(FORMAT_FILES "")
|
||||
endif ()
|
||||
|
||||
if (${BUILD_LOGGING})
|
||||
message(STATUS "Building ${Yellow}logging${ColourReset} cxx files")
|
||||
file(GLOB_RECURSE LOGGING_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/logging/*.cpp")
|
||||
else ()
|
||||
set(LOGGING_FILES "")
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
|
||||
|
@ -111,7 +118,7 @@ endif ()
|
|||
include_directories(include/)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/config/)
|
||||
|
||||
add_library(${BLT_TARGET} ${STD_FILES} ${PROFILING_FILES} ${FS_FILES} ${PARSE_FILES} ${FORMAT_FILES})
|
||||
add_library(${BLT_TARGET} ${STD_FILES} ${PROFILING_FILES} ${FS_FILES} ${PARSE_FILES} ${FORMAT_FILES} ${LOGGING_FILES})
|
||||
|
||||
string(REPLACE "+" "\\+" escaped_source ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
string(APPEND escaped_source "/src/blt/.*/")
|
||||
|
@ -166,8 +173,9 @@ install(TARGETS ${BLT_TARGET}
|
|||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
macro(blt_add_project name source type)
|
||||
macro(blt_add_test name source type)
|
||||
|
||||
message("Adding project ${name} of type ${type}" DEBUG)
|
||||
project(${name}-${type})
|
||||
|
||||
add_executable(${name}-${type} ${source})
|
||||
|
@ -178,8 +186,10 @@ macro(blt_add_project name source type)
|
|||
|
||||
target_link_libraries(${name}-${type} PRIVATE BLT)
|
||||
|
||||
target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
endif()
|
||||
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
|
||||
|
||||
if (${TRACK_ALLOCATIONS})
|
||||
|
@ -212,7 +222,9 @@ endmacro()
|
|||
if (${BUILD_TESTS})
|
||||
message("Building tests for version ${BLT_VERSION}")
|
||||
|
||||
blt_add_project(blt-iterator tests/iterator_tests.cpp test)
|
||||
blt_add_test(blt_iterator tests/iterator_tests.cpp test)
|
||||
blt_add_test(blt_argparse tests/argparse_tests.cpp test)
|
||||
blt_add_test(blt_logging tests/logger_tests.cpp test)
|
||||
|
||||
message("Built tests")
|
||||
endif ()
|
||||
|
|
56
README.md
56
README.md
|
@ -1,24 +1,50 @@
|
|||
# **BLT v0.20**
|
||||
# **BLT v5.1**
|
||||
A C++17 common utilities library to make thing easy!
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# ***Features***
|
||||
- ## blt/fs
|
||||
- ### loader.h
|
||||
- std::string blt::fs::getFile(std::string_view path)
|
||||
- Gets the entire file as a string.
|
||||
- std::vector\<std::string> blt::fs::getLinesFromFile(std::string_view path)
|
||||
- Gets the entire file as a string, then splits on the new line character. Then returns a vector of those lines
|
||||
- std::vector\<std::string> blt::fs::recursiveInclude(std::string_view path, std::string include_header, std::vector<include_guard> guards);
|
||||
- Recursively include in order based on the include string (include_header) marked by the include guards
|
||||
- Defaults to C/C++/GLSL preprocessor style (Was designed for GLSL)
|
||||
- std::string blt::fs::loadBrainFuckFile(const std::string& path)
|
||||
- Load a brainfuck file recursively, uses ~ to mark the include path
|
||||
- ### nbt.h
|
||||
- probably needs to be remade (TODO)
|
||||
# Features
|
||||
|
||||
## BLT Format
|
||||
|
||||
This module provides general string formatting utilities. *Note: this folder contains mostly outdated library files and will be updated in the future.*
|
||||
|
||||
### Files
|
||||
|
||||
- **boxing.h**
|
||||
Simple utility for drawing boxes around blocks of text.
|
||||
|
||||
- **format.h**
|
||||
Legacy library file containing various utilities for creating formatted output. Also includes methods for writing Java UTF8 strings.
|
||||
|
||||
## BLT Filesystem
|
||||
This module provides helper classes for filesystem objects. It seeks to offer an interface that is simpler than the one provided by the standard library.
|
||||
Specifically, the number of functions required to implement is significantly lower,
|
||||
and the interface is generally cleaner. Eventually, this module aims to support various file formats,
|
||||
such as Minecraft's NBT system. Currently, there is an existing NBT file, but it was written when I was first learning C++.
|
||||
|
||||
### Files
|
||||
|
||||
- **filesystem.h**
|
||||
This is the base file which includes all other files. You should use the other options as this can be a heavy file to include
|
||||
|
||||
- **path_helper.h**
|
||||
This file provides functions for interfacing with paths. Specifically, as of this moment it only provides an interface for getting the base name of a file path.
|
||||
|
||||
- **loader.h**
|
||||
- `std::string blt::fs::getFile(std::string_view path)`
|
||||
- Gets the entire file as a string.
|
||||
- `std::vector\<std::string> blt::fs::getLinesFromFile(std::string_view path)`
|
||||
- Gets the entire file as a string, then splits on the new line character. Then returns a vector of those lines
|
||||
- `std::vector\<std::string> blt::fs::recursiveInclude(std::string_view path, std::string include_header, std::vector<include_guard> guards)`
|
||||
- Recursively include in order based on the include string (include_header) marked by the include guards
|
||||
- Defaults to C/C++/GLSL preprocessor style (Was designed for GLSL)
|
||||
- `std::string blt::fs::loadBrainFuckFile(const std::string& path)`
|
||||
- Load a brainfuck file recursively, uses ~ to mark the include path
|
||||
- ### nbt.h
|
||||
- probably needs to be remade (TODO)
|
||||
- ## blt/math
|
||||
- ### averages.h
|
||||
- blt::averagizer_o_matic
|
||||
|
|
|
@ -23,7 +23,9 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|||
message(STATUS "GCC libs: ${Green}stdc++fs${ColourReset}")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always)
|
||||
target_link_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always)
|
||||
target_link_options(${PROJECT_NAME} PUBLIC -rdynamic)
|
||||
if (NOT WIN32)
|
||||
target_link_options(${PROJECT_NAME} PUBLIC -rdynamic)
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC stdc++fs)
|
||||
include(GNUInstallDirs)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import subprocess
|
||||
|
||||
#---------------------------------------
|
||||
# CONFIG
|
||||
#---------------------------------------
|
||||
|
||||
VERSION_BEGIN_STR = "set(BLT_VERSION "
|
||||
VERSION_END_STR = ")"
|
||||
|
||||
#---------------------------------------
|
||||
# DO NOT TOUCH
|
||||
#---------------------------------------
|
||||
|
||||
type = input("What kind of commit is this ((M)ajor, (m)inor, (p)atch)? ")
|
||||
|
||||
def load_cmake():
|
||||
cmake_file = open("CMakeLists.txt", 'r')
|
||||
cmake_text = cmake_file.read()
|
||||
cmake_file.close()
|
||||
return cmake_text
|
||||
|
||||
def write_cmake(cmake_text):
|
||||
cmake_file = open("CMakeLists.txt", 'w')
|
||||
cmake_file.write(cmake_text)
|
||||
cmake_file.close()
|
||||
|
||||
def get_version(cmake_text):
|
||||
begin = cmake_text.find(VERSION_BEGIN_STR) + len(find_text)
|
||||
end = cmake_text.find(VERSION_END_STR, begin)
|
||||
return (cmake_text[begin:end], begin, end)
|
||||
|
||||
def split_version(cmake_text):
|
||||
version, begin, end = get_version(cmake_text)
|
||||
version_parts = version.split('.')
|
||||
return (version_parts, begin, end)
|
||||
|
||||
def recombine(cmake_text, version_parts, begin, end):
|
||||
constructed_version = version_parts[0] + '.' + version_parts[1] + '.' + version_parts[2]
|
||||
constructed_text_begin = cmake_text[0:begin]
|
||||
constrcuted_text_end = cmake_text[end::]
|
||||
return constructed_text_begin + constructed_version + constrcuted_text_end
|
||||
|
||||
|
||||
def inc_major(cmake_text):
|
||||
version_parts, begin, end = split_version(cmake_text)
|
||||
version_parts[0] = str(int(version_parts[0]) + 1)
|
||||
return recombine(cmake_text, version_parts, begin, end)
|
||||
|
||||
def inc_minor(cmake_text):
|
||||
version_parts, begin, end = split_version(cmake_text)
|
||||
version_parts[1] = str(int(version_parts[1]) + 1)
|
||||
return recombine(cmake_text, version_parts, begin, end)
|
||||
|
||||
def inc_patch(cmake_text):
|
||||
version_parts, begin, end = split_version(cmake_text)
|
||||
version_parts[2] = str(int(version_parts[2]) + 1)
|
||||
return recombine(cmake_text, version_parts, begin, end)
|
||||
|
||||
if type.startswith('M'):
|
||||
print("Selected major")
|
||||
write_cmake(inc_major(load_cmake()))
|
||||
elif type.startswith('m'):
|
||||
print("Selected minor")
|
||||
write_cmake(inc_minor(load_cmake()))
|
||||
elif type.startswith('p') or type.startswith('P') or len(type) == 0:
|
||||
print("Selected patch")
|
||||
write_cmake(inc_patch(load_cmake()))
|
||||
|
||||
#subprocess.call("./py_commit_helper.sh")
|
||||
subprocess.call("git", "add", "*")
|
||||
subprocess.call("git", "commit")
|
||||
subprocess.call("sh -e 'git remote | xargs -L1 git push --all'")
|
|
@ -51,4 +51,12 @@
|
|||
#error Filesystem ops not supported!\
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
||||
#define BLT_OS_WINDOWS
|
||||
#elif defined(__linux__) || defined(__unix__)
|
||||
#define BLT_OS_LINUX
|
||||
#else
|
||||
#define BLT_OS_UNKNOWN
|
||||
#endif
|
||||
|
||||
#endif //BLT_COMPATIBILITY_H
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#define BLT_BOXING_H
|
||||
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <string>
|
||||
|
||||
namespace blt
|
||||
|
@ -84,25 +84,6 @@ namespace blt
|
|||
Logger& logger;
|
||||
};
|
||||
|
||||
template<>
|
||||
class log_box_t<blt::logging::logger> : detail::log_box_base_t
|
||||
{
|
||||
public:
|
||||
log_box_t(blt::logging::logger logger, std::string_view title, blt::size_t padding = 0): detail::log_box_base_t(title, padding), logger(logger)
|
||||
{
|
||||
make_full_title(logger);
|
||||
logger << '\n';
|
||||
}
|
||||
|
||||
~log_box_t()
|
||||
{
|
||||
make_full_width_line(logger);
|
||||
logger << '\n';
|
||||
}
|
||||
private:
|
||||
blt::logging::logger logger;
|
||||
};
|
||||
|
||||
template<typename Logger>
|
||||
log_box_t(Logger&& logger, std::string_view, blt::size_t) -> log_box_t<Logger>;
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_FS_BOUNDED_WRITER_H
|
||||
#define BLT_FS_BOUNDED_WRITER_H
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <blt/fs/fwddecl.h>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
inline auto basic_naming_function = [](const size_t invocation, std::string prefix) {
|
||||
prefix += '-';
|
||||
prefix += std::to_string(invocation);
|
||||
prefix += ".txt";
|
||||
return prefix;
|
||||
};
|
||||
|
||||
using naming_function_t = std::function<std::string(size_t, std::string)>;
|
||||
|
||||
class fwriter_t : public writer_t
|
||||
{
|
||||
public:
|
||||
explicit fwriter_t(const std::string& name, std::string mode = "ab"): m_mode(std::move(mode))
|
||||
{
|
||||
fwriter_t::newfile(name);
|
||||
}
|
||||
|
||||
// create a writer without creating a new file. Writing without calling newfile is UB
|
||||
explicit fwriter_t(std::string mode = "ab"): m_mode(std::move(mode))
|
||||
{}
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
virtual void newfile(const std::string& new_name);
|
||||
|
||||
void flush() override;
|
||||
|
||||
protected:
|
||||
std::string m_mode;
|
||||
FILE* m_file = nullptr;
|
||||
};
|
||||
|
||||
// ReSharper disable once CppClassCanBeFinal
|
||||
class buffered_writer : public fwriter_t
|
||||
{
|
||||
public:
|
||||
explicit buffered_writer(const std::string& name, size_t buffer_size = 1024 * 128);
|
||||
explicit buffered_writer(size_t buffer_size = 1024 * 128);
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
void newfile(const std::string& new_name) override;
|
||||
|
||||
protected:
|
||||
size_t m_current_pos = 0;
|
||||
std::vector<char> m_buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a bounded writer where after a specified number of bytes a new file will be opened and written to instead.
|
||||
*/
|
||||
// ReSharper disable once CppClassCanBeFinal
|
||||
class bounded_writer : public fwriter_t
|
||||
{
|
||||
public:
|
||||
explicit bounded_writer(fwriter_t& writer, std::optional<std::string> base_name, size_t max_size = 1024 * 1024 * 10,
|
||||
naming_function_t naming_function = basic_naming_function);
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
void newfile(const std::string& new_name) override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
private:
|
||||
fwriter_t* m_writer;
|
||||
std::optional<std::string> m_base_name;
|
||||
size_t m_current_invocation = 0;
|
||||
size_t m_max_size;
|
||||
size_t m_currently_written = 0;
|
||||
// inputs: current invocation, then basename string
|
||||
// returns: name of the file to write to
|
||||
naming_function_t m_naming_function;
|
||||
};
|
||||
|
||||
struct time_t
|
||||
{
|
||||
i32 year = 0, month = 0, day = 1, hour = -1;
|
||||
|
||||
time_t(const i32 year, const i32 month, const i32 day, const i32 hour) : year{year}, month{month}, day{day}, hour{hour}
|
||||
{}
|
||||
|
||||
time_t(const i32 year, const i32 month, const i32 day) : year{year}, month{month}, day{day}
|
||||
{}
|
||||
|
||||
time_t() = default;
|
||||
};
|
||||
|
||||
// ReSharper disable once CppClassCanBeFinal
|
||||
class rotating_writer : public fwriter_t
|
||||
{
|
||||
public:
|
||||
rotating_writer(fwriter_t& writer, time_t period);
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
void newfile(const std::string& new_name) override;
|
||||
|
||||
void newfile();
|
||||
|
||||
void check_for_time();
|
||||
|
||||
static time_t get_current_time();
|
||||
|
||||
private:
|
||||
fwriter_t* m_writer;
|
||||
time_t m_period;
|
||||
time_t m_last_time;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_FS_BOUNDED_WRITER_H
|
|
@ -19,145 +19,15 @@
|
|||
#ifndef BLT_FILESYSTEM_H
|
||||
#define BLT_FILESYSTEM_H
|
||||
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iosfwd>
|
||||
#include <sstream>
|
||||
#include <blt/fs/fwddecl.h>
|
||||
#include <blt/fs/file_writers.h>
|
||||
#include <blt/fs/stream_wrappers.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
/**
|
||||
* A simple interface which provides a way of reading the next block of data from a resource.
|
||||
* The interface provides a single function "read" which will read a specified number of bytes into the buffer.
|
||||
* The implementation for this could be fstreams, zlib deflate, or any method filesystem access.
|
||||
* Reading of a large number of bytes ( > block size) is guaranteed to not significantly increase the read time and will likely result in a
|
||||
* direct passthrough to the underlying system. Small reads will be buffered, hence the name "block" reader.
|
||||
*/
|
||||
class block_reader
|
||||
{
|
||||
protected:
|
||||
// 32768 block size seems the fastest on my system
|
||||
unsigned long m_bufferSize;
|
||||
public:
|
||||
explicit block_reader(size_t bufferSize): m_bufferSize(bufferSize)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Reads bytes from the internal filesystem implementation
|
||||
* @param buffer buffer to copy the read bytes into
|
||||
* @param bytes number of bytes to read
|
||||
* @return status code. non-zero return codes indicates a failure has occurred.
|
||||
*/
|
||||
virtual int read(char* buffer, size_t bytes) = 0;
|
||||
|
||||
virtual size_t gcount() = 0;
|
||||
|
||||
virtual char get()
|
||||
{
|
||||
char c[1];
|
||||
read(c, 1);
|
||||
return c[0];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A buffered block writer without a definite backend implementation. Exactly the same as a block_reader but for writing to the filesystem.
|
||||
*/
|
||||
class block_writer
|
||||
{
|
||||
protected:
|
||||
unsigned long m_bufferSize;
|
||||
public:
|
||||
explicit block_writer(unsigned long bufferSize): m_bufferSize(bufferSize)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Writes the bytes to the filesystem backend implementation
|
||||
* @param buffer bytes to write
|
||||
* @param bytes number of bytes to write
|
||||
* @return non-zero code if failure
|
||||
*/
|
||||
virtual int write(char* buffer, size_t bytes) = 0;
|
||||
|
||||
virtual int put(char c)
|
||||
{
|
||||
char a[1];
|
||||
a[0] = c;
|
||||
return write(a, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the internal buffer is written to the filesystem.
|
||||
*/
|
||||
virtual void flush() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* fstream implementation of the block reader.
|
||||
*/
|
||||
class fstream_block_reader : public block_reader
|
||||
{
|
||||
private:
|
||||
std::fstream& m_stream;
|
||||
char* m_buffer = nullptr;
|
||||
size_t readIndex = 0;
|
||||
public:
|
||||
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize = 131072);
|
||||
|
||||
explicit fstream_block_reader(fstream_block_reader& copy) = delete;
|
||||
|
||||
explicit fstream_block_reader(fstream_block_reader&& move) = delete;
|
||||
|
||||
fstream_block_reader& operator=(const fstream_block_reader& copy) = delete;
|
||||
|
||||
fstream_block_reader& operator=(const fstream_block_reader&& move) = delete;
|
||||
|
||||
int read(char* buffer, size_t bytes) override;
|
||||
|
||||
size_t gcount() final
|
||||
{
|
||||
return m_stream.gcount();
|
||||
}
|
||||
|
||||
~fstream_block_reader()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
class fstream_block_writer : public block_writer
|
||||
{
|
||||
private:
|
||||
std::fstream& m_stream;
|
||||
char* m_buffer;
|
||||
size_t writeIndex = 0;
|
||||
|
||||
void flush_internal();
|
||||
|
||||
public:
|
||||
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize = 131072):
|
||||
block_writer(bufferSize), m_stream(stream), m_buffer(new char[bufferSize])
|
||||
{}
|
||||
|
||||
explicit fstream_block_writer(fstream_block_writer& copy) = delete;
|
||||
|
||||
explicit fstream_block_writer(fstream_block_writer&& move) = delete;
|
||||
|
||||
fstream_block_writer& operator=(const fstream_block_writer& copy) = delete;
|
||||
|
||||
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
|
||||
|
||||
int write(char* buffer, size_t bytes) override;
|
||||
|
||||
inline void flush() override
|
||||
{
|
||||
flush_internal();
|
||||
}
|
||||
|
||||
~fstream_block_writer()
|
||||
{
|
||||
flush_internal();
|
||||
delete[] m_buffer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_FILESYSTEM_H
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_FS_FWDDECL_H
|
||||
#define BLT_FS_FWDDECL_H
|
||||
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
/**
|
||||
* A simple interface which provides a way of reading the next block of data from a resource. This is designed to replace the overly complex
|
||||
* std::ostream
|
||||
*/
|
||||
class reader_t
|
||||
{
|
||||
public:
|
||||
virtual ~reader_t() = default;
|
||||
explicit reader_t() = default;
|
||||
|
||||
reader_t(const reader_t&) = delete;
|
||||
reader_t& operator=(const reader_t&) = delete;
|
||||
|
||||
/**
|
||||
* Reads bytes from the internal filesystem implementation
|
||||
* @param buffer buffer to copy the read bytes into
|
||||
* @param bytes number of bytes to read
|
||||
* @return number of bytes read, or negative value if error. Errors are not required and can just return 0
|
||||
*/
|
||||
virtual i64 read(char* buffer, size_t bytes) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A block writer without a definite backend implementation. Exactly the same as a block_reader but for writing to the filesystem.
|
||||
* this is designed to replace the overly complex std::istream
|
||||
*/
|
||||
class writer_t
|
||||
{
|
||||
public:
|
||||
virtual ~writer_t() = default;
|
||||
explicit writer_t() = default;
|
||||
|
||||
writer_t(const writer_t&) = delete;
|
||||
writer_t& operator=(const writer_t&) = delete;
|
||||
|
||||
/**
|
||||
* Writes the bytes to the filesystem backend implementation
|
||||
* @param buffer bytes to write
|
||||
* @param bytes number of bytes to write
|
||||
* @return number of bytes, or negative value if error. Zero is also a valid return, not indicating error in itself but can be the result of one.
|
||||
*/
|
||||
virtual i64 write(const char* buffer, size_t bytes) = 0;
|
||||
|
||||
/**
|
||||
* Optional flush command which syncs the underlying objects
|
||||
*/
|
||||
virtual void flush()
|
||||
{};
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_FS_FWDDECL_H
|
|
@ -13,7 +13,7 @@
|
|||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <blt/std/string.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace blt::fs
|
||||
|
|
|
@ -15,26 +15,26 @@
|
|||
|
||||
#include "blt/format/format.h"
|
||||
#include "blt/fs/filesystem.h"
|
||||
#include "blt/std/logging.h"
|
||||
#include "blt/logging/logging.h"
|
||||
#include "blt/std/memory.h"
|
||||
|
||||
#include <blt/std/hashmap.h>
|
||||
|
||||
namespace blt::nbt {
|
||||
|
||||
void writeUTF8String(blt::fs::block_writer& stream, const std::string& str);
|
||||
void writeUTF8String(blt::fs::writer_t& stream, const std::string& str);
|
||||
|
||||
std::string readUTF8String(blt::fs::block_reader& stream);
|
||||
std::string readUTF8String(blt::fs::reader_t& stream);
|
||||
|
||||
template<typename T>
|
||||
inline static void writeData(blt::fs::block_writer& out, const T& d){
|
||||
inline static void writeData(blt::fs::writer_t& out, const T& d){
|
||||
char data[sizeof(T)];
|
||||
mem::toBytes(d, data);
|
||||
out.write(data, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static void readData(blt::fs::block_reader& in, T& d) {
|
||||
inline static void readData(blt::fs::reader_t& in, T& d) {
|
||||
char data[sizeof(T)];
|
||||
in.read(data, sizeof(T));
|
||||
mem::fromBytes(data, &d);
|
||||
|
@ -63,12 +63,12 @@ namespace blt::nbt {
|
|||
public:
|
||||
explicit tag_t(nbt_tag type): type(type) {};
|
||||
explicit tag_t(nbt_tag type, std::string name): type(type), name(std::move(name)) {}
|
||||
virtual void writePayload(blt::fs::block_writer& out) = 0;
|
||||
virtual void readPayload(blt::fs::block_reader& in) = 0;
|
||||
void writeName(blt::fs::block_writer& out) {
|
||||
virtual void writePayload(blt::fs::writer_t& out) = 0;
|
||||
virtual void readPayload(blt::fs::reader_t& in) = 0;
|
||||
void writeName(blt::fs::writer_t& out) {
|
||||
writeUTF8String(out, name);
|
||||
}
|
||||
void readName(blt::fs::block_reader& in) {
|
||||
void readName(blt::fs::reader_t& in) {
|
||||
name = readUTF8String(in);
|
||||
}
|
||||
[[nodiscard]] inline nbt_tag getType() const {
|
||||
|
@ -87,11 +87,11 @@ namespace blt::nbt {
|
|||
public:
|
||||
explicit tag(nbt_tag type): tag_t(type) {}
|
||||
tag(nbt_tag type, std::string name, T t): tag_t(type, std::move(name)), t(std::move(t)) {}
|
||||
void writePayload(blt::fs::block_writer& out) override {
|
||||
void writePayload(blt::fs::writer_t& out) override {
|
||||
if constexpr(std::is_arithmetic<T>::value)
|
||||
writeData(out, t);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) override {
|
||||
void readPayload(blt::fs::reader_t& in) override {
|
||||
if constexpr(std::is_arithmetic<T>::value)
|
||||
readData(in, t);
|
||||
}
|
||||
|
@ -102,9 +102,9 @@ namespace blt::nbt {
|
|||
|
||||
class tag_end : public tag<char> {
|
||||
public:
|
||||
void writePayload(blt::fs::block_writer&) final {}
|
||||
void writePayload(blt::fs::writer_t&) final {}
|
||||
// nothing to read
|
||||
void readPayload(blt::fs::block_reader&) final {}
|
||||
void readPayload(blt::fs::reader_t&) final {}
|
||||
};
|
||||
|
||||
class tag_byte : public tag<int8_t> {
|
||||
|
@ -147,13 +147,13 @@ namespace blt::nbt {
|
|||
public:
|
||||
tag_byte_array(): tag(nbt_tag::BYTE_ARRAY) {}
|
||||
tag_byte_array(const std::string& name, const std::vector<int8_t>& v): tag(nbt_tag::BYTE_ARRAY, name, v) {}
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
auto length = (int32_t) t.size();
|
||||
writeData(out, length);
|
||||
// TODO on the writer (remove need for cast + more std::fstream functions)
|
||||
out.write(reinterpret_cast<char*>(t.data()), length);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
int32_t length;
|
||||
readData(in, length);
|
||||
t.reserve(length);
|
||||
|
@ -165,10 +165,10 @@ namespace blt::nbt {
|
|||
public:
|
||||
tag_string(): tag(nbt_tag::STRING) {}
|
||||
tag_string(const std::string& name, const std::string& s): tag(nbt_tag::STRING, name, s) {}
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
writeUTF8String(out, t);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
t = readUTF8String(in);
|
||||
}
|
||||
};
|
||||
|
@ -177,13 +177,13 @@ namespace blt::nbt {
|
|||
public:
|
||||
tag_int_array(): tag(nbt_tag::INT_ARRAY) {}
|
||||
tag_int_array(const std::string& name, const std::vector<int32_t>& v): tag(nbt_tag::INT_ARRAY, name, v) {}
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
auto length = (int32_t) t.size();
|
||||
writeData(out, length);
|
||||
for (int i = 0; i < length; i++)
|
||||
writeData(out, t[i]);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
int32_t length;
|
||||
readData(in, length);
|
||||
t.reserve(length);
|
||||
|
@ -196,13 +196,13 @@ namespace blt::nbt {
|
|||
public:
|
||||
tag_long_array(): tag(nbt_tag::LONG_ARRAY) {}
|
||||
tag_long_array(const std::string& name, const std::vector<int64_t>& v): tag(nbt_tag::LONG_ARRAY, name, v) {}
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
auto length = (int32_t) t.size();
|
||||
writeData(out, length);
|
||||
for (int i = 0; i < length; i++)
|
||||
writeData(out, t[i]);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
int32_t length;
|
||||
readData(in, length);
|
||||
t.reserve(length);
|
||||
|
@ -262,7 +262,7 @@ namespace blt::nbt {
|
|||
public:
|
||||
tag_list(): tag(nbt_tag::LIST) {}
|
||||
tag_list(const std::string& name, const std::vector<tag_t*>& v): tag(nbt_tag::LIST, name, v) {}
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
if (t.empty())
|
||||
writeData(out, (char)nbt_tag::END);
|
||||
else
|
||||
|
@ -273,7 +273,7 @@ namespace blt::nbt {
|
|||
v->writePayload(out);
|
||||
}
|
||||
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
char id;
|
||||
int32_t length;
|
||||
readData(in, id);
|
||||
|
@ -305,7 +305,7 @@ namespace blt::nbt {
|
|||
auto& tag = t[i];
|
||||
T t;
|
||||
if (tag->getType() != t.getType()) {
|
||||
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||
BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
|
||||
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||
}
|
||||
return dynamic_cast<T*>(tag);
|
||||
|
@ -342,7 +342,7 @@ namespace blt::nbt {
|
|||
auto& tag = t[name];
|
||||
T t;
|
||||
if (tag->getType() != t.getType()) {
|
||||
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||
BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
|
||||
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||
}
|
||||
return dynamic_cast<T*>(tag);
|
||||
|
@ -356,18 +356,20 @@ namespace blt::nbt {
|
|||
t[tag->getName()] = tag;
|
||||
}
|
||||
|
||||
void writePayload(blt::fs::block_writer& out) final {
|
||||
void writePayload(blt::fs::writer_t& out) final {
|
||||
for (const auto& v : t){
|
||||
auto tag = v.second;
|
||||
out.put((char) tag->getType());
|
||||
auto c = (char) tag->getType();
|
||||
out.write(&c, 1);
|
||||
tag->writeName(out);
|
||||
tag->writePayload(out);
|
||||
}
|
||||
out.put('\0');
|
||||
const char c = '\0';
|
||||
out.write(&c, 1);
|
||||
}
|
||||
void readPayload(blt::fs::block_reader& in) final {
|
||||
void readPayload(blt::fs::reader_t& in) final {
|
||||
char type;
|
||||
while ((type = in.get()) != (char)nbt_tag::END){
|
||||
while ((in.read(&type, 1), type) != (char)nbt_tag::END){
|
||||
auto* v = _internal_::toType(type);
|
||||
v->readName(in);
|
||||
v->readPayload(in);
|
||||
|
@ -390,10 +392,10 @@ namespace blt::nbt {
|
|||
|
||||
class NBTReader {
|
||||
private:
|
||||
blt::fs::block_reader& reader;
|
||||
blt::fs::reader_t& reader;
|
||||
tag_compound* root = nullptr;
|
||||
public:
|
||||
explicit NBTReader(blt::fs::block_reader& reader): reader(reader) {}
|
||||
explicit NBTReader(blt::fs::reader_t& reader): reader(reader) {}
|
||||
|
||||
void read();
|
||||
|
||||
|
@ -407,7 +409,7 @@ namespace blt::nbt {
|
|||
auto& tag = root->get()[name];
|
||||
T t;
|
||||
if (tag->getType() != t.getType()) {
|
||||
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||
BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
|
||||
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||
}
|
||||
return dynamic_cast<T*>(tag);
|
||||
|
@ -419,9 +421,9 @@ namespace blt::nbt {
|
|||
|
||||
class NBTWriter {
|
||||
private:
|
||||
blt::fs::block_writer& writer;
|
||||
blt::fs::writer_t& writer;
|
||||
public:
|
||||
explicit NBTWriter(blt::fs::block_writer& writer): writer(writer) {}
|
||||
explicit NBTWriter(blt::fs::writer_t& writer): writer(writer) {}
|
||||
/**
|
||||
* Write a compound tag and then DELETES the tag. If you don't wish for the memory to be freed, please use the reference version!
|
||||
* @param root root NBT tag to write, this function assumes ownership of this pointer.
|
||||
|
@ -431,7 +433,8 @@ namespace blt::nbt {
|
|||
delete root;
|
||||
}
|
||||
void write(tag_compound& root){
|
||||
writer.put((char)nbt_tag::COMPOUND);
|
||||
auto c = (char)nbt_tag::COMPOUND;
|
||||
writer.write(&c, 1);
|
||||
root.writeName(writer);
|
||||
root.writePayload(writer);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_FS_PATH_HELPER_H
|
||||
#define BLT_FS_PATH_HELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
|
||||
std::string base_name(const std::string& str);
|
||||
std::string_view base_name_sv(std::string_view str);
|
||||
|
||||
std::string filename(const std::string& str);
|
||||
std::string_view filename_sv(std::string_view str);
|
||||
|
||||
std::string extension(const std::string& str);
|
||||
std::string_view extension_sv(std::string_view str);
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_FS_PATH_HELPER_H
|
|
@ -0,0 +1,143 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_FS_STREAM_WRAPPERS_H
|
||||
#define BLT_FS_STREAM_WRAPPERS_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include <sstream>
|
||||
#include <blt/fs/fwddecl.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
/**
|
||||
* reader_t wrapper for fstream
|
||||
*/
|
||||
class fstream_reader_t final : public reader_t
|
||||
{
|
||||
public:
|
||||
explicit fstream_reader_t(std::istream& stream);
|
||||
|
||||
explicit fstream_reader_t(fstream_reader_t& copy) = delete;
|
||||
|
||||
fstream_reader_t& operator=(const fstream_reader_t& copy) = delete;
|
||||
|
||||
i64 read(char* buffer, size_t bytes) override;
|
||||
|
||||
private:
|
||||
std::istream* m_stream;
|
||||
};
|
||||
|
||||
class fstream_writer_t final : public writer_t
|
||||
{
|
||||
public:
|
||||
explicit fstream_writer_t(std::ostream& stream);
|
||||
|
||||
explicit fstream_writer_t(fstream_writer_t& copy) = delete;
|
||||
|
||||
fstream_writer_t& operator=(const fstream_writer_t& copy) = delete;
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
virtual ~fstream_writer_t() override // NOLINT
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream* m_stream;
|
||||
};
|
||||
|
||||
class reader_wrapper_t
|
||||
{
|
||||
public:
|
||||
explicit reader_wrapper_t(reader_t& reader): m_reader(&reader)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void read(T& out)
|
||||
{
|
||||
if (!m_reader->read(reinterpret_cast<char*>(&out), sizeof(T)))
|
||||
throw std::runtime_error("Failed to read from reader");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend reader_wrapper_t& operator>>(reader_wrapper_t& reader, T& t)
|
||||
{
|
||||
reader.read(t);
|
||||
return reader;
|
||||
}
|
||||
|
||||
private:
|
||||
reader_t* m_reader;
|
||||
};
|
||||
|
||||
class writer_wrapper_t
|
||||
{
|
||||
public:
|
||||
explicit writer_wrapper_t(writer_t& writer): m_writer(&writer)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& t)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
m_writer->write(reinterpret_cast<const char*>(&t), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend writer_wrapper_t& operator<<(writer_wrapper_t& writer, const T& t)
|
||||
{
|
||||
writer.write(t);
|
||||
return writer;
|
||||
}
|
||||
|
||||
private:
|
||||
writer_t* m_writer;
|
||||
};
|
||||
|
||||
class writer_string_wrapper_t
|
||||
{
|
||||
public:
|
||||
explicit writer_string_wrapper_t(writer_t& writer): m_writer(&writer)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& t)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << t;
|
||||
const auto str = ss.str();
|
||||
m_writer->write(str.data(), str.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend writer_string_wrapper_t& operator<<(writer_string_wrapper_t& writer, const T& t)
|
||||
{
|
||||
writer.write(t);
|
||||
return writer;
|
||||
}
|
||||
|
||||
private:
|
||||
writer_t* m_writer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_FS_STREAM_WRAPPERS_H
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_FS_THREADED_WRITERS_H
|
||||
#define BLT_FS_THREADED_WRITERS_H
|
||||
|
||||
#include <mutex>
|
||||
#include <blt/fs/fwddecl.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
// ReSharper disable once CppClassCanBeFinal
|
||||
class concurrent_file_writer : public writer_t
|
||||
{
|
||||
public:
|
||||
explicit concurrent_file_writer(writer_t* writer);
|
||||
|
||||
i64 write(const char* buffer, size_t bytes) override;
|
||||
|
||||
void flush() override;
|
||||
private:
|
||||
writer_t* m_writer;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_FS_THREADED_WRITERS_H
|
|
@ -27,9 +27,42 @@
|
|||
#include <blt/meta/iterator.h>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <blt/meta/type_traits.h>
|
||||
|
||||
namespace blt::iterator
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<typename T>
|
||||
static auto forward_as_tuple(T&& t) -> decltype(auto)
|
||||
{
|
||||
using Decay = std::decay_t<T>;
|
||||
static_assert(!(meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>), "Tuple or pair passed to forward_as_tuple! Must not be a tuple!");
|
||||
if constexpr (std::is_lvalue_reference_v<T>)
|
||||
{
|
||||
return std::forward_as_tuple(std::forward<T>(t));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_tuple(std::forward<T>(t));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tuple>
|
||||
static auto ensure_tuple(Tuple&& tuple) -> decltype(auto)
|
||||
{
|
||||
using Decay = std::decay_t<Tuple>;
|
||||
if constexpr (meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>)
|
||||
{
|
||||
return std::forward<Tuple>(tuple);
|
||||
}
|
||||
else
|
||||
{
|
||||
return forward_as_tuple(std::forward<Tuple>(tuple));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
struct base_wrapper
|
||||
{
|
||||
|
@ -42,19 +75,21 @@ namespace blt::iterator
|
|||
|
||||
base_wrapper operator--(int)
|
||||
{
|
||||
static_assert(meta::is_bidirectional_or_better_category_v<typename Derived::iterator_category>, "Iterator must allow random access");
|
||||
static_assert(meta::is_bidirectional_or_better_category_v<typename Derived::iterator_category>,
|
||||
"Iterator must allow bidirectional access");
|
||||
auto tmp = *this;
|
||||
--*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
auto operator[](blt::ptrdiff_t n) const
|
||||
auto operator[](ptrdiff_t n) const
|
||||
{
|
||||
static_assert(meta::is_random_access_iterator_category_v<typename Derived::iterator_category>, "Iterator must allow random access");
|
||||
static_assert(meta::is_random_access_iterator_category_v<typename Derived::iterator_category>,
|
||||
"Iterator must allow bidirectional access");
|
||||
return *(*this + n);
|
||||
}
|
||||
|
||||
friend base_wrapper operator+(blt::ptrdiff_t n, const base_wrapper& a)
|
||||
friend base_wrapper operator+(ptrdiff_t n, const base_wrapper& a)
|
||||
{
|
||||
return a + n;
|
||||
}
|
||||
|
@ -95,9 +130,8 @@ namespace blt::iterator
|
|||
};
|
||||
|
||||
template <typename Iter, typename Derived, bool dereference = false>
|
||||
struct passthrough_wrapper : public base_wrapper<Derived>
|
||||
struct passthrough_wrapper : base_wrapper<Derived>
|
||||
{
|
||||
public:
|
||||
explicit passthrough_wrapper(Iter iter): iter(std::move(iter))
|
||||
{
|
||||
}
|
||||
|
@ -107,7 +141,7 @@ namespace blt::iterator
|
|||
return iter;
|
||||
}
|
||||
|
||||
friend blt::ptrdiff_t operator-(const passthrough_wrapper& a, const passthrough_wrapper& b)
|
||||
friend ptrdiff_t operator-(const passthrough_wrapper& a, const passthrough_wrapper& b)
|
||||
{
|
||||
return a.base() - b.base();
|
||||
}
|
||||
|
@ -117,7 +151,7 @@ namespace blt::iterator
|
|||
};
|
||||
|
||||
template <typename Iter, typename Derived>
|
||||
struct passthrough_wrapper<Iter, Derived, true> : public passthrough_wrapper<Iter, Derived>
|
||||
struct passthrough_wrapper<Iter, Derived, true> : passthrough_wrapper<Iter, Derived>
|
||||
{
|
||||
using passthrough_wrapper<Iter, Derived>::passthrough_wrapper;
|
||||
|
||||
|
@ -215,6 +249,54 @@ namespace blt::iterator
|
|||
Pred func;
|
||||
};
|
||||
|
||||
template <typename Iter>
|
||||
class const_wrapper : public deref_only_wrapper<Iter, const_wrapper<Iter>>
|
||||
{
|
||||
public:
|
||||
using ref_return = meta::deref_return_t<Iter>;
|
||||
|
||||
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
|
||||
using value_type = std::conditional_t<std::is_reference_v<ref_return>, std::remove_reference_t<ref_return>, ref_return>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
explicit const_wrapper(Iter iter): deref_only_wrapper<Iter, const_wrapper>(std::move(iter))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static auto make_const(T&& value) -> decltype(auto)
|
||||
{
|
||||
using Decay = std::decay_t<T>;
|
||||
if constexpr (std::is_lvalue_reference_v<T>)
|
||||
{
|
||||
return const_cast<const Decay&>(value);
|
||||
} else
|
||||
{
|
||||
return static_cast<const Decay>(value);
|
||||
}
|
||||
}
|
||||
|
||||
auto operator*() const
|
||||
{
|
||||
if constexpr (std::is_reference_v<ref_return>)
|
||||
{
|
||||
return const_cast<const value_type&>(*this->iter);
|
||||
} else if constexpr (meta::is_tuple_v<value_type> || meta::is_pair_v<value_type>)
|
||||
{
|
||||
return std::apply([](auto&&... args)
|
||||
{
|
||||
return std::tuple<decltype(make_const(std::forward<decltype(args)>(args)))...>{make_const(std::forward<decltype(args)>(args))...};
|
||||
}, *this->iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return *this->iter;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template <typename Derived>
|
||||
|
@ -382,6 +464,30 @@ namespace blt::iterator
|
|||
return enumerate_iterator_container{begin(), end(), static_cast<blt::size_t>(std::distance(begin(), end()))};
|
||||
}
|
||||
|
||||
auto flatten() const
|
||||
{
|
||||
return iterator_container<flatten_wrapper<IterBase, false>>{
|
||||
blt::iterator::flatten_wrapper<IterBase, false>{m_begin},
|
||||
blt::iterator::flatten_wrapper<IterBase, false>{m_end}
|
||||
};
|
||||
}
|
||||
|
||||
auto flatten_all() const
|
||||
{
|
||||
return iterator_container<flatten_wrapper<IterBase, true>>{
|
||||
blt::iterator::flatten_wrapper<IterBase, true>{m_begin},
|
||||
blt::iterator::flatten_wrapper<IterBase, true>{m_end}
|
||||
};
|
||||
}
|
||||
|
||||
auto as_const() const
|
||||
{
|
||||
return iterator_container<const_wrapper<IterBase>>{
|
||||
const_wrapper<IterBase>{m_begin},
|
||||
const_wrapper<IterBase>{m_end}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
auto map(Func func) const
|
||||
{
|
||||
|
|
|
@ -20,120 +20,112 @@
|
|||
#define BLT_ITERATOR_ENUMERATE_H
|
||||
|
||||
#include <blt/iterator/common.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
||||
namespace iterator
|
||||
{
|
||||
/**
|
||||
* struct which is returned by the enumerator.
|
||||
* @tparam T type to store.
|
||||
*/
|
||||
template<typename T>
|
||||
struct enumerate_item
|
||||
{
|
||||
blt::size_t index;
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename Iter>
|
||||
template <typename Iter>
|
||||
class enumerate_wrapper : public passthrough_wrapper<Iter, enumerate_wrapper<Iter>>
|
||||
{
|
||||
public:
|
||||
enumerate_wrapper(blt::size_t index, Iter iter): passthrough_wrapper<Iter, enumerate_wrapper<Iter>>(std::move(iter)), index(index)
|
||||
{}
|
||||
public:
|
||||
enumerate_wrapper(const size_t index, Iter iter): passthrough_wrapper<Iter, enumerate_wrapper<Iter>>(std::move(iter)), index(index)
|
||||
{
|
||||
}
|
||||
|
||||
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
|
||||
using value_type = enumerate_item<meta::deref_return_t<Iter>>;
|
||||
using difference_type = blt::ptrdiff_t;
|
||||
using pointer = value_type;
|
||||
using reference = value_type;
|
||||
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
|
||||
using value_type = enumerate_item<meta::deref_return_t<Iter>>;
|
||||
using difference_type = blt::ptrdiff_t;
|
||||
using pointer = value_type;
|
||||
using reference = value_type;
|
||||
|
||||
enumerate_item<meta::deref_return_t<Iter>> operator*() const
|
||||
{
|
||||
return {index, *this->iter};
|
||||
}
|
||||
enumerate_item<meta::deref_return_t<Iter>> operator*() const
|
||||
{
|
||||
return {index, *this->iter};
|
||||
}
|
||||
|
||||
enumerate_wrapper& operator++()
|
||||
{
|
||||
++index;
|
||||
++this->iter;
|
||||
return *this;
|
||||
}
|
||||
enumerate_wrapper& operator++()
|
||||
{
|
||||
++index;
|
||||
++this->iter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
enumerate_wrapper& operator--()
|
||||
{
|
||||
--index;
|
||||
--this->iter;
|
||||
return *this;
|
||||
}
|
||||
enumerate_wrapper& operator--()
|
||||
{
|
||||
--index;
|
||||
--this->iter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend enumerate_wrapper operator+(const enumerate_wrapper& a, blt::ptrdiff_t n)
|
||||
{
|
||||
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
|
||||
auto copy = a;
|
||||
copy.index += n;
|
||||
copy.iter = copy.iter + n;
|
||||
return copy;
|
||||
}
|
||||
friend enumerate_wrapper operator+(const enumerate_wrapper& a, blt::ptrdiff_t n)
|
||||
{
|
||||
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
|
||||
auto copy = a;
|
||||
copy.index += n;
|
||||
copy.iter = copy.iter + n;
|
||||
return copy;
|
||||
}
|
||||
|
||||
friend enumerate_wrapper operator-(const enumerate_wrapper& a, blt::ptrdiff_t n)
|
||||
{
|
||||
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
|
||||
auto copy = a;
|
||||
copy.index -= n;
|
||||
copy.iter = copy.iter - n;
|
||||
return copy;
|
||||
}
|
||||
friend enumerate_wrapper operator-(const enumerate_wrapper& a, blt::ptrdiff_t n)
|
||||
{
|
||||
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
|
||||
auto copy = a;
|
||||
copy.index -= n;
|
||||
copy.iter = copy.iter - n;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private:
|
||||
blt::size_t index;
|
||||
private:
|
||||
blt::size_t index;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
class enumerate_iterator_container : public iterator::iterator_container<iterator::enumerate_wrapper<Iter>>
|
||||
template <typename Iter>
|
||||
class enumerate_iterator_container : public blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>
|
||||
{
|
||||
public:
|
||||
using iterator::iterator_container<iterator::enumerate_wrapper<Iter>>::iterator_container;
|
||||
public:
|
||||
using blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>::iterator_container;
|
||||
|
||||
enumerate_iterator_container(Iter begin, Iter end, blt::size_t size):
|
||||
iterator::iterator_container<iterator::enumerate_wrapper<Iter>>(
|
||||
iterator::enumerate_wrapper<Iter>{0, std::move(begin)}, iterator::enumerate_wrapper<Iter>{size, std::move(end)})
|
||||
{}
|
||||
enumerate_iterator_container(Iter begin, Iter end, blt::size_t size):
|
||||
blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>(
|
||||
blt::iterator::enumerate_wrapper<Iter>{0, std::move(begin)}, blt::iterator::enumerate_wrapper<Iter>{size, std::move(end)})
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iter>
|
||||
template <typename Iter>
|
||||
enumerate_iterator_container(Iter, Iter, blt::size_t) -> enumerate_iterator_container<Iter>;
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
static inline auto enumerate(T& container)
|
||||
{
|
||||
return enumerate_iterator_container{container.begin(), container.end(),
|
||||
static_cast<blt::size_t>(std::distance(container.begin(), container.end()))};
|
||||
return enumerate_iterator_container{
|
||||
container.begin(), container.end(),
|
||||
static_cast<blt::size_t>(std::distance(container.begin(), container.end()))
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
static inline auto enumerate(const T& container)
|
||||
{
|
||||
return enumerate_iterator_container{container.begin(), container.end(),
|
||||
static_cast<blt::size_t>(std::distance(container.begin(), container.end()))};
|
||||
return enumerate_iterator_container{
|
||||
container.begin(), container.end(),
|
||||
static_cast<blt::size_t>(std::distance(container.begin(), container.end()))
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, blt::size_t size>
|
||||
static inline auto enumerate(const T(& container)[size])
|
||||
template <typename T, blt::size_t size>
|
||||
static inline auto enumerate(const T (&container)[size])
|
||||
{
|
||||
return enumerate_iterator_container{&container[0], &container[size], size};
|
||||
}
|
||||
|
||||
template<typename T, blt::size_t size>
|
||||
static inline auto enumerate(T(& container)[size])
|
||||
template <typename T, blt::size_t size>
|
||||
static inline auto enumerate(T (&container)[size])
|
||||
{
|
||||
return enumerate_iterator_container{&container[0], &container[size], size};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_ITERATOR_ENUMERATE_H
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_ITERATOR_FLATTEN_H
|
||||
#define BLT_ITERATOR_FLATTEN_H
|
||||
|
||||
#include <blt/iterator/common.h>
|
||||
#include <blt/meta/type_traits.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace blt::iterator
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename Tuple>
|
||||
static auto flatten_recursive(Tuple&& tuple) -> decltype(auto)
|
||||
{
|
||||
using Decay = std::decay_t<Tuple>;
|
||||
if constexpr (meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>)
|
||||
{
|
||||
return std::apply([](auto&&... args)
|
||||
{
|
||||
return std::tuple_cat(flatten_recursive(std::forward<decltype(args)>(args))...);
|
||||
}, std::forward<Tuple>(tuple));
|
||||
}
|
||||
else
|
||||
{
|
||||
return forward_as_tuple(std::forward<Tuple>(tuple));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tuple>
|
||||
static auto flatten(Tuple&& tuple) -> decltype(auto)
|
||||
{
|
||||
return std::apply([](auto&&... args)
|
||||
{
|
||||
return std::tuple_cat(ensure_tuple(std::forward<decltype(args)>(args))...);
|
||||
}, std::forward<Tuple>(tuple));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iter, bool Recursive>
|
||||
class flatten_wrapper : public deref_only_wrapper<Iter, flatten_wrapper<Iter, Recursive>>
|
||||
{
|
||||
public:
|
||||
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
|
||||
using value_type = std::conditional_t<Recursive,
|
||||
std::remove_reference_t<decltype(detail::flatten_recursive(*std::declval<Iter>()))>,
|
||||
std::remove_reference_t<decltype(detail::flatten(*std::declval<Iter>()))>>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
explicit flatten_wrapper(Iter iter):
|
||||
deref_only_wrapper<Iter, flatten_wrapper>(std::move(iter))
|
||||
{
|
||||
}
|
||||
|
||||
auto operator*() const -> decltype(auto)
|
||||
{
|
||||
if constexpr (Recursive)
|
||||
{
|
||||
return detail::flatten_recursive(*this->iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return detail::flatten(*this->iter);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //BLT_ITERATOR_FLATTEN_H
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef BLT_ITERATOR_FWDDECL_H
|
||||
#define BLT_ITERATOR_FWDDECL_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
template<typename... Iter>
|
||||
|
@ -33,7 +35,7 @@ namespace blt
|
|||
struct iterator_pair;
|
||||
|
||||
template<typename T>
|
||||
struct enumerate_item;
|
||||
using enumerate_item = std::pair<size_t, T>;
|
||||
|
||||
template<typename Iter>
|
||||
class enumerate_wrapper;
|
||||
|
@ -47,6 +49,12 @@ namespace blt
|
|||
template<typename Iter, typename Pred>
|
||||
class filter_wrapper;
|
||||
|
||||
template<typename Iter, bool Recursive>
|
||||
class flatten_wrapper;
|
||||
|
||||
template<typename Iter>
|
||||
class const_wrapper;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<typename Derived>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <blt/iterator/common.h>
|
||||
#include <blt/iterator/zip.h>
|
||||
#include <blt/iterator/enumerate.h>
|
||||
#include <blt/iterator/flatten.h>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <blt/iterator/common.h>
|
||||
#include <tuple>
|
||||
#include <limits>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
@ -112,11 +113,11 @@ namespace blt
|
|||
class zip_iterator_container : public iterator::iterator_container<iterator::zip_wrapper<Iter...>>
|
||||
{
|
||||
public:
|
||||
using iterator::iterator_container<iterator::zip_wrapper<Iter...>>::iterator_container;
|
||||
using blt::iterator::iterator_container<blt::iterator::zip_wrapper<Iter...>>::iterator_container;
|
||||
|
||||
explicit zip_iterator_container(iterator::iterator_pair<Iter>... iterator_pairs):
|
||||
iterator::iterator_container<iterator::zip_wrapper<Iter...>>(iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.begin)...},
|
||||
iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.end)...})
|
||||
explicit zip_iterator_container(blt::iterator::iterator_pair<Iter>... iterator_pairs):
|
||||
blt::iterator::iterator_container<blt::iterator::zip_wrapper<Iter...>>(blt::iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.begin)...},
|
||||
blt::iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.end)...})
|
||||
{}
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_COLORS_H
|
||||
#define BLT_LOGGING_COLORS_H
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
#define BLT_ANSI_ESCAPE "\x1B"
|
||||
#define BLT_ANSI_CSI BLT_ANSI_ESCAPE "["
|
||||
#define BLT_ANSI_DSC BLT_ANSI_ESCAPE "P"
|
||||
#define BLT_ANSI_OSC BLT_ANSI_ESCAPE "]"
|
||||
|
||||
// https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
|
||||
namespace blt::logging::ansi
|
||||
{
|
||||
namespace color
|
||||
{
|
||||
inline std::array<u8, 10> reset_sequences = {0, 22, 22, 23, 24, 25, 26, 27, 28, 29};
|
||||
|
||||
enum class color_mode : u8
|
||||
{
|
||||
RESET_ALL = 0,
|
||||
BOLD = 1,
|
||||
DIM = 2,
|
||||
ITALIC = 3,
|
||||
UNDERLINE = 4,
|
||||
BLINK = 5,
|
||||
REVERSE = 7,
|
||||
HIDDEN = 8,
|
||||
STRIKE_THROUGH = 9, };
|
||||
|
||||
enum class color8 : u8
|
||||
{
|
||||
BLACK = 0,
|
||||
RED = 1,
|
||||
GREEN = 2,
|
||||
YELLOW = 3,
|
||||
BLUE = 4,
|
||||
MAGENTA = 5,
|
||||
CYAN = 6,
|
||||
WHITE = 7,
|
||||
DEFAULT = 9
|
||||
};
|
||||
|
||||
enum class color8_bright : u8
|
||||
{
|
||||
BLACK = 0,
|
||||
RED = 1,
|
||||
GREEN = 2,
|
||||
YELLOW = 3,
|
||||
BLUE = 4,
|
||||
MAGENTA = 5,
|
||||
CYAN = 6,
|
||||
WHITE = 7
|
||||
};
|
||||
|
||||
struct rgb_t
|
||||
{
|
||||
u8 r, g, b;
|
||||
};
|
||||
|
||||
struct color256
|
||||
{
|
||||
explicit color256(u8 index) : color(index)
|
||||
{}
|
||||
|
||||
color256(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
|
||||
{
|
||||
if (r > 5)
|
||||
throw std::invalid_argument("r must be between 0 and 5");
|
||||
if (g > 5)
|
||||
throw std::invalid_argument("g must be between 0 and 5");
|
||||
if (b > 5)
|
||||
throw std::invalid_argument("b must be between 0 and 5");
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 index() const
|
||||
{
|
||||
if (std::holds_alternative<u8>(color))
|
||||
return std::get<u8>(color);
|
||||
const auto [r, g, b] = std::get<rgb_t>(color);
|
||||
return (r * 36) + (g * 6) + b + 16;
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<u8, rgb_t> color;
|
||||
};
|
||||
|
||||
struct color_rgb
|
||||
{
|
||||
color_rgb(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
|
||||
{}
|
||||
|
||||
rgb_t color;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T>
|
||||
struct color_holder
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
T color;
|
||||
bool alt = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct color_converter
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct color_converter<color8>
|
||||
{
|
||||
static std::string to_string(const color_holder<color8> color)
|
||||
{
|
||||
return (color.alt ? "4" : "3") + std::to_string(static_cast<u8>(color.color));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct color_converter<color8_bright>
|
||||
{
|
||||
static std::string to_string(const color_holder<color8_bright> color)
|
||||
{
|
||||
return (color.alt ? "10" : "9") + std::to_string(static_cast<u8>(color.color));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct color_converter<color_mode>
|
||||
{
|
||||
static std::string to_string(const color_holder<color_mode> color)
|
||||
{
|
||||
return color.alt ? std::to_string(reset_sequences[static_cast<u8>(color.color)]) : std::to_string(static_cast<u8>(color.color));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct color_converter<color256>
|
||||
{
|
||||
static std::string to_string(const color_holder<color256> color)
|
||||
{
|
||||
return (color.alt ? "48;5;" : "38;5;") + std::to_string(color.color.index());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct color_converter<color_rgb>
|
||||
{
|
||||
static std::string to_string(const color_holder<color_rgb> color)
|
||||
{
|
||||
return (color.alt ? "48;2;" : "38;2;") + std::to_string(color.color.color.r) + ";" + std::to_string(color.color.color.g) + ";" +
|
||||
std::to_string(color.color.color.b);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ensure_holder
|
||||
{
|
||||
static color_holder<T> make(const T& color)
|
||||
{
|
||||
return color_holder<T>{color, false};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ensure_holder<color_holder<T>>
|
||||
{
|
||||
static color_holder<T> make(const color_holder<T>& color)
|
||||
{
|
||||
return color;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct decay
|
||||
{
|
||||
using type = std::decay_t<T>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct decay<color_holder<T>>
|
||||
{
|
||||
using type = std::decay_t<T>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using decay_t = typename decay<T>::type;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto fg(const T& color)
|
||||
{
|
||||
return detail::color_holder<T>{color, false};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto bg(const T& color)
|
||||
{
|
||||
return detail::color_holder<T>{color, true};
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string build(const Args&... args)
|
||||
{
|
||||
std::string result = BLT_ANSI_CSI;
|
||||
((result += detail::color_converter<detail::decay_t<Args>>::to_string(detail::ensure_holder<Args>::make(args)), result += ';'), ...);
|
||||
return result.substr(0, result.size() - 1) + "m";
|
||||
}
|
||||
}
|
||||
|
||||
namespace general
|
||||
{
|
||||
inline const std::string bell = "\x07";
|
||||
inline const std::string bs = "\x08";
|
||||
inline const std::string horizontal_tab = "\x09";
|
||||
inline const std::string linefeed = "\x0A";
|
||||
inline const std::string vertical_tab = "\x0B";
|
||||
inline const std::string form_feed = "\x0C";
|
||||
inline const std::string carriage_return = "\x0D";
|
||||
inline const std::string escape = BLT_ANSI_ESCAPE;
|
||||
inline const std::string del = "\x7F";
|
||||
inline const std::string csi = BLT_ANSI_CSI;
|
||||
inline const std::string dsc = BLT_ANSI_DSC;
|
||||
inline const std::string osc = BLT_ANSI_OSC;
|
||||
}
|
||||
|
||||
namespace cursor
|
||||
{
|
||||
inline const std::string home = BLT_ANSI_CSI "H";
|
||||
inline const std::string lower_left_corner = BLT_ANSI_ESCAPE " F";
|
||||
inline const std::string hide_cursor = BLT_ANSI_CSI "?2 5l";
|
||||
inline const std::string show_cursor = BLT_ANSI_CSI "?2 5h";
|
||||
inline const std::string report_position = BLT_ANSI_CSI "6n";
|
||||
|
||||
template <bool UseH = true>
|
||||
std::string move_to(const i64 line, const i64 column)
|
||||
{
|
||||
if constexpr (UseH)
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "H";
|
||||
else
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "f";
|
||||
}
|
||||
|
||||
inline std::string move_up(const i64 lines)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "A";
|
||||
}
|
||||
|
||||
inline std::string move_down(const i64 lines)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "B";
|
||||
}
|
||||
|
||||
inline std::string move_right(const i64 columns)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "C";
|
||||
}
|
||||
|
||||
inline std::string move_left(const i64 columns)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "D";
|
||||
}
|
||||
|
||||
inline std::string move_begin_down(const i64 lines)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "E";
|
||||
}
|
||||
|
||||
inline std::string move_begin_up(const i64 lines)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "F";
|
||||
}
|
||||
|
||||
inline std::string move_to(const i64 column)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(column) + "G";
|
||||
}
|
||||
|
||||
inline const std::string request_cursor_position = BLT_ANSI_CSI "6n";
|
||||
inline const std::string move_up_one_line = BLT_ANSI_ESCAPE " M";
|
||||
inline const std::string save_cursor_position_dec = BLT_ANSI_ESCAPE " 7";
|
||||
inline const std::string restore_cursor_position_dec = BLT_ANSI_ESCAPE " 8";
|
||||
inline const std::string save_cursor_position_sco = BLT_ANSI_CSI "s";
|
||||
inline const std::string restore_cursor_position_sco = BLT_ANSI_CSI "u";
|
||||
}
|
||||
|
||||
namespace scroll
|
||||
{
|
||||
inline std::string scroll_up(const int lines)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "S";
|
||||
};
|
||||
}
|
||||
|
||||
namespace erase
|
||||
{
|
||||
inline const std::string to_end_of_screen = BLT_ANSI_CSI "0J";
|
||||
inline const std::string from_begin_of_screen = BLT_ANSI_CSI "1J";
|
||||
inline const std::string entire_screen = BLT_ANSI_CSI "2J";
|
||||
inline const std::string saved_lines = BLT_ANSI_CSI "3J";
|
||||
inline const std::string to_end_of_line = BLT_ANSI_CSI "0K";
|
||||
inline const std::string from_begin_of_line = BLT_ANSI_CSI "1K";
|
||||
inline const std::string entire_line = BLT_ANSI_CSI "2K";
|
||||
}
|
||||
|
||||
enum class mode : u8
|
||||
{
|
||||
mono40x25_text = 0,
|
||||
color40x25_text = 1,
|
||||
mono80x25_text = 2,
|
||||
color80x25_text = 3,
|
||||
color320x200_4color_graphics = 4,
|
||||
mono320x200_graphics = 5,
|
||||
mono640x200_graphics = 6,
|
||||
line_wrapping = 7,
|
||||
color320x200_graphics = 13,
|
||||
color640x200_16color_graphics = 14,
|
||||
mono640x350_2color_graphics = 15,
|
||||
color640x350_16color_graphics = 16,
|
||||
mono640x480_2color_graphics = 17,
|
||||
color640x480_16color_graphics = 18,
|
||||
color320_200_256color_graphics = 19
|
||||
};
|
||||
|
||||
inline std::string use_mode(const mode mode)
|
||||
{
|
||||
return std::string(BLT_ANSI_CSI) + "=" + std::to_string(static_cast<u8>(mode)) + "h";
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_COLORS_H
|
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_FMT_TOKENIZER_H
|
||||
#define BLT_LOGGING_FMT_TOKENIZER_H
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
enum class fmt_token_type : u8
|
||||
{
|
||||
STRING,
|
||||
NUMBER,
|
||||
SPACE,
|
||||
COLON,
|
||||
DOT,
|
||||
MINUS,
|
||||
PLUS,
|
||||
POUND,
|
||||
LEFT_CHEVRON,
|
||||
RIGHT_CHEVRON,
|
||||
OPEN_BRACKET,
|
||||
CLOSE_BRACKET,
|
||||
CARET
|
||||
};
|
||||
|
||||
enum class fmt_align_t : u8
|
||||
{
|
||||
LEFT,
|
||||
CENTER,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
enum class fmt_sign_t : u8
|
||||
{
|
||||
SPACE,
|
||||
PLUS,
|
||||
MINUS
|
||||
};
|
||||
|
||||
enum class fmt_type_t : u8
|
||||
{
|
||||
BINARY, // 'b'
|
||||
CHAR, // 'c'
|
||||
DECIMAL, // 'd'
|
||||
OCTAL, // 'o'
|
||||
HEX, // 'x'
|
||||
HEX_FLOAT, // 'a'
|
||||
EXPONENT, // 'e'
|
||||
FIXED_POINT, // 'f'
|
||||
GENERAL, // 'g'
|
||||
TYPE, // 't'
|
||||
UNSPECIFIED // default
|
||||
};
|
||||
|
||||
struct fmt_spec_t
|
||||
{
|
||||
i64 arg_id = -1;
|
||||
i64 width = -1;
|
||||
i64 precision = -1;
|
||||
fmt_type_t type = fmt_type_t::UNSPECIFIED;
|
||||
fmt_sign_t sign = fmt_sign_t::MINUS;
|
||||
fmt_align_t alignment = fmt_align_t::RIGHT;
|
||||
std::optional<char> prefix_char;
|
||||
bool uppercase = false;
|
||||
bool alternate_form = false;
|
||||
};
|
||||
|
||||
struct fmt_token_t
|
||||
{
|
||||
fmt_token_type type;
|
||||
std::string_view value;
|
||||
};
|
||||
|
||||
class fmt_tokenizer_t
|
||||
{
|
||||
public:
|
||||
explicit fmt_tokenizer_t() = default;
|
||||
|
||||
static fmt_token_type get_type(char c);
|
||||
|
||||
std::optional<fmt_token_t> next();
|
||||
|
||||
std::vector<fmt_token_t> tokenize(std::string_view fmt);
|
||||
|
||||
private:
|
||||
size_t m_pos = 0;
|
||||
std::string_view m_fmt{};
|
||||
};
|
||||
|
||||
|
||||
class fmt_parser_t
|
||||
{
|
||||
public:
|
||||
explicit fmt_parser_t(std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>>& handlers): m_handlers(handlers)
|
||||
{
|
||||
}
|
||||
|
||||
fmt_token_t& peek(const size_t offset)
|
||||
{
|
||||
return m_tokens[m_pos + offset];
|
||||
}
|
||||
|
||||
fmt_token_t& peek()
|
||||
{
|
||||
return m_tokens[m_pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_next() const
|
||||
{
|
||||
return m_pos < m_tokens.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_next(const size_t offset) const
|
||||
{
|
||||
return (m_pos + offset) < m_tokens.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] fmt_token_t& next()
|
||||
{
|
||||
return m_tokens[m_pos++];
|
||||
}
|
||||
|
||||
void consume()
|
||||
{
|
||||
++m_pos;
|
||||
}
|
||||
|
||||
void consume(const size_t amount)
|
||||
{
|
||||
m_pos += amount;
|
||||
}
|
||||
|
||||
const fmt_spec_t& parse(std::string_view fmt);
|
||||
|
||||
private:
|
||||
static bool is_align_t(fmt_token_type type);
|
||||
|
||||
void parse_fmt_field();
|
||||
void parse_arg_id();
|
||||
std::string parse_arg_or_number();
|
||||
|
||||
void parse_fmt_spec();
|
||||
void parse_fmt_spec_fill();
|
||||
void parse_fmt_spec_align();
|
||||
void parse_fmt_spec_sign();
|
||||
void parse_fmt_spec_form();
|
||||
void parse_fmt_spec_width();
|
||||
void parse_fmt_spec_precision();
|
||||
|
||||
void parse_fill();
|
||||
void parse_align();
|
||||
void parse_sign();
|
||||
void parse_form();
|
||||
void parse_width();
|
||||
void parse_precision();
|
||||
void parse_type();
|
||||
|
||||
size_t m_pos = 0;
|
||||
std::vector<fmt_token_t> m_tokens;
|
||||
fmt_tokenizer_t m_tokenizer;
|
||||
fmt_spec_t m_spec;
|
||||
|
||||
std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>>& m_handlers;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_FMT_TOKENIZER_H
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_FWDDECL_H
|
||||
#define BLT_LOGGING_FWDDECL_H
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
struct logger_t;
|
||||
enum class fmt_token_type : u8;
|
||||
enum class fmt_align_t : u8;
|
||||
enum class fmt_sign_t : u8;
|
||||
enum class fmt_type_t : u8;
|
||||
struct fmt_spec_t;
|
||||
struct fmt_token_t;
|
||||
class fmt_tokenizer_t;
|
||||
class fmt_parser_t;
|
||||
|
||||
class injector_t;
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_FWDDECL_H
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_INJECTOR_H
|
||||
#define BLT_LOGGING_INJECTOR_H
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
struct injector_output_t
|
||||
{
|
||||
std::string new_logging_output;
|
||||
// should we continue processing the injector call chain?
|
||||
bool should_continue = true;
|
||||
// should we log the resulting string at the end of the injector call chain? If false for any injector, it becomes false for all injectors.
|
||||
bool should_log = true;
|
||||
};
|
||||
|
||||
class injector_t
|
||||
{
|
||||
public:
|
||||
virtual ~injector_t() = default;
|
||||
virtual injector_output_t inject(const std::string& input) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_INJECTOR_H
|
|
@ -0,0 +1,286 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_LOGGING_H
|
||||
#define BLT_LOGGING_LOGGING_H
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <blt/logging/fmt_tokenizer.h>
|
||||
#include <blt/logging/logging_config.h>
|
||||
#include <blt/meta/is_streamable.h>
|
||||
#include <blt/std/utility.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
struct logger_t
|
||||
{
|
||||
explicit logger_t(std::ostream& stream): m_stream(stream)
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
std::string log(std::string fmt, Args&&... args)
|
||||
{
|
||||
if (fmt.empty())
|
||||
return fmt;
|
||||
auto sequence = std::make_integer_sequence<size_t, sizeof...(Args)>{};
|
||||
m_arg_print_funcs.clear();
|
||||
m_arg_print_funcs.resize(sizeof...(Args));
|
||||
create_conv_funcs(sequence, std::forward<Args>(args)...);
|
||||
compile(std::move(fmt));
|
||||
process_strings();
|
||||
return to_string();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
|
||||
private:
|
||||
template <typename... Args, size_t... Indexes>
|
||||
void create_conv_funcs(std::integer_sequence<size_t, Indexes...>, Args&&... args)
|
||||
{
|
||||
((handle_func<Indexes>(std::forward<Args>(args))), ...);
|
||||
}
|
||||
|
||||
template <size_t index, typename T>
|
||||
void handle_func(const T& t)
|
||||
{
|
||||
m_arg_print_funcs[index] = [&t, this](std::ostream& stream, const fmt_spec_t& type) {
|
||||
switch (type.sign)
|
||||
{
|
||||
case fmt_sign_t::SPACE:
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
if (type.type != fmt_type_t::BINARY && static_cast<i64>(t) >= 0l)
|
||||
stream << ' ';
|
||||
}
|
||||
break;
|
||||
case fmt_sign_t::PLUS:
|
||||
case fmt_sign_t::MINUS:
|
||||
break;
|
||||
}
|
||||
switch (type.type)
|
||||
{
|
||||
case fmt_type_t::BINARY:
|
||||
{
|
||||
if constexpr (std::is_trivially_copyable_v<T>)
|
||||
{
|
||||
// copy bytes of type
|
||||
char buffer[sizeof(T)];
|
||||
std::memcpy(buffer, &t, sizeof(T));
|
||||
// display prefix
|
||||
if (type.alternate_form)
|
||||
stream << '0' << (type.uppercase ? 'B' : 'b');
|
||||
// print bytes
|
||||
for (size_t i = 0; i < sizeof(T); ++i)
|
||||
{
|
||||
// print bits
|
||||
for (size_t j = 0; j < 8; ++j)
|
||||
stream << ((buffer[i] & (1 << j)) ? '1' : '0');
|
||||
// special seperator defined via sign (weird hack, change?)
|
||||
if (type.sign == fmt_sign_t::SPACE && i != sizeof(T) - 1)
|
||||
stream << ' ';
|
||||
}
|
||||
} else
|
||||
{
|
||||
if constexpr (blt::meta::is_streamable_v<T>)
|
||||
stream << t;
|
||||
else
|
||||
stream << "{INVALID TYPE}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fmt_type_t::CHAR:
|
||||
if constexpr (std::is_arithmetic_v<T> || std::is_convertible_v<T, char>)
|
||||
{
|
||||
stream << static_cast<char>(t);
|
||||
} else
|
||||
{
|
||||
if constexpr (blt::meta::is_streamable_v<T>)
|
||||
stream << t;
|
||||
else
|
||||
stream << "{INVALID TYPE}";
|
||||
}
|
||||
break;
|
||||
case fmt_type_t::TYPE:
|
||||
stream << blt::type_string<T>();
|
||||
break;
|
||||
default:
|
||||
handle_type(stream, type);
|
||||
if constexpr (blt::meta::is_streamable_v<T>)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, char> || std::is_same_v<T, signed char>)
|
||||
stream << static_cast<int>(t);
|
||||
else if constexpr (std::is_same_v<T, unsigned char>)
|
||||
stream << static_cast<unsigned int>(t);
|
||||
else
|
||||
stream << t;
|
||||
} else
|
||||
stream << "{INVALID TYPE}";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t find_ending_brace(size_t begin) const;
|
||||
void setup_stream(const fmt_spec_t& spec) const;
|
||||
std::string process_string(std::string_view str);
|
||||
void process_strings();
|
||||
static void handle_type(std::ostream& stream, const fmt_spec_t& spec);
|
||||
|
||||
static void exponential(std::ostream& stream);
|
||||
static void fixed(std::ostream& stream);
|
||||
|
||||
void compile(std::string fmt);
|
||||
|
||||
std::optional<std::pair<size_t, size_t>> consume_to_next_fmt();
|
||||
|
||||
std::string m_fmt;
|
||||
std::ostream& m_stream;
|
||||
fmt_parser_t m_parser{m_arg_print_funcs};
|
||||
// normal sections of string
|
||||
std::vector<std::string_view> m_string_sections;
|
||||
// processed format specs
|
||||
std::vector<fmt_spec_t> m_fmt_specs;
|
||||
std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>> m_arg_print_funcs;
|
||||
size_t m_last_fmt_pos = 0;
|
||||
size_t m_arg_pos = 0;
|
||||
};
|
||||
|
||||
void print(std::string str);
|
||||
|
||||
void newline();
|
||||
|
||||
logger_t& get_global_logger();
|
||||
|
||||
logging_config_t& get_global_config();
|
||||
|
||||
std::ostream& get_local_stream();
|
||||
|
||||
void set_thread_name(const std::string& name);
|
||||
|
||||
const std::string& get_thread_name();
|
||||
|
||||
template <typename... Args>
|
||||
void print(std::string fmt, Args&&... args)
|
||||
{
|
||||
auto& logger = get_global_logger();
|
||||
print(logger.log(std::move(fmt), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void print(std::ostream& stream, std::string fmt, Args&&... args)
|
||||
{
|
||||
auto& logger = get_global_logger();
|
||||
stream << logger.log(std::move(fmt), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void println(std::string fmt, Args&&... args)
|
||||
{
|
||||
print(std::move(fmt), std::forward<Args>(args)...);
|
||||
newline();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void println(std::ostream& stream, std::string fmt, Args&&... args)
|
||||
{
|
||||
print(stream, std::move(fmt), std::forward<Args>(args)...);
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void log(const log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args)
|
||||
{
|
||||
auto& logger = get_global_logger();
|
||||
const auto& config = get_global_config();
|
||||
std::string user_str = logger.log(std::move(fmt), std::forward<Args>(args)...);
|
||||
if (!user_str.empty() && user_str.back() == '\n')
|
||||
user_str.pop_back();
|
||||
if (level == log_level_t::NONE)
|
||||
{
|
||||
print(user_str);
|
||||
newline();
|
||||
return;
|
||||
}
|
||||
auto log_fmt_str = config.generate(user_str, get_thread_name(), level, file, line);
|
||||
if (log_fmt_str)
|
||||
print(std::move(*log_fmt_str));
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__clang__) || defined(__llvm__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_LOGGING
|
||||
#define BLT_LOG(level, fmt, ...)
|
||||
|
||||
#else
|
||||
#define BLT_LOG(level, fmt, ...) blt::logging::log(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef BLT_DISABLE_TRACE
|
||||
#define BLT_TRACE(format, ...)
|
||||
#else
|
||||
#define BLT_TRACE(format, ...) BLT_LOG(blt::logging::log_level_t::TRACE, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_DEBUG
|
||||
#define BLT_DEBUG(format, ...)
|
||||
#else
|
||||
#define BLT_DEBUG(format, ...) BLT_LOG(blt::logging::log_level_t::DEBUG, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_INFO
|
||||
#define BLT_INFO(format, ...)
|
||||
#else
|
||||
#define BLT_INFO(format, ...) BLT_LOG(blt::logging::log_level_t::INFO, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_WARN
|
||||
#define BLT_WARN(format, ...)
|
||||
#else
|
||||
#define BLT_WARN(format, ...) BLT_LOG(blt::logging::log_level_t::WARN, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_ERROR
|
||||
#define BLT_ERROR(format, ...)
|
||||
#else
|
||||
#define BLT_ERROR(format, ...) BLT_LOG(blt::logging::log_level_t::ERROR, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_FATAL
|
||||
#define BLT_FATAL(format, ...)
|
||||
#else
|
||||
#define BLT_FATAL(format, ...) BLT_LOG(blt::logging::log_level_t::FATAL, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__llvm__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // BLT_LOGGING_LOGGING_H
|
|
@ -0,0 +1,243 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_LOGGING_CONFIG_H
|
||||
#define BLT_LOGGING_LOGGING_CONFIG_H
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <blt/fs/fwddecl.h>
|
||||
#include <blt/logging/fwddecl.h>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
namespace tags
|
||||
{
|
||||
// Current Year
|
||||
inline constexpr auto YEAR = "{YEAR}";
|
||||
// Current Month
|
||||
inline constexpr auto MONTH = "{MONTH}";
|
||||
// Current Day
|
||||
inline constexpr auto DAY = "{DAY}";
|
||||
// Current Hour
|
||||
inline constexpr auto HOUR = "{HOUR}";
|
||||
// Current Minute
|
||||
inline constexpr auto MINUTE = "{MINUTE}";
|
||||
// Current Second
|
||||
inline constexpr auto SECOND = "{SECOND}";
|
||||
// Current Millisecond
|
||||
inline constexpr auto MILLISECOND = "{MS}";
|
||||
// Current Nanosecond
|
||||
inline constexpr auto NANOSECOND = "{NS}";
|
||||
// Current Unix time in milliseconds
|
||||
inline constexpr auto UNIX_TIME = "{UNIX}";
|
||||
// Current Unix time in nanosecond
|
||||
inline constexpr auto UNIX_TIME_NANO = "{UNIX_NANO}";
|
||||
// Formatted ISO year-month-day in a single variable
|
||||
inline constexpr auto ISO_YEAR = "{ISO_YEAR}";
|
||||
// Formatted hour:minute:second in a single variable
|
||||
inline constexpr auto TIME = "{TIME}";
|
||||
// Formatted year-month-day hour:minute:second in a single variable
|
||||
inline constexpr auto FULL_TIME = "{FULL_TIME}";
|
||||
// Color of the current log level, empty string if use_color = false
|
||||
inline constexpr auto LOG_COLOR = "{LC}";
|
||||
// Color of the error color, empty string if use_color = false
|
||||
inline constexpr auto ERROR_COLOR = "{EC}";
|
||||
// Empty is use_color = false or if log level is not an error. Otherwise, {EC}
|
||||
inline constexpr auto CONDITIONAL_ERROR_COLOR = "{CEC}";
|
||||
// Resets all ANSI sequences
|
||||
inline constexpr auto RESET = "{RESET}";
|
||||
// Current log level
|
||||
inline constexpr auto LOG_LEVEL = "{LL}";
|
||||
// Current thread name. Requires you to manually set the thread name using blt::logging::set_thread_name() from that thread.
|
||||
inline constexpr auto THREAD_NAME = "{TN}";
|
||||
// Current file from where the log call was invoked.
|
||||
inline constexpr auto FILE = "{FILE}";
|
||||
// Current line from where the log call was invoked
|
||||
inline constexpr auto LINE = "{LINE}";
|
||||
// User string input, formatted with provided args
|
||||
inline constexpr auto STR = "{STR}";
|
||||
|
||||
namespace detail
|
||||
{
|
||||
enum class log_tag_token_t : u8
|
||||
{
|
||||
YEAR,
|
||||
MONTH,
|
||||
DAY,
|
||||
HOUR,
|
||||
MINUTE,
|
||||
SECOND,
|
||||
MS,
|
||||
NS,
|
||||
UNIX,
|
||||
UNIX_NANO,
|
||||
ISO_YEAR,
|
||||
TIME,
|
||||
FULL_TIME,
|
||||
LC,
|
||||
EC,
|
||||
CEC,
|
||||
RESET,
|
||||
LL,
|
||||
TN,
|
||||
FILE,
|
||||
LINE,
|
||||
STR,
|
||||
// token used to describe that a non-format token should be consumed. aka a normal string from the file.
|
||||
CONTENT
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum class log_level_t : u8
|
||||
{
|
||||
TRACE,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
FATAL,
|
||||
NONE
|
||||
};
|
||||
|
||||
inline constexpr size_t LOG_LEVEL_COUNT = 6;
|
||||
|
||||
class logging_config_t
|
||||
{
|
||||
friend logger_t;
|
||||
|
||||
public:
|
||||
logging_config_t()
|
||||
{
|
||||
compile();
|
||||
}
|
||||
|
||||
void compile();
|
||||
|
||||
logging_config_t& add_log_output(fs::writer_t& writer)
|
||||
{
|
||||
m_log_outputs.push_back(&writer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& add_injector(injector_t& injector)
|
||||
{
|
||||
m_injectors.push_back(&injector);
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_log_format(std::string format)
|
||||
{
|
||||
m_log_format = std::move(format);
|
||||
compile();
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_error_color(std::string color)
|
||||
{
|
||||
m_error_color = std::move(color);
|
||||
compile();
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_log_level_colors(std::array<std::string, LOG_LEVEL_COUNT> colors)
|
||||
{
|
||||
m_log_level_colors = std::move(colors);
|
||||
compile();
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_log_level_names(std::array<std::string, LOG_LEVEL_COUNT> names)
|
||||
{
|
||||
m_log_level_names = std::move(names);
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_level(const log_level_t level)
|
||||
{
|
||||
this->m_level = level;
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_use_color(const bool use_color)
|
||||
{
|
||||
this->m_use_color = use_color;
|
||||
compile();
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_print_full_name(const bool print_full_name)
|
||||
{
|
||||
this->m_print_full_name = print_full_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
logging_config_t& set_ensure_alignment(const bool ensure_alignment)
|
||||
{
|
||||
this->m_ensure_alignment = ensure_alignment;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<const std::vector<tags::detail::log_tag_token_t>&, const std::vector<std::string>&> get_log_tag_tokens() const
|
||||
{
|
||||
return {m_log_tag_tokens, m_log_tag_content};
|
||||
}
|
||||
|
||||
std::optional<std::string> generate(const std::string& user_str, const std::string& thread_name, log_level_t level, const char* file,
|
||||
i32 line) const;
|
||||
|
||||
[[nodiscard]] const std::vector<injector_t*>& get_injectors() const
|
||||
{
|
||||
return m_injectors;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<injector_t*> m_injectors;
|
||||
// wrappers for streams exist in blt/fs/stream_wrappers.h
|
||||
std::vector<fs::writer_t*> m_log_outputs = get_default_log_outputs();
|
||||
std::string m_log_format = get_default_log_format();
|
||||
std::string m_error_color = get_default_error_color();
|
||||
std::array<std::string, LOG_LEVEL_COUNT> m_log_level_colors = get_default_log_level_colors();
|
||||
std::array<std::string, LOG_LEVEL_COUNT> m_log_level_names = get_default_log_level_names();
|
||||
log_level_t m_level = log_level_t::TRACE;
|
||||
|
||||
bool m_use_color = true;
|
||||
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
|
||||
bool m_print_full_name = false;
|
||||
// this will attempt to use the maximum possible size for each printed element, then align to that.
|
||||
// This creates output where the user message always starts at the same column.
|
||||
bool m_ensure_alignment = true;
|
||||
|
||||
size_t m_longest_name_length = 0;
|
||||
|
||||
std::vector<std::string> m_log_tag_content;
|
||||
std::vector<tags::detail::log_tag_token_t> m_log_tag_tokens;
|
||||
|
||||
static std::string get_default_log_format();
|
||||
static std::vector<fs::writer_t*> get_default_log_outputs();
|
||||
static std::array<std::string, LOG_LEVEL_COUNT> get_default_log_level_colors();
|
||||
static std::array<std::string, LOG_LEVEL_COUNT> get_default_log_level_names();
|
||||
static std::string get_default_error_color();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_LOGGING_CONFIG_H
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_STATUS_H
|
||||
#define BLT_LOGGING_STATUS_H
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <blt/logging/injector.h>
|
||||
#include <blt/math/vectors.h>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
class status_bar_t;
|
||||
|
||||
class status_item_t
|
||||
{
|
||||
public:
|
||||
virtual ~status_item_t() = default;
|
||||
|
||||
void assign(status_bar_t* bar)
|
||||
{
|
||||
m_status = bar;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual i32 lines_used() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual std::string print(vec2i screen_size, i32 max_printed_length) const = 0;
|
||||
protected:
|
||||
status_bar_t* m_status = nullptr;
|
||||
};
|
||||
|
||||
class status_progress_bar_t final : public status_item_t
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::string print(vec2i screen_size, i32 max_printed_length) const override;
|
||||
|
||||
void set_progress(double progress);
|
||||
|
||||
void add_progress(const double progress)
|
||||
{
|
||||
set_progress(get_progress() + progress);
|
||||
}
|
||||
|
||||
[[nodiscard]] double get_progress() const
|
||||
{
|
||||
return m_progress;
|
||||
}
|
||||
private:
|
||||
double m_progress = 0.0;
|
||||
};
|
||||
|
||||
class status_bar_t final : public injector_t
|
||||
{
|
||||
public:
|
||||
explicit status_bar_t();
|
||||
|
||||
injector_output_t inject(const std::string& input) override;
|
||||
|
||||
status_bar_t& add(status_item_t& item)
|
||||
{
|
||||
item.assign(this);
|
||||
m_status_items.push_back(&item);
|
||||
compute_size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void redraw() const;
|
||||
|
||||
~status_bar_t() override;
|
||||
private:
|
||||
void compute_size();
|
||||
|
||||
std::vector<status_item_t*> m_status_items;
|
||||
i32 m_status_size = 0;
|
||||
i32 m_max_printed_length = 0;
|
||||
vec2i m_screen_size;
|
||||
vec2i m_last_log_position;
|
||||
vec2i m_begin_position;
|
||||
|
||||
std::mutex m_print_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_LOGGING_STATUS_H
|
|
@ -0,0 +1,170 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_MATH_AABB_H
|
||||
#define BLT_MATH_AABB_H
|
||||
|
||||
#include <array>
|
||||
#include <blt/math/vectors.h>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
// yes I could use the vector (see tower defense game commit log for this)
|
||||
// this feels nicer
|
||||
template <typename T = float>
|
||||
class axis_t
|
||||
{
|
||||
public:
|
||||
axis_t(const T min, const T max): m_min(min), m_max(max)
|
||||
{}
|
||||
|
||||
[[nodiscard]] bool intersects(const T p) const
|
||||
{
|
||||
return p >= m_min && p <= m_max;
|
||||
}
|
||||
|
||||
template <typename G>
|
||||
[[nodiscard]] bool intersects(const axis_t<G>& other) const
|
||||
{
|
||||
return static_cast<T>(other.m_min) <= m_max && static_cast<T>(other.m_max) >= m_min;
|
||||
}
|
||||
|
||||
[[nodiscard]] T min() const
|
||||
{
|
||||
return m_min;
|
||||
}
|
||||
|
||||
[[nodiscard]] T max() const
|
||||
{
|
||||
return m_max;
|
||||
}
|
||||
|
||||
[[nodiscard]] T length() const
|
||||
{
|
||||
return m_max - m_min;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_min, m_max;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <u32 Axis, typename T>
|
||||
class axis_aligned_bounding_box_base_t
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] vec<T, Axis> get_center() const
|
||||
{
|
||||
vec<T, Axis> min;
|
||||
for (u32 i = 0; i < Axis; i++)
|
||||
min[i] = m_axes[i].min();
|
||||
const auto center = get_size() / 2.0f;
|
||||
return min + center;
|
||||
}
|
||||
|
||||
[[nodiscard]] vec<T, Axis> get_size() const
|
||||
{
|
||||
vec<T, Axis> size;
|
||||
for (u32 i = 0; i < Axis; i++)
|
||||
size[i] = m_axes[i].length();
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename G>
|
||||
[[nodiscard]] bool intersects(const axis_aligned_bounding_box_base_t<Axis, G>& other) const
|
||||
{
|
||||
for (u32 i = 0; i < Axis; i++)
|
||||
if (!m_axes[i].intersects(other.m_axes[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename G>
|
||||
[[nodiscard]] bool intersects(const vec<G, Axis>& point) const
|
||||
{
|
||||
for (u32 i = 0; i < Axis; i++)
|
||||
if (!m_axes[i].intersects(point[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
axis_t<T>& operator[](u32 i)
|
||||
{
|
||||
return m_axes[i];
|
||||
}
|
||||
|
||||
axis_t<T>& axis(u32 i)
|
||||
{
|
||||
if (i >= Axis)
|
||||
throw std::out_of_range("Axis index out of range");
|
||||
return m_axes[i];
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<axis_t<T>, Axis> m_axes;
|
||||
};
|
||||
}
|
||||
|
||||
template <u32 Axis, typename T>
|
||||
class axis_aligned_bounding_box_t : public detail::axis_aligned_bounding_box_base_t<Axis, T>
|
||||
{
|
||||
public:
|
||||
using detail::axis_aligned_bounding_box_base_t<Axis, T>::axis_aligned_bounding_box_base_t;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class axis_aligned_bounding_box_t<2, T> : public detail::axis_aligned_bounding_box_base_t<2, T>
|
||||
{
|
||||
public:
|
||||
using detail::axis_aligned_bounding_box_base_t<2, T>::axis_aligned_bounding_box_base_t;
|
||||
|
||||
[[nodiscard]] vec2 min() const
|
||||
{
|
||||
return {this->m_axes[0].min(), this->m_axes[1].min()};
|
||||
}
|
||||
|
||||
[[nodiscard]] vec2 max() const
|
||||
{
|
||||
return {this->m_axes[0].max(), this->m_axes[1].max()};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class axis_aligned_bounding_box_t<3, T> : public detail::axis_aligned_bounding_box_base_t<3, T>
|
||||
{
|
||||
public:
|
||||
using detail::axis_aligned_bounding_box_base_t<2, T>::axis_aligned_bounding_box_base_t;
|
||||
|
||||
[[nodiscard]] vec3 min() const
|
||||
{
|
||||
return {this->m_axes[0].min(), this->m_axes[1].min(), this->m_axes[2].min()};
|
||||
}
|
||||
|
||||
[[nodiscard]] vec3s max() const
|
||||
{
|
||||
return {this->m_axes[0].max(), this->m_axes[1].max(), this->m_axes[2].max()};
|
||||
}
|
||||
};
|
||||
|
||||
using aabb_2d_t = axis_aligned_bounding_box_t<2, float>;
|
||||
using aabb_3d_t = axis_aligned_bounding_box_t<3, float>;
|
||||
}
|
||||
|
||||
#endif //BLT_MATH_AABB_H
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_MATH_BOUNDING_BOX_H
|
||||
#define BLT_MATH_BOUNDING_BOX_H
|
||||
|
||||
#include <blt/math/aabb.h>
|
||||
|
||||
namespace blt {
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_MATH_BOUNDING_BOX_H
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include <blt/math/vectors.h>
|
||||
#include <blt/math/matrix.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/utility.h>
|
||||
#include "blt/std/string.h"
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ namespace blt
|
|||
|
||||
constexpr float EPSILON = std::numeric_limits<float>::epsilon();
|
||||
|
||||
static inline constexpr bool f_equal(float v1, float v2)
|
||||
static constexpr bool f_equal(const float v1, const float v2, const float range = 1)
|
||||
{
|
||||
return v1 >= v2 - EPSILON && v1 <= v2 + EPSILON;
|
||||
return v1 >= v2 - (EPSILON * range) && v1 <= v2 + (EPSILON * range);
|
||||
}
|
||||
|
||||
template <typename T, blt::u32 size>
|
||||
|
@ -107,6 +107,11 @@ namespace blt
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::array<T, size>& to_array() const
|
||||
{
|
||||
return elements;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline T x() const
|
||||
{
|
||||
return elements[0];
|
||||
|
@ -423,6 +428,50 @@ namespace blt
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename G, u32 size>
|
||||
constexpr bool operator>=(const vec<T, size>& left, const vec<G, size>& right)
|
||||
{
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
if (left[i] < right[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename G, u32 size>
|
||||
constexpr bool operator>(const vec<T, size>& left, const vec<G, size>& right)
|
||||
{
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
if (left[i] <= right[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename G, u32 size>
|
||||
constexpr bool operator<(const vec<T, size>& left, const vec<G, size>& right)
|
||||
{
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
if (left[i] >= right[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename G, u32 size>
|
||||
constexpr bool operator<=(const vec<T, size>& left, const vec<G, size>& right)
|
||||
{
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
if (left[i] > right[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename G, blt::u32 size>
|
||||
inline constexpr bool operator!=(const vec<T, size>& left, const vec<G, size>& right)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_META_IS_STREAMABLE_H
|
||||
#define BLT_META_IS_STREAMABLE_H
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace blt::meta
|
||||
{
|
||||
// https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists
|
||||
template<typename T>
|
||||
class is_streamable
|
||||
{
|
||||
private:
|
||||
template<typename Subs>
|
||||
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<Subs>(), std::true_type())
|
||||
{
|
||||
return std::declval<std::true_type>();
|
||||
}
|
||||
|
||||
template<typename>
|
||||
static auto test(...) -> std::false_type
|
||||
{
|
||||
return std::declval<std::false_type>();
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline constexpr bool is_streamable_v = is_streamable<T>::value;
|
||||
}
|
||||
|
||||
#endif //BLT_META_IS_STREAMABLE_H
|
|
@ -23,6 +23,7 @@
|
|||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <ostream>
|
||||
#include <blt/meta/is_streamable.h>
|
||||
|
||||
namespace blt::meta
|
||||
{
|
||||
|
@ -71,30 +72,6 @@ namespace blt::meta
|
|||
template<typename Lambda>
|
||||
lambda_helper(Lambda) -> lambda_helper<Lambda, decltype(&Lambda::operator())>;
|
||||
|
||||
// https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists
|
||||
template<typename T>
|
||||
class is_streamable
|
||||
{
|
||||
private:
|
||||
template<typename Subs>
|
||||
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<Subs>(), std::true_type())
|
||||
{
|
||||
return std::declval<std::true_type>();
|
||||
}
|
||||
|
||||
template<typename>
|
||||
static auto test(...) -> std::false_type
|
||||
{
|
||||
return std::declval<std::false_type>();
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline constexpr bool is_streamable_v = is_streamable<T>::value;
|
||||
|
||||
template<typename T>
|
||||
struct arrow_return
|
||||
{
|
||||
|
@ -114,7 +91,7 @@ namespace blt::meta
|
|||
template<typename T>
|
||||
struct deref_return
|
||||
{
|
||||
using type = typename std::invoke_result_t<decltype(&T::operator*), T&>;
|
||||
using type = std::invoke_result_t<decltype(&T::operator*), T&>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_META_TYPE_TRAITS_H
|
||||
#define BLT_META_TYPE_TRAITS_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
|
||||
namespace blt::meta
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<typename... Args>
|
||||
void empty_apply_function(Args...)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline auto lambda = [](auto...)
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_volatile_t<std::remove_const_t<std::remove_reference_t<T>>>;
|
||||
|
||||
template <typename U>
|
||||
using add_const_ref_t = std::conditional_t<std::is_reference_v<U>, const std::remove_reference_t<U>&, const U>;
|
||||
|
||||
template <typename>
|
||||
struct is_tuple : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
struct is_tuple<std::tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename>
|
||||
struct is_pair : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename G>
|
||||
struct is_pair<std::pair<T, G>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_tuple_v = is_tuple<T>::value;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_pair_v = is_pair<T>::value;
|
||||
}
|
||||
|
||||
#endif // BLT_META_TYPE_TRAITS_H
|
|
@ -0,0 +1,811 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_PARSE_ARGPARSE_V2_H
|
||||
#define BLT_PARSE_ARGPARSE_V2_H
|
||||
|
||||
#include <complex>
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/std/hashmap.h>
|
||||
#include <blt/meta/meta.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <blt/iterator/iterator.h>
|
||||
#include <blt/meta/type_traits.h>
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/std/expected.h>
|
||||
#include <blt/std/ranges.h>
|
||||
#include <blt/std/utility.h>
|
||||
|
||||
namespace blt::argparse
|
||||
{
|
||||
class argument_string_t;
|
||||
class argument_consumer_t;
|
||||
class argument_parser_t;
|
||||
class argument_subparser_t;
|
||||
class argument_builder_t;
|
||||
class argument_storage_t;
|
||||
class argument_positional_storage_t;
|
||||
|
||||
enum class action_t
|
||||
{
|
||||
STORE,
|
||||
STORE_CONST,
|
||||
STORE_TRUE,
|
||||
STORE_FALSE,
|
||||
APPEND,
|
||||
APPEND_CONST,
|
||||
EXTEND,
|
||||
COUNT,
|
||||
HELP,
|
||||
VERSION
|
||||
};
|
||||
|
||||
enum class nargs_t
|
||||
{
|
||||
IF_POSSIBLE,
|
||||
ALL,
|
||||
ALL_AT_LEAST_ONE
|
||||
};
|
||||
|
||||
using nargs_v = std::variant<nargs_t, i32>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class bad_flag final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit bad_flag(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class bad_positional final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit bad_positional(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class missing_argument_error final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit missing_argument_error(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class missing_value_error final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit missing_value_error(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class type_error final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit type_error(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class unexpected_argument_error final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit unexpected_argument_error(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class bad_choice_error final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit bad_choice_error(const std::string& message): std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class subparse_error final : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit subparse_error(const std::string_view found_string, std::vector<std::vector<std::string_view>> allowed_strings):
|
||||
m_found_string(found_string),
|
||||
m_allowed_strings(std::move(allowed_strings))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::vector<std::string_view>>& get_allowed_strings() const
|
||||
{
|
||||
return m_allowed_strings;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view get_found_string() const
|
||||
{
|
||||
return m_found_string;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string error_string() const;
|
||||
|
||||
[[nodiscard]] const char* what() const noexcept override
|
||||
{
|
||||
m_error_string = error_string();
|
||||
return m_error_string.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::string m_error_string;
|
||||
std::string_view m_found_string;
|
||||
std::vector<std::vector<std::string_view>> m_allowed_strings;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
struct arg_data_helper_t
|
||||
{
|
||||
using variant_t = std::variant<Args..., std::vector<Args>...>;
|
||||
using arg_t = meta::arg_helper<Args...>;
|
||||
using arg_vec_t = meta::arg_helper<std::vector<Args>...>;
|
||||
|
||||
template <typename T>
|
||||
constexpr static bool is_type_stored_v = std::disjunction_v<std::is_same<T, Args>...>;
|
||||
|
||||
template <typename DefaultPrimitiveAction, typename DefaultListAction>
|
||||
static auto make_visitor(const DefaultPrimitiveAction& primitive_action, const DefaultListAction& list_action)
|
||||
{
|
||||
return lambda_visitor{
|
||||
([&primitive_action](Args& arg)
|
||||
{
|
||||
return primitive_action(arg);
|
||||
})...,
|
||||
([&list_action](std::vector<Args>& arg_vec)
|
||||
{
|
||||
return list_action(arg_vec);
|
||||
})...
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
using arg_meta_type_helper_t = arg_data_helper_t<bool, i8, i16, i32, i64, u8, u16, u32, u64, float, double, std::string>;
|
||||
using arg_data_t = arg_meta_type_helper_t::variant_t;
|
||||
|
||||
template <typename T>
|
||||
struct arg_string_converter_t
|
||||
{
|
||||
static T convert(const std::string_view value)
|
||||
{
|
||||
static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string_view> || std::is_same_v<T, std::string>,
|
||||
"Type must be arithmetic, string_view or string!");
|
||||
const std::string temp{value};
|
||||
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
{
|
||||
return std::stof(temp);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
return std::stod(temp);
|
||||
}
|
||||
else if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return static_cast<T>(std::stoull(temp));
|
||||
}
|
||||
else if constexpr (std::is_signed_v<T>)
|
||||
{
|
||||
return static_cast<T>(std::stoll(temp));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string_view>)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
return std::string(value);
|
||||
}
|
||||
BLT_UNREACHABLE;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
auto ensure_is_string(T&& t)
|
||||
{
|
||||
using NO_REF = meta::remove_cvref_t<T>;
|
||||
if constexpr (std::is_same_v<NO_REF, bool>)
|
||||
return t ? "true" : "false";
|
||||
else if constexpr (std::is_arithmetic_v<NO_REF> && !(std::is_same_v<NO_REF, char>
|
||||
|| std::is_same_v<NO_REF, unsigned char> || std::is_same_v<NO_REF, signed char>))
|
||||
return std::to_string(std::forward<T>(t));
|
||||
else
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
void test();
|
||||
}
|
||||
|
||||
class argument_string_t
|
||||
{
|
||||
public:
|
||||
explicit argument_string_t(const std::string_view input, const hashset_t<char>& allowed_flag_prefix): m_argument(input),
|
||||
m_allowed_flag_prefix(&allowed_flag_prefix)
|
||||
{
|
||||
process_argument();
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] std::string_view get_flag() const
|
||||
{
|
||||
return m_flag_section;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view get_name() const
|
||||
{
|
||||
return m_name_section;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view value() const
|
||||
{
|
||||
return get_name();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_flag() const
|
||||
{
|
||||
return !m_flag_section.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view get_argument() const
|
||||
{
|
||||
return m_argument;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* This function takes the command line argument represented by this class,
|
||||
* stored in m_argument and converts into flag side and name side arguments.
|
||||
*
|
||||
* What this means is in the case of a flag provided, for example passing --foo or --bar, this function will split the argument into
|
||||
*
|
||||
* m_flag_section = "--"
|
||||
* m_name_section = "foo" || "bar"
|
||||
*
|
||||
* If the argument is purely positional, meaning there is no flag prefix,
|
||||
* this function will do nothing and m_flag_section will be true for .empty()
|
||||
*
|
||||
* For example, if you provide res/some/folder/or/file as a command line positional argument,
|
||||
* this function will create the following internal state:
|
||||
*
|
||||
* m_flag_section = ""
|
||||
* m_flag_section.empty() == true
|
||||
* m_name_section = "res/some/folder/or/file"
|
||||
*
|
||||
* m_argument is not modified by this function
|
||||
*/
|
||||
void process_argument()
|
||||
{
|
||||
// user provides a list of allowed prefix characters to argument_parser_t, which is then provided to this class at construction time
|
||||
// it is not the job of this class to validate flag prefixes beyond this. // TODO: requiring this pointer is a code smell.
|
||||
size_t start = 0;
|
||||
for (; start < m_argument.size() && m_allowed_flag_prefix->contains(m_argument[start]); start++)
|
||||
{
|
||||
}
|
||||
|
||||
m_flag_section = {m_argument.data(), start};
|
||||
m_name_section = {m_argument.data() + start, m_argument.size() - start};
|
||||
}
|
||||
|
||||
std::string_view m_argument;
|
||||
std::string_view m_flag_section;
|
||||
std::string_view m_name_section;
|
||||
const hashset_t<char>* m_allowed_flag_prefix;
|
||||
};
|
||||
|
||||
class argument_consumer_t
|
||||
{
|
||||
public:
|
||||
explicit argument_consumer_t(const span<argument_string_t>& args): m_absolute_begin(args.data()), m_begin(args.data() + 1),
|
||||
m_end(args.data() + args.size())
|
||||
{
|
||||
BLT_ASSERT(!args.empty() &&
|
||||
"Argument consumer must have at least one argument allocated to it. First argument is always assumed to be program");
|
||||
}
|
||||
|
||||
[[nodiscard]] const argument_string_t& absolute_first() const
|
||||
{
|
||||
return *m_absolute_begin;
|
||||
}
|
||||
|
||||
[[nodiscard]] argument_string_t peek(const i32 offset = 0) const
|
||||
{
|
||||
return *(m_begin + offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] argument_string_t r_peek(const i32 offset = 0) const
|
||||
{
|
||||
return *(m_end - 1 - offset);
|
||||
}
|
||||
|
||||
argument_string_t consume()
|
||||
{
|
||||
return *(m_begin++);
|
||||
}
|
||||
|
||||
argument_string_t r_consume()
|
||||
{
|
||||
return *(--m_end);
|
||||
}
|
||||
|
||||
[[nodiscard]] i32 remaining() const
|
||||
{
|
||||
return static_cast<i32>(size());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool can_consume(const i32 amount = 0) const
|
||||
{
|
||||
return amount < remaining();
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] ptrdiff_t size() const
|
||||
{
|
||||
return m_end - m_begin;
|
||||
}
|
||||
|
||||
argument_string_t* m_absolute_begin;
|
||||
argument_string_t* m_begin;
|
||||
argument_string_t* m_end;
|
||||
};
|
||||
|
||||
class argument_storage_t
|
||||
{
|
||||
friend argument_parser_t;
|
||||
friend argument_subparser_t;
|
||||
friend argument_builder_t;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
[[nodiscard]] const T& get(const std::string_view key) const
|
||||
{
|
||||
return std::get<T>(m_data.at(key));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& get(const std::string_view key) const
|
||||
{
|
||||
return std::get<std::string>(m_data.at(key));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const std::string_view key) const
|
||||
{
|
||||
return m_data.find(key) != m_data.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
private:
|
||||
void add(const argument_storage_t& values)
|
||||
{
|
||||
for (const auto& value : values.m_data)
|
||||
m_data.insert(value);
|
||||
}
|
||||
|
||||
hashmap_t<std::string, detail::arg_data_t> m_data;
|
||||
};
|
||||
|
||||
class argument_builder_t
|
||||
{
|
||||
friend argument_parser_t;
|
||||
|
||||
public:
|
||||
argument_builder_t()
|
||||
{
|
||||
m_dest_func = [](const std::string_view dest, argument_storage_t& storage, std::string_view value)
|
||||
{
|
||||
storage.m_data.emplace(std::string{dest}, std::string{value});
|
||||
};
|
||||
m_dest_vec_func = [](const std::string_view dest, argument_storage_t& storage, const std::vector<std::string>& values)
|
||||
{
|
||||
storage.m_data.emplace(std::string{dest}, values);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
argument_builder_t& as_type()
|
||||
{
|
||||
static_assert(detail::arg_data_helper_t<T>::template is_type_stored_v<T>, "Type is not valid to be stored/converted as an argument");
|
||||
m_dest_func = [](const std::string_view dest, argument_storage_t& storage, std::string_view value)
|
||||
{
|
||||
storage.m_data.emplace(std::string{dest}, detail::arg_string_converter_t<T>::convert(value));
|
||||
};
|
||||
m_dest_vec_func = [](const std::string_view dest, argument_storage_t& storage, const std::vector<std::string>& values)
|
||||
{
|
||||
if (storage.m_data.contains(dest))
|
||||
{
|
||||
auto& data = storage.m_data[dest];
|
||||
if (!std::holds_alternative<std::vector<T>>(data))
|
||||
throw detail::type_error("Invalid type conversion. Trying to add type " + blt::type_string<T>() +
|
||||
" but this does not match existing type index '" + std::to_string(data.index()) + "'!");
|
||||
auto& converted_values = std::get<std::vector<T>>(data);
|
||||
for (const auto& value : values)
|
||||
converted_values.push_back(detail::arg_string_converter_t<T>::convert(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<T> converted_values;
|
||||
for (const auto& value : values)
|
||||
converted_values.push_back(detail::arg_string_converter_t<T>::convert(value));
|
||||
storage.m_data.emplace(std::string{dest}, std::move(converted_values));
|
||||
}
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& make_flag()
|
||||
{
|
||||
return set_action(action_t::STORE_TRUE);
|
||||
}
|
||||
|
||||
argument_builder_t& set_action(action_t action);
|
||||
|
||||
argument_builder_t& set_required(const bool required)
|
||||
{
|
||||
m_required = required;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_nargs(const nargs_v nargs)
|
||||
{
|
||||
m_nargs = nargs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_metavar(const std::string& metavar)
|
||||
{
|
||||
m_metavar = metavar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_help(const std::string& help)
|
||||
{
|
||||
m_help = help;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_choices(const std::vector<std::string>& choices)
|
||||
{
|
||||
m_choices = hashset_t<std::string>{};
|
||||
for (const auto& choice : choices)
|
||||
m_choices->emplace(choice);
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_choices(const hashset_t<std::string>& choices)
|
||||
{
|
||||
m_choices = choices;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
argument_builder_t& set_choices(Args&&... args)
|
||||
{
|
||||
m_choices = hashset_t<std::string>{};
|
||||
((m_choices->emplace(detail::ensure_is_string(std::forward<Args>(args)))), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_default(const detail::arg_data_t& default_value)
|
||||
{
|
||||
m_default_value = default_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_const(const detail::arg_data_t& const_value)
|
||||
{
|
||||
m_const_value = const_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_builder_t& set_dest(const std::string_view& dest)
|
||||
{
|
||||
m_dest = dest;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
action_t m_action = action_t::STORE;
|
||||
bool m_required = false; // do we require this argument to be provided as an argument?
|
||||
nargs_v m_nargs = 1; // number of arguments to consume
|
||||
std::optional<std::string> m_metavar; // variable name to be used in the help string
|
||||
std::optional<std::string> m_help; // help string to be used in the help string
|
||||
std::optional<hashset_t<std::string>> m_choices; // optional allowed choices for this argument
|
||||
std::optional<detail::arg_data_t> m_default_value;
|
||||
std::optional<detail::arg_data_t> m_const_value;
|
||||
std::optional<std::string> m_dest;
|
||||
// dest, storage, value input
|
||||
std::function<void(std::string_view, argument_storage_t&, std::string_view)> m_dest_func;
|
||||
// dest, storage, value input
|
||||
std::function<void(std::string_view, argument_storage_t&, const std::vector<std::string>& values)> m_dest_vec_func;
|
||||
};
|
||||
|
||||
class argument_positional_storage_t
|
||||
{
|
||||
public:
|
||||
explicit argument_positional_storage_t(std::vector<std::pair<std::string, argument_builder_t>> storage): positional_arguments(
|
||||
std::move(storage))
|
||||
{
|
||||
}
|
||||
|
||||
argument_builder_t& peek()
|
||||
{
|
||||
return positional_arguments[current_positional].second;
|
||||
}
|
||||
|
||||
argument_builder_t& next()
|
||||
{
|
||||
return positional_arguments[current_positional++].second;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_positional() const
|
||||
{
|
||||
return current_positional < positional_arguments.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto remaining() const
|
||||
{
|
||||
return iterate(positional_arguments).skip(current_positional);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, argument_builder_t>> positional_arguments;
|
||||
size_t current_positional = 0;
|
||||
};
|
||||
|
||||
class argument_parser_t
|
||||
{
|
||||
friend argument_subparser_t;
|
||||
explicit argument_parser_t(const argument_subparser_t* parent): m_parent(parent)
|
||||
{
|
||||
}
|
||||
public:
|
||||
explicit argument_parser_t(const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {},
|
||||
const std::optional<std::string_view> version = {},
|
||||
const std::optional<std::string_view> usage = {}, const std::optional<std::string_view> name = {}):
|
||||
m_name(name), m_usage(usage), m_description(description), m_epilogue(epilogue), m_version(version)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Aliases>
|
||||
argument_builder_t& add_flag(const std::string_view arg, Aliases... aliases)
|
||||
{
|
||||
static_assert(
|
||||
std::conjunction_v<std::disjunction<std::is_convertible<Aliases, std::string>, std::is_constructible<
|
||||
std::string, Aliases>>...>,
|
||||
"Arguments must be of type string_view, convertible to string_view or be string_view constructable");
|
||||
m_argument_builders.emplace_back(std::make_unique<argument_builder_t>());
|
||||
m_argument_builders.back()->set_dest(arg);
|
||||
m_flag_arguments.emplace(arg, m_argument_builders.back().get());
|
||||
(m_flag_arguments.emplace(aliases, m_argument_builders.back().get()), ...);
|
||||
return *m_argument_builders.back().get();
|
||||
}
|
||||
|
||||
argument_builder_t& add_positional(const std::string_view arg)
|
||||
{
|
||||
auto& b = m_positional_arguments.emplace_back(arg, argument_builder_t{}).second;
|
||||
b.set_dest(std::string{arg});
|
||||
b.set_required(true);
|
||||
b.set_nargs(1);
|
||||
return b;
|
||||
}
|
||||
|
||||
argument_subparser_t* add_subparser(std::string_view dest);
|
||||
|
||||
argument_parser_t& with_help()
|
||||
{
|
||||
add_flag("--help", "-h").set_action(action_t::HELP).set_help("Show this help menu and exit");
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_parser_t& with_version()
|
||||
{
|
||||
add_flag("--version").set_action(action_t::VERSION);
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_storage_t parse(argument_consumer_t& consumer); // NOLINT
|
||||
|
||||
argument_storage_t parse(const std::vector<std::string_view>& args)
|
||||
{
|
||||
std::vector<argument_string_t> arg_strings;
|
||||
arg_strings.reserve(args.size());
|
||||
for (const auto& arg : args)
|
||||
arg_strings.emplace_back(arg, allowed_flag_prefixes);
|
||||
argument_consumer_t consumer{arg_strings};
|
||||
return parse(consumer);
|
||||
}
|
||||
|
||||
argument_storage_t parse(const std::vector<std::string>& args)
|
||||
{
|
||||
std::vector<argument_string_t> arg_strings;
|
||||
arg_strings.reserve(args.size());
|
||||
for (const auto& arg : args)
|
||||
arg_strings.emplace_back(arg, allowed_flag_prefixes);
|
||||
argument_consumer_t consumer{arg_strings};
|
||||
return parse(consumer);
|
||||
}
|
||||
|
||||
argument_storage_t parse(const int argc, const char** argv)
|
||||
{
|
||||
std::vector<argument_string_t> arg_strings;
|
||||
arg_strings.reserve(argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
arg_strings.emplace_back(argv[i], allowed_flag_prefixes);
|
||||
argument_consumer_t consumer{arg_strings};
|
||||
return parse(consumer);
|
||||
}
|
||||
|
||||
void print_help();
|
||||
|
||||
void print_usage();
|
||||
|
||||
void print_version() const;
|
||||
|
||||
argument_parser_t& set_name(const std::optional<std::string>& name)
|
||||
{
|
||||
m_name = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
argument_parser_t& set_usage(const std::optional<std::string>& usage)
|
||||
{
|
||||
m_usage = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string>& get_usage() const
|
||||
{
|
||||
return m_usage;
|
||||
}
|
||||
|
||||
argument_parser_t& set_description(const std::optional<std::string>& description)
|
||||
{
|
||||
m_description = description;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string>& get_description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
argument_parser_t& set_epilogue(const std::optional<std::string>& epilogue)
|
||||
{
|
||||
m_epilogue = epilogue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string>& get_epilogue() const
|
||||
{
|
||||
return m_epilogue;
|
||||
}
|
||||
|
||||
argument_parser_t& set_version(const std::optional<std::string>& version)
|
||||
{
|
||||
m_epilogue = version;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string>& get_version() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
[[nodiscard]] const hashset_t<char>& get_allowed_flag_prefixes() const
|
||||
{
|
||||
return allowed_flag_prefixes;
|
||||
}
|
||||
|
||||
private:
|
||||
void handle_compound_flags(hashset_t<std::string>& found_flags, argument_storage_t& parsed_args, argument_consumer_t& consumer,
|
||||
const argument_string_t& arg);
|
||||
void parse_flag(argument_storage_t& parsed_args, argument_consumer_t& consumer, std::string_view arg);
|
||||
void parse_positional(argument_storage_t& parsed_args, argument_consumer_t& consumer, argument_positional_storage_t& storage,
|
||||
std::string_view arg);
|
||||
static void handle_missing_and_default_args(hashmap_t<std::string_view, argument_builder_t*>& arguments,
|
||||
const hashset_t<std::string>& found, argument_storage_t& parsed_args, std::string_view type);
|
||||
static expected<std::vector<std::string>, std::string> consume_until_flag_or_end(argument_consumer_t& consumer,
|
||||
hashset_t<std::string>* allowed_choices);
|
||||
static std::vector<std::string> consume_argc(i32 argc, argument_consumer_t& consumer, hashset_t<std::string>* allowed_choices,
|
||||
std::string_view arg);
|
||||
|
||||
std::optional<std::string> m_name;
|
||||
std::optional<std::string> m_usage;
|
||||
std::optional<std::string> m_description;
|
||||
std::optional<std::string> m_epilogue;
|
||||
std::optional<std::string> m_version;
|
||||
const argument_subparser_t* m_parent = nullptr;
|
||||
std::vector<std::pair<std::string_view, argument_subparser_t>> m_subparsers;
|
||||
std::vector<std::unique_ptr<argument_builder_t>> m_argument_builders;
|
||||
hashmap_t<std::string_view, argument_builder_t*> m_flag_arguments;
|
||||
std::vector<std::pair<std::string, argument_builder_t>> m_positional_arguments;
|
||||
hashset_t<char> allowed_flag_prefixes = {'-', '+', '/'};
|
||||
};
|
||||
|
||||
class argument_subparser_t
|
||||
{
|
||||
friend argument_parser_t;
|
||||
public:
|
||||
explicit argument_subparser_t(const argument_parser_t& parent): m_parent(&parent)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Aliases>
|
||||
argument_parser_t* add_parser(const std::string_view name, Aliases... aliases)
|
||||
{
|
||||
static_assert(
|
||||
std::conjunction_v<std::disjunction<std::is_convertible<Aliases, std::string_view>, std::is_constructible<
|
||||
std::string_view, Aliases>>...>,
|
||||
"Arguments must be of type string_view, convertible to string_view or be string_view constructable");
|
||||
m_parsers.emplace_back(new argument_parser_t{this});
|
||||
m_aliases[name] = m_parsers.back().get();
|
||||
((m_aliases[std::string_view{aliases}] = m_parsers.back().get()), ...);
|
||||
return m_parsers.back().get();
|
||||
}
|
||||
|
||||
argument_subparser_t* set_help(const std::optional<std::string>& help)
|
||||
{
|
||||
m_help = help;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the next argument using the provided argument consumer.
|
||||
*
|
||||
* This function uses an argument consumer to extract and process the next argument.
|
||||
* If the argument is a flag or if it cannot be matched against the available parsers,
|
||||
* an exception is thrown.
|
||||
*
|
||||
* @param consumer Reference to an argument_consumer_t object, which handles argument parsing.
|
||||
* The consumer provides the next argument to be parsed.
|
||||
*
|
||||
* @throws detail::subparse_error If the argument is a flag or does not match any known parser.
|
||||
*/
|
||||
std::pair<argument_string_t, argument_storage_t> parse(argument_consumer_t& consumer); // NOLINT
|
||||
|
||||
private:
|
||||
[[nodiscard]] hashmap_t<argument_parser_t*, std::vector<std::string_view>> get_allowed_strings() const;
|
||||
|
||||
// annoying compatability because im lazy
|
||||
static std::vector<std::vector<std::string_view>> to_vec(const hashmap_t<argument_parser_t*, std::vector<std::string_view>>& map);
|
||||
|
||||
const argument_parser_t* m_parent;
|
||||
std::optional<std::string> m_last_parsed_parser; // bad hack
|
||||
std::optional<std::string> m_help;
|
||||
std::vector<std::unique_ptr<argument_parser_t>> m_parsers;
|
||||
hashmap_t<std::string_view, argument_parser_t*> m_aliases;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_PARSE_ARGPARSE_V2_H
|
|
@ -27,7 +27,7 @@
|
|||
#include <blt/std/hashmap.h>
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/std/expected.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <variant>
|
||||
|
||||
namespace blt
|
||||
|
@ -223,8 +223,12 @@ namespace blt
|
|||
|
||||
std::string_view from_last()
|
||||
{
|
||||
if (!hasNext())
|
||||
return std::string_view(&raw_string[last_read_index], raw_string.size() - last_read_index);
|
||||
if (!hasNext()) {
|
||||
auto size = raw_string.size() - last_read_index;
|
||||
if (size > 0)
|
||||
return std::string_view(&raw_string[last_read_index], size);
|
||||
return "";
|
||||
}
|
||||
auto token = storage[getCurrentIndex()];
|
||||
auto len = ((&token.token.back()) - &raw_string[last_read_index]);
|
||||
auto str = std::string_view(&raw_string[last_read_index], len);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <blt/std/queue.h>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -54,7 +54,7 @@ namespace blt::profiling {
|
|||
profile getProfile(const std::string& profileName);
|
||||
|
||||
void printProfile(
|
||||
const std::string& profileName, logging::log_level loggingLevel = logging::log_level::NONE,
|
||||
const std::string& profileName, logging::log_level_t loggingLevel = logging::log_level_t::NONE,
|
||||
bool averageHistory = false
|
||||
);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ namespace blt
|
|||
void endInterval(interval_t* interval);
|
||||
|
||||
void printProfile(profile_t& profiler, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
|
||||
sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE);
|
||||
sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE);
|
||||
|
||||
void writeProfile(std::ostream& stream, profile_t& profiler,
|
||||
std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
|
||||
|
@ -113,7 +113,7 @@ namespace blt
|
|||
void endInterval(const std::string& profile_name, const std::string& interval_name);
|
||||
|
||||
void printProfile(const std::string& profile_name, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
|
||||
sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE);
|
||||
sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE);
|
||||
|
||||
void writeProfile(std::ostream& stream, const std::string& profile_name,
|
||||
std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,7 @@
|
|||
#include <blt/std/mmap.h>
|
||||
#include <blt/compatibility.h>
|
||||
#include <stdexcept>
|
||||
#include "logging.h"
|
||||
#include "blt/logging/logging.h"
|
||||
#include <cstdlib>
|
||||
#include <atomic>
|
||||
|
||||
|
|
|
@ -375,12 +375,12 @@ namespace blt
|
|||
};
|
||||
|
||||
template<typename T, typename E>
|
||||
class expected<T, E, false>
|
||||
class expected<T, E, false> : public expected<T, E, true>
|
||||
{
|
||||
public:
|
||||
using expected<T, E, true>::expected;
|
||||
|
||||
constexpr expected(const expected<T, E, false>& copy) = delete;
|
||||
constexpr expected(const expected& copy) = delete;
|
||||
|
||||
expected& operator=(const expected& copy) = delete;
|
||||
};
|
||||
|
|
|
@ -1,929 +0,0 @@
|
|||
/*
|
||||
* Created by Brett on 20/07/23.
|
||||
* Licensed under GNU General Public License V3.0
|
||||
* See LICENSE file for license detail
|
||||
*/
|
||||
|
||||
#ifndef BLT_TESTS_LOGGING2_H
|
||||
#define BLT_TESTS_LOGGING2_H
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <blt/config.h>
|
||||
#include <blt/std/types.h>
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
|
||||
namespace ansi
|
||||
{
|
||||
inline auto ESC(std::string_view str)
|
||||
{
|
||||
return std::string("\033") += str;
|
||||
}
|
||||
|
||||
inline const auto CUR_HOME = ESC("[H");
|
||||
|
||||
inline auto CUR_MOVE(blt::size_t line, blt::size_t column)
|
||||
{
|
||||
return ESC("[{" + std::to_string(line) + "};{" + std::to_string(column) + "}H");
|
||||
}
|
||||
|
||||
inline auto CUR_UP(blt::size_t lines)
|
||||
{
|
||||
return ESC("[" + std::to_string(lines) + "A");
|
||||
}
|
||||
|
||||
inline auto CUR_DOWN(blt::size_t lines)
|
||||
{
|
||||
return ESC("[" + std::to_string(lines) + "B");
|
||||
}
|
||||
|
||||
inline auto CUR_RIGHT(blt::size_t columns)
|
||||
{
|
||||
return ESC("[" + std::to_string(columns) + "C");
|
||||
}
|
||||
|
||||
inline auto CUR_LEFT(blt::size_t columns)
|
||||
{
|
||||
return ESC("[" + std::to_string(columns) + "D");
|
||||
}
|
||||
|
||||
inline auto CUR_BEGIN_NEXT(blt::size_t lines_down)
|
||||
{
|
||||
return ESC("[" + std::to_string(lines_down) + "E");
|
||||
}
|
||||
|
||||
inline auto CUR_BEGIN_PREV(blt::size_t lines_up)
|
||||
{
|
||||
return ESC("[" + std::to_string(lines_up) + "F");
|
||||
}
|
||||
|
||||
inline auto CUR_COLUMN(blt::size_t column)
|
||||
{
|
||||
return ESC("[" + std::to_string(column) + "G");
|
||||
}
|
||||
|
||||
inline auto CUR_POS()
|
||||
{
|
||||
return ESC("[6n");
|
||||
}
|
||||
|
||||
inline auto CUR_SCROLL_UP()
|
||||
{
|
||||
return ESC(" M");
|
||||
}
|
||||
|
||||
inline auto CUR_SAVE_DEC()
|
||||
{
|
||||
return ESC(" 7");
|
||||
}
|
||||
|
||||
inline auto CUR_LOAD_DEC()
|
||||
{
|
||||
return ESC(" 8");
|
||||
}
|
||||
|
||||
inline auto CUR_SAVE_SCO()
|
||||
{
|
||||
return ESC("[s");
|
||||
}
|
||||
|
||||
inline auto CUR_LOAD_SCO()
|
||||
{
|
||||
return ESC("[u");
|
||||
}
|
||||
|
||||
inline auto RESET = ESC("[0m");
|
||||
inline auto BOLD = "1";
|
||||
inline auto RESET_BOLD = "22";
|
||||
inline auto DIM = "2";
|
||||
inline auto RESET_DIM = "22";
|
||||
inline auto ITALIC = "3";
|
||||
inline auto RESET_ITALIC = "23";
|
||||
inline auto UNDERLINE = "4";
|
||||
inline auto RESET_UNDERLINE = "24";
|
||||
inline auto BLINKING = "5";
|
||||
inline auto RESET_BLINKING = "25";
|
||||
inline auto INVERSE = "7";
|
||||
inline auto RESET_INVERSE = "27";
|
||||
inline auto HIDDEN = "8";
|
||||
inline auto RESET_HIDDEN = "28";
|
||||
inline auto STRIKETHROUGH = "9";
|
||||
inline auto RESET_STRIKETHROUGH = "29";
|
||||
|
||||
inline auto COLOR_DEFAULT = "39";
|
||||
inline auto BACKGROUND_DEFAULT = "49";
|
||||
|
||||
inline auto BLACK = "30";
|
||||
inline auto RED = "31";
|
||||
inline auto GREEN = "32";
|
||||
inline auto YELLOW = "33";
|
||||
inline auto BLUE = "34";
|
||||
inline auto MAGENTA = "35";
|
||||
inline auto CYAN = "36";
|
||||
inline auto WHITE = "37";
|
||||
inline auto BLACK_BACKGROUND = "40";
|
||||
inline auto RED_BACKGROUND = "41";
|
||||
inline auto GREEN_BACKGROUND = "42";
|
||||
inline auto YELLOW_BACKGROUND = "43";
|
||||
inline auto BLUE_BACKGROUND = "44";
|
||||
inline auto MAGENTA_BACKGROUND = "45";
|
||||
inline auto CYAN_BACKGROUND = "46";
|
||||
inline auto WHITE_BACKGROUND = "47";
|
||||
|
||||
inline auto BRIGHT_BLACK = "90";
|
||||
inline auto BRIGHT_RED = "91";
|
||||
inline auto BRIGHT_GREEN = "92";
|
||||
inline auto BRIGHT_YELLOW = "93";
|
||||
inline auto BRIGHT_BLUE = "94";
|
||||
inline auto BRIGHT_MAGENTA = "95";
|
||||
inline auto BRIGHT_CYAN = "96";
|
||||
inline auto BRIGHT_WHITE = "97";
|
||||
inline auto BRIGHT_BLACK_BACKGROUND = "100";
|
||||
inline auto BRIGHT_RED_BACKGROUND = "101";
|
||||
inline auto BRIGHT_GREEN_BACKGROUND = "102";
|
||||
inline auto BRIGHT_YELLOW_BACKGROUND = "103";
|
||||
inline auto BRIGHT_BLUE_BACKGROUND = "104";
|
||||
inline auto BRIGHT_MAGENTA_BACKGROUND = "105";
|
||||
inline auto BRIGHT_CYAN_BACKGROUND = "106";
|
||||
inline auto BRIGHT_WHITE_BACKGROUND = "107";
|
||||
|
||||
template<typename... Args>
|
||||
inline auto make_color(Args... colors)
|
||||
{
|
||||
std::string mode;
|
||||
((mode += std::string(colors) + ";"), ...);
|
||||
return ESC("[" + mode.substr(0, mode.size() - 1) + "m");
|
||||
}
|
||||
}
|
||||
|
||||
enum class log_level
|
||||
{
|
||||
// default
|
||||
NONE,
|
||||
// low level
|
||||
TRACE0, TRACE1, TRACE2, TRACE3,
|
||||
// normal
|
||||
TRACE, DEBUG, INFO,
|
||||
// errors
|
||||
WARN, ERROR, FATAL,
|
||||
};
|
||||
|
||||
struct tag_func_param
|
||||
{
|
||||
blt::logging::log_level level;
|
||||
const std::string& file, line, raw_string, formatted_string;
|
||||
};
|
||||
|
||||
struct tag
|
||||
{
|
||||
// tag without the ${{ or }}
|
||||
std::string tag;
|
||||
// function to run: log level, file, line, and raw user input string are provided
|
||||
std::function<std::string(const tag_func_param&)> func;
|
||||
};
|
||||
|
||||
struct log_format
|
||||
{
|
||||
/**
|
||||
* the log output format is the string which will be used to create the log output string
|
||||
*
|
||||
* Available tags:
|
||||
* - ${{YEAR}} // current year
|
||||
* - ${{MONTH}} // current month
|
||||
* - ${{DAY}} // current day
|
||||
* - ${{HOUR}} // current hour
|
||||
* - ${{MINUTE}} // current minute
|
||||
* - ${{SECOND}} // current second
|
||||
* - ${{MS}} // current unix time
|
||||
* - ${{NS}} // current ns from the high resolution system timer
|
||||
* - ${{ISO_YEAR}} // ISO formatted 'year-month-day' in a single variable
|
||||
* - ${{TIME}} // 'hour:minute:second' formatted string in a single variable
|
||||
* - ${{FULL_TIME}} // 'year-month-day hour:minute:second' in a single variable
|
||||
* - ${{LF}} // log level color (ANSI color code)
|
||||
* - ${{ER}} // Error red
|
||||
* - ${{CNR}} // conditional error red (only outputs if log level is an error!)
|
||||
* - ${{RC}} // ANSI color reset
|
||||
* - ${{LOG_LEVEL}} // current log level
|
||||
* - ${{THREAD_NAME}} // current thread name, NOTE: thread names must be set by calling "setThreadName()" from the thread in question!
|
||||
* - ${{FILE}} // file from which the macro is invoked
|
||||
* - ${{LINE}} // line in the from which the macro is invoked
|
||||
* - ${{RAW_STR}} // raw user string without formatting applied (NOTE: format args are not provided!)
|
||||
* - ${{STR}} // the user supplied string (format applied!)
|
||||
*/
|
||||
std::string logOutputFormat = "\033[94m[${{TIME}}]${{RC}} ${{LF}}[${{LOG_LEVEL}}]${{RC}} \033[35m(${{FILE}}:${{LINE}})${{RC}} ${{CNR}}${{STR}}${{RC}}\n";
|
||||
std::string levelNames[11] = {"STDOUT", "TRACE0", "TRACE1", "TRACE2", "TRACE3", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
||||
std::string levelColors[11] = {"\033[0m", "\033[22;97m", "\033[97m", "\033[97m", "\033[97m", "\033[97m", "\033[36m", "\033[92m", "\033[93m",
|
||||
"\033[91m", "\033[97;41m"};
|
||||
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
|
||||
bool printFullFileName = false;
|
||||
// the logging lib will keep track of the largest line found so far and try to keep the spacing accordingly
|
||||
// this is not thread safe!
|
||||
bool ensureAlignment = false;
|
||||
// should we log to file?
|
||||
bool logToFile = false;
|
||||
// should we log to console?
|
||||
bool logToConsole = true;
|
||||
// where should we log? (empty for current binary directory) should end in a / if not empty!
|
||||
std::string logFilePath;
|
||||
// logs to a file called $fileName_$count.log where count is the number of rollover files
|
||||
// this accepts any of the macros above, using level names and colors should work but isn't supported.
|
||||
std::string logFileName = "${{ISO_YEAR}}";
|
||||
// default limit on file size: 10mb;
|
||||
size_t logMaxFileSize = 1024 * 1024 * 10;
|
||||
/**
|
||||
* Variables below this line should never be changed by the user!
|
||||
*/
|
||||
// the current alignment width found (you shouldn't chance this variable!)
|
||||
size_t currentWidth = 0;
|
||||
// current number of file roll-overs. you shouldn't change this either.
|
||||
size_t currentRollover = 0;
|
||||
std::string lastFile;
|
||||
};
|
||||
|
||||
struct logger
|
||||
{
|
||||
log_level level;
|
||||
const char* file;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct empty_logger
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args);
|
||||
|
||||
void log_stream_internal(const std::string& str, const logger& logger);
|
||||
|
||||
template<typename T>
|
||||
inline std::string to_string_stream(const T& t)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << t;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static void log_stream(const T& t, const logger& logger)
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
|
||||
{
|
||||
log_stream_internal(std::to_string(t), logger);
|
||||
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char*>)
|
||||
{
|
||||
log_stream_internal(t, logger);
|
||||
} else
|
||||
{
|
||||
log_stream_internal(to_string_stream(t), logger);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void log(T t, log_level level, const char* file, int line, ...)
|
||||
{
|
||||
std::va_list args;
|
||||
va_start(args, line);
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
log_internal(std::to_string(t), level, file, line, args);
|
||||
} else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
log_internal(t, level, file, line, args);
|
||||
} else if constexpr (std::is_same_v<T, const char*>)
|
||||
{
|
||||
log_internal(std::string(t), level, file, line, args);
|
||||
} else
|
||||
{
|
||||
log_internal(to_string_stream(t), level, file, line, args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline const blt::logging::logger& operator<<(const blt::logging::logger& out, const T& t)
|
||||
{
|
||||
log_stream(t, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline const blt::logging::empty_logger& operator<<(const blt::logging::empty_logger& out, const T&)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
void flush();
|
||||
|
||||
void newline();
|
||||
|
||||
void setThreadName(const std::string& name);
|
||||
|
||||
void setLogFormat(const log_format& format);
|
||||
|
||||
void setLogColor(log_level level, const std::string& newFormat);
|
||||
|
||||
void setLogName(log_level level, const std::string& newFormat);
|
||||
|
||||
void setLogOutputFormat(const std::string& newFormat);
|
||||
|
||||
void setLogToFile(bool shouldLogToFile);
|
||||
|
||||
void setLogToConsole(bool shouldLogToConsole);
|
||||
|
||||
void setLogPath(const std::string& path);
|
||||
|
||||
void setLogFileName(const std::string& fileName);
|
||||
|
||||
void setMaxFileSize(size_t fileSize);
|
||||
}
|
||||
|
||||
//#define BLT_LOGGING_IMPLEMENTATION
|
||||
#ifdef BLT_LOGGING_IMPLEMENTATION
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdarg>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#if defined(CXX17_FILESYSTEM) || defined (CXX17_FILESYSTEM_LIBFS)
|
||||
#include <filesystem>
|
||||
#elif defined(CXX11_EXP_FILESYSTEM) || defined (CXX11_EXP_FILESYSTEM_LIBFS)
|
||||
#include <experimental/filesystem>
|
||||
#else
|
||||
#include <filesystem>
|
||||
#endif
|
||||
#include <ios>
|
||||
#include <fstream>
|
||||
|
||||
template<typename K, typename V>
|
||||
using hashmap = std::unordered_map<K, V>;
|
||||
|
||||
namespace blt::logging {
|
||||
|
||||
/**
|
||||
* Used to store fast associations between built in tags and their respective values
|
||||
*/
|
||||
class tag_map {
|
||||
private:
|
||||
tag* tags;
|
||||
size_t size;
|
||||
|
||||
[[nodiscard]] static inline size_t hash(const tag& t) {
|
||||
size_t h = t.tag[1] * 3 - t.tag[0];
|
||||
return h - 100;
|
||||
}
|
||||
|
||||
// TODO: fix
|
||||
void expand() {
|
||||
auto newSize = size * 2;
|
||||
auto newTags = new tag[newSize];
|
||||
for (size_t i = 0; i < size; i++)
|
||||
newTags[i] = tags[i];
|
||||
delete[] tags;
|
||||
tags = newTags;
|
||||
size = newSize;
|
||||
}
|
||||
public:
|
||||
tag_map(std::initializer_list<tag> initial_tags){
|
||||
size_t max = 0;
|
||||
for (const auto& t : initial_tags)
|
||||
max = std::max(max, hash(t));
|
||||
tags = new tag[(size = max+1)];
|
||||
for (const auto& t : initial_tags)
|
||||
insert(t);
|
||||
}
|
||||
tag_map(const tag_map& copy) {
|
||||
tags = new tag[(size = copy.size)];
|
||||
for (size_t i = 0; i < size; i++)
|
||||
tags[i] = copy.tags[i];
|
||||
}
|
||||
|
||||
void insert(const tag& t) {
|
||||
auto h = hash(t);
|
||||
//if (h > size)
|
||||
// expand();
|
||||
if (!tags[h].tag.empty())
|
||||
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
|
||||
tags[h] = t;
|
||||
}
|
||||
|
||||
tag& operator[](const std::string& name) const {
|
||||
auto h = hash(tag{name, nullptr});
|
||||
if (h > size)
|
||||
std::cerr << "Tag out of bounds";
|
||||
return tags[h];
|
||||
}
|
||||
|
||||
~tag_map(){
|
||||
delete[] tags;
|
||||
tags = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class LogFileWriter {
|
||||
private:
|
||||
std::string m_path;
|
||||
std::fstream* output = nullptr;
|
||||
public:
|
||||
explicit LogFileWriter() = default;
|
||||
|
||||
void writeLine(const std::string& path, const std::string& line){
|
||||
if (path != m_path || output == nullptr){
|
||||
clear();
|
||||
delete output;
|
||||
output = new std::fstream(path, std::ios::out | std::ios::app);
|
||||
if (!output->good()){
|
||||
throw std::runtime_error("Unable to open console filestream!\n");
|
||||
}
|
||||
}
|
||||
if (!output->good()){
|
||||
std::cerr << "There has been an error in the logging file stream!\n";
|
||||
output->clear();
|
||||
}
|
||||
*output << line;
|
||||
}
|
||||
|
||||
void clear(){
|
||||
if (output != nullptr) {
|
||||
try {
|
||||
output->flush();
|
||||
output->close();
|
||||
} catch (std::exception& e){
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~LogFileWriter() {
|
||||
clear();
|
||||
delete(output);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
#define BLT_NOW() auto t = std::time(nullptr); tm now{}; localtime_s(&now, &t)
|
||||
#else
|
||||
#define BLT_NOW() auto t = std::time(nullptr); auto now_ptr = std::localtime(&t); auto& now = *now_ptr
|
||||
#endif
|
||||
|
||||
//#define BLT_NOW() auto t = std::time(nullptr); tm now; localtime_s(&now, &t); //auto now = std::localtime(&t)
|
||||
#define BLT_ISO_YEAR(S) auto S = std::to_string(now.tm_year + 1900); \
|
||||
S += '-'; \
|
||||
S += ensureHasDigits(now.tm_mon+1, 2); \
|
||||
S += '-'; \
|
||||
S += ensureHasDigits(now.tm_mday, 2);
|
||||
#define BLT_CUR_TIME(S) auto S = ensureHasDigits(now.tm_hour, 2); \
|
||||
S += ':'; \
|
||||
S += ensureHasDigits(now.tm_min, 2); \
|
||||
S += ':'; \
|
||||
S += ensureHasDigits(now.tm_sec, 2);
|
||||
|
||||
static inline std::string ensureHasDigits(int current, int digits) {
|
||||
std::string asString = std::to_string(current);
|
||||
auto length = digits - asString.length();
|
||||
if (length <= 0)
|
||||
return asString;
|
||||
std::string zeros;
|
||||
zeros.reserve(length);
|
||||
for (unsigned int i = 0; i < length; i++){
|
||||
zeros += '0';
|
||||
}
|
||||
return zeros + asString;
|
||||
}
|
||||
|
||||
log_format loggingFormat {};
|
||||
hashmap<std::thread::id, std::string> loggingThreadNames;
|
||||
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
|
||||
LogFileWriter writer;
|
||||
|
||||
const std::unique_ptr<tag_map> tagMap = std::make_unique<tag_map>(tag_map{
|
||||
{"YEAR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return std::to_string(now.tm_year);
|
||||
}},
|
||||
{"MONTH", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now.tm_mon+1, 2);
|
||||
}},
|
||||
{"DAY", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now.tm_mday, 2);
|
||||
}},
|
||||
{"HOUR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now.tm_hour, 2);
|
||||
}},
|
||||
{"MINUTE", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now.tm_min, 2);
|
||||
}},
|
||||
{"SECOND", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now.tm_sec, 2);
|
||||
}},
|
||||
{"MS", [](const tag_func_param&) -> std::string {
|
||||
return std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
|
||||
);
|
||||
}},
|
||||
{"NS", [](const tag_func_param&) -> std::string {
|
||||
return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
|
||||
);
|
||||
}},
|
||||
{"ISO_YEAR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_ISO_YEAR(returnStr);
|
||||
return returnStr;
|
||||
}},
|
||||
{"TIME", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_CUR_TIME(returnStr);
|
||||
return returnStr;
|
||||
}},
|
||||
{"FULL_TIME", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_ISO_YEAR(ISO);
|
||||
BLT_CUR_TIME(TIME);
|
||||
ISO += ' ';
|
||||
ISO += TIME;
|
||||
return ISO;
|
||||
}},
|
||||
{"LF", [](const tag_func_param& f) -> std::string {
|
||||
return loggingFormat.levelColors[(int)f.level];
|
||||
}},
|
||||
{"ER", [](const tag_func_param&) -> std::string {
|
||||
return loggingFormat.levelColors[(int)log_level::ERROR];
|
||||
}},
|
||||
{"CNR", [](const tag_func_param& f) -> std::string {
|
||||
return f.level >= log_level::ERROR ? loggingFormat.levelColors[(int)log_level::ERROR] : "";
|
||||
}},
|
||||
{"RC", [](const tag_func_param&) -> std::string {
|
||||
return "\033[0m";
|
||||
}},
|
||||
{"LOG_LEVEL", [](const tag_func_param& f) -> std::string {
|
||||
return loggingFormat.levelNames[(int)f.level];
|
||||
}},
|
||||
{"THREAD_NAME", [](const tag_func_param&) -> std::string {
|
||||
if (loggingThreadNames.find(std::this_thread::get_id()) == loggingThreadNames.end())
|
||||
return "UNKNOWN";
|
||||
return loggingThreadNames[std::this_thread::get_id()];
|
||||
}},
|
||||
{"FILE", [](const tag_func_param& f) -> std::string {
|
||||
return f.file;
|
||||
}},
|
||||
{"LINE", [](const tag_func_param& f) -> std::string {
|
||||
return f.line;
|
||||
}},
|
||||
{"RAW_STR", [](const tag_func_param& f) -> std::string {
|
||||
return f.raw_string;
|
||||
}},
|
||||
{"STR", [](const tag_func_param& f) -> std::string {
|
||||
return f.formatted_string;
|
||||
}}
|
||||
});
|
||||
|
||||
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
|
||||
size_t pos = 0;
|
||||
std::vector<std::string> tokens;
|
||||
while ((pos = s.find(delim)) != std::string::npos) {
|
||||
auto token = s.substr(0, pos);
|
||||
tokens.push_back(token);
|
||||
s.erase(0, pos + delim.length());
|
||||
}
|
||||
tokens.push_back(s);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
inline std::string filename(const std::string& path){
|
||||
if (loggingFormat.printFullFileName)
|
||||
return path;
|
||||
auto paths = split(path, "/");
|
||||
auto final = paths[paths.size()-1];
|
||||
if (final == "/")
|
||||
return paths[paths.size()-2];
|
||||
return final;
|
||||
}
|
||||
|
||||
class string_parser {
|
||||
private:
|
||||
std::string _str;
|
||||
size_t _pos;
|
||||
public:
|
||||
explicit string_parser(std::string str): _str(std::move(str)), _pos(0) {}
|
||||
|
||||
inline char next(){
|
||||
return _str[_pos++];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool has_next() const {
|
||||
return _pos < _str.size();
|
||||
}
|
||||
};
|
||||
|
||||
std::string stripANSI(const std::string& str){
|
||||
string_parser parser(str);
|
||||
std::string out;
|
||||
while (parser.has_next()){
|
||||
char c = parser.next();
|
||||
if (c == '\033'){
|
||||
while (parser.has_next() && parser.next() != 'm');
|
||||
} else
|
||||
out += c;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void applyCFormatting(const std::string& format, std::string& output, std::va_list& args){
|
||||
// args must be copied because they will be consumed by the first vsnprintf
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
|
||||
auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1;
|
||||
auto* buffer = new char[static_cast<unsigned long>(buffer_size)];
|
||||
|
||||
vsnprintf(buffer, buffer_size, format.c_str(), args);
|
||||
output = std::string(buffer);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the next character in the parser is a tag opening, if not output the buffer to the out string
|
||||
*/
|
||||
inline bool tagOpening(string_parser& parser, std::string& out){
|
||||
char c = ' ';
|
||||
if (parser.has_next() && (c = parser.next()) == '{')
|
||||
if (parser.has_next() && (c = parser.next()) == '{')
|
||||
return true;
|
||||
else
|
||||
out += c;
|
||||
else
|
||||
out += c;
|
||||
return false;
|
||||
}
|
||||
|
||||
void parseString(string_parser& parser, std::string& out, const std::string& userStr, log_level level, const char* file, int line){
|
||||
while (parser.has_next()){
|
||||
char c = parser.next();
|
||||
std::string nonTag;
|
||||
if (c == '$' && tagOpening(parser, nonTag)){
|
||||
std::string tag;
|
||||
while (parser.has_next()){
|
||||
c = parser.next();
|
||||
if (c == '}')
|
||||
break;
|
||||
tag += c;
|
||||
}
|
||||
c = parser.next();
|
||||
if (parser.has_next() && c != '}') {
|
||||
std::cerr << "Error processing tag, is not closed with two '}'!\n";
|
||||
break;
|
||||
}
|
||||
if (loggingFormat.ensureAlignment && tag == "STR") {
|
||||
auto currentOutputWidth = out.size();
|
||||
auto& longestWidth = loggingFormat.currentWidth;
|
||||
longestWidth = std::max(longestWidth, currentOutputWidth);
|
||||
// pad with spaces
|
||||
if (currentOutputWidth != longestWidth){
|
||||
for (size_t i = currentOutputWidth; i < longestWidth; i++)
|
||||
out += ' ';
|
||||
}
|
||||
}
|
||||
tag_func_param param{
|
||||
level, filename({file}), std::to_string(line), userStr, userStr
|
||||
};
|
||||
out += (*tagMap)[tag].func(param);
|
||||
} else {
|
||||
out += c;
|
||||
out += nonTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string applyFormatString(const std::string& str, log_level level, const char* file, int line){
|
||||
// this can be speedup by preprocessing the string into an easily callable class
|
||||
// where all the variables are ready to be substituted in one step
|
||||
// and all static information already entered
|
||||
string_parser parser(loggingFormat.logOutputFormat);
|
||||
std::string out;
|
||||
parseString(parser, out, str, level, file, line);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args) {
|
||||
std::string withoutLn = format;
|
||||
auto len = withoutLn.length();
|
||||
|
||||
if (len > 0 && withoutLn[len - 1] == '\n')
|
||||
withoutLn = withoutLn.substr(0, len-1);
|
||||
|
||||
std::string out;
|
||||
|
||||
applyCFormatting(withoutLn, out, args);
|
||||
|
||||
if (level == log_level::NONE){
|
||||
std::cout << out << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string finalFormattedOutput = applyFormatString(out, level, file, line);
|
||||
|
||||
if (loggingFormat.logToConsole)
|
||||
std::cout << finalFormattedOutput;
|
||||
|
||||
|
||||
if (loggingFormat.logToFile){
|
||||
string_parser parser(loggingFormat.logFileName);
|
||||
std::string fileName;
|
||||
parseString(parser, fileName, withoutLn, level, file, line);
|
||||
|
||||
auto path = loggingFormat.logFilePath;
|
||||
if (!path.empty() && path[path.length()-1] != '/')
|
||||
path += '/';
|
||||
|
||||
// if the file has changed (new day in default case) we should reset the rollover count
|
||||
if (loggingFormat.lastFile != fileName){
|
||||
loggingFormat.currentRollover = 0;
|
||||
loggingFormat.lastFile = fileName;
|
||||
}
|
||||
|
||||
path += fileName;
|
||||
path += '-';
|
||||
path += std::to_string(loggingFormat.currentRollover);
|
||||
path += ".log";
|
||||
|
||||
if (std::filesystem::exists(path)) {
|
||||
auto fileSize = std::filesystem::file_size(path);
|
||||
|
||||
// will start on next file
|
||||
if (fileSize > loggingFormat.logMaxFileSize)
|
||||
loggingFormat.currentRollover++;
|
||||
}
|
||||
|
||||
writer.writeLine(path, stripANSI(finalFormattedOutput));
|
||||
}
|
||||
//std::cout.flush();
|
||||
}
|
||||
|
||||
void log_stream_internal(const std::string& str, const logger& logger) {
|
||||
auto& s = loggingStreamLines[std::this_thread::get_id()][logger.level];
|
||||
// s += str;
|
||||
for (char c : str){
|
||||
s += c;
|
||||
if (c == '\n'){
|
||||
log(s, logger.level, logger.file, logger.line);
|
||||
s = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setThreadName(const std::string& name) {
|
||||
loggingThreadNames[std::this_thread::get_id()] = name;
|
||||
}
|
||||
|
||||
void setLogFormat(const log_format& format){
|
||||
loggingFormat = format;
|
||||
}
|
||||
void setLogColor(log_level level, const std::string& newFormat){
|
||||
loggingFormat.levelColors[(int)level] = newFormat;
|
||||
}
|
||||
void setLogName(log_level level, const std::string& newFormat){
|
||||
loggingFormat.levelNames[(int)level] = newFormat;
|
||||
}
|
||||
void setLogOutputFormat(const std::string& newFormat){
|
||||
loggingFormat.logOutputFormat = newFormat;
|
||||
}
|
||||
void setLogToFile(bool shouldLogToFile){
|
||||
loggingFormat.logToFile = shouldLogToFile;
|
||||
}
|
||||
void setLogToConsole(bool shouldLogToConsole){
|
||||
loggingFormat.logToConsole = shouldLogToConsole;
|
||||
}
|
||||
void setLogPath(const std::string& path){
|
||||
loggingFormat.logFilePath = path;
|
||||
}
|
||||
void setLogFileName(const std::string& fileName){
|
||||
loggingFormat.logFileName = fileName;
|
||||
}
|
||||
void setMaxFileSize(size_t fileSize) {
|
||||
loggingFormat.logMaxFileSize = fileSize;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
std::cerr.flush();
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
void newline()
|
||||
{
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__llvm__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_LOGGING
|
||||
#define BLT_LOG(format, level, ...)
|
||||
#define BLT_LOG_STREAM(level)
|
||||
#define BLT_TRACE0_STREAM
|
||||
#define BLT_TRACE1_STREAM
|
||||
#define BLT_TRACE2_STREAM
|
||||
#define BLT_TRACE3_STREAM
|
||||
#define BLT_TRACE_STREAM
|
||||
#define BLT_DEBUG_STREAM
|
||||
#define BLT_INFO_STREAM
|
||||
#define BLT_WARN_STREAM
|
||||
#define BLT_ERROR_STREAM
|
||||
#define BLT_FATAL_STREAM
|
||||
#define BLT_TRACE(format, ...)
|
||||
#define BLT_DEBUG(format, ...)
|
||||
#define BLT_INFO(format, ...)
|
||||
#define BLT_WARN(format, ...)
|
||||
#define BLT_ERROR(format, ...)
|
||||
#define BLT_FATAL(format, ...)
|
||||
#else
|
||||
#define BLT_NEWLINE() blt::logging::newline()
|
||||
#define BLT_LOG(format, level, ...) blt::logging::log(format, level, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#define BLT_LOG_STREAM(level) blt::logging::logger{level, __FILE__, __LINE__}
|
||||
#ifdef BLT_DISABLE_TRACE
|
||||
#define BLT_TRACE(format, ...)
|
||||
#define BLT_TRACE0_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE1_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE2_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE3_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_TRACE(format, ...) BLT_LOG(format, blt::logging::log_level::TRACE, ##__VA_ARGS__)
|
||||
#define BLT_TRACE0_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE0)
|
||||
#define BLT_TRACE1_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE1)
|
||||
#define BLT_TRACE2_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE2)
|
||||
#define BLT_TRACE3_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE3)
|
||||
#define BLT_TRACE_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_DEBUG
|
||||
#define BLT_DEBUG(format, ...)
|
||||
#define BLT_DEBUG_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_DEBUG(format, ...) BLT_LOG(format, blt::logging::log_level::DEBUG, ##__VA_ARGS__)
|
||||
#define BLT_DEBUG_STREAM BLT_LOG_STREAM(blt::logging::log_level::DEBUG)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_INFO
|
||||
#define BLT_INFO(format, ...)
|
||||
#define BLT_INFO_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_INFO(format, ...) BLT_LOG(format, blt::logging::log_level::INFO, ##__VA_ARGS__)
|
||||
#define BLT_INFO_STREAM BLT_LOG_STREAM(blt::logging::log_level::INFO)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_WARN
|
||||
#define BLT_WARN(format, ...)
|
||||
#define BLT_WARN_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_WARN(format, ...) BLT_LOG(format, blt::logging::log_level::WARN, ##__VA_ARGS__)
|
||||
#define BLT_WARN_STREAM BLT_LOG_STREAM(blt::logging::log_level::WARN)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_ERROR
|
||||
#define BLT_ERROR(format, ...)
|
||||
#define BLT_ERROR_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_ERROR(format, ...) BLT_LOG(format, blt::logging::log_level::ERROR, ##__VA_ARGS__)
|
||||
#define BLT_ERROR_STREAM BLT_LOG_STREAM(blt::logging::log_level::ERROR)
|
||||
#endif
|
||||
|
||||
#ifdef BLT_DISABLE_FATAL
|
||||
#define BLT_FATAL(format, ...)
|
||||
#define BLT_FATAL_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_FATAL(format, ...) BLT_LOG(format, blt::logging::log_level::FATAL, ##__VA_ARGS__)
|
||||
#define BLT_FATAL_STREAM BLT_LOG_STREAM(blt::logging::log_level::FATAL)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__llvm__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif //BLT_TESTS_LOGGING2_H
|
|
@ -33,13 +33,11 @@ namespace blt
|
|||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = typename std::iterator_traits<T>::value_type;
|
||||
using pointer = typename std::iterator_traits<T>::pointer;
|
||||
using const_pointer = const typename std::iterator_traits<T>::pointer;
|
||||
using reference = typename std::iterator_traits<T>::reference;
|
||||
using const_reference = const typename std::iterator_traits<T>::reference;
|
||||
using difference_type = typename std::iterator_traits<T>::difference_type;
|
||||
using iterator_category = typename std::iterator_traits<T>::iterator_category;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using iterator = ptr_iterator<T>;
|
||||
using const_iterator = ptr_iterator<const T>;
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace blt::mem
|
|||
return fromBytes<little_endian>(in, *out);
|
||||
}
|
||||
|
||||
static std::size_t next_byte_allocation(std::size_t prev_size, std::size_t default_allocation_block = 8192, std::size_t default_size = 16)
|
||||
inline std::size_t next_byte_allocation(std::size_t prev_size, std::size_t default_allocation_block = 8192, std::size_t default_size = 16)
|
||||
{
|
||||
if (prev_size < default_size)
|
||||
return default_size;
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#ifndef BLT_MMAP_H
|
||||
#define BLT_MMAP_H
|
||||
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/std/types.h>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
// size of 2mb in bytes
|
||||
inline constexpr blt::size_t BLT_2MB_SIZE = 2048 * 1024;
|
||||
|
@ -41,7 +43,7 @@ namespace blt
|
|||
public:
|
||||
bad_alloc_t() = default;
|
||||
|
||||
explicit bad_alloc_t(std::string_view str): str(str)
|
||||
explicit bad_alloc_t(const std::string_view str): str(str)
|
||||
{}
|
||||
|
||||
explicit bad_alloc_t(std::string str): str(std::move(str))
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace blt
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8a889d3699b3c09ade435641fb034427f3fd12b6
|
||||
Subproject commit 154c63489e84d5569d3b466342a2ae8fd99e4734
|
|
@ -6,7 +6,7 @@
|
|||
#include <blt/format/format.h>
|
||||
#include <blt/std/string.h>
|
||||
#include <cmath>
|
||||
#include "blt/std/logging.h"
|
||||
#include "blt/logging/logging.h"
|
||||
#include "blt/std/assert.h"
|
||||
#include "blt/std/utility.h"
|
||||
#include <stack>
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <blt/fs/file_writers.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
i64 fwriter_t::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
return static_cast<i64>(std::fwrite(buffer, 1, bytes, m_file));
|
||||
}
|
||||
|
||||
void fwriter_t::flush()
|
||||
{
|
||||
writer_t::flush();
|
||||
std::fflush(m_file);
|
||||
}
|
||||
|
||||
void fwriter_t::newfile(const std::string& new_name)
|
||||
{
|
||||
if (m_file != nullptr)
|
||||
std::fclose(m_file);
|
||||
m_file = std::fopen(new_name.c_str(), m_mode.c_str());
|
||||
if (!m_file)
|
||||
throw std::runtime_error("Failed to open file for writing");
|
||||
}
|
||||
|
||||
buffered_writer::buffered_writer(const std::string& name, const size_t buffer_size): fwriter_t{name, "ab"}
|
||||
{
|
||||
m_buffer.resize(buffer_size);
|
||||
}
|
||||
|
||||
buffered_writer::buffered_writer(const size_t buffer_size)
|
||||
{
|
||||
m_buffer.resize(buffer_size);
|
||||
}
|
||||
|
||||
void buffered_writer::newfile(const std::string& new_name)
|
||||
{
|
||||
fwriter_t::newfile(new_name);
|
||||
setvbuf(this->m_file, nullptr, _IONBF, 0);
|
||||
}
|
||||
|
||||
i64 buffered_writer::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
if (bytes > m_buffer.size())
|
||||
return fwriter_t::write(buffer, bytes);
|
||||
if (bytes + m_current_pos > m_buffer.size())
|
||||
flush();
|
||||
std::memcpy(m_buffer.data() + m_current_pos, buffer, bytes);
|
||||
m_current_pos += bytes;
|
||||
return static_cast<i64>(bytes);
|
||||
}
|
||||
|
||||
void buffered_writer::flush()
|
||||
{
|
||||
fwriter_t::write(m_buffer.data(), m_current_pos);
|
||||
fwriter_t::flush();
|
||||
m_current_pos = 0;
|
||||
}
|
||||
|
||||
bounded_writer::bounded_writer(fwriter_t& writer, std::optional<std::string> base_name, const size_t max_size, naming_function_t naming_function):
|
||||
m_writer(&writer), m_base_name(std::move(base_name)), m_max_size(max_size), m_naming_function(std::move(naming_function))
|
||||
{
|
||||
bounded_writer::newfile(m_base_name.value_or(""));
|
||||
}
|
||||
|
||||
i64 bounded_writer::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
m_currently_written += bytes;
|
||||
if (m_currently_written > m_max_size)
|
||||
this->newfile(m_base_name.value_or(""));
|
||||
return m_writer->write(buffer, bytes);
|
||||
}
|
||||
|
||||
void bounded_writer::newfile(const std::string& new_name)
|
||||
{
|
||||
++m_current_invocation;
|
||||
m_currently_written = 0;
|
||||
m_writer->newfile(m_naming_function(m_current_invocation, new_name));
|
||||
}
|
||||
|
||||
void bounded_writer::flush()
|
||||
{
|
||||
m_writer->flush();
|
||||
}
|
||||
|
||||
rotating_writer::rotating_writer(fwriter_t& writer, const time_t period): m_writer{&writer}, m_period{period}
|
||||
{
|
||||
newfile();
|
||||
}
|
||||
|
||||
i64 rotating_writer::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
check_for_time();
|
||||
return m_writer->write(buffer, bytes);
|
||||
}
|
||||
|
||||
void rotating_writer::flush()
|
||||
{
|
||||
check_for_time();
|
||||
m_writer->flush();
|
||||
}
|
||||
|
||||
void rotating_writer::newfile(const std::string& new_name)
|
||||
{
|
||||
m_writer->newfile(new_name);
|
||||
}
|
||||
|
||||
void rotating_writer::newfile()
|
||||
{
|
||||
m_last_time = get_current_time();
|
||||
std::string name;
|
||||
name += std::to_string(m_last_time.year);
|
||||
name += "-" + std::to_string(m_last_time.month);
|
||||
name += "-" + std::to_string(m_last_time.day);
|
||||
if (m_period.hour >= 0)
|
||||
name += "-" + std::to_string(m_last_time.hour);
|
||||
name += ".txt";
|
||||
newfile(name);
|
||||
}
|
||||
|
||||
void rotating_writer::check_for_time()
|
||||
{
|
||||
const auto current_time = get_current_time();
|
||||
if ((m_period.hour > 0 && current_time.hour > m_last_time.hour + m_period.hour) ||
|
||||
(m_period.day > 0 && current_time.day > m_last_time.day + m_period.day) ||
|
||||
(m_period.month > 0 && current_time.month > m_last_time.month + m_period.month) ||
|
||||
(m_period.year > 0 && current_time.year > m_last_time.year + m_period.year))
|
||||
newfile();
|
||||
}
|
||||
|
||||
time_t rotating_writer::get_current_time()
|
||||
{
|
||||
const std::time_t time = std::time(nullptr);
|
||||
const auto current_time = std::localtime(&time);
|
||||
return {current_time->tm_year + 1900, current_time->tm_mon + 1, current_time->tm_mday, current_time->tm_hour};
|
||||
}
|
||||
}
|
|
@ -17,63 +17,4 @@
|
|||
*/
|
||||
#include <blt/fs/filesystem.h>
|
||||
#include <cstring>
|
||||
#include <blt/std/logging.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
|
||||
fstream_block_reader::fstream_block_reader(std::fstream& stream, size_t bufferSize):
|
||||
block_reader(bufferSize), m_stream(stream), m_buffer(new char[bufferSize])
|
||||
{
|
||||
if (!m_stream.good() || m_stream.fail())
|
||||
BLT_WARN("Provided std::fstream is not good! Clearing!");
|
||||
m_stream.clear();
|
||||
}
|
||||
|
||||
int fstream_block_reader::read(char* buffer, size_t bytes)
|
||||
{
|
||||
if (readIndex == 0)
|
||||
m_stream.read(m_buffer, (long) m_bufferSize);
|
||||
if (readIndex + bytes >= m_bufferSize)
|
||||
{
|
||||
// copy out all the data from the current buffer
|
||||
auto bytesLeft = m_bufferSize - readIndex;
|
||||
memcpy(buffer, m_buffer + readIndex, bytesLeft);
|
||||
readIndex = 0;
|
||||
// now to prevent large scale reading in small blocks, we should just read the entire thing into the buffer.
|
||||
m_stream.read(buffer + bytesLeft, (long) (bytes - bytesLeft));
|
||||
} else
|
||||
{
|
||||
// but in the case that the size of the data read is small, we should read in blocks and copy from that buffer
|
||||
// that should be quicker since file operations are slow.
|
||||
std::memcpy(buffer, m_buffer + readIndex, bytes);
|
||||
readIndex += bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fstream_block_writer::write(char* buffer, size_t bytes)
|
||||
{
|
||||
if (writeIndex + bytes >= m_bufferSize)
|
||||
{
|
||||
// in an attempt to stay efficient we write out the old buffer and the new buffer
|
||||
// since there is a good chance there is more than a buffer's worth of data being written
|
||||
// otherwise the buffer is almost full and can be written anyway. (this might be bad for performance especially if the FS wants round numbers)
|
||||
m_stream.write(m_buffer, (long) writeIndex);
|
||||
writeIndex = 0;
|
||||
m_stream.write(buffer, (long) bytes);
|
||||
} else
|
||||
{
|
||||
std::memcpy(m_buffer + writeIndex, buffer, bytes);
|
||||
writeIndex += bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fstream_block_writer::flush_internal()
|
||||
{
|
||||
m_stream.write(m_buffer, (long) writeIndex);
|
||||
writeIndex = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -82,8 +82,8 @@ std::string blt::fs::getFile(std::string_view path)
|
|||
file_contents = file_stream.str();
|
||||
} catch (std::ifstream::failure& e)
|
||||
{
|
||||
BLT_WARN("Unable to read file '%s'!\n", std::string(path).c_str());
|
||||
BLT_WARN("Exception: %s", e.what());
|
||||
BLT_WARN("Unable to read file '{}'!\n", std::string(path).c_str());
|
||||
BLT_WARN("Exception: {}", e.what());
|
||||
BLT_THROW(std::runtime_error("Failed to read file!\n"));
|
||||
}
|
||||
return file_contents;
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
* See LICENSE file for license detail
|
||||
*/
|
||||
#include <blt/fs/nbt.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace blt::nbt {
|
||||
void writeUTF8String(blt::fs::block_writer& stream, const std::string& str) {
|
||||
void writeUTF8String(blt::fs::writer_t& stream, const std::string& str) {
|
||||
blt::string::utf8_string str8 = blt::string::createUTFString(str);
|
||||
stream.write(str8.characters, str8.size);
|
||||
delete[] str8.characters;
|
||||
}
|
||||
|
||||
std::string readUTF8String(blt::fs::block_reader& stream) {
|
||||
std::string readUTF8String(blt::fs::reader_t& stream) {
|
||||
int16_t utflen;
|
||||
|
||||
readData(stream, utflen);
|
||||
|
@ -33,9 +33,10 @@ namespace blt::nbt {
|
|||
}
|
||||
|
||||
void NBTReader::read() {
|
||||
char t = reader.get();
|
||||
char t;
|
||||
reader.read(&t, 1);
|
||||
if (t != (char)nbt_tag::COMPOUND) {
|
||||
BLT_WARN("Found %d", t);
|
||||
BLT_WARN("Found {:d}", t);
|
||||
throw std::runtime_error("Incorrectly formatted NBT data! Root tag must be a compound tag!");
|
||||
}
|
||||
root = new tag_compound;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/fs/path_helper.h>
|
||||
#include <blt/std/string.h>
|
||||
#include <blt/compatibility.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
#ifdef BLT_WINDOWS
|
||||
constexpr static char delim = '\\';
|
||||
#else
|
||||
constexpr static char delim = '/';
|
||||
#endif
|
||||
|
||||
std::string base_name(const std::string& str)
|
||||
{
|
||||
return std::string(base_name_sv(str));
|
||||
}
|
||||
|
||||
std::string_view base_name_sv(const std::string_view str)
|
||||
{
|
||||
const auto parts = string::split_sv(str, delim);
|
||||
const auto file_parts = string::split_sv(parts.back(), '.');
|
||||
return file_parts.front();
|
||||
}
|
||||
|
||||
std::string filename(const std::string& str)
|
||||
{
|
||||
return std::string(filename_sv(str));
|
||||
}
|
||||
|
||||
std::string_view filename_sv(const std::string_view str)
|
||||
{
|
||||
const auto parts = string::split_sv(str, delim);
|
||||
return parts.back();
|
||||
}
|
||||
|
||||
std::string extension(const std::string& str)
|
||||
{
|
||||
return std::string(extension_sv(str));
|
||||
}
|
||||
|
||||
std::string_view extension_sv(const std::string_view str)
|
||||
{
|
||||
const auto parts = string::split_sv(str, delim);
|
||||
const auto file_parts = parts.back().find_first_of('.');
|
||||
return parts.back().substr(std::min(file_parts + 1, parts.back().size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <blt/fs/stream_wrappers.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
fstream_reader_t::fstream_reader_t(std::istream& stream): m_stream{&stream}
|
||||
{}
|
||||
|
||||
i64 fstream_reader_t::read(char* buffer, const size_t bytes)
|
||||
{
|
||||
return m_stream->readsome(buffer, static_cast<std::streamsize>(bytes));
|
||||
}
|
||||
|
||||
fstream_writer_t::fstream_writer_t(std::ostream& stream): m_stream{&stream}
|
||||
{}
|
||||
|
||||
i64 fstream_writer_t::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
m_stream->write(buffer, static_cast<std::streamsize>(bytes));
|
||||
return static_cast<i64>(bytes);
|
||||
}
|
||||
|
||||
void fstream_writer_t::flush()
|
||||
{
|
||||
m_stream->flush();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/fs/threaded_writers.h>
|
||||
|
||||
namespace blt::fs
|
||||
{
|
||||
concurrent_file_writer::concurrent_file_writer(writer_t* writer): m_writer{writer}
|
||||
{}
|
||||
|
||||
i64 concurrent_file_writer::write(const char* buffer, const size_t bytes)
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_writer->write(buffer, bytes);
|
||||
}
|
||||
|
||||
void concurrent_file_writer::flush()
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_writer->flush();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/logging/ansi.h>
|
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <blt/logging/fmt_tokenizer.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
fmt_token_type fmt_tokenizer_t::get_type(const char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return fmt_token_type::NUMBER;
|
||||
case '+':
|
||||
return fmt_token_type::PLUS;
|
||||
case '-':
|
||||
return fmt_token_type::MINUS;
|
||||
case '.':
|
||||
return fmt_token_type::DOT;
|
||||
case ':':
|
||||
return fmt_token_type::COLON;
|
||||
case ' ':
|
||||
return fmt_token_type::SPACE;
|
||||
case '#':
|
||||
return fmt_token_type::POUND;
|
||||
case '<':
|
||||
return fmt_token_type::LEFT_CHEVRON;
|
||||
case '>':
|
||||
return fmt_token_type::RIGHT_CHEVRON;
|
||||
case '^':
|
||||
return fmt_token_type::CARET;
|
||||
case '{':
|
||||
return fmt_token_type::OPEN_BRACKET;
|
||||
case '}':
|
||||
return fmt_token_type::CLOSE_BRACKET;
|
||||
default:
|
||||
return fmt_token_type::STRING;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<fmt_token_t> fmt_tokenizer_t::next()
|
||||
{
|
||||
if (m_pos >= m_fmt.size())
|
||||
return {};
|
||||
bool is_escaped = false;
|
||||
if (m_fmt[m_pos] == '\\')
|
||||
{
|
||||
is_escaped = true;
|
||||
++m_pos;
|
||||
}
|
||||
switch (const auto base_type = get_type(m_fmt[m_pos]))
|
||||
{
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::PLUS:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::DOT:
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::POUND:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::CARET:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
if (is_escaped)
|
||||
return fmt_token_t{fmt_token_type::STRING, std::string_view{m_fmt.data() + m_pos++, 1}};
|
||||
return fmt_token_t{base_type, std::string_view{m_fmt.data() + m_pos++, 1}};
|
||||
default:
|
||||
{
|
||||
const auto begin = m_pos;
|
||||
for (; m_pos < m_fmt.size() && get_type(m_fmt[m_pos]) == base_type; ++m_pos)
|
||||
{
|
||||
}
|
||||
return fmt_token_t{base_type, std::string_view{m_fmt.data() + begin, m_pos - begin}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fmt_token_t> fmt_tokenizer_t::tokenize(const std::string_view fmt)
|
||||
{
|
||||
m_fmt = fmt;
|
||||
m_pos = 0;
|
||||
std::vector<fmt_token_t> tokens;
|
||||
while (auto token = next())
|
||||
tokens.push_back(*token);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
const fmt_spec_t& fmt_parser_t::parse(const std::string_view fmt)
|
||||
{
|
||||
m_spec = {};
|
||||
m_pos = 0;
|
||||
m_tokens = m_tokenizer.tokenize(fmt);
|
||||
|
||||
parse_fmt_field();
|
||||
|
||||
return m_spec;
|
||||
}
|
||||
|
||||
bool fmt_parser_t::is_align_t(const fmt_token_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::CARET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_field()
|
||||
{
|
||||
if (!has_next())
|
||||
throw std::runtime_error("Expected token when parsing format field");
|
||||
const auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::NUMBER:
|
||||
parse_arg_id();
|
||||
if (has_next())
|
||||
{
|
||||
if (peek().type == fmt_token_type::COLON)
|
||||
parse_fmt_spec();
|
||||
else
|
||||
throw std::runtime_error("Expected ':' when parsing format field after arg id!");
|
||||
}
|
||||
break;
|
||||
case fmt_token_type::COLON:
|
||||
parse_fmt_spec();
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
if (has_next())
|
||||
parse_type();
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_arg_id()
|
||||
{
|
||||
const auto [type, value] = next();
|
||||
if (type != fmt_token_type::NUMBER)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Expected number when parsing arg id, unexpected value '" << value << '\'';
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
m_spec.arg_id = std::stoll(std::string(value));
|
||||
}
|
||||
|
||||
std::string fmt_parser_t::parse_arg_or_number()
|
||||
{
|
||||
auto [type, value] = next();
|
||||
if (type == fmt_token_type::NUMBER)
|
||||
return std::string(value);
|
||||
if (type == fmt_token_type::OPEN_BRACKET)
|
||||
{
|
||||
auto [next_type, next_value] = next();
|
||||
if (next_type != fmt_token_type::NUMBER)
|
||||
throw std::runtime_error("Expected number when parsing arg or number, unexpected value '" + std::string(next_value) + '\'');
|
||||
if (next().type != fmt_token_type::CLOSE_BRACKET)
|
||||
throw std::runtime_error("Expected closing bracket when parsing arg or number, unexpected value '" + std::string(next_value) + '\'');
|
||||
// TODO: this feels like an evil hack.
|
||||
const auto arg_id = std::stoul(std::string(next_value));
|
||||
if (arg_id >= m_handlers.size())
|
||||
throw std::runtime_error("Invalid arg id " + std::to_string(arg_id) + ", max arg supported: " + std::to_string(m_handlers.size()));
|
||||
std::stringstream ss;
|
||||
m_handlers[arg_id](ss, fmt_spec_t{});
|
||||
return ss.str();
|
||||
}
|
||||
throw std::runtime_error("Expected number when parsing arg or number, unexpected value '" + std::string(value) + '\'');
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_spec()
|
||||
{
|
||||
// consume :
|
||||
consume();
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::STRING:
|
||||
// if there is a token beyond this string, it is not a type string
|
||||
if (has_next(1))
|
||||
parse_fmt_spec_fill();
|
||||
return;
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::CARET:
|
||||
parse_fmt_spec_align();
|
||||
break;
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Begin)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
parse_fmt_spec_width();
|
||||
break;
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
parse_fmt_spec_sign();
|
||||
break;
|
||||
case fmt_token_type::POUND:
|
||||
parse_fmt_spec_form();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_spec_fill()
|
||||
{
|
||||
parse_fill();
|
||||
if (!has_next())
|
||||
return;
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::CARET:
|
||||
parse_fmt_spec_align();
|
||||
break;
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
parse_fmt_spec_width();
|
||||
break;
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
parse_fmt_spec_sign();
|
||||
break;
|
||||
case fmt_token_type::POUND:
|
||||
parse_fmt_spec_form();
|
||||
break;
|
||||
case fmt_token_type::STRING:
|
||||
return;
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Fill)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_spec_align()
|
||||
{
|
||||
parse_align();
|
||||
if (!has_next())
|
||||
return;
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::STRING:
|
||||
return;
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
parse_fmt_spec_width();
|
||||
break;
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
parse_fmt_spec_sign();
|
||||
break;
|
||||
case fmt_token_type::POUND:
|
||||
parse_fmt_spec_form();
|
||||
break;
|
||||
case fmt_token_type::CARET:
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Align)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle start of fmt, with sign
|
||||
void fmt_parser_t::parse_fmt_spec_sign()
|
||||
{
|
||||
parse_sign();
|
||||
if (!has_next())
|
||||
return;
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::STRING:
|
||||
return;
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::CARET:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Sign)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
parse_fmt_spec_width();
|
||||
break;
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
case fmt_token_type::POUND:
|
||||
parse_fmt_spec_form();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_spec_form()
|
||||
{
|
||||
parse_form();
|
||||
if (!has_next())
|
||||
return;
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::STRING:
|
||||
return;
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
case fmt_token_type::POUND:
|
||||
case fmt_token_type::CARET:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Form)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
parse_fmt_spec_width();
|
||||
break;
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// handle width parsing
|
||||
void fmt_parser_t::parse_fmt_spec_width()
|
||||
{
|
||||
parse_width();
|
||||
if (!has_next())
|
||||
return;
|
||||
auto [type, value] = peek();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::STRING:
|
||||
return;
|
||||
case fmt_token_type::COLON:
|
||||
case fmt_token_type::MINUS:
|
||||
case fmt_token_type::PLUS:
|
||||
case fmt_token_type::SPACE:
|
||||
case fmt_token_type::POUND:
|
||||
case fmt_token_type::NUMBER:
|
||||
case fmt_token_type::CARET:
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
case fmt_token_type::OPEN_BRACKET:
|
||||
case fmt_token_type::CLOSE_BRACKET:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "(Stage (Width)) Invalid token type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
case fmt_token_type::DOT:
|
||||
parse_fmt_spec_precision();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fmt_spec_precision()
|
||||
{
|
||||
// consume .
|
||||
consume();
|
||||
parse_precision();
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_fill()
|
||||
{
|
||||
auto [type, value] = next();
|
||||
if (type != fmt_token_type::STRING)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Expected string when parsing fill, got " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
m_spec.prefix_char = value.front();
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_align()
|
||||
{
|
||||
auto [type, value] = next();
|
||||
switch (type)
|
||||
{
|
||||
case fmt_token_type::LEFT_CHEVRON:
|
||||
m_spec.alignment = fmt_align_t::LEFT;
|
||||
break;
|
||||
case fmt_token_type::RIGHT_CHEVRON:
|
||||
m_spec.alignment = fmt_align_t::RIGHT;
|
||||
break;
|
||||
case fmt_token_type::CARET:
|
||||
m_spec.alignment = fmt_align_t::CENTER;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Invalid align type " << static_cast<u8>(type) << " value " << value;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_sign()
|
||||
{
|
||||
auto [_, value] = next();
|
||||
if (value.size() > 1)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Sign contains more than one character, we are not sure how to interpret this. Value '" << value << "'";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
switch (value[0])
|
||||
{
|
||||
case '+':
|
||||
m_spec.sign = fmt_sign_t::PLUS;
|
||||
break;
|
||||
case '-':
|
||||
m_spec.sign = fmt_sign_t::MINUS;
|
||||
break;
|
||||
case ' ':
|
||||
m_spec.sign = fmt_sign_t::SPACE;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Invalid sign " << value[0];
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_form()
|
||||
{
|
||||
consume();
|
||||
m_spec.alternate_form = true;
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_width()
|
||||
{
|
||||
const auto value = parse_arg_or_number();
|
||||
if (value.front() == '0' && !m_spec.prefix_char.has_value())
|
||||
m_spec.prefix_char = '0';
|
||||
m_spec.width = std::stoll(value);
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_precision()
|
||||
{
|
||||
if (!has_next())
|
||||
throw std::runtime_error("Missing token when parsing precision");
|
||||
auto value = parse_arg_or_number();
|
||||
m_spec.precision = std::stoll(std::string(value));
|
||||
}
|
||||
|
||||
void fmt_parser_t::parse_type()
|
||||
{
|
||||
auto [_, value] = next();
|
||||
if (value.size() != 1)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Type contains more than one character, we are not sure how to interpret this value '" << value << "'";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
m_spec.uppercase = std::isupper(value.front());
|
||||
switch (value.front())
|
||||
{
|
||||
case 'b':
|
||||
case 'B':
|
||||
m_spec.type = fmt_type_t::BINARY;
|
||||
break;
|
||||
case 'c':
|
||||
m_spec.type = fmt_type_t::CHAR;
|
||||
break;
|
||||
case 'd':
|
||||
m_spec.type = fmt_type_t::DECIMAL;
|
||||
break;
|
||||
case 'o':
|
||||
m_spec.type = fmt_type_t::OCTAL;
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
m_spec.type = fmt_type_t::HEX;
|
||||
break;
|
||||
case 'a':
|
||||
case 'A':
|
||||
m_spec.type = fmt_type_t::HEX_FLOAT;
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
m_spec.type = fmt_type_t::EXPONENT;
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
m_spec.type = fmt_type_t::FIXED_POINT;
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
m_spec.type = fmt_type_t::GENERAL;
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
m_spec.type = fmt_type_t::TYPE;
|
||||
break;
|
||||
default:
|
||||
std::stringstream ss;
|
||||
ss << "Invalid type " << value;
|
||||
ss << std::endl << std::endl;
|
||||
ss << "Expected one of: " << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "b | B" << std::setw(6) << ' ' << "Print as binary output" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "c" << std::setw(6) << ' ' << "Print as character output" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "d" << std::setw(6) << ' ' << "Print as decimal (base 10) output" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "o" << std::setw(6) << ' ' << "Print as octal (base 8) output" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "x | X" << std::setw(6) << ' ' << "Print as hexadecimal (base 16) output" <<
|
||||
std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "a | A" << std::setw(6) << ' ' << "Print floats as hexadecimal (base 16) output"
|
||||
<< std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "e | E" << std::setw(6) << ' ' << "Print floats in scientific notation" <<
|
||||
std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "f | F" << std::setw(6) << ' ' <<
|
||||
"Print floats in fixed point output, useful for setting precision of output" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "g | G" << std::setw(6) << ' ' <<
|
||||
"Print floats in general output, switching between fixed point and scientific notation based on magnitude" << std::endl;
|
||||
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "t | T" << std::setw(6) << ' ' << "Print the type as a string" << std::endl;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <blt/logging/injector.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/hashmap.h>
|
||||
#include <blt/std/types.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
struct global_context_t
|
||||
{
|
||||
logging_config_t global_config;
|
||||
};
|
||||
|
||||
static global_context_t global_context;
|
||||
|
||||
struct logging_thread_context_t
|
||||
{
|
||||
std::stringstream stream;
|
||||
std::stringstream logging_stream;
|
||||
std::string thread_name;
|
||||
logger_t logger{stream};
|
||||
};
|
||||
|
||||
logging_thread_context_t& get_thread_context()
|
||||
{
|
||||
thread_local logging_thread_context_t context;
|
||||
return context;
|
||||
}
|
||||
|
||||
std::string logger_t::to_string() const
|
||||
{
|
||||
return dynamic_cast<std::stringstream&>(m_stream).str();
|
||||
}
|
||||
|
||||
size_t logger_t::find_ending_brace(size_t begin) const
|
||||
{
|
||||
size_t braces = 0;
|
||||
for (; begin < m_fmt.size(); ++begin)
|
||||
{
|
||||
if (m_fmt[begin] == '{')
|
||||
++braces;
|
||||
else if (m_fmt[begin] == '}')
|
||||
--braces;
|
||||
if (braces == 0)
|
||||
return begin;
|
||||
}
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
void logger_t::setup_stream(const fmt_spec_t& spec) const
|
||||
{
|
||||
if (spec.prefix_char)
|
||||
m_stream << std::setfill(*spec.prefix_char);
|
||||
else
|
||||
m_stream << std::setfill(' ');
|
||||
switch (spec.alignment)
|
||||
{
|
||||
case fmt_align_t::LEFT:
|
||||
m_stream << std::left;
|
||||
break;
|
||||
case fmt_align_t::CENTER:
|
||||
// TODO?
|
||||
break;
|
||||
case fmt_align_t::RIGHT:
|
||||
m_stream << std::right;
|
||||
break;
|
||||
}
|
||||
if (spec.width > 0)
|
||||
m_stream << std::setw(static_cast<i32>(spec.width));
|
||||
else
|
||||
m_stream << std::setw(0);
|
||||
if (spec.precision > 0)
|
||||
m_stream << std::setprecision(static_cast<i32>(spec.precision));
|
||||
else
|
||||
m_stream << std::setprecision(16);
|
||||
if (spec.alternate_form)
|
||||
m_stream << std::showbase;
|
||||
else
|
||||
m_stream << std::noshowbase;
|
||||
if (spec.uppercase)
|
||||
m_stream << std::uppercase;
|
||||
else
|
||||
m_stream << std::nouppercase;
|
||||
if (spec.sign == fmt_sign_t::PLUS)
|
||||
m_stream << std::showpos;
|
||||
else
|
||||
m_stream << std::noshowpos;
|
||||
}
|
||||
|
||||
std::string logger_t::process_string(const std::string_view str)
|
||||
{
|
||||
auto result = std::string(str);
|
||||
size_t pos = 0;
|
||||
while (pos = result.find('{', pos), pos != std::string::npos)
|
||||
{
|
||||
if (pos > 0 && result[pos - 1] == '\\')
|
||||
{
|
||||
auto before = result.substr(0, pos - 1);
|
||||
auto after = result.substr(pos);
|
||||
result = before + after;
|
||||
} else
|
||||
++pos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void logger_t::process_strings()
|
||||
{
|
||||
auto spec_it = m_fmt_specs.begin();
|
||||
auto str_it = m_string_sections.begin();
|
||||
for (; spec_it != m_fmt_specs.end(); ++spec_it, ++str_it)
|
||||
{
|
||||
m_stream << process_string(*str_it);
|
||||
auto arg_pos = spec_it->arg_id;
|
||||
if (arg_pos == -1)
|
||||
arg_pos = static_cast<i64>(m_arg_pos++);
|
||||
|
||||
setup_stream(*spec_it);
|
||||
m_arg_print_funcs[arg_pos](m_stream, *spec_it);
|
||||
}
|
||||
m_stream << process_string(*str_it);
|
||||
}
|
||||
|
||||
void logger_t::handle_type(std::ostream& stream, const fmt_spec_t& spec)
|
||||
{
|
||||
switch (spec.type)
|
||||
{
|
||||
case fmt_type_t::DECIMAL:
|
||||
stream << std::noboolalpha;
|
||||
stream << std::dec;
|
||||
break;
|
||||
case fmt_type_t::OCTAL:
|
||||
stream << std::oct;
|
||||
break;
|
||||
case fmt_type_t::HEX:
|
||||
stream << std::hex;
|
||||
break;
|
||||
case fmt_type_t::HEX_FLOAT:
|
||||
stream << std::hexfloat;
|
||||
break;
|
||||
case fmt_type_t::EXPONENT:
|
||||
stream << std::scientific;
|
||||
break;
|
||||
case fmt_type_t::FIXED_POINT:
|
||||
stream << std::fixed;
|
||||
break;
|
||||
case fmt_type_t::GENERAL:
|
||||
stream << std::defaultfloat;
|
||||
break;
|
||||
case fmt_type_t::UNSPECIFIED:
|
||||
stream << std::boolalpha;
|
||||
stream << std::dec;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void logger_t::exponential(std::ostream& stream)
|
||||
{
|
||||
stream << std::scientific;
|
||||
}
|
||||
|
||||
void logger_t::fixed(std::ostream& stream)
|
||||
{
|
||||
stream << std::fixed;
|
||||
}
|
||||
|
||||
void logger_t::compile(std::string fmt)
|
||||
{
|
||||
m_fmt = std::move(fmt);
|
||||
m_last_fmt_pos = 0;
|
||||
m_arg_pos = 0;
|
||||
auto& ss = dynamic_cast<std::stringstream&>(m_stream);
|
||||
ss.str("");
|
||||
m_stream.clear();
|
||||
m_string_sections.clear();
|
||||
m_fmt_specs.clear();
|
||||
ptrdiff_t last_pos = 0;
|
||||
while (auto pair = consume_to_next_fmt())
|
||||
{
|
||||
const auto [begin, end] = *pair;
|
||||
m_string_sections.emplace_back(m_fmt.data() + last_pos, begin - last_pos);
|
||||
if (end - begin > 1)
|
||||
m_fmt_specs.push_back(m_parser.parse(std::string_view{m_fmt.data() + static_cast<ptrdiff_t>(begin) + 1, end - begin - 1}));
|
||||
else
|
||||
m_fmt_specs.emplace_back();
|
||||
last_pos = static_cast<ptrdiff_t>(end) + 1;
|
||||
}
|
||||
m_string_sections.emplace_back(m_fmt.data() + last_pos, m_fmt.size() - last_pos);
|
||||
m_last_fmt_pos = 0;
|
||||
}
|
||||
|
||||
std::optional<std::pair<size_t, size_t>> logger_t::consume_to_next_fmt()
|
||||
{
|
||||
auto begin = m_fmt.find('{', m_last_fmt_pos);
|
||||
while (begin != std::string::npos && begin > 0 && m_fmt[begin - 1] == '\\')
|
||||
begin = m_fmt.find('{', begin + 1);;
|
||||
if (begin == std::string::npos)
|
||||
return {};
|
||||
const auto end = find_ending_brace(begin);
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Invalid format string, missing closing '}' near " << m_fmt.substr(std::min(static_cast<i64>(begin) - 5, static_cast<i64>(0)));
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
m_last_fmt_pos = end + 1;
|
||||
return std::pair{begin, end};
|
||||
}
|
||||
|
||||
logger_t& get_global_logger()
|
||||
{
|
||||
return get_thread_context().logger;
|
||||
}
|
||||
|
||||
void print(std::string str)
|
||||
{
|
||||
const auto& config = get_global_config();
|
||||
bool should_print = true;
|
||||
if (!config.get_injectors().empty())
|
||||
{
|
||||
for (const auto& injector : config.get_injectors())
|
||||
{
|
||||
auto [new_logging_output, should_continue, should_log] = injector->inject(str);
|
||||
if (!should_log)
|
||||
should_print = false;
|
||||
str = std::move(new_logging_output);
|
||||
if (!should_continue)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (should_print)
|
||||
std::cout << str;
|
||||
}
|
||||
|
||||
void newline()
|
||||
{
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
logging_config_t& get_global_config()
|
||||
{
|
||||
return global_context.global_config;
|
||||
}
|
||||
|
||||
void set_thread_name(const std::string& name)
|
||||
{
|
||||
get_thread_context().thread_name = name;
|
||||
}
|
||||
|
||||
const std::string& get_thread_name()
|
||||
{
|
||||
return get_thread_context().thread_name;
|
||||
}
|
||||
|
||||
std::ostream& get_local_stream()
|
||||
{
|
||||
auto& context = get_thread_context();
|
||||
context.logging_stream.str("");
|
||||
return context.logging_stream;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <blt/fs/path_helper.h>
|
||||
#include <blt/fs/stream_wrappers.h>
|
||||
#include <blt/logging/ansi.h>
|
||||
#include <blt/logging/logging_config.h>
|
||||
#include <blt/std/hashmap.h>
|
||||
#include <blt/std/system.h>
|
||||
#include <blt/std/time.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
namespace tags::detail
|
||||
{
|
||||
hashmap_t<std::string_view, log_tag_token_t> make_map()
|
||||
{
|
||||
hashmap_t<std::string_view, log_tag_token_t> map{};
|
||||
map[YEAR] = log_tag_token_t::YEAR;
|
||||
map[MONTH] = log_tag_token_t::MONTH;
|
||||
map[DAY] = log_tag_token_t::DAY;
|
||||
map[HOUR] = log_tag_token_t::HOUR;
|
||||
map[MINUTE] = log_tag_token_t::MINUTE;
|
||||
map[SECOND] = log_tag_token_t::SECOND;
|
||||
map[MILLISECOND] = log_tag_token_t::MS;
|
||||
map[NANOSECOND] = log_tag_token_t::NS;
|
||||
map[UNIX_TIME] = log_tag_token_t::UNIX;
|
||||
map[UNIX_TIME_NANO] = log_tag_token_t::UNIX_NANO;
|
||||
map[ISO_YEAR] = log_tag_token_t::ISO_YEAR;
|
||||
map[TIME] = log_tag_token_t::TIME;
|
||||
map[FULL_TIME] = log_tag_token_t::FULL_TIME;
|
||||
map[LOG_COLOR] = log_tag_token_t::LC;
|
||||
map[ERROR_COLOR] = log_tag_token_t::EC;
|
||||
map[CONDITIONAL_ERROR_COLOR] = log_tag_token_t::CEC;
|
||||
map[RESET] = log_tag_token_t::RESET;
|
||||
map[LOG_LEVEL] = log_tag_token_t::LL;
|
||||
map[THREAD_NAME] = log_tag_token_t::TN;
|
||||
map[FILE] = log_tag_token_t::FILE;
|
||||
map[LINE] = log_tag_token_t::LINE;
|
||||
map[STR] = log_tag_token_t::STR;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
void logging_config_t::compile()
|
||||
{
|
||||
static hashmap_t<std::string_view, tags::detail::log_tag_token_t> tag_map = tags::detail::make_map();
|
||||
m_log_tag_content.clear();
|
||||
m_log_tag_tokens.clear();
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < m_log_format.size(); ++i)
|
||||
{
|
||||
size_t start = i;
|
||||
while (i < m_log_format.size() && m_log_format[i] != '{')
|
||||
++i;
|
||||
if (i == m_log_format.size() || (i < m_log_format.size() && (i - start) > 0))
|
||||
{
|
||||
m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + start, i - start));
|
||||
m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
|
||||
if (i == m_log_format.size())
|
||||
break;
|
||||
}
|
||||
start = i;
|
||||
while (i < m_log_format.size() && m_log_format[i] != '}')
|
||||
++i;
|
||||
const auto tag = std::string_view(m_log_format.data() + start, i - start + 1);
|
||||
auto it = tag_map.find(tag);
|
||||
if (it == tag_map.end())
|
||||
throw std::runtime_error("Invalid log tag: " + std::string(tag));
|
||||
m_log_tag_tokens.emplace_back(it->second);
|
||||
}
|
||||
|
||||
if (i < m_log_format.size())
|
||||
{
|
||||
m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + i, m_log_format.size() - i));
|
||||
m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
|
||||
}
|
||||
|
||||
m_longest_name_length = 0;
|
||||
for (const auto& name : m_log_level_names)
|
||||
m_longest_name_length = std::max(m_longest_name_length, name.size());
|
||||
}
|
||||
|
||||
std::string add_year(const tm* current_time)
|
||||
{
|
||||
return std::to_string(current_time->tm_year + 1900);
|
||||
}
|
||||
|
||||
std::string add_month(const tm* current_time, const bool ensure_alignment)
|
||||
{
|
||||
auto str = std::to_string(current_time->tm_mon + 1);
|
||||
if (ensure_alignment && str.size() < 2)
|
||||
str.insert(str.begin(), '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string add_day(const tm* current_time, const bool ensure_alignment)
|
||||
{
|
||||
auto str = std::to_string(current_time->tm_mday);
|
||||
if (ensure_alignment && str.size() < 2)
|
||||
str.insert(str.begin(), '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string add_hour(const tm* current_time, const bool ensure_alignment)
|
||||
{
|
||||
auto str = std::to_string(current_time->tm_hour);
|
||||
if (ensure_alignment && str.size() < 2)
|
||||
str.insert(str.begin(), '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string add_minute(const tm* current_time, const bool ensure_alignment)
|
||||
{
|
||||
auto str = std::to_string(current_time->tm_min);
|
||||
if (ensure_alignment && str.size() < 2)
|
||||
str.insert(str.begin(), '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string add_second(const tm* current_time, const bool ensure_alignment)
|
||||
{
|
||||
auto str = std::to_string(current_time->tm_sec);
|
||||
if (ensure_alignment && str.size() < 2)
|
||||
str.insert(str.begin(), '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
std::optional<std::string> logging_config_t::generate(const std::string& user_str, const std::string& thread_name, const log_level_t level,
|
||||
const char* file, const i32 line) const
|
||||
{
|
||||
if (level < m_level)
|
||||
return {};
|
||||
|
||||
std::string fmt;
|
||||
|
||||
const std::time_t time = std::time(nullptr);
|
||||
const auto current_time = std::localtime(&time);
|
||||
const auto millis_time = system::getCurrentTimeMilliseconds();
|
||||
const auto nano_time = system::getCurrentTimeNanoseconds();
|
||||
|
||||
size_t content = 0;
|
||||
for (const auto& log_tag_token : m_log_tag_tokens)
|
||||
{
|
||||
switch (log_tag_token)
|
||||
{
|
||||
case tags::detail::log_tag_token_t::YEAR:
|
||||
fmt += add_year(current_time);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::MONTH:
|
||||
fmt += add_month(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::DAY:
|
||||
fmt += add_day(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::HOUR:
|
||||
fmt += add_hour(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::MINUTE:
|
||||
fmt += add_minute(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::SECOND:
|
||||
fmt += add_second(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::MS:
|
||||
{
|
||||
auto str = std::to_string(millis_time % 1000);
|
||||
if (m_ensure_alignment)
|
||||
{
|
||||
for (size_t i = str.size(); i < 4; ++i)
|
||||
str.insert(str.begin(), '0');
|
||||
}
|
||||
fmt += str;
|
||||
break;
|
||||
}
|
||||
case tags::detail::log_tag_token_t::NS:
|
||||
{
|
||||
auto str = std::to_string(nano_time % 1000000000ul);
|
||||
if (m_ensure_alignment)
|
||||
{
|
||||
for (size_t i = str.size(); i < 9; ++i)
|
||||
str.insert(str.begin(), '0');
|
||||
}
|
||||
fmt += str;
|
||||
break;
|
||||
}
|
||||
case tags::detail::log_tag_token_t::UNIX:
|
||||
{
|
||||
fmt += std::to_string(millis_time);
|
||||
break;
|
||||
}
|
||||
case tags::detail::log_tag_token_t::UNIX_NANO:
|
||||
{
|
||||
fmt += std::to_string(nano_time);
|
||||
break;
|
||||
}
|
||||
case tags::detail::log_tag_token_t::ISO_YEAR:
|
||||
{
|
||||
fmt += add_year(current_time);
|
||||
fmt += '-';
|
||||
fmt += add_month(current_time, m_ensure_alignment);
|
||||
fmt += '-';
|
||||
fmt += add_day(current_time, m_ensure_alignment);
|
||||
break;
|
||||
}
|
||||
case tags::detail::log_tag_token_t::TIME:
|
||||
fmt += add_hour(current_time, m_ensure_alignment);
|
||||
fmt += ':';
|
||||
fmt += add_minute(current_time, m_ensure_alignment);
|
||||
fmt += ':';
|
||||
fmt += add_second(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::FULL_TIME:
|
||||
fmt += add_year(current_time);
|
||||
fmt += '-';
|
||||
fmt += add_month(current_time, m_ensure_alignment);
|
||||
fmt += '-';
|
||||
fmt += add_day(current_time, m_ensure_alignment);
|
||||
fmt += ' ';
|
||||
fmt += add_hour(current_time, m_ensure_alignment);
|
||||
fmt += ':';
|
||||
fmt += add_minute(current_time, m_ensure_alignment);
|
||||
fmt += ':';
|
||||
fmt += add_second(current_time, m_ensure_alignment);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::LC:
|
||||
if (!m_use_color)
|
||||
break;
|
||||
fmt += m_log_level_colors[static_cast<u8>(level)];
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::EC:
|
||||
if (!m_use_color)
|
||||
break;
|
||||
fmt += m_error_color;
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::CEC:
|
||||
if (!m_use_color)
|
||||
break;
|
||||
if (static_cast<u8>(level) >= static_cast<u8>(log_level_t::ERROR))
|
||||
fmt += m_error_color;
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::RESET:
|
||||
if (!m_use_color)
|
||||
break;
|
||||
fmt += build(ansi::color::color_mode::RESET_ALL);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::LL:
|
||||
fmt += m_log_level_names[static_cast<u8>(level)];
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::TN:
|
||||
fmt += thread_name;
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::FILE:
|
||||
if (m_print_full_name)
|
||||
fmt += file;
|
||||
else
|
||||
fmt += fs::filename_sv(file);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::LINE:
|
||||
fmt += std::to_string(line);
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::STR:
|
||||
fmt += user_str;
|
||||
break;
|
||||
case tags::detail::log_tag_token_t::CONTENT:
|
||||
fmt += m_log_tag_content[content++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
std::string logging_config_t::get_default_log_format()
|
||||
{
|
||||
return build(fg(ansi::color::color8_bright::BLUE)) + "[" + tags::FULL_TIME + "]" + tags::RESET + " " + tags::LOG_COLOR + "[" + tags::LOG_LEVEL
|
||||
+ "]" + tags::RESET + " " + build(fg(ansi::color::color8::MAGENTA)) + "(" + tags::FILE + ":" + tags::LINE + ")" + tags::RESET + " " +
|
||||
tags::CONDITIONAL_ERROR_COLOR + tags::STR + tags::RESET + "\n";
|
||||
}
|
||||
|
||||
std::vector<fs::writer_t*> logging_config_t::get_default_log_outputs()
|
||||
{
|
||||
static fs::fstream_writer_t cout_writer{std::cout};
|
||||
std::vector<fs::writer_t*> outputs{};
|
||||
outputs.push_back(&cout_writer);
|
||||
return outputs;
|
||||
}
|
||||
|
||||
std::array<std::string, LOG_LEVEL_COUNT> logging_config_t::get_default_log_level_colors()
|
||||
{
|
||||
return {
|
||||
// TRACE
|
||||
build(fg(ansi::color::color8_bright::WHITE)),
|
||||
// DEBUG
|
||||
build(fg(ansi::color::color8::CYAN)),
|
||||
// INFO
|
||||
build(fg(ansi::color::color8_bright::GREEN)),
|
||||
// WARN
|
||||
build(fg(ansi::color::color8_bright::YELLOW)),
|
||||
// ERROR
|
||||
build(fg(ansi::color::color8_bright::RED)),
|
||||
// FATAL
|
||||
build(fg(ansi::color::color8_bright::WHITE), bg(ansi::color::color8_bright::RED)),
|
||||
};
|
||||
}
|
||||
|
||||
std::array<std::string, LOG_LEVEL_COUNT> logging_config_t::get_default_log_level_names()
|
||||
{
|
||||
return {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL",};
|
||||
}
|
||||
|
||||
std::string logging_config_t::get_default_error_color()
|
||||
{
|
||||
return build(fg(ansi::color::color8_bright::RED));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#ifdef unix
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <blt/logging/ansi.h>
|
||||
#include <blt/logging/fmt_tokenizer.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/logging/status.h>
|
||||
#include <blt/math/vectors.h>
|
||||
#include <blt/math/log_util.h>
|
||||
|
||||
namespace blt::logging
|
||||
{
|
||||
vec2i get_cursor_position()
|
||||
{
|
||||
#ifdef unix
|
||||
termios save{}, raw{};
|
||||
|
||||
tcgetattr(0, &save);
|
||||
cfmakeraw(&raw);
|
||||
tcsetattr(0,TCSANOW, &raw);
|
||||
|
||||
char buf[32];
|
||||
char cmd[] = "\033[6n";
|
||||
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
if (isatty(fileno(stdin)))
|
||||
{
|
||||
ssize_t i = write(1, cmd, sizeof(cmd));
|
||||
(void) i;
|
||||
i = read(0, buf, sizeof(buf));
|
||||
(void) i;
|
||||
|
||||
int sep = 0;
|
||||
int end = 0;
|
||||
for (int i = 2; i < 8; i++)
|
||||
{
|
||||
if (buf[i] == ';')
|
||||
sep = i;
|
||||
if (buf[i] == 'R')
|
||||
{
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
row = std::stoi(std::string(buf + 2, buf + sep));
|
||||
col = std::stoi(std::string(buf + sep + 1, buf + end));
|
||||
}
|
||||
|
||||
tcsetattr(0,TCSANOW, &save);
|
||||
|
||||
return vec2i{row, col};
|
||||
#else
|
||||
return {0,0};
|
||||
#endif
|
||||
}
|
||||
|
||||
#define SIZE 100
|
||||
|
||||
vec2i get_screen_size()
|
||||
{
|
||||
#ifdef unix
|
||||
char in[SIZE] = "";
|
||||
int each = 0;
|
||||
int ch = 0;
|
||||
int rows = 0;
|
||||
int cols = 0;
|
||||
termios original, changed;
|
||||
|
||||
// change terminal settings
|
||||
tcgetattr( STDIN_FILENO, &original);
|
||||
changed = original;
|
||||
changed.c_lflag &= ~( ICANON | ECHO);
|
||||
changed.c_cc[VMIN] = 1;
|
||||
changed.c_cc[VTIME] = 0;
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &changed);
|
||||
|
||||
printf ( "\033[2J"); //clear screen
|
||||
|
||||
printf ( "\033[9999;9999H"); // cursor should move as far as it can
|
||||
|
||||
printf ( "\033[6n"); // ask for cursor position
|
||||
while ( ( ch = getchar ()) != 'R') { // R terminates the response
|
||||
if ( EOF == ch) {
|
||||
break;
|
||||
}
|
||||
if ( isprint ( ch)) {
|
||||
if ( each + 1 < SIZE) {
|
||||
in[each] = ch;
|
||||
each++;
|
||||
in[each] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf ( "\033[1;1H"); // move to upper left corner
|
||||
if ( 2 == sscanf ( in, "[%d;%d", &rows, &cols)) {
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &original);
|
||||
return {rows, cols};
|
||||
}
|
||||
throw std::runtime_error("Could not get screen size");
|
||||
#else
|
||||
return {0,0};
|
||||
#endif
|
||||
}
|
||||
|
||||
i32 get_size_no_ansi(const std::string& str)
|
||||
{
|
||||
i32 size = 0;
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
{
|
||||
if (str[i] == BLT_ANSI_ESCAPE[0])
|
||||
{
|
||||
while (i < str.size())
|
||||
{
|
||||
if (std::isalpha(str[i++]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
++size;
|
||||
}
|
||||
return size - 1;
|
||||
}
|
||||
|
||||
std::string status_progress_bar_t::print(const vec2i, const i32 max_printed_length) const
|
||||
{
|
||||
std::string output = "[";
|
||||
output.reserve(max_printed_length);
|
||||
const auto amount_filled = (max_printed_length - 2) * m_progress;
|
||||
auto amount_filled_int = static_cast<i32>(amount_filled);
|
||||
const auto frac = amount_filled - static_cast<double>(amount_filled_int);
|
||||
|
||||
for (i64 i = 0; i < amount_filled_int; i++)
|
||||
output += '#';
|
||||
if (frac >= 0.5)
|
||||
{
|
||||
output += '|';
|
||||
++amount_filled_int;
|
||||
}
|
||||
for (i64 i = amount_filled_int; i < max_printed_length - 2; i++)
|
||||
output += ' ';
|
||||
|
||||
output += ']';
|
||||
return output;
|
||||
}
|
||||
|
||||
void status_progress_bar_t::set_progress(const double progress)
|
||||
{
|
||||
if (std::isnan(progress) || progress < 0 || progress > 1 || std::isinf(progress))
|
||||
throw std::invalid_argument("Progress must be between 0 and 1 (got: " + std::to_string(progress) + ")");
|
||||
m_progress = progress;
|
||||
// m_status->redraw();
|
||||
}
|
||||
|
||||
status_bar_t::status_bar_t()
|
||||
{
|
||||
m_screen_size = get_screen_size();
|
||||
std::cout << ansi::cursor::home << std::flush;
|
||||
std::cout << ansi::erase::entire_screen << std::flush;
|
||||
m_begin_position = m_last_log_position = get_cursor_position();
|
||||
std::cout << ansi::cursor::hide_cursor << std::flush;
|
||||
}
|
||||
|
||||
injector_output_t status_bar_t::inject(const std::string& input)
|
||||
{
|
||||
std::scoped_lock lock{m_print_mutex};
|
||||
injector_output_t output{input, false, false};
|
||||
if (output.new_logging_output.back() != '\n')
|
||||
output.new_logging_output += '\n';
|
||||
|
||||
if (get_cursor_position() != m_begin_position)
|
||||
{
|
||||
for (int i = 0; i < m_status_size; i++)
|
||||
std::cout << ansi::erase::entire_line << ansi::cursor::move_begin_up(1) << std::flush;
|
||||
}
|
||||
std::cout << ansi::erase::entire_line << std::flush;
|
||||
std::cout << output.new_logging_output << std::flush;
|
||||
m_max_printed_length = std::max(get_size_no_ansi(output.new_logging_output), m_max_printed_length);
|
||||
m_last_log_position = get_cursor_position();
|
||||
redraw();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void status_bar_t::compute_size()
|
||||
{
|
||||
m_status_size = 0;
|
||||
for (const auto* ptr : m_status_items)
|
||||
m_status_size += ptr->lines_used();
|
||||
}
|
||||
|
||||
void status_bar_t::redraw() const
|
||||
{
|
||||
std::cout << ansi::cursor::move_to(m_last_log_position.x(), m_last_log_position.y());
|
||||
for (const auto* ptr : m_status_items)
|
||||
std::cout << ansi::erase::entire_line << ptr->print(m_screen_size, m_max_printed_length) << std::endl;
|
||||
std::cout << std::flush;
|
||||
}
|
||||
|
||||
status_bar_t::~status_bar_t()
|
||||
{
|
||||
std::cout << ansi::cursor::show_cursor << std::flush;
|
||||
}
|
||||
}
|
|
@ -196,7 +196,7 @@ namespace blt
|
|||
printUsage();
|
||||
std::cout << getProgramName() << ": error: flag '" << flag << "' expected " << properties.a_nargs.args
|
||||
<< " argument(s) but found '" << tokenizer.get() << "' instead!\n";
|
||||
//BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args);
|
||||
//BLT_WARN("Expected {:d} arguments, found flag instead!", properties.a_nargs.args);
|
||||
return false;
|
||||
}
|
||||
// get the value and advance
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,231 +26,231 @@
|
|||
#include <charconv>
|
||||
#include "blt/std/assert.h"
|
||||
#include "blt/std/utility.h"
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
|
||||
namespace blt::parse
|
||||
{
|
||||
class char_tokenizer
|
||||
{
|
||||
private:
|
||||
std::string_view string;
|
||||
std::size_t current_pos = 0;
|
||||
public:
|
||||
explicit char_tokenizer(std::string_view view): string(view)
|
||||
{}
|
||||
class char_tokenizer
|
||||
{
|
||||
private:
|
||||
std::string_view string;
|
||||
std::size_t current_pos = 0;
|
||||
|
||||
inline char advance()
|
||||
{
|
||||
return string[current_pos++];
|
||||
}
|
||||
public:
|
||||
explicit char_tokenizer(std::string_view view): string(view)
|
||||
{}
|
||||
|
||||
inline bool has_next(size_t offset = 0)
|
||||
{
|
||||
return current_pos + offset < string.size();
|
||||
}
|
||||
inline char advance()
|
||||
{
|
||||
return string[current_pos++];
|
||||
}
|
||||
|
||||
inline std::string_view read_fully()
|
||||
{
|
||||
return blt::string::trim(string.substr(current_pos));
|
||||
}
|
||||
};
|
||||
inline bool has_next(size_t offset = 0)
|
||||
{
|
||||
return current_pos + offset < string.size();
|
||||
}
|
||||
|
||||
template<typename T = float>
|
||||
T get(std::string_view str)
|
||||
{
|
||||
T x;
|
||||
// TODO: GCC version. C++17 supports from_chars but GCC8.5 doesn't have floating point.
|
||||
#if __cplusplus >= BLT_CPP20
|
||||
inline std::string_view read_fully()
|
||||
{
|
||||
return blt::string::trim(string.substr(current_pos));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = float>
|
||||
T get(std::string_view str)
|
||||
{
|
||||
T x;
|
||||
// TODO: GCC version. C++17 supports from_chars but GCC8.5 doesn't have floating point.
|
||||
#if __cplusplus >= BLT_CPP20
|
||||
const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), x);
|
||||
#else
|
||||
auto ec = std::errc();
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
x = static_cast<T>(std::stod(std::string(str)));
|
||||
} else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
x = static_cast<T>(std::stoll(std::string(str)));
|
||||
} else
|
||||
static_assert(
|
||||
"You are using a c++ version which does not support the required std::from_chars, manual conversion has failed to find a type!");
|
||||
#endif
|
||||
// probably not needed.
|
||||
if (ec != std::errc())
|
||||
{
|
||||
// int i;
|
||||
// const auto [ptr2, ec2] = std::from_chars(str.data(), str.data() + str.size(), i);
|
||||
// if (ec2 == std::errc())
|
||||
// {
|
||||
// x = static_cast<float>(i);
|
||||
// } else
|
||||
// {
|
||||
BLT_WARN("Unable to parse string '%s' into number!", std::string(str).c_str());
|
||||
x = 0;
|
||||
// }
|
||||
}
|
||||
return x;
|
||||
}
|
||||
#else
|
||||
auto ec = std::errc();
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
x = static_cast<T>(std::stod(std::string(str)));
|
||||
} else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
x = static_cast<T>(std::stoll(std::string(str)));
|
||||
} else
|
||||
static_assert(
|
||||
"You are using a c++ version which does not support the required std::from_chars, manual conversion has failed to find a type!");
|
||||
#endif
|
||||
// probably not needed.
|
||||
if (ec != std::errc())
|
||||
{
|
||||
// int i;
|
||||
// const auto [ptr2, ec2] = std::from_chars(str.data(), str.data() + str.size(), i);
|
||||
// if (ec2 == std::errc())
|
||||
// {
|
||||
// x = static_cast<float>(i);
|
||||
// } else
|
||||
// {
|
||||
BLT_WARN("Unable to parse string '{}' into number!", std::string(str).c_str());
|
||||
x = 0;
|
||||
// }
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void obj_loader::parse_vertex_line(char_tokenizer& tokenizer)
|
||||
{
|
||||
char type = tokenizer.advance();
|
||||
void obj_loader::parse_vertex_line(char_tokenizer& tokenizer)
|
||||
{
|
||||
char type = tokenizer.advance();
|
||||
|
||||
if (type == 'p')
|
||||
{
|
||||
BLT_WARN("Unexpected type '%c' (not supported)", type);
|
||||
return;
|
||||
}
|
||||
if (type == 'p')
|
||||
{
|
||||
BLT_WARN("Unexpected type '{:c}' (not supported)", type);
|
||||
return;
|
||||
}
|
||||
|
||||
auto elements = blt::string::split(std::string(tokenizer.read_fully()), " ");
|
||||
BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!");
|
||||
float x = get(elements[0]), y = get(elements[1]);
|
||||
BLT_DEBUG_STREAM << "Loaded value of (" << x << ", " << y << ")";
|
||||
if (elements.size() < 3)
|
||||
{
|
||||
if (type == 't')
|
||||
uvs.push_back(uv_t{x, y});
|
||||
else
|
||||
BLT_ERROR("Unable to parse line '%s' type '%c' not recognized for arg count", std::string(tokenizer.read_fully()).c_str(), type);
|
||||
} else
|
||||
{
|
||||
float z = get(elements[2]);
|
||||
BLT_DEBUG_STREAM << " with z: " << z;
|
||||
if (!handle_vertex_and_normals(x, y, z, type))
|
||||
BLT_ERROR("Unable to parse line '%s' type '%c' not recognized", std::string(tokenizer.read_fully()).c_str(), type);
|
||||
}
|
||||
BLT_DEBUG_STREAM << "\n";
|
||||
}
|
||||
auto elements = string::split(tokenizer.read_fully(), " ");
|
||||
BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!");
|
||||
float x = get(elements[0]), y = get(elements[1]);
|
||||
BLT_DEBUG("Loaded value of ({}, {})", x, y);
|
||||
if (elements.size() < 3)
|
||||
{
|
||||
if (type == 't')
|
||||
uvs.push_back(uv_t{x, y});
|
||||
else
|
||||
BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized for arg count", tokenizer.read_fully(), type);
|
||||
} else
|
||||
{
|
||||
float z = get(elements[2]);
|
||||
BLT_DEBUG(" with z: {}", z);
|
||||
if (!handle_vertex_and_normals(x, y, z, type))
|
||||
BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized", tokenizer.read_fully(), type);
|
||||
}
|
||||
}
|
||||
|
||||
bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type)
|
||||
{
|
||||
if (std::isspace(type))
|
||||
{
|
||||
vertices.push_back(vertex_t{x, y, z});
|
||||
} else if (type == 'n')
|
||||
{
|
||||
normals.push_back(normal_t{x, y, z});
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type)
|
||||
{
|
||||
if (std::isspace(type))
|
||||
{
|
||||
vertices.push_back(vertex_t{x, y, z});
|
||||
} else if (type == 'n')
|
||||
{
|
||||
normals.push_back(normal_t{x, y, z});
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
obj_model_t quick_load(std::string_view file)
|
||||
{
|
||||
return obj_loader().parseFile(file);
|
||||
}
|
||||
obj_model_t quick_load(std::string_view file)
|
||||
{
|
||||
return obj_loader().parseFile(file);
|
||||
}
|
||||
|
||||
obj_model_t obj_loader::parseFile(std::string_view file)
|
||||
{
|
||||
auto lines = blt::fs::getLinesFromFile(std::string(file));
|
||||
for (const auto& [index, line] : blt::enumerate(lines))
|
||||
{
|
||||
current_line = index;
|
||||
char_tokenizer token(line);
|
||||
if (!token.has_next() || token.read_fully().empty())
|
||||
continue;
|
||||
switch (token.advance())
|
||||
{
|
||||
case '#':
|
||||
continue;
|
||||
case 'f':
|
||||
parse_face(token);
|
||||
break;
|
||||
case 'v':
|
||||
parse_vertex_line(token);
|
||||
break;
|
||||
case 'o':
|
||||
{
|
||||
current_object.object_names.emplace_back(token.read_fully());
|
||||
BLT_TRACE("Setting object '%s'", std::string(current_object.object_name).c_str());
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
{
|
||||
while (token.has_next() && token.advance() != ' ')
|
||||
{}
|
||||
BLT_WARN("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
if (!current_object.indices.empty())
|
||||
data.push_back(current_object);
|
||||
current_object = {};
|
||||
while (token.has_next() && token.advance() != ' ')
|
||||
{}
|
||||
current_object.material = token.read_fully();
|
||||
//BLT_WARN("Using material '%s'", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
//BLT_WARN("Using shading: %s", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
data.push_back(current_object);
|
||||
return {std::move(vertex_data), std::move(data), std::move(materials)};
|
||||
}
|
||||
obj_model_t obj_loader::parseFile(std::string_view file)
|
||||
{
|
||||
auto lines = blt::fs::getLinesFromFile(std::string(file));
|
||||
for (const auto& [index, line] : blt::enumerate(lines))
|
||||
{
|
||||
current_line = index;
|
||||
char_tokenizer token(line);
|
||||
if (!token.has_next() || token.read_fully().empty())
|
||||
continue;
|
||||
switch (token.advance())
|
||||
{
|
||||
case '#':
|
||||
continue;
|
||||
case 'f':
|
||||
parse_face(token);
|
||||
break;
|
||||
case 'v':
|
||||
parse_vertex_line(token);
|
||||
break;
|
||||
case 'o':
|
||||
{
|
||||
current_object.object_names.emplace_back(token.read_fully());
|
||||
BLT_TRACE("Setting object '{}'", std::string(current_object.object_name).c_str());
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
{
|
||||
while (token.has_next() && token.advance() != ' ')
|
||||
{}
|
||||
BLT_WARN("Material '{}' needs to be loaded!", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
if (!current_object.indices.empty())
|
||||
data.push_back(current_object);
|
||||
current_object = {};
|
||||
while (token.has_next() && token.advance() != ' ')
|
||||
{}
|
||||
current_object.material = token.read_fully();
|
||||
//BLT_WARN("Using material '{}'", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
//BLT_WARN("Using shading: {}", std::string(token.read_fully()).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
data.push_back(current_object);
|
||||
return {std::move(vertex_data), std::move(data), std::move(materials)};
|
||||
}
|
||||
|
||||
void obj_loader::parse_face(char_tokenizer& tokenizer)
|
||||
{
|
||||
auto faces = blt::string::split(std::string(tokenizer.read_fully()), ' ');
|
||||
if (faces.size() == 3)
|
||||
{
|
||||
triangle_t triangle{};
|
||||
handle_face_vertex(faces, triangle.v);
|
||||
current_object.indices.push_back(triangle);
|
||||
} else if (faces.size() == 4)
|
||||
{
|
||||
quad_t quad{};
|
||||
handle_face_vertex(faces, quad.v);
|
||||
triangle_t t1{};
|
||||
triangle_t t2{};
|
||||
void obj_loader::parse_face(char_tokenizer& tokenizer)
|
||||
{
|
||||
auto faces = blt::string::split(std::string(tokenizer.read_fully()), ' ');
|
||||
if (faces.size() == 3)
|
||||
{
|
||||
triangle_t triangle{};
|
||||
handle_face_vertex(faces, triangle.v);
|
||||
current_object.indices.push_back(triangle);
|
||||
} else if (faces.size() == 4)
|
||||
{
|
||||
quad_t quad{};
|
||||
handle_face_vertex(faces, quad.v);
|
||||
triangle_t t1{};
|
||||
triangle_t t2{};
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
t1.v[i] = quad.v[i];
|
||||
t2.v[0] = quad.v[0];
|
||||
t2.v[1] = quad.v[2];
|
||||
t2.v[2] = quad.v[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
t1.v[i] = quad.v[i];
|
||||
t2.v[0] = quad.v[0];
|
||||
t2.v[1] = quad.v[2];
|
||||
t2.v[2] = quad.v[3];
|
||||
|
||||
current_object.indices.push_back(t1);
|
||||
current_object.indices.push_back(t2);
|
||||
} else
|
||||
BLT_WARN("Unsupported face vertex count of %d on line %d!", faces.size(), current_line);
|
||||
}
|
||||
current_object.indices.push_back(t1);
|
||||
current_object.indices.push_back(t2);
|
||||
} else
|
||||
BLT_WARN("Unsupported face vertex count of {:d} on line {:d}!", faces.size(), current_line);
|
||||
}
|
||||
|
||||
void obj_loader::handle_face_vertex(const std::vector<std::string>& face_list, int32_t* arr)
|
||||
{
|
||||
for (const auto& [e_index, value] : blt::enumerate(face_list))
|
||||
{
|
||||
auto indices = blt::string::split(value, '/');
|
||||
BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!");
|
||||
void obj_loader::handle_face_vertex(const std::vector<std::string>& face_list, int32_t* arr)
|
||||
{
|
||||
for (const auto& [e_index, value] : blt::enumerate(face_list))
|
||||
{
|
||||
auto indices = blt::string::split(value, '/');
|
||||
BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!");
|
||||
|
||||
auto vi = get<std::int32_t>(indices[0]) - 1;
|
||||
auto ui = get<std::int32_t>(indices[1]) - 1;
|
||||
auto ni = get<std::int32_t>(indices[2]) - 1;
|
||||
auto vi = get<std::int32_t>(indices[0]) - 1;
|
||||
auto ui = get<std::int32_t>(indices[1]) - 1;
|
||||
auto ni = get<std::int32_t>(indices[2]) - 1;
|
||||
|
||||
BLT_DEBUG("Found vertex: %d, UV: %d, and normal: %d", vi, ui, ni);
|
||||
BLT_DEBUG("Found vertex: {:d}, UV: {:d}, and normal: {:d}", vi, ui, ni);
|
||||
|
||||
face_t face{vi, ui, ni};
|
||||
face_t face{vi, ui, ni};
|
||||
|
||||
auto loc = vertex_map.find(face);
|
||||
if (loc == vertex_map.end())
|
||||
{
|
||||
BLT_DEBUG("DID NOT FIND FACE!");
|
||||
auto index = static_cast<std::int32_t>(vertex_data.size());
|
||||
vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]});
|
||||
BLT_DEBUG("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", vertices[vi].x(), vertices[vi].y(), vertices[vi].z(),
|
||||
uvs[ui].x(), uvs[ui].y(), normals[ni].x(), normals[ni].y(), normals[ni].z());
|
||||
vertex_map.insert({face, index});
|
||||
arr[e_index] = index;
|
||||
} else
|
||||
{
|
||||
BLT_TRACE("Using cached data; %d; map size: %d", loc->second, vertex_data.size());
|
||||
//const auto& d = vertex_data[loc->second];
|
||||
BLT_TRACE("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", d.vertex.x(), d.vertex.y(), d.vertex.z(),
|
||||
d.uv.x(), d.uv.y(), d.normal.x(), d.normal.y(), d.normal.z());
|
||||
arr[e_index] = loc->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto loc = vertex_map.find(face);
|
||||
if (loc == vertex_map.end())
|
||||
{
|
||||
BLT_DEBUG("DID NOT FIND FACE!");
|
||||
auto index = static_cast<std::int32_t>(vertex_data.size());
|
||||
vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]});
|
||||
BLT_DEBUG("Vertex: ({.4f}, {.4f}, {.4f}), UV: ({.4f}, {.4f}), Normal: ({.4f}, {.4f}, {:.4f})", vertices[vi].x(), vertices[vi].y(),
|
||||
vertices[vi].z(), uvs[ui].x(), uvs[ui].y(), normals[ni].x(), normals[ni].y(), normals[ni].z());
|
||||
vertex_map.insert({face, index});
|
||||
arr[e_index] = index;
|
||||
} else
|
||||
{
|
||||
BLT_TRACE("Using cached data; {:d}; map size: {:d}", loc->second, vertex_data.size());
|
||||
//const auto& d = vertex_data[loc->second];
|
||||
BLT_TRACE("Vertex: ({.4f}, {.4f}, {.4f}), UV: ({.4f}, {.4f}), Normal: ({.4f}, {.4f}, {:.4f})", d.vertex.x(), d.vertex.y(),
|
||||
d.vertex.z(), d.uv.x(), d.uv.y(), d.normal.x(), d.normal.y(), d.normal.z());
|
||||
arr[e_index] = loc->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
#include <blt/parse/templating.h>
|
||||
#include <blt/std/string.h>
|
||||
#include <cctype>
|
||||
#include "blt/std/logging.h"
|
||||
#include "blt/logging/logging.h"
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
@ -275,7 +275,7 @@ namespace blt
|
|||
values.push_back(b1 ^ b2);
|
||||
break;
|
||||
default:
|
||||
BLT_WARN("Unexpected token '%s'", std::string(next.token).c_str());
|
||||
BLT_WARN("Unexpected token '{}'", std::string(next.token).c_str());
|
||||
return blt::unexpected(template_parser_failure_t::BOOL_TYPE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <blt/std/time.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <blt/format/format.h>
|
||||
|
@ -32,9 +32,9 @@ namespace blt::profiling {
|
|||
difference(difference), name(std::move(name)), total(total) {}
|
||||
};
|
||||
|
||||
inline void println(const std::vector<std::string>&& lines, logging::log_level level) {
|
||||
inline void println(const std::vector<std::string>&& lines, const logging::log_level_t level) {
|
||||
for (const auto& line : lines)
|
||||
BLT_LOG_STREAM(level) << line << "\n";
|
||||
BLT_LOG(level, "{}", line);
|
||||
// auto& logger = logging::getLoggerFromLevel(level);
|
||||
// for (const auto& line : lines)
|
||||
// logger << line << "\n";
|
||||
|
@ -108,7 +108,7 @@ namespace blt::profiling {
|
|||
}
|
||||
|
||||
void printProfile(
|
||||
const std::string& profileName, logging::log_level loggingLevel, bool averageHistory
|
||||
const std::string& profileName, const logging::log_level_t loggingLevel, const bool averageHistory
|
||||
) {
|
||||
auto& profile = profiles[profileName];
|
||||
const auto& intervals = profile.intervals;
|
||||
|
|
|
@ -187,11 +187,11 @@ namespace blt
|
|||
stream << line << "\n";
|
||||
}
|
||||
|
||||
void printProfile(profile_t& profiler, const std::uint32_t flags, sort_by sort, blt::logging::log_level log_level)
|
||||
void printProfile(profile_t& profiler, const std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level)
|
||||
{
|
||||
std::stringstream stream;
|
||||
writeProfile(stream, profiler, flags, sort);
|
||||
BLT_LOG_STREAM(log_level) << stream.str();
|
||||
BLT_LOG(log_level, "{}", stream.str());
|
||||
}
|
||||
|
||||
profile_t::~profile_t()
|
||||
|
@ -237,7 +237,7 @@ namespace blt
|
|||
profiles.erase(profile_name);
|
||||
}
|
||||
|
||||
void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level log_level)
|
||||
void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level)
|
||||
{
|
||||
if (profiles.find(profile_name) == profiles.end())
|
||||
return;
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
* Licensed under GNU General Public License V3.0
|
||||
* See LICENSE file for license detail
|
||||
*/
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/std/utility.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/std/string.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/std/string.h>
|
||||
#include <blt/std/utility.h>
|
||||
|
||||
struct abort_exception final : public std::exception
|
||||
{
|
||||
|
@ -48,8 +48,8 @@ struct abort_exception final : public std::exception
|
|||
|
||||
#ifdef IS_GNU_BACKTRACE
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <cstdlib>
|
||||
#include <execinfo.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -86,7 +86,7 @@ namespace blt
|
|||
#ifdef IS_GNU_BACKTRACE
|
||||
BLT_STACK_TRACE(50);
|
||||
|
||||
BLT_ERROR("An exception '%s' has occurred in file '%s:%d'", what, path, line);
|
||||
BLT_ERROR("An exception '{}' has occurred in file '{}:{:d}'", what, path, line);
|
||||
BLT_ERROR("Stack Trace:");
|
||||
printStacktrace(messages, size, path, line);
|
||||
|
||||
|
@ -103,7 +103,7 @@ namespace blt
|
|||
#ifdef IS_GNU_BACKTRACE
|
||||
BLT_STACK_TRACE(50);
|
||||
|
||||
BLT_ERROR("The assertion '%s' has failed in file '%s:%d'", expression, path, line);
|
||||
BLT_ERROR("The assertion '{}' has failed in file '{}:{:d}'", expression, path, line);
|
||||
if (msg != nullptr)
|
||||
BLT_ERROR(msg);
|
||||
BLT_ERROR("Stack Trace:");
|
||||
|
@ -175,8 +175,8 @@ namespace blt
|
|||
BLT_STACK_TRACE(50);
|
||||
#endif
|
||||
BLT_FATAL("----{BLT ABORT}----");
|
||||
BLT_FATAL("\tWhat: %s", what);
|
||||
BLT_FATAL("\tCalled from %s:%d", path, line);
|
||||
BLT_FATAL("\tWhat: {}", what);
|
||||
BLT_FATAL("\tCalled from {}:{:d}", path, line);
|
||||
#ifdef IS_GNU_BACKTRACE
|
||||
printStacktrace(messages, size, path, line);
|
||||
|
||||
|
|
|
@ -15,68 +15,52 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/std/error.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
|
||||
namespace blt::error
|
||||
{
|
||||
void print_socket_error()
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINVAL:
|
||||
BLT_WARN("Invalid argument");
|
||||
break;
|
||||
case EACCES:
|
||||
BLT_WARN("Permission denied");
|
||||
break;
|
||||
case EPERM:
|
||||
BLT_WARN("Operation not permitted");
|
||||
break;
|
||||
case EADDRINUSE:
|
||||
BLT_WARN("Address already in use");
|
||||
break;
|
||||
case EADDRNOTAVAIL:
|
||||
BLT_WARN("Cannot copy_fast requested address");
|
||||
break;
|
||||
case EAFNOSUPPORT:
|
||||
BLT_WARN("Address family not supported by protocol");
|
||||
break;
|
||||
case EAGAIN:
|
||||
BLT_WARN("Try again");
|
||||
break;
|
||||
case EALREADY:
|
||||
BLT_WARN("Operation already in progress");
|
||||
break;
|
||||
case EBADF:
|
||||
BLT_WARN("Bad file number");
|
||||
break;
|
||||
case ECONNREFUSED:
|
||||
BLT_WARN("Connection refused");
|
||||
break;
|
||||
case EFAULT:
|
||||
BLT_WARN("Bad address");
|
||||
break;
|
||||
case EINPROGRESS:
|
||||
BLT_WARN("Operation now in progress");
|
||||
break;
|
||||
case EINTR:
|
||||
BLT_WARN("Interrupted system call");
|
||||
break;
|
||||
case EISCONN:
|
||||
BLT_WARN("Transport endpoint is already connected");
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
BLT_WARN("Network is unreachable");
|
||||
break;
|
||||
case ENOTSOCK:
|
||||
BLT_WARN("Socket operation_t on non-socket");
|
||||
break;
|
||||
case EPROTOTYPE:
|
||||
BLT_WARN("Protocol wrong type for socket");
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
BLT_WARN("Connection timed out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
void print_socket_error()
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINVAL: BLT_WARN("Invalid argument");
|
||||
break;
|
||||
case EACCES: BLT_WARN("Permission denied");
|
||||
break;
|
||||
case EPERM: BLT_WARN("Operation not permitted");
|
||||
break;
|
||||
case EADDRINUSE: BLT_WARN("Address already in use");
|
||||
break;
|
||||
case EADDRNOTAVAIL: BLT_WARN("Cannot copy_fast requested address");
|
||||
break;
|
||||
case EAFNOSUPPORT: BLT_WARN("Address family not supported by protocol");
|
||||
break;
|
||||
case EAGAIN: BLT_WARN("Try again");
|
||||
break;
|
||||
case EALREADY: BLT_WARN("Operation already in progress");
|
||||
break;
|
||||
case EBADF: BLT_WARN("Bad file number");
|
||||
break;
|
||||
case ECONNREFUSED: BLT_WARN("Connection refused");
|
||||
break;
|
||||
case EFAULT: BLT_WARN("Bad address");
|
||||
break;
|
||||
case EINPROGRESS: BLT_WARN("Operation now in progress");
|
||||
break;
|
||||
case EINTR: BLT_WARN("Interrupted system call");
|
||||
break;
|
||||
case EISCONN: BLT_WARN("Transport endpoint is already connected");
|
||||
break;
|
||||
case ENETUNREACH: BLT_WARN("Network is unreachable");
|
||||
break;
|
||||
case ENOTSOCK: BLT_WARN("Socket operation_t on non-socket");
|
||||
break;
|
||||
case EPROTOTYPE: BLT_WARN("Protocol wrong type for socket");
|
||||
break;
|
||||
case ETIMEDOUT: BLT_WARN("Connection timed out");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
* Created by Brett on 20/07/23.
|
||||
* Licensed under GNU General Public License V3.0
|
||||
* See LICENSE file for license detail
|
||||
*/
|
||||
#define BLT_LOGGING_IMPLEMENTATION
|
||||
#include <blt/std/logging.h>
|
|
@ -15,6 +15,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/mmap.h>
|
||||
|
||||
#ifdef __unix__
|
||||
|
@ -112,7 +113,7 @@ namespace blt
|
|||
#ifdef __unix__
|
||||
if (munmap(ptr, bytes))
|
||||
{
|
||||
BLT_ERROR_STREAM << "Failed to deallocate\n";
|
||||
BLT_ERROR("Failed to deallocate");
|
||||
throw bad_alloc_t(handle_mmap_error());
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/std/simd.h>
|
||||
#include <blt/std/logging.h>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* See LICENSE file for license detail
|
||||
*/
|
||||
#include <blt/std/system.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(WIN32)
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
|
@ -94,7 +94,7 @@ namespace blt
|
|||
if (GetProcessTimes(GetCurrentProcess(),
|
||||
&starttime, &exittime, &kerneltime, &usertime) == 0)
|
||||
{
|
||||
BLT_WARN("Unable to get process resource usage, error: %d", GetLastError());
|
||||
BLT_WARN("Unable to get process resource usage, error: {:d}", GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ namespace blt
|
|||
#else
|
||||
if (getrusage(who, (struct rusage*) &usage) != 0)
|
||||
{
|
||||
BLT_ERROR("Failed to get rusage %d", errno);
|
||||
BLT_ERROR("Failed to get rusage {:d}", errno);
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
This is a println with a stream
|
||||
This is a mixed print 25 with multiple types 34.233400
|
||||
What about just a new line character?
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/parse/argparse_v2.h>
|
||||
|
||||
int main(){
|
||||
blt::argparse::detail::test();
|
||||
return 0;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/math/vectors.h>
|
||||
|
@ -64,18 +64,18 @@ void test_enumerate()
|
|||
{
|
||||
blt::log_box_t box(std::cout, "Enumerate Tests", 25);
|
||||
for (const auto& [index, item] : blt::enumerate(array_1))
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
|
||||
BLT_TRACE("");
|
||||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).rev())
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
|
||||
BLT_TRACE("");
|
||||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).take(3))
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index < 3);
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ void test_enumerate()
|
|||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).take(3).rev())
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index < 3);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ void test_enumerate()
|
|||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).skip(3))
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index >= 3);
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ void test_enumerate()
|
|||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev())
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index >= 3);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ void test_enumerate()
|
|||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).take(5))
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index >= 3 && index < (array_1.size() - 5) + 3);
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ void test_enumerate()
|
|||
|
||||
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev().take(5))
|
||||
{
|
||||
BLT_TRACE_STREAM << index << " : " << item << "\n";
|
||||
BLT_TRACE("{}, : {}", index, item);
|
||||
BLT_ASSERT(index >= 5);
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ void test_pairs()
|
|||
blt::log_box_t box(std::cout, "Pairs Tests", 25);
|
||||
for (auto [a1, a2] : blt::in_pairs(array_1, array_2))
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << "\n";
|
||||
BLT_TRACE("{}, : {}", a1, a2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,32 +134,32 @@ void test_zip()
|
|||
blt::log_box_t box(std::cout, "Zip Tests", 25);
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1))
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1).take(3))
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take(3).rev())
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take_or(13))
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).rev().take(3))
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).skip(2).rev())
|
||||
{
|
||||
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n";
|
||||
BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,33 +168,33 @@ void test_iterate()
|
|||
blt::log_box_t box(std::cout, "Iterate Tests", 25);
|
||||
for (auto v : blt::iterate(array_1))
|
||||
{
|
||||
BLT_TRACE_STREAM << "Element: " << v << "\n";
|
||||
BLT_TRACE("Element: {:.4f}", v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto v : blt::iterate(array_1).skip(5))
|
||||
{
|
||||
BLT_TRACE_STREAM << "Element: " << v << "\n";
|
||||
BLT_TRACE("Element: {:.4f}", v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto v : blt::iterate(array_1).take(5))
|
||||
{
|
||||
BLT_TRACE_STREAM << "Element: " << v << "\n";
|
||||
BLT_TRACE("Element: {:.4f}", v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto v : blt::iterate(array_1).rev())
|
||||
{
|
||||
BLT_TRACE_STREAM << "Element: " << v << "\n";
|
||||
BLT_TRACE("Element: {:.4f}", v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [a, b] : blt::iterate(array_1).zip(list_1))
|
||||
{
|
||||
BLT_TRACE_STREAM << "Zip: " << a << " " << b << "\n";
|
||||
BLT_TRACE("Zip: {:.4f} {:.4f}", a, b);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [i, data] : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); }).zip(list_1).skip(3).take(4).enumerate())
|
||||
{
|
||||
auto [a, b] = data;
|
||||
BLT_TRACE_STREAM << "Map + Zip + Skip + Take + Enumerate (Index: " << i << ")> " << a << " " << b << "\n";
|
||||
BLT_TRACE("Map + Zip + Skip + Take + Enumerate (Index: {})> {:.4f} {:.4f}", i, a, b);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto [i, data] : blt::iterate(array_1).map(
|
||||
|
@ -203,7 +203,7 @@ void test_iterate()
|
|||
}).zip(list_1).skip(3).take(4).enumerate())
|
||||
{
|
||||
auto [a, b] = data;
|
||||
BLT_TRACE_STREAM << "Map + Zip + Skip + Take + Enumerate (Index: " << i << ")> " << a << " " << b << "\n";
|
||||
BLT_TRACE("Map + Zip + Skip + Take + Enumerate (Index: {})> {:.4f} {:.4f}", i, a, b);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto a : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); })
|
||||
|
@ -212,16 +212,16 @@ void test_iterate()
|
|||
if (!a)
|
||||
continue;
|
||||
auto v = *a;
|
||||
BLT_TRACE_STREAM << " So this one works? " << v << "\n";
|
||||
BLT_TRACE(" So this one works? {:.4f}", v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
for (auto a : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); })
|
||||
.enumerate().filter([](const auto& f) { return f.value.x() > 0.5; }))
|
||||
.enumerate().filter([](const auto& f) { return f.second.x() > 0.5; }))
|
||||
{
|
||||
if (!a)
|
||||
continue;
|
||||
auto [index, v] = *a;
|
||||
BLT_TRACE_STREAM << " So this one works? (" << index << ")" << v << "\n";
|
||||
BLT_TRACE(" So this one works? ({}) {:.4f}", index, v);
|
||||
}
|
||||
BLT_TRACE("================================");
|
||||
// for (auto a : blt::iterate(array_1).filter([](const auto& f) { return f.x() > 3 && f.y() < 6; }).take(2))
|
||||
|
@ -233,7 +233,7 @@ void test_iterate()
|
|||
// }
|
||||
for (auto a : blt::iterate(array_1).map([](const auto& f) { return f.x() > 3 && f.y() < 6; }))
|
||||
{
|
||||
BLT_TRACE_STREAM << " How about this one?? " << a << "\n";
|
||||
BLT_TRACE(" How about this one?? ({}) {:.4f}", a);
|
||||
}
|
||||
|
||||
// for (auto [value, a] : blt::iterate(array_1).map(
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <blt/fs/filesystem.h>
|
||||
#include <blt/logging/ansi.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/logging/status.h>
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/std/utility.h>
|
||||
|
||||
struct some_silly_type_t
|
||||
{};
|
||||
|
||||
auto expected_str = std::string(R"(This is a println!
|
||||
This is a println with args '42'
|
||||
This is a println with multiple args '42' '32.342311859130859375' 'Hello World!'
|
||||
This is a 'Well so am I except cooler :3' fmt string with positionals 'I am a string!'
|
||||
This is a println with a sign +4120
|
||||
This is a println with a sign -4120
|
||||
This is a println with a space 4120
|
||||
This is a println with a space -4120
|
||||
This is a println with a minus 4120
|
||||
This is a println with a minus -4120
|
||||
This is a println with a with 4120
|
||||
This is a println with a with leading zeros 0000004120
|
||||
This is a println with a precision 42.2323423490
|
||||
This is a println with hex 109a
|
||||
This is a println with hex with leading 0x109a
|
||||
This is a println with binary 0b00110010000110100101011000000000
|
||||
This is a println with binary with space 0b10110010 00011010 01010110 00000000
|
||||
This is a println with binary with space 10100010 00000000 00000000 00000000
|
||||
This is a println with octal 015015
|
||||
This is a println with hexfloat 0x1.926e978d4fdf4p+8
|
||||
This is a println with exponent 4.4320902431999996e+07
|
||||
This is a println with exponent 9.5324342340423400e+15
|
||||
This is a println with general 953243.49
|
||||
This is a println with general 9.532433240234033e+17
|
||||
This is a println with a char B
|
||||
This is a println with type some_silly_type_t
|
||||
This is a println with boolean true
|
||||
This is a println with boolean as int 0
|
||||
This is a println with boolean as hex 0x1
|
||||
This is a println with boolean as octal 1
|
||||
This is a println with alignment left 64 end value
|
||||
This is a println with alignment right 46 end value
|
||||
This is a println with alignment left (fill) 46******** end value
|
||||
This is a println with alignment right (fill) ********46 end value
|
||||
This is a println with alignment right (fill with reserved character) ^^^^^^^^46 end value
|
||||
This is a println with fill no alignment %%%%%%%%%%%%%%%%%%46 end value
|
||||
This is a println with arg reference 46.02
|
||||
This is a println with arg reference &&&&&&&&&&&&&&&&&&&&
|
||||
)");
|
||||
|
||||
std::pair<bool, std::string> compare_strings(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
const auto size = std::min(s1.size(), s2.size());
|
||||
size_t index = 0;
|
||||
for (; index < size; ++index)
|
||||
{
|
||||
if (s1[index] != s2[index])
|
||||
{
|
||||
std::stringstream ss;
|
||||
const auto i1 = std::max(static_cast<blt::i64>(index) - 32, 0l);
|
||||
const auto l1 = std::min(static_cast<blt::i64>(size) - i1, 65l);
|
||||
ss << "Strings differ at index " << index << "!\n";
|
||||
ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl;
|
||||
return {false, ss.str()};
|
||||
}
|
||||
}
|
||||
if (s1.size() != s2.size())
|
||||
return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"};
|
||||
return {true, ""};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::stringstream ss;
|
||||
blt::logging::println(ss, "This is a println!");
|
||||
blt::logging::println(ss, "This is a println with args '{}'", 42);
|
||||
blt::logging::println(ss, "This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!");
|
||||
blt::logging::println(ss, "This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
|
||||
blt::logging::println(ss, "This is a println with a sign {:+}", 4120);
|
||||
blt::logging::println(ss, "This is a println with a sign {:+}", -4120);
|
||||
blt::logging::println(ss, "This is a println with a space {: }", 4120);
|
||||
blt::logging::println(ss, "This is a println with a space {: }", -4120);
|
||||
blt::logging::println(ss, "This is a println with a minus {:-}", 4120);
|
||||
blt::logging::println(ss, "This is a println with a minus {:-}", -4120);
|
||||
blt::logging::println(ss, "This is a println with a with {:10}", 4120);
|
||||
blt::logging::println(ss, "This is a println with a with leading zeros {:010}", 4120);
|
||||
blt::logging::println(ss, "This is a println with a precision {:.10f}", 42.232342349);
|
||||
blt::logging::println(ss, "This is a println with hex {:.10x}", 4250);
|
||||
blt::logging::println(ss, "This is a println with hex with leading {:#.10x}", 4250);
|
||||
blt::logging::println(ss, "This is a println with binary {:#b}", 6969420);
|
||||
blt::logging::println(ss, "This is a println with binary with space {: #b}", 6969421);
|
||||
blt::logging::println(ss, "This is a println with binary with space {: b}", 69);
|
||||
blt::logging::println(ss, "This is a println with octal {:#o}", 6669);
|
||||
blt::logging::println(ss, "This is a println with hexfloat {:a}", 402.4320);
|
||||
blt::logging::println(ss, "This is a println with exponent {:e}", 44320902.4320);
|
||||
blt::logging::println(ss, "This is a println with exponent {:e}", 9532434234042340.0);
|
||||
blt::logging::println(ss, "This is a println with general {:g}", 953243.49);
|
||||
blt::logging::println(ss, "This is a println with general {:g}", 953243324023403240.49);
|
||||
blt::logging::println(ss, "This is a println with a char {:c}", 66);
|
||||
blt::logging::println(ss, "This is a println with type {:t}", some_silly_type_t{});
|
||||
blt::logging::println(ss, "This is a println with boolean {}", true);
|
||||
blt::logging::println(ss, "This is a println with boolean as int {:d}", false);
|
||||
blt::logging::println(ss, "This is a println with boolean as hex {:#x}", true);
|
||||
blt::logging::println(ss, "This is a println with boolean as octal {:o}", true);
|
||||
blt::logging::println(ss, "This is a println with alignment left {:<10} end value", 64);
|
||||
blt::logging::println(ss, "This is a println with alignment right {:>10} end value", 46);
|
||||
blt::logging::println(ss, "This is a println with alignment left (fill) {:*<10} end value", 46);
|
||||
blt::logging::println(ss, "This is a println with alignment right (fill) {:*>10} end value", 46);
|
||||
blt::logging::println(ss, "This is a println with alignment right (fill with reserved character) {:\\^>10} end value", 46);
|
||||
blt::logging::println(ss, "This is a println with fill no alignment {:%20} end value", 46);
|
||||
blt::logging::println(ss, "This is a println with arg reference {0:{1}.{2}f}", 46.0232, 20, 2);
|
||||
blt::logging::println(ss, "This is a println with arg reference {0:&{1}}", "", 20);
|
||||
// blt::logging::print(ss.str());
|
||||
auto [passed, error_msg] = compare_strings(expected_str, ss.str());
|
||||
BLT_ASSERT_MSG(passed && "Logger logged string doesn't match precomputed expected string!", error_msg.c_str());
|
||||
|
||||
namespace ansi = blt::logging::ansi;
|
||||
namespace color = ansi::color;
|
||||
|
||||
// for (blt::u8 r = 0; r < 6; r++)
|
||||
// {
|
||||
// for (blt::u8 g = 0; g < 6; g++)
|
||||
// {
|
||||
// for (blt::u8 b = 0; b < 6; b++)
|
||||
// {
|
||||
// blt::logging::println("{}This is a println with a color {:#3x} {:#3x} {:#3x}{}",
|
||||
// build(fg(color::color256{r, g, b}), bg(color::color256{
|
||||
// static_cast<unsigned char>(5 - r),
|
||||
// static_cast<unsigned char>(5 - g),
|
||||
// static_cast<unsigned char>(5 - b)
|
||||
// })), r, g, b, build(color::color_mode::RESET_ALL));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// blt::logging::println("{}This is a color now with background{}",
|
||||
// build(color::color_mode::BOLD, fg(color::color8::RED), color::color_mode::DIM, bg(color::color_rgb(0, 100, 255))),
|
||||
// build(color::color_mode::RESET_ALL));
|
||||
|
||||
|
||||
std::ofstream os("test.txt");
|
||||
blt::fs::fstream_writer_t wtr(os);
|
||||
blt::fs::writer_string_wrapper_t writer(wtr);
|
||||
|
||||
writer.write("This is a println with a stream\n");
|
||||
writer.write("This is a mixed print ");
|
||||
writer.write(std::to_string(25));
|
||||
writer.write(" with multiple types ");
|
||||
writer.write(std::to_string(34.23340));
|
||||
writer.write('\n');
|
||||
writer.write("What about just a new line character?\n");
|
||||
|
||||
// blt::logging::println("Logged {} characters", charCount);
|
||||
//
|
||||
// BLT_TRACE("Hello this is am empty trace!");
|
||||
// BLT_TRACE("This is a trace with data {} {} {}", "bad at code", 413, "boy");
|
||||
//
|
||||
// BLT_DEBUG("This is complete? {}", "this is working!");
|
||||
// BLT_INFO("Hello there!");
|
||||
// BLT_WARN("This is a warning!");
|
||||
// BLT_ERROR("This is an error!");
|
||||
// BLT_FATAL("This is a fatal error!");
|
||||
// BLT_TRACE("This is a pointer {:f}", &charCount);
|
||||
//
|
||||
// BLT_TRACE("Now time to test the logger status box");
|
||||
|
||||
blt::logging::status_progress_bar_t progress;
|
||||
blt::logging::status_bar_t status;
|
||||
status.add(progress);
|
||||
blt::logging::get_global_config().add_injector(status);
|
||||
|
||||
progress.set_progress(1.0 / 103.0);
|
||||
BLT_TRACE("Hello There!");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
|
||||
progress.set_progress(2.0 / 103.0);
|
||||
BLT_TRACE("I am printing stuff!");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
|
||||
progress.set_progress(3.0 / 103.0);
|
||||
BLT_TRACE("How are you!?");
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
progress.set_progress((4.0 + i) / 103.0);
|
||||
BLT_INFO("I am printing some output {} times!", i + 1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
|
||||
|
||||
/*std::cout << "\033[2J";
|
||||
constexpr int totalRows = 24;
|
||||
// std::cout << "\033[1;" << (totalRows - 1) << "r";
|
||||
std::cout << use_mode(ansi::mode::color80x25_text);
|
||||
|
||||
for (int i = 1; i <= 10; ++i)
|
||||
{
|
||||
|
||||
std::cout << "\033[1;1H";
|
||||
std::cout << "printed line " << i << std::endl;
|
||||
std::cout << "\033[" << totalRows << ";1H";
|
||||
std::cout << "[----status----]" << std::flush;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
std::cout << "\033[r";*/
|
||||
|
||||
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
|
||||
}
|
Loading…
Reference in New Issue