main
Brett 2025-03-06 15:15:25 -05:00
parent c4a29ade19
commit dad09f5f1b
12 changed files with 301 additions and 260 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake)
set(BLT_VERSION 5.1.12)
set(BLT_VERSION 5.1.13)
set(BLT_TARGET BLT)

View File

@ -19,144 +19,48 @@
#ifndef BLT_FILESYSTEM_H
#define BLT_FILESYSTEM_H
#include <fstream>
#include <ios>
#include <iosfwd>
#include <blt/fs/fwddecl.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.
* reader_t wrapper for fstream
*/
class block_reader
class fstream_reader_t final : public reader_t
{
protected:
// 32768 block size seems the fastest on my system
unsigned long m_bufferSize;
public:
explicit block_reader(size_t bufferSize): m_bufferSize(bufferSize)
{}
explicit fstream_reader_t(std::istream& stream);
/**
* 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;
explicit fstream_reader_t(fstream_reader_t& copy) = delete;
virtual size_t gcount() = 0;
fstream_reader_t& operator=(const fstream_reader_t& copy) = delete;
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
{
i64 read(char* buffer, size_t bytes) override;
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;
}
std::istream* m_stream;
};
class fstream_block_writer : public block_writer
class fstream_writer_t final : public writer_t
{
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_writer_t(std::ostream& stream);
explicit fstream_block_writer(fstream_block_writer& copy) = delete;
explicit fstream_writer_t(fstream_writer_t& copy) = delete;
explicit fstream_block_writer(fstream_block_writer&& move) = delete;
fstream_writer_t& operator=(const fstream_writer_t& copy) = delete;
fstream_block_writer& operator=(const fstream_block_writer& copy) = delete;
i64 write(char* buffer, size_t bytes) override;
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
void flush() override;
int write(char* buffer, size_t bytes) override;
inline void flush() override
virtual ~fstream_writer_t() override // NOLINT
{
flush_internal();
flush();
}
~fstream_block_writer()
{
flush_internal();
delete[] m_buffer;
}
private:
std::ostream* m_stream;
};
}

77
include/blt/fs/fwddecl.h Normal file
View File

@ -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(char* buffer, size_t bytes) = 0;
/**
* Optional flush command which syncs the underlying objects
*/
virtual void flush()
{};
};
}
#endif //BLT_FS_FWDDECL_H

View File

@ -22,19 +22,19 @@
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);
@ -356,7 +356,7 @@ 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());
@ -365,7 +365,7 @@ namespace blt::nbt {
}
out.put('\0');
}
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){
auto* v = _internal_::toType(type);
@ -390,10 +390,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();
@ -419,9 +419,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.

View File

@ -19,11 +19,11 @@
#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>
#include <functional>
namespace blt::logging
{

View File

@ -0,0 +1,35 @@
#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;
}
#endif //BLT_LOGGING_FWDDECL_H

View File

@ -19,14 +19,15 @@
#ifndef BLT_LOGGING_LOGGING_H
#define BLT_LOGGING_LOGGING_H
#include <cstring>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include <cstring>
#include <functional>
#include <blt/logging/fmt_tokenizer.h>
#include <blt/std/utility.h>
#include <blt/logging/fwddecl.h>
#include <blt/meta/is_streamable.h>
#include <blt/std/utility.h>
namespace blt::logging
{

View File

@ -19,15 +19,16 @@
#ifndef BLT_LOGGING_LOGGING_CONFIG_H
#define BLT_LOGGING_LOGGING_CONFIG_H
#include <optional>
#include <string>
#include <blt/std/types.h>
#include <blt/logging/ansi.h>
#include <array>
#include <ostream>
#include <string>
#include <vector>
#include <blt/logging/logging.h>
#include <blt/std/types.h>
namespace blt::logging
{
enum class log_level_t
enum class log_level_t : u8
{
TRACE,
DEBUG,
@ -41,17 +42,24 @@ namespace blt::logging
class logging_config_t
{
friend logger_t;
public:
std::optional<std::string> log_file_path;
std::string log_format = "";
std::array<std::string, LOG_LEVEL_COUNT> log_level_colors = {
};
std::vector<std::ostream*> log_outputs = get_default_log_outputs();
std::string log_format = get_default_log_format();
std::array<std::string, LOG_LEVEL_COUNT> log_level_colors = get_default_log_level_colors();
std::array<std::string, LOG_LEVEL_COUNT> log_level_names = get_default_log_level_names();
log_level_t level = log_level_t::TRACE;
bool use_color = true;
bool log_to_console = true;
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
bool 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 ensure_alignment = true;
private:
static std::string get_default_log_format();
static std::vector<std::ostream*> 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();
};
}

View File

@ -21,59 +21,25 @@
namespace blt::fs
{
fstream_reader_t::fstream_reader_t(std::istream& stream): m_stream{&stream}
{}
fstream_block_reader::fstream_block_reader(std::fstream& stream, size_t bufferSize):
block_reader(bufferSize), m_stream(stream), m_buffer(new char[bufferSize])
i64 fstream_reader_t::read(char* buffer, const size_t bytes)
{
if (!m_stream.good() || m_stream.fail())
BLT_WARN("Provided std::fstream is not good! Clearing!");
m_stream.clear();
return m_stream->readsome(buffer, static_cast<std::streamsize>(bytes));
}
int fstream_block_reader::read(char* buffer, size_t bytes)
fstream_writer_t::fstream_writer_t(std::ostream& stream): m_stream{&stream}
{}
i64 fstream_writer_t::write(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;
m_stream->write(buffer, static_cast<std::streamsize>(bytes));
return static_cast<i64>(bytes);
}
int fstream_block_writer::write(char* buffer, size_t bytes)
void fstream_writer_t::flush()
{
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;
m_stream->flush();
}
return 0;
}
void fstream_block_writer::flush_internal()
{
m_stream.write(m_buffer, (long) writeIndex);
writeIndex = 0;
}
}

View File

@ -10,13 +10,13 @@
#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);

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/logging/logging_config.h>
#include <blt/logging/ansi.h>
namespace blt::logging
{

View File

@ -23,7 +23,7 @@
#include <blt/std/utility.h>
#include <thread>
#include <chrono>
#include <fstream>
struct some_silly_type_t
{};
@ -89,6 +89,43 @@ std::pair<bool, std::string> compare_strings(const std::string& s1, const std::s
return {true, ""};
}
struct seqbuf final : std::streambuf
{
seqbuf(std::streambuf* destBuf, std::size_t& charCount) : m_dest(destBuf), m_count(charCount)
{}
protected:
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::eof();
++m_count;
blt::logging::println("We are printing a character {:c}", ch);
return m_dest->sputc(static_cast<char>(ch));
}
std::streamsize xsputn(const char* s, std::streamsize count) override
{
blt::logging::println("We are printing a series of characters {} {}", count, std::string_view{s, static_cast<size_t>(count)});
m_count += static_cast<std::size_t>(count);
return m_dest->sputn(s, count);
}
int_type underflow() override
{
return m_dest->sgetc();
}
int sync() override
{
return m_dest->pubsync();
}
private:
std::streambuf* m_dest;
std::size_t& m_count;
};
int main()
{
std::stringstream ss;
@ -153,16 +190,28 @@ int main()
}
}
blt::logging::println("{}This is a color now with background{}",
build(color::color_mode::BOLD, fg(color::color8::CYAN), color::color_mode::DIM, bg(color::color8::YELLOW)),
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::cout << "\033[2J";
std::ofstream os("test.txt");
size_t charCount = 0;
seqbuf hi{os.rdbuf(), charCount};
dynamic_cast<std::ostream&>(os).rdbuf(&hi);
os << "This is a println with a stream" << std::endl;
os << "This is a mixed print " << 25 << " with multiple types " << 34.23340 << std::endl;
os << "What about just a new line character?\n";
blt::logging::println("Logged {} characters", charCount);
/*std::cout << "\033[2J";
constexpr int totalRows = 24;
std::cout << "\033[1;" << (totalRows - 1) << "r";
// 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";
@ -170,7 +219,7 @@ int main()
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "\033[r";
std::cout << "\033[r";*/
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
}