From f25285dc37959dbb6987b184a820c950e4ff2500 Mon Sep 17 00:00:00 2001 From: K1ngst0m Date: Fri, 27 Oct 2023 01:46:08 +0800 Subject: [PATCH] replaced 3rd-party logger with a custom implementation --- cmake/AphExternal.cmake | 1 - engine/CMakeLists.txt | 5 - engine/common/logger.cpp | 24 + engine/common/logger.h | 114 +- external/reckless/CMakeLists.txt | 64 - external/reckless/LICENSE.txt | 20 - external/reckless/reckless/Tuprules.lua | 1 - external/reckless/reckless/_Tuprules.tup | 1 - .../reckless/include/reckless/basic_log.hpp | 367 --- .../include/reckless/crash_handler.hpp | 49 - .../include/reckless/detail/fd_writer.hpp | 50 - .../include/reckless/detail/lockless_cv.hpp | 80 - .../reckless/detail/mpsc_ring_buffer.hpp | 159 -- .../include/reckless/detail/platform.hpp | 394 ---- .../include/reckless/detail/spsc_event.hpp | 166 -- .../include/reckless/detail/trace_log.hpp | 5 - .../include/reckless/detail/utility.hpp | 99 - .../reckless/include/reckless/file_writer.hpp | 41 - .../reckless/include/reckless/ntoa.hpp | 79 - .../include/reckless/output_buffer.hpp | 235 -- .../reckless/include/reckless/policy_log.hpp | 276 --- .../include/reckless/severity_log.hpp | 97 - .../include/reckless/stdout_writer.hpp | 60 - .../include/reckless/template_formatter.hpp | 107 - .../reckless/include/reckless/writer.hpp | 63 - external/reckless/reckless/lib/Tupfile.lua | 5 - external/reckless/reckless/lib/_Tupfile | 2 - external/reckless/reckless/reckless.vcxproj | 154 -- .../reckless/reckless.vcxproj.filters | 108 - external/reckless/reckless/src/Tupfile.lua | 7 - external/reckless/reckless/src/_Tupfile | 2 - external/reckless/reckless/src/basic_log.cpp | 617 ----- .../reckless/src/crash_handler_unix.cpp | 118 - .../reckless/src/crash_handler_win32.cpp | 69 - external/reckless/reckless/src/fd_writer.cpp | 161 -- .../reckless/reckless/src/file_writer.cpp | 112 - .../reckless/reckless/src/lockless_cv.cpp | 99 - .../reckless/src/mpsc_ring_buffer.cpp | 165 -- external/reckless/reckless/src/ntoa.cpp | 1997 ----------------- .../reckless/reckless/src/output_buffer.cpp | 324 --- external/reckless/reckless/src/platform.cpp | 89 - external/reckless/reckless/src/policy_log.cpp | 24 - .../reckless/src/spsc_event_win32.cpp | 41 - .../reckless/src/template_formatter.cpp | 328 --- external/reckless/reckless/src/trace_log.cpp | 31 - external/reckless/reckless/src/unit_test.hpp | 196 -- external/reckless/reckless/src/writer.cpp | 67 - .../reckless/reckless/unittest/.gitignore | 1 - external/reckless/reckless/unittest/_Tupfile | 4 - 49 files changed, 93 insertions(+), 7185 deletions(-) delete mode 100644 external/reckless/CMakeLists.txt delete mode 100644 external/reckless/LICENSE.txt delete mode 100644 external/reckless/reckless/Tuprules.lua delete mode 100644 external/reckless/reckless/_Tuprules.tup delete mode 100644 external/reckless/reckless/include/reckless/basic_log.hpp delete mode 100644 external/reckless/reckless/include/reckless/crash_handler.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/fd_writer.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/lockless_cv.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/mpsc_ring_buffer.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/platform.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/spsc_event.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/trace_log.hpp delete mode 100644 external/reckless/reckless/include/reckless/detail/utility.hpp delete mode 100644 external/reckless/reckless/include/reckless/file_writer.hpp delete mode 100644 external/reckless/reckless/include/reckless/ntoa.hpp delete mode 100644 external/reckless/reckless/include/reckless/output_buffer.hpp delete mode 100644 external/reckless/reckless/include/reckless/policy_log.hpp delete mode 100644 external/reckless/reckless/include/reckless/severity_log.hpp delete mode 100644 external/reckless/reckless/include/reckless/stdout_writer.hpp delete mode 100644 external/reckless/reckless/include/reckless/template_formatter.hpp delete mode 100644 external/reckless/reckless/include/reckless/writer.hpp delete mode 100644 external/reckless/reckless/lib/Tupfile.lua delete mode 100644 external/reckless/reckless/lib/_Tupfile delete mode 100644 external/reckless/reckless/reckless.vcxproj delete mode 100644 external/reckless/reckless/reckless.vcxproj.filters delete mode 100644 external/reckless/reckless/src/Tupfile.lua delete mode 100644 external/reckless/reckless/src/_Tupfile delete mode 100644 external/reckless/reckless/src/basic_log.cpp delete mode 100644 external/reckless/reckless/src/crash_handler_unix.cpp delete mode 100644 external/reckless/reckless/src/crash_handler_win32.cpp delete mode 100644 external/reckless/reckless/src/fd_writer.cpp delete mode 100644 external/reckless/reckless/src/file_writer.cpp delete mode 100644 external/reckless/reckless/src/lockless_cv.cpp delete mode 100644 external/reckless/reckless/src/mpsc_ring_buffer.cpp delete mode 100644 external/reckless/reckless/src/ntoa.cpp delete mode 100644 external/reckless/reckless/src/output_buffer.cpp delete mode 100644 external/reckless/reckless/src/platform.cpp delete mode 100644 external/reckless/reckless/src/policy_log.cpp delete mode 100644 external/reckless/reckless/src/spsc_event_win32.cpp delete mode 100644 external/reckless/reckless/src/template_formatter.cpp delete mode 100644 external/reckless/reckless/src/trace_log.cpp delete mode 100644 external/reckless/reckless/src/unit_test.hpp delete mode 100644 external/reckless/reckless/src/writer.cpp delete mode 100644 external/reckless/reckless/unittest/.gitignore delete mode 100644 external/reckless/reckless/unittest/_Tupfile diff --git a/cmake/AphExternal.cmake b/cmake/AphExternal.cmake index c1af1b2b..fe072b5b 100644 --- a/cmake/AphExternal.cmake +++ b/cmake/AphExternal.cmake @@ -36,7 +36,6 @@ set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) add_subdirectory(${APH_EXTERNAL_DIR}) add_subdirectory(${APH_EXTERNAL_DIR}/mimalloc EXCLUDE_FROM_ALL) add_subdirectory(${APH_EXTERNAL_DIR}/glfw EXCLUDE_FROM_ALL) -add_subdirectory(${APH_EXTERNAL_DIR}/reckless EXCLUDE_FROM_ALL) add_subdirectory(${APH_EXTERNAL_DIR}/volk EXCLUDE_FROM_ALL) add_subdirectory(${APH_EXTERNAL_DIR}/spirv-cross EXCLUDE_FROM_ALL) add_subdirectory(${APH_EXTERNAL_DIR}/shaderc EXCLUDE_FROM_ALL) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 999871a4..edba4b8c 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -22,7 +22,6 @@ add_library(engine STATIC ${base_src}) aph_compiler_options(engine) target_include_directories(engine SYSTEM PUBLIC - ${APH_EXTERNAL_DIR}/reckless/reckless/include ${APH_EXTERNAL_DIR}/volk PRIVATE @@ -50,13 +49,9 @@ target_link_libraries(engine PRIVATE ${xcb_libraries} ${cmake_thread_libs_init} volk - reckless - # imgui shaderc_shared spirv-cross-core slang dl dw - # mmgr - # spirv-cross-reflect ) diff --git a/engine/common/logger.cpp b/engine/common/logger.cpp index 3ae1bc13..1a0da416 100644 --- a/engine/common/logger.cpp +++ b/engine/common/logger.cpp @@ -2,4 +2,28 @@ namespace aph { +void Logger::flush() +{ + std::lock_guard lock(mutex); + if(file_stream.is_open()) + { + file_stream.flush(); + } + std::cout.flush(); +} +Logger::Logger() : log_level(Level::Debug), file_stream("log.txt", std::ofstream::app) +{ + if(!file_stream.is_open()) + { + std::cerr << "Failed to open log file." << std::endl; + } +} +std::string Logger::getCurrentTime() +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::ostringstream oss; + oss << std::put_time(&tm, "[%Y-%m-%d %H:%M:%S]"); + return oss.str(); +} } // namespace aph diff --git a/engine/common/logger.h b/engine/common/logger.h index 07743ff0..d993028c 100644 --- a/engine/common/logger.h +++ b/engine/common/logger.h @@ -1,77 +1,101 @@ #ifndef LOGGER_H_ #define LOGGER_H_ -#include -#include -#include - namespace aph { class Logger { -private: - using log_t = reckless::severity_log, // 4 spaces of indent - ' ', // Field separator - reckless::severity_field // Show severity marker (D/I/W/E) first - >; +public: + enum class Level: uint8_t + { + Debug, + Info, + Warn, + Error, + None + }; - Logger() : m_fileWriter{"log.txt"}, m_logger{&m_stdcoutWriter} {} + void setLogLevel(Level level) { log_level = level; } + + Logger(Logger const&) = delete; + Logger& operator=(Logger const&) = delete; -public: - // Delete copy constructor and assignment operator - Logger(Logger const&) = delete; - Logger& operator=(Logger const&) = delete; static Logger& Get() { static Logger instance; return instance; } -public: - void flush() {} + void flush(); template - void debug(std::string_view fmt, Args&&... args); + void debug(std::string_view fmt, Args&&... args) + { + if(log_level <= Level::Debug) + log("D", fmt, std::forward(args)...); + } template - void warn(std::string_view fmt, Args&&... args); + void warn(std::string_view fmt, Args&&... args) + { + if(log_level <= Level::Warn) + log("W", fmt, std::forward(args)...); + } template - void info(std::string_view fmt, Args&&... args); + void info(std::string_view fmt, Args&&... args) + { + if(log_level <= Level::Info) + log("I", fmt, std::forward(args)...); + } template - void error(std::string_view fmt, Args&&... args); + void error(std::string_view fmt, Args&&... args) + { + if(log_level <= Level::Error) + log("E", fmt, std::forward(args)...); + } private: - reckless::file_writer m_fileWriter{"log.txt"}; - reckless::stdout_writer m_stdcoutWriter{}; - log_t m_logger; -}; + Logger(); -template -void Logger::debug(std::string_view fmt, Args&&... args) -{ - m_logger.debug(fmt.data(), std::forward(args)...); -} + // conversion for most types + template + T to_format(const T& val) { + return val; + } -template -void Logger::warn(std::string_view fmt, Args&&... args) -{ - m_logger.warn(fmt.data(), std::forward(args)...); -} -template -void Logger::info(std::string_view fmt, Args&&... args) -{ - m_logger.info(fmt.data(), std::forward(args)...); -} -template -void Logger::error(std::string_view fmt, Args&&... args) -{ - m_logger.error(fmt.data(), std::forward(args)...); -} -} // namespace aph + // specialization for std::string + const char* to_format(const std::string& val) { + return val.c_str(); + } + template + void log(const char* level, std::string_view fmt, Args&&... args) + { + std::lock_guard lock(mutex); + + std::ostringstream ss; + ss << getCurrentTime() << " [" << level << "] "; + char buffer[512]; + std::snprintf(buffer, sizeof(buffer), fmt.data(), to_format(args)...); + ss << buffer << "\n"; + + std::cout << ss.str(); + if(file_stream.is_open()) + { + file_stream << ss.str(); + } + } + + std::string getCurrentTime(); + Level log_level; + std::ofstream file_stream; + std::mutex mutex; +}; + +} // namespace aph #define LOG_FLUSH() \ do \ diff --git a/external/reckless/CMakeLists.txt b/external/reckless/CMakeLists.txt deleted file mode 100644 index b2ef949c..00000000 --- a/external/reckless/CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -if(NOT MSVC) - set(CMAKE_CXX_COMPILER_NAMES g++ clang++ icpc c++ cxx) -endif() -project(reckless LANGUAGES CXX) -CMAKE_MINIMUM_REQUIRED(VERSION 3.5) - -################################################################################ -# Options -################################################################################ - -option(RECKLESS_BUILD_EXAMPLES "Build the examples" OFF) - -################################################################################ -# Add Flags -################################################################################ -set(CMAKE_CXX_STANDARD 11) -if(MSVC) - add_compile_options(/W4 /O3) -else() - add_compile_options(-Wall -Wextra -O3 -g) -endif() - -################################################################################ -# Build Libraries -################################################################################ -include_directories("reckless/include") -include_directories("performance_log/include") - - -set (SRC_LIST -reckless/src/output_buffer.cpp -reckless/src/ntoa.cpp -reckless/src/template_formatter.cpp -reckless/src/writer.cpp -reckless/src/basic_log.cpp -reckless/src/policy_log.cpp -reckless/src/file_writer.cpp -reckless/src/fd_writer.cpp -reckless/src/mpsc_ring_buffer.cpp -reckless/src/platform.cpp -reckless/src/lockless_cv.cpp -) - -if(WIN32) - set (SRC_LIST ${SRC_LIST} - reckless/src/spsc_event_win32.cpp - reckless/src/crash_handler_win32.cpp - ) -else() - set (SRC_LIST ${SRC_LIST} - reckless/src/crash_handler_unix.cpp - ) -endif() - -add_library(reckless STATIC ${SRC_LIST}) - -################################################################################ -# Build Examples -################################################################################ -if (RECKLESS_BUILD_EXAMPLES) - message (STATUS "Making example applications") - add_subdirectory(examples) -endif () - diff --git a/external/reckless/LICENSE.txt b/external/reckless/LICENSE.txt deleted file mode 100644 index 86f91ee8..00000000 --- a/external/reckless/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -reckless logging -Copyright 2015-2020 Mattias Flodin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/external/reckless/reckless/Tuprules.lua b/external/reckless/reckless/Tuprules.lua deleted file mode 100644 index 8bcffdc6..00000000 --- a/external/reckless/reckless/Tuprules.lua +++ /dev/null @@ -1 +0,0 @@ -table.insert(OPTIONS.includes, tup.getcwd() .. '/include') diff --git a/external/reckless/reckless/_Tuprules.tup b/external/reckless/reckless/_Tuprules.tup deleted file mode 100644 index 5e19e883..00000000 --- a/external/reckless/reckless/_Tuprules.tup +++ /dev/null @@ -1 +0,0 @@ -CXXFLAGS += $(INCLUDESWITCH)$(TUP_CWD)/include diff --git a/external/reckless/reckless/include/reckless/basic_log.hpp b/external/reckless/reckless/include/reckless/basic_log.hpp deleted file mode 100644 index 0d5f1d8d..00000000 --- a/external/reckless/reckless/include/reckless/basic_log.hpp +++ /dev/null @@ -1,367 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_BASIC_LOG_HPP -#define RECKLESS_BASIC_LOG_HPP - -#include -#include -#include // likely, RECKLESS_CACHE_LINE_SIZE -#include // index_sequence -#include -#include - -#include -#include -#include -#include // system_error, error_code -#include // current_exception, exception_ptr -#include // type_info -#include - -#if defined(__unix__) -#include // pthread_self -#endif - -namespace reckless { -namespace detail { -#if defined(_WIN32) - extern "C" unsigned long __stdcall GetCurrentThreadId(); -#endif - - enum class frame_status : char { - // The frame is currently free for use. - uninitialized, - // Frame was allocated but then the error_flag_ check failed, leaving - // the frame uninitialized and it should be skipped by using - // frame_header::frame_size. - failed_error_check, - // Frame was allocated but then the constructor failed, leaving the - // dispatch pointer valid but without initialized data. It should be - // skipped by using frame_header::pdispatch_funtion and - // dispatch_operation::get_typeid. - failed_initialization, - // Frame is initialized and valid. - initialized, - // Frame is a shutdown marker, telling the worker thread to finish up - // and exit. - shutdown_marker, - // Frame is a panic-shutdown watermark, telling the worker thread that - // it should process no more frames and just wait until the program - // crashes. - panic_shutdown_marker - }; - - enum dispatch_operation { - // The dispatch function should call the formatter to write to the - // output buffer. - invoke_formatter, - // The dispatch function should return frame size type information for - // the formatter instead of calling it. This is used during error - // handling. - get_typeid - }; - - typedef std::size_t formatter_dispatch_function_t(dispatch_operation, void*, void*); - - struct frame_header { - union { - formatter_dispatch_function_t* pdispatch_function; // valid when status is failed_initialization or initialized - std::size_t frame_size; // valid when status == failed_error_check - }; - frame_status status; - }; - - template - std::size_t input_frame_dispatch(dispatch_operation operation, void* arg1, void* arg2); - -} - -using format_error_callback_t = std::function; - -class basic_log : private output_buffer { -public: - basic_log(); - basic_log(writer* pwriter) - { - open(pwriter); - } - basic_log(writer* pwriter, - std::size_t input_buffer_capacity, - std::size_t output_buffer_capacity) - { - open(pwriter, input_buffer_capacity, output_buffer_capacity); - } - virtual ~basic_log(); - - basic_log(basic_log const&) = delete; - basic_log& operator=(basic_log const&) = delete; - - void open(writer* pwriter); - void open(writer* pwriter, - std::size_t input_buffer_capacity, - std::size_t output_buffer_capacity); - - // Wait for the output worker to flush its remaining output queue, then shut - // down the background thread and release all buffers. Writing to the log - // after close() was called leads to undefined behavior. If the writer - // returns any error (temporary or permanent) at this point, then the ec - // parameter is set to the error code. Otherwise, ec.clear() is called. - virtual void close(std::error_code& ec) noexcept; - - // Call close(error_code) and throw writer_error if it fails. - virtual void close(); - - // Wake up the output worker thread to cause immediate output of all queued - // writes. Then block until the most recent write has been formatted in the - // output buffer, and finally flush the output buffer. If the output buffer - // flush generates an error, then that error is returned in ec. Otherwise - // ec.clear() is called. - virtual void flush(std::error_code& ec); - - // Call flush(error_code) and throw writer_error if it fails. - virtual void flush(); - - void format_error_callback( - format_error_callback_t callback = format_error_callback_t()) - { - std::lock_guard lk(callback_mutex_); - format_error_callback_ = move(callback); - } - - using output_buffer::writer_error_callback; - using output_buffer::temporary_error_policy; - using output_buffer::permanent_error_policy; - - void start_panic_flush(); - void await_panic_flush(); - bool await_panic_flush(unsigned int miliseconds); - - // Provide access to the internal worker-thread object. The intent is to - // allow platform-specific manipulation of the thread, such as setting - // priority or affinity. - std::thread& worker_thread() - { - return output_thread_; - } - - unsigned input_buffer_full_count() const - { - return detail::atomic_load_relaxed(&input_buffer_full_count_); - } - - std::size_t input_buffer_high_watermark() const - { - return detail::atomic_load_relaxed(&input_buffer_high_watermark_); - } - - using output_buffer::output_buffer_full_count; - using output_buffer::output_buffer_high_watermark; - -protected: - template - void write(Args&&... args) - { - using namespace detail; - typedef std::tuple::type...> args_t; - std::size_t const args_align = alignof(args_t); - std::size_t const args_offset = (sizeof(frame_header) + - args_align-1)/args_align*args_align; - std::size_t const frame_size_unaligned = args_offset + sizeof(args_t); - std::size_t const frame_size = (frame_size_unaligned + - RECKLESS_CACHE_LINE_SIZE-1)/RECKLESS_CACHE_LINE_SIZE* - RECKLESS_CACHE_LINE_SIZE; - -#ifdef RECKLESS_DEBUG - // If this assert triggers then you have tried to write to the log from - // within the worker thread (from writer_error_callback?). That's bad - // because it can lead to a deadlock. Instead, you need to format your - // string yourself and write directly to the output_buffer. - -#if defined(_POSIX_VERSION) - assert(!pthread_equal(pthread_self(), output_worker_native_handle_)); -#elif defined(_WIN32) - assert(GetCurrentThreadId() == output_worker_native_id_); -#endif - -#endif // RECKLESS_DEBUG - - frame_header* pframe = push_input_frame(frame_size); - pframe->pdispatch_function = &detail::input_frame_dispatch< - Formatter, - typename std::decay::type... - >; - - void* pargs = static_cast(static_cast(pframe)) - + args_offset; - // Let the compiler know that the placement-new call below - // doesn't need to perform a null-pointer check. - assume(pargs != nullptr); - - try { - new (pargs) args_t(std::forward(args)...); - } catch(...) { - atomic_store_release(&pframe->status, frame_status::failed_initialization); - throw; - } - atomic_store_release(&pframe->status, frame_status::initialized); - } - -private: - detail::frame_header* push_input_frame(std::size_t size); - detail::frame_header* push_input_frame_blind(std::size_t frame_size); - detail::frame_header* push_input_frame_slow_path( - detail::frame_header * pframe, bool error, std::size_t size); - - void output_worker(); - std::size_t wait_for_input(); - detail::frame_status acquire_frame(void* pframe); - std::size_t process_frame(void* pframe); - std::size_t skip_frame(void* pframe); - void clear_frame(void* pframe, std::size_t frame_size); - - void flush_output_buffer(); - - [[noreturn]] - void on_panic_flush_done(); - bool is_open() - { - return output_thread_.joinable(); - } - - detail::mpsc_ring_buffer input_buffer_; - detail::spsc_event input_buffer_full_event_; - detail::lockless_cv input_buffer_empty_event_; - - std::mutex callback_mutex_; - format_error_callback_t format_error_callback_; // access synchronized by callback_mutex_ - std::thread output_thread_; - detail::spsc_event panic_flush_done_event_; - - unsigned input_buffer_full_count_ = 0; - std::size_t input_buffer_high_watermark_ = 0; - -#if defined(_POSIX_VERSION) - pthread_t output_worker_native_handle_; -#elif defined(_WIN32) - unsigned long output_worker_native_id_; -#else - static_assert(false, "threading not implemented for this platform") -#endif // RECKLESS_DEBUG -}; - -class writer_error : public std::system_error { -public: - writer_error(std::error_code ec) : - system_error(ec) - { - } - char const* what() const noexcept override; -}; - -inline detail::frame_header* basic_log::push_input_frame( - std::size_t size) -{ - using namespace detail; - // This is possibly useless micro-optimization but after all, making this - // path fast is the whole point of the library so I'll indulge myself. - // Instead of having one branch for checking the result of - // input_buffer_.push and another for checking the error flag, we combine - // both checks into one. That means we have to mark the allocated input - // frame as failed_error_check if it succeeds but the error check does not. - auto pframe = static_cast(input_buffer_.push(size)); - auto error = atomic_load_acquire(&error_flag_); - std::uint64_t no_error = ~static_cast(error); - no_error &= reinterpret_cast(pframe); - if(likely(no_error != 0)) - return pframe; - else - return push_input_frame_slow_path(pframe, error, size); -} - -inline detail::frame_header* basic_log::push_input_frame_blind( - std::size_t size) -{ - using namespace detail; - auto pframe = static_cast(input_buffer_.push(size)); - if(likely(pframe != nullptr)) - return pframe; - else - return push_input_frame_slow_path(nullptr, false, size); -} - -namespace detail { - -template -void formatter_dispatch_helper(output_buffer* poutput, std::tuple&& args, index_sequence) -{ - Formatter::format(poutput, std::forward(std::get(args))...); -} - -template -std::size_t input_frame_dispatch(dispatch_operation operation, void* arg1, void* arg2) -{ - using namespace detail; - typedef std::tuple args_t; - std::size_t const args_align = alignof(args_t); - std::size_t const status_offset = sizeof(formatter_dispatch_function_t*); - std::size_t const args_offset = (status_offset + sizeof(frame_status) + - args_align-1)/args_align*args_align; - std::size_t const frame_size_unaligned = args_offset + sizeof(args_t); - std::size_t const frame_size = (frame_size_unaligned + - RECKLESS_CACHE_LINE_SIZE-1)/RECKLESS_CACHE_LINE_SIZE* - RECKLESS_CACHE_LINE_SIZE; - - typename make_index_sequence::type indexes; - - if(likely(operation == invoke_formatter)) { - auto poutput = static_cast(arg1); - auto pinput = static_cast(arg2); - struct args_owner { - args_owner(args_t& args) : - args(args) - { - } - ~args_owner() - { - // We need to do this from a destructor in case calling the - // formatter throws an exception. We can't just do it in a catch - // clause because we want uncaught_exception to return true during - // the call. - args.~args_t(); - } - args_t& args; - }; - - args_owner args(*reinterpret_cast(pinput + args_offset)); - formatter_dispatch_helper(poutput, move(args.args), indexes); - return frame_size; - } else { - // operation == get_typeid - *static_cast(arg1) = &typeid(args_t); - return frame_size; - } -} - -} // namespace detail -} // namespace reckless - -#endif // RECKLESS_BASIC_LOG_HPP diff --git a/external/reckless/reckless/include/reckless/crash_handler.hpp b/external/reckless/reckless/include/reckless/crash_handler.hpp deleted file mode 100644 index b9029984..00000000 --- a/external/reckless/reckless/include/reckless/crash_handler.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -namespace reckless { -class basic_log; -void install_crash_handler(std::initializer_list logs); -void uninstall_crash_handler(); - -inline void install_crash_handler(basic_log* plog) -{ - install_crash_handler({plog}); -} - -class scoped_crash_handler { -public: - scoped_crash_handler(basic_log* plog) - { - install_crash_handler(plog); - } - scoped_crash_handler(std::initializer_list logs) - { - install_crash_handler(logs); - } - ~scoped_crash_handler() - { - uninstall_crash_handler(); - } -}; -} // namespace reckless diff --git a/external/reckless/reckless/include/reckless/detail/fd_writer.hpp b/external/reckless/reckless/include/reckless/detail/fd_writer.hpp deleted file mode 100644 index 75f83c43..00000000 --- a/external/reckless/reckless/include/reckless/detail/fd_writer.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_FD_WRITER_HPP -#define RECKLESS_FD_WRITER_HPP - -#include - -namespace reckless { -namespace detail { - -class fd_writer : public writer { -public: -#if defined(__unix__) - fd_writer(int fd) : fd_(fd) {} -#elif defined(_WIN32) - fd_writer(void* handle) : handle_(handle) {} -#endif - - std::size_t write(void const* pbuffer, std::size_t count, std::error_code& ec) noexcept override; - -#if defined(__unix__) - int fd_; -#elif defined(_WIN32) - void* handle_; -#endif -}; - -} // namespace detail -} // namespace reckless - -#endif // RECKLESS_FD_WRITER_HPP diff --git a/external/reckless/reckless/include/reckless/detail/lockless_cv.hpp b/external/reckless/reckless/include/reckless/detail/lockless_cv.hpp deleted file mode 100644 index 95b90292..00000000 --- a/external/reckless/reckless/include/reckless/detail/lockless_cv.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_DETAIL_LOCKLESS_CV_HPP -#define RECKLESS_DETAIL_LOCKLESS_CV_HPP - -#include - -namespace reckless { -namespace detail { - -// This provides the functionality of a condition variable for lockless code. -// Normally a condition variable uses a mutex to ensure that the condition -// doesn't have time to change between checking the condition and waiting. This -// prevents the risk of race conditions that occurs when using events, whereby -// -// 1. Thread 1 checks the condition and it is not in the desired state. -// 2. Thread 1 is preempted by thread 2, which changes the condition and -// signals the event. -// 3. Thread, 3 which is waiting on the event starts executing and resets the -// event. -// 4. Thread 1 is scheduled again and begins to wait for the event even though -// the condition is now already in the desired state. It might stall here -// indefinitely waiting for the event to be signaled again. -// -// Since we don't want to take a lock before checking the condition, we provide -// an API to read the change-notification count instead. The intended usage is -// 1. Call notify_count() and save the count. -// 2. Check the condition of the shared data. -// 3. If the shared data does not fulfill the desired condition, call wait() -// and provide the notify count. The wait will occur only if the notify -// count is still holds the expected value. If not, the call completes -// immediately and execution restarts at 1. -// -// The read in step 1 has acquire semantics, guaranteeing that any writes to the -// shared data done by another thread will occur before it notifies any waiters. -// The notify_all() operation increases the count using corresponding release -// semantics. -// -// Note that, just like with ordinary condition variables, a completed wait() -// does not guarantee that the desired condition is fulfilled. -class lockless_cv { -public: - lockless_cv() : notify_count_(0) {} - - unsigned notify_count() const - { - return atomic_load_acquire(¬ify_count_); - } - - void notify_all(); - void wait(unsigned expected_notify_count); - void wait(unsigned expected_notify_count, unsigned milliseconds); - -private: - unsigned notify_count_; -}; - -} // namespace detail -} // namespace reckless - -#endif // RECKLESS_DETAIL_LOCKLESS_CV_HPP diff --git a/external/reckless/reckless/include/reckless/detail/mpsc_ring_buffer.hpp b/external/reckless/reckless/include/reckless/detail/mpsc_ring_buffer.hpp deleted file mode 100644 index 199f7af8..00000000 --- a/external/reckless/reckless/include/reckless/detail/mpsc_ring_buffer.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "platform.hpp" // likely, atomic_*, pause - -#include // size_t -#include // uint64_t, uintptr_t -#include // memset - -namespace reckless { -namespace detail { - -// This is a lock-free, multiple-producer, single-consumer "magic ring buffer": -// a ring buffer / circular buffer that takes advantage of the virtual memory -// system to map the same physical memory twice to consecutive memory addresses. -// This avoids the added complexity of having to deal with buffer wraparound in -// the middle of an allocated block. Furthermore, it uses 64-bit offsets that -// never wrap to represent the read and write positions of the buffer. That is, -// the read position is always less than or equal to the write position, hence -// requiring only a simple subtraction to determine how much buffer space is -// currently occupied. More importantly, doing this avoids the ABA problem. The -// final address to use for actual memory access is computed by a modulo -// operation once a block has been allocated. We assume that the 64-bit position -// variables will never overflow. A process that writes 100 GiB every second -// would take five years for this to happen. -class mpsc_ring_buffer { -public: - mpsc_ring_buffer() - { - rewind(); - pbuffer_start_ = nullptr; - capacity_ = 0; - } - - mpsc_ring_buffer(std::size_t capacity) - { - init(capacity); - } - - ~mpsc_ring_buffer() - { - destroy(); - } - - void reserve(std::size_t capacity) - { - destroy(); - init(capacity); - } - - void* push(std::size_t size) noexcept - { - auto capacity = capacity_; - for(;;pause()) { - auto wp = atomic_load_relaxed(&next_write_position_); - auto rp = atomic_load_relaxed(&next_read_position_); - auto nwp = wp + size; - if(likely(nwp - rp <= capacity)) { - } else { - return nullptr; - } - - if(atomic_compare_exchange_weak_relaxed(&next_write_position_, wp, nwp)) - return pbuffer_start_ + (wp & (capacity-1)); - } - } - - // Allocate all remaining space, filling the buffer to its capacity. - void deplete() noexcept - { - auto capacity = capacity_; - for(;;pause()) { - auto wp = atomic_load_relaxed(&next_write_position_); - auto rp = atomic_load_relaxed(&next_read_position_); - auto used = wp - rp; - auto remaining = capacity - used; - auto nwp = wp + remaining; - if(atomic_compare_exchange_weak_relaxed(&next_write_position_, wp, nwp)) - return; - } - } - - std::uint64_t read_position() noexcept - { - return next_read_position_; - } - - void* address(std::uint64_t position) noexcept - { - return pbuffer_start_ + (position & (capacity_ - 1)); - } - - std::size_t size() noexcept - { - auto wp = atomic_load_relaxed(&next_write_position_); - return static_cast(wp - next_read_position_); - } - - void pop_release(std::size_t size) noexcept - { - atomic_store_release(&next_read_position_, next_read_position_+size); - } - - void pop_relaxed(std::size_t size) noexcept - { - atomic_store_relaxed(&next_read_position_, next_read_position_+size); - } - -private: - void init(std::size_t capacity); - void destroy(); - void rewind() - { - next_write_position_ = 0; - next_read_position_ = 0; - } - - // To avoid false sharing that triggers unnecessary cache-line - // ping pong we segment these variables by how they are accessed. - // First, the variables that are only read by both consumer and producer. - char padding1_[RECKLESS_CACHE_LINE_SIZE]; - char* pbuffer_start_; - std::size_t capacity_; - char padding2_[RECKLESS_CACHE_LINE_SIZE - - sizeof(char*) - sizeof(std::size_t)]; - - // Next, variables that are updated by the producer and read by - // the consumer. Strictly the consumer does not access - // next_read_position_cached_ at all, but when it is updated it - // always happens together with next_write_position_ anyway. - std::uint64_t next_write_position_; - char padding3_[RECKLESS_CACHE_LINE_SIZE - 1*8]; - - // Finally, next_read_position_ is updated by the consumer and - // somtimes read by the producer. - std::uint64_t next_read_position_; - char padding4_[RECKLESS_CACHE_LINE_SIZE - 8]; -}; - -} // namespace detail -} // namespace reckless diff --git a/external/reckless/reckless/include/reckless/detail/platform.hpp b/external/reckless/reckless/include/reckless/detail/platform.hpp deleted file mode 100644 index 1927e3a3..00000000 --- a/external/reckless/reckless/include/reckless/detail/platform.hpp +++ /dev/null @@ -1,394 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_DETAIL_PLATFORM_HPP -#define RECKLESS_DETAIL_PLATFORM_HPP - -#ifndef RECKLESS_CACHE_LINE_SIZE -#define RECKLESS_CACHE_LINE_SIZE 64 -#endif - -#include // uint64_t, int64_t -#include // enable_if, underlying_type - -#if defined(__GNUC__) -#define RECKLESS_TLS __thread -#elif defined(_MSC_VER) -#define RECKLESS_TLS __declspec(thread) -#else -// Note that __thread/declspec(thread) is not exactly the same as C++11 -// thread_local since the latter has a runtime penalty. See -// https://stackoverflow.com/questions/13106049/c11-gcc-4-8-thread-local-performance-penalty -// https://gcc.gnu.org/gcc-4.8/changes.html#cxx -static_assert(false, "RECKLESS_TLS is not implemented for this compiler") -#endif - -#if defined(_MSC_VER) -# if defined(_M_IX86) || defined(_M_X64) - extern "C" { - long _InterlockedIncrement(long volatile* Addend); - __int64 _InterlockedCompareExchange64(__int64 volatile* Destination, __int64 Exchange, __int64 Comparand); - long _InterlockedExchangeAdd(long volatile * Addend, long Value); - void _mm_prefetch(char const* p, int i); - void _mm_pause(void); - void __cpuid(int[4], int); - unsigned __int64 __rdtsc(); - unsigned __int64 __rdtscp(unsigned int *); - } -# pragma intrinsic(_InterlockedIncrement) -# pragma intrinsic(_InterlockedCompareExchange64) -# pragma intrinsic(_InterlockedExchangeAdd) -# pragma intrinsic(_mm_prefetch) -# pragma intrinsic(_mm_pause) -# pragma intrinsic(__rdtsc) -# pragma intrinsic(__rdtscp) -# else - static_assert(false, "Only x86/x64 support is implemented for this compiler"); -# endif -#endif - -namespace reckless { -namespace detail { - -template -T atomic_load_relaxed(T const* pvalue) -{ -#if defined(__GNUC__) - // TODO check what this does on gcc; - return __atomic_load_n(pvalue, __ATOMIC_RELAXED); -#elif defined(_MSC_VER) - // Chapter 7, Part 3A - System Programming Guide of Intel's processor manuals: - // 8.1.1 Guaranteed Atomic Operations - // The Pentium processor (and newer processors since) guarantees that the following - // additional memory operations will always be carried out atomically : - // * Reading or writing a quadword aligned on a 64-bit boundary - // - // On IA-32 the VC++ stdlib goes through many hoops with interlocked Ors, loops etc - // to achieve atomicity when it really isn't necessary. Same applies for other atomic - // load/store functions. - return *pvalue; -#else - static_assert(false, "atomic_load_relaxed is not implemented for this compiler"); -#endif -} - -template -void atomic_store_relaxed(T* ptarget, T value, - typename std::enable_if::value>::type* = nullptr) -{ -#if defined(__GNUC__) - // TODO check what this does on gcc; - __atomic_store_n(ptarget, value, __ATOMIC_RELAXED); -#elif defined(_MSC_VER) - *ptarget = value; -#else - static_assert(false, "atomic_store_relaxed is not implemented for this compiler"); -#endif -} - - -template -void atomic_store_relaxed(T* ptarget, T value, - typename std::enable_if::value>::type* = nullptr) -{ - using UT = typename std::underlying_type::type; - atomic_store_relaxed(static_cast(static_cast(ptarget)), - static_cast(value)); -} - -template -T atomic_load_acquire(T const* pvalue, - typename std::enable_if::value>::type* = nullptr) -{ -#if defined(__GNUC__) - return __atomic_load_n(pvalue, __ATOMIC_ACQUIRE); -#elif defined(_MSC_VER) - return *pvalue; -#else - static_assert(false, "atomic_store_relaxed is not implemented for this compiler"); -#endif -} - -template -T atomic_load_acquire(T const* pvalue, - typename std::enable_if::value>::type* = nullptr) -{ - using UT = typename std::underlying_type::type; - return static_cast(atomic_load_acquire(static_cast( - static_cast(pvalue)))); -} - -template -void atomic_store_release(T* ptarget, T value, - typename std::enable_if::value>::type* = nullptr) -{ -#if defined(__GNUC__) - __atomic_store_n(ptarget, value, __ATOMIC_RELEASE); -#elif defined(_MSC_VER) - *ptarget = value; -#else - static_assert(false, "atomic_store_release is not implemented for this compiler"); -#endif -} - -template -void atomic_store_release(T* ptarget, T value, - typename std::enable_if::value>::type* = nullptr) -{ - using UT = typename std::underlying_type::type; - return atomic_store_release(static_cast(static_cast(ptarget)), - static_cast(value)); -} - -#if defined(__GNUC__) - -template -T atomic_fetch_add_relaxed(T* ptarget, U value) -{ - return __atomic_fetch_add(ptarget, value, __ATOMIC_RELAXED); -} - -template -T atomic_fetch_add_release(T* ptarget, U value) -{ - return __atomic_fetch_add(ptarget, value, __ATOMIC_RELEASE); -} - -#elif defined(_MSC_VER) - -inline long atomic_fetch_add_relaxed(long* ptarget, long value) -{ - return _InterlockedExchangeAdd(ptarget, value); -} -inline long atomic_fetch_add_release(long* ptarget, long value) -{ - // TODO this can use _InterlockedExchangeAdd_rel on ARM. - return _InterlockedExchangeAdd(ptarget, value); -} - -inline int atomic_fetch_add_relaxed(int* ptarget, int value) -{ - return atomic_fetch_add_relaxed(reinterpret_cast(ptarget), static_cast(value)); -} - -inline int atomic_fetch_add_release(int* ptarget, int value) -{ - return atomic_fetch_add_release(reinterpret_cast(ptarget), static_cast(value)); -} - -inline unsigned atomic_fetch_add_relaxed(unsigned* ptarget, unsigned value) -{ - return atomic_fetch_add_relaxed(reinterpret_cast(ptarget), static_cast(value)); -} - -inline unsigned atomic_fetch_add_release(unsigned* ptarget, unsigned value) -{ - return atomic_fetch_add_release(reinterpret_cast(ptarget), static_cast(value)); -} - -#else -static_assert(false, "atomic_add_relaxed is not implemented for this compiler"); -#endif - -#if defined(__GNUC__) -template -T atomic_increment_fetch_relaxed(T* ptarget) -{ - return __atomic_add_fetch(ptarget, 1, __ATOMIC_RELAXED); -} - -#elif defined(_MSC_VER) -inline long atomic_increment_fetch_relaxed(int* ptarget) -{ - return _InterlockedIncrement(static_cast(static_cast(ptarget))); -} -inline long atomic_increment_fetch_relaxed(long* ptarget) -{ - return _InterlockedIncrement(ptarget); -} - -inline unsigned atomic_increment_fetch_relaxed(unsigned* ptarget) -{ - return atomic_increment_fetch_relaxed(static_cast(static_cast(ptarget))); -} - -#else -static_assert(false, "atomic_increment_fetch_relaxed is not implemented for this compiler"); -#endif - -#if defined(__GNUC__) -template -bool atomic_compare_exchange_weak_relaxed(T* ptarget, T* pexpected, T desired) -{ - return __atomic_compare_exchange_n(ptarget, pexpected, desired, true, - __ATOMIC_RELAXED, __ATOMIC_RELAXED); -} -template -bool atomic_compare_exchange_weak_relaxed(T* ptarget, T expected, T desired) -{ - return __atomic_compare_exchange_n(ptarget, &expected, desired, true, - __ATOMIC_RELAXED, __ATOMIC_RELAXED); -} -#elif defined(_MSC_VER) -inline bool atomic_compare_exchange_weak_relaxed(std::uint64_t* ptarget, - std::uint64_t* pexpected, std::uint64_t desired) -{ - auto expected = *pexpected; - std::uint64_t actual = _InterlockedCompareExchange64( - reinterpret_cast(ptarget), - reinterpret_cast(desired), - reinterpret_cast(expected)); - if (actual == expected) { - return true; - } else { - *pexpected = actual; - return false; - } -} - -inline bool atomic_compare_exchange_weak_relaxed(std::uint64_t* ptarget, - std::uint64_t expected, std::uint64_t desired) -{ - std::uint64_t actual = _InterlockedCompareExchange64( - reinterpret_cast(ptarget), - reinterpret_cast(desired), - reinterpret_cast(expected)); - return actual == expected; -} - -#else - static_assert(false, "atomic_compare_exchange_weak_relaxed is not implemented for this platform") -#endif - -inline bool likely(bool expr) { -#ifdef __GNUC__ - return __builtin_expect(expr, true); -#else - return expr; -#endif -} - -inline bool unlikely(bool expr) { -#ifdef __GNUC__ - return __builtin_expect(expr, false); -#else - return expr; -#endif -} - -inline void assume(bool condition) -{ -#if defined(__GNUC__) - // FIXME check that this is still working - if(!condition) - __builtin_unreachable(); -#elif defined(_MSC_VER) - __assume(condition); -#else - static_assert(false, "assume() is not implemented for this compiler"); -#endif -} - -inline std::uint64_t rdtsc() -{ -#if defined(__GNUC__) - return __builtin_ia32_rdtsc(); -#elif defined(_MSC_VER) - return __rdtsc(); -#else - static_assert(false, "rdtsc() is not implemented for this compiler"); -#endif -} - -inline std::uint64_t serializing_performance_timestamp_begin() -{ -#if defined(__GNUC__) - std::uint64_t t_high; - std::uint64_t t_low; - std::uint64_t b, c; - asm volatile( - "cpuid\n\t" - "rdtsc\n\t" - : "=a"(t_low), "=b"(b), "=c"(c), "=d"(t_high)); - return (t_high << 32) | static_cast(t_low); -#elif defined(_MSC_VER) - int cpuinfo[4]; - __cpuid(cpuinfo, 0); - __rdtsc(); -#else - static_assert(false, "serializing_performance_timestamp_begin() is not implemented for this compiler"); -#endif -} - -inline std::uint64_t serializing_performance_timestamp_end() -{ -#if defined(__GNUC__) - std::uint64_t t_high; - std::uint64_t t_low; - std::uint64_t aux; - asm volatile("rdtscp\n\t" : "=a"(t_low), "=c"(aux), "=d"(t_high)); - std::uint64_t a, b, c, d; - asm volatile("cpuid\n\t" : "=a"(a), "=b"(b), "=c"(c), "=d"(d)); - return (t_high << 32) | static_cast(t_low); -#elif defined(_MSC_VER) - unsigned int aux; - __rdtscp(&aux); - int cpuinfo[4]; - __cpuid(cpuinfo, 0); -#else - static_assert(false, "serializing_performance_timestamp_end() is not implemented for this compiler"); -#endif -} - -template -inline void prefetch(void const* ptr) -{ -#if defined(__GNUC__) - __builtin_prefetch(ptr, 0, Locality); -#elif defined(_MSC_VER) - _mm_prefetch(static_cast(ptr), Locality); -#else - static_assert(false, "prefetch() is not implemented for this compiler"); -#endif -} - -inline void pause() -{ -#if defined(__clang__) - asm volatile("pause"); -#elif defined(__GNUC__) - __builtin_ia32_pause(); -#elif defined(_MSC_VER) - _mm_pause(); -#else - static_assert(false, "pause() is not implemented for this compiler"); -#endif -} - -unsigned get_page_size(); -extern unsigned const page_size; - -void set_thread_name(char const* name); - -} // detail -} // reckless - -#endif // RECKLESS_DETAIL_PLATFORM_HPP diff --git a/external/reckless/reckless/include/reckless/detail/spsc_event.hpp b/external/reckless/reckless/include/reckless/detail/spsc_event.hpp deleted file mode 100644 index 54b21ef8..00000000 --- a/external/reckless/reckless/include/reckless/detail/spsc_event.hpp +++ /dev/null @@ -1,166 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_DETAIL_SPSC_EVENT_HPP -#define RECKLESS_DETAIL_SPSC_EVENT_HPP -#include -#include - -#if defined(__linux__) -#include -#include -#include -#include -#include - -namespace reckless { -namespace detail { -// TODO what makes this single-producer single-consumer? We aren't using it -// that way, since multiple threads are both waiting and signaling, so it -// should really be mpmc_event. -class spsc_event { -public: - spsc_event() : signal_(0) - { - } - - void signal() - { - // TODO possible performance improvement: If we know nobody is waiting, - // then we don't need to signal the futex. - atomic_exchange_explicit(&signal_, 1, std::memory_order_release); - sys_futex(&signal_, FUTEX_WAKE, 1, nullptr, nullptr, 0); - } - - void wait() - { - int signal = atomic_exchange_explicit(&signal_, 0, std::memory_order_acquire); - while(not signal) { - // TODO may be beneficial to just put a cpu pause here before - // reading the value again, i.e. a spinlock kind of construction. - sys_futex(&signal_, FUTEX_WAIT, 0, nullptr, nullptr, 0); - signal = atomic_exchange_explicit(&signal_, 0, std::memory_order_acquire); - } - } - - bool wait(unsigned milliseconds) - { - int signal = atomic_exchange_explicit(&signal_, 0, std::memory_order_acquire); - if(signal) - return true; - struct timespec start; - if(0 != clock_gettime(CLOCK_MONOTONIC, &start)) - throw std::system_error(errno, std::system_category()); - - unsigned elapsed_ms = 0; - struct timespec timeout = {0, 0}; - - do { - unsigned remaining_ms = milliseconds - elapsed_ms; - timeout.tv_sec = remaining_ms/1000; - timeout.tv_nsec = static_cast(remaining_ms%1000)*1000000; - sys_futex(&signal_, FUTEX_WAIT, 0, &timeout, nullptr, 0); - signal = atomic_exchange_explicit(&signal_, 0, std::memory_order_acquire); - if(signal) - return true; - - struct timespec now; - if(0 != clock_gettime(CLOCK_MONOTONIC, &now)) - throw std::system_error(errno, std::system_category()); - - elapsed_ms = 1000*static_cast(now.tv_sec - start.tv_sec); - elapsed_ms += (now.tv_nsec - start.tv_nsec)/1000000; - } while(elapsed_ms < milliseconds); - return false; - } - -private: - int sys_futex(void *addr1, int op, int val1, struct timespec const *timeout, - void *addr2, int val3) - { - return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); - } - - int atomic_exchange_explicit(int* pvalue, int new_value, std::memory_order) - { - // FIXME this can be replaced with __atomic builtins. - int res = new_value; - asm volatile("xchg %0, %1\n\t" - : "+r"(res), "+m"(*pvalue) - : - : "memory", "cc"); - return res; - } - - int signal_; -}; - -#elif defined(_WIN32) -#if !defined(_MSC_VER) -static_assert(false, "spsc_event is not implemented for Windows on this compiler") -#endif - -#include - -namespace reckless { -namespace detail { - -extern "C" { - unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); - int __stdcall SetEvent(void* hEvent); -} - -class spsc_event { -public: - spsc_event(); - ~spsc_event(); - void signal() - { - SetEvent(handle_); - } - - void wait() - { - bool success = wait(0xFFFFFFFF); // INFINITE - (void)success; - assert(success); - } - - bool wait(unsigned milliseconds) - { - auto result = WaitForSingleObject(handle_, milliseconds); - assert(result == 0 || result == 0x102L); // WAIT_OBJECT_0 || WAIT_TIMEOUT - return result == 0; - } - -private: - void* handle_; -}; - -#else -static_assert(false, "spsc_event is not implemented for this OS") - -#endif - -} // namespace detail -} // namespace reckless - -#endif // RECKLESS_DETAIL_SPSC_EVENT_HPP diff --git a/external/reckless/reckless/include/reckless/detail/trace_log.hpp b/external/reckless/reckless/include/reckless/detail/trace_log.hpp deleted file mode 100644 index 1910054c..00000000 --- a/external/reckless/reckless/include/reckless/detail/trace_log.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef RECKLESS_ENABLE_TRACE_LOG -#include -#else -#define RECKLESS_TRACE(Event, ...) do {} while(false) -#endif // RECKLESS_ENABLE_TRACE_LOG diff --git a/external/reckless/reckless/include/reckless/detail/utility.hpp b/external/reckless/reckless/include/reckless/detail/utility.hpp deleted file mode 100644 index 86bae68e..00000000 --- a/external/reckless/reckless/include/reckless/detail/utility.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_DETAIL_UTILITY_HPP -#define RECKLESS_DETAIL_UTILITY_HPP - -#include // size_t -#include // uintptr_t -#include // enable_if, is_pointer, remove_pointer - -namespace reckless { -namespace detail { - -template -struct replace_pointer_type; - -template -struct replace_pointer_type -{ - using type = To*; -}; - -template -struct replace_pointer_type -{ - using type = To const*; -}; - -template -T char_cast(char* p) -{ - return static_cast(static_cast(p)); -} - -template -T char_cast(char const* p) -{ - return static_cast(static_cast(p)); -} - -template -typename replace_pointer_type::type -char_cast(T p) -{ - using void_ptr_t = typename replace_pointer_type::type; - using char_ptr_t = typename replace_pointer_type::type; - return static_cast(static_cast(p)); -} - -inline constexpr bool is_power_of_two(std::size_t v) -{ - return (v & (v - 1)) == 0; -} - -template -struct index_sequence -{ -}; - -template -struct make_index_sequence_helper -{ - typedef typename make_index_sequence_helper::type type; -}; - -template -struct make_index_sequence_helper -{ - typedef index_sequence type; -}; - -template -struct make_index_sequence -{ - typedef typename make_index_sequence_helper<0, N>::type type; -}; - -} -} - -#endif // RECKLESS_DETAIL_UTILITY_HPP diff --git a/external/reckless/reckless/include/reckless/file_writer.hpp b/external/reckless/reckless/include/reckless/file_writer.hpp deleted file mode 100644 index 3951716d..00000000 --- a/external/reckless/reckless/include/reckless/file_writer.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_FILE_WRITER_HPP -#define RECKLESS_FILE_WRITER_HPP - -#include "detail/fd_writer.hpp" - -namespace reckless { - -class file_writer : public detail::fd_writer { -public: - file_writer(char const* path); -#if defined(_WIN32) - file_writer(wchar_t const* path); -#endif - - ~file_writer(); -}; - -} // namespace reckless - -#endif // RECKLESS_FILE_WRITER_HPP diff --git a/external/reckless/reckless/include/reckless/ntoa.hpp b/external/reckless/reckless/include/reckless/ntoa.hpp deleted file mode 100644 index 84ddc135..00000000 --- a/external/reckless/reckless/include/reckless/ntoa.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_DETAIL_ITOA_HPP -#define RECKLESS_DETAIL_ITOA_HPP -#include - -#include - -namespace reckless { - -unsigned const UNSPECIFIED_PRECISION = std::numeric_limits::max(); -// TODO It probably doesn't hurt to document these members even though they're -// in the printf docs. Also, would be nice to reduce footprint e.g. with a -// bitset. -struct conversion_specification { - conversion_specification() : - minimum_field_width(0), - precision(UNSPECIFIED_PRECISION), - plus_sign(0), - left_justify(false), - alternative_form(false), - pad_with_zeroes(false), - uppercase(false) - { - } - unsigned minimum_field_width; - unsigned precision; - char plus_sign; - bool left_justify; - bool alternative_form; - bool pad_with_zeroes; - bool uppercase; -}; - -void itoa_base10(output_buffer* pbuffer, int value, conversion_specification const& cs); -void itoa_base10(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs); -void itoa_base10(output_buffer* pbuffer, long value, conversion_specification const& cs); -void itoa_base10(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs); -void itoa_base10(output_buffer* pbuffer, long long value, conversion_specification const& cs); -void itoa_base10(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs); - -void itoa_base16(output_buffer* pbuffer, int value, conversion_specification const& cs); -void itoa_base16(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs); -void itoa_base16(output_buffer* pbuffer, long value, conversion_specification const& cs); -void itoa_base16(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs); -void itoa_base16(output_buffer* pbuffer, long long value, conversion_specification const& cs); -void itoa_base16(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs); - -void ftoa_base10_f(output_buffer* pbuffer, double value, conversion_specification const& cs); -void ftoa_base10_g(output_buffer* pbuffer, double value, conversion_specification const& cs); - -namespace detail -{ - extern char const decimal_digits[201]; - extern std::uint64_t const power_lut[19]; -} - -} // namespace reckless - -#endif // RECKLESS_DETAIL_ITOA_HPP diff --git a/external/reckless/reckless/include/reckless/output_buffer.hpp b/external/reckless/reckless/include/reckless/output_buffer.hpp deleted file mode 100644 index ac9527dd..00000000 --- a/external/reckless/reckless/include/reckless/output_buffer.hpp +++ /dev/null @@ -1,235 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_OUTPUT_BUFFER_HPP -#define RECKLESS_OUTPUT_BUFFER_HPP - -#include "detail/platform.hpp" // likely -#include "detail/spsc_event.hpp" - -#include // size_t -#include // bad_alloc -#include // strlen, memcpy -#include // function -#include -#include // system_error, error_code -#include - -namespace reckless { -class writer; -class output_buffer; - -enum class error_policy { - ignore, - notify_on_recovery, - block, - fail_immediately - // TODO: policy to invoke callback from background thread -}; - -// Thrown if output_buffer::reserve() is used to allocate more than can fit in -// the output buffer during formatting of a single input frame. If this happens -// then you need to either enlarge the output buffer or reduce the amount of -// data produced by your formatter. -class excessive_output_by_frame : public std::bad_alloc { -public: - char const* what() const noexcept override; -}; - -// Thrown if output_buffer::flush fails. This inherits from bad_alloc because -// it makes sense in the context where the formatter calls -// output_buffer::reserve(), and a flush intended to create more space in the -// buffer fails. In that context, it is essentially an allocation error. The -// formatter may catch this if it wishes, but it is expected that most will -// just let it fall through to the worker thread, where it will be dealt with -// appropriately. -// -// TODO we need to make flush() private, clients can't go around calling it in -// the formatter because if it fails and the exception reaches the worker -// thread then we'll end up assuming the buffer ran out of space and something -// was lost. -class flush_error : public std::bad_alloc { -public: - flush_error(std::error_code const& error_code) : - error_code_(error_code) - { - } - char const* what() const noexcept override; - std::error_code const& code() const - { - return error_code_; - } - -private: - std::error_code error_code_; -}; - -using writer_error_callback_t = std::function; - -class output_buffer { -public: - output_buffer(); - // TODO hide functions that are not relevant to the client, e.g. move - // assignment, empty(), flush etc? - output_buffer(writer* pwriter, std::size_t max_capacity); - ~output_buffer(); - - char* reserve(std::size_t size) - { - std::size_t remaining = pbuffer_end_ - pcommit_end_; - if(detail::likely(size <= remaining)) - return pcommit_end_; - else - return reserve_slow_path(size); - } - - void commit(std::size_t size) - { - pcommit_end_ += size; - } - - void write(void const* buf, std::size_t count); - - void write(char const* s) - { - write(s, std::strlen(s)); - } - - void write(char c) - { - char* p = reserve(1); - *p = c; - commit(1); - } - - unsigned output_buffer_full_count() const - { - return detail::atomic_load_relaxed(&output_buffer_full_count_); - } - - std::size_t output_buffer_high_watermark() const - { - return detail::atomic_load_relaxed(&output_buffer_high_watermark_); - } - -protected: - void reset() noexcept; - // throw bad_alloc if unable to malloc() the buffer. - void reset(writer* pwriter, std::size_t max_capacity); - - // Put a watermark indicating where the last complete output frame ends. - void frame_end() - { - pframe_end_ = pcommit_end_; - } - // Notify that an input frame was lost because of a flush error. - void lost_frame() - { - ++lost_input_frames_; - revert_frame(); - } - - void revert_frame() - { - // Undo everything that has been written during the current input frame. - pcommit_end_ = pframe_end_; - } - - bool has_complete_frame() const - { - return pframe_end_ != pbuffer_; - } - - // Need to make flush() public because of g++ bug 66957 - // -#ifdef __GNUC__ -public: -#endif - void flush(); -#ifdef __GNUC__ -protected: -#endif - - // Must not write to the log since it may cause a deadlock. May not throw - // exceptions. - void writer_error_callback(writer_error_callback_t callback = writer_error_callback_t()) - { - std::lock_guard lk(writer_error_callback_mutex_); - writer_error_callback_ = move(callback); - } - - error_policy temporary_error_policy() const - { - return temporary_error_policy_.load(std::memory_order_relaxed); - } - - void temporary_error_policy(error_policy ep) - { - temporary_error_policy_.store(ep, std::memory_order_relaxed); - } - - error_policy permanent_error_policy() const - { - return permanent_error_policy_.load(std::memory_order_relaxed); - } - - void permanent_error_policy(error_policy ep) - { - assert(ep != error_policy::notify_on_recovery - && ep != error_policy::block); - permanent_error_policy_.store(ep, std::memory_order_relaxed); - } - - detail::spsc_event shared_input_queue_full_event_; // FIXME rename to something that indicates this is used for all "notifications" to the worker thread - - std::atomic temporary_error_policy_{error_policy::ignore}; - std::atomic permanent_error_policy_{error_policy::fail_immediately}; - std::error_code error_code_; // error code for error state - bool error_flag_ = false; // error state - bool panic_flush_ = false; - -private: - output_buffer(output_buffer const&) = delete; - output_buffer& operator=(output_buffer const&) = delete; - - char* reserve_slow_path(std::size_t size); - void increment_output_buffer_full_count() - { - detail::atomic_increment_fetch_relaxed(&output_buffer_full_count_); - } - - writer* pwriter_ = nullptr; - char* pbuffer_ = nullptr; - char* pframe_end_ = nullptr; - char* pcommit_end_ = nullptr; - char* pbuffer_end_ = nullptr; - unsigned lost_input_frames_ = 0; - std::error_code initial_error_; // Keeps track of the first error that caused lost_input_frames_ to become non-zero. - std::mutex writer_error_callback_mutex_; - writer_error_callback_t writer_error_callback_; - - unsigned output_buffer_full_count_ = 0; - std::size_t output_buffer_high_watermark_ = 0; -}; - -} - -#endif // RECKLESS_OUTPUT_BUFFER_HPP diff --git a/external/reckless/reckless/include/reckless/policy_log.hpp b/external/reckless/reckless/include/reckless/policy_log.hpp deleted file mode 100644 index cdd65397..00000000 --- a/external/reckless/reckless/include/reckless/policy_log.hpp +++ /dev/null @@ -1,276 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_POLICY_LOG_HPP -#define RECKLESS_POLICY_LOG_HPP - -#include -#include -#include // RECKLESS_TLS -#include // detail::decimal_digits -#include // forward -#include // memset -#include // size_t -#include // clock_gettime - -namespace reckless { - -#if defined(_WIN32) -namespace detail { -extern "C" { - void __stdcall GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime); - int __stdcall FileTimeToLocalFileTime(void const* lpFileTime, void* lpLocalFileTime); - int __stdcall FileTimeToSystemTime(void const* lpFileTime, void* lpSystemTime); -} -} -#endif - -class timestamp_field { -public: -#if defined(__unix__) - timestamp_field() - { -#if defined(__linux__) - clock_gettime(CLOCK_REALTIME_COARSE, &ts_); -#else - clock_gettime(CLOCK_REALTIME, , &ts_); -#endif - } - - bool format(output_buffer* pbuffer) - { - struct tm tm; - localtime_r(&ts_.tv_sec, &tm); - - format_timestamp(pbuffer, - tm.tm_year + 1900, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - static_cast(ts_.tv_nsec/1000000u)); - - return true; - } - -private: - struct timespec ts_; - -#elif defined(_WIN32) - - timestamp_field() - { - reckless::detail::GetSystemTimeAsFileTime(&ft_); - } - - bool format(output_buffer* pbuffer) - { -#pragma pack(push, 8) - struct SYSTEMTIME { - unsigned short wYear; - unsigned short wMonth; - unsigned short wDayOfWeek; - unsigned short wDay; - unsigned short wHour; - unsigned short wMinute; - unsigned short wSecond; - unsigned short wMilliseconds; - }; -#pragma pack(pop) - filetime ft_local; - reckless::detail::FileTimeToLocalFileTime(&ft_, &ft_local); - SYSTEMTIME st; - reckless::detail::FileTimeToSystemTime(&ft_local, &st); - format_timestamp(pbuffer, - st.wYear, - st.wMonth, - st.wDay, - st.wHour, - st.wMinute, - st.wSecond, - st.wMilliseconds); - return true; - } - -private: -#pragma pack(push, 8) - struct filetime { - unsigned long dwLowDateTime; - unsigned long dwHighDateTime; - }; -#pragma pack(pop) - filetime ft_; -#else - static_assert(false, "timestamp_field is not implemented for this OS") -#endif - - - static void write_digit_pair(char* ptarget, std::size_t i, - unsigned short digits) - { - ptarget[i] = reckless::detail::decimal_digits[2*digits]; - ptarget[i+1] = reckless::detail::decimal_digits[2*digits+1]; - } - - static void format_timestamp(output_buffer* pbuffer, - unsigned short year, unsigned short month, unsigned short day, - unsigned short hour, unsigned short minute, unsigned short second, - unsigned short milliseconds) - { - // YYYY-MM-DD HH:MM:SS.FFF - char* p = pbuffer->reserve(4+1+2+1+2 + 1 + 2+1+2+1+2 + 1 + 3); - - unsigned short century = year / 100; - year -= 100*century; - - write_digit_pair(p, 0, century); - write_digit_pair(p, 2, year); - p[4] = '-'; - write_digit_pair(p, 5, month); - p[7] = '-'; - write_digit_pair(p, 8, day); - p[10] = ' '; - - write_digit_pair(p, 11, hour); - p[13] = ':'; - write_digit_pair(p, 14, minute); - p[16] = ':'; - write_digit_pair(p, 17, second); - p[19] = '.'; - - unsigned short centiseconds = milliseconds/10; - milliseconds -= 10*centiseconds; - write_digit_pair(p, 20, centiseconds); - p[22] = reckless::detail::decimal_digits[2*milliseconds+1]; - pbuffer->commit(23); - } -}; - -class scoped_indent -{ -public: - scoped_indent() - { - ++level_; - } - ~scoped_indent() - { - --level_; - } - - static unsigned level() - { - return level_; - } - -private: - static RECKLESS_TLS unsigned level_; -}; - -class no_indent -{ -public: - no_indent() - { - } - - void apply(output_buffer*) - { - } -}; - -template -class indent -{ -public: - indent() : level_(scoped_indent::level()) - { - } - - void apply(output_buffer* pbuffer) - { - auto n = Multiplier*level_; - char* p = pbuffer->reserve(n); - std::memset(p, Character, n); - pbuffer->commit(n); - } - -private: - unsigned level_; -}; - -template -class policy_formatter { -public: - template - static void format(output_buffer* pbuffer, Fields&&... fields, - IndentPolicy indent, char const* pformat, Args&&... args) - { - format_fields(pbuffer, fields...); - indent.apply(pbuffer); - template_formatter::format(pbuffer, pformat, std::forward(args)...); -#if defined(_WIN32) - auto p = pbuffer->reserve(2); - p[0] = '\r'; - p[1] = '\n'; - pbuffer->commit(2); -#else - auto p = pbuffer->reserve(1); - *p = '\n'; - pbuffer->commit(1); -#endif - } - -private: - template - static void format_fields(output_buffer* pbuffer, Field&& field, Remaining&&... remaining) - { - field.format(pbuffer); - char* p = pbuffer->reserve(1); - *p = Separator; - pbuffer->commit(1); - format_fields(pbuffer, std::forward(remaining)...); - } - static void format_fields(output_buffer*) - { - } -}; - -template -class policy_log : public basic_log { -public: - using basic_log::basic_log; - - template - void write(char const* fmt, Args&&... args) - { - basic_log::write>( - HeaderFields()..., - IndentPolicy(), - fmt, - std::forward(args)...); - } -}; - -} // namespace reckless - -#endif // RECKLESS_POLICY_LOG_HPP diff --git a/external/reckless/reckless/include/reckless/severity_log.hpp b/external/reckless/reckless/include/reckless/severity_log.hpp deleted file mode 100644 index 415b01ff..00000000 --- a/external/reckless/reckless/include/reckless/severity_log.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_SEVERITY_LOG_HPP -#define RECKLESS_SEVERITY_LOG_HPP - -#include - -namespace reckless { -class severity_field { -public: - severity_field(char severity) : severity_(severity) {} - - void format(output_buffer* poutput_buffer) const - { - char* p = poutput_buffer->reserve(1); - *p = severity_; - poutput_buffer->commit(1); - } - -private: - char severity_; -}; - -namespace detail { - template - HeaderField construct_header_field(char) - { - return HeaderField(); - } - - template <> - inline severity_field construct_header_field(char severity) - { - return severity_field(severity); - } -} - -template -class severity_log : public basic_log { -public: - using basic_log::basic_log; - - template - void debug(char const* fmt, Args&&... args) - { - write('D', fmt, std::forward(args)...); - } - template - void info(char const* fmt, Args&&... args) - { - write('I', fmt, std::forward(args)...); - } - template - void warn(char const* fmt, Args&&... args) - { - write('W', fmt, std::forward(args)...); - } - template - void error(char const* fmt, Args&&... args) - { - write('E', fmt, std::forward(args)...); - } - -private: - template - void write(char severity, char const* fmt, Args&&... args) - { - basic_log::write>( - detail::construct_header_field(severity)..., - IndentPolicy(), - fmt, - std::forward(args)...); - } -}; - -} // namespace reckless - -#endif // RECKLESS_SEVERITY_LOG_HPP diff --git a/external/reckless/reckless/include/reckless/stdout_writer.hpp b/external/reckless/reckless/include/reckless/stdout_writer.hpp deleted file mode 100644 index e32c7f25..00000000 --- a/external/reckless/reckless/include/reckless/stdout_writer.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_STDOUT_WRITER_HPP -#define RECKLESS_STDOUT_WRITER_HPP - -#include "detail/fd_writer.hpp" - -namespace reckless { - -#if defined(_WIN32) -namespace detail { - unsigned long const RECKLESS_STD_OUTPUT_HANDLE = static_cast(-11); - unsigned long const RECKLESS_STD_ERROR_HANDLE = static_cast(-12); - extern "C" { - void* __stdcall GetStdHandle(unsigned long nStdHandle); - } -} // namespace detail -#endif - -class stdout_writer : public detail::fd_writer { -public: -#if defined(__unix__) - stdout_writer() : fd_writer(1) { } -#elif defined(_WIN32) - stdout_writer() : fd_writer(detail::GetStdHandle(detail::RECKLESS_STD_OUTPUT_HANDLE)) { } -#endif -}; - -class stderr_writer : public detail::fd_writer { -public: -#if defined(__unix__) - stderr_writer() : fd_writer(2) { } -#elif defined(_WIN32) - stderr_writer() : fd_writer(detail::GetStdHandle(detail::RECKLESS_STD_ERROR_HANDLE)) { } -#endif -}; - -} // namespace reckless - -#endif // RECKLESS_STDOUT_WRITER_HPP - diff --git a/external/reckless/reckless/include/reckless/template_formatter.hpp b/external/reckless/reckless/include/reckless/template_formatter.hpp deleted file mode 100644 index fbe73f93..00000000 --- a/external/reckless/reckless/include/reckless/template_formatter.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_TEMPLATE_FORMATTER_HPP -#define RECKLESS_TEMPLATE_FORMATTER_HPP - -#include // forward -#include -#include // is_convertible - -namespace reckless { - -class output_buffer; -namespace detail { - template - char const* invoke_custom_format(output_buffer* pbuffer, - char const* pformat, T&& v); -} - -class template_formatter { -public: - static void format(output_buffer* pbuffer, char const* pformat); - - template - static void format(output_buffer* pbuffer, char const* pformat, - T&& value, Args&&... args) - { - pformat = next_specifier(pbuffer, pformat); - if(!pformat) - return; - - char const* pnext_format = detail::invoke_custom_format(pbuffer, - pformat, std::forward(value)); - if(pnext_format) - pformat = pnext_format; - else - append_percent(pbuffer); - return template_formatter::format(pbuffer, pformat, - std::forward(args)...); - } - -private: - static void append_percent(output_buffer* pbuffer); - static char const* next_specifier(output_buffer* pbuffer, - char const* pformat); -}; - -char const* format(output_buffer* pbuffer, char const* pformat, char v); -char const* format(output_buffer* pbuffer, char const* pformat, signed char v); -char const* format(output_buffer* pbuffer, char const* pformat, unsigned char v); -char const* format(output_buffer* pbuffer, char const* pformat, wchar_t v); -char const* format(output_buffer* pbuffer, char const* pformat, char16_t v); -char const* format(output_buffer* pbuffer, char const* pformat, char32_t v); - -char const* format(output_buffer* pbuffer, char const* pformat, short v); -char const* format(output_buffer* pbuffer, char const* pformat, unsigned short v); -char const* format(output_buffer* pbuffer, char const* pformat, int v); -char const* format(output_buffer* pbuffer, char const* pformat, unsigned int v); -char const* format(output_buffer* pbuffer, char const* pformat, long v); -char const* format(output_buffer* pbuffer, char const* pformat, unsigned long v); -char const* format(output_buffer* pbuffer, char const* pformat, long long v); -char const* format(output_buffer* pbuffer, char const* pformat, unsigned long long v); - -char const* format(output_buffer* pbuffer, char const* pformat, float v); -char const* format(output_buffer* pbuffer, char const* pformat, double v); -char const* format(output_buffer* pbuffer, char const* pformat, long double v); - -char const* format(output_buffer* pbuffer, char const* pformat, char const* v); -char const* format(output_buffer* pbuffer, char const* pformat, std::string const& v); - -char const* format(output_buffer* pbuffer, char const* pformat, void const* p); - -namespace detail { -// TODO can we invoke free format() using argument-dependent lookup without -// causing infinite recursion on this member function, without this -// intermediary kludge? -template -char const* invoke_custom_format(output_buffer* pbuffer, char const* pformat, T&& v) -{ - typedef decltype(format(pbuffer, pformat, std::forward(v))) return_type; - static_assert(std::is_convertible::value, - "return type of format must be char const*"); - return format(pbuffer, pformat, std::forward(v)); -} -} // namesapce detail - -} // namespace reckless - -#endif // RECKLESS_TEMPLATE_FORMATTER_HPP diff --git a/external/reckless/reckless/include/reckless/writer.hpp b/external/reckless/reckless/include/reckless/writer.hpp deleted file mode 100644 index e85dfe78..00000000 --- a/external/reckless/reckless/include/reckless/writer.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef RECKLESS_WRITER_HPP -#define RECKLESS_WRITER_HPP - -#include // size_t -#include // error_code, error_condition - -// TODO synchronous log for wrapping a channel and calling the formatter immediately. Or, just add a bool to basic_log? - -namespace reckless { - -// TODO this is a bit vague, rename to e.g. log_target or something? -class writer { -public: - enum errc - { - temporary_failure = 1, - permanent_failure = 2 - }; - static std::error_category const& error_category(); - - virtual ~writer() = 0; - virtual std::size_t write(void const* pbuffer, std::size_t count, - std::error_code& ec) noexcept = 0; -}; - -inline std::error_condition make_error_condition(writer::errc ec) -{ - return std::error_condition(ec, writer::error_category()); -} - -inline std::error_code make_error_code(writer::errc ec) -{ - return std::error_code(ec, writer::error_category()); -} -} // namespace reckless - -namespace std -{ - template <> - struct is_error_condition_enum : public true_type {}; -} -#endif // RECKLESS_WRITER_HPP diff --git a/external/reckless/reckless/lib/Tupfile.lua b/external/reckless/reckless/lib/Tupfile.lua deleted file mode 100644 index 522376da..00000000 --- a/external/reckless/reckless/lib/Tupfile.lua +++ /dev/null @@ -1,5 +0,0 @@ -sources = filter_platform_files(tup.glob("../src/*.cpp")) -objects = table.map(sources, function(name) - return '../src/' .. tup.base(name) .. OBJSUFFIX -end) -library('reckless', objects) diff --git a/external/reckless/reckless/lib/_Tupfile b/external/reckless/reckless/lib/_Tupfile deleted file mode 100644 index 854f3c0c..00000000 --- a/external/reckless/reckless/lib/_Tupfile +++ /dev/null @@ -1,2 +0,0 @@ -include_rules -: ../src/*.o |> !ar |> libreckless.a diff --git a/external/reckless/reckless/reckless.vcxproj b/external/reckless/reckless/reckless.vcxproj deleted file mode 100644 index 4cb5d9e3..00000000 --- a/external/reckless/reckless/reckless.vcxproj +++ /dev/null @@ -1,154 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {2E9EC99E-FB59-42F0-B949-6D10A41916E4} - reckless - 10.0.17763.0 - - - - true - v141 - StaticLibrary - - - false - v141 - true - StaticLibrary - - - true - v141 - StaticLibrary - - - false - v141 - true - StaticLibrary - - - - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - true - true - true - true - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/external/reckless/reckless/reckless.vcxproj.filters b/external/reckless/reckless/reckless.vcxproj.filters deleted file mode 100644 index 69b353b3..00000000 --- a/external/reckless/reckless/reckless.vcxproj.filters +++ /dev/null @@ -1,108 +0,0 @@ - - - - - {5ca5ad7b-c8c1-4ab1-b9ee-940ec0918c44} - - - {44ffc4b6-2345-42e3-88e2-04ccd847f89c} - - - {f8e60ad4-e28f-40d4-9172-19b37acada3c} - - - - - src - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless - - - include/reckless\detail - - - include/reckless\detail - - - include/reckless\detail - - - include/reckless\detail - - - include/reckless\detail - - - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - \ No newline at end of file diff --git a/external/reckless/reckless/src/Tupfile.lua b/external/reckless/reckless/src/Tupfile.lua deleted file mode 100644 index a33106e8..00000000 --- a/external/reckless/reckless/src/Tupfile.lua +++ /dev/null @@ -1,7 +0,0 @@ -if tup.getconfig('TRACE_LOG') != '' then - table.insert(OPTIONS.define, 'RECKLESS_ENABLE_TRACE_LOG') -end -table.insert(OPTIONS.includes, '../../performance_log/include') -for i, name in ipairs(filter_platform_files(tup.glob("*.cpp"))) do - compile(name) -end diff --git a/external/reckless/reckless/src/_Tupfile b/external/reckless/reckless/src/_Tupfile deleted file mode 100644 index 4f3e5828..00000000 --- a/external/reckless/reckless/src/_Tupfile +++ /dev/null @@ -1,2 +0,0 @@ -include_rules -: foreach *.cpp |> !cxx |> diff --git a/external/reckless/reckless/src/basic_log.cpp b/external/reckless/reckless/src/basic_log.cpp deleted file mode 100644 index 7435d393..00000000 --- a/external/reckless/reckless/src/basic_log.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include - -#include -#include // max, min -#include // sleep_for -#include // ostringstream -#include // hours - -using reckless::detail::likely; - -namespace reckless { - -namespace { -// Since logger threads do not normally signal any event (unless the queue -// fills up), we have to poll the input buffer. We use an exponential back off -// to not use too many CPU cycles and allow other threads to run, but we don't -// wait for more than one second. -unsigned max_input_buffer_poll_period_ms = 1000u; -unsigned input_buffer_poll_period_inverse_growth_factor = 4; - -#ifdef RECKLESS_ENABLE_TRACE_LOG -struct output_worker_start_event : - public detail::timestamped_trace_event -{ - std::string format() const - { - return timestamped_trace_event::format() + " output_worker_start"; - } -}; - -struct wait_for_input_start_event : - public detail::timestamped_trace_event -{ - std::string format() const - { - return timestamped_trace_event::format() + " wait_for_input start"; - } -}; - -struct wait_for_input_finish_event : - public detail::timestamped_trace_event -{ - std::string format() const - { - return timestamped_trace_event::format() + " wait_for_input finish"; - } -}; - -struct input_buffer_full_wait_start_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() + - " input_buffer_full_wait start"; - } -}; - -struct input_buffer_full_wait_finish_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() - + " input_buffer_full_wait finish"; - } -}; - -struct process_batch_start_event : public detail::timestamped_trace_event { - std::size_t batch_size; - - process_batch_start_event(std::size_t size) : - batch_size(size) - { - } - - std::string format() const - { - std::ostringstream ostr; - ostr << timestamped_trace_event::format() << " process_batch start " << - batch_size; - return ostr.str(); - } -}; - -struct process_batch_finish_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() - + " process_batch finish"; - } -}; - -struct process_frame_start_event : public detail::timestamped_trace_event { - std::string format() const - { - std::ostringstream ostr; - ostr << timestamped_trace_event::format() << " process_frame start "; - return ostr.str(); - } -}; - -struct process_frame_finish_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() - + " process_frame finish"; - } -}; - - -struct format_frame_start_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() + - " format_frame start"; - } -}; - -struct format_frame_finish_event : public detail::timestamped_trace_event { - std::string format() const - { - return timestamped_trace_event::format() - + " format_frame finish"; - } -}; - -#endif // RECKLESS_ENABLE_TRACE_LOG - -} // anonymous namespace - -char const* writer_error::what() const noexcept -{ - return "writer error"; -} - -basic_log::basic_log() -{ -} - -basic_log::~basic_log() -{ - if(is_open()) { - std::error_code error; - close(error); - } -} - -void basic_log::open(writer* pwriter) -{ - open(pwriter, 0, 0); -} - -void basic_log::open(writer* pwriter, - std::size_t input_buffer_capacity, - std::size_t output_buffer_capacity) -{ - assert(!is_open()); - - // We used to use the page size for input buffer capacity. - // However, after introducing the new ring buffer for Windows - // support, it is impossible to have a size less than 64 KiB on - // Windows due to granularity constraints. Additionally, since we - // are storing the entire input frame in this buffer and have - // stricter alignment requests, we need a larger buffer to fit a - // reasonable amount of frames. So, for better performance and - // more similar behavior between Unix/Windows, we set this to 64 - // KiB. - // - // The downside to this fairly big size is that during load the - // latency of log calls may become erratic, as a large queue means - // there will be a long wait for it to flush if it happens to fill - // up. - if(input_buffer_capacity == 0) - input_buffer_capacity = 64*1024; - - // We always write the contents of the output buffer whenever we - // have depleted the input queue, but if it fills up before that - // happens then we have to flush all finished frames to disk. - // Ideally this should not happen, as benchmarks have shown that - // it reduces performance (perhaps due to the memmove() call - // taking some time or maybe just because of additional - // transitions to the kernel). On the other hand, if the caller - // manages to maintain a balance between input rate and the rate - // at which log lines are formatted, so that the input queue never - // depletes and always has more content, then the output buffer - // needs to have a maximum size or it will eventually eat all - // available memory. - // - // The buffer currently has a fixed size and we set it based on an - // assumption on how much space will be used if the entire input - // queue is full of log entries. We assume an input frame will use - // up one cache line and that a log line will be 80 bytes in size. - if(output_buffer_capacity == 0) { - auto assumed_count = (input_buffer_capacity+RECKLESS_CACHE_LINE_SIZE-1)/ - RECKLESS_CACHE_LINE_SIZE; - output_buffer_capacity = assumed_count * 80; - } - - input_buffer_.reserve(input_buffer_capacity); - output_buffer::reset(pwriter, output_buffer_capacity); - output_thread_ = std::thread(std::mem_fn(&basic_log::output_worker), this); -} - -void basic_log::close(std::error_code& ec) noexcept -{ - using namespace detail; - assert(is_open()); - - frame_header* pframe = push_input_frame_blind(RECKLESS_CACHE_LINE_SIZE); - atomic_store_relaxed(&pframe->status, frame_status::shutdown_marker); - input_buffer_full_event_.signal(); - - // We're going to assume that join() will not throw here, since all the - // documented error conditions would be the result of a bug. - output_thread_.join(); - assert(input_buffer_.size() == 0); - - output_buffer::reset(); - input_buffer_.reserve(0); - - if(atomic_load_acquire(&error_flag_)) - ec = error_code_; - else - ec.clear(); - - assert(!is_open()); -} - -void basic_log::close() -{ - std::error_code error; - close(error); - if(error) - throw writer_error(error); -} - -void basic_log::flush(std::error_code& ec) -{ - struct formatter { - static void format(output_buffer* poutput, detail::spsc_event* pevent, - std::error_code* perror) - { - // Need downcast to get access to protected members in - // output_buffer. - auto const plog = static_cast(poutput); - try { - if(plog->has_complete_frame()) - plog->output_buffer::flush(); - perror->clear(); - } catch(flush_error const& e) { - *perror = e.code(); - } - pevent->signal(); - } - }; - detail::spsc_event event; - write(&event, &ec); - input_buffer_full_event_.signal(); - event.wait(); -} - -void basic_log::flush() -{ - std::error_code error; - basic_log::flush(error); - if(error) - throw writer_error(error); -} - -void basic_log::start_panic_flush() -{ - using namespace detail; - frame_header* pframe = push_input_frame_blind(RECKLESS_CACHE_LINE_SIZE); - // To reduce interference from other running threads that write to the log - // during a panic flush, we set panic_flush_ = true. This stops the - // background thread from releasing consumed memory to the buffer. Then we - // call input_buffer_.deplete(), which will eat up all available memory - // from the buffer, effectively preventing all other threads from pushing - // more data to the buffer. Instead, they'll sit nicely and block until - // the flush is done. - - // I'm uncertain about the theoretical correctness of this, but at least - // I'm pretty sure it will work on x86/x64, since Intel has a strongly- - // ordered cache coherency model. The thing we don't want to happen is that - // the store to panic_flush_ (below) becomes visible to the background - // thread before the input frame is allocated (above). Since panic_flush_ - // signals to the background thread that it should no longer free up space - // in the buffer, that could mean that if the buffer is full then the - // allocation above will block indefinitely. - // - // It seems to me that what I need is a memory ordering that is the opposite - // of memory_order_release: store operations *below* the barrier may not be - // moved above it. - // - // Anyway, until we run into problems in practice, I think it will be - // sufficient to tell the *compiler* that it may not move anything - // above this barrier. - std::atomic_signal_fence(std::memory_order_seq_cst); - atomic_store_relaxed(&panic_flush_, true); - input_buffer_.deplete(); - - atomic_store_release(&pframe->status, frame_status::panic_shutdown_marker); - input_buffer_full_event_.signal(); -} - -void basic_log::await_panic_flush() -{ - panic_flush_done_event_.wait(); -} - -bool basic_log::await_panic_flush(unsigned int milliseconds) -{ - return panic_flush_done_event_.wait(milliseconds); -} - -detail::frame_header* basic_log::push_input_frame_slow_path( - detail::frame_header* pframe, bool error, std::size_t size) -{ - using namespace detail; - while(true) { - auto notify_count = input_buffer_empty_event_.notify_count(); - pframe = static_cast(input_buffer_.push(size)); - error = atomic_load_acquire(&error_flag_); - if (pframe != nullptr || error) - break; - - atomic_increment_fetch_relaxed(&input_buffer_full_count_); - input_buffer_full_event_.signal(); - RECKLESS_TRACE(input_buffer_full_wait_start_event); - input_buffer_empty_event_.wait(notify_count); - RECKLESS_TRACE(input_buffer_full_wait_finish_event); - } - if(!error) { - return pframe; - } else { - if(pframe) { - pframe->frame_size = size; - atomic_store_release(&pframe->status, frame_status::failed_error_check); - } - throw writer_error(error_code_); - } -} - -void basic_log::output_worker() -{ - using namespace detail; - RECKLESS_TRACE(output_worker_start_event); - - // This code is compiled into a static library, whereas write() is inlined - // and compiled in the client application's environment. - // We don't know if the client application defines RECKLESS_DEBUG or not, - // so we have to assume it does and make sure to set the worker-thread ID, - // lest write() will misbehave. -#if defined(__unix__) - output_worker_native_handle_ = pthread_self(); -#elif defined(_WIN32) - output_worker_native_id_ = GetCurrentThreadId(); -#endif - - set_thread_name("reckless output worker"); - - frame_status status = frame_status::uninitialized; - while(likely(status < frame_status::shutdown_marker)) { - auto batch_size = wait_for_input(); - - atomic_store_relaxed(&input_buffer_high_watermark_, - std::max(input_buffer_high_watermark_, batch_size)); - RECKLESS_TRACE(process_batch_start_event, batch_size); - - auto batch_start = input_buffer_.read_position(); - auto batch_end = batch_start + batch_size; - auto read_position = batch_start; - - bool panic_flush = false; - do - { - while(read_position != batch_end && - likely(status < frame_status::shutdown_marker)) - { - void* pframe = input_buffer_.address(read_position); - status = acquire_frame(pframe); - - std::size_t frame_size; - if(likely(status == frame_status::initialized)) - frame_size = process_frame(pframe); - else if(status == frame_status::failed_error_check) - frame_size = static_cast(pframe)->frame_size; - else if(status == frame_status::failed_initialization) - frame_size = skip_frame(pframe); - else if(status == frame_status::shutdown_marker) - frame_size = RECKLESS_CACHE_LINE_SIZE; - else { - assert(status == frame_status::panic_shutdown_marker); - // We are in panic-flush mode and reached the shutdown marker. That - // means we are done. - on_panic_flush_done(); // never returns - } - - clear_frame(pframe, frame_size); - read_position += frame_size; - } - assert(read_position == batch_end); - - // Return memory to the input buffer to be used by other threads, - // but only if we are not in a panic-flush state. - // See start_panic_flush() for more information. - panic_flush = atomic_load_relaxed(&panic_flush_); - if(likely(!panic_flush)) { - input_buffer_.pop_release(batch_size); - } else { - // As a consequence of not returning memory to the input buffer, - // on the next batch iteration input_buffer_.front() is going - // to return exactly the same position that we already - // processed, meaning we will hang waiting for it to become - // initialized. So instead of continuing normally we just update - // the batch end to reflect the current size of the buffer - // (which is going to end up equal to the full capacity of the - // buffer), let pframe remain at its current position, and loop - // around until we reach the panic_shutdown_marker frame. - batch_size = input_buffer_.size(); - batch_end = batch_start + batch_size; - } - } while(unlikely(panic_flush)); - RECKLESS_TRACE(process_batch_finish_event); - } - - if(output_buffer::has_complete_frame()) { - // Can't do much here if there is a flush error here since we are - // shutting down. The error code will be checked by close() when - // the worker thread finishes. - // FIXME if the error policy is e.g. block then we may never leave here - // and if it's ignore then the error won't be seen. Can we change the - // policy during exit? - flush_output_buffer(); - } -} - -std::size_t basic_log::wait_for_input() -{ - auto size = input_buffer_.size(); - if(likely(size != 0)) { - // It's not exactly *likely* that there is input in the buffer, but we - // want this to be a "hot path" so that we perform our best when there - // is a lot of load. - return size; - } - - RECKLESS_TRACE(wait_for_input_start_event); - // Poll the input buffer until something comes in. - unsigned wait_time_ms = 0; - while(true) { - input_buffer_empty_event_.notify_all(); - - // The output buffer is flushed at least once before waiting for more - // input. This makes sure that data gets sent to the writer immediately - // whenever there's a pause in incoming log messages. If this flush - // fails due to a temporary error then there may still be data - // lingering, so we need to keep trying to flush with each iteration as - // long as data remains in the output buffer. - if(output_buffer::has_complete_frame()) { - flush_output_buffer(); - // The flush acts as a wait, so check the input buffer - // again before waiting on the event. - size = input_buffer_.size(); - if(size != 0) - break; - } - - input_buffer_full_event_.wait(wait_time_ms); - size = input_buffer_.size(); - if(size != 0) - break; - - wait_time_ms += std::max(1u, - wait_time_ms/input_buffer_poll_period_inverse_growth_factor); - wait_time_ms = std::min(wait_time_ms, max_input_buffer_poll_period_ms); - } - RECKLESS_TRACE(wait_for_input_finish_event); - return size; -} - -detail::frame_status basic_log::acquire_frame(void* pframe) -{ - using namespace detail; - auto pheader = static_cast(pframe); - auto status = atomic_load_acquire(&pheader->status); - if(likely(status != frame_status::uninitialized)) - return status; - - // Poll the frame status until it is no longer uninitialized. We don't need - // to be as sophisticated as in wait_for_input and try to flush the output - // buffer etc, since we know another thread is just in the process of - // putting data in the frame. If we have to wait more than a millisecond - // here then something has probably gone very wrong. - unsigned wait_time_ms = 0; - while(true) { - input_buffer_full_event_.wait(wait_time_ms); - status = atomic_load_acquire(&pheader->status); - if(status != frame_status::uninitialized) - return status; - - wait_time_ms += std::max(1u, - wait_time_ms/input_buffer_poll_period_inverse_growth_factor); - wait_time_ms = std::min(wait_time_ms, max_input_buffer_poll_period_ms); - } -} - -std::size_t basic_log::process_frame(void* pframe) -{ - //RECKLESS_TRACE(process_frame_start_event); - using namespace detail; - auto pheader = static_cast(pframe); - auto pdispatch = pheader->pdispatch_function; - - std::size_t frame_size; - try { - frame_size = (*pdispatch)(invoke_formatter, - static_cast(this), pframe); - output_buffer::frame_end(); - } catch(flush_error const&) { - // A flush error occurs here if there was not enough space in - // the output buffer and it had to be flushed to make room, but - // the flush failed. This means that we lose the frame. - output_buffer::lost_frame(); - std::type_info const* pti; - frame_size = (*pdispatch)(get_typeid, &pti, nullptr); - } catch(...) { - output_buffer::revert_frame(); - std::type_info const* pti; - frame_size = (*pdispatch)(get_typeid, &pti, nullptr); - std::lock_guard lk(callback_mutex_); - if(format_error_callback_) { - try { - format_error_callback_(this, std::current_exception(), - *pti); - } catch(...) { - } - } - } - - //RECKLESS_TRACE(process_frame_finish_event); - return frame_size; -} - -std::size_t basic_log::skip_frame(void* pframe) -{ - using namespace detail; - auto pdispatch = static_cast(pframe)->pdispatch_function; - std::type_info const* pti; - return (*pdispatch)(get_typeid, &pti, nullptr); -} - -void basic_log::clear_frame(void* pframe, std::size_t frame_size) -{ - using namespace detail; - auto pcframe = static_cast(pframe); - for(std::size_t offset=0; offset!=frame_size; - offset += RECKLESS_CACHE_LINE_SIZE) - { - auto pheader = char_cast(pcframe + offset); - atomic_store_relaxed(&pheader->status, frame_status::uninitialized); - } -} - -void basic_log::flush_output_buffer() -{ - try { - output_buffer::flush(); - } catch(flush_error const&) { - // flush_error is only thrown to unwind the stack from - // output_buffer::reserve()/flush() so that the current - // formatting operation can be aborted. No need to do anything - // at this point. The only time we care about flush_error is - // when it happens during formatting of an input frame, - // because then we need to account for a lost frame. - } -} - -void basic_log::on_panic_flush_done() -{ - if(output_buffer::has_complete_frame()) { - // We get one chance to flush what remains in the output buffer. If it - // fails now then we'll just have to live with that and crash. - try { - output_buffer::flush(); - } catch(...) { - }; - } - - panic_flush_done_event_.signal(); - // Sleep and wait for death. - while(true) - std::this_thread::sleep_for(std::chrono::hours(1)); -} - -} // namespace reckless diff --git a/external/reckless/reckless/src/crash_handler_unix.cpp b/external/reckless/reckless/src/crash_handler_unix.cpp deleted file mode 100644 index edebbf52..00000000 --- a/external/reckless/reckless/src/crash_handler_unix.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include - -#include // memset -#include -#include // sort, unique -#include -#include -#include -#include // sigaction - -namespace reckless { -namespace { -// We set handlers for all signals that have a default behavior to -// terminate the process or generate a core dump (and that have not had -// their handler set to something else already). -std::initializer_list const signals = { - // POSIX.1-1990 signals - SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, - SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, - // SUSv2 + POSIX.1-2001 signals - SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ, - // Various other signals - SIGIOT, SIGSTKFLT, SIGIO, SIGPWR, - }; - -std::vector g_logs; -std::vector> g_old_sigactions; - -void signal_handler(int) -{ - for(basic_log* plog : g_logs) - plog->start_panic_flush(); - for(basic_log* plog : g_logs) - plog->await_panic_flush(); -} -} // anonymous namespace - -void install_crash_handler(std::initializer_list logs) -{ - assert(logs.size() != 0); - assert(g_logs.empty()); - g_logs.assign(begin(logs), end(logs)); - - struct sigaction act; - std::memset(&act, 0, sizeof(act)); - act.sa_handler = &signal_handler; - sigfillset(&act.sa_mask); - act.sa_flags = SA_RESETHAND; - // Some signals are synonyms for each other. Some are explictly specified - // as such, but others may just be implemented that way on specific - // systems. So we'll remove duplicate entries here before we loop through - // all the signal numbers. - std::vector unique_signals(signals); - sort(begin(unique_signals), end(unique_signals)); - unique_signals.erase(unique(begin(unique_signals), end(unique_signals)), - end(unique_signals)); - try { - g_old_sigactions.reserve(unique_signals.size()); - for(auto signal : unique_signals) { - struct sigaction oldact; - if(0 != sigaction(signal, nullptr, &oldact)) - throw std::system_error(errno, std::system_category()); - if(oldact.sa_handler == SIG_DFL) { - if(0 != sigaction(signal, &act, nullptr)) - { - if(errno == EINVAL) { - // If we get EINVAL then we assume that the kernel - // does not know about this particular signal number. - continue; - } - throw std::system_error(errno, std::system_category()); - } - g_old_sigactions.push_back({signal, oldact}); - } - } - } catch(...) { - uninstall_crash_handler(); - throw; - } -} - -void uninstall_crash_handler() -{ - assert(!g_logs.empty()); - while(!g_old_sigactions.empty()) { - auto const& p = g_old_sigactions.back(); - auto signal = p.first; - auto const& oldact = p.second; - if(0 != sigaction(signal, &oldact, nullptr)) - throw std::system_error(errno, std::system_category()); - g_old_sigactions.pop_back(); - } - g_logs.clear(); -} - -} // namespace reckless diff --git a/external/reckless/reckless/src/crash_handler_win32.cpp b/external/reckless/reckless/src/crash_handler_win32.cpp deleted file mode 100644 index 6f89a909..00000000 --- a/external/reckless/reckless/src/crash_handler_win32.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include -#include - -#include -#include - -namespace reckless { - -namespace { -std::vector g_logs; -LPTOP_LEVEL_EXCEPTION_FILTER g_old_exception_filter = nullptr; - -LONG WINAPI exception_filter(_EXCEPTION_POINTERS *ExceptionInfo) -{ - for(basic_log* plog : g_logs) - plog->start_panic_flush(); - for(basic_log* plog : g_logs) - plog->await_panic_flush(); - if (g_old_exception_filter == nullptr) - return EXCEPTION_CONTINUE_SEARCH; - else - return g_old_exception_filter(ExceptionInfo); -} - -} // anonymous namespace - -void install_crash_handler(std::initializer_list logs) -{ - assert(logs.size() != 0); - assert(g_logs.empty()); - g_logs.assign(begin(logs), end(logs)); - - g_old_exception_filter = SetUnhandledExceptionFilter(&exception_filter); -} - -void uninstall_crash_handler() -{ - assert(!g_logs.empty()); - SetUnhandledExceptionFilter(g_old_exception_filter); - g_old_exception_filter = nullptr; - g_logs.clear(); -} - -} // namespace reckless diff --git a/external/reckless/reckless/src/fd_writer.cpp b/external/reckless/reckless/src/fd_writer.cpp deleted file mode 100644 index 82e73849..00000000 --- a/external/reckless/reckless/src/fd_writer.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#if defined(__unix__) -#include // errno, EINTR -#include // write - -#elif defined(_WIN32) -#define NOMINMAX -#include -#include -#endif - -namespace { - class error_category : public std::error_category { - public: - char const* name() const noexcept override - { - return "reckless::fd_writer"; - } - std::error_condition default_error_condition(int code) const noexcept override - { - return std::system_category().default_error_condition(code); - } - bool equivalent(int code, std::error_condition const& condition) const noexcept override - { - if(condition.category() == reckless::writer::error_category()) - return file_writer_to_writer_category(code) == condition.value(); - else - return std::system_category().equivalent(code, condition); - } - // This overload is called if we have an error_condition that belongs to - // this error_category, and compare it to an error_code belonging to - // another error_category. Since the error_category is in the anonymous - // namespace, the only way that would happen is if you convert an - // error_code that comes from here into an error_condition, then try to - // compare it to an error_code from another category. It's a - // pathological situation, but the correct way to implement it should be - // to map the condition value from this category to - // reckless::writer::error_category() if the error_code is in that - // category, then do the comparison. It essentially reverses the - // error_code / error_condition relationship. - bool equivalent(std::error_code const& code, int condition) const noexcept override - { - if(code.category() == reckless::writer::error_category()) - return file_writer_to_writer_category(condition) == code.value(); - else - return std::system_category().equivalent(code, condition); - } - std::string message(int condition) const override - { - return std::system_category().message(condition); - } - private: - int file_writer_to_writer_category(int code) const - { -#if defined(__unix__) - switch(code) { - case ENOSPC: - case ENOBUFS: - case EDQUOT: - case EIO: - return reckless::writer::temporary_failure; - default: - return reckless::writer::permanent_failure; - } -#elif defined(_WIN32) - switch(code) { - case ERROR_BUSY: - case ERROR_DISK_FULL: - case ERROR_HANDLE_DISK_FULL: - case ERROR_LOCK_VIOLATION: - case ERROR_NOT_ENOUGH_MEMORY: - case ERROR_NOT_ENOUGH_QUOTA: - case ERROR_NOT_READY: - case ERROR_OPERATION_ABORTED: - case ERROR_OUTOFMEMORY: - case ERROR_READ_FAULT: - case ERROR_RETRY: - case ERROR_SHARING_VIOLATION: - case ERROR_WRITE_FAULT: - case ERROR_WRITE_PROTECT: - return reckless::writer::temporary_failure; - default: - return reckless::writer::permanent_failure; - } -#endif - } - }; - - error_category const& get_error_category() - { - static error_category cat; - return cat; - } - -} - -namespace reckless { -namespace detail { - -#if defined(__unix__) -std::size_t fd_writer::write(void const* pbuffer, std::size_t count, std::error_code& ec) noexcept -{ - char const* p = static_cast(pbuffer); - char const* pend = p + count; - ec.clear(); - while(p != pend) { - ssize_t written = ::write(fd_, p, count); - if(written == -1) { - if(errno != EINTR) { - ec.assign(errno, get_error_category()); - break; - } - } else { - p += written; - } - } - return p - static_cast(pbuffer); -} - -#elif defined(_WIN32) -std::size_t fd_writer::write(void const* pbuffer, std::size_t count, std::error_code& ec) noexcept -{ - DWORD written; - assert(count < std::numeric_limits::max()); - if(WriteFile(handle_, pbuffer, static_cast(count), &written, NULL)) { - assert(written == count); - ec.clear(); - return count; - } else { - int err = GetLastError(); - ec.assign(err, get_error_category()); - return written; - } -} - -#endif - -} // namespace detail -} // namespace reckless diff --git a/external/reckless/reckless/src/file_writer.cpp b/external/reckless/reckless/src/file_writer.cpp deleted file mode 100644 index 036fe9f5..00000000 --- a/external/reckless/reckless/src/file_writer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "reckless/file_writer.hpp" - -#include -#include -#include // numeric_limits - -#if defined(__unix__) -#include // open -#include // open, lseek -#include // open -#include // errno -#include // lseek, close - -#elif defined(_WIN32) - -#define NOMINMAX -#include - -#endif - -#if defined(__unix__) -namespace { -int open_file(char const* path) -{ - auto full_access = - S_IRUSR | S_IWUSR | - S_IRGRP | S_IWGRP | - S_IROTH | S_IWOTH; - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, full_access); - if(fd == -1) - throw std::system_error(errno, std::system_category()); - return fd; -} - -} - -reckless::file_writer::file_writer(char const* path) : - fd_writer(open_file(path)) -{ -} - -reckless::file_writer::~file_writer() -{ - if(fd_ != -1) { - while(-1 == close(fd_)) { - if(errno != EINTR) - break; - } - } -} - -#elif defined(_WIN32) -namespace { - template - HANDLE createfile_generic(F CreateFileX, T const* path) - { - // From NtCreateFile documentation: - // (https://msdn.microsoft.com/en-us/library/bb432380.aspx) - // If only the FILE_APPEND_DATA and SYNCHRONIZE flags are set, the caller - // can write only to the end of the file, and any offset information on - // writes to the file is ignored. However, the file is automatically - // extended as necessary for this type of write operation. - // TODO what happens if the user deletes the file while we are writing? - HANDLE h = CreateFileX(path, - FILE_APPEND_DATA | SYNCHRONIZE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(h == INVALID_HANDLE_VALUE) - throw std::system_error(GetLastError(), std::system_category()); - return h; - } -} -reckless::file_writer::file_writer(char const* path) : - fd_writer(createfile_generic(CreateFileA, path)) -{ -} - -reckless::file_writer::file_writer(wchar_t const* path) : - fd_writer(createfile_generic(CreateFileW, path)) -{ -} - -reckless::file_writer::~file_writer() -{ - CloseHandle(handle_); -} - -#endif diff --git a/external/reckless/reckless/src/lockless_cv.cpp b/external/reckless/reckless/src/lockless_cv.cpp deleted file mode 100644 index 606152a3..00000000 --- a/external/reckless/reckless/src/lockless_cv.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#if defined(__linux__) - -#include -#include -#include -#include -#include - -namespace reckless { -namespace detail { - -namespace { - -int sys_futex(void *addr1, int op, int val1, struct timespec const *timeout, - void *addr2, int val3) -{ - return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); -} - -} // anonymous namespace - -void lockless_cv::notify_all() -{ - atomic_fetch_add_release(¬ify_count_, 1); - sys_futex(¬ify_count_, FUTEX_WAKE_PRIVATE, 0x7fffffff, - nullptr, nullptr, 0); -} - -void lockless_cv::wait(unsigned expected_notify_count) -{ - sys_futex(¬ify_count_, FUTEX_WAIT_PRIVATE, expected_notify_count, - nullptr, 0, 0); -} - -void lockless_cv::wait(unsigned expected_notify_count, unsigned milliseconds) -{ - struct timespec timeout = {0, 0}; - timeout.tv_sec = milliseconds/1000; - timeout.tv_nsec = static_cast(milliseconds%1000)*1000000; - - sys_futex(¬ify_count_, FUTEX_WAIT_PRIVATE, - expected_notify_count, &timeout, 0, 0); -} - -} // namespace detail -} // namespace reckless - -#elif defined(_WIN32) - -#include - -namespace reckless { -namespace detail { - -void lockless_cv::notify_all() -{ - atomic_fetch_add_release(¬ify_count_, 1); - WakeByAddressAll(¬ify_count_); -} - -void lockless_cv::wait(unsigned expected_notify_count) -{ - WaitOnAddress(¬ify_count_, &expected_notify_count, sizeof(unsigned), INFINITE); -} - -void lockless_cv::wait(unsigned expected_notify_count, unsigned milliseconds) -{ - WaitOnAddress(¬ify_count_, &expected_notify_count, sizeof(unsigned), milliseconds); -} - -} // namespace detail -} // namespace reckless - -#else - static_assert(false, "lockless_cv is not implemented for this platform") -#endif diff --git a/external/reckless/reckless/src/mpsc_ring_buffer.cpp b/external/reckless/reckless/src/mpsc_ring_buffer.cpp deleted file mode 100644 index de96d4c5..00000000 --- a/external/reckless/reckless/src/mpsc_ring_buffer.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include // bad_alloc - -#if defined(__linux__) -#include // mmap -#include // shmget/shmat -#include // shmget/shmat -#include // S_IRUSR/S_IWUSR -#include // get_page_size - -#include -#elif defined(_WIN32) -#include -#endif - -namespace { - template - T round_nearest_power_of_2(T v, typename std::enable_if::type* = nullptr) - { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; - } - - template - T round_nearest_power_of_2(T v, typename std::enable_if::type* = nullptr) - { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v |= v >> 32; - v++; - return v; - } - - std::size_t round_capacity(std::size_t capacity) - { -#if defined(__linux__) - std::size_t const granularity = reckless::detail::get_page_size(); -#elif defined(_WIN32) - std::size_t const granularity = 64*1024; -#endif - auto n_segments = (capacity + granularity - 1)/granularity; - n_segments = round_nearest_power_of_2(n_segments); - return n_segments*granularity; - } -} // anonymous namespace - -namespace reckless { -namespace detail { - -void mpsc_ring_buffer::init(std::size_t capacity) -{ - if(capacity == 0) { - rewind(); - pbuffer_start_ = nullptr; - capacity_ = 0; - return; - } - - capacity = round_capacity(capacity); - -#if defined(__linux__) - int shm = shmget(IPC_PRIVATE, capacity, IPC_CREAT | S_IRUSR | S_IWUSR); - if(shm == -1) - throw std::bad_alloc(); - - void* pbase = nullptr; - while(!pbase) { - pbase = mmap(nullptr, 2*capacity, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if(MAP_FAILED == pbase) - throw std::bad_alloc(); - munmap(pbase, 2*capacity); - pbase = shmat(shm, pbase, 0); - if(pbase) { - if((void*)-1 == shmat(shm, static_cast(pbase) + capacity, 0)) - { - shmdt(pbase); - pbase = nullptr; - } - } - } - shmctl(shm, IPC_RMID, nullptr); - -#elif defined(_WIN32) - HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, - PAGE_READWRITE, static_cast((capacity >> 31)>>1), - static_cast(capacity & 0xffffffff), nullptr); - if(mapping == NULL) - throw std::bad_alloc(); - - void* pbase = nullptr; - while(!pbase) { - pbase = VirtualAlloc(0, 2*capacity, MEM_RESERVE, PAGE_NOACCESS); - if (!pbase) { - CloseHandle(mapping); - throw std::bad_alloc(); - } - VirtualFree(pbase, 0, MEM_RELEASE); - pbase = MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, capacity, - pbase); - if(pbase) { - if(!MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, capacity, - static_cast(pbase) + capacity)) - { - UnmapViewOfFile(pbase); - pbase = nullptr; - } - } - } - CloseHandle(mapping); -#endif - - rewind(); - std::memset(pbase, 0, capacity); - pbuffer_start_ = static_cast(pbase); - capacity_ = capacity; -} - -void mpsc_ring_buffer::destroy() -{ - if(!pbuffer_start_) - return; -#if defined(__linux__) - shmdt(pbuffer_start_ + capacity_); - shmdt(pbuffer_start_); - -#elif defined(_WIN32) - UnmapViewOfFile(pbuffer_start_ + capacity_); - UnmapViewOfFile(pbuffer_start_); -#endif -} - -} // namespace detail -} // namespace reckless diff --git a/external/reckless/reckless/src/ntoa.cpp b/external/reckless/reckless/src/ntoa.cpp deleted file mode 100644 index 6ce8c9b0..00000000 --- a/external/reckless/reckless/src/ntoa.cpp +++ /dev/null @@ -1,1997 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include - -#include // max, min -#include // is_unsigned -#include -#include // memset -#include // lrint, llrint, frexp - -namespace reckless { -namespace detail { - -char const decimal_digits[201] = - "00010203040506070809" - "10111213141516171819" - "20212223242526272829" - "30313233343536373839" - "40414243444546474849" - "50515253545556575859" - "60616263646566676869" - "70717273747576777879" - "80818283848586878889" - "90919293949596979899"; - -std::uint64_t const power_lut[19] = { - 1, // 0 - 10, // 1 - 100, // 2 - 1000, // 3 - 10000, // 4 - 100000, // 5 - 1000000, // 6 - 10000000, // 7 - 100000000, // 8 - 1000000000, // 9 - 10000000000, // 10 - 100000000000, // 11 - 1000000000000, // 12 - 10000000000000, // 13 - 100000000000000, // 14 - 1000000000000000, // 15 - 10000000000000000, // 16 - 100000000000000000, // 17 - 1000000000000000000 // 18 -}; -} // namespace detail - -namespace { - -template -typename std::make_unsigned::type unsigned_cast(T v) -{ - return static_cast::type>(v); -} - -//void prefetch_digits() -//{ -// detail::prefetch(decimal_digits, sizeof(decimal_digits)); -//} -//void prefetch_power_lut() -//{ -// detail::prefetch(power_lut, sizeof(power_lut)); -//} - -template -typename std::enable_if::value, unsigned>::type -utoa_generic_base10_preallocated(char* str, unsigned pos, Unsigned value) -{ - using detail::decimal_digits; - - if(value == 0) - return pos; - // FIXME is this right? What if value /= 100 yields 0 here? Do we get an - // additional unnecessary 0? - while(value >= 100) { - Unsigned remainder = value % 100; - value /= 100; - std::size_t offset = 2*static_cast(remainder); - char d1 = decimal_digits[offset]; - char d2 = decimal_digits[offset+1]; - str[pos-1] = d2; - str[pos-2] = d1; - pos -= 2; - } - if(value < 10) { - --pos; - str[pos] = '0' + static_cast(unsigned_cast(value)); - } else { - std::size_t offset = static_cast(2*value); - char d1 = decimal_digits[offset]; - char d2 = decimal_digits[offset+1]; - pos -= 2; - str[pos+1] = d2; - str[pos] = d1; - } - return pos; -} - -template -typename std::enable_if::value, Unsigned>::type -utoa_generic_base10_preallocated(char* str, unsigned pos, Unsigned value, unsigned digits) -{ - using detail::decimal_digits; - - while(digits >= 2) { - Unsigned remainder = value % 100; - value /= 100; - std::size_t offset = 2*static_cast(remainder); - char d1 = decimal_digits[offset]; - char d2 = decimal_digits[offset+1]; - str[pos-1] = d2; - str[pos-2] = d1; - pos -= 2; - digits -= 2; - } - if(digits == 1) { - --pos; - str[pos] = '0' + static_cast(unsigned_cast(value % 10)); - value /= 10; - } - return value; -} - -template -typename std::enable_if::value, unsigned>::type -utoa_generic_base16_preallocated(char* str, unsigned pos, Unsigned value) -{ - char const digit_offset = '0'; - char const hexdigit_offset = (Uppercase? 'A' : 'a') - 10; - while(value) { - --pos; - char v = value % 0x10; - value /= 0x10; - str[pos] = v + (v<10? digit_offset : hexdigit_offset); - } - return pos; -} - -// For special case v=0, this returns 0. -template -typename std::enable_if::value, unsigned>::type -log2(T v) -{ - // From Sean Eron Anderson's "Bit Twiddling Hacks" collection. - unsigned r; - unsigned shift; - - r = (v > 0xFFFF) << 4; v >>= r; - shift = (v > 0xFF ) << 3; v >>= shift; r |= shift; - shift = (v > 0xF ) << 2; v >>= shift; r |= shift; - shift = (v > 0x3 ) << 1; v >>= shift; r |= shift; - r |= (v >> 1); - return r; -} - -// For special case v=0, this returns 0. -template -typename std::enable_if::value, unsigned>::type -log2(T v) -{ - // log2 for 32-bit but extended for 64 bits. - unsigned r; - unsigned shift; - - r = (v > 0xFFFFFFFF) << 5; v >>= r; - shift = (v > 0xFFFF) << 4; v >>= shift; r |= shift; - shift = (v > 0xFF ) << 3; v >>= shift; r |= shift; - shift = (v > 0xF ) << 2; v >>= shift; r |= shift; - shift = (v > 0x3 ) << 1; v >>= shift; r |= shift; - r |= (v >> 1); - return r; -} - -// For special case x=0, this returns 0. -template -typename std::enable_if::value && sizeof(Uint32) == 4, unsigned>::type -log10(Uint32 x) -{ - // Compute logarithm by "binary search" based on magnitude/number of digits - // in number. If we partition evenly (i.e. on 5) we get: - // 0123456789 - // 01234 56789 - // 01 234 56 789 - // 0 1 2 34 5 6 7 89 - // 3 4 8 9 - // - // But if we partition on 4 instead we get - // 0123456789 - // 0123 456789 - // 01 23 456 789 - // 0 1 2 3 4 56 7 89 - // 5 6 8 9 - // - // And if we partition on 4, we get - // 0123456789 - // 012 3456789 - // 0 12 345 6789 - // 1 2 3 45 67 89 - // 4 5 6 7 8 9 - // - // In all cases we get a maximum height of 5, but when we partition on 4 we - // get a shorter depth for small numbers, which is probably more useful. - - if(x < 1000u) { - // It's 0, 1 or 2. - if(x < 10u) { - return 0; - } else { - // It's 1 or 2. - if(x < 100u) { - return 1u; - } else { - return 2u; - } - } - } else { - // It's 3, 4, 5, 6, 7, 8 or 9. - if(x < 1000000u) { - // It's 3, 4, or 5. - if(x < 10000u) { - return 3; - } else { - // It's 4 or 5. - if(x < 100000u) { - return 4; - } else { - return 5; - } - } - } else { - // It's 6, 7, 8 or 9. - if(x < 100000000u) { - // It's 6 or 7. - if(x < 10000000u) { - return 6; - } else { - return 7; - } - } else { - // It's 8 or 9. - if(x < 1000000000u) { - return 8; - } else { - return 9; - } - } - } - } -} - -// For special case x=0, this returns 0. -template -typename std::enable_if::value && sizeof(Uint64) == 8, unsigned>::type -log10(Uint64 x) -{ - // Same principle as for the uint32_t variant, but for the binary tree - // notation we now have A through J representing 10 to 19. Split-even - // approach gives us: - // 0123456789ABCDEFGHIJ - // 0123456789 ABCDEFGHIJ - // 01234 56789 ABCDE FGHIJ - // 01 234 56 789 AB CDE FG HIJ - // 0 1 2 34 5 6 7 89 A B C DE F G H IJ - // 3 4 8 9 D E I J - // - // Splitting on 4 in the first branch gives us: - // 012 3456789ABCDEFGHIJ - // 0 12 3456789A BCDEFGHIJ - // 1 2 3456 789A BCDE FGHIJ - // 34 56 78 9A BC DE FG HIJ - // 3 4 5 6 7 8 9 A B C D E F G H IJ - // I J - // Faster for small number and lower average height. - if(x < 1000u) { - // It's 0, 1 or 2. - if(x < 10u) { - return 0; - } else { - // It's 1 or 2. - if(x < 100u) { - return 1u; - } else { - return 2u; - } - } - } else { - // It's 3 through 19. - if(x < 100000000000u) { - // It's 3, 4, 5, 6, 7, 8, 9 or 10. - if(x < 10000000u) { - // It's 3, 4, 5 or 6. - if(x < 100000u) { - // It's 3 or 4. - if(x < 10000u) { - return 3u; - } else { - return 4u; - } - } else { - // It's 5 or 6. - if(x < 1000000u) { - return 5u; - } else { - return 6u; - } - } - } else { - // It's 7, 8, 9 or 10. - if(x < 1000000000u) { - // It's 7 or 8. - if(x < 100000000u) { - return 7u; - } else { - return 8u; - } - } else { - // It's 9 or 10. - if(x < 10000000000u) { - return 9u; - } else { - return 10u; - } - } - } - } else { - // It's 11, 12, 13, 14, 15, 16, 17, 18, or 19. - if(x < 1000000000000000u) { - // It's 11, 12, 13 or 14. - if(x < 10000000000000u) { - // It's 11 or 12. - if(x < 1000000000000u) { - return 11u; - } else { - return 12u; - } - } else { - // It's 13 or 14. - if(x < 100000000000000u) { - return 13u; - } else { - return 14u; - } - } - } else { - // It's 15, 16, 17, 18 or 19. - if(x < 100000000000000000u) { - // It's 15 or 16. - if(x < 10000000000000000u) { - return 15u; - } else { - return 16u; - } - } else { - // It's 17, 18 or 19. - if(x < 1000000000000000000u) { - return 17u; - } else { - // It's 19 or 20. - if(x < 10000000000000000000u) { - return 18u; - } else { - return 19u; - } - } - } - } - } - } -} - -template -typename std::enable_if::value, Unsigned>::type -log16(Unsigned v) -{ - return log2(v)/4; -} - -template -void itoa_generic_base10(output_buffer* pbuffer, bool negative, Unsigned value, conversion_specification const& cs) -{ - // TODO measure if these prefetches help - //prefetch_digits(); - char sign; - if(negative) { - sign = '-'; - } else { - sign = cs.plus_sign; - } - - // We have either - // [sign] [zeroes] [digits] [padding] - // [---precision---] - // [--------------size--------------] - // or - // [padding] [sign] [zeroes] [digits] - // [---precision---] - // [--------------size--------------] - // depending on if it's left-justified or not. - unsigned digits = value? log10(value) + 1 : 0; - unsigned precision = (cs.precision == UNSPECIFIED_PRECISION? 1 : cs.precision); - unsigned zeroes = precision>digits? precision - digits : 0; - unsigned content_size = !!sign + zeroes + digits; - unsigned size = std::max(content_size, cs.minimum_field_width); - unsigned padding = size - content_size; - if(cs.pad_with_zeroes && !cs.left_justify && cs.precision == UNSPECIFIED_PRECISION) - { - zeroes += padding; - padding = 0; - } - - char* str = pbuffer->reserve(size); - unsigned pos = size; - - if(cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - pos = utoa_generic_base10_preallocated(str, pos, value); - pos -= zeroes; - std::memset(str+pos, '0', zeroes); - if(sign) - str[--pos] = sign; - if(!cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - assert(pos == 0); - pbuffer->commit(size); -} - -template -typename std::enable_if::value, void>::type -itoa_generic_base10(output_buffer* pbuffer, Integer value, conversion_specification const& cs) -{ - bool negative = value < 0; - typename std::make_unsigned::type uv; - if(negative) - uv = unsigned_cast(-value); - else - uv = unsigned_cast(value); - itoa_generic_base10(pbuffer, negative, uv, cs); -} - -template -typename std::enable_if::value, void>::type -itoa_generic_base10(output_buffer* pbuffer, Integer value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, false, value, cs); -} - -template -void itoa_generic_base16(output_buffer* pbuffer, bool negative, Unsigned value, conversion_specification const& cs) -{ - char sign = negative? '-' : cs.plus_sign; - unsigned digits = 0; - unsigned prefix = 0; - if(value != 0) { - digits = static_cast(log16(value) + 1); - if(cs.alternative_form) - prefix = 2; - } - unsigned precision = (cs.precision == UNSPECIFIED_PRECISION? 1 : cs.precision); - unsigned zeroes = precision>digits? precision - digits : 0; - unsigned content_size = !!sign + prefix + zeroes + digits; - unsigned size = std::max(content_size, cs.minimum_field_width); - unsigned padding = size - content_size; - if(cs.pad_with_zeroes && !cs.left_justify && cs.precision == UNSPECIFIED_PRECISION) - { - zeroes += padding; - padding = 0; - } - - char* str = pbuffer->reserve(size); - unsigned pos = size; - if(cs.left_justify) { - pos -= padding; - std::memset(str + pos, ' ', padding); - } - - if(cs.uppercase) - pos = utoa_generic_base16_preallocated(str, pos, value); - else - pos = utoa_generic_base16_preallocated(str, pos, value); - - pos -= zeroes; - std::memset(str + pos, '0', zeroes); - if(prefix) { - str[--pos] = cs.uppercase? 'X' : 'x'; - str[--pos] = '0'; - } - if(sign) - str[--pos] = sign; - if(!cs.left_justify) { - pos -= padding; - std::memset(str + pos, ' ', padding); - } - - assert(pos == 0); - pbuffer->commit(size); -} - -template -typename std::enable_if::value, void>::type -itoa_generic_base16(output_buffer* pbuffer, Integer value, conversion_specification const& cs) -{ - bool negative = value < 0; - typename std::make_unsigned::type uv; - if(negative) - uv = unsigned_cast(-value); - else - uv = unsigned_cast(value); - itoa_generic_base16(pbuffer, negative, uv, cs); -} - -template -typename std::enable_if::value, void>::type -itoa_generic_base16(output_buffer* pbuffer, Integer value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, false, value, cs); -} - -#if defined(__GNUC__) -template -Float fxtract(Float v, Float* exp) -{ - Float significand; - asm("fxtract" : "=t"(significand), "=u"(*exp) : "0"(v)); - return significand; -} -#elif defined(_MSC_VER) - -double fxtract(long double v, long double* exp) -{ - // So here's what you might think we can do. - // long double significand; - // long double exp; - // __asm { - // fld [v] - // fxtract - // fstp [exp] - // fstp [significand] - // } - // - // But MSVC does not support inline asm on x64, and don't have fxtract as - // an intrinsic, so we're kinda screwed. Additionally, they decided they - // don't need no 80-bit long double any longer, so we're even more screwed. - // I'm not sure how they expect anyone to be able to do this kind of stuff - // with the same quality and speed as e.g. gcc, but... their call. Instead - // we'll just extract the bits from the in-memory representation. - - int iexp; - auto significand = std::frexp(v, &iexp); - *exp = iexp - 1; - return static_cast(2*significand); - - //std::uint16_t const* pv = reinterpret_cast(&v); - //// Highest 16 bits is seeeeeee eeeemmmm. We want the e part which is the - //// exponent. - //unsigned exponent = pv[3]; - //exponent = (exponent >> 4) & 0x7ff; - //*exp = static_cast(exponent) - 1023; -} - -#else -static_assert(false, "fxtract is not implemented for this compiler") -#endif - -template -inline std::uint_fast64_t u64_rint(Float value) -{ - if(std::is_convertible::value) - return static_cast(std::lrint(value)); - else - return static_cast(std::llrint(value)); -} - -struct decimal18 -{ - bool sign; - std::uint64_t mantissa; - int exponent; -}; - -decimal18 binary64_to_decimal18(double v) -{ - if(v == 0) - return {std::signbit(v), 0, 0}; - - long double e2; - long double m2 = fxtract(static_cast(v), &e2); - - // We have - // v = m2 * 2^e2 [1 <= m2 < 2] (1) - // and want a new representation - // v = m10 * 10^e10 [1 <= m10 < 10]. - // - // (1) can be rewritten as - // m2 * 10^(C*e2) [C = log(2)/log(10)] = - // m2 * 10^(e10i + e10f) [e10i = trunc(C*e2), e10f=frac(C*e2)] = - // m2 * 10^e10i * 10^e10f. - // - // If m2 * 10^e10f turns out to be in the range [1, 10) then this gives us - // a value for m10 (and consequently for e10=C*e2i). But assuming e2f is a - // positive fraction, we have - // 0 <= e2f < 1 - // 1 <= 10^e2f < 10. - // 1 <= m2 * 10^e2f < 20. - // - // We can adjust for m2 * 10^e2f >= 10 by dividing m2 by 10 and increasing - // e10 by one. Hence, with D = e2*log(2)/log(10) we have - // m10 = m2 * 10^(frac(D)) - // e10 = trunc(D). - // when m2*10^(frac(D)) < 10, or - // m10 = m2 * 10^(frac(D)) / 10 - // e10 = trunc(D) + 1. - // when m2*10^(frac(D)) >= 10, i.e. normalization requires at most a shift - // to the right by 1 digit. - // - // If e2f is a negative fraction we get - // -1 < e2f <= 0 - // 0.1 <= 10^e2f <= 1. - // 0.1 <= m2 * 10^e2f < 2. - // Note that this is the range obtained for a positive exponent except - // divided by 10. If we multiply this by 10 and subtract 1 from the - // exponent then we get the same situation as above, i.e. - // 1 <= 10 * m2 * 10^e2f < 20. - - long double d = e2; - d *= 0.30102999566398119521L; - long double e10i; - long double e10f = std::modf(d, &e10i); - long double m10; - if(e2 < 0) { - e10f += 1; - e10i -= 1; - } - - m10 = m2*powl(10, e10f + 16); - std::uint64_t mantissa = static_cast(std::llrint(std::abs(m10))); - if(mantissa < 100000000000000000u) - mantissa *= 10; - else - e10i += 1.0L; - // TODO rounding of m10 might cause it to overflow, right?. We need to - // adjust again in that case. But try to reproduce this scenario first so - // we know it's needed. - - return {std::signbit(v), mantissa, static_cast(e10i)}; -} - -std::uint64_t rounded_divide(bool sign, std::uint64_t value, std::uint64_t divisor) -{ - if(!sign) { - // +divisor/2 to turn truncation into rounding. - return (value + divisor/2) / divisor; - } else { - // This is the same rounding as above but we must pretend that the - // value is negative and reproduce the corresponding behavior. - // Adding divisor-1 turns the flooring behavior of integer division - // into a ceiling behavior. - return (value + (divisor-1) - divisor/2) / divisor; - } -} -std::uint64_t rounded_rshift(bool sign, std::uint64_t value, unsigned digits) -{ - return rounded_divide(sign, value, detail::power_lut[digits]); -} - -unsigned count_trailing_zeroes(decimal18 const& dv, unsigned truncated_digits) -{ - std::uint64_t mantissa = rounded_rshift(dv.sign, dv.mantissa, truncated_digits); - unsigned low = 0; - unsigned high = 18u-truncated_digits; - // Find the lowest x such that mantissa % 10^x != 0. - // Then the number of trailing zeroes is x-1. - while(low < high) { - unsigned pos = (low + high)/2; - if(mantissa % detail::power_lut[pos] == 0) - low = pos + 1; - else - high = pos; - } - - return low-1; -} - -unsigned count_trailing_zeroes_after_dot(decimal18 const& dv) -{ - // We need to get rid of the least significant digit because a double can't - // always represent a zero digit at that position. - // (e.g. 123.456 becomes 123.456000000000003). - std::uint64_t mantissa = rounded_divide(dv.sign, dv.mantissa, 10); - unsigned digits_before_dot = unsigned_cast(std::max(0, dv.exponent+1)); - if(digits_before_dot>17) { - // All significant digits are in front of the dots. No trailing zeroes - return 0; - } - unsigned max_trailing_zeroes = unsigned_cast(17 - digits_before_dot); - unsigned low = 0; - unsigned high = max_trailing_zeroes+1; - // Find the lowest x such that mantissa % 10^x != 0. - // Then the number of trailing zeroes is x-1. - while(low < high) { - unsigned pos = (low + high)/2; - if(mantissa % detail::power_lut[pos] == 0) - low = pos + 1; - else - high = pos; - } - - // +1 because we always treat the least significant digit (that we stripped - // at function entry) as if it were zero. - return low-1 + 1; -} - -void write_special_category(output_buffer* pbuffer, double value, conversion_specification const& cs, char const* category) -{ - char sign = std::signbit(value)? '-' : cs.plus_sign; - unsigned content_size = 3 + (sign? 1 : 0); - unsigned size = std::max(content_size, cs.minimum_field_width); - unsigned padding = size - content_size; - char* str = pbuffer->reserve(size); - unsigned pos = 0; - if(cs.left_justify) { - if(sign) - str[pos++] = sign; - memcpy(str+pos, category, 3); - pos += 3; - std::memset(str+pos, ' ', padding); - } else { - std::memset(str, ' ', padding); - pos += padding; - if(sign) - str[pos++] = sign; - memcpy(str+pos, category, 3); - } - pbuffer->commit(size); -} - -void write_nan(output_buffer* pbuffer, double value, conversion_specification const& cs) -{ - return write_special_category(pbuffer, value, cs, "nan"); -} - -void write_inf(output_buffer* pbuffer, double value, conversion_specification const& cs) -{ - return write_special_category(pbuffer, value, cs, "inf"); -} - -void ftoa_base10_f_normal(output_buffer* pbuffer, decimal18 dv, unsigned precision, conversion_specification const& cs) -{ - // [sign] [digits_before_dot] [zeroes_before_dot] [dot] [zeroes_after_dot] [digits_after_dot] [suffix_zeroes] - char sign = dv.sign? '-' : cs.plus_sign; - unsigned digits_before_dot; - unsigned zeroes_before_dot; - unsigned zeroes_after_dot; - unsigned digits_after_dot; - - if(dv.exponent>=17) { - // All digits from the mantissa are on the left-hand side of the dot. - digits_before_dot = 18; - zeroes_before_dot = unsigned_cast(dv.exponent)+1 - 18; - zeroes_after_dot = precision; - digits_after_dot = 0; - } else if(dv.exponent < 0) { - // All digits from the mantissa are on the right-hand side of the dot. - // We set zeroes_before_dot=1 to generate a zero on the left-hand side, - // since this is what people expect and it's what stdio does. - digits_before_dot = 0; - zeroes_before_dot = 1; - zeroes_after_dot = std::min(unsigned_cast(-dv.exponent)-1, precision); - digits_after_dot = std::min(18u, precision - zeroes_after_dot); - } else { - // There are digits from the mantissa both on the left- and right-hand - // sides of the dot. - if(dv.mantissa == 0) { - // Special case for +-0: utoa_generic_base10_preallocated does not - // output any digits for a zero value, so we need to fake it in by - // using zeroes_before_dot instead. - digits_before_dot = 0; - zeroes_before_dot = 1; - zeroes_after_dot = 0; - digits_after_dot = 0; - } else { - digits_before_dot = unsigned_cast(dv.exponent+1); - zeroes_before_dot = 0; - zeroes_after_dot = 0; - digits_after_dot = std::min(18 - digits_before_dot, precision); - } - } - unsigned suffix_zeroes = precision - (zeroes_after_dot + digits_after_dot); - - bool dot = cs.alternative_form - || (zeroes_after_dot + digits_after_dot + suffix_zeroes != 0); - - // Reduce the mantissa part to the requested number of digits. - auto mantissa_digits = digits_before_dot + digits_after_dot; - std::uint64_t mantissa = rounded_rshift(dv.sign, dv.mantissa, 18-mantissa_digits); - auto order = detail::power_lut[mantissa_digits]; - bool carry = mantissa >= order; - if(carry) { - // When reducing the mantissa, rounding caused it to overflow. For - // example, when formatting 0.095 with precision=2, we have - // zeroes_before_dot=1, zeroes_after_dot=1, mantissa_digits=1 and - // suffix_zeroes=0. When trying to reduce the mantissa to 1 digit we - // end up not with 9, but with 10 due to rounding. In this case we need - // to make room for an extra digit from the mantissa, which we'll take - // from zeroes_after_dot or by increasing digits_before_dot. - // - // Logically this can only happen if there are actual digits from the - // mantissa on the right-hand side of the dot. If all the digits are on - // the left-hand side then no reduction will take place, because only - // the number of fractional digits are user-controllable. So, if - // zeroes_before_dot is nonzero then that's only to generate a filler - // zero (see if clause above where dv.exponent < 0). If the overflow - // now produces a digit on the left-hand side of the dot then we must - // cancel out that zero by setting zeroes_before_dot=0. - if(zeroes_after_dot) { - --zeroes_after_dot; - ++digits_after_dot; - } else { - ++digits_before_dot; - zeroes_before_dot = 0; - } - ++mantissa_digits; - } - - unsigned content_size = !!sign + digits_before_dot + zeroes_before_dot - + dot + zeroes_after_dot + digits_after_dot + suffix_zeroes; - unsigned size = std::max(cs.minimum_field_width, content_size); - unsigned padding = size - content_size; - unsigned pad_zeroes = 0; - if(cs.pad_with_zeroes && !cs.left_justify) { - pad_zeroes = padding; - padding = 0; - } - - char* str = pbuffer->reserve(size); - unsigned pos = size; - if(cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - - if(dot) { - pos -= suffix_zeroes; - std::memset(str+pos, '0', suffix_zeroes); - mantissa = utoa_generic_base10_preallocated(str, pos, mantissa, - digits_after_dot); - pos -= digits_after_dot; - - pos -= zeroes_after_dot; - std::memset(str+pos, '0', zeroes_after_dot); - str[--pos] = '.'; - } - - pos -= zeroes_before_dot; - std::memset(str+pos, '0', zeroes_before_dot); - pos = utoa_generic_base10_preallocated(str, pos, mantissa); - pos -= pad_zeroes; - std::memset(str+pos, '0', pad_zeroes); - if(sign) - str[--pos] = sign; - - if(!cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - pbuffer->commit(size); - assert(pos == 0); -} - -void ftoa_base10_e_normal(output_buffer* pbuffer, decimal18 dv, - unsigned precision, conversion_specification const& cs) -{ - // We have either - // [sign] [digit] [dot] [digits_after_dot] [e] [exponent sign] [exponent_digits] - // or - // [padding] [sign] [zero_padding] [digit] [dot] [digits_after_dot] [e] [exponent sign] [exponent_digits] - // depending on if it's left-justified or not. - - char exponent_sign; - unsigned exponent; - if(dv.exponent < 0) { - exponent_sign = '-'; - exponent = unsigned_cast(-dv.exponent); - } else { - exponent_sign = '+'; - exponent = unsigned_cast(dv.exponent); - } - unsigned digits_after_dot = precision; - int dot = digits_after_dot!=0 || cs.alternative_form; - - int exponent_digits; - // Apparently stdio never prints less than two digits for the exponent, - // so we'll do the same to stay consistent. Maximum value of the exponent - // in a double is 308 so we won't get more than 3 digits. - if(exponent < 100) - exponent_digits = 2; - else - exponent_digits = 3; - - char sign = dv.sign? '-' : cs.plus_sign; - unsigned content_size = !!sign + 1 + dot + digits_after_dot + 1 + 1 + exponent_digits; - unsigned size = std::max(cs.minimum_field_width, content_size); - unsigned padding = size - content_size; - unsigned pad_zeroes = 0; - if(cs.pad_with_zeroes && !cs.left_justify) { - pad_zeroes = padding; - padding = 0; - } - - // Get rid of digits we don't want in the mantissa. - std::uint64_t mantissa = rounded_rshift(dv.sign, dv.mantissa, - 18-(digits_after_dot+1)); - - char* str = pbuffer->reserve(size); - - unsigned pos = size; - if(cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - - utoa_generic_base10_preallocated(str, pos, exponent, exponent_digits); - pos -= exponent_digits; - str[--pos] = exponent_sign; - str[--pos] = 'e'; - - mantissa = utoa_generic_base10_preallocated(str, pos, mantissa, digits_after_dot); - pos -= digits_after_dot; - - if(dot) - str[--pos] = '.'; - - str[--pos] = '0' + static_cast(mantissa); - - pos -= pad_zeroes; - std::memset(str+pos, '0', pad_zeroes); - if(sign) - str[0] = sign; - - if(!cs.left_justify) { - pos -= padding; - std::memset(str+pos, ' ', padding); - } - pbuffer->commit(size); -} - -} // anonymous namespace - -// TODO define these for more integer sizes -void itoa_base10(output_buffer* pbuffer, int value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base10(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base10(output_buffer* pbuffer, long value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base10(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base10(output_buffer* pbuffer, long long value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base10(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs) -{ - itoa_generic_base10(pbuffer, value, cs); -} - -void itoa_base16(output_buffer* pbuffer, int value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} - -void itoa_base16(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} - -void itoa_base16(output_buffer* pbuffer, long value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} - -void itoa_base16(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} - -void itoa_base16(output_buffer* pbuffer, long long value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} -void itoa_base16(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs) -{ - itoa_generic_base16(pbuffer, value, cs); -} - -void ftoa_base10_f(output_buffer* pbuffer, double value, conversion_specification const& cs) -{ - // TODO measure if these prefetches help - //prefetch_digits(); - //prefetch_power_lut(); - auto category = std::fpclassify(value); - if(category == FP_NAN) { - return write_nan(pbuffer, value, cs); - } else if(category == FP_INFINITE) { - return write_inf(pbuffer, value, cs); - } else { - decimal18 dv = binary64_to_decimal18(value); - int p = cs.precision == UNSPECIFIED_PRECISION? 6 : cs.precision; - return ftoa_base10_f_normal(pbuffer, dv, p, cs); - } -} - -void ftoa_base10_g(output_buffer* pbuffer, double value, conversion_specification const& cs) -{ - // The idea of %g is that the precision says how many significant digits we - // want in the string representation. If the number of significant digits is - // not enough to represent all the digits up to the dot (i.e. it is higher than - // the number's exponent), then %e notation is used instead. - // - // A special feature of %g which is not present in %e and %f is that if there - // are trailing zeroes in the fraction then those will be removed, unless - // alternative mode is requested. Alternative mode also means that the period - // stays, no matter if there are any fractional decimals remaining or - // not. - //prefetch_digits(); - //prefetch_power_lut(); - auto category = std::fpclassify(value); - if(category == FP_NAN) { - return write_nan(pbuffer, value, cs); - } else if(category == FP_INFINITE) { - return write_inf(pbuffer, value, cs); - } else { - int const minimum_exponent = -4; - decimal18 dv = binary64_to_decimal18(value); - - int p; - if(cs.precision == UNSPECIFIED_PRECISION) - p = 6; - else if(cs.precision == 0) - p = 1; - else { - p = cs.precision; - if(!cs.alternative_form) { - // We are supposed to remove trailing zeroes, so we can't ever - // get more than 18 digits because the rest will be zero - // anyway, as this is the number of digits in the mantissa. - p = std::min(p, 18); - } - } - unsigned truncated_digits = 18 - p; - - if(p > dv.exponent && dv.exponent >= minimum_exponent) { - unsigned trailing_zeroes = 0; - if(!cs.alternative_form) { - trailing_zeroes = count_trailing_zeroes_after_dot(dv); - if(trailing_zeroes > truncated_digits) - trailing_zeroes -= truncated_digits; - else - trailing_zeroes = 0; - } - p = p - 1 - dv.exponent - trailing_zeroes; - p = std::max(0, p); - return ftoa_base10_f_normal(pbuffer, dv, p, cs); - } else { - unsigned trailing_zeroes = 0; - if(!cs.alternative_form) - trailing_zeroes = count_trailing_zeroes(dv, truncated_digits); - p = p - 1 - trailing_zeroes; - return ftoa_base10_e_normal(pbuffer, dv, p, cs); - } - } -} - -} // namespace reckless - -#ifdef UNIT_TEST -#include "unit_test.hpp" - -#include -#include // istringstream, ostringstream -#include // iomanip -#include -#include // mismatch - -namespace reckless { -namespace detail { -class whitebox_output_buffer : public output_buffer { -public: - using output_buffer::output_buffer; - using output_buffer::flush; -}; - -class log10_suite { -public: - void uint32() - { - test(10); - } - - void uint64() - { - test(20); - } - -private: - template - void test(std::size_t digits) - { - T v = 0; - TEST(log10(v) == 0); - v = 1; - TEST(log10(v) == 0); - v = 10; - for(unsigned i=1; i!=digits; ++i) - { - //std::cerr << v-1 << " -> " << i-1 << '?' << std::endl; - TEST(log10(v-1) == i-1); - //std::cerr << v << " -> " << i << '?' << std::endl; - TEST(log10(v) == i); - v *= 10; - } - } -}; - -unit_test::suite log10_tests = { - TESTCASE(log10_suite::uint32), - TESTCASE(log10_suite::uint64), -}; - -class string_writer : public writer { -public: - std::size_t write(void const* pbuffer, std::size_t count, std::error_code& ec) noexcept override - { - auto pc = static_cast(pbuffer); - buffer_.insert(buffer_.end(), pc, pc + count); - ec.clear(); - return count; - } - - void reset() - { - buffer_.clear(); - } - - std::string const& str() const - { - return buffer_; - } - -private: - std::string buffer_; -}; - -class itoa_base10_suite -{ -public: - itoa_base10_suite() : - output_buffer_(&writer_, 1024) - { - } - - void positive() - { - TEST(convert(0) == "0"); - TEST(convert(1) == "1"); - TEST(convert(2) == "2"); - TEST(convert(10) == "10"); - TEST(convert(100) == "100"); - TEST(convert(std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); - } - - void negative() - { - TEST(convert(-1) == "-1"); - TEST(convert(-2) == "-2"); - TEST(convert(-10) == "-10"); - TEST(convert(-100) == "-100"); - TEST(convert(std::numeric_limits::min()) == std::to_string(std::numeric_limits::min())); - } - - void field_width() - { - conversion_specification cs; - cs.minimum_field_width = 11; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0, cs) == " 0"); - TEST(convert(1, cs) == " 1"); - TEST(convert(2, cs) == " 2"); - TEST(convert(10, cs) == " 10"); - TEST(convert(100, cs) == " 100"); - - TEST(convert(-1, cs) == " -1"); - TEST(convert(-2, cs) == " -2"); - TEST(convert(-10, cs) == " -10"); - TEST(convert(-100, cs) == " -100"); - } - - void left_justify() - { - conversion_specification cs; - cs.minimum_field_width = 11; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = true; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0, cs) == "0 "); - TEST(convert(1, cs) == "1 "); - TEST(convert(2, cs) == "2 "); - TEST(convert(10, cs) == "10 "); - TEST(convert(100, cs) == "100 "); - - TEST(convert(-1, cs) == "-1 "); - TEST(convert(-2, cs) == "-2 "); - TEST(convert(-10, cs) == "-10 "); - TEST(convert(-100, cs) == "-100 "); - } - - void precision() - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 0; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = 0; - - TEST(convert(0, cs) == ""); - TEST(convert(1, cs) == "1"); - TEST(convert(10, cs) == "10"); - - cs.precision = 1; - - TEST(convert(0, cs) == "0"); - TEST(convert(1, cs) == "1"); - TEST(convert(10, cs) == "10"); - - cs.precision = 2; - - TEST(convert(0, cs) == "00"); - TEST(convert(1, cs) == "01"); - TEST(convert(10, cs) == "10"); - - cs.precision = 3; - - TEST(convert(0, cs) == "000"); - TEST(convert(1, cs) == "001"); - TEST(convert(10, cs) == "010"); - } - - void sign() - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 0; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = '+'; - cs.uppercase = false; - - TEST(convert(0, cs) == "+"); - TEST(convert(1, cs) == "+1"); - TEST(convert(10, cs) == "+10"); - TEST(convert(-10, cs) == "-10"); - - cs.plus_sign = ' '; - TEST(convert(1, cs) == " 1"); - TEST(convert(-1, cs) == "-1"); - } - - void precision_and_padding() - { - conversion_specification cs; - cs.minimum_field_width = 5; - cs.precision = 3; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = true; - cs.plus_sign = 0; - cs.uppercase = false; - - // "for d, i, o, u, x, and X conversions, if a precision is specified, - // the 0 flag is ignored." - TEST(convert(0, cs) == " 000"); - TEST(convert(1, cs) == " 001"); - TEST(convert(999, cs) == " 999"); - TEST(convert(1000, cs) == " 1000"); - TEST(convert(99999, cs) == "99999"); - TEST(convert(999999, cs) == "999999"); - - cs.precision = UNSPECIFIED_PRECISION; - TEST(convert(0, cs) == "00000"); - TEST(convert(1, cs) == "00001"); - TEST(convert(1000, cs) == "01000"); - } - -private: - template - std::string convert(T v) - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - return convert(v, cs); - } - template - std::string convert(T v, conversion_specification const& cs) - { - writer_.reset(); - - itoa_base10(&output_buffer_, v, cs); - - output_buffer_.flush(); - return writer_.str(); - } - - string_writer writer_; - whitebox_output_buffer output_buffer_; -}; - -unit_test::suite itoa_base10_tests = { - TESTCASE(itoa_base10_suite::positive), - TESTCASE(itoa_base10_suite::negative), - TESTCASE(itoa_base10_suite::field_width), - TESTCASE(itoa_base10_suite::left_justify), - TESTCASE(itoa_base10_suite::precision), - TESTCASE(itoa_base10_suite::sign), - TESTCASE(itoa_base10_suite::precision_and_padding) -}; - -class itoa_base16_suite -{ -public: - itoa_base16_suite() : - output_buffer_(&writer_, 1024) - { - } - - void positive() - { - TEST(convert(0) == "0"); - TEST(convert(1) == "1"); - TEST(convert(2) == "2"); - TEST(convert(0x10) == "10"); - TEST(convert(0x100) == "100"); - std::ostringstream ostr; - ostr << std::hex << std::numeric_limits::max(); - TEST(convert(std::numeric_limits::max()) == ostr.str()); - } - - void negative() - { - TEST(convert(-1) == "-1"); - TEST(convert(-2) == "-2"); - TEST(convert(-0x10) == "-10"); - TEST(convert(-0x100) == "-100"); - - // stdlib doesn't support signed integers for hexadecimal output so - // we'll have to do some patchwork. - long long v = std::numeric_limits::max(); - std::ostringstream ostr; - ostr << std::hex << v; - std::string expected = std::string("-") + ostr.str(); - TEST(convert(-std::numeric_limits::max()) == expected); - } - - void field_width() - { - conversion_specification cs; - cs.minimum_field_width = 11; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0x0, cs) == " 0"); - TEST(convert(0x1, cs) == " 1"); - TEST(convert(0x2, cs) == " 2"); - TEST(convert(0x10, cs) == " 10"); - TEST(convert(0x100, cs) == " 100"); - - TEST(convert(-0x1, cs) == " -1"); - TEST(convert(-0x2, cs) == " -2"); - TEST(convert(-0x10, cs) == " -10"); - TEST(convert(-0x100, cs) == " -100"); - } - - void left_justify() - { - conversion_specification cs; - cs.minimum_field_width = 11; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = true; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0x0, cs) == "0 "); - TEST(convert(0x1, cs) == "1 "); - TEST(convert(0x2, cs) == "2 "); - TEST(convert(0x10, cs) == "10 "); - TEST(convert(0x100, cs) == "100 "); - - TEST(convert(-0x1, cs) == "-1 "); - TEST(convert(-0x2, cs) == "-2 "); - TEST(convert(-0x10, cs) == "-10 "); - TEST(convert(-0x100, cs) == "-100 "); - } - - void precision() - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 0; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = 0; - - TEST(convert(0, cs) == ""); - TEST(convert(1, cs) == "1"); - TEST(convert(0x10, cs) == "10"); - - cs.precision = 1; - - TEST(convert(0x0, cs) == "0"); - TEST(convert(0x1, cs) == "1"); - TEST(convert(0x10, cs) == "10"); - - cs.precision = 2; - - TEST(convert(0x0, cs) == "00"); - TEST(convert(0x1, cs) == "01"); - TEST(convert(0x10, cs) == "10"); - - cs.precision = 3; - - TEST(convert(0x0, cs) == "000"); - TEST(convert(0x1, cs) == "001"); - TEST(convert(0x10, cs) == "010"); - } - - void sign() - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 0; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = '+'; - cs.uppercase = false; - - TEST(convert(0x0, cs) == "+"); - TEST(convert(0x1, cs) == "+1"); - TEST(convert(0x10, cs) == "+10"); - TEST(convert(-0x10, cs) == "-10"); - - cs.plus_sign = ' '; - TEST(convert(0x01, cs) == " 1"); - TEST(convert(-0x1, cs) == "-1"); - } - - void precision_and_padding() - { - conversion_specification cs; - cs.minimum_field_width = 5; - cs.precision = 3; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = true; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0x0, cs) == " 000"); - TEST(convert(0x1, cs) == " 001"); - TEST(convert(0x999, cs) == " 999"); - TEST(convert(0x1000, cs) == " 1000"); - TEST(convert(0x99999, cs) == "99999"); - TEST(convert(0x999999, cs) == "999999"); - - cs.precision = UNSPECIFIED_PRECISION; - TEST(convert(0x0, cs) == "00000"); - TEST(convert(0x1, cs) == "00001"); - TEST(convert(0x1000, cs) == "01000"); - } - - void xcase() - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = false; - cs.alternative_form = true; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0xabcdef, cs) == "0xabcdef"); - TEST(convert(-0xabcdef, cs) == "-0xabcdef"); - cs.uppercase = true; - TEST(convert(0xabcdef, cs) == "0XABCDEF"); - TEST(convert(-0xabcdef, cs) == "-0XABCDEF"); - } - -private: - - template - std::string convert(T v) - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = UNSPECIFIED_PRECISION; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - return convert(v, cs); - } - template - std::string convert(T v, conversion_specification const& cs) - { - writer_.reset(); - - itoa_base16(&output_buffer_, v, cs); - - output_buffer_.flush(); - return writer_.str(); - } - - string_writer writer_; - whitebox_output_buffer output_buffer_; -}; - -unit_test::suite itoa_base16_tests = { - TESTCASE(itoa_base16_suite::positive), - TESTCASE(itoa_base16_suite::negative), - TESTCASE(itoa_base16_suite::field_width), - TESTCASE(itoa_base16_suite::left_justify), - TESTCASE(itoa_base16_suite::precision), - TESTCASE(itoa_base16_suite::sign), - TESTCASE(itoa_base16_suite::precision_and_padding), - TESTCASE(itoa_base16_suite::xcase) -}; - -class ftoa_base10_f -{ -public: - ftoa_base10_f() : - output_buffer_(&writer_, 1024) - { - } - - void normal() - { - TEST(convert(1.5, 2) == "1.50"); - TEST(convert(1.234567890, 4) == "1.2346"); - TEST(convert(1.2345678901234567, 16) == "1.2345678901234567"); - TEST(convert(1.2345678901234567, 17) == "1.23456789012345670"); - TEST(convert(1.2345678901234567, 25) == "1.2345678901234567000000000"); - TEST(convert(1.7976931348623157e308, 3) == "179769313486231563000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"); - TEST(convert(1234.5678, 0) == "1235"); - TEST(convert(1234.5678, 1) == "1234.6"); - - TEST(convert(0.3, 1) == "0.3"); - TEST(convert(0.3, 2) == "0.30"); - TEST(convert(0.3, 20) == "0.29999999999999999000"); - - TEST(convert(1.2345e20, 5) == "123450000000000000000.00000"); - TEST(convert(1.2345e20, 0) == "123450000000000000000"); - TEST(convert(1.2345e2, 5) == "123.45000"); - TEST(convert(1.2345e-20, 5) == "0.00000"); - TEST(convert(0.5, 0) == "1"); - TEST(convert(0.05, 1) == "0.1"); - TEST(convert(9.9, 0) == "10"); - TEST(convert(0, 0) == "0"); - TEST(convert(1.23456789012345670, 20) == "1.23456789012345670000"); - TEST(convert(0.123456789012345670, 20) == "0.12345678901234566300"); - - TEST(convert(0.000123, 6) == "0.000123"); - } - - void padding() - { - conversion_specification cs; - cs.minimum_field_width = 7; - cs.precision = 3; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = true; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0.5, cs) == "000.500"); - TEST(convert(-0.5, cs) == "-00.500"); - TEST(convert(0.0, cs) == "000.000"); - TEST(convert(-0.0, cs) == "-00.000"); - - cs.left_justify = true; - - TEST(convert(0.5, cs) == "0.500 "); - TEST(convert(-0.5, cs) == "-0.500 "); - TEST(convert(0.0, cs) == "0.000 "); - TEST(convert(-0.0, cs) == "-0.000 "); - } - -private: - std::string convert(double number, conversion_specification const& cs) - { - writer_.reset(); - reckless::ftoa_base10_f(&output_buffer_, number, cs); - output_buffer_.flush(); - //std::cout << '[' << writer_.str() << ']' << std::endl; - return writer_.str(); - } - - std::string convert(double number, unsigned precision) - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = precision; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - return convert(number, cs); - } - - string_writer writer_; - whitebox_output_buffer output_buffer_; -}; - -unit_test::suite ftoa_base10_precision_tests = { - TESTCASE(ftoa_base10_f::normal), - TESTCASE(ftoa_base10_f::padding), -}; - -#define TEST_FTOA(number) test_conversion_quality(number, __FILE__, __LINE__) - -class ftoa_base10_g -{ -public: - static int const PERFECT_QUALITY = std::numeric_limits::max(); - ftoa_base10_g() : - output_buffer_(&writer_, 1024) - { - } - - void greater_than_one() - { - TEST_FTOA(1.0); - TEST_FTOA(10.0); - TEST_FTOA(123.0); - TEST_FTOA(123.456); - TEST_FTOA(123456); - TEST_FTOA(12345678901234567890.0); - TEST_FTOA(1.23456789e300); - TEST_FTOA(1.2345678901234567e308); - TEST_FTOA(1.7976931348623157e308); - TEST_FTOA(std::nextafter(1.7976931348623157e308, 2.0)); - } - - void fractional() - { - // TODO test zero with precision>0 etc - TEST_FTOA(0.1); - TEST_FTOA(0.01); - TEST_FTOA(0.001); - TEST_FTOA(0.0); - TEST_FTOA(0.123456); - TEST_FTOA(0.00000123456); - TEST_FTOA(0.00000000000000000123456); - // TODO below test works but doesn't give perfect output. Can we fix? - //TEST_FTOA(0.0000000000000000012345678901234567890); - TEST_FTOA(-0.0); - TEST_FTOA(-0.1); - } - - void negative() - { - TEST_FTOA(-1.0); - TEST_FTOA(-123.456); - TEST_FTOA(-123456); - TEST_FTOA(-12345678901234567890.0); - TEST_FTOA(-1.23456789e300); - } - - void subnormals() - { - TEST_FTOA(2.2250738585072009e-308); - TEST_FTOA(0.04e-308); - TEST_FTOA(0.00001234e-308); - TEST_FTOA(4.9406564584124654e-324); - TEST_FTOA(0.0); - } - - void special() - { - TEST(convert(-0.0) == "-0"); - TEST(convert(std::numeric_limits::quiet_NaN()) == "nan"); - TEST(convert(std::numeric_limits::signaling_NaN()) == "nan"); - TEST(convert(std::nan("1")) == "nan"); - TEST(convert(std::nan("2")) == "nan"); - TEST(convert(-std::numeric_limits::quiet_NaN()) == "-nan"); - TEST(convert(std::numeric_limits::infinity()) == "inf"); - TEST(convert(-std::numeric_limits::infinity()) == "-inf"); - - conversion_specification cs; - cs.minimum_field_width = 6; - cs.precision = 0; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(-std::numeric_limits::quiet_NaN(), cs) == " -nan"); - TEST(convert(std::numeric_limits::quiet_NaN(), cs) == " nan"); - cs.left_justify = true; - TEST(convert(-std::numeric_limits::quiet_NaN(), cs) == "-nan "); - TEST(convert(std::numeric_limits::quiet_NaN(), cs) == "nan "); - cs.plus_sign = '+'; - TEST(convert(std::numeric_limits::quiet_NaN(), cs) == "+nan "); - cs.left_justify = false; - TEST(convert(std::numeric_limits::quiet_NaN(), cs) == " +nan"); - } - - void scientific() - { - TEST(convert(1.2345e-8, 6) == "1.2345e-08"); - TEST(convert(1.2345e-10, 6) == "1.2345e-10"); - TEST(convert(1.2345e+10, 6) == "1.2345e+10"); - TEST(convert(1.2345e+10, 1) == "1e+10"); - TEST(convert(1.6345e+10, 1) == "2e+10"); - TEST(convert(1.6645e+10, 2) == "1.7e+10"); - TEST(convert(1.6645e+10, 2) == "1.7e+10"); - TEST(convert(1.7976931348623157e308, 5) == "1.7977e+308"); - TEST(convert(4.9406564584124654e-324, 5) == "4.9407e-324"); - } - - void padding() - { - conversion_specification cs; - cs.minimum_field_width = 5; - cs.precision = 3; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = true; - cs.plus_sign = 0; - cs.uppercase = false; - - TEST(convert(0, cs) == "00000"); - TEST(convert(1, cs) == "00001"); - TEST(convert(999, cs) == "00999"); - TEST(convert(1000, cs) == "1e+03"); - TEST(convert(0.01, cs) == "00.01"); - } - - void random() - { - std::mt19937_64 rng; - int const total = 100000000; - int perfect = 0; - int correct_superfluous = 0; - int quality_counts[18] = {0}; - std::vector superfluous_counts; - int i = 0; - while(i != total) { - std::uint64_t bits = rng(); - bits &= (std::uint64_t(1)<<63)-1; - unsigned exponent = static_cast(bits >> 52); - if(exponent == 0 || exponent == 2047) - continue; - double const v = *reinterpret_cast(&bits); - //double const v = 1.116216926772162e-277; - - auto quality = get_conversion_quality(v); - if(quality == PERFECT_QUALITY) { - perfect++; - } else { - assert(quality < 18); - if(quality>=0) - quality_counts[quality]++; - else { - quality = -quality; - if(static_cast(quality) >= superfluous_counts.size()) - superfluous_counts.resize(quality+1); - superfluous_counts[quality]++; - correct_superfluous++; - } - } - ++i; - } - std::cout << " perfect conversions: " << perfect; - std::cout << " (" << 100*static_cast(perfect)/total << "%)\n"; - std::cout << " correct conversions: " << (perfect+correct_superfluous); - std::cout << " (" << 100*static_cast(perfect+correct_superfluous)/total << "%)\n"; - std::cout << " Count of N correct significant digits:\n"; - for(std::size_t i=0; i!=18; ++i) - std::cout << " " << i << ": " << quality_counts[i] << - " (" << 100*static_cast(quality_counts[i])/total << "%)\n"; - std::cout << " Count of N superfluous/unneeded digits:\n"; - for(std::size_t i=0; i!=superfluous_counts.size(); ++i) - std::cout << " " << i << ": " << superfluous_counts[i] << - " (" << 100*static_cast(superfluous_counts[i])/total << "%)\n"; - } - -private: - std::pair normalize_for_comparison(std::string const& number) - { - auto exponent_pos = number.find('e'); - std::string mantissa(number, 0, exponent_pos); - auto decimal_point = mantissa.find('.'); - if(decimal_point != std::string::npos) - mantissa.erase(decimal_point, 1); - auto nonzero_pos = mantissa.find_first_not_of('0'); - if(nonzero_pos != std::string::npos) - mantissa.erase(0, nonzero_pos); - - std::string exponent; - if(exponent_pos != std::string::npos) - exponent.assign(number, exponent_pos+1, std::string::npos); - - return {mantissa, exponent}; - } - - // PERFECT_QUALITY return: Character-for-character correct. - // Positive return: number of correct significant digits - // Negative return: number of superfluous significant digits. - int get_conversion_quality(double number) - { - std::string const& str = convert(number); - std::istringstream istr(str); - double number2; - istr >> number2; - //{ - char buf[32]; - std::sprintf(buf, "%.18g", number); - if(str == buf) - return PERFECT_QUALITY; - std::string mantissa1, exponent1; - tie(mantissa1, exponent1) = normalize_for_comparison(str); - - std::string mantissa2, exponent2; - tie(mantissa2, exponent2) = normalize_for_comparison(buf); - - assert(exponent1 == exponent2); - //assert(mantissa1.size() == mantissa2.size()); - auto size = std::min(mantissa1.size(), mantissa2.size()); - std::size_t correct_digits = mismatch( - begin(mantissa1), begin(mantissa1)+size, - begin(mantissa2)).first - begin(mantissa1); - auto expected = mantissa2.size(); - if(correct_digits < expected) { - // Only some of the characters we produced match the characters - // produced by stdio. Return how many digits were correct. - // However: sometimes we actually perform better than the - // runtime library does. An example of this is 123.456 where we - // get "123.456" and stdio prints "123.456000000000003" which - // converts back to the exact same floating-point number. Hence - // "123.456" would have been sufficient. By counting "correct - // digits" it looks as if we got only 6 matching digits and got - // the other 12 wrong. To avoid reporting this as poor quality, - // we check whether our string converts back to the same value - // and if it does, we consider it to be perfect quality. - //std::istringstream istr(str); - //double number2; - //istr >> number2; - if(number != number2) - return static_cast(correct_digits); - else - return PERFECT_QUALITY; - } else { - assert(mantissa1.size() > expected); - return -static_cast(mantissa1.size() - expected); - } - } - - std::string convert(double number, conversion_specification const& cs) - { - writer_.reset(); - reckless::ftoa_base10_g(&output_buffer_, number, cs); - output_buffer_.flush(); - return writer_.str(); - } - - std::string convert(double number, int significant_digits=18) - { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = significant_digits; - cs.left_justify = false; - cs.alternative_form = false; - cs.pad_with_zeroes = false; - cs.plus_sign = 0; - cs.uppercase = false; - - auto str = convert(number, cs); - //char buf[128]; - //std::sprintf(buf, "%.*g", significant_digits, number); - //std::cout << str << '\t' << buf << std::endl; - return str; - } - - void test_conversion_quality(double number, char const* file, int line) - { - if(get_conversion_quality(number) != PERFECT_QUALITY) - { - std::ostringstream ostr; - ostr << "conversion of " << number; - throw unit_test::error(ostr.str(), file, line); - } - } - - string_writer writer_; - whitebox_output_buffer output_buffer_; -}; - -unit_test::suite ftoa_base10_tests = { - TESTCASE(ftoa_base10_g::greater_than_one), - TESTCASE(ftoa_base10_g::fractional), - TESTCASE(ftoa_base10_g::negative), - TESTCASE(ftoa_base10_g::subnormals), - TESTCASE(ftoa_base10_g::special), - TESTCASE(ftoa_base10_g::scientific), - TESTCASE(ftoa_base10_g::padding), - TESTCASE(ftoa_base10_g::random) -}; - -} // namespace detail -} // namespace reckless - -UNIT_TEST_MAIN(); -#endif diff --git a/external/reckless/reckless/src/output_buffer.cpp b/external/reckless/reckless/src/output_buffer.cpp deleted file mode 100644 index bcd762aa..00000000 --- a/external/reckless/reckless/src/output_buffer.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#include // atomic_store_release - -#ifdef RECKLESS_ENABLE_TRACE_LOG -#include -#else -#define RECKLESS_TRACE(Event, ...) do {} while(false) -#endif // RECKLESS_ENABLE_TRACE_LOG - -#include // malloc, free -#include -#include // max, min - -namespace reckless { - -#ifdef RECKLESS_ENABLE_TRACE_LOG -namespace { -struct flush_output_buffer_start_event : - public detail::timestamped_trace_event -{ - std::string format() const - { - return timestamped_trace_event::format() + " flush_output_buffer start"; - } -}; - -struct flush_output_buffer_finish_event : - public detail::timestamped_trace_event -{ - std::string format() const - { - return timestamped_trace_event::format() + " flush_output_buffer finish"; - } -}; - - -struct output_buffer_full_event -{ - std::string format() const - { - return "output_buffer_full"; - } -}; -} -#endif // RECKLESS_ENABLE_TRACE_LOG - -char const* excessive_output_by_frame::what() const noexcept -{ - return "excessive output by frame"; -} - -char const* flush_error::what() const noexcept -{ - return "flush error"; -} - -using detail::likely; - -output_buffer::output_buffer() -{ -} - -output_buffer::output_buffer(writer* pwriter, std::size_t max_capacity) -{ - reset(pwriter, max_capacity); -} - -void output_buffer::reset() noexcept -{ - std::free(pbuffer_); - pwriter_ = nullptr; - pbuffer_ = nullptr; - pcommit_end_ = nullptr; - pbuffer_end_ = nullptr; - lost_input_frames_ = 0; -} - -void output_buffer::reset(writer* pwriter, std::size_t max_capacity) -{ - using namespace detail; - auto pbuffer = static_cast(std::malloc(max_capacity)); - if(!pbuffer) - throw std::bad_alloc(); - std::free(pbuffer_); - pbuffer_ = pbuffer; - - pwriter_ = pwriter; - pframe_end_ = pbuffer_; - pcommit_end_ = pbuffer_; - pbuffer_end_ = pbuffer_ + max_capacity; -} - -output_buffer::~output_buffer() -{ - std::free(pbuffer_); -} - -// FIXME I think this code is wrong. Review and check it against the invariants -// regarding error state etc. -void output_buffer::write(void const* buf, std::size_t count) -{ - // TODO this could be smarter by writing from the client-provided - // buffer instead of copying the data. - auto const buffer_size = pbuffer_end_ - pbuffer_; - - char const* pinput = static_cast(buf); - auto remaining_input = count; - auto available_buffer = static_cast(pbuffer_end_ - pcommit_end_); - while(true) { - if(likely(remaining_input <= available_buffer)) - break; - std::memcpy(pcommit_end_, pinput, available_buffer); - pinput += available_buffer; - remaining_input -= available_buffer; - available_buffer = buffer_size; - pcommit_end_ = pbuffer_end_; - RECKLESS_TRACE(output_buffer_full_event); - - increment_output_buffer_full_count(); - flush(); - } - - std::memcpy(pcommit_end_, pinput, remaining_input); - pcommit_end_ += remaining_input; -} - -void output_buffer::flush() -{ - using namespace reckless::detail; - RECKLESS_TRACE(flush_output_buffer_start_event); - - // TODO keep track of a high watermark, i.e. max value of pcommit_end_. - // Clear every second or some such. Use madvise to release unused memory. - - // If there is a temporary error for long enough that the buffer gets full - // and we have to throw away data, but we resume writing later, then we do - // not want to end up with half-written frames in the middle of the log - // file. So, we only write data up until the end of the last complete input - // frame. - std::size_t remaining = pframe_end_ - pbuffer_; - atomic_store_relaxed(&output_buffer_high_watermark_, - std::max(output_buffer_high_watermark_, remaining)); - unsigned block_time_ms = 0; - while(true) { - std::error_code error; - std::size_t written; - if(remaining == 0) { - RECKLESS_TRACE(flush_output_buffer_finish_event); - return; - } - try { - // FIXME the crash mentioned below happens if you have g_log as a - // global object and have a writer with local scope (e.g. in - // main()), *even if you do not write to the log after the writer - // goes out of scope*, because there can be stuff lingering in the - // async queue. This makes the error pretty obscure, and we should - // guard against it. Perhaps by taking the writer as a shared_ptr, - // or at least by leaving a huge warning in the documentation. - - // NOTE if you get a crash here, it could be because your log object has a - // longer lifetime than the writer (i.e. the writer has been destroyed - // already). - written = pwriter_->write(pbuffer_, remaining, error); - } catch(...) { - // It is a fatal error for the writer to throw an exception, - // because we can't tell how much data was written to the target - // before the exception occurred. Errors should be reported via the - // error code parameter. - // TODO assign a more specific error code to this so the client can - // know what went wrong. - error.assign(writer::permanent_failure, writer::error_category()); - written = 0; - } - if(likely(!error)) - assert(written == remaining); // A successful writer must write *all* data. - else - assert(written <= remaining); // A failing writer may write no data, some data, or all data (but no more than that). - - // Discard the data that was written, preserve data that remains. We could - // avoid this copy by using a circular buffer, but in practice it should be - // rare to have any data remaining in the buffer. It only happens if the - // buffer fills up entirely (forcing us to flush in the middle of a frame) - // or if there is an error in the writer. - // TODO On the other hand when the buffer does fill up, that's when we are under - // the highest load. Shouldn't we perform as efficiently as possible then? - std::size_t remaining_data = (pcommit_end_ - pbuffer_) - written; - std::memmove(pbuffer_, pbuffer_+written, remaining_data); - pframe_end_ -= written; - pcommit_end_ -= written; - - if(likely(!error)) { - error_code_.clear(); - atomic_store_release(&error_flag_, false); - if(likely(!lost_input_frames_)) { - RECKLESS_TRACE(flush_output_buffer_finish_event); - return; - } else { - // Frames were discarded because of earlier errors in - // notify_on_recovery mode. Now that the writer is working and - // there is space in the buffer, we can notify the callback - // function about lost frames. - // - // The callback may put additional data in the output buffer. To - // ensure that we do not leave data hanging around indefinitely, - // we need to make sure that the extra data is also written. - // This is particularly important when we're flushing as part of - // shutting down the logger, as we could lose output if we don't - // flush all of it. - // - // To accomplish the additional writes we fall through after - // invoking the callback, and allow control to flow to the top - // of the while loop and issue another write. - auto lif = lost_input_frames_; - lost_input_frames_ = 0; - writer_error_callback_t callback; - { - std::lock_guard lk(writer_error_callback_mutex_); - callback = writer_error_callback_; - } - if(callback) { - try { - callback(this, initial_error_, lif); - } catch(...) { - // It's an error for the callback to throw an exception. - assert(false); - } - initial_error_.clear(); - frame_end(); - } - remaining = pframe_end_ - pbuffer_; // Update byte-remaining count. - } - } else { - error_policy ep; - if(error == writer::temporary_failure) - ep = temporary_error_policy_.load(std::memory_order_relaxed); - else - ep = permanent_error_policy_.load(std::memory_order_relaxed); - - switch(ep) { - case error_policy::ignore: - throw flush_error(error); - case error_policy::notify_on_recovery: - // We will notify the client about this once the writer - // starts working again. - if(!initial_error_) - initial_error_ = error; - throw flush_error(error); - case error_policy::block: - // To give the client the appearance of blocking, we need to - // poll the writer, i.e. check periodically whether writing is - // now working, until it starts working again. We don't remove - // anything from the input queue while this happens, hence any - // client threads that are writing log events will start - // blocking once the input queue fills up. We use - // shared_input_queue_full_event_ for an exponentially - // increasing wait time between polls. That way we can check - // the panic-flush flag early, which will be set in case the - // program crashes. - // - // If the program crashes while the writer is failing (not an - // unlikely scenario since circumstances are already ominous), - // then we have a dilemma. We could keep on blocking, but then - // we are withholding a crashing program from generating a core - // dump until the writer starts working. Or we could just throw - // the input queue away and pretend we're done with the panic - // flush, so the program can die in peace. But then we will - // lose log data that might be vital to determining the cause - // of the crash. I've chosen the latter option, because I think - // it's not likely that the log data will ever make it past the - // writer anyway, even if we do keep on blocking. - shared_input_queue_full_event_.wait(block_time_ms); - if(atomic_load_relaxed(&panic_flush_)) - throw flush_error(error); - block_time_ms += std::max(1u, block_time_ms/4); - block_time_ms = std::min(block_time_ms, 1000u); - break; - case error_policy::fail_immediately: - if(!error_flag_) { - error_code_ = error; - atomic_store_release(&error_flag_, true); - } - throw flush_error(error); - } - } - } -} - -char* output_buffer::reserve_slow_path(std::size_t size) -{ - std::size_t frame_size = (pcommit_end_ - pframe_end_) + size; - std::size_t buffer_size = pbuffer_end_ - pbuffer_; - if(likely(frame_size <= buffer_size)) { - } else { - throw excessive_output_by_frame(); - } - - increment_output_buffer_full_count(); - flush(); - return pcommit_end_; -} - -} // namespace reckless diff --git a/external/reckless/reckless/src/platform.cpp b/external/reckless/reckless/src/platform.cpp deleted file mode 100644 index 2f659275..00000000 --- a/external/reckless/reckless/src/platform.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#if defined(__unix__) -#include // pthread_setname_np, pthread_self -#endif -#if defined(__linux__) -#include // sysconf -#endif -#if defined(_WIN32) -#include // GetSystemInfo -#endif - -namespace reckless { -namespace detail { - -unsigned get_page_size() -{ -#if defined(__linux__) - long sz = sysconf(_SC_PAGESIZE); - return static_cast(sz); -#elif defined(_WIN32) - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; -#else - static_assert(false, "get_page_size() is not implemented for this OS"); -#endif -} - -void set_thread_name(char const* name) -{ -#if defined(__unix__) - pthread_setname_np(pthread_self(), name); - -#elif defined(_WIN32) - // How to: Set a Thread Name in Native Code - // https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx - - DWORD const MS_VC_EXCEPTION = 0x406D1388; -#pragma pack(push,8) - struct THREADNAME_INFO - { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - }; -#pragma pack(pop) - - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = static_cast(-1); - info.dwFlags = 0; - __try{ - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), - (ULONG_PTR*)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER){ - } -#endif // _WIN32 -} - - -unsigned const page_size = get_page_size(); - -} // detail -} // reckless diff --git a/external/reckless/reckless/src/policy_log.cpp b/external/reckless/reckless/src/policy_log.cpp deleted file mode 100644 index 5837f432..00000000 --- a/external/reckless/reckless/src/policy_log.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -RECKLESS_TLS unsigned reckless::scoped_indent::level_ = 0; diff --git a/external/reckless/reckless/src/spsc_event_win32.cpp b/external/reckless/reckless/src/spsc_event_win32.cpp deleted file mode 100644 index 72746cd5..00000000 --- a/external/reckless/reckless/src/spsc_event_win32.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#include - -namespace reckless { -namespace detail { - -spsc_event::spsc_event() : handle_(CreateEvent(NULL, FALSE, FALSE, NULL)) -{ - if(handle_ == NULL) - throw std::bad_alloc(); -} - -spsc_event::~spsc_event() -{ - CloseHandle(handle_); -} - -} -} \ No newline at end of file diff --git a/external/reckless/reckless/src/template_formatter.cpp b/external/reckless/reckless/src/template_formatter.cpp deleted file mode 100644 index 3d137fc1..00000000 --- a/external/reckless/reckless/src/template_formatter.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include - -#include -#include -#include - -namespace reckless { -namespace { - - unsigned atou(char const*& s) - { - unsigned v = *s - '0'; - ++s; - if(!isdigit(*s)) - return v; - v = 10*v + *s - '0'; - ++s; - while(isdigit(*s)) { - v = 10*v + *s - '0'; - ++s; - } - return v; - } - - bool isdigit(char c) - { - return c >= '0' && c <= '9'; - } - - // TODO for people writing custom format functions, it would be nice to - // have access to this. Also being able to just skip past the conversion - // specification so they can check the format character. - char const* parse_conversion_specification(conversion_specification* pspec, char const* pformat) - { - bool left_justify = false; - bool alternative_form = false; - bool show_plus_sign = false; - bool blank_sign = false; - bool pad_with_zeroes = false; - while(true) { - char flag = *pformat; - if(flag == '-') - left_justify = true; - else if(flag == '+') - show_plus_sign = true; - else if (flag == ' ') - blank_sign = true; - else if(flag == '#') - alternative_form = true; - else if(flag == '0') - pad_with_zeroes = true; - else - break; - ++pformat; - } - - pspec->minimum_field_width = isdigit(*pformat)? atou(pformat) : 0; - if(*pformat == '.') { - ++pformat; - pspec->precision = isdigit(*pformat)? atou(pformat) : UNSPECIFIED_PRECISION; - } else { - pspec->precision = UNSPECIFIED_PRECISION; - } - if(show_plus_sign) - pspec->plus_sign = '+'; - else if(blank_sign) - pspec->plus_sign = ' '; - else - pspec->plus_sign = 0; - - pspec->left_justify = left_justify; - pspec->alternative_form = alternative_form; - pspec->pad_with_zeroes = pad_with_zeroes; - pspec->uppercase = false; - - return pformat; - } - - template - char const* generic_format_int(output_buffer* pbuffer, char const* pformat, T v) - { - conversion_specification spec; - pformat = parse_conversion_specification(&spec, pformat); - char f = *pformat; - if(f == 'd') { - itoa_base10(pbuffer, v, spec); - return pformat + 1; - } else if(f == 'x') { - spec.uppercase = false; - itoa_base16(pbuffer, v, spec); - return pformat + 1; - } else if(f == 'X') { - spec.uppercase = true; - itoa_base16(pbuffer, v, spec); - return pformat + 1; - } else if(f == 'b') { - // TODO - return nullptr; - } else { - return nullptr; - } - } - - template - char const* generic_format_float(output_buffer* pbuffer, char const* pformat, T v) - { - conversion_specification cs; - pformat = parse_conversion_specification(&cs, pformat); - char f = *pformat; - if(f != 'f') - return nullptr; - - ftoa_base10_f(pbuffer, static_cast(v), cs); - return pformat + 1; - } - - template - char const* generic_format_char(output_buffer* pbuffer, char const* pformat, T v) - { - char f = *pformat; - if(f == 's') { - char* p = pbuffer->reserve(1); - *p = static_cast(v); - pbuffer->commit(1); - return pformat + 1; - } else { - return generic_format_int(pbuffer, pformat, static_cast(v)); - } - } - -} // anonymous namespace - -char const* format(output_buffer* pbuffer, char const* pformat, char v) -{ - return generic_format_char(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, signed char v) -{ - return generic_format_char(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, unsigned char v) -{ - return generic_format_char(pbuffer, pformat, v); -} - -//char const* format(output_buffer* pbuffer, char const* pformat, wchar_t v); -//char const* format(output_buffer* pbuffer, char const* pformat, char16_t v); -//char const* format(output_buffer* pbuffer, char const* pformat, char32_t v); - -char const* format(output_buffer* pbuffer, char const* pformat, short v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, unsigned short v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, int v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, unsigned int v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, long v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, unsigned long v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, long long v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, unsigned long long v) -{ - return generic_format_int(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, float v) -{ - return generic_format_float(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, double v) -{ - return generic_format_float(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, long double v) -{ - return generic_format_float(pbuffer, pformat, v); -} - -char const* format(output_buffer* pbuffer, char const* pformat, char const* v) -{ - char c = *pformat; - if(c =='s') { - auto len = std::strlen(v); - char* p = pbuffer->reserve(len); - std::memcpy(p, v, len); - pbuffer->commit(len); - } else if(c == 'p') { - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 1; - cs.plus_sign = 0; - cs.left_justify = false; - cs.alternative_form = true; - cs.pad_with_zeroes = false; - cs.uppercase = false; - itoa_base16(pbuffer, reinterpret_cast(v), cs); - } else { - return nullptr; - } - - return pformat + 1; -} - -char const* format(output_buffer* pbuffer, char const* pformat, std::string const& v) -{ - if(*pformat != 's') - return nullptr; - auto len = v.size(); - char* p = pbuffer->reserve(len); - std::memcpy(p, v.data(), len); - pbuffer->commit(len); - return pformat + 1; -} - -char const* format(output_buffer* pbuffer, char const* pformat, void const* p) -{ - char c = *pformat; - if(c != 'p' && c !='s') - return nullptr; - - conversion_specification cs; - cs.minimum_field_width = 0; - cs.precision = 1; - cs.plus_sign = 0; - cs.left_justify = false; - cs.alternative_form = true; - cs.pad_with_zeroes = false; - cs.uppercase = false; - - itoa_base16(pbuffer, reinterpret_cast(p), cs); - return pformat+1; -} - -void template_formatter::append_percent(output_buffer* pbuffer) -{ - auto p = pbuffer->reserve(1u); - *p = '%'; - pbuffer->commit(1u); -} - -char const* template_formatter::next_specifier(output_buffer* pbuffer, - char const* pformat) -{ - while(true) { - char const* pspecifier = pformat; - char c = *pspecifier; - while(c != '%' && c != '\0') - c = *(++pspecifier); - - auto len = pspecifier - pformat; - auto p = pbuffer->reserve(len); - std::memcpy(p, pformat, len); - pbuffer->commit(len); - if(*pspecifier == '\0') - return nullptr; - - pformat = pspecifier + 1; - - if(*pformat != '%') - return pformat; - - // Found "%%". Add a single '%' and continue. - ++pformat; - append_percent(pbuffer); - } -} - -void template_formatter::format(output_buffer* pbuffer, char const* pformat) -{ - // There are no remaining arguments to format, so we will ignore additional - // format specifications that might occur in the format string. However, we - // still need to treat "%%" as "%". We'll iterate over on next_specifier - // and when it finds a format specifier, we append a '%' and move on. - while((pformat = next_specifier(pbuffer, pformat)) != nullptr) - append_percent(pbuffer); -} - -} // namespace reckless diff --git a/external/reckless/reckless/src/trace_log.cpp b/external/reckless/reckless/src/trace_log.cpp deleted file mode 100644 index 82142c53..00000000 --- a/external/reckless/reckless/src/trace_log.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef RECKLESS_ENABLE_TRACE_LOG -#include -namespace reckless { -namespace detail { -trace_log g_trace_log(128*1024*1024); -} -} - -#endif diff --git a/external/reckless/reckless/src/unit_test.hpp b/external/reckless/reckless/src/unit_test.hpp deleted file mode 100644 index a71ed385..00000000 --- a/external/reckless/reckless/src/unit_test.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifdef UNIT_TEST - -#include // size_t -#include -#include -#include -#include // cout, endl -#include // logic_error -#include // ostringstream - -namespace unit_test { - -extern char const* g_current_testcase; -struct no_context -{ -}; - -template -class test { -public: - test(char const* name, void (Context::*ptest_function)()) : - name_(name), - ptest_function_(ptest_function) - { - } - void operator()(Context& ctx) - { - (ctx.*ptest_function_)(); - } - - char const* name() const - { - return name_; - } - -private: - char const* name_; - void (Context::*ptest_function_)(); -}; - -template <> -class test { -public: - test(char const* name, void (*ptest_function)()) : - name_(name), - ptest_function_(ptest_function) - { - } - void operator()(no_context&) - { - (*ptest_function_)(); - } - - char const* name() const - { - return name_; - } - -private: - char const* name_; - void (*ptest_function_)(); -}; - -class suite_base { -public: - virtual ~suite_base() = 0; - virtual void operator()() = 0; - virtual std::size_t succeeded() const = 0; - virtual std::size_t count() const = 0; -}; - -inline suite_base::~suite_base() {} - -inline std::vector& get_test_suites() -{ - static std::vector test_suites; - return test_suites; -} - -inline void register_suite(suite_base* psuite) -{ - get_test_suites().push_back(psuite); -} - -template -class suite : public suite_base { -public: - suite(std::initializer_list> tests) : - tests_(tests), - succeeded_(0) - { - register_suite(this); - } - - void operator()() override - { - Context c; - for(test& t : tests_) { - g_current_testcase = t.name(); - std::cout << t.name() << std::endl; - try { - t(c); - } catch(std::exception const& e) { - std::cout << " " << e.what() << std::endl; - continue; - } - ++succeeded_; - } - } - - std::size_t succeeded() const override - { - return succeeded_; - } - std::size_t count() const override - { - return tests_.size(); - } - -private: - std::vector> tests_; - std::size_t succeeded_; -}; - -inline int run() -{ - std::size_t succeeded = 0; - for(suite_base* psuite : get_test_suites()) { - (*psuite)(); - if(psuite->succeeded() == psuite->count()) - ++succeeded; - } - return 0; -} - -class error : public std::logic_error { -public: - error(std::string const& expression, char const* file, unsigned line) : - logic_error(make_what(expression, file, line)), - expression_(expression), - file_(file), - line_(line) - { - } - -private: - static std::string make_what(std::string const& expression, char const* file, unsigned line) - { - std::ostringstream ostr; - ostr << file << '(' << line << "): error in \"" << g_current_testcase << "\": test " << expression << " failed"; - return ostr.str(); - } - - std::string expression_; - char const* file_; - unsigned line_; -}; - -#define UNIT_TEST_MAIN() \ -namespace unit_test { \ -char const* g_current_testcase; \ -} \ -int main() \ -{ \ - return unit_test::run(); \ -} \ -int main() - - - -#define TEST(a) if(a) {} else throw unit_test::error(#a, __FILE__, __LINE__) -#define TESTCASE(name) {#name, &name} - -} // namespace unit_test -#endif diff --git a/external/reckless/reckless/src/writer.cpp b/external/reckless/reckless/src/writer.cpp deleted file mode 100644 index 9a1a435d..00000000 --- a/external/reckless/reckless/src/writer.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* This file is part of reckless logging - * Copyright 2015-2020 Mattias Flodin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -namespace reckless { - -namespace { -class error_category_t : public std::error_category { -public: - char const* name() const noexcept override; - std::error_condition default_error_condition(int code) const noexcept override; - std::string message(int condition) const override; -}; - -} // anonymous namespace - -writer::~writer() -{ -} - -std::error_category const& writer::error_category() -{ - static error_category_t ec; - return ec; -} - -char const* error_category_t::name() const noexcept -{ - return "reckless::writer"; -} - -std::error_condition error_category_t::default_error_condition(int code) const noexcept -{ - return static_cast(code); -} - -std::string error_category_t::message(int condition) const -{ - switch(static_cast(condition)) { - case writer::temporary_failure: - return "temporary failure while writing log"; - case writer::permanent_failure: - return "permanent failure while writing log"; - } - throw std::invalid_argument("invalid condition code"); -} - -} // namespace reckless diff --git a/external/reckless/reckless/unittest/.gitignore b/external/reckless/reckless/unittest/.gitignore deleted file mode 100644 index d474ce77..00000000 --- a/external/reckless/reckless/unittest/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/unit_test diff --git a/external/reckless/reckless/unittest/_Tupfile b/external/reckless/reckless/unittest/_Tupfile deleted file mode 100644 index 5c3a8216..00000000 --- a/external/reckless/reckless/unittest/_Tupfile +++ /dev/null @@ -1,4 +0,0 @@ -include_rules -CXXFLAGS += -DUNIT_TEST -Wno-strict-aliasing -: foreach ../src/*.cpp |> !cxx |> %B.o -: *.o | $(RECKLESS_LIB)/libreckless.a $(PERFORMANCE_LOG_LIB)/libperformance_log.a |> !ld |> unit_test