diff --git a/CMakeLists.txt b/CMakeLists.txt index d2c94d7..f30080a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 5.1.17) +set(BLT_VERSION 5.2.0) set(BLT_TARGET BLT) diff --git a/include/blt/fs/file_writers.h b/include/blt/fs/file_writers.h index d617de5..db25ed6 100644 --- a/include/blt/fs/file_writers.h +++ b/include/blt/fs/file_writers.h @@ -28,11 +28,15 @@ namespace blt::fs { - inline auto basic_naming_function = [](const size_t invocation, const std::optional& prefix) { - return prefix.value_or("") + "-" + std::to_string(invocation) + ".txt"; + inline auto basic_naming_function = [](const size_t invocation, std::string prefix) { + prefix += '-'; + prefix += std::to_string(invocation); + prefix += ".txt"; + return prefix; }; - // ReSharper disable once CppClassCanBeFinal + using naming_function_t = std::function; + class fwriter_t : public writer_t { public: @@ -41,6 +45,10 @@ namespace blt::fs 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); @@ -62,6 +70,8 @@ namespace blt::fs void flush() override; + void newfile(const std::string& new_name) override; + protected: size_t m_current_pos = 0; std::vector m_buffer; @@ -74,21 +84,61 @@ namespace blt::fs class bounded_writer : public fwriter_t { public: - explicit bounded_writer(fwriter_t& writer, std::optional base_name, - const std::function)>& naming_function = basic_naming_function, - size_t max_size = 1024 * 1024 * 10); + explicit bounded_writer(fwriter_t& writer, std::optional 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 = nullptr; + fwriter_t* m_writer; std::optional m_base_name; size_t m_current_invocation = 0; - size_t m_max_size = 0; + size_t m_max_size; size_t m_currently_written = 0; - // inputs: current invocation, optional string provided to the constructor + // inputs: current invocation, then basename string // returns: name of the file to write to - std::function&)> m_naming_function; + 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; }; } diff --git a/include/blt/fs/threaded_writers.h b/include/blt/fs/threaded_writers.h new file mode 100644 index 0000000..293c397 --- /dev/null +++ b/include/blt/fs/threaded_writers.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 . + */ + +#ifndef BLT_FS_THREADED_WRITERS_H +#define BLT_FS_THREADED_WRITERS_H + +#include +#include + +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 diff --git a/include/blt/logging/logging_config.h b/include/blt/logging/logging_config.h index 29438f3..c5a843c 100644 --- a/include/blt/logging/logging_config.h +++ b/include/blt/logging/logging_config.h @@ -20,11 +20,11 @@ #define BLT_LOGGING_LOGGING_CONFIG_H #include -#include #include #include #include #include +#include namespace blt::logging { @@ -44,7 +44,8 @@ namespace blt::logging { friend logger_t; public: - std::vector log_outputs = get_default_log_outputs(); + // wrappers for streams exist in blt/fs/stream_wrappers.h + std::vector log_outputs = get_default_log_outputs(); std::string log_format = get_default_log_format(); std::array log_level_colors = get_default_log_level_colors(); std::array log_level_names = get_default_log_level_names(); @@ -57,7 +58,7 @@ namespace blt::logging bool ensure_alignment = true; private: static std::string get_default_log_format(); - static std::vector get_default_log_outputs(); + static std::vector get_default_log_outputs(); static std::array get_default_log_level_colors(); static std::array get_default_log_level_names(); }; diff --git a/libraries/parallel-hashmap b/libraries/parallel-hashmap index 93201da..7ef2e73 160000 --- a/libraries/parallel-hashmap +++ b/libraries/parallel-hashmap @@ -1 +1 @@ -Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3 +Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5 diff --git a/src/blt/fs/file_writers.cpp b/src/blt/fs/file_writers.cpp index fb832a7..4c47b7c 100644 --- a/src/blt/fs/file_writers.cpp +++ b/src/blt/fs/file_writers.cpp @@ -15,11 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include -#include #include +#include #include +#include +#include namespace blt::fs { @@ -48,6 +49,12 @@ namespace blt::fs 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()) @@ -61,20 +68,86 @@ namespace blt::fs void buffered_writer::flush() { - fwriter_t::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 base_name, - const std::function)>& naming_function, - const size_t max_size): fwriter_t{naming_function(0, base_name)}, m_writer(&writer), - m_base_name(std::move(base_name)), m_max_size(max_size), - m_naming_function(naming_function) - {} - - i64 bounded_writer::write(const char* buffer, size_t bytes) + bounded_writer::bounded_writer(fwriter_t& writer, std::optional 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)) { - return fwriter_t::write(buffer, bytes); + 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, current_time->tm_mon, current_time->tm_mday, current_time->tm_hour}; } } diff --git a/src/blt/fs/threaded_wrtiers.cpp b/src/blt/fs/threaded_wrtiers.cpp new file mode 100644 index 0000000..7a5cba6 --- /dev/null +++ b/src/blt/fs/threaded_wrtiers.cpp @@ -0,0 +1,36 @@ +/* + * + * 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 . + */ +#include + +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(); + } +} \ No newline at end of file diff --git a/src/blt/logging/logging_config.cpp b/src/blt/logging/logging_config.cpp index fcb072d..10bd0f6 100644 --- a/src/blt/logging/logging_config.cpp +++ b/src/blt/logging/logging_config.cpp @@ -20,5 +20,15 @@ namespace blt::logging { + std::string logging_config_t::get_default_log_format() + {} -} \ No newline at end of file + std::vector logging_config_t::get_default_log_outputs() + {} + + std::array logging_config_t::get_default_log_level_colors() + {} + + std::array logging_config_t::get_default_log_level_names() + {} +}