Compare commits

...

177 Commits
v1 ... main

Author SHA1 Message Date
Brett 949b2841f4 add const 2025-04-26 22:38:17 -04:00
Brett c04383b471 tested concrete types. everything works 2025-04-26 16:35:56 -04:00
Brett 5981a244c2 nope 2025-04-26 00:40:39 -04:00
Brett 5aacd7df9d make visitor + tests 2025-04-25 00:21:40 -04:00
Brett fdc1fbf3ca things seem to work. need to clean code. 2025-04-24 00:07:02 -04:00
Brett 1420dca957 partially working 2025-04-23 16:54:00 -04:00
Brett 84921fffa7 tuples! 2025-04-23 15:04:37 -04:00
Brett 27c07594a8 misses 2025-04-22 23:46:01 -04:00
Brett a9a3e1bf43 variants closer to working 2025-04-22 21:05:44 -04:00
Brett 8f3cbcb2fd partial variant 2025-04-22 00:41:16 -04:00
Brett f0e9475dcc silly boy 2025-04-21 15:00:57 -04:00
Brett 8d3a088049 more types 2025-04-21 13:12:22 -04:00
Brett 7e68950a16 Merge remote-tracking branch 'refs/remotes/origin/main' 2025-04-21 01:23:49 -04:00
Brett 606def7c10 some variant detail 2025-04-21 01:23:39 -04:00
Brett bc34be9496 take args 2025-04-20 20:46:12 -04:00
Brett e5a3c9d669 why is it still virtual 2025-04-20 16:20:50 -04:00
Brett fb092422a8 this variant is funny 2025-04-19 22:26:25 -04:00
Brett f245b7531e basic custom variant fixing some issues with the STL. currently unstable and untested. 2025-04-19 20:36:51 -04:00
Brett 2bac310e55 silly variant 2025-04-19 17:57:53 -04:00
Brett dcd4bf17ee more config options 2025-04-19 16:38:57 -04:00
Brett 57ddcafcda config changes 2025-04-19 16:29:16 -04:00
Brett a1bc8cf1c2 meow 2025-04-17 20:48:54 -04:00
Brett 90cf177c57 fix curl requirement 2025-04-17 02:18:49 -04:00
Brett 6cdfab39cf hi 2025-04-17 01:20:00 -04:00
Brett 6161d9b794 uwu 2025-04-12 18:27:48 -04:00
Brett b6fc170399 can't seem to use fetch api 2025-04-11 16:27:55 -04:00
Brett e2dc35fea9 requests feature with cURL and emscript 2025-04-11 16:05:28 -04:00
Brett c241085afb todo emscript for ptr_storage 2025-04-11 15:16:54 -04:00
Brett 1b4ad25bcf silly 2025-04-11 14:44:45 -04:00
Brett 78c219cc67 io changes 2025-04-11 12:42:38 -04:00
Brett 09d1a82268 changes 2025-04-09 01:14:07 -04:00
Brett 322a533fd9 fix default nix 2025-04-07 02:05:42 -04:00
Brett 2d9b96f115 silly 2025-04-06 22:05:00 -04:00
Brett 3cdceda227 more silly 2025-04-06 20:29:38 -04:00
Brett 4c3e3951b3 silly 2025-04-06 20:28:14 -04:00
Brett 3f83c04b8c otel 2025-04-06 17:27:45 -04:00
Brett 685753b217 Merge remote-tracking branch 'origin' 2025-04-06 17:23:37 -04:00
Brett 0fed009bdf otel 2025-04-06 17:23:31 -04:00
Brett 729a16ab57 logging? 2025-04-03 00:42:09 -04:00
Brett 284743c683 add range to f_equal 2025-04-02 18:33:47 -04:00
Brett 8b23715ddd logger patch hack 2025-04-01 19:58:06 -04:00
Brett 0ebbc198c5 fix bump 2025-03-31 17:53:18 -04:00
Brett 4f9f61d63a silly 2025-03-31 17:52:04 -04:00
Brett 9a05c86b02 length function 2025-03-25 19:30:35 -04:00
Brett 8922a9e78c silly bounding boxes 2025-03-25 18:37:39 -04:00
Brett 79148a8506 compairsion operators on vectors 2025-03-19 19:38:29 -04:00
Brett ebf0a80774 fix empty log 2025-03-13 19:44:17 -04:00
Brett 2822522484 zip needs limits now? 2025-03-13 14:58:11 -04:00
Brett 24de97acdd stupid error 2025-03-13 14:57:16 -04:00
Tri11Paragon f8cf71e152 add flatten to iterator 2025-03-13 13:59:23 -07:00
Tri11Paragon 0bd3519e7e fix for windows 2025-03-13 13:47:44 -07:00
Tri11Paragon 19f9dead27 Merge commit '6d44477' 2025-03-13 13:06:37 -07:00
Tri11Paragon 6d44477958 add msvc check 2025-03-13 13:06:18 -07:00
Brett 2f8a0bba91 fix missing logging depend 2025-03-12 23:57:08 -04:00
Brett 4fab2595bc fix argparse help 2025-03-12 23:26:44 -04:00
Brett 04a5c01704 const 2025-03-12 18:34:04 -04:00
Brett 8416b69f5d contains is const 2025-03-12 18:33:51 -04:00
Brett e8b6210034 better status 2025-03-12 18:14:10 -04:00
Brett afab2f8043 status bounds check 2025-03-12 18:13:33 -04:00
Brett 0890663f7a add flush 2025-03-12 17:30:49 -04:00
Brett 363ce6a5db silly 2025-03-12 16:26:13 -04:00
Brett a968a285e7 status 2025-03-12 12:19:19 -04:00
Brett 8c88c6296f screen size 2025-03-11 00:23:05 -04:00
Brett 6bcb5ff2f5 part 2025-03-10 23:54:44 -04:00
Brett 767bd95fbe parker is a stupid faggot 2025-03-10 17:42:49 -04:00
Brett 3f59528ecc silly 2025-03-10 15:51:38 -04:00
Brett e885df622a injectors 2025-03-10 11:27:04 -04:00
Brett 2b1086ee15 injectors 2025-03-10 10:27:08 -04:00
Brett fa0e70bda4 minor change 2025-03-10 00:41:35 -04:00
Brett 78a4ad9f39 i think all the old logging is replaced 2025-03-10 00:29:20 -04:00
Brett deacad5358 stupid work log stuff 2025-03-09 23:14:27 -04:00
Brett b334bbcae4 minor fixes, all files now use new logging libray, expect things to be broken! 2025-03-09 23:04:41 -04:00
Brett be46e8552b logging library is done 2025-03-09 22:47:49 -04:00
Brett 60536fd602 i think logging is working. need to make macros and test 2025-03-09 15:06:31 -04:00
Brett 07a11656fa config works 2025-03-07 18:40:43 -05:00
Brett 66efccf095 silly cable 2025-03-07 16:42:26 -05:00
Brett dbff8fd8b3 thread safe wrappers + logging config + time rotation 2025-03-07 14:55:08 -05:00
Brett 83df06f1c0 file writers 2025-03-07 00:43:54 -05:00
Brett f1027d33a4 starting on bounded writers 2025-03-06 20:41:18 -05:00
Brett f15ce17215 silly little boxer 2025-03-06 18:54:05 -05:00
Brett 29ffdaaacd write strings 2025-03-06 15:33:35 -05:00
Brett dad09f5f1b free fun 2025-03-06 15:15:25 -05:00
Brett c4a29ade19 ansi test 2025-03-06 01:09:44 -05:00
Brett 08e19c067e ansi 2025-03-05 01:55:14 -05:00
Brett fcbc6b947d working on ansi 2025-03-04 22:02:51 -05:00
Brett 1e30544cff print statements 2025-03-04 16:17:16 -05:00
Brett 637b4fa0e6 nice error messages 2025-03-04 13:58:14 -05:00
Brett 4e6863aafa streamable testing 2025-03-04 11:35:23 -05:00
Brett a1c1f51cb4 parser fix again 2025-03-04 11:17:05 -05:00
Brett a2a60b18b5 fix issue in parser 2025-03-04 11:05:59 -05:00
Brett 25187319ab alternative form 2025-03-04 01:26:33 -05:00
Brett b2c3820ed0 basic logger works 2025-03-04 00:44:48 -05:00
Brett 74878d6b43 silly code 2025-03-03 20:36:10 -05:00
Brett 4ac592beca minor work on parser 2025-03-03 15:44:42 -05:00
Brett 0490f50e3c tokenizer, basic logging works 2025-03-03 01:52:20 -05:00
Brett ff2d77a1cd this is a breaking change (logging) 2025-03-02 21:21:19 -05:00
Brett dd030a9b5b silly code 2025-03-01 21:56:57 -05:00
Brett 21f1e66bff logging files 2025-03-01 20:44:25 -05:00
Brett be721a4e52 slight change, quotes around strings 2025-02-28 18:08:52 -05:00
Brett ecd3d1a701 i think argparse is done 2025-02-28 18:08:20 -05:00
Brett 8902e17b40 fix issue with compiling on clang, as_const now works with tuple types 2025-02-28 17:10:47 -05:00
Brett bac63ae815 flatten, and flatten recursive. as_const doesn't work. 2025-02-28 12:39:13 -05:00
Brett e48fdf0c86 flatten partially works 2025-02-28 02:29:30 -05:00
Brett d8f943ba62 chhange 2025-02-27 15:19:50 -05:00
Brett 58ea39be08 i am now working on flatten 2025-02-27 15:17:59 -05:00
Brett 6935ae4785 this doesn't work rn 2025-02-27 01:53:22 -05:00
Brett 17e6b507ea parsing 2025-02-25 16:29:36 -05:00
Brett 0913208e6b help with choices 2025-02-25 12:34:14 -05:00
Brett 1b6d23fad4 more help 2025-02-25 02:14:19 -05:00
Brett fc27d7503a some help 2025-02-24 22:22:21 -05:00
Brett 34184d46a3 need to update add 2025-02-24 18:28:38 -05:00
Brett 7129c929eb some update 2025-02-23 23:41:34 -05:00
Brett 8b03dda1fe some help 2025-02-22 18:39:51 -05:00
Brett f8ed21fda5 more tests 2025-02-22 13:38:36 -05:00
Brett 30f975e165 more tests, i think most things work now? 2025-02-21 16:37:05 -05:00
Brett f403e8a69b minor work 2025-02-20 23:19:49 -05:00
Brett e8e891d4fc a few more tests 2025-02-20 00:50:49 -05:00
Brett 389762e2ae positionals are broken because im silly 2025-02-20 00:47:27 -05:00
Brett 188e9dba88 postionals may work now? 2025-02-19 20:52:55 -05:00
Brett 5f9ea32671 idk what changed 2025-02-19 16:29:17 -05:00
Brett c23759ac6d fix a lot of bugs, current testing works 2025-02-19 13:28:58 -05:00
Brett 56611d5aef begin testing, flag work. something is really broken though 2025-02-19 02:26:31 -05:00
Brett e0d36269bf i made some changes 2025-02-18 01:32:26 -05:00
Brett 96e5343d02 more work on argparse 2025-02-18 00:46:30 -05:00
Brett 7dc19efbaa more metaprogramming fun 2025-02-17 21:43:09 -05:00
Brett 735371b7bd silly works now i need to bed" 2025-02-17 02:20:40 -05:00
Brett fe6ce712e7 partial solution 2025-02-17 01:56:27 -05:00
Brett a78ad58479 gotta think of a way of handling the whole "templates are silly" thing 2025-02-17 01:47:42 -05:00
Brett 6e5caf3ac5 good work on argparse 2025-02-16 23:22:00 -05:00
Brett d7373ac832 more argparse work 2025-02-13 17:47:27 -05:00
Brett 31b28c7787 usages in subparsers 2025-02-13 14:04:39 -05:00
Brett 89d95dfec4 working on subparsers 2025-02-13 13:59:59 -05:00
Brett 44a57e5ec2 i am tired 2025-02-13 01:53:21 -05:00
Brett 0b2dad0dda forgot about . in files 2025-02-12 22:18:15 -05:00
Brett 174b46ae94 path_helper file provides base_name of a path. Argparse working on 2025-02-12 22:17:04 -05:00
Brett 457dd5203b hi 2025-02-12 19:42:50 -05:00
Brett a437935ab0 Argparse v2 breaking change 2025-02-12 15:43:54 -05:00
Brett 3726f6840f starting arge parse 2025-02-12 02:54:22 -05:00
Brett 02b8f54c7e expected should inherit from itself when not copy constructable 2025-01-28 00:35:08 -05:00
Brett eb73a6e189 revert change that breaks graphics lib 2025-01-28 00:00:47 -05:00
Brett baa5952666 decltype(auto) on the select method 2025-01-21 21:10:10 -05:00
Brett 74c1010118 fix missing paren 2025-01-17 14:48:50 -05:00
Brett 28cbc89840 hi 2025-01-17 14:30:55 -05:00
Brett d436f1b93a mem test 2025-01-17 12:19:06 -05:00
Brett 1aa7f12c0f fix ptr stuff 2025-01-16 19:47:13 -05:00
Brett 41c6dea002 ptr stuff seems to wrok 2025-01-16 19:33:12 -05:00
Brett 2e6abb8013 breaking changes to imports, adding pointer helpers 2025-01-16 15:24:08 -05:00
Brett eca09acc01 static function checks 2025-01-15 19:08:02 -05:00
Brett 4c462dff38 minor change 2025-01-13 14:43:32 -05:00
Brett 8133553ed8 make write profile work 2025-01-11 17:58:28 -05:00
Brett 9860845831 add zip for single iterator pairs 2025-01-07 13:51:18 -05:00
Brett c953ef3544 what's different? 2025-01-07 13:49:32 -05:00
Brett 1798980ac6 hash_value on vec 2024-12-10 00:33:04 -05:00
Brett d1a9aab859 make byte functions more concise 2024-12-09 16:00:08 -05:00
Brett d32b5d398a forgot a cast 2024-11-15 12:54:34 -05:00
Brett 0431106b7e vectors now use correct ops + ret is statically calculated now. vec_cast is a thing 2024-11-15 12:53:27 -05:00
Brett 1c931b2515 make this compile 2024-11-11 18:19:32 -05:00
Brett 05e5fcf7f1 binary tree 2024-11-11 16:47:18 -05:00
Brett 7bf24a11a5 initiailizer list now works for dynamic spans 2024-11-08 19:22:06 -05:00
Brett f7ef78f351 ranges now accept container 2024-11-08 19:03:29 -05:00
Tri11Paragon ea16aa3847 fixes for windows 2024-10-31 19:28:43 -07:00
Brett e81f590f5e string changes, add path seperator 2024-10-21 15:02:13 -04:00
Brett 3003e424e1 make enumerate use distance 2024-10-05 14:49:53 -04:00
Brett a9f5b9e97d very silly const 2024-10-05 14:48:04 -04:00
Brett 7656e43e85 data in matrix 2024-10-05 14:40:36 -04:00
Brett ce4e1807de make vectors actually have bipolar function 2024-10-05 01:13:24 -04:00
Brett dafbe6ea8b iterators with begin(), end(). vector / matrix bipolar 2024-10-05 01:11:11 -04:00
Brett ffeab29e5f streamable object to string function in logging 2024-10-04 14:51:27 -04:00
Brett 463e36e48d why 2024-10-04 13:47:10 -04:00
Brett 15e1b263a4 silly 2024-10-04 13:45:35 -04:00
Brett ff5ba4a667 types for compile 2024-10-04 13:41:27 -04:00
Brett 7fa4fd0af2 some ansi codes 2024-10-03 19:55:04 -04:00
Brett bbb48a6b8f not so clever maps 2024-10-02 00:32:18 -04:00
Brett ec02eb8419 iterators 2024-10-01 17:56:23 -04:00
Brett 2163baf57c mapping 2024-10-01 17:39:43 -04:00
Brett e7bfedd142 commit script patch 2024-10-01 16:49:25 -04:00
Brett f997305912 last change was a breaking change, sorry! 2024-10-01 16:46:28 -04:00
101 changed files with 11335 additions and 3628 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 1.1.8) set(BLT_VERSION 5.4.15)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)
@ -12,11 +12,15 @@ option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF) option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF)
option(BLT_LOGGING_THREAD_SAFE "Make sure logging is thread synced" ON)
option(BLT_DEBUG_OTEL "Use Open Telemetry for debugging" OFF)
option(BUILD_STD "Build the BLT standard utilities." ON) option(BUILD_STD "Build the BLT standard utilities." ON)
option(BUILD_PROFILING "Build the BLT profiler extension" ON) option(BUILD_PROFILING "Build the BLT profiler extension" ON)
option(BUILD_FS "Build the BLT FS utilities including the NBT + eNBT extension" ON) option(BUILD_FS "Build the BLT FS utilities including the NBT + eNBT extension" ON)
option(BUILD_PARSE "Build the BLT parsers" ON) option(BUILD_PARSE "Build the BLT parsers" ON)
option(BUILD_FORMAT "Build the BLT formatters" ON) option(BUILD_FORMAT "Build the BLT formatters" ON)
option(BUILD_LOGGING "Build the BLT logging utilities" ON)
option(BUILD_TESTS "Build the BLT test set" OFF) option(BUILD_TESTS "Build the BLT test set" OFF)
@ -29,6 +33,21 @@ option(BLT_DISABLE_WARN "Disable blt::logging BLT_WARN macro" OFF)
option(BLT_DISABLE_ERROR "Disable blt::logging BLT_ERROR macro" OFF) option(BLT_DISABLE_ERROR "Disable blt::logging BLT_ERROR macro" OFF)
option(BLT_DISABLE_FATAL "Disable blt::logging BLT_FATAL macro" OFF) option(BLT_DISABLE_FATAL "Disable blt::logging BLT_FATAL macro" OFF)
if (BLT_DEBUG_OTEL)
message(STATUS "Searching in path '${CMAKE_PREFIX_PATH}'")
find_package(absl REQUIRED)
find_package(protobuf REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(gRPC REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(prometheus-cpp CONFIG REQUIRED)
find_package(opentelemetry-cpp CONFIG REQUIRED)
endif ()
if (BLT_LOGGING_THREAD_SAFE)
add_compile_definitions(BLT_LOGGING_THREAD_SAFE)
endif ()
if(${BLT_DISABLE_STATS}) if(${BLT_DISABLE_STATS})
add_compile_definitions(BLT_DISABLE_STATS) add_compile_definitions(BLT_DISABLE_STATS)
endif () endif ()
@ -70,8 +89,14 @@ if (${BUILD_FORMAT})
message(STATUS "Building ${Yellow}format${ColourReset} cxx files") message(STATUS "Building ${Yellow}format${ColourReset} cxx files")
file(GLOB_RECURSE FORMAT_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/format/*.cpp") file(GLOB_RECURSE FORMAT_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/format/*.cpp")
else () else ()
set(FORMAT_FILES "" set(FORMAT_FILES "")
include/blt/std/iterator.h) endif ()
if (${BUILD_LOGGING})
message(STATUS "Building ${Yellow}logging${ColourReset} cxx files")
file(GLOB_RECURSE LOGGING_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/logging/*.cpp")
else ()
set(LOGGING_FILES "")
endif () endif ()
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
@ -83,6 +108,7 @@ endif ()
#include zlib if the user has it. #include zlib if the user has it.
find_package(ZLIB QUIET) find_package(ZLIB QUIET)
find_package(CURL QUIET)
if (${ZLIB_FOUND}) if (${ZLIB_FOUND})
include_directories(${ZLIB_INCLUDE_DIRS}) include_directories(${ZLIB_INCLUDE_DIRS})
@ -90,10 +116,17 @@ else ()
message("ZLIB was not found, this is fine however if you wish you use gzip with NBT it is required.") message("ZLIB was not found, this is fine however if you wish you use gzip with NBT it is required.")
endif () endif ()
if (${CURL_FOUND})
message(STATUS "Linking cURL!")
include_directories(${CURL_INCLUDE_DIRS})
else ()
message(STATUS "cURL not found, some library features will be disabled!")
endif ()
include_directories(include/) include_directories(include/)
include_directories(${CMAKE_CURRENT_BINARY_DIR}/config/) include_directories(${CMAKE_CURRENT_BINARY_DIR}/config/)
add_library(${BLT_TARGET} ${STD_FILES} ${PROFILING_FILES} ${FS_FILES} ${PARSE_FILES} ${FORMAT_FILES}) add_library(${BLT_TARGET} ${STD_FILES} ${PROFILING_FILES} ${FS_FILES} ${PARSE_FILES} ${FORMAT_FILES} ${LOGGING_FILES})
string(REPLACE "+" "\\+" escaped_source ${CMAKE_CURRENT_SOURCE_DIR}) string(REPLACE "+" "\\+" escaped_source ${CMAKE_CURRENT_SOURCE_DIR})
string(APPEND escaped_source "/src/blt/.*/") string(APPEND escaped_source "/src/blt/.*/")
@ -123,6 +156,11 @@ if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
target_include_directories(${BLT_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap) target_include_directories(${BLT_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
endif () endif ()
if (${CURL_FOUND})
target_include_directories(${BLT_TARGET} PUBLIC ${CURL_INCLUDE_DIRS})
target_link_libraries(${BLT_TARGET} PUBLIC ${CURL_LIBRARIES})
endif ()
message("BLT ${Yellow}${BLT_VERSION}${ColourReset} Successfully included!") message("BLT ${Yellow}${BLT_VERSION}${ColourReset} Successfully included!")
message("Installing to ${CMAKE_INSTALL_LIBDIR} with headers at ${CMAKE_INSTALL_INCLUDEDIR}") message("Installing to ${CMAKE_INSTALL_LIBDIR} with headers at ${CMAKE_INSTALL_INCLUDEDIR}")
@ -148,8 +186,9 @@ install(TARGETS ${BLT_TARGET}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
macro(blt_add_project name source type) macro(blt_add_test name source type)
message("Adding project ${name} of type ${type}" DEBUG)
project(${name}-${type}) project(${name}-${type})
add_executable(${name}-${type} ${source}) add_executable(${name}-${type} ${source})
@ -160,8 +199,10 @@ macro(blt_add_project name source type)
target_link_libraries(${name}-${type} PRIVATE BLT) target_link_libraries(${name}-${type} PRIVATE BLT)
target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) if (NOT MSVC)
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
endif()
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL}) target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
if (${TRACK_ALLOCATIONS}) if (${TRACK_ALLOCATIONS})
@ -194,9 +235,17 @@ endmacro()
if (${BUILD_TESTS}) if (${BUILD_TESTS})
message("Building tests for version ${BLT_VERSION}") message("Building tests for version ${BLT_VERSION}")
blt_add_project(blt-iterator tests/iterator_tests.cpp test) blt_add_test(blt_iterator tests/iterator_tests.cpp test)
blt_add_test(blt_argparse tests/argparse_tests.cpp test)
blt_add_test(blt_logging tests/logger_tests.cpp test)
blt_add_test(blt_variant tests/variant_tests.cpp test)
message("Built tests") message("Built tests")
endif () endif ()
project(BLT) project(BLT)
if (BLT_DEBUG_OTEL)
target_link_libraries(${BLT_TARGET} PUBLIC ${OPENTELEMETRY_CPP_LIBRARIES})
target_include_directories(${BLT_TARGET} PUBLIC ${OPENTELEMETRY_CPP_INCLUDE_DIRS})
endif ()

View File

@ -1,24 +1,50 @@
# **BLT v0.20** # **BLT v5.1**
A C++17 common utilities library to make thing easy! A C++17 common utilities library to make thing easy!
![Icon](icon_large.png) ![Icon](icon_large.png)
--- ---
# ***Features*** # Features
- ## blt/fs
- ### loader.h ## BLT Format
- std::string blt::fs::getFile(std::string_view path)
- Gets the entire file as a string. This module provides general string formatting utilities. *Note: this folder contains mostly outdated library files and will be updated in the future.*
- std::vector\<std::string> blt::fs::getLinesFromFile(std::string_view path)
- Gets the entire file as a string, then splits on the new line character. Then returns a vector of those lines ### Files
- std::vector\<std::string> blt::fs::recursiveInclude(std::string_view path, std::string include_header, std::vector<include_guard> guards);
- Recursively include in order based on the include string (include_header) marked by the include guards - **boxing.h**
- Defaults to C/C++/GLSL preprocessor style (Was designed for GLSL) Simple utility for drawing boxes around blocks of text.
- std::string blt::fs::loadBrainFuckFile(const std::string& path)
- Load a brainfuck file recursively, uses ~ to mark the include path - **format.h**
- ### nbt.h Legacy library file containing various utilities for creating formatted output. Also includes methods for writing Java UTF8 strings.
- probably needs to be remade (TODO)
## BLT Filesystem
This module provides helper classes for filesystem objects. It seeks to offer an interface that is simpler than the one provided by the standard library.
Specifically, the number of functions required to implement is significantly lower,
and the interface is generally cleaner. Eventually, this module aims to support various file formats,
such as Minecraft's NBT system. Currently, there is an existing NBT file, but it was written when I was first learning C++.
### Files
- **filesystem.h**
This is the base file which includes all other files. You should use the other options as this can be a heavy file to include
- **path_helper.h**
This file provides functions for interfacing with paths. Specifically, as of this moment it only provides an interface for getting the base name of a file path.
- **loader.h**
- `std::string blt::fs::getFile(std::string_view path)`
- Gets the entire file as a string.
- `std::vector\<std::string> blt::fs::getLinesFromFile(std::string_view path)`
- Gets the entire file as a string, then splits on the new line character. Then returns a vector of those lines
- `std::vector\<std::string> blt::fs::recursiveInclude(std::string_view path, std::string include_header, std::vector<include_guard> guards)`
- Recursively include in order based on the include string (include_header) marked by the include guards
- Defaults to C/C++/GLSL preprocessor style (Was designed for GLSL)
- `std::string blt::fs::loadBrainFuckFile(const std::string& path)`
- Load a brainfuck file recursively, uses ~ to mark the include path
- ### nbt.h
- probably needs to be remade (TODO)
- ## blt/math - ## blt/math
- ### averages.h - ### averages.h
- blt::averagizer_o_matic - blt::averagizer_o_matic

View File

@ -23,7 +23,9 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "GCC libs: ${Green}stdc++fs${ColourReset}") message(STATUS "GCC libs: ${Green}stdc++fs${ColourReset}")
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always) target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always)
target_link_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always) target_link_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -fdiagnostics-color=always)
target_link_options(${PROJECT_NAME} PUBLIC -rdynamic) if (NOT WIN32)
target_link_options(${PROJECT_NAME} PUBLIC -rdynamic)
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC stdc++fs) target_link_libraries(${PROJECT_NAME} PUBLIC stdc++fs)
include(GNUInstallDirs) include(GNUInstallDirs)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!python3
import subprocess import subprocess
import argparse import argparse
@ -199,6 +199,8 @@ def make_release(env: EnvData, name):
'X-GitHub-Api-Version': '2022-11-28' 'X-GitHub-Api-Version': '2022-11-28'
} }
for url in urls: for url in urls:
if not "github" in url:
continue
response = requests.post(url, headers=headers, data=json.dumps(data)) response = requests.post(url, headers=headers, data=json.dumps(data))
if response.status_code == 201: if response.status_code == 201:
print('Release created successfully!') print('Release created successfully!')

View File

@ -1,74 +0,0 @@
#!/usr/bin/python3
import subprocess
#---------------------------------------
# CONFIG
#---------------------------------------
VERSION_BEGIN_STR = "set(BLT_VERSION "
VERSION_END_STR = ")"
#---------------------------------------
# DO NOT TOUCH
#---------------------------------------
type = input("What kind of commit is this ((M)ajor, (m)inor, (p)atch)? ")
def load_cmake():
cmake_file = open("CMakeLists.txt", 'r')
cmake_text = cmake_file.read()
cmake_file.close()
return cmake_text
def write_cmake(cmake_text):
cmake_file = open("CMakeLists.txt", 'w')
cmake_file.write(cmake_text)
cmake_file.close()
def get_version(cmake_text):
begin = cmake_text.find(VERSION_BEGIN_STR) + len(find_text)
end = cmake_text.find(VERSION_END_STR, begin)
return (cmake_text[begin:end], begin, end)
def split_version(cmake_text):
version, begin, end = get_version(cmake_text)
version_parts = version.split('.')
return (version_parts, begin, end)
def recombine(cmake_text, version_parts, begin, end):
constructed_version = version_parts[0] + '.' + version_parts[1] + '.' + version_parts[2]
constructed_text_begin = cmake_text[0:begin]
constrcuted_text_end = cmake_text[end::]
return constructed_text_begin + constructed_version + constrcuted_text_end
def inc_major(cmake_text):
version_parts, begin, end = split_version(cmake_text)
version_parts[0] = str(int(version_parts[0]) + 1)
return recombine(cmake_text, version_parts, begin, end)
def inc_minor(cmake_text):
version_parts, begin, end = split_version(cmake_text)
version_parts[1] = str(int(version_parts[1]) + 1)
return recombine(cmake_text, version_parts, begin, end)
def inc_patch(cmake_text):
version_parts, begin, end = split_version(cmake_text)
version_parts[2] = str(int(version_parts[2]) + 1)
return recombine(cmake_text, version_parts, begin, end)
if type.startswith('M'):
print("Selected major")
write_cmake(inc_major(load_cmake()))
elif type.startswith('m'):
print("Selected minor")
write_cmake(inc_minor(load_cmake()))
elif type.startswith('p') or type.startswith('P') or len(type) == 0:
print("Selected patch")
write_cmake(inc_patch(load_cmake()))
#subprocess.call("./py_commit_helper.sh")
subprocess.call("git", "add", "*")
subprocess.call("git", "commit")
subprocess.call("sh -e 'git remote | xargs -L1 git push --all'")

44
default.nix Normal file
View File

@ -0,0 +1,44 @@
{ pkgs ? (import <nixpkgs> {
config.allowUnfree = true;
config.segger-jlink.acceptLicense = true;
}), ... }:
pkgs.mkShell
{
buildInputs = with pkgs; [
cmake
gcc
clang
emscripten
ninja
renderdoc
valgrind
gtest
opentelemetry-cpp
opentelemetry-cpp.dev
];
nativeBuildInputs = with pkgs; [
pkg-config
opentelemetry-cpp
opentelemetry-cpp.dev
];
propagatedBuildInputs = with pkgs; [
abseil-cpp
protobuf
grpc
prometheus-cpp
prometheus-cpp.dev
openssl
openssl.dev
opentelemetry-cpp
opentelemetry-cpp.dev
civetweb
civetweb.dev
c-ares
c-ares.dev
nlohmann_json
glibc
glibc.dev
curl
];
LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver-32/lib";
}

View File

@ -51,4 +51,12 @@
#error Filesystem ops not supported!\ #error Filesystem ops not supported!\
#endif #endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#define BLT_OS_WINDOWS
#elif defined(__linux__) || defined(__unix__)
#define BLT_OS_LINUX
#else
#define BLT_OS_UNKNOWN
#endif
#endif //BLT_COMPATIBILITY_H #endif //BLT_COMPATIBILITY_H

View File

@ -20,7 +20,7 @@
#define BLT_BOXING_H #define BLT_BOXING_H
#include <blt/std/types.h> #include <blt/std/types.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <string> #include <string>
namespace blt namespace blt
@ -84,25 +84,6 @@ namespace blt
Logger& logger; Logger& logger;
}; };
template<>
class log_box_t<blt::logging::logger> : detail::log_box_base_t
{
public:
log_box_t(blt::logging::logger logger, std::string_view title, blt::size_t padding = 0): detail::log_box_base_t(title, padding), logger(logger)
{
make_full_title(logger);
logger << '\n';
}
~log_box_t()
{
make_full_width_line(logger);
logger << '\n';
}
private:
blt::logging::logger logger;
};
template<typename Logger> template<typename Logger>
log_box_t(Logger&& logger, std::string_view, blt::size_t) -> log_box_t<Logger>; log_box_t(Logger&& logger, std::string_view, blt::size_t) -> log_box_t<Logger>;

View File

@ -0,0 +1,56 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_CSTDIO_WRAPPERS_H
#define BLT_FS_CSTDIO_WRAPPERS_H
#include <blt/fs/fwddecl.h>
namespace blt::fs
{
class file_reader_t final : public reader_t
{
public:
explicit file_reader_t(void* file): m_file(file)
{
}
i64 read(char* buffer, size_t bytes) override;
private:
void* m_file;
};
class file_writer_t final : public writer_t
{
public:
explicit file_writer_t(void* file): m_file(file)
{
}
i64 write(const char* buffer, size_t bytes) override;
i64 tell() override;
void seek(i64 offset, seek_origin origin = seek_origin::seek_set) override;
private:
void* m_file;
};
}
#endif //BLT_FS_CSTDIO_WRAPPERS_H

View File

@ -0,0 +1,146 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_BOUNDED_WRITER_H
#define BLT_FS_BOUNDED_WRITER_H
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <blt/fs/fwddecl.h>
#include <blt/std/types.h>
namespace blt::fs
{
inline auto basic_naming_function = [](const size_t invocation, std::string prefix) {
prefix += '-';
prefix += std::to_string(invocation);
prefix += ".txt";
return prefix;
};
using naming_function_t = std::function<std::string(size_t, std::string)>;
class fwriter_t : public writer_t
{
public:
explicit fwriter_t(const std::string& name, std::string mode = "ab"): m_mode(std::move(mode))
{
fwriter_t::newfile(name);
}
// create a writer without creating a new file. Writing without calling newfile is UB
explicit fwriter_t(std::string mode = "ab"): m_mode(std::move(mode))
{}
i64 write(const char* buffer, size_t bytes) override;
virtual void newfile(const std::string& new_name);
void flush() override;
protected:
std::string m_mode;
FILE* m_file = nullptr;
};
// ReSharper disable once CppClassCanBeFinal
class buffered_writer : public fwriter_t
{
public:
explicit buffered_writer(const std::string& name, size_t buffer_size = 1024 * 128);
explicit buffered_writer(size_t buffer_size = 1024 * 128);
i64 write(const char* buffer, size_t bytes) override;
void flush() override;
void newfile(const std::string& new_name) override;
protected:
size_t m_current_pos = 0;
std::vector<char> m_buffer;
};
/**
* Creates a bounded writer where after a specified number of bytes a new file will be opened and written to instead.
*/
// ReSharper disable once CppClassCanBeFinal
class bounded_writer : public fwriter_t
{
public:
explicit bounded_writer(fwriter_t& writer, std::optional<std::string> base_name, size_t max_size = 1024 * 1024 * 10,
naming_function_t naming_function = basic_naming_function);
i64 write(const char* buffer, size_t bytes) override;
void newfile(const std::string& new_name) override;
void flush() override;
private:
fwriter_t* m_writer;
std::optional<std::string> m_base_name;
size_t m_current_invocation = 0;
size_t m_max_size;
size_t m_currently_written = 0;
// inputs: current invocation, then basename string
// returns: name of the file to write to
naming_function_t m_naming_function;
};
struct time_t
{
i32 year = 0, month = 0, day = 1, hour = -1;
time_t(const i32 year, const i32 month, const i32 day, const i32 hour) : year{year}, month{month}, day{day}, hour{hour}
{}
time_t(const i32 year, const i32 month, const i32 day) : year{year}, month{month}, day{day}
{}
time_t() = default;
};
// ReSharper disable once CppClassCanBeFinal
class rotating_writer : public fwriter_t
{
public:
rotating_writer(fwriter_t& writer, time_t period);
i64 write(const char* buffer, size_t bytes) override;
void flush() override;
void newfile(const std::string& new_name) override;
void newfile();
void check_for_time();
static time_t get_current_time();
private:
fwriter_t* m_writer;
time_t m_period;
time_t m_last_time;
};
}
#endif //BLT_FS_BOUNDED_WRITER_H

View File

@ -19,145 +19,15 @@
#ifndef BLT_FILESYSTEM_H #ifndef BLT_FILESYSTEM_H
#define BLT_FILESYSTEM_H #define BLT_FILESYSTEM_H
#include <fstream> #include <iosfwd>
#include <ios> #include <sstream>
#include <blt/fs/fwddecl.h>
#include <blt/fs/file_writers.h>
#include <blt/fs/stream_wrappers.h>
namespace blt::fs namespace blt::fs
{ {
/**
* A simple interface which provides a way of reading the next block of data from a resource.
* The interface provides a single function "read" which will read a specified number of bytes into the buffer.
* The implementation for this could be fstreams, zlib deflate, or any method filesystem access.
* Reading of a large number of bytes ( > block size) is guaranteed to not significantly increase the read time and will likely result in a
* direct passthrough to the underlying system. Small reads will be buffered, hence the name "block" reader.
*/
class block_reader
{
protected:
// 32768 block size seems the fastest on my system
unsigned long m_bufferSize;
public:
explicit block_reader(size_t bufferSize): m_bufferSize(bufferSize)
{}
/**
* Reads bytes from the internal filesystem implementation
* @param buffer buffer to copy the read bytes into
* @param bytes number of bytes to read
* @return status code. non-zero return codes indicates a failure has occurred.
*/
virtual int read(char* buffer, size_t bytes) = 0;
virtual size_t gcount() = 0;
virtual char get()
{
char c[1];
read(c, 1);
return c[0];
}
};
/**
* A buffered block writer without a definite backend implementation. Exactly the same as a block_reader but for writing to the filesystem.
*/
class block_writer
{
protected:
unsigned long m_bufferSize;
public:
explicit block_writer(unsigned long bufferSize): m_bufferSize(bufferSize)
{}
/**
* Writes the bytes to the filesystem backend implementation
* @param buffer bytes to write
* @param bytes number of bytes to write
* @return non-zero code if failure
*/
virtual int write(char* buffer, size_t bytes) = 0;
virtual int put(char c)
{
char a[1];
a[0] = c;
return write(a, 1);
}
/**
* Ensures that the internal buffer is written to the filesystem.
*/
virtual void flush() = 0;
};
/**
* fstream implementation of the block reader.
*/
class fstream_block_reader : public block_reader
{
private:
std::fstream& m_stream;
char* m_buffer = nullptr;
size_t readIndex = 0;
public:
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize = 131072);
explicit fstream_block_reader(fstream_block_reader& copy) = delete;
explicit fstream_block_reader(fstream_block_reader&& move) = delete;
fstream_block_reader& operator=(const fstream_block_reader& copy) = delete;
fstream_block_reader& operator=(const fstream_block_reader&& move) = delete;
int read(char* buffer, size_t bytes) override;
size_t gcount() final
{
return m_stream.gcount();
}
~fstream_block_reader()
{
delete[] m_buffer;
}
};
class fstream_block_writer : public block_writer
{
private:
std::fstream& m_stream;
char* m_buffer;
size_t writeIndex = 0;
void flush_internal();
public:
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize = 131072):
block_writer(bufferSize), m_stream(stream), m_buffer(new char[bufferSize])
{}
explicit fstream_block_writer(fstream_block_writer& copy) = delete;
explicit fstream_block_writer(fstream_block_writer&& move) = delete;
fstream_block_writer& operator=(const fstream_block_writer& copy) = delete;
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
int write(char* buffer, size_t bytes) override;
inline void flush() override
{
flush_internal();
}
~fstream_block_writer()
{
flush_internal();
delete[] m_buffer;
}
};
} }
#endif //BLT_FILESYSTEM_H #endif //BLT_FILESYSTEM_H

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

@ -0,0 +1,107 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_FWDDECL_H
#define BLT_FS_FWDDECL_H
#include <blt/std/types.h>
#include <cstdio>
namespace blt::fs
{
/**
* A simple interface which provides a way of reading the next block of data from a resource. This is designed to replace the overly complex
* std::ostream
*/
class reader_t
{
public:
virtual ~reader_t() = default;
explicit reader_t() = default;
reader_t(const reader_t&) = delete;
reader_t& operator=(const reader_t&) = delete;
/**
* Reads bytes from the internal filesystem implementation
* @param buffer buffer to copy the read bytes into
* @param bytes number of bytes to read
* @return number of bytes read, or negative value if error. Errors are not required and can just return 0
*/
virtual i64 read(char* buffer, size_t bytes) = 0;
virtual i64 read(void* buffer, const size_t bytes)
{
return this->read(static_cast<char*>(buffer), bytes);
}
};
/**
* A block writer without a definite backend implementation. Exactly the same as a block_reader but for writing to the filesystem.
* this is designed to replace the overly complex std::istream
*/
class writer_t
{
public:
enum class seek_origin
{
// Seek from current position
seek_cur = SEEK_CUR,
// Seek from end of file. Not valid on binary streams
seek_end = SEEK_END,
// Seek from start of file
seek_set = SEEK_SET
};
virtual ~writer_t() = default;
explicit writer_t() = default;
writer_t(const writer_t&) = delete;
writer_t& operator=(const writer_t&) = delete;
/**
* Writes the bytes to the filesystem backend implementation
* @param buffer bytes to write
* @param bytes number of bytes to write
* @return number of bytes, or negative value if error. Zero is also a valid return, not indicating error in itself but can be the result of one.
*/
virtual i64 write(const char* buffer, size_t bytes) = 0;
virtual i64 tell()
{
return 0;
}
virtual void seek(i64, seek_origin)
{
}
i64 write(const void* buffer, const size_t bytes)
{
return this->write(static_cast<const char*>(buffer), bytes);
}
/**
* Optional flush command which syncs the underlying objects
*/
virtual void flush()
{};
};
}
#endif //BLT_FS_FWDDECL_H

View File

@ -13,7 +13,7 @@
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <blt/std/string.h> #include <blt/std/string.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <unordered_set> #include <unordered_set>
namespace blt::fs namespace blt::fs

View File

@ -15,26 +15,26 @@
#include "blt/format/format.h" #include "blt/format/format.h"
#include "blt/fs/filesystem.h" #include "blt/fs/filesystem.h"
#include "blt/std/logging.h" #include "blt/logging/logging.h"
#include "blt/std/memory.h" #include "blt/std/memory.h"
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
namespace blt::nbt { namespace blt::nbt {
void writeUTF8String(blt::fs::block_writer& stream, const std::string& str); void writeUTF8String(blt::fs::writer_t& stream, const std::string& str);
std::string readUTF8String(blt::fs::block_reader& stream); std::string readUTF8String(blt::fs::reader_t& stream);
template<typename T> template<typename T>
inline static void writeData(blt::fs::block_writer& out, const T& d){ inline static void writeData(blt::fs::writer_t& out, const T& d){
char data[sizeof(T)]; char data[sizeof(T)];
mem::toBytes(d, data); mem::toBytes(d, data);
out.write(data, sizeof(T)); out.write(data, sizeof(T));
} }
template<typename T> template<typename T>
inline static void readData(blt::fs::block_reader& in, T& d) { inline static void readData(blt::fs::reader_t& in, T& d) {
char data[sizeof(T)]; char data[sizeof(T)];
in.read(data, sizeof(T)); in.read(data, sizeof(T));
mem::fromBytes(data, &d); mem::fromBytes(data, &d);
@ -63,12 +63,12 @@ namespace blt::nbt {
public: public:
explicit tag_t(nbt_tag type): type(type) {}; explicit tag_t(nbt_tag type): type(type) {};
explicit tag_t(nbt_tag type, std::string name): type(type), name(std::move(name)) {} explicit tag_t(nbt_tag type, std::string name): type(type), name(std::move(name)) {}
virtual void writePayload(blt::fs::block_writer& out) = 0; virtual void writePayload(blt::fs::writer_t& out) = 0;
virtual void readPayload(blt::fs::block_reader& in) = 0; virtual void readPayload(blt::fs::reader_t& in) = 0;
void writeName(blt::fs::block_writer& out) { void writeName(blt::fs::writer_t& out) {
writeUTF8String(out, name); writeUTF8String(out, name);
} }
void readName(blt::fs::block_reader& in) { void readName(blt::fs::reader_t& in) {
name = readUTF8String(in); name = readUTF8String(in);
} }
[[nodiscard]] inline nbt_tag getType() const { [[nodiscard]] inline nbt_tag getType() const {
@ -87,11 +87,11 @@ namespace blt::nbt {
public: public:
explicit tag(nbt_tag type): tag_t(type) {} explicit tag(nbt_tag type): tag_t(type) {}
tag(nbt_tag type, std::string name, T t): tag_t(type, std::move(name)), t(std::move(t)) {} tag(nbt_tag type, std::string name, T t): tag_t(type, std::move(name)), t(std::move(t)) {}
void writePayload(blt::fs::block_writer& out) override { void writePayload(blt::fs::writer_t& out) override {
if constexpr(std::is_arithmetic<T>::value) if constexpr(std::is_arithmetic<T>::value)
writeData(out, t); writeData(out, t);
} }
void readPayload(blt::fs::block_reader& in) override { void readPayload(blt::fs::reader_t& in) override {
if constexpr(std::is_arithmetic<T>::value) if constexpr(std::is_arithmetic<T>::value)
readData(in, t); readData(in, t);
} }
@ -102,9 +102,9 @@ namespace blt::nbt {
class tag_end : public tag<char> { class tag_end : public tag<char> {
public: public:
void writePayload(blt::fs::block_writer&) final {} void writePayload(blt::fs::writer_t&) final {}
// nothing to read // nothing to read
void readPayload(blt::fs::block_reader&) final {} void readPayload(blt::fs::reader_t&) final {}
}; };
class tag_byte : public tag<int8_t> { class tag_byte : public tag<int8_t> {
@ -147,13 +147,13 @@ namespace blt::nbt {
public: public:
tag_byte_array(): tag(nbt_tag::BYTE_ARRAY) {} tag_byte_array(): tag(nbt_tag::BYTE_ARRAY) {}
tag_byte_array(const std::string& name, const std::vector<int8_t>& v): tag(nbt_tag::BYTE_ARRAY, name, v) {} tag_byte_array(const std::string& name, const std::vector<int8_t>& v): tag(nbt_tag::BYTE_ARRAY, name, v) {}
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
auto length = (int32_t) t.size(); auto length = (int32_t) t.size();
writeData(out, length); writeData(out, length);
// TODO on the writer (remove need for cast + more std::fstream functions) // TODO on the writer (remove need for cast + more std::fstream functions)
out.write(reinterpret_cast<char*>(t.data()), length); out.write(reinterpret_cast<char*>(t.data()), length);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
int32_t length; int32_t length;
readData(in, length); readData(in, length);
t.reserve(length); t.reserve(length);
@ -165,10 +165,10 @@ namespace blt::nbt {
public: public:
tag_string(): tag(nbt_tag::STRING) {} tag_string(): tag(nbt_tag::STRING) {}
tag_string(const std::string& name, const std::string& s): tag(nbt_tag::STRING, name, s) {} tag_string(const std::string& name, const std::string& s): tag(nbt_tag::STRING, name, s) {}
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
writeUTF8String(out, t); writeUTF8String(out, t);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
t = readUTF8String(in); t = readUTF8String(in);
} }
}; };
@ -177,13 +177,13 @@ namespace blt::nbt {
public: public:
tag_int_array(): tag(nbt_tag::INT_ARRAY) {} tag_int_array(): tag(nbt_tag::INT_ARRAY) {}
tag_int_array(const std::string& name, const std::vector<int32_t>& v): tag(nbt_tag::INT_ARRAY, name, v) {} tag_int_array(const std::string& name, const std::vector<int32_t>& v): tag(nbt_tag::INT_ARRAY, name, v) {}
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
auto length = (int32_t) t.size(); auto length = (int32_t) t.size();
writeData(out, length); writeData(out, length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
writeData(out, t[i]); writeData(out, t[i]);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
int32_t length; int32_t length;
readData(in, length); readData(in, length);
t.reserve(length); t.reserve(length);
@ -196,13 +196,13 @@ namespace blt::nbt {
public: public:
tag_long_array(): tag(nbt_tag::LONG_ARRAY) {} tag_long_array(): tag(nbt_tag::LONG_ARRAY) {}
tag_long_array(const std::string& name, const std::vector<int64_t>& v): tag(nbt_tag::LONG_ARRAY, name, v) {} tag_long_array(const std::string& name, const std::vector<int64_t>& v): tag(nbt_tag::LONG_ARRAY, name, v) {}
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
auto length = (int32_t) t.size(); auto length = (int32_t) t.size();
writeData(out, length); writeData(out, length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
writeData(out, t[i]); writeData(out, t[i]);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
int32_t length; int32_t length;
readData(in, length); readData(in, length);
t.reserve(length); t.reserve(length);
@ -262,7 +262,7 @@ namespace blt::nbt {
public: public:
tag_list(): tag(nbt_tag::LIST) {} tag_list(): tag(nbt_tag::LIST) {}
tag_list(const std::string& name, const std::vector<tag_t*>& v): tag(nbt_tag::LIST, name, v) {} tag_list(const std::string& name, const std::vector<tag_t*>& v): tag(nbt_tag::LIST, name, v) {}
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
if (t.empty()) if (t.empty())
writeData(out, (char)nbt_tag::END); writeData(out, (char)nbt_tag::END);
else else
@ -273,7 +273,7 @@ namespace blt::nbt {
v->writePayload(out); v->writePayload(out);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
char id; char id;
int32_t length; int32_t length;
readData(in, id); readData(in, id);
@ -305,7 +305,7 @@ namespace blt::nbt {
auto& tag = t[i]; auto& tag = t[i];
T t; T t;
if (tag->getType() != t.getType()) { if (tag->getType() != t.getType()) {
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType()); BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
throw std::runtime_error("Requested Tag does not match stored type!"); throw std::runtime_error("Requested Tag does not match stored type!");
} }
return dynamic_cast<T*>(tag); return dynamic_cast<T*>(tag);
@ -342,7 +342,7 @@ namespace blt::nbt {
auto& tag = t[name]; auto& tag = t[name];
T t; T t;
if (tag->getType() != t.getType()) { if (tag->getType() != t.getType()) {
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType()); BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
throw std::runtime_error("Requested Tag does not match stored type!"); throw std::runtime_error("Requested Tag does not match stored type!");
} }
return dynamic_cast<T*>(tag); return dynamic_cast<T*>(tag);
@ -356,18 +356,20 @@ namespace blt::nbt {
t[tag->getName()] = tag; t[tag->getName()] = tag;
} }
void writePayload(blt::fs::block_writer& out) final { void writePayload(blt::fs::writer_t& out) final {
for (const auto& v : t){ for (const auto& v : t){
auto tag = v.second; auto tag = v.second;
out.put((char) tag->getType()); auto c = (char) tag->getType();
out.write(&c, 1);
tag->writeName(out); tag->writeName(out);
tag->writePayload(out); tag->writePayload(out);
} }
out.put('\0'); const char c = '\0';
out.write(&c, 1);
} }
void readPayload(blt::fs::block_reader& in) final { void readPayload(blt::fs::reader_t& in) final {
char type; char type;
while ((type = in.get()) != (char)nbt_tag::END){ while ((in.read(&type, 1), type) != (char)nbt_tag::END){
auto* v = _internal_::toType(type); auto* v = _internal_::toType(type);
v->readName(in); v->readName(in);
v->readPayload(in); v->readPayload(in);
@ -390,10 +392,10 @@ namespace blt::nbt {
class NBTReader { class NBTReader {
private: private:
blt::fs::block_reader& reader; blt::fs::reader_t& reader;
tag_compound* root = nullptr; tag_compound* root = nullptr;
public: public:
explicit NBTReader(blt::fs::block_reader& reader): reader(reader) {} explicit NBTReader(blt::fs::reader_t& reader): reader(reader) {}
void read(); void read();
@ -407,7 +409,7 @@ namespace blt::nbt {
auto& tag = root->get()[name]; auto& tag = root->get()[name];
T t; T t;
if (tag->getType() != t.getType()) { if (tag->getType() != t.getType()) {
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType()); BLT_WARN("Expected tag of type {:d} but got tag of type {:d}", (char)t.getType(), (char)tag->getType());
throw std::runtime_error("Requested Tag does not match stored type!"); throw std::runtime_error("Requested Tag does not match stored type!");
} }
return dynamic_cast<T*>(tag); return dynamic_cast<T*>(tag);
@ -419,9 +421,9 @@ namespace blt::nbt {
class NBTWriter { class NBTWriter {
private: private:
blt::fs::block_writer& writer; blt::fs::writer_t& writer;
public: public:
explicit NBTWriter(blt::fs::block_writer& writer): writer(writer) {} explicit NBTWriter(blt::fs::writer_t& writer): writer(writer) {}
/** /**
* Write a compound tag and then DELETES the tag. If you don't wish for the memory to be freed, please use the reference version! * Write a compound tag and then DELETES the tag. If you don't wish for the memory to be freed, please use the reference version!
* @param root root NBT tag to write, this function assumes ownership of this pointer. * @param root root NBT tag to write, this function assumes ownership of this pointer.
@ -431,7 +433,8 @@ namespace blt::nbt {
delete root; delete root;
} }
void write(tag_compound& root){ void write(tag_compound& root){
writer.put((char)nbt_tag::COMPOUND); auto c = (char)nbt_tag::COMPOUND;
writer.write(&c, 1);
root.writeName(writer); root.writeName(writer);
root.writePayload(writer); root.writePayload(writer);
} }

View File

@ -0,0 +1,39 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_PATH_HELPER_H
#define BLT_FS_PATH_HELPER_H
#include <string>
#include <string_view>
namespace blt::fs
{
std::string base_name(const std::string& str);
std::string_view base_name_sv(std::string_view str);
std::string filename(const std::string& str);
std::string_view filename_sv(std::string_view str);
std::string extension(const std::string& str);
std::string_view extension_sv(std::string_view str);
}
#endif //BLT_FS_PATH_HELPER_H

View File

@ -0,0 +1,139 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_STREAM_WRAPPERS_H
#define BLT_FS_STREAM_WRAPPERS_H
#include <iosfwd>
#include <sstream>
#include <blt/fs/fwddecl.h>
namespace blt::fs
{
/**
* reader_t wrapper for fstream
*/
class fstream_reader_t final : public reader_t
{
public:
explicit fstream_reader_t(std::istream& stream);
i64 read(char* buffer, size_t bytes) override;
private:
std::istream* m_stream;
};
class fstream_writer_t final : public writer_t
{
public:
explicit fstream_writer_t(std::ostream& stream);
i64 write(const char* buffer, size_t bytes) override;
void flush() override;
i64 tell() override;
void seek(i64 offset, seek_origin origin = seek_origin::seek_set) override;
virtual ~fstream_writer_t() override // NOLINT
{
flush();
}
private:
std::ostream* m_stream;
};
class reader_wrapper_t
{
public:
explicit reader_wrapper_t(reader_t& reader): m_reader(&reader)
{}
template <typename T>
void read(T& out)
{
if (!m_reader->read(reinterpret_cast<char*>(&out), sizeof(T)))
throw std::runtime_error("Failed to read from reader");
}
template <typename T>
friend reader_wrapper_t& operator>>(reader_wrapper_t& reader, T& t)
{
reader.read(t);
return reader;
}
private:
reader_t* m_reader;
};
class writer_wrapper_t
{
public:
explicit writer_wrapper_t(writer_t& writer): m_writer(&writer)
{}
template <typename T>
void write(const T& t)
{
static_assert(std::is_trivially_copyable_v<T>);
m_writer->write(reinterpret_cast<const char*>(&t), sizeof(T));
}
template <typename T>
friend writer_wrapper_t& operator<<(writer_wrapper_t& writer, const T& t)
{
writer.write(t);
return writer;
}
private:
writer_t* m_writer;
};
class writer_string_wrapper_t
{
public:
explicit writer_string_wrapper_t(writer_t& writer): m_writer(&writer)
{}
template <typename T>
void write(const T& t)
{
std::stringstream ss;
ss << t;
const auto str = ss.str();
m_writer->write(str.data(), str.size());
}
template <typename T>
friend writer_string_wrapper_t& operator<<(writer_string_wrapper_t& writer, const T& t)
{
writer.write(t);
return writer;
}
private:
writer_t* m_writer;
};
}
#endif //BLT_FS_STREAM_WRAPPERS_H

View File

@ -0,0 +1,42 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_FS_THREADED_WRITERS_H
#define BLT_FS_THREADED_WRITERS_H
#include <mutex>
#include <blt/fs/fwddecl.h>
namespace blt::fs
{
// ReSharper disable once CppClassCanBeFinal
class concurrent_file_writer : public writer_t
{
public:
explicit concurrent_file_writer(writer_t* writer);
i64 write(const char* buffer, size_t bytes) override;
void flush() override;
private:
writer_t* m_writer;
std::mutex m_mutex;
};
}
#endif //BLT_FS_THREADED_WRITERS_H

View File

@ -25,10 +25,45 @@
#include <blt/iterator/fwddecl.h> #include <blt/iterator/fwddecl.h>
#include <blt/meta/meta.h> #include <blt/meta/meta.h>
#include <blt/meta/iterator.h> #include <blt/meta/iterator.h>
#include <functional>
#include <optional>
#include <blt/meta/type_traits.h>
namespace blt::iterator namespace blt::iterator
{ {
template<typename Derived> namespace detail
{
template<typename T>
static auto forward_as_tuple(T&& t) -> decltype(auto)
{
using Decay = std::decay_t<T>;
static_assert(!(meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>), "Tuple or pair passed to forward_as_tuple! Must not be a tuple!");
if constexpr (std::is_lvalue_reference_v<T>)
{
return std::forward_as_tuple(std::forward<T>(t));
}
else
{
return std::make_tuple(std::forward<T>(t));
}
}
template <typename Tuple>
static auto ensure_tuple(Tuple&& tuple) -> decltype(auto)
{
using Decay = std::decay_t<Tuple>;
if constexpr (meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>)
{
return std::forward<Tuple>(tuple);
}
else
{
return forward_as_tuple(std::forward<Tuple>(tuple));
}
}
}
template <typename Derived>
struct base_wrapper struct base_wrapper
{ {
base_wrapper operator++(int) base_wrapper operator++(int)
@ -40,19 +75,21 @@ namespace blt::iterator
base_wrapper operator--(int) base_wrapper operator--(int)
{ {
static_assert(meta::is_bidirectional_or_better_category_v<typename Derived::iterator_category>, "Iterator must allow random access"); static_assert(meta::is_bidirectional_or_better_category_v<typename Derived::iterator_category>,
"Iterator must allow bidirectional access");
auto tmp = *this; auto tmp = *this;
--*this; --*this;
return tmp; return tmp;
} }
auto operator[](blt::ptrdiff_t n) const auto operator[](ptrdiff_t n) const
{ {
static_assert(meta::is_random_access_iterator_category_v<typename Derived::iterator_category>, "Iterator must allow random access"); static_assert(meta::is_random_access_iterator_category_v<typename Derived::iterator_category>,
"Iterator must allow bidirectional access");
return *(*this + n); return *(*this + n);
} }
friend base_wrapper operator+(blt::ptrdiff_t n, const base_wrapper& a) friend base_wrapper operator+(ptrdiff_t n, const base_wrapper& a)
{ {
return a + n; return a + n;
} }
@ -92,29 +129,29 @@ namespace blt::iterator
} }
}; };
template<typename Iter, typename Derived, bool dereference = false> template <typename Iter, typename Derived, bool dereference = false>
struct passthrough_wrapper : public base_wrapper<Derived> struct passthrough_wrapper : base_wrapper<Derived>
{ {
public: explicit passthrough_wrapper(Iter iter): iter(std::move(iter))
explicit passthrough_wrapper(Iter iter): iter(std::move(iter)) {
{} }
auto base() const auto base() const
{ {
return iter; return iter;
} }
friend blt::ptrdiff_t operator-(const passthrough_wrapper& a, const passthrough_wrapper& b) friend ptrdiff_t operator-(const passthrough_wrapper& a, const passthrough_wrapper& b)
{ {
return a.base() - b.base(); return a.base() - b.base();
} }
protected: protected:
Iter iter; mutable Iter iter;
}; };
template<typename Iter, typename Derived> template <typename Iter, typename Derived>
struct passthrough_wrapper<Iter, Derived, true> : public passthrough_wrapper<Iter, Derived> struct passthrough_wrapper<Iter, Derived, true> : passthrough_wrapper<Iter, Derived>
{ {
using passthrough_wrapper<Iter, Derived>::passthrough_wrapper; using passthrough_wrapper<Iter, Derived>::passthrough_wrapper;
@ -122,190 +159,367 @@ namespace blt::iterator
{ {
return *this->iter; return *this->iter;
} }
};
// zip_wrapper& operator++() template <typename Iter, typename Derived>
// { class deref_only_wrapper : public passthrough_wrapper<Iter, Derived, false>
// return *this; {
// } public:
// using passthrough_wrapper<Iter, Derived, false>::passthrough_wrapper;
// zip_wrapper& operator--()
// { deref_only_wrapper& operator++()
// return *this; {
// } ++this->iter;
// return *this;
// friend zip_wrapper operator+(const zip_wrapper& a, blt::ptrdiff_t n) }
// {
// static_assert(std::is_same_v<iterator_category, std::random_access_iterator_tag>, deref_only_wrapper& operator--()
// "Iterator must allow random access"); {
// } --this->iter;
// return *this;
// friend zip_wrapper operator-(const zip_wrapper& a, blt::ptrdiff_t n) }
// {
// static_assert(std::is_same_v<iterator_category, std::random_access_iterator_tag>, deref_only_wrapper& operator+(blt::ptrdiff_t n)
// "Iterator must allow random access"); {
// } static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
this->iter = this->iter + n;
return *this;
}
deref_only_wrapper& operator-(blt::ptrdiff_t n)
{
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
this->iter = this->iter - n;
return *this;
}
};
template <typename Iter, typename Func>
class map_wrapper : public deref_only_wrapper<Iter, map_wrapper<Iter, Func>>
{
public:
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
using value_type = std::invoke_result_t<Func, meta::deref_return_t<Iter>>;
using difference_type = blt::ptrdiff_t;
using pointer = value_type;
using reference = value_type;
map_wrapper(Iter iter, Func func):
deref_only_wrapper<Iter, map_wrapper<Iter, Func>>(std::move(iter)), func(std::move(func))
{
}
reference operator*() const
{
return func(*this->iter);
}
private:
Func func;
};
template <typename Iter, typename Pred>
class filter_wrapper : public deref_only_wrapper<Iter, filter_wrapper<Iter, Pred>>
{
public:
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
using value_type = std::conditional_t<
std::is_reference_v<meta::deref_return_t<Iter>>,
std::optional<std::reference_wrapper<std::remove_reference_t<meta::deref_return_t<Iter>>>>,
std::optional<meta::deref_return_t<Iter>>>;
using difference_type = blt::ptrdiff_t;
using pointer = value_type;
using reference = value_type;
filter_wrapper(Iter iter, Pred func):
deref_only_wrapper<Iter, filter_wrapper<Iter, Pred>>(std::move(iter)), func(std::move(func))
{
}
reference operator*() const
{
if (!func(*this->iter))
{
return {};
}
return *this->iter;
}
private:
Pred func;
};
template <typename Iter>
class const_wrapper : public deref_only_wrapper<Iter, const_wrapper<Iter>>
{
public:
using ref_return = meta::deref_return_t<Iter>;
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
using value_type = std::conditional_t<std::is_reference_v<ref_return>, std::remove_reference_t<ref_return>, ref_return>;
using difference_type = ptrdiff_t;
using pointer = const value_type*;
using reference = const value_type&;
explicit const_wrapper(Iter iter): deref_only_wrapper<Iter, const_wrapper>(std::move(iter))
{
}
template<typename T>
static auto make_const(T&& value) -> decltype(auto)
{
using Decay = std::decay_t<T>;
if constexpr (std::is_lvalue_reference_v<T>)
{
return const_cast<const Decay&>(value);
} else
{
return static_cast<const Decay>(value);
}
}
auto operator*() const
{
if constexpr (std::is_reference_v<ref_return>)
{
return const_cast<const value_type&>(*this->iter);
} else if constexpr (meta::is_tuple_v<value_type> || meta::is_pair_v<value_type>)
{
return std::apply([](auto&&... args)
{
return std::tuple<decltype(make_const(std::forward<decltype(args)>(args)))...>{make_const(std::forward<decltype(args)>(args))...};
}, *this->iter);
}
else
{
return *this->iter;
}
}
}; };
namespace impl namespace impl
{ {
template<typename Derived> template <typename Derived>
class skip_t class skip_t
{ {
private: private:
template<bool check> template <bool check>
auto skip_base(blt::size_t n) auto skip_base(blt::size_t n)
{ {
auto* d = static_cast<Derived*>(this); auto* d = static_cast<Derived*>(this);
auto begin = d->begin(); auto begin = d->begin();
auto end = d->end(); auto end = d->end();
if constexpr (meta::is_random_access_iterator_category_v<typename Derived::iterator_category>) if constexpr (meta::is_random_access_iterator_category_v<typename Derived::iterator_category>)
{
// random access iterators can have math directly applied to them.
if constexpr (check)
{ {
// random access iterators can have math directly applied to them. return Derived{begin + std::min(static_cast<blt::ptrdiff_t>(n), std::distance(begin, end)), end};
if constexpr (check) }
{ else
return Derived{begin + std::min(static_cast<blt::ptrdiff_t>(n), std::distance(begin, end)), end};
} else
{
return Derived{begin + n, end};
}
} else
{ {
for (blt::size_t i = 0; i < n; i++) return Derived{begin + n, end};
{
if constexpr (check)
{
if (begin == end)
break;
}
++begin;
}
return Derived{std::move(begin), std::move(end)};
} }
} }
else
{
for (blt::size_t i = 0; i < n; i++)
{
if constexpr (check)
{
if (begin == end)
break;
}
++begin;
}
return Derived{std::move(begin), std::move(end)};
}
}
public: public:
auto skip(blt::size_t n) auto skip(blt::size_t n)
{ return skip_base<false>(n); } {
return skip_base<false>(n);
}
auto skip_or(blt::size_t n) auto skip_or(blt::size_t n)
{ return skip_base<true>(n); } {
return skip_base<true>(n);
}
}; };
template<typename Derived> template <typename Derived>
class take_t class take_t
{ {
private: private:
template<bool check> template <bool check>
auto take_base(blt::size_t n) auto take_base(blt::size_t n)
{ {
static_assert(!meta::is_input_iterator_category_v<typename Derived::iterator_category>, static_assert(!meta::is_input_iterator_category_v<typename Derived::iterator_category>,
"Cannot .take() on an input iterator!"); "Cannot .take() on an input iterator!");
auto* d = static_cast<Derived*>(this); auto* d = static_cast<Derived*>(this);
auto begin = d->begin(); auto begin = d->begin();
auto end = d->end(); auto end = d->end();
// take variant for forward and bidirectional iterators // take variant for forward and bidirectional iterators
if constexpr (meta::is_forward_iterator_category_v<typename Derived::iterator_category> || if constexpr (meta::is_forward_iterator_category_v<typename Derived::iterator_category> ||
meta::is_bidirectional_iterator_category_v<typename Derived::iterator_category>) meta::is_bidirectional_iterator_category_v<typename Derived::iterator_category>)
{
// with these guys we have to loop forward to move the iterators. an unfortunate inefficiency
auto new_end = begin;
for (blt::size_t i = 0; i < n; i++)
{ {
// with these guys we have to loop forward to move the iterators. an unfortunate inefficiency
auto new_end = begin;
for (blt::size_t i = 0; i < n; i++)
{
if constexpr (check)
{
if (new_end == end)
break;
}
++new_end;
}
return Derived{std::move(begin), std::move(new_end)};
} else if constexpr (meta::is_random_access_iterator_category_v<typename Derived::iterator_category>)
{
// random access iterators can have math directly applied to them.
if constexpr (check) if constexpr (check)
{ {
return Derived{begin, begin + std::min(static_cast<blt::ptrdiff_t>(n), std::distance(begin, end))}; if (new_end == end)
} else break;
{
return Derived{begin, begin + n};
} }
++new_end;
}
return Derived{std::move(begin), std::move(new_end)};
}
else if constexpr (meta::is_random_access_iterator_category_v<typename Derived::iterator_category>)
{
// random access iterators can have math directly applied to them.
if constexpr (check)
{
return Derived{begin, begin + std::min(static_cast<blt::ptrdiff_t>(n), std::distance(begin, end))};
}
else
{
return Derived{begin, begin + n};
} }
} }
}
public: public:
auto take(blt::size_t n) auto take(blt::size_t n)
{ return take_base<false>(n); } {
return take_base<false>(n);
}
auto take_or(blt::size_t n) auto take_or(blt::size_t n)
{ return take_base<true>(n); } {
return take_base<true>(n);
}
}; };
} }
template<typename IterBase> template <typename IterBase>
class iterator_container : public impl::take_t<iterator_container<IterBase>>, class iterator_container : public impl::take_t<iterator_container<IterBase>>,
public impl::skip_t<iterator_container<IterBase>> public impl::skip_t<iterator_container<IterBase>>
{ {
public: public:
using iterator_category = typename std::iterator_traits<IterBase>::iterator_category; using iterator_category = typename std::iterator_traits<IterBase>::iterator_category;
using iterator = IterBase; using iterator = IterBase;
iterator_container(IterBase begin, IterBase end): m_begin(std::move(begin)), m_end(std::move(end)) iterator_container(IterBase begin, IterBase end): m_begin(std::move(begin)), m_end(std::move(end))
{} {
}
template<typename Iter> template <typename Iter>
iterator_container(Iter&& begin, Iter&& end): m_begin(std::forward<Iter>(begin)), m_end(std::forward<Iter>(end)) iterator_container(Iter&& begin, Iter&& end): m_begin(std::forward<Iter>(begin)), m_end(std::forward<Iter>(end))
{} {
}
auto rev() const auto rev() const
{ {
static_assert(meta::is_bidirectional_or_better_category_v<iterator_category>, static_assert(meta::is_bidirectional_or_better_category_v<iterator_category>,
".rev() must be used with bidirectional (or better) iterators!"); ".rev() must be used with bidirectional (or better) iterators!");
return iterator_container<std::reverse_iterator<IterBase>>{std::reverse_iterator<IterBase>{end()}, return iterator_container<std::reverse_iterator<IterBase>>{
std::reverse_iterator<IterBase>{begin()}}; std::reverse_iterator<IterBase>{end()},
} std::reverse_iterator<IterBase>{begin()}
};
}
template<typename... Iter> template <typename... Iter>
auto zip(iterator_pair<Iter>... iterator_pairs) const auto zip(iterator_pair<Iter>... iterator_pairs) const
{ {
return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()}, iterator_pairs...); return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()}, iterator_pairs...);
} }
template<typename... Container> template <typename... Container>
auto zip(Container& ... containers) const auto zip(Container&... containers) const
{ {
return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()}, return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()},
iterator_pair{containers.begin(), containers.end()}...); iterator_pair{containers.begin(), containers.end()}...);
} }
template<typename... Container> template <typename... Container>
auto zip(const Container& ... containers) const auto zip(const Container&... containers) const
{ {
return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()}, return zip_iterator_container(iterator_pair<decltype(begin())>{begin(), end()},
iterator_pair{containers.begin(), containers.end()}...); iterator_pair{containers.begin(), containers.end()}...);
} }
auto enumerate() const template <typename Iter>
{ auto zip(Iter begin, Iter end)
return enumerate_iterator_container{begin(), end(), static_cast<blt::size_t>(std::distance(begin(), end()))}; {
} return zip(iterator_pair<Iter>{begin, end});
}
auto begin() const auto enumerate() const
{ {
return m_begin; return enumerate_iterator_container{begin(), end(), static_cast<blt::size_t>(std::distance(begin(), end()))};
} }
auto end() const auto flatten() const
{ {
return m_end; return iterator_container<flatten_wrapper<IterBase, false>>{
} blt::iterator::flatten_wrapper<IterBase, false>{m_begin},
blt::iterator::flatten_wrapper<IterBase, false>{m_end}
};
}
protected: auto flatten_all() const
IterBase m_begin; {
IterBase m_end; return iterator_container<flatten_wrapper<IterBase, true>>{
blt::iterator::flatten_wrapper<IterBase, true>{m_begin},
blt::iterator::flatten_wrapper<IterBase, true>{m_end}
};
}
auto as_const() const
{
return iterator_container<const_wrapper<IterBase>>{
const_wrapper<IterBase>{m_begin},
const_wrapper<IterBase>{m_end}
};
}
template <typename Func>
auto map(Func func) const
{
return iterator_container<blt::iterator::map_wrapper<IterBase, Func>>{
blt::iterator::map_wrapper<IterBase, Func>{m_begin, func},
blt::iterator::map_wrapper<IterBase, Func>{m_end, func}
};
}
template <typename Pred>
auto filter(Pred pred) const
{
return iterator_container<blt::iterator::filter_wrapper<IterBase, Pred>>{
blt::iterator::filter_wrapper<IterBase, Pred>{m_begin, pred},
blt::iterator::filter_wrapper<IterBase, Pred>{m_end, pred}
};
}
auto begin() const
{
return m_begin;
}
auto end() const
{
return m_end;
}
protected:
IterBase m_begin;
IterBase m_end;
}; };
} }
#endif //BLT_ITERATOR_ITER_COMMON #endif //BLT_ITERATOR_ITER_COMMON

View File

@ -20,132 +20,112 @@
#define BLT_ITERATOR_ENUMERATE_H #define BLT_ITERATOR_ENUMERATE_H
#include <blt/iterator/common.h> #include <blt/iterator/common.h>
#include <tuple>
namespace blt namespace blt
{ {
namespace iterator namespace iterator
{ {
/** template <typename Iter>
* struct which is returned by the enumerator.
* @tparam T type to store.
*/
template<typename T>
struct enumerate_item
{
blt::size_t index;
T value;
};
template<typename Iter>
class enumerate_wrapper : public passthrough_wrapper<Iter, enumerate_wrapper<Iter>> class enumerate_wrapper : public passthrough_wrapper<Iter, enumerate_wrapper<Iter>>
{ {
public: public:
enumerate_wrapper(blt::size_t index, Iter iter): passthrough_wrapper<Iter, enumerate_wrapper<Iter>>(std::move(iter)), index(index) enumerate_wrapper(const size_t index, Iter iter): passthrough_wrapper<Iter, enumerate_wrapper<Iter>>(std::move(iter)), index(index)
{} {
}
using iterator_category = typename std::iterator_traits<Iter>::iterator_category; using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
using value_type = enumerate_item<meta::deref_return_t<Iter>>; using value_type = enumerate_item<meta::deref_return_t<Iter>>;
using difference_type = blt::ptrdiff_t; using difference_type = blt::ptrdiff_t;
using pointer = value_type; using pointer = value_type;
using reference = value_type; using reference = value_type;
enumerate_item<meta::deref_return_t<Iter>> operator*() const enumerate_item<meta::deref_return_t<Iter>> operator*() const
{ {
return {index, *this->iter}; return {index, *this->iter};
} }
enumerate_wrapper& operator++() enumerate_wrapper& operator++()
{ {
++index; ++index;
++this->iter; ++this->iter;
return *this; return *this;
} }
enumerate_wrapper& operator--() enumerate_wrapper& operator--()
{ {
--index; --index;
--this->iter; --this->iter;
return *this; return *this;
} }
friend enumerate_wrapper operator+(const enumerate_wrapper& a, blt::ptrdiff_t n) friend enumerate_wrapper operator+(const enumerate_wrapper& a, blt::ptrdiff_t n)
{ {
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access"); static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
auto copy = a; auto copy = a;
copy.index += n; copy.index += n;
copy.iter = copy.iter + n; copy.iter = copy.iter + n;
return copy; return copy;
} }
friend enumerate_wrapper operator-(const enumerate_wrapper& a, blt::ptrdiff_t n) friend enumerate_wrapper operator-(const enumerate_wrapper& a, blt::ptrdiff_t n)
{ {
static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access"); static_assert(meta::is_random_access_iterator_v<Iter>, "Iterator must allow random access");
auto copy = a; auto copy = a;
copy.index -= n; copy.index -= n;
copy.iter = copy.iter - n; copy.iter = copy.iter - n;
return copy; return copy;
} }
private: private:
blt::size_t index; blt::size_t index;
}; };
} }
template<typename Iter> template <typename Iter>
class enumerate_iterator_container : public iterator::iterator_container<iterator::enumerate_wrapper<Iter>> class enumerate_iterator_container : public blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>
{ {
public: public:
using iterator::iterator_container<iterator::enumerate_wrapper<Iter>>::iterator_container; using blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>::iterator_container;
enumerate_iterator_container(Iter begin, Iter end, blt::size_t size): enumerate_iterator_container(Iter begin, Iter end, blt::size_t size):
iterator::iterator_container<iterator::enumerate_wrapper<Iter>>( blt::iterator::iterator_container<blt::iterator::enumerate_wrapper<Iter>>(
iterator::enumerate_wrapper<Iter>{0, std::move(begin)}, iterator::enumerate_wrapper<Iter>{size, std::move(end)}) blt::iterator::enumerate_wrapper<Iter>{0, std::move(begin)}, blt::iterator::enumerate_wrapper<Iter>{size, std::move(end)})
{} {
}
}; };
template<typename Iter> template <typename Iter>
enumerate_iterator_container(Iter, Iter, blt::size_t) -> enumerate_iterator_container<Iter>; enumerate_iterator_container(Iter, Iter, blt::size_t) -> enumerate_iterator_container<Iter>;
namespace iterator::impl template <typename T>
static inline auto enumerate(T& container)
{ {
template<typename Derived> return enumerate_iterator_container{
class enumerate_t container.begin(), container.end(),
{ static_cast<blt::size_t>(std::distance(container.begin(), container.end()))
public:
auto enumerate()
{
auto* d = static_cast<Derived*>(this);
return enumerate_iterator_container{d->begin(), d->end(), static_cast<blt::size_t>(std::distance(d->begin(), d->end()))};
}
}; };
} }
template<typename T> template <typename T>
static inline auto enumerate(T& container)
{
return enumerate_iterator_container{container.begin(), container.end(), container.size()};
}
template<typename T>
static inline auto enumerate(const T& container) static inline auto enumerate(const T& container)
{ {
return enumerate_iterator_container{container.begin(), container.end(), container.size()}; return enumerate_iterator_container{
container.begin(), container.end(),
static_cast<blt::size_t>(std::distance(container.begin(), container.end()))
};
} }
template<typename T, blt::size_t size> template <typename T, blt::size_t size>
static inline auto enumerate(const T(& container)[size]) static inline auto enumerate(const T (&container)[size])
{ {
return enumerate_iterator_container{&container[0], &container[size], size}; return enumerate_iterator_container{&container[0], &container[size], size};
} }
template<typename T, blt::size_t size> template <typename T, blt::size_t size>
static inline auto enumerate(T(& container)[size]) static inline auto enumerate(T (&container)[size])
{ {
return enumerate_iterator_container{&container[0], &container[size], size}; return enumerate_iterator_container{&container[0], &container[size], size};
} }
} }
#endif //BLT_ITERATOR_ENUMERATE_H #endif //BLT_ITERATOR_ENUMERATE_H

View File

@ -0,0 +1,89 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_ITERATOR_FLATTEN_H
#define BLT_ITERATOR_FLATTEN_H
#include <blt/iterator/common.h>
#include <blt/meta/type_traits.h>
#include <tuple>
namespace blt::iterator
{
namespace detail
{
template <typename Tuple>
static auto flatten_recursive(Tuple&& tuple) -> decltype(auto)
{
using Decay = std::decay_t<Tuple>;
if constexpr (meta::is_tuple_v<Decay> || meta::is_pair_v<Decay>)
{
return std::apply([](auto&&... args)
{
return std::tuple_cat(flatten_recursive(std::forward<decltype(args)>(args))...);
}, std::forward<Tuple>(tuple));
}
else
{
return forward_as_tuple(std::forward<Tuple>(tuple));
}
}
template <typename Tuple>
static auto flatten(Tuple&& tuple) -> decltype(auto)
{
return std::apply([](auto&&... args)
{
return std::tuple_cat(ensure_tuple(std::forward<decltype(args)>(args))...);
}, std::forward<Tuple>(tuple));
}
}
template <typename Iter, bool Recursive>
class flatten_wrapper : public deref_only_wrapper<Iter, flatten_wrapper<Iter, Recursive>>
{
public:
using iterator_category = typename std::iterator_traits<Iter>::iterator_category;
using value_type = std::conditional_t<Recursive,
std::remove_reference_t<decltype(detail::flatten_recursive(*std::declval<Iter>()))>,
std::remove_reference_t<decltype(detail::flatten(*std::declval<Iter>()))>>;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
explicit flatten_wrapper(Iter iter):
deref_only_wrapper<Iter, flatten_wrapper>(std::move(iter))
{
}
auto operator*() const -> decltype(auto)
{
if constexpr (Recursive)
{
return detail::flatten_recursive(*this->iter);
}
else
{
return detail::flatten(*this->iter);
}
}
};
}
#endif //BLT_ITERATOR_FLATTEN_H

View File

@ -19,6 +19,8 @@
#ifndef BLT_ITERATOR_FWDDECL_H #ifndef BLT_ITERATOR_FWDDECL_H
#define BLT_ITERATOR_FWDDECL_H #define BLT_ITERATOR_FWDDECL_H
#include <utility>
namespace blt namespace blt
{ {
template<typename... Iter> template<typename... Iter>
@ -33,7 +35,7 @@ namespace blt
struct iterator_pair; struct iterator_pair;
template<typename T> template<typename T>
struct enumerate_item; using enumerate_item = std::pair<size_t, T>;
template<typename Iter> template<typename Iter>
class enumerate_wrapper; class enumerate_wrapper;
@ -41,6 +43,18 @@ namespace blt
template<typename... Iter> template<typename... Iter>
struct zip_wrapper; struct zip_wrapper;
template<typename Iter, typename Func>
class map_wrapper;
template<typename Iter, typename Pred>
class filter_wrapper;
template<typename Iter, bool Recursive>
class flatten_wrapper;
template<typename Iter>
class const_wrapper;
namespace impl namespace impl
{ {
template<typename Derived> template<typename Derived>
@ -48,12 +62,6 @@ namespace blt
template<typename Derived> template<typename Derived>
class take_t; class take_t;
template<typename Derived>
class zip_t;
template<typename Derived>
class enumerate_t;
} }
} }
} }

View File

@ -22,6 +22,7 @@
#include <blt/iterator/common.h> #include <blt/iterator/common.h>
#include <blt/iterator/zip.h> #include <blt/iterator/zip.h>
#include <blt/iterator/enumerate.h> #include <blt/iterator/enumerate.h>
#include <blt/iterator/flatten.h>
#include <type_traits> #include <type_traits>
#include <iterator> #include <iterator>
#include <tuple> #include <tuple>
@ -29,6 +30,12 @@
namespace blt namespace blt
{ {
template<typename T>
static inline auto iterate(T begin, T end)
{
return iterator::iterator_container<T>{std::move(begin), std::move(end)};
}
template<typename T> template<typename T>
static inline auto iterate(T& container) static inline auto iterate(T& container)
{ {

View File

@ -21,6 +21,7 @@
#include <blt/iterator/common.h> #include <blt/iterator/common.h>
#include <tuple> #include <tuple>
#include <limits>
namespace blt namespace blt
{ {
@ -112,30 +113,15 @@ namespace blt
class zip_iterator_container : public iterator::iterator_container<iterator::zip_wrapper<Iter...>> class zip_iterator_container : public iterator::iterator_container<iterator::zip_wrapper<Iter...>>
{ {
public: public:
using iterator::iterator_container<iterator::zip_wrapper<Iter...>>::iterator_container; using blt::iterator::iterator_container<blt::iterator::zip_wrapper<Iter...>>::iterator_container;
explicit zip_iterator_container(iterator::iterator_pair<Iter>... iterator_pairs): explicit zip_iterator_container(blt::iterator::iterator_pair<Iter>... iterator_pairs):
iterator::iterator_container<iterator::zip_wrapper<Iter...>>(iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.begin)...}, blt::iterator::iterator_container<blt::iterator::zip_wrapper<Iter...>>(blt::iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.begin)...},
iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.end)...}) blt::iterator::zip_wrapper<Iter...>{std::move(iterator_pairs.end)...})
{} {}
}; };
namespace iterator::impl
{
template<typename Derived>
class zip_t
{
public:
template<typename... Iter>
auto zip(iterator::iterator_pair<Iter>... iterator_pairs)
{
auto* d = static_cast<Derived*>(this);
return zip_iterator_container(iterator::iterator_pair<decltype(d->begin())>{d->begin(), d->end()}, iterator_pairs...);
}
};
}
/* /*
* CTAD for the zip containers * CTAD for the zip containers
*/ */

353
include/blt/logging/ansi.h Normal file
View File

@ -0,0 +1,353 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_COLORS_H
#define BLT_LOGGING_COLORS_H
#include <array>
#include <stdexcept>
#include <string>
#include <variant>
#include <blt/std/types.h>
#define BLT_ANSI_ESCAPE "\x1B"
#define BLT_ANSI_CSI BLT_ANSI_ESCAPE "["
#define BLT_ANSI_DSC BLT_ANSI_ESCAPE "P"
#define BLT_ANSI_OSC BLT_ANSI_ESCAPE "]"
// https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
namespace blt::logging::ansi
{
namespace color
{
inline std::array<u8, 10> reset_sequences = {0, 22, 22, 23, 24, 25, 26, 27, 28, 29};
enum class color_mode : u8
{
RESET_ALL = 0,
BOLD = 1,
DIM = 2,
ITALIC = 3,
UNDERLINE = 4,
BLINK = 5,
REVERSE = 7,
HIDDEN = 8,
STRIKE_THROUGH = 9, };
enum class color8 : u8
{
BLACK = 0,
RED = 1,
GREEN = 2,
YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7,
DEFAULT = 9
};
enum class color8_bright : u8
{
BLACK = 0,
RED = 1,
GREEN = 2,
YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7
};
struct rgb_t
{
u8 r, g, b;
};
struct color256
{
explicit color256(u8 index) : color(index)
{}
color256(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
{
if (r > 5)
throw std::invalid_argument("r must be between 0 and 5");
if (g > 5)
throw std::invalid_argument("g must be between 0 and 5");
if (b > 5)
throw std::invalid_argument("b must be between 0 and 5");
}
[[nodiscard]] u8 index() const
{
if (std::holds_alternative<u8>(color))
return std::get<u8>(color);
const auto [r, g, b] = std::get<rgb_t>(color);
return (r * 36) + (g * 6) + b + 16;
}
private:
std::variant<u8, rgb_t> color;
};
struct color_rgb
{
color_rgb(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
{}
rgb_t color;
};
namespace detail
{
template <typename T>
struct color_holder
{
using value_type = T;
T color;
bool alt = false;
};
template <typename T>
struct color_converter
{};
template <>
struct color_converter<color8>
{
static std::string to_string(const color_holder<color8> color)
{
return (color.alt ? "4" : "3") + std::to_string(static_cast<u8>(color.color));
}
};
template <>
struct color_converter<color8_bright>
{
static std::string to_string(const color_holder<color8_bright> color)
{
return (color.alt ? "10" : "9") + std::to_string(static_cast<u8>(color.color));
}
};
template <>
struct color_converter<color_mode>
{
static std::string to_string(const color_holder<color_mode> color)
{
return color.alt ? std::to_string(reset_sequences[static_cast<u8>(color.color)]) : std::to_string(static_cast<u8>(color.color));
}
};
template <>
struct color_converter<color256>
{
static std::string to_string(const color_holder<color256> color)
{
return (color.alt ? "48;5;" : "38;5;") + std::to_string(color.color.index());
}
};
template <>
struct color_converter<color_rgb>
{
static std::string to_string(const color_holder<color_rgb> color)
{
return (color.alt ? "48;2;" : "38;2;") + std::to_string(color.color.color.r) + ";" + std::to_string(color.color.color.g) + ";" +
std::to_string(color.color.color.b);
}
};
template <typename T>
struct ensure_holder
{
static color_holder<T> make(const T& color)
{
return color_holder<T>{color, false};
}
};
template <typename T>
struct ensure_holder<color_holder<T>>
{
static color_holder<T> make(const color_holder<T>& color)
{
return color;
}
};
template<typename T>
struct decay
{
using type = std::decay_t<T>;
};
template<typename T>
struct decay<color_holder<T>>
{
using type = std::decay_t<T>;
};
template<typename T>
using decay_t = typename decay<T>::type;
}
template <typename T>
auto fg(const T& color)
{
return detail::color_holder<T>{color, false};
}
template <typename T>
auto bg(const T& color)
{
return detail::color_holder<T>{color, true};
}
template <typename... Args>
std::string build(const Args&... args)
{
std::string result = BLT_ANSI_CSI;
((result += detail::color_converter<detail::decay_t<Args>>::to_string(detail::ensure_holder<Args>::make(args)), result += ';'), ...);
return result.substr(0, result.size() - 1) + "m";
}
}
namespace general
{
inline const std::string bell = "\x07";
inline const std::string bs = "\x08";
inline const std::string horizontal_tab = "\x09";
inline const std::string linefeed = "\x0A";
inline const std::string vertical_tab = "\x0B";
inline const std::string form_feed = "\x0C";
inline const std::string carriage_return = "\x0D";
inline const std::string escape = BLT_ANSI_ESCAPE;
inline const std::string del = "\x7F";
inline const std::string csi = BLT_ANSI_CSI;
inline const std::string dsc = BLT_ANSI_DSC;
inline const std::string osc = BLT_ANSI_OSC;
}
namespace cursor
{
inline const std::string home = BLT_ANSI_CSI "H";
inline const std::string lower_left_corner = BLT_ANSI_ESCAPE " F";
inline const std::string hide_cursor = BLT_ANSI_CSI "?2 5l";
inline const std::string show_cursor = BLT_ANSI_CSI "?2 5h";
inline const std::string report_position = BLT_ANSI_CSI "6n";
template <bool UseH = true>
std::string move_to(const i64 line, const i64 column)
{
if constexpr (UseH)
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "H";
else
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "f";
}
inline std::string move_up(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "A";
}
inline std::string move_down(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "B";
}
inline std::string move_right(const i64 columns)
{
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "C";
}
inline std::string move_left(const i64 columns)
{
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "D";
}
inline std::string move_begin_down(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "E";
}
inline std::string move_begin_up(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "F";
}
inline std::string move_to(const i64 column)
{
return std::string(BLT_ANSI_CSI) + std::to_string(column) + "G";
}
inline const std::string request_cursor_position = BLT_ANSI_CSI "6n";
inline const std::string move_up_one_line = BLT_ANSI_ESCAPE " M";
inline const std::string save_cursor_position_dec = BLT_ANSI_ESCAPE " 7";
inline const std::string restore_cursor_position_dec = BLT_ANSI_ESCAPE " 8";
inline const std::string save_cursor_position_sco = BLT_ANSI_CSI "s";
inline const std::string restore_cursor_position_sco = BLT_ANSI_CSI "u";
}
namespace scroll
{
inline std::string scroll_up(const int lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "S";
};
}
namespace erase
{
inline const std::string to_end_of_screen = BLT_ANSI_CSI "0J";
inline const std::string from_begin_of_screen = BLT_ANSI_CSI "1J";
inline const std::string entire_screen = BLT_ANSI_CSI "2J";
inline const std::string saved_lines = BLT_ANSI_CSI "3J";
inline const std::string to_end_of_line = BLT_ANSI_CSI "0K";
inline const std::string from_begin_of_line = BLT_ANSI_CSI "1K";
inline const std::string entire_line = BLT_ANSI_CSI "2K";
}
enum class mode : u8
{
mono40x25_text = 0,
color40x25_text = 1,
mono80x25_text = 2,
color80x25_text = 3,
color320x200_4color_graphics = 4,
mono320x200_graphics = 5,
mono640x200_graphics = 6,
line_wrapping = 7,
color320x200_graphics = 13,
color640x200_16color_graphics = 14,
mono640x350_2color_graphics = 15,
color640x350_16color_graphics = 16,
mono640x480_2color_graphics = 17,
color640x480_16color_graphics = 18,
color320_200_256color_graphics = 19
};
inline std::string use_mode(const mode mode)
{
return std::string(BLT_ANSI_CSI) + "=" + std::to_string(static_cast<u8>(mode)) + "h";
}
}
#endif //BLT_LOGGING_COLORS_H

View File

@ -0,0 +1,188 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_FMT_TOKENIZER_H
#define BLT_LOGGING_FMT_TOKENIZER_H
#include <functional>
#include <optional>
#include <string_view>
#include <vector>
#include <blt/std/types.h>
namespace blt::logging
{
enum class fmt_token_type : u8
{
STRING,
NUMBER,
SPACE,
COLON,
DOT,
MINUS,
PLUS,
POUND,
LEFT_CHEVRON,
RIGHT_CHEVRON,
OPEN_BRACKET,
CLOSE_BRACKET,
CARET
};
enum class fmt_align_t : u8
{
LEFT,
CENTER,
RIGHT
};
enum class fmt_sign_t : u8
{
SPACE,
PLUS,
MINUS
};
enum class fmt_type_t : u8
{
BINARY, // 'b'
CHAR, // 'c'
DECIMAL, // 'd'
OCTAL, // 'o'
HEX, // 'x'
HEX_FLOAT, // 'a'
EXPONENT, // 'e'
FIXED_POINT, // 'f'
GENERAL, // 'g'
TYPE, // 't'
UNSPECIFIED // default
};
struct fmt_spec_t
{
i64 arg_id = -1;
i64 width = -1;
i64 precision = -1;
fmt_type_t type = fmt_type_t::UNSPECIFIED;
fmt_sign_t sign = fmt_sign_t::MINUS;
fmt_align_t alignment = fmt_align_t::RIGHT;
std::optional<char> prefix_char;
bool uppercase = false;
bool alternate_form = false;
};
struct fmt_token_t
{
fmt_token_type type;
std::string_view value;
};
class fmt_tokenizer_t
{
public:
explicit fmt_tokenizer_t() = default;
static fmt_token_type get_type(char c);
std::optional<fmt_token_t> next();
std::vector<fmt_token_t> tokenize(std::string_view fmt);
private:
size_t m_pos = 0;
std::string_view m_fmt{};
};
class fmt_parser_t
{
public:
explicit fmt_parser_t(std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>>& handlers): m_handlers(handlers)
{
}
fmt_token_t& peek(const size_t offset)
{
return m_tokens[m_pos + offset];
}
fmt_token_t& peek()
{
return m_tokens[m_pos];
}
[[nodiscard]] bool has_next() const
{
return m_pos < m_tokens.size();
}
[[nodiscard]] bool has_next(const size_t offset) const
{
return (m_pos + offset) < m_tokens.size();
}
[[nodiscard]] fmt_token_t& next()
{
return m_tokens[m_pos++];
}
void consume()
{
++m_pos;
}
void consume(const size_t amount)
{
m_pos += amount;
}
const fmt_spec_t& parse(std::string_view fmt);
private:
static bool is_align_t(fmt_token_type type);
void parse_fmt_field();
void parse_arg_id();
std::string parse_arg_or_number();
void parse_fmt_spec();
void parse_fmt_spec_fill();
void parse_fmt_spec_align();
void parse_fmt_spec_sign();
void parse_fmt_spec_form();
void parse_fmt_spec_width();
void parse_fmt_spec_precision();
void parse_fill();
void parse_align();
void parse_sign();
void parse_form();
void parse_width();
void parse_precision();
void parse_type();
size_t m_pos = 0;
std::vector<fmt_token_t> m_tokens;
fmt_tokenizer_t m_tokenizer;
fmt_spec_t m_spec;
std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>>& m_handlers;
};
}
#endif //BLT_LOGGING_FMT_TOKENIZER_H

View File

@ -0,0 +1,37 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_FWDDECL_H
#define BLT_LOGGING_FWDDECL_H
namespace blt::logging
{
struct logger_t;
enum class fmt_token_type : u8;
enum class fmt_align_t : u8;
enum class fmt_sign_t : u8;
enum class fmt_type_t : u8;
struct fmt_spec_t;
struct fmt_token_t;
class fmt_tokenizer_t;
class fmt_parser_t;
class injector_t;
}
#endif //BLT_LOGGING_FWDDECL_H

View File

@ -0,0 +1,41 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_INJECTOR_H
#define BLT_LOGGING_INJECTOR_H
namespace blt::logging
{
struct injector_output_t
{
std::string new_logging_output;
// should we continue processing the injector call chain?
bool should_continue = true;
// should we log the resulting string at the end of the injector call chain? If false for any injector, it becomes false for all injectors.
bool should_log = true;
};
class injector_t
{
public:
virtual ~injector_t() = default;
virtual injector_output_t inject(const std::string& input) = 0;
};
}
#endif //BLT_LOGGING_INJECTOR_H

View File

@ -0,0 +1,286 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_LOGGING_H
#define BLT_LOGGING_LOGGING_H
#include <cstring>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include <blt/logging/fmt_tokenizer.h>
#include <blt/logging/logging_config.h>
#include <blt/meta/is_streamable.h>
#include <blt/std/utility.h>
namespace blt::logging
{
struct logger_t
{
explicit logger_t(std::ostream& stream): m_stream(stream)
{}
template <typename... Args>
std::string log(std::string fmt, Args&&... args)
{
if (fmt.empty())
return fmt;
auto sequence = std::make_integer_sequence<size_t, sizeof...(Args)>{};
m_arg_print_funcs.clear();
m_arg_print_funcs.resize(sizeof...(Args));
create_conv_funcs(sequence, std::forward<Args>(args)...);
compile(std::move(fmt));
process_strings();
return to_string();
}
[[nodiscard]] std::string to_string() const;
private:
template <typename... Args, size_t... Indexes>
void create_conv_funcs(std::integer_sequence<size_t, Indexes...>, Args&&... args)
{
((handle_func<Indexes>(std::forward<Args>(args))), ...);
}
template <size_t index, typename T>
void handle_func(const T& t)
{
m_arg_print_funcs[index] = [&t](std::ostream& stream, const fmt_spec_t& type) {
switch (type.sign)
{
case fmt_sign_t::SPACE:
if constexpr (std::is_arithmetic_v<T>)
{
if (type.type != fmt_type_t::BINARY && static_cast<i64>(t) >= 0l)
stream << ' ';
}
break;
case fmt_sign_t::PLUS:
case fmt_sign_t::MINUS:
break;
}
switch (type.type)
{
case fmt_type_t::BINARY:
{
if constexpr (std::is_trivially_copyable_v<T>)
{
// copy bytes of type
char buffer[sizeof(T)];
std::memcpy(buffer, &t, sizeof(T));
// display prefix
if (type.alternate_form)
stream << '0' << (type.uppercase ? 'B' : 'b');
// print bytes
for (size_t i = 0; i < sizeof(T); ++i)
{
// print bits
for (size_t j = 0; j < 8; ++j)
stream << ((buffer[i] & (1 << j)) ? '1' : '0');
// special seperator defined via sign (weird hack, change?)
if (type.sign == fmt_sign_t::SPACE && i != sizeof(T) - 1)
stream << ' ';
}
} else
{
if constexpr (blt::meta::is_streamable_v<T>)
stream << t;
else
stream << "{INVALID TYPE}";
}
break;
}
case fmt_type_t::CHAR:
if constexpr (std::is_arithmetic_v<T> || std::is_convertible_v<T, char>)
{
stream << static_cast<char>(t);
} else
{
if constexpr (blt::meta::is_streamable_v<T>)
stream << t;
else
stream << "{INVALID TYPE}";
}
break;
case fmt_type_t::TYPE:
stream << blt::type_string<T>();
break;
default:
handle_type(stream, type);
if constexpr (blt::meta::is_streamable_v<T>)
{
if constexpr (std::is_same_v<T, char> || std::is_same_v<T, signed char>)
stream << static_cast<int>(t);
else if constexpr (std::is_same_v<T, unsigned char>)
stream << static_cast<unsigned int>(t);
else
stream << t;
} else
stream << "{INVALID TYPE}";
}
};
}
[[nodiscard]] size_t find_ending_brace(size_t begin) const;
void setup_stream(const fmt_spec_t& spec) const;
std::string process_string(std::string_view str);
void process_strings();
static void handle_type(std::ostream& stream, const fmt_spec_t& spec);
static void exponential(std::ostream& stream);
static void fixed(std::ostream& stream);
void compile(std::string fmt);
std::optional<std::pair<size_t, size_t>> consume_to_next_fmt();
std::string m_fmt;
std::ostream& m_stream;
fmt_parser_t m_parser{m_arg_print_funcs};
// normal sections of string
std::vector<std::string_view> m_string_sections;
// processed format specs
std::vector<fmt_spec_t> m_fmt_specs;
std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>> m_arg_print_funcs;
size_t m_last_fmt_pos = 0;
size_t m_arg_pos = 0;
};
void print(std::string str);
void newline();
logger_t& get_global_logger();
logging_config_t& get_global_config();
std::ostream& get_local_stream();
void set_thread_name(const std::string& name);
const std::string& get_thread_name();
template <typename... Args>
void print(std::string fmt, Args&&... args)
{
auto& logger = get_global_logger();
print(logger.log(std::move(fmt), std::forward<Args>(args)...));
}
template <typename... Args>
void print(std::ostream& stream, std::string fmt, Args&&... args)
{
auto& logger = get_global_logger();
stream << logger.log(std::move(fmt), std::forward<Args>(args)...);
}
template <typename... Args>
void println(std::string fmt, Args&&... args)
{
print(std::move(fmt), std::forward<Args>(args)...);
newline();
}
template <typename... Args>
void println(std::ostream& stream, std::string fmt, Args&&... args)
{
print(stream, std::move(fmt), std::forward<Args>(args)...);
stream << std::endl;
}
template <typename... Args>
void log(const log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args)
{
auto& logger = get_global_logger();
const auto& config = get_global_config();
std::string user_str = logger.log(std::move(fmt), std::forward<Args>(args)...);
if (!user_str.empty() && user_str.back() == '\n')
user_str.pop_back();
if (level == log_level_t::NONE)
{
print(user_str);
newline();
return;
}
auto log_fmt_str = config.generate(user_str, get_thread_name(), level, file, line);
if (log_fmt_str)
print(std::move(*log_fmt_str));
}
namespace detail
{
}
}
#if defined(__clang__) || defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#ifdef BLT_DISABLE_LOGGING
#define BLT_LOG(level, fmt, ...)
#else
#define BLT_LOG(level, fmt, ...) blt::logging::log(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#ifdef BLT_DISABLE_TRACE
#define BLT_TRACE(format, ...)
#else
#define BLT_TRACE(format, ...) BLT_LOG(blt::logging::log_level_t::TRACE, format, ##__VA_ARGS__)
#endif
#ifdef BLT_DISABLE_DEBUG
#define BLT_DEBUG(format, ...)
#else
#define BLT_DEBUG(format, ...) BLT_LOG(blt::logging::log_level_t::DEBUG, format, ##__VA_ARGS__)
#endif
#ifdef BLT_DISABLE_INFO
#define BLT_INFO(format, ...)
#else
#define BLT_INFO(format, ...) BLT_LOG(blt::logging::log_level_t::INFO, format, ##__VA_ARGS__)
#endif
#ifdef BLT_DISABLE_WARN
#define BLT_WARN(format, ...)
#else
#define BLT_WARN(format, ...) BLT_LOG(blt::logging::log_level_t::WARN, format, ##__VA_ARGS__)
#endif
#ifdef BLT_DISABLE_ERROR
#define BLT_ERROR(format, ...)
#else
#define BLT_ERROR(format, ...) BLT_LOG(blt::logging::log_level_t::ERROR, format, ##__VA_ARGS__)
#endif
#ifdef BLT_DISABLE_FATAL
#define BLT_FATAL(format, ...)
#else
#define BLT_FATAL(format, ...) BLT_LOG(blt::logging::log_level_t::FATAL, format, ##__VA_ARGS__)
#endif
#endif
#if defined(__clang__) || defined(__llvm__)
#pragma clang diagnostic pop
#endif
#endif // BLT_LOGGING_LOGGING_H

View File

@ -0,0 +1,243 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_LOGGING_CONFIG_H
#define BLT_LOGGING_LOGGING_CONFIG_H
#include <array>
#include <optional>
#include <string>
#include <vector>
#include <blt/fs/fwddecl.h>
#include <blt/logging/fwddecl.h>
#include <blt/std/types.h>
namespace blt::logging
{
namespace tags
{
// Current Year
inline constexpr auto YEAR = "{YEAR}";
// Current Month
inline constexpr auto MONTH = "{MONTH}";
// Current Day
inline constexpr auto DAY = "{DAY}";
// Current Hour
inline constexpr auto HOUR = "{HOUR}";
// Current Minute
inline constexpr auto MINUTE = "{MINUTE}";
// Current Second
inline constexpr auto SECOND = "{SECOND}";
// Current Millisecond
inline constexpr auto MILLISECOND = "{MS}";
// Current Nanosecond
inline constexpr auto NANOSECOND = "{NS}";
// Current Unix time in milliseconds
inline constexpr auto UNIX_TIME = "{UNIX}";
// Current Unix time in nanosecond
inline constexpr auto UNIX_TIME_NANO = "{UNIX_NANO}";
// Formatted ISO year-month-day in a single variable
inline constexpr auto ISO_YEAR = "{ISO_YEAR}";
// Formatted hour:minute:second in a single variable
inline constexpr auto TIME = "{TIME}";
// Formatted year-month-day hour:minute:second in a single variable
inline constexpr auto FULL_TIME = "{FULL_TIME}";
// Color of the current log level, empty string if use_color = false
inline constexpr auto LOG_COLOR = "{LC}";
// Color of the error color, empty string if use_color = false
inline constexpr auto ERROR_COLOR = "{EC}";
// Empty is use_color = false or if log level is not an error. Otherwise, {EC}
inline constexpr auto CONDITIONAL_ERROR_COLOR = "{CEC}";
// Resets all ANSI sequences
inline constexpr auto RESET = "{RESET}";
// Current log level
inline constexpr auto LOG_LEVEL = "{LL}";
// Current thread name. Requires you to manually set the thread name using blt::logging::set_thread_name() from that thread.
inline constexpr auto THREAD_NAME = "{TN}";
// Current file from where the log call was invoked.
inline constexpr auto FILE = "{FILE}";
// Current line from where the log call was invoked
inline constexpr auto LINE = "{LINE}";
// User string input, formatted with provided args
inline constexpr auto STR = "{STR}";
namespace detail
{
enum class log_tag_token_t : u8
{
YEAR,
MONTH,
DAY,
HOUR,
MINUTE,
SECOND,
MS,
NS,
UNIX,
UNIX_NANO,
ISO_YEAR,
TIME,
FULL_TIME,
LC,
EC,
CEC,
RESET,
LL,
TN,
FILE,
LINE,
STR,
// token used to describe that a non-format token should be consumed. aka a normal string from the file.
CONTENT
};
}
}
enum class log_level_t : u8
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NONE
};
inline constexpr size_t LOG_LEVEL_COUNT = 6;
class logging_config_t
{
friend logger_t;
public:
logging_config_t()
{
compile();
}
void compile();
logging_config_t& add_log_output(fs::writer_t& writer)
{
m_log_outputs.push_back(&writer);
return *this;
}
logging_config_t& add_injector(injector_t& injector)
{
m_injectors.push_back(&injector);
return *this;
}
logging_config_t& set_log_format(std::string format)
{
m_log_format = std::move(format);
compile();
return *this;
}
logging_config_t& set_error_color(std::string color)
{
m_error_color = std::move(color);
compile();
return *this;
}
logging_config_t& set_log_level_colors(std::array<std::string, LOG_LEVEL_COUNT> colors)
{
m_log_level_colors = std::move(colors);
compile();
return *this;
}
logging_config_t& set_log_level_names(std::array<std::string, LOG_LEVEL_COUNT> names)
{
m_log_level_names = std::move(names);
return *this;
}
logging_config_t& set_level(const log_level_t level)
{
this->m_level = level;
return *this;
}
logging_config_t& set_use_color(const bool use_color)
{
this->m_use_color = use_color;
compile();
return *this;
}
logging_config_t& set_print_full_name(const bool print_full_name)
{
this->m_print_full_name = print_full_name;
return *this;
}
logging_config_t& set_ensure_alignment(const bool ensure_alignment)
{
this->m_ensure_alignment = ensure_alignment;
return *this;
}
[[nodiscard]] std::pair<const std::vector<tags::detail::log_tag_token_t>&, const std::vector<std::string>&> get_log_tag_tokens() const
{
return {m_log_tag_tokens, m_log_tag_content};
}
std::optional<std::string> generate(const std::string& user_str, const std::string& thread_name, log_level_t level, const char* file,
i32 line) const;
[[nodiscard]] const std::vector<injector_t*>& get_injectors() const
{
return m_injectors;
}
private:
std::vector<injector_t*> m_injectors;
// wrappers for streams exist in blt/fs/stream_wrappers.h
std::vector<fs::writer_t*> m_log_outputs = get_default_log_outputs();
std::string m_log_format = get_default_log_format();
std::string m_error_color = get_default_error_color();
std::array<std::string, LOG_LEVEL_COUNT> m_log_level_colors = get_default_log_level_colors();
std::array<std::string, LOG_LEVEL_COUNT> m_log_level_names = get_default_log_level_names();
log_level_t m_level = log_level_t::TRACE;
bool m_use_color = true;
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
bool m_print_full_name = false;
// this will attempt to use the maximum possible size for each printed element, then align to that.
// This creates output where the user message always starts at the same column.
bool m_ensure_alignment = true;
size_t m_longest_name_length = 0;
std::vector<std::string> m_log_tag_content;
std::vector<tags::detail::log_tag_token_t> m_log_tag_tokens;
static std::string get_default_log_format();
static std::vector<fs::writer_t*> get_default_log_outputs();
static std::array<std::string, LOG_LEVEL_COUNT> get_default_log_level_colors();
static std::array<std::string, LOG_LEVEL_COUNT> get_default_log_level_names();
static std::string get_default_error_color();
};
}
#endif //BLT_LOGGING_LOGGING_CONFIG_H

View File

@ -0,0 +1,105 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_STATUS_H
#define BLT_LOGGING_STATUS_H
#include <mutex>
#include <string>
#include <blt/logging/injector.h>
#include <blt/math/vectors.h>
#include <blt/std/types.h>
namespace blt::logging
{
class status_bar_t;
class status_item_t
{
public:
virtual ~status_item_t() = default;
void assign(status_bar_t* bar)
{
m_status = bar;
}
[[nodiscard]] virtual i32 lines_used() const
{
return 1;
}
[[nodiscard]] virtual std::string print(vec2i screen_size, i32 max_printed_length) const = 0;
protected:
status_bar_t* m_status = nullptr;
};
class status_progress_bar_t final : public status_item_t
{
public:
[[nodiscard]] std::string print(vec2i screen_size, i32 max_printed_length) const override;
void set_progress(double progress);
void add_progress(const double progress)
{
set_progress(get_progress() + progress);
}
[[nodiscard]] double get_progress() const
{
return m_progress;
}
private:
double m_progress = 0.0;
};
class status_bar_t final : public injector_t
{
public:
explicit status_bar_t();
injector_output_t inject(const std::string& input) override;
status_bar_t& add(status_item_t& item)
{
item.assign(this);
m_status_items.push_back(&item);
compute_size();
return *this;
}
void redraw() const;
~status_bar_t() override;
private:
void compute_size();
std::vector<status_item_t*> m_status_items;
i32 m_status_size = 0;
i32 m_max_printed_length = 0;
vec2i m_screen_size;
vec2i m_last_log_position;
vec2i m_begin_position;
std::mutex m_print_mutex;
};
}
#endif //BLT_LOGGING_STATUS_H

170
include/blt/math/aabb.h Normal file
View File

@ -0,0 +1,170 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_MATH_AABB_H
#define BLT_MATH_AABB_H
#include <array>
#include <blt/math/vectors.h>
#include <blt/std/types.h>
namespace blt
{
// yes I could use the vector (see tower defense game commit log for this)
// this feels nicer
template <typename T = float>
class axis_t
{
public:
axis_t(const T min, const T max): m_min(min), m_max(max)
{}
[[nodiscard]] bool intersects(const T p) const
{
return p >= m_min && p <= m_max;
}
template <typename G>
[[nodiscard]] bool intersects(const axis_t<G>& other) const
{
return static_cast<T>(other.m_min) <= m_max && static_cast<T>(other.m_max) >= m_min;
}
[[nodiscard]] T min() const
{
return m_min;
}
[[nodiscard]] T max() const
{
return m_max;
}
[[nodiscard]] T length() const
{
return m_max - m_min;
}
private:
T m_min, m_max;
};
namespace detail
{
template <u32 Axis, typename T>
class axis_aligned_bounding_box_base_t
{
public:
[[nodiscard]] vec<T, Axis> get_center() const
{
vec<T, Axis> min;
for (u32 i = 0; i < Axis; i++)
min[i] = m_axes[i].min();
const auto center = get_size() / 2.0f;
return min + center;
}
[[nodiscard]] vec<T, Axis> get_size() const
{
vec<T, Axis> size;
for (u32 i = 0; i < Axis; i++)
size[i] = m_axes[i].length();
return size;
}
template <typename G>
[[nodiscard]] bool intersects(const axis_aligned_bounding_box_base_t<Axis, G>& other) const
{
for (u32 i = 0; i < Axis; i++)
if (!m_axes[i].intersects(other.m_axes[i]))
return false;
return true;
}
template <typename G>
[[nodiscard]] bool intersects(const vec<G, Axis>& point) const
{
for (u32 i = 0; i < Axis; i++)
if (!m_axes[i].intersects(point[i]))
return false;
return true;
}
axis_t<T>& operator[](u32 i)
{
return m_axes[i];
}
axis_t<T>& axis(u32 i)
{
if (i >= Axis)
throw std::out_of_range("Axis index out of range");
return m_axes[i];
}
protected:
std::array<axis_t<T>, Axis> m_axes;
};
}
template <u32 Axis, typename T>
class axis_aligned_bounding_box_t : public detail::axis_aligned_bounding_box_base_t<Axis, T>
{
public:
using detail::axis_aligned_bounding_box_base_t<Axis, T>::axis_aligned_bounding_box_base_t;
};
template <typename T>
class axis_aligned_bounding_box_t<2, T> : public detail::axis_aligned_bounding_box_base_t<2, T>
{
public:
using detail::axis_aligned_bounding_box_base_t<2, T>::axis_aligned_bounding_box_base_t;
[[nodiscard]] vec2 min() const
{
return {this->m_axes[0].min(), this->m_axes[1].min()};
}
[[nodiscard]] vec2 max() const
{
return {this->m_axes[0].max(), this->m_axes[1].max()};
}
};
template <typename T>
class axis_aligned_bounding_box_t<3, T> : public detail::axis_aligned_bounding_box_base_t<3, T>
{
public:
using detail::axis_aligned_bounding_box_base_t<2, T>::axis_aligned_bounding_box_base_t;
[[nodiscard]] vec3 min() const
{
return {this->m_axes[0].min(), this->m_axes[1].min(), this->m_axes[2].min()};
}
[[nodiscard]] vec3s max() const
{
return {this->m_axes[0].max(), this->m_axes[1].max(), this->m_axes[2].max()};
}
};
using aabb_2d_t = axis_aligned_bounding_box_t<2, float>;
using aabb_3d_t = axis_aligned_bounding_box_t<3, float>;
}
#endif //BLT_MATH_AABB_H

View File

@ -0,0 +1,28 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_MATH_BOUNDING_BOX_H
#define BLT_MATH_BOUNDING_BOX_H
#include <blt/math/aabb.h>
namespace blt {
}
#endif //BLT_MATH_BOUNDING_BOX_H

View File

@ -9,7 +9,7 @@
#include <blt/math/vectors.h> #include <blt/math/vectors.h>
#include <blt/math/matrix.h> #include <blt/math/matrix.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <blt/std/utility.h> #include <blt/std/utility.h>
#include "blt/std/string.h" #include "blt/std/string.h"

View File

@ -12,6 +12,7 @@
#include <type_traits> #include <type_traits>
#include <array> #include <array>
#include <initializer_list> #include <initializer_list>
#include "blt/iterator/iterator.h"
#ifndef M_PI #ifndef M_PI
// MSVC does not have M_PI // MSVC does not have M_PI
@ -24,6 +25,10 @@ namespace blt
template<typename T, blt::u32 rows, blt::u32 columns> template<typename T, blt::u32 rows, blt::u32 columns>
class generalized_matrix class generalized_matrix
{ {
public:
static constexpr auto data_rows = rows;
static constexpr auto data_columns = columns;
private:
using matrix_t = generalized_matrix<T, rows, columns>; using matrix_t = generalized_matrix<T, rows, columns>;
enum class init_type enum class init_type
{ {
@ -163,6 +168,14 @@ namespace blt
return copy; return copy;
} }
[[nodiscard]] constexpr matrix_t bipolar() const
{
matrix_t copy = *this;
for (auto& v : copy.data)
v = v.bipolar();
return copy;
}
constexpr inline const blt::vec<T, rows>& operator[](u32 column) const constexpr inline const blt::vec<T, rows>& operator[](u32 column) const
{ {
return data[column]; return data[column];

View File

@ -17,377 +17,406 @@
namespace blt namespace blt
{ {
#define MSVC_COMPILER (!defined(__GNUC__) && !defined(__clang__)) #define MSVC_COMPILER (!defined(__GNUC__) && !defined(__clang__))
constexpr float EPSILON = std::numeric_limits<float>::epsilon(); constexpr float EPSILON = std::numeric_limits<float>::epsilon();
static inline constexpr bool f_equal(float v1, float v2) static constexpr bool f_equal(const float v1, const float v2, const float range = 1)
{ {
return v1 >= v2 - EPSILON && v1 <= v2 + EPSILON; return v1 >= v2 - (EPSILON * range) && v1 <= v2 + (EPSILON * range);
} }
template<typename T, blt::u32 size> template <typename T, blt::u32 size>
struct vec struct vec
{ {
static_assert(std::is_arithmetic_v<T> && "blt::vec must be created using an arithmetic type!"); static_assert(std::is_arithmetic_v<T> && "blt::vec must be created using an arithmetic type!");
private:
std::array<T, size> elements;
public:
constexpr vec()
{
for (auto& v : elements)
v = static_cast<T>(0);
}
/** private:
* Create a vector with initializer list, if the initializer list doesn't contain enough values to fill this vec, it will use t std::array<T, size> elements;
* @param t default value to fill with
* @param args list of args public:
*/ constexpr static blt::u32 data_size = size;
template<typename U, std::enable_if_t<std::is_same_v<T, U> || std::is_convertible_v<U, T>, bool> = true>
constexpr vec(U t, std::initializer_list<U> args): elements() constexpr vec()
{
for (auto& v : elements)
v = static_cast<T>(0);
}
/**
* Create a vector with initializer list, if the initializer list doesn't contain enough values to fill this vec, it will use t
* @param t default value to fill with
* @param args list of args
*/
template <typename U, std::enable_if_t<std::is_same_v<T, U> || std::is_convertible_v<U, T>, bool> = true>
constexpr vec(U t, std::initializer_list<U> args): elements()
{
auto b = args.begin();
for (auto& v : elements)
{ {
auto b = args.begin(); if (b == args.end())
for (auto& v : elements)
{ {
if (b == args.end())
{
v = t;
continue;
}
v = *b;
++b;
}
}
/**
* Create a vector from an initializer list, if the list doesn't have enough elements it will be filled with the default value (0)
* @param args
*/
template<typename U, std::enable_if_t<std::is_same_v<T, U> || std::is_convertible_v<U, T>, bool> = true>
constexpr vec(std::initializer_list<U> args): vec(U(), args)
{}
template<typename... Args, std::enable_if_t<sizeof...(Args) == size, bool> = true>
constexpr explicit vec(Args... args): vec(std::array<T, size>{static_cast<T>(args)...})
{}
constexpr explicit vec(T t)
{
for (auto& v : elements)
v = t; v = t;
} continue;
constexpr explicit vec(const T elem[size])
{
for (size_t i = 0; i < size; i++)
elements[i] = elem[i];
}
constexpr explicit vec(std::array<T, size> elem): elements(elem)
{}
template<typename G, size_t base_size, std::enable_if_t<std::is_convertible_v<G, T>, bool> = true>
constexpr explicit vec(std::array<G, base_size> el): elements()
{
auto b = el.begin();
auto m = elements.begin();
while (b != el.end() && m != elements.end())
{
*m = *b;
++m;
++b;
} }
v = *b;
++b;
} }
}
[[nodiscard]] constexpr inline T x() const /**
{ * Create a vector from an initializer list, if the list doesn't have enough elements it will be filled with the default value (0)
return elements[0]; * @param args
} */
template <typename U, std::enable_if_t<std::is_same_v<T, U> || std::is_convertible_v<U, T>, bool> = true>
constexpr vec(std::initializer_list<U> args): vec(U(), args)
{
}
[[nodiscard]] constexpr inline T y() const template <typename... Args, std::enable_if_t<sizeof...(Args) == size, bool> = true>
{ constexpr explicit vec(Args... args): vec(std::array<T, size>{static_cast<T>(args)...})
static_assert(size > 1); {
return elements[1]; }
}
[[nodiscard]] constexpr inline T z() const constexpr explicit vec(T t)
{ {
static_assert(size > 2); for (auto& v : elements)
return elements[2]; v = t;
} }
[[nodiscard]] constexpr inline T w() const constexpr explicit vec(const T elem[size])
{ {
static_assert(size > 3); for (size_t i = 0; i < size; i++)
return elements[3]; elements[i] = elem[i];
} }
[[nodiscard]] constexpr inline vec<T, size> abs() const constexpr explicit vec(std::array<T, size> elem): elements(elem)
{ {
auto copy = *this; }
for (auto& v : copy.elements)
v = std::abs(v);
return copy;
}
[[nodiscard]] constexpr inline T magnitude() const template <typename G, size_t base_size, std::enable_if_t<std::is_convertible_v<G, T>, bool> = true>
constexpr explicit vec(std::array<G, base_size> el): elements()
{
auto b = el.begin();
auto m = elements.begin();
while (b != el.end() && m != elements.end())
{ {
T total = 0; *m = *b;
for (blt::u32 i = 0; i < size; i++) ++m;
total += elements[i] * elements[i]; ++b;
return std::sqrt(total);
} }
}
[[nodiscard]] constexpr inline vec<T, size> normalize() const [[nodiscard]] const std::array<T, size>& to_array() const
{ {
T mag = this->magnitude(); return elements;
if (mag == 0) }
return vec<T, size>(*this);
return *this / mag;
}
constexpr inline T& operator[](blt::size_t index) [[nodiscard]] constexpr inline T x() const
{ {
return elements[index]; return elements[0];
} }
constexpr inline T operator[](blt::size_t index) const [[nodiscard]] constexpr inline T y() const
{ {
return elements[index]; static_assert(size > 1);
} return elements[1];
}
constexpr inline vec<T, size>& operator=(T v) [[nodiscard]] constexpr inline T z() const
{ {
for (blt::u32 i = 0; i < size; i++) static_assert(size > 2);
elements[i] = v; return elements[2];
return *this; }
}
constexpr inline vec<T, size> operator-() [[nodiscard]] constexpr inline T w() const
{ {
vec<T, size> initializer{}; static_assert(size > 3);
for (blt::u32 i = 0; i < size; i++) return elements[3];
initializer[i] = -elements[i]; }
return vec<T, size>{initializer};
}
constexpr inline vec<T, size>& operator+=(const vec<T, size>& other) [[nodiscard]] constexpr inline vec<T, size> abs() const
{ {
for (blt::u32 i = 0; i < size; i++) auto copy = *this;
elements[i] += other[i]; for (auto& v : copy.elements)
return *this; v = std::abs(v);
} return copy;
}
constexpr inline vec<T, size>& operator*=(const vec<T, size>& other) [[nodiscard]] constexpr inline vec<T, size> bipolar() const
{ {
for (blt::u32 i = 0; i < size; i++) auto copy = *this;
elements[i] *= other[i]; for (auto& v : copy.elements)
return *this; v = v >= 0 ? 1 : -1;
} return copy;
}
constexpr inline vec<T, size>& operator+=(T f) [[nodiscard]] constexpr inline T magnitude() const
{ {
for (blt::u32 i = 0; i < size; i++) T total = 0;
elements[i] += f; for (blt::u32 i = 0; i < size; i++)
return *this; total += elements[i] * elements[i];
} return std::sqrt(total);
}
constexpr inline vec<T, size>& operator*=(T f) [[nodiscard]] constexpr inline vec<T, size> normalize() const
{ {
for (blt::u32 i = 0; i < size; i++) T mag = this->magnitude();
elements[i] *= f; if (mag == 0)
return *this; return vec<T, size>(*this);
} return *this / mag;
}
constexpr inline vec<T, size>& operator-=(const vec<T, size>& other) constexpr inline T& operator[](blt::size_t index)
{ {
for (blt::u32 i = 0; i < size; i++) return elements[index];
elements[i] -= other[i]; }
return *this;
}
constexpr inline vec<T, size>& operator-=(T f) constexpr inline T operator[](blt::size_t index) const
{ {
for (blt::u32 i = 0; i < size; i++) return elements[index];
elements[i] -= f; }
return *this;
}
/** constexpr inline vec<T, size>& operator=(T v)
* performs the dot product of left * right {
*/ for (blt::u32 i = 0; i < size; i++)
constexpr static inline T dot(const vec<T, size>& left, const vec<T, size>& right) elements[i] = v;
{ return *this;
T dot = 0; }
for (blt::u32 i = 0; i < size; i++)
dot += left[i] * right[i];
return dot;
}
constexpr static inline vec<T, size> cross( constexpr inline vec<T, size> operator-()
const vec<T, size>& left, const vec<T, size>& right {
) vec<T, size> initializer{};
{ for (blt::u32 i = 0; i < size; i++)
// cross is only defined on vectors of size 3. 2D could be implemented, which is a TODO initializer[i] = -elements[i];
static_assert(size == 3); return vec<T, size>{initializer};
return {left.y() * right.z() - left.z() * right.y(), }
left.z() * right.x() - left.x() * right.z(),
left.x() * right.y() - left.y() * right.x()};
}
constexpr static inline vec<T, size> project( constexpr inline vec<T, size>& operator+=(const vec<T, size>& other)
const vec<T, size>& u, const vec<T, size>& v {
) for (blt::u32 i = 0; i < size; i++)
{ elements[i] += other[i];
T du = dot(u); return *this;
T dv = dot(v); }
return (du / dv) * v;
}
constexpr inline auto* data() constexpr inline vec<T, size>& operator*=(const vec<T, size>& other)
{ {
return elements.data(); for (blt::u32 i = 0; i < size; i++)
} elements[i] *= other[i];
return *this;
}
[[nodiscard]] constexpr inline const auto* data() const constexpr inline vec<T, size>& operator+=(T f)
{ {
return elements.data(); for (blt::u32 i = 0; i < size; i++)
} elements[i] += f;
return *this;
}
constexpr auto begin() constexpr inline vec<T, size>& operator*=(T f)
{ {
return elements.begin(); for (blt::u32 i = 0; i < size; i++)
} elements[i] *= f;
return *this;
}
constexpr auto end() constexpr inline vec<T, size>& operator-=(const vec<T, size>& other)
{ {
return elements.end(); for (blt::u32 i = 0; i < size; i++)
} elements[i] -= other[i];
return *this;
}
constexpr auto rbegin() constexpr inline vec<T, size>& operator-=(T f)
{ {
return elements.rbegin(); for (blt::u32 i = 0; i < size; i++)
} elements[i] -= f;
return *this;
}
constexpr auto rend() /**
{ * performs the dot product of left * right
return elements.rend(); */
} constexpr static inline T dot(const vec<T, size>& left, const vec<T, size>& right)
{
T dot = 0;
for (blt::u32 i = 0; i < size; i++)
dot += left[i] * right[i];
return dot;
}
[[nodiscard]] constexpr auto cbegin() const constexpr static inline vec<T, size> cross(
{ const vec<T, size>& left, const vec<T, size>& right
return elements.cbegin(); )
} {
// cross is only defined on vectors of size 3. 2D could be implemented, which is a TODO
static_assert(size == 3);
return {
left.y() * right.z() - left.z() * right.y(),
left.z() * right.x() - left.x() * right.z(),
left.x() * right.y() - left.y() * right.x()
};
}
[[nodiscard]] constexpr auto cend() const constexpr static inline vec<T, size> project(
{ const vec<T, size>& u, const vec<T, size>& v
return elements.cend(); )
} {
T du = dot(u);
T dv = dot(v);
return (du / dv) * v;
}
constexpr inline auto* data()
{
return elements.data();
}
[[nodiscard]] constexpr inline const auto* data() const
{
return elements.data();
}
[[nodiscard]] constexpr auto begin() const
{
return elements.begin();
}
[[nodiscard]] constexpr auto end() const
{
return elements.end();
}
[[nodiscard]] constexpr auto rbegin() const
{
return elements.rbegin();
}
[[nodiscard]] constexpr auto rend() const
{
return elements.rend();
}
[[nodiscard]] constexpr auto cbegin() const
{
return elements.cbegin();
}
[[nodiscard]] constexpr auto cend() const
{
return elements.cend();
}
friend std::size_t hash_value(const vec& obj)
{
std::size_t seed = 0x5410391E;
for (const auto& v : obj)
seed ^= (seed << 6) + (seed >> 2) + std::hash<T>{}(v);
return seed;
}
}; };
template<typename T, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() + std::declval<G>())>
inline constexpr vec<T, size> operator+(const vec<T, size>& left, const vec<T, size>& right) inline constexpr vec<R, size> operator+(const vec<T, size>& left, const vec<G, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] + right[i]; initializer[i] = static_cast<R>(left[i]) + static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() - std::declval<G>())>
inline constexpr vec<T, size> operator-(const vec<T, size>& left, const vec<T, size>& right) inline constexpr vec<R, size> operator-(const vec<T, size>& left, const vec<G, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] - right[i]; initializer[i] = static_cast<R>(left[i]) - static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() + std::declval<G>())>
inline constexpr vec<T, size> operator+(const vec<T, size>& left, G right) inline constexpr vec<R, size> operator+(const vec<T, size>& left, G right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] + static_cast<T>(right); initializer[i] = static_cast<R>(left[i]) + static_cast<R>(right);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() - std::declval<G>())>
inline constexpr vec<T, size> operator-(const vec<T, size>& left, G right) inline constexpr vec<R, size> operator-(const vec<T, size>& left, G right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] + static_cast<T>(right); initializer[i] = static_cast<R>(left[i]) - static_cast<R>(right);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() + std::declval<G>())>
inline constexpr vec<T, size> operator+(G left, const vec<T, size>& right) inline constexpr vec<R, size> operator+(G left, const vec<T, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = static_cast<T>(left) + right[i]; initializer[i] = static_cast<R>(left) + static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() - std::declval<G>())>
inline constexpr vec<T, size> operator-(G left, const vec<T, size>& right) inline constexpr vec<R, size> operator-(G left, const vec<T, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = static_cast<T>(left) - right[i]; initializer[i] = static_cast<R>(left) - static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() * std::declval<G>())>
inline constexpr vec<T, size> operator*(const vec<T, size>& left, const vec<T, size>& right) inline constexpr vec<R, size> operator*(const vec<T, size>& left, const vec<G, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] * right[i]; initializer[i] = static_cast<R>(left[i]) * static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() * std::declval<G>())>
inline constexpr vec<T, size> operator*(const vec<T, size>& left, G right) inline constexpr vec<R, size> operator*(const vec<T, size>& left, G right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] * static_cast<T>(right); initializer[i] = static_cast<R>(left[i]) * static_cast<R>(right);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() * std::declval<G>())>
inline constexpr vec<T, size> operator*(G left, const vec<T, size>& right) inline constexpr vec<R, size> operator*(G left, const vec<T, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = static_cast<T>(left) * right[i]; initializer[i] = static_cast<R>(left) * static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() / std::declval<G>())>
inline constexpr vec<T, size> operator/(const vec<T, size>& left, G right) inline constexpr vec<R, size> operator/(const vec<T, size>& left, G right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = left[i] / static_cast<T>(right); initializer[i] = static_cast<R>(left[i]) / static_cast<R>(right);
return initializer; return initializer;
} }
template<typename T, typename G, blt::u32 size> template <typename T, typename G, blt::u32 size, typename R = decltype(std::declval<T>() / std::declval<G>())>
inline constexpr vec<T, size> operator/(G left, const vec<T, size>& right) inline constexpr vec<R, size> operator/(G left, const vec<T, size>& right)
{ {
vec<T, size> initializer{}; vec<R, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
initializer[i] = static_cast<T>(left) / right[i]; initializer[i] = static_cast<R>(left) / static_cast<R>(right[i]);
return initializer; return initializer;
} }
template<typename T, blt::u32 size> template <typename T, typename G, blt::u32 size>
inline constexpr bool operator==(const vec<T, size>& left, const vec<T, size>& right) inline constexpr bool operator==(const vec<T, size>& left, const vec<G, size>& right)
{ {
constexpr double E = std::numeric_limits<T>::epsilon(); constexpr double E = std::numeric_limits<T>::epsilon();
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
@ -399,19 +428,63 @@ namespace blt
return true; return true;
} }
template<typename T, blt::u32 size> template <typename T, typename G, u32 size>
inline constexpr bool operator!=(const vec<T, size>& left, const vec<T, size>& right) constexpr bool operator>=(const vec<T, size>& left, const vec<G, size>& right)
{
for (u32 i = 0; i < size; i++)
{
if (left[i] < right[i])
return false;
}
return true;
}
template <typename T, typename G, u32 size>
constexpr bool operator>(const vec<T, size>& left, const vec<G, size>& right)
{
for (u32 i = 0; i < size; i++)
{
if (left[i] <= right[i])
return false;
}
return true;
}
template <typename T, typename G, u32 size>
constexpr bool operator<(const vec<T, size>& left, const vec<G, size>& right)
{
for (u32 i = 0; i < size; i++)
{
if (left[i] >= right[i])
return false;
}
return true;
}
template <typename T, typename G, u32 size>
constexpr bool operator<=(const vec<T, size>& left, const vec<G, size>& right)
{
for (u32 i = 0; i < size; i++)
{
if (left[i] > right[i])
return false;
}
return true;
}
template <typename T, typename G, blt::u32 size>
inline constexpr bool operator!=(const vec<T, size>& left, const vec<G, size>& right)
{ {
return !(left == right); return !(left == right);
} }
template<typename T, blt::u32 size> template <typename Ret, typename T, blt::u32 size>
inline constexpr bool operator&&(const vec<T, size>& left, const vec<T, size>& right) inline constexpr vec<Ret, size> vec_cast(const vec<T, size>& conv)
{ {
vec<Ret, size> initializer{};
for (blt::u32 i = 0; i < size; i++) for (blt::u32 i = 0; i < size; i++)
if (!f_equal(left[i], right[i])) initializer[i] = static_cast<Ret>(conv[i]);
return false; return initializer;
return true;
} }
using vec2f = vec<float, 2>; using vec2f = vec<float, 2>;
@ -450,25 +523,27 @@ namespace blt
return color4{r, g, b, 1.0f}; return color4{r, g, b, 1.0f};
} }
template<typename ValueType, u32 size> template <typename ValueType, u32 size>
inline constexpr blt::vec<ValueType, 2> make_vec2(const blt::vec<ValueType, size>& t, size_t fill = 0) inline constexpr blt::vec<ValueType, 2> make_vec2(const blt::vec<ValueType, size>& t, size_t fill = 0)
{ {
if constexpr (size >= 2) if constexpr (size >= 2)
{ {
return blt::vec<ValueType, 2>(t.x(), t.y()); return blt::vec<ValueType, 2>(t.x(), t.y());
} else }
else
{ {
return blt::vec<ValueType, 2>(t.x(), fill); return blt::vec<ValueType, 2>(t.x(), fill);
} }
} }
template<typename ValueType, u32 size> template <typename ValueType, u32 size>
inline constexpr blt::vec<ValueType, 3> make_vec3(const blt::vec<ValueType, size>& t, size_t fill = 0) inline constexpr blt::vec<ValueType, 3> make_vec3(const blt::vec<ValueType, size>& t, size_t fill = 0)
{ {
if constexpr (size >= 3) if constexpr (size >= 3)
{ {
return blt::vec<ValueType, 3>(t.x(), t.y(), t.z()); return blt::vec<ValueType, 3>(t.x(), t.y(), t.z());
} else }
else
{ {
blt::vec<ValueType, 3> ret; blt::vec<ValueType, 3> ret;
for (size_t i = 0; i < size; i++) for (size_t i = 0; i < size; i++)
@ -479,13 +554,14 @@ namespace blt
} }
} }
template<typename ValueType, u32 size> template <typename ValueType, u32 size>
inline constexpr blt::vec<ValueType, 4> make_vec4(const blt::vec<ValueType, size>& t, size_t fill = 0) inline constexpr blt::vec<ValueType, 4> make_vec4(const blt::vec<ValueType, size>& t, size_t fill = 0)
{ {
if constexpr (size >= 4) if constexpr (size >= 4)
{ {
return blt::vec<ValueType, 4>(t.x(), t.y(), t.z(), t.w()); return blt::vec<ValueType, 4>(t.x(), t.y(), t.z(), t.w());
} else }
else
{ {
blt::vec<ValueType, 4> ret; blt::vec<ValueType, 4> ret;
for (size_t i = 0; i < size; i++) for (size_t i = 0; i < size; i++)
@ -515,7 +591,7 @@ namespace blt
// Gram-Schmidt orthonormalization algorithm // Gram-Schmidt orthonormalization algorithm
static inline void gramSchmidt(std::vector<vec3>& vectors) static inline void gramSchmidt(std::vector<vec3>& vectors)
{ {
int n = (int) vectors.size(); int n = (int)vectors.size();
std::vector<vec3> basis; std::vector<vec3> basis;
// normalize first vector // normalize first vector

View File

@ -0,0 +1,55 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_CODEGEN_H
#define BLT_META_CODEGEN_H
namespace blt
{
#define BLT_CONST_LVALUE_GETTER(TYPE, NAME) const TYPE& get_##NAME() const { return NAME; }
#define BLT_LVALUE_GETTER(TYPE, NAME) TYPE& get_##NAME() { return NAME; }
#define BLT_PRVALUE_GETTER(TYPE, NAME) TYPE get_##NAME() const { return NAME; }
#define BLT_GLVALUE_GETTER(TYPE, NAME) \
BLT_CONST_LVALUE_GETTER(TYPE, NAME) \
BLT_LVALUE_GETTER(TYPE, NAME)
#define BLT_PRVALUE_SETTER(TYPE, NAME) \
auto& set_##NAME(TYPE new_##NAME) \
{ \
NAME = new_##NAME; \
return *this; \
}
#define BLT_PRVALUE_MOVE_SETTER(TYPE, NAME) \
auto& set_##NAME(TYPE new_##NAME) \
{ \
NAME = std::move(new_##NAME); \
return *this; \
}
#define BLT_LVALUE_SETTER(TYPE, NAME) \
auto& set_##NAME(const TYPE& new_##NAME) \
{ \
NAME = new_##NAME; \
return *this; \
}
}
#endif //BLT_META_CODEGEN_H

View File

@ -23,22 +23,47 @@
namespace blt namespace blt
{ {
#define BLT_MAKE_GETTER(TYPE, NAME) \ #define BLT_MAKE_GETTER_LVALUE(TYPE, NAME) \
TYPE& get_##NAME() { return NAME; } \ TYPE& get_##NAME() { return NAME; }
#define BLT_MAKE_GETTER_CLVALUE(TYPE, NAME) \
const TYPE& get_##NAME() const { return NAME; } const TYPE& get_##NAME() const { return NAME; }
#define BLT_MAKE_SETTER(TYPE, NAME) \ #define BLT_MAKE_GETTER_RVALUE(TYPE, NAME) \
TYPE get_##NAME() const { return NAME; }
#define BLT_MAKE_GETTER(TYPE, NAME) \
BLT_MAKE_GETTER_LVALUE(TYPE, NAME) \
BLT_MAKE_GETTER_CLVALUE(TYPE, NAME)
#define BLT_MAKE_SETTER_LVALUE(TYPE, NAME) \
auto& set_##NAME(const TYPE& new_##NAME) \ auto& set_##NAME(const TYPE& new_##NAME) \
{ \ { \
NAME = new_##NAME; \ NAME = new_##NAME; \
return *this; \ return *this; \
} \ }
#define BLT_MAKE_SETTER_RVALUE(TYPE, NAME) \
auto& set_##NAME(TYPE&& new_##NAME) \ auto& set_##NAME(TYPE&& new_##NAME) \
{ \ { \
NAME = std::move(new_##NAME); \ NAME = std::move(new_##NAME); \
return *this; \ return *this; \
} }
#define BLT_MAKE_VALUE(TYPE, NAME) \
TYPE NAME; \
BLT_MAKE_GETTER_CLVALUE(TYPE, NAME) \
BLT_MAKE_SETTER_RVALUE(TYPE, NAME)
#define BLT_MAKE_VALUE_DEFAULT(TYPE, NAME, DEFAULT) \
TYPE NAME = DEFAULT; \
BLT_MAKE_GETTER_CLVALUE(TYPE, NAME) \
BLT_MAKE_SETTER_RVALUE(TYPE, NAME)
#define BLT_MAKE_SETTER(TYPE, NAME) \
BLT_MAKE_SETTER_LVALUE(TYPE, NAME) \
BLT_MAKE_SETTER_RVALUE(TYPE, NAME)
#define BLT_MAKE_GETTER_AND_SETTER(TYPE, NAME) \ #define BLT_MAKE_GETTER_AND_SETTER(TYPE, NAME) \
BLT_MAKE_GETTER(TYPE, NAME) \ BLT_MAKE_GETTER(TYPE, NAME) \
BLT_MAKE_SETTER(TYPE, NAME) BLT_MAKE_SETTER(TYPE, NAME)

151
include/blt/meta/function.h Normal file
View File

@ -0,0 +1,151 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_FUNCTION_H
#define BLT_META_FUNCTION_H
#include <functional>
#include <tuple>
namespace blt::meta
{
struct std_function_tag
{};
struct function_ptr_tag
{};
struct lambda_tag
{};
struct member_function_ptr_tag
{};
template <typename Func>
struct function_like;
template <typename Return, typename... Args>
struct function_like<std::function<Return(Args...)>>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = std_function_tag;
};
template <typename Return, typename... Args>
struct function_like<Return (*)(Args...)>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = function_ptr_tag;
};
template <typename Return, typename... Args>
struct function_like<Return (*)(Args...) noexcept>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = function_ptr_tag;
};
template <typename Return, typename Class, typename... Args>
struct function_like<Return (Class::*)(Args...)>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = member_function_ptr_tag;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct function_like<Return (Class::*)(Args...) const>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = member_function_ptr_tag;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct function_like<Return (Class::*)(Args...) noexcept>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = member_function_ptr_tag;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct function_like<Return (Class::*)(Args...) const noexcept>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using tag = member_function_ptr_tag;
using class_type = Class;
};
template <typename>
struct lambda_traits
{};
template <typename Return, typename Class, typename... Args>
struct lambda_traits<Return (Class::*)(Args...) const>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct lambda_traits<Return (Class::*)(Args...) noexcept>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct lambda_traits<Return (Class::*)(Args...) const noexcept>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using class_type = Class;
};
template <typename Return, typename Class, typename... Args>
struct lambda_traits<Return (Class::*)(Args...)>
{
using return_type = Return;
using args_tuple = std::tuple<Args...>;
using class_type = Class;
};
template <typename Func>
struct function_like
{
private:
using lambda_trait = lambda_traits<decltype(&std::decay_t<Func>::operator())>;
public:
using tag_type = lambda_tag;
using return_type = typename lambda_trait::return_type;
using args_tuple = typename lambda_trait::args_tuple;
using class_type = typename lambda_trait::class_type;
};
}
#endif //BLT_META_FUNCTION_H

View File

@ -0,0 +1,51 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_IS_STREAMABLE_H
#define BLT_META_IS_STREAMABLE_H
#include <ostream>
namespace blt::meta
{
// https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists
template<typename T>
class is_streamable
{
private:
template<typename Subs>
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<Subs>(), std::true_type())
{
return std::declval<std::true_type>();
}
template<typename>
static auto test(...) -> std::false_type
{
return std::declval<std::false_type>();
}
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
}
#endif //BLT_META_IS_STREAMABLE_H

View File

@ -23,6 +23,7 @@
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include <ostream> #include <ostream>
#include <blt/meta/is_streamable.h>
namespace blt::meta namespace blt::meta
{ {
@ -71,128 +72,10 @@ namespace blt::meta
template<typename Lambda> template<typename Lambda>
lambda_helper(Lambda) -> lambda_helper<Lambda, decltype(&Lambda::operator())>; lambda_helper(Lambda) -> lambda_helper<Lambda, decltype(&Lambda::operator())>;
// https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists
template<typename T>
class is_streamable
{
private:
template<typename Subs>
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<Subs>(), std::true_type())
{
return std::declval<std::true_type>();
}
template<typename>
static auto test(...) -> std::false_type
{
return std::declval<std::false_type>();
}
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
namespace detail
{
template<typename Or>
struct value_type_helper
{
template<typename Subs>
inline static constexpr auto get(int) -> typename Subs::value_type
{
return std::declval<Subs::value_type>();
}
template<typename>
inline static constexpr Or get(...)
{
return std::declval<Or>();
}
};
template<typename Or>
struct reference_type_helper
{
template<typename Subs>
inline static constexpr auto get(int) -> typename Subs::reference
{
return std::declval<typename Subs::reference>();
}
template<typename>
inline static constexpr Or get(...)
{
return std::declval<Or>();
}
};
template<typename Or>
struct const_reference_type_helper
{
template<typename Subs>
inline static constexpr auto get(int) -> typename Subs::const_reference
{
return std::declval<typename Subs::const_reference>();
}
template<typename>
inline static constexpr Or get(...)
{
return std::declval<Or>();
}
};
template<typename Or>
struct pointer_type_helper
{
template<typename Subs>
inline static constexpr auto get(int) -> typename Subs::pointer
{
return std::declval<typename Subs::pointer>();
}
template<typename>
inline static constexpr Or get(...)
{
return std::declval<Or>();
}
};
template<typename Or>
struct difference_type_helper
{
template<typename Subs>
inline static constexpr auto get(int) -> typename Subs::difference_type
{
return std::declval<typename Subs::difference_type>();
}
template<typename>
inline static constexpr Or get(...)
{
return std::declval<Or>();
}
};
}
template<typename T, typename Or>
using value_type_t = decltype(detail::value_type_helper<Or>::template get<T>(0));
template<typename T, typename Or>
using difference_t = decltype(detail::difference_type_helper<Or>::template get<T>(0));
template<typename T, typename Or>
using pointer_t = decltype(detail::pointer_type_helper<Or>::template get<T>(0));
template<typename T, typename Or>
using reference_t = decltype(detail::reference_type_helper<Or>::template get<T>(0));
template<typename T, typename Or>
using const_reference_t = decltype(detail::const_reference_type_helper<Or>::template get<T>(0));
template<typename T> template<typename T>
struct arrow_return struct arrow_return
{ {
using type = typename std::result_of<decltype(&T::operator->)(T*)>::type; using type = typename std::invoke_result_t<decltype(&T::operator->), T*>;
}; };
template<typename T> template<typename T>
@ -208,7 +91,7 @@ namespace blt::meta
template<typename T> template<typename T>
struct deref_return struct deref_return
{ {
using type = typename std::result_of<decltype(&T::operator*)(T*)>::type; using type = std::invoke_result_t<decltype(&T::operator*), T&>;
}; };
template<typename T> template<typename T>
@ -231,6 +114,15 @@ namespace blt::meta
template<typename T> \ template<typename T> \
inline constexpr bool has_func_##FUNC##_v = has_func_##FUNC<T>::value; inline constexpr bool has_func_##FUNC##_v = has_func_##FUNC<T>::value;
#define BLT_META_MAKE_STATIC_FUNCTION_CHECK(FUNC, ...)\
template<typename T, typename = void> \
class has_static_func_##FUNC : public std::false_type \
{}; \
template<typename T> \
class has_static_func_##FUNC<T, std::void_t<decltype(T::FUNC(,##__VA_ARGS__))>> : public std::true_type \
{}; \
template<typename T> \
inline constexpr bool has_static_func_##FUNC##_v = has_static_func_##FUNC<T>::value;
#define BLT_META_MAKE_MEMBER_CHECK(MEMBER)\ #define BLT_META_MAKE_MEMBER_CHECK(MEMBER)\
template<typename T, typename = void> \ template<typename T, typename = void> \

View File

@ -0,0 +1,31 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_PASSTHROUGH_H
#define BLT_META_PASSTHROUGH_H
namespace blt::meta
{
template <typename T>
struct passthrough
{
using type = T;
};
}
#endif //BLT_META_PASSTHROUGH_H

View File

@ -0,0 +1,143 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_FILTER_VOID_H
#define BLT_META_FILTER_VOID_H
#include <tuple>
#include <type_traits>
namespace blt::meta
{
template <template<typename...> typename Func, typename Tuple>
struct apply_tuple;
template <template<typename...> typename Func, typename... Values>
struct apply_tuple<Func, std::tuple<Values...>>
{
using type = Func<Values...>;
};
template <template<typename...> typename Func, typename Tuple>
using apply_tuple_t = typename apply_tuple<Func, Tuple>::type;
template <typename... Ts>
struct filter_void;
template <>
struct filter_void<>
{
using type = std::tuple<>;
};
template <typename T, typename... Ts>
struct filter_void<T, Ts...>
{
using type = std::conditional_t<std::is_void_v<T>, typename filter_void<Ts...>::type, decltype(std::tuple_cat(
std::declval<std::tuple<T>>(), std::declval<typename filter_void<Ts...>::type>()))>;
};
template <typename... Ts>
struct filter_void<std::tuple<Ts...>>
{
using type = typename filter_void<Ts...>::type;
};
template <typename... Ts>
using filter_void_t = typename filter_void<Ts...>::type;
template <template<typename...> typename Func, typename ArgsTuple, typename... Ts>
struct filter_func;
template <template<typename...> typename Func, typename ArgsTuple>
struct filter_func<Func, ArgsTuple>
{
using type = std::tuple<>;
};
template <template<typename...> typename Func, typename ArgsTuple, typename T, typename... Ts>
struct filter_func<Func, ArgsTuple, T, Ts...>
{
using ArgsTupleWithT = decltype(std::tuple_cat(std::declval<ArgsTuple>(), std::declval<std::tuple<T>>()));
using type = std::conditional_t<apply_tuple_t<Func, ArgsTupleWithT>::value, decltype( std::tuple_cat(
std::declval<std::tuple<T>>(),
std::declval<typename filter_func<Func, ArgsTuple, Ts...>::type>())), typename filter_func<
Func, ArgsTuple, Ts...>::type>;
};
template <template<typename...> typename Func, typename ArgsTuple, typename... Ts>
struct filter_func<Func, ArgsTuple, std::tuple<Ts...>>
{
using type = typename filter_func<Func, ArgsTuple, Ts...>::type;
};
template <template<typename...> typename Func, typename ArgsTuple, typename... Ts>
using filter_func_t = typename filter_func<Func, ArgsTuple, Ts...>::type;
template <typename...>
struct tuple_contains;
template <typename T>
struct tuple_contains<T, std::tuple<>> : std::false_type
{};
template <typename T, typename T1, typename... Ts>
struct tuple_contains<T, std::tuple<T1, Ts...>> : std::conditional_t<
std::is_same_v<T, T1> || tuple_contains<T, std::tuple<Ts...>>::value, std::true_type, std::false_type>
{};
template <typename T, typename Tuple>
constexpr static bool tuple_contains_v = tuple_contains<T, Tuple>::value;
template <typename... Ts>
struct unique_types;
template <>
struct unique_types<>
{
using type = std::tuple<>;
};
template <typename T>
struct unique_types<T>
{
using type = std::tuple<T>;
};
template <typename T, typename... Rest>
struct unique_types<T, Rest...>
{
using rest_unique = typename unique_types<Rest...>::type;
using type = std::conditional_t<tuple_contains_v<T, rest_unique>, rest_unique, decltype(std::tuple_cat(
std::declval<rest_unique>(), std::declval<std::tuple<T>>()))>;
};
template <typename Tuple>
struct unique_tuple;
template <typename... Ts>
struct unique_tuple<std::tuple<Ts...>>
{
using type = typename unique_types<Ts...>::type;
};
template <typename Tuple>
using unique_tuple_t = typename unique_tuple<Tuple>::type;
}
#endif //BLT_META_FILTER_VOID_H

View File

@ -0,0 +1,73 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_META_TYPE_TRAITS_H
#define BLT_META_TYPE_TRAITS_H
#include <type_traits>
#include <tuple>
namespace blt::meta
{
namespace detail
{
template<typename... Args>
void empty_apply_function(Args...)
{
}
inline auto lambda = [](auto...)
{
};
}
template <typename T>
using remove_cvref_t = std::remove_volatile_t<std::remove_const_t<std::remove_reference_t<T>>>;
template <typename U>
using add_const_ref_t = std::conditional_t<std::is_reference_v<U>, const std::remove_reference_t<U>&, const U>;
template <typename>
struct is_tuple : std::false_type
{
};
template <typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type
{
};
template <typename>
struct is_pair : std::false_type
{
};
template <typename T, typename G>
struct is_pair<std::pair<T, G>> : std::true_type
{
};
template <typename T>
static constexpr bool is_tuple_v = is_tuple<T>::value;
template <typename T>
static constexpr bool is_pair_v = is_pair<T>::value;
}
#endif // BLT_META_TYPE_TRAITS_H

View File

@ -0,0 +1,812 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_PARSE_ARGPARSE_V2_H
#define BLT_PARSE_ARGPARSE_V2_H
#include <complex>
#include <blt/std/types.h>
#include <blt/std/hashmap.h>
#include <blt/meta/meta.h>
#include <string>
#include <string_view>
#include <vector>
#include <variant>
#include <optional>
#include <memory>
#include <functional>
#include <type_traits>
#include <blt/iterator/iterator.h>
#include <blt/meta/type_traits.h>
#include <blt/std/assert.h>
#include <blt/std/expected.h>
#include <blt/std/ranges.h>
#include <blt/std/utility.h>
#include <blt/std/variant.h>
namespace blt::argparse
{
class argument_string_t;
class argument_consumer_t;
class argument_parser_t;
class argument_subparser_t;
class argument_builder_t;
class argument_storage_t;
class argument_positional_storage_t;
enum class action_t
{
STORE,
STORE_CONST,
STORE_TRUE,
STORE_FALSE,
APPEND,
APPEND_CONST,
EXTEND,
COUNT,
HELP,
VERSION
};
enum class nargs_t
{
IF_POSSIBLE,
ALL,
ALL_AT_LEAST_ONE
};
using nargs_v = std::variant<nargs_t, i32>;
namespace detail
{
class bad_flag final : public std::runtime_error
{
public:
explicit bad_flag(const std::string& message): std::runtime_error(message)
{
}
};
class bad_positional final : public std::runtime_error
{
public:
explicit bad_positional(const std::string& message): std::runtime_error(message)
{
}
};
class missing_argument_error final : public std::runtime_error
{
public:
explicit missing_argument_error(const std::string& message): std::runtime_error(message)
{
}
};
class missing_value_error final : public std::runtime_error
{
public:
explicit missing_value_error(const std::string& message): std::runtime_error(message)
{
}
};
class type_error final : public std::runtime_error
{
public:
explicit type_error(const std::string& message): std::runtime_error(message)
{
}
};
class unexpected_argument_error final : public std::runtime_error
{
public:
explicit unexpected_argument_error(const std::string& message): std::runtime_error(message)
{
}
};
class bad_choice_error final : public std::runtime_error
{
public:
explicit bad_choice_error(const std::string& message): std::runtime_error(message)
{
}
};
class subparse_error final : public std::exception
{
public:
explicit subparse_error(const std::string_view found_string, std::vector<std::vector<std::string_view>> allowed_strings):
m_found_string(found_string),
m_allowed_strings(std::move(allowed_strings))
{
}
[[nodiscard]] const std::vector<std::vector<std::string_view>>& get_allowed_strings() const
{
return m_allowed_strings;
}
[[nodiscard]] std::string_view get_found_string() const
{
return m_found_string;
}
[[nodiscard]] std::string error_string() const;
[[nodiscard]] const char* what() const noexcept override
{
m_error_string = error_string();
return m_error_string.c_str();
}
private:
mutable std::string m_error_string;
std::string_view m_found_string;
std::vector<std::vector<std::string_view>> m_allowed_strings;
};
template <typename... Args>
struct arg_data_helper_t
{
using variant_t = std::variant<Args..., std::vector<Args>...>;
using arg_t = meta::arg_helper<Args...>;
using arg_vec_t = meta::arg_helper<std::vector<Args>...>;
template <typename T>
constexpr static bool is_type_stored_v = std::disjunction_v<std::is_same<T, Args>...>;
template <typename DefaultPrimitiveAction, typename DefaultListAction>
static auto make_visitor(const DefaultPrimitiveAction& primitive_action, const DefaultListAction& list_action)
{
return lambda_visitor{
([&primitive_action](Args& arg)
{
return primitive_action(arg);
})...,
([&list_action](std::vector<Args>& arg_vec)
{
return list_action(arg_vec);
})...
};
}
};
using arg_meta_type_helper_t = arg_data_helper_t<bool, i8, i16, i32, i64, u8, u16, u32, u64, float, double, std::string>;
using arg_data_t = arg_meta_type_helper_t::variant_t;
template <typename T>
struct arg_string_converter_t
{
static T convert(const std::string_view value)
{
static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string_view> || std::is_same_v<T, std::string>,
"Type must be arithmetic, string_view or string!");
const std::string temp{value};
if constexpr (std::is_same_v<T, float>)
{
return std::stof(temp);
}
else if constexpr (std::is_same_v<T, double>)
{
return std::stod(temp);
}
else if constexpr (std::is_unsigned_v<T>)
{
return static_cast<T>(std::stoull(temp));
}
else if constexpr (std::is_signed_v<T>)
{
return static_cast<T>(std::stoll(temp));
}
else if constexpr (std::is_same_v<T, std::string_view>)
{
return value;
}
else if constexpr (std::is_same_v<T, std::string>)
{
return std::string(value);
}
BLT_UNREACHABLE;
}
};
template <typename T>
auto ensure_is_string(T&& t)
{
using NO_REF = meta::remove_cvref_t<T>;
if constexpr (std::is_same_v<NO_REF, bool>)
return t ? "true" : "false";
else if constexpr (std::is_arithmetic_v<NO_REF> && !(std::is_same_v<NO_REF, char>
|| std::is_same_v<NO_REF, unsigned char> || std::is_same_v<NO_REF, signed char>))
return std::to_string(std::forward<T>(t));
else
return std::forward<T>(t);
}
void test();
}
class argument_string_t
{
public:
explicit argument_string_t(const std::string_view input, const hashset_t<char>& allowed_flag_prefix): m_argument(input),
m_allowed_flag_prefix(&allowed_flag_prefix)
{
process_argument();
}
[[nodiscard]] std::string_view get_flag() const
{
return m_flag_section;
}
[[nodiscard]] std::string_view get_name() const
{
return m_name_section;
}
[[nodiscard]] std::string_view value() const
{
return get_name();
}
[[nodiscard]] bool is_flag() const
{
return !m_flag_section.empty();
}
[[nodiscard]] std::string_view get_argument() const
{
return m_argument;
}
private:
/**
* This function takes the command line argument represented by this class,
* stored in m_argument and converts into flag side and name side arguments.
*
* What this means is in the case of a flag provided, for example passing --foo or --bar, this function will split the argument into
*
* m_flag_section = "--"
* m_name_section = "foo" || "bar"
*
* If the argument is purely positional, meaning there is no flag prefix,
* this function will do nothing and m_flag_section will be true for .empty()
*
* For example, if you provide res/some/folder/or/file as a command line positional argument,
* this function will create the following internal state:
*
* m_flag_section = ""
* m_flag_section.empty() == true
* m_name_section = "res/some/folder/or/file"
*
* m_argument is not modified by this function
*/
void process_argument()
{
// user provides a list of allowed prefix characters to argument_parser_t, which is then provided to this class at construction time
// it is not the job of this class to validate flag prefixes beyond this. // TODO: requiring this pointer is a code smell.
size_t start = 0;
for (; start < m_argument.size() && m_allowed_flag_prefix->contains(m_argument[start]); start++)
{
}
m_flag_section = {m_argument.data(), start};
m_name_section = {m_argument.data() + start, m_argument.size() - start};
}
std::string_view m_argument;
std::string_view m_flag_section;
std::string_view m_name_section;
const hashset_t<char>* m_allowed_flag_prefix;
};
class argument_consumer_t
{
public:
explicit argument_consumer_t(const span<argument_string_t>& args): m_absolute_begin(args.data()), m_begin(args.data() + 1),
m_end(args.data() + args.size())
{
BLT_ASSERT(!args.empty() &&
"Argument consumer must have at least one argument allocated to it. First argument is always assumed to be program");
}
[[nodiscard]] const argument_string_t& absolute_first() const
{
return *m_absolute_begin;
}
[[nodiscard]] argument_string_t peek(const i32 offset = 0) const
{
return *(m_begin + offset);
}
[[nodiscard]] argument_string_t r_peek(const i32 offset = 0) const
{
return *(m_end - 1 - offset);
}
argument_string_t consume()
{
return *(m_begin++);
}
argument_string_t r_consume()
{
return *(--m_end);
}
[[nodiscard]] i32 remaining() const
{
return static_cast<i32>(size());
}
[[nodiscard]] bool can_consume(const i32 amount = 0) const
{
return amount < remaining();
}
private:
[[nodiscard]] ptrdiff_t size() const
{
return m_end - m_begin;
}
argument_string_t* m_absolute_begin;
argument_string_t* m_begin;
argument_string_t* m_end;
};
class argument_storage_t
{
friend argument_parser_t;
friend argument_subparser_t;
friend argument_builder_t;
public:
template <typename T>
[[nodiscard]] const T& get(const std::string_view key) const
{
return std::get<T>(m_data.at(key));
}
[[nodiscard]] const std::string& get(const std::string_view key) const
{
return std::get<std::string>(m_data.at(key));
}
[[nodiscard]] bool contains(const std::string_view key) const
{
return m_data.find(key) != m_data.end();
}
[[nodiscard]] size_t size() const
{
return m_data.size();
}
private:
void add(const argument_storage_t& values)
{
for (const auto& value : values.m_data)
m_data.insert(value);
}
hashmap_t<std::string, detail::arg_data_t> m_data;
};
class argument_builder_t
{
friend argument_parser_t;
public:
argument_builder_t()
{
m_dest_func = [](const std::string_view dest, argument_storage_t& storage, std::string_view value)
{
storage.m_data.emplace(std::string{dest}, std::string{value});
};
m_dest_vec_func = [](const std::string_view dest, argument_storage_t& storage, const std::vector<std::string>& values)
{
storage.m_data.emplace(std::string{dest}, values);
};
}
template <typename T>
argument_builder_t& as_type()
{
static_assert(detail::arg_data_helper_t<T>::template is_type_stored_v<T>, "Type is not valid to be stored/converted as an argument");
m_dest_func = [](const std::string_view dest, argument_storage_t& storage, std::string_view value)
{
storage.m_data.emplace(std::string{dest}, detail::arg_string_converter_t<T>::convert(value));
};
m_dest_vec_func = [](const std::string_view dest, argument_storage_t& storage, const std::vector<std::string>& values)
{
if (storage.m_data.contains(dest))
{
auto& data = storage.m_data[dest];
if (!std::holds_alternative<std::vector<T>>(data))
throw detail::type_error("Invalid type conversion. Trying to add type " + blt::type_string<T>() +
" but this does not match existing type index '" + std::to_string(data.index()) + "'!");
auto& converted_values = std::get<std::vector<T>>(data);
for (const auto& value : values)
converted_values.push_back(detail::arg_string_converter_t<T>::convert(value));
}
else
{
std::vector<T> converted_values;
for (const auto& value : values)
converted_values.push_back(detail::arg_string_converter_t<T>::convert(value));
storage.m_data.emplace(std::string{dest}, std::move(converted_values));
}
};
return *this;
}
argument_builder_t& make_flag()
{
return set_action(action_t::STORE_TRUE);
}
argument_builder_t& set_action(action_t action);
argument_builder_t& set_required(const bool required)
{
m_required = required;
return *this;
}
argument_builder_t& set_nargs(const nargs_v nargs)
{
m_nargs = nargs;
return *this;
}
argument_builder_t& set_metavar(const std::string& metavar)
{
m_metavar = metavar;
return *this;
}
argument_builder_t& set_help(const std::string& help)
{
m_help = help;
return *this;
}
argument_builder_t& set_choices(const std::vector<std::string>& choices)
{
m_choices = hashset_t<std::string>{};
for (const auto& choice : choices)
m_choices->emplace(choice);
return *this;
}
argument_builder_t& set_choices(const hashset_t<std::string>& choices)
{
m_choices = choices;
return *this;
}
template <typename... Args>
argument_builder_t& set_choices(Args&&... args)
{
m_choices = hashset_t<std::string>{};
((m_choices->emplace(detail::ensure_is_string(std::forward<Args>(args)))), ...);
return *this;
}
argument_builder_t& set_default(const detail::arg_data_t& default_value)
{
m_default_value = default_value;
return *this;
}
argument_builder_t& set_const(const detail::arg_data_t& const_value)
{
m_const_value = const_value;
return *this;
}
argument_builder_t& set_dest(const std::string_view& dest)
{
m_dest = dest;
return *this;
}
private:
action_t m_action = action_t::STORE;
bool m_required = false; // do we require this argument to be provided as an argument?
nargs_v m_nargs = 1; // number of arguments to consume
std::optional<std::string> m_metavar; // variable name to be used in the help string
std::optional<std::string> m_help; // help string to be used in the help string
std::optional<hashset_t<std::string>> m_choices; // optional allowed choices for this argument
std::optional<detail::arg_data_t> m_default_value;
std::optional<detail::arg_data_t> m_const_value;
std::optional<std::string> m_dest;
// dest, storage, value input
std::function<void(std::string_view, argument_storage_t&, std::string_view)> m_dest_func;
// dest, storage, value input
std::function<void(std::string_view, argument_storage_t&, const std::vector<std::string>& values)> m_dest_vec_func;
};
class argument_positional_storage_t
{
public:
explicit argument_positional_storage_t(std::vector<std::pair<std::string, argument_builder_t>> storage): positional_arguments(
std::move(storage))
{
}
argument_builder_t& peek()
{
return positional_arguments[current_positional].second;
}
argument_builder_t& next()
{
return positional_arguments[current_positional++].second;
}
[[nodiscard]] bool has_positional() const
{
return current_positional < positional_arguments.size();
}
[[nodiscard]] auto remaining() const
{
return iterate(positional_arguments).skip(current_positional);
}
private:
std::vector<std::pair<std::string, argument_builder_t>> positional_arguments;
size_t current_positional = 0;
};
class argument_parser_t
{
friend argument_subparser_t;
explicit argument_parser_t(const argument_subparser_t* parent): m_parent(parent)
{
}
public:
explicit argument_parser_t(const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {},
const std::optional<std::string_view> version = {},
const std::optional<std::string_view> usage = {}, const std::optional<std::string_view> name = {}):
m_name(name), m_usage(usage), m_description(description), m_epilogue(epilogue), m_version(version)
{
}
template <typename... Aliases>
argument_builder_t& add_flag(const std::string_view arg, Aliases... aliases)
{
static_assert(
std::conjunction_v<std::disjunction<std::is_convertible<Aliases, std::string>, std::is_constructible<
std::string, Aliases>>...>,
"Arguments must be of type string_view, convertible to string_view or be string_view constructable");
m_argument_builders.emplace_back(std::make_unique<argument_builder_t>());
m_argument_builders.back()->set_dest(arg);
m_flag_arguments.emplace(arg, m_argument_builders.back().get());
(m_flag_arguments.emplace(aliases, m_argument_builders.back().get()), ...);
return *m_argument_builders.back().get();
}
argument_builder_t& add_positional(const std::string_view arg)
{
auto& b = m_positional_arguments.emplace_back(arg, argument_builder_t{}).second;
b.set_dest(std::string{arg});
b.set_required(true);
b.set_nargs(1);
return b;
}
argument_subparser_t* add_subparser(std::string_view dest);
argument_parser_t& with_help()
{
add_flag("--help", "-h").set_action(action_t::HELP).set_help("Show this help menu and exit");
return *this;
}
argument_parser_t& with_version()
{
add_flag("--version").set_action(action_t::VERSION);
return *this;
}
argument_storage_t parse(argument_consumer_t& consumer); // NOLINT
argument_storage_t parse(const std::vector<std::string_view>& args)
{
std::vector<argument_string_t> arg_strings;
arg_strings.reserve(args.size());
for (const auto& arg : args)
arg_strings.emplace_back(arg, allowed_flag_prefixes);
argument_consumer_t consumer{arg_strings};
return parse(consumer);
}
argument_storage_t parse(const std::vector<std::string>& args)
{
std::vector<argument_string_t> arg_strings;
arg_strings.reserve(args.size());
for (const auto& arg : args)
arg_strings.emplace_back(arg, allowed_flag_prefixes);
argument_consumer_t consumer{arg_strings};
return parse(consumer);
}
argument_storage_t parse(const int argc, const char** argv)
{
std::vector<argument_string_t> arg_strings;
arg_strings.reserve(argc);
for (int i = 0; i < argc; ++i)
arg_strings.emplace_back(argv[i], allowed_flag_prefixes);
argument_consumer_t consumer{arg_strings};
return parse(consumer);
}
void print_help();
void print_usage();
void print_version() const;
argument_parser_t& set_name(const std::optional<std::string>& name)
{
m_name = name;
return *this;
}
argument_parser_t& set_usage(const std::optional<std::string>& usage)
{
m_usage = usage;
return *this;
}
[[nodiscard]] const std::optional<std::string>& get_usage() const
{
return m_usage;
}
argument_parser_t& set_description(const std::optional<std::string>& description)
{
m_description = description;
return *this;
}
[[nodiscard]] const std::optional<std::string>& get_description() const
{
return m_description;
}
argument_parser_t& set_epilogue(const std::optional<std::string>& epilogue)
{
m_epilogue = epilogue;
return *this;
}
[[nodiscard]] const std::optional<std::string>& get_epilogue() const
{
return m_epilogue;
}
argument_parser_t& set_version(const std::optional<std::string>& version)
{
m_epilogue = version;
return *this;
}
[[nodiscard]] const std::optional<std::string>& get_version() const
{
return m_version;
}
[[nodiscard]] const hashset_t<char>& get_allowed_flag_prefixes() const
{
return allowed_flag_prefixes;
}
private:
void handle_compound_flags(hashset_t<std::string>& found_flags, argument_storage_t& parsed_args, argument_consumer_t& consumer,
const argument_string_t& arg);
void parse_flag(argument_storage_t& parsed_args, argument_consumer_t& consumer, std::string_view arg);
void parse_positional(argument_storage_t& parsed_args, argument_consumer_t& consumer, argument_positional_storage_t& storage,
std::string_view arg);
static void handle_missing_and_default_args(hashmap_t<std::string_view, argument_builder_t*>& arguments,
const hashset_t<std::string>& found, argument_storage_t& parsed_args, std::string_view type);
static expected<std::vector<std::string>, std::string> consume_until_flag_or_end(argument_consumer_t& consumer,
hashset_t<std::string>* allowed_choices);
static std::vector<std::string> consume_argc(i32 argc, argument_consumer_t& consumer, hashset_t<std::string>* allowed_choices,
std::string_view arg);
std::optional<std::string> m_name;
std::optional<std::string> m_usage;
std::optional<std::string> m_description;
std::optional<std::string> m_epilogue;
std::optional<std::string> m_version;
const argument_subparser_t* m_parent = nullptr;
std::vector<std::pair<std::string_view, argument_subparser_t>> m_subparsers;
std::vector<std::unique_ptr<argument_builder_t>> m_argument_builders;
hashmap_t<std::string_view, argument_builder_t*> m_flag_arguments;
std::vector<std::pair<std::string, argument_builder_t>> m_positional_arguments;
hashset_t<char> allowed_flag_prefixes = {'-', '+', '/'};
};
class argument_subparser_t
{
friend argument_parser_t;
public:
explicit argument_subparser_t(const argument_parser_t& parent): m_parent(&parent)
{
}
template <typename... Aliases>
argument_parser_t* add_parser(const std::string_view name, Aliases... aliases)
{
static_assert(
std::conjunction_v<std::disjunction<std::is_convertible<Aliases, std::string_view>, std::is_constructible<
std::string_view, Aliases>>...>,
"Arguments must be of type string_view, convertible to string_view or be string_view constructable");
m_parsers.emplace_back(new argument_parser_t{this});
m_aliases[name] = m_parsers.back().get();
((m_aliases[std::string_view{aliases}] = m_parsers.back().get()), ...);
return m_parsers.back().get();
}
argument_subparser_t* set_help(const std::optional<std::string>& help)
{
m_help = help;
return this;
}
/**
* Parses the next argument using the provided argument consumer.
*
* This function uses an argument consumer to extract and process the next argument.
* If the argument is a flag or if it cannot be matched against the available parsers,
* an exception is thrown.
*
* @param consumer Reference to an argument_consumer_t object, which handles argument parsing.
* The consumer provides the next argument to be parsed.
*
* @throws detail::subparse_error If the argument is a flag or does not match any known parser.
*/
std::pair<argument_string_t, argument_storage_t> parse(argument_consumer_t& consumer); // NOLINT
private:
[[nodiscard]] hashmap_t<argument_parser_t*, std::vector<std::string_view>> get_allowed_strings() const;
// annoying compatability because im lazy
static std::vector<std::vector<std::string_view>> to_vec(const hashmap_t<argument_parser_t*, std::vector<std::string_view>>& map);
const argument_parser_t* m_parent;
std::optional<std::string> m_last_parsed_parser; // bad hack
std::optional<std::string> m_help;
std::vector<std::unique_ptr<argument_parser_t>> m_parsers;
hashmap_t<std::string_view, argument_parser_t*> m_aliases;
};
}
#endif //BLT_PARSE_ARGPARSE_V2_H

View File

@ -27,7 +27,7 @@
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <blt/std/expected.h> #include <blt/std/expected.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <variant> #include <variant>
namespace blt namespace blt
@ -223,8 +223,12 @@ namespace blt
std::string_view from_last() std::string_view from_last()
{ {
if (!hasNext()) if (!hasNext()) {
return std::string_view(&raw_string[last_read_index], raw_string.size() - last_read_index); auto size = raw_string.size() - last_read_index;
if (size > 0)
return std::string_view(&raw_string[last_read_index], size);
return "";
}
auto token = storage[getCurrentIndex()]; auto token = storage[getCurrentIndex()];
auto len = ((&token.token.back()) - &raw_string[last_read_index]); auto len = ((&token.token.back()) - &raw_string[last_read_index]);
auto str = std::string_view(&raw_string[last_read_index], len); auto str = std::string_view(&raw_string[last_read_index], len);

View File

@ -11,7 +11,7 @@
#include <blt/std/queue.h> #include <blt/std/queue.h>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <fstream> #include <fstream>
#include <cstdint> #include <cstdint>
@ -54,7 +54,7 @@ namespace blt::profiling {
profile getProfile(const std::string& profileName); profile getProfile(const std::string& profileName);
void printProfile( void printProfile(
const std::string& profileName, logging::log_level loggingLevel = logging::log_level::NONE, const std::string& profileName, logging::log_level_t loggingLevel = logging::log_level_t::NONE,
bool averageHistory = false bool averageHistory = false
); );

View File

@ -11,7 +11,7 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
namespace blt namespace blt
{ {
@ -74,7 +74,8 @@ namespace blt
std::string name; std::string name;
explicit profile_t(std::string name): name(std::move(name)) explicit profile_t(std::string name): name(std::move(name))
{} {
}
profile_t(const profile_t& p) = delete; profile_t(const profile_t& p) = delete;
@ -97,9 +98,11 @@ namespace blt
void endInterval(interval_t* interval); void endInterval(interval_t* interval);
void printProfile(profile_t& profiler, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, void printProfile(profile_t& profiler, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE); sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE);
void writeProfile(std::ifstream& stream, const profile_t& profiler); void writeProfile(std::ostream& stream, profile_t& profiler,
std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
sort_by sort = sort_by::CYCLES);
void clearProfile(profile_t& profiler); void clearProfile(profile_t& profiler);
@ -110,37 +113,39 @@ namespace blt
void endInterval(const std::string& profile_name, const std::string& interval_name); void endInterval(const std::string& profile_name, const std::string& interval_name);
void printProfile(const std::string& profile_name, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, void printProfile(const std::string& profile_name, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE); sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE);
void writeProfile(std::ifstream& stream, const std::string& profile_name); void writeProfile(std::ostream& stream, const std::string& profile_name,
std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL,
sort_by sort = sort_by::CYCLES);
} }
class auto_interval class auto_interval
{ {
private: private:
interval_t* iv; interval_t* iv;
public:
auto_interval(std::string interval_name, profile_t& profiler)
{
iv = blt::createInterval(profiler, std::move(interval_name));
blt::startInterval(iv);
}
explicit auto_interval(interval_t* iv): iv(iv) public:
{ auto_interval(std::string interval_name, profile_t& profiler)
blt::startInterval(iv); {
} iv = blt::createInterval(profiler, std::move(interval_name));
blt::startInterval(iv);
}
auto_interval(const auto_interval& i) = delete; explicit auto_interval(interval_t* iv): iv(iv)
{
blt::startInterval(iv);
}
auto_interval& operator=(const auto_interval& i) = delete; auto_interval(const auto_interval& i) = delete;
~auto_interval() auto_interval& operator=(const auto_interval& i) = delete;
{
blt::endInterval(iv); ~auto_interval()
} {
blt::endInterval(iv);
}
}; };
} }
#ifdef BLT_DISABLE_PROFILING #ifdef BLT_DISABLE_PROFILING
@ -152,22 +157,22 @@ namespace blt
/** /**
* Starts an interval to be measured, when ended the row will be added to the specified profile. * Starts an interval to be measured, when ended the row will be added to the specified profile.
*/ */
#define BLT_START_INTERVAL(profileName, intervalName) blt::_internal::startInterval(profileName, intervalName) #define BLT_START_INTERVAL(profileName, intervalName) blt::_internal::startInterval(profileName, intervalName)
/** /**
* Ends an interval, adds the interval to the profile. * Ends an interval, adds the interval to the profile.
*/ */
#define BLT_END_INTERVAL(profileName, intervalName) blt::_internal::endInterval(profileName, intervalName) #define BLT_END_INTERVAL(profileName, intervalName) blt::_internal::endInterval(profileName, intervalName)
/** /**
* Prints the profile order from least time to most time. * Prints the profile order from least time to most time.
* @param profileName the profile to print * @param profileName the profile to print
* @param loggingLevel blt::logging::LOG_LEVEL to log with (default: BLT_NONE) * @param loggingLevel blt::logging::LOG_LEVEL to log with (default: BLT_NONE)
* @param averageHistory use the historical collection of interval rows in an average or just the latest? (default: false) * @param averageHistory use the historical collection of interval rows in an average or just the latest? (default: false)
*/ */
#define BLT_PRINT_PROFILE(profileName, ...) blt::_internal::printProfile(profileName, ##__VA_ARGS__) #define BLT_PRINT_PROFILE(profileName, ...) blt::_internal::printProfile(profileName, ##__VA_ARGS__)
/** /**
* writes the profile to an output stream, ordered from least time to most time, in CSV format. * writes the profile to an output stream, ordered from least time to most time, in CSV format.
*/ */
#define BLT_WRITE_PROFILE(stream, profileName) blt::_internal::writeProfile(stream, profileName) #define BLT_WRITE_PROFILE(stream, profileName) blt::_internal::writeProfile(stream, profileName)
#endif #endif
#endif //BLT_PROFILER_V2_H #endif //BLT_PROFILER_V2_H

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,12 @@
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#include <blt/std/allocator.h> #include <blt/std/allocator.h>
#include <blt/std/types.h>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
// TODO: blt::queue // TODO: blt::queue
#include <queue> #include <queue>
#include <stack>
namespace blt namespace blt
{ {
@ -142,6 +144,185 @@ namespace blt
} }
}; };
template<typename K, typename V>
class range_tree_t
{
public:
struct node_t
{
K k;
V v;
blt::i64 children = 0;
node_t(K k, V v): k(std::move(k)), v(std::move(v))
{}
};
void insert(K k, V v)
{
auto insert_point = nodes.begin();
auto insert_parent = insert_point;
while (insert_point != nodes.end())
{
// no children
if (insert_point->children == 0)
{
++insert_point->children;
++insert_point;
break;
} else if (insert_point->children == 1)
{
// 1 child case
insert_parent = insert_point;
// find if child is min & move to it
++insert_point;
bool min = insert_point->k < insert_parent->k;
if (k < insert_parent->k)
{
// if the parent's child is a min value, then we can safely move towards it
if (min)
continue;
else
{
// otherwise we can break and this will insert the new node as the new min.
++insert_parent->children;
break;
}
} else
{
// parents child is min, so we move past it
if (min)
{
insert_point = skip_children(insert_point);
// can break as we insert here
++insert_parent->children;
break;
} else
{
// parents child is max, we can safely move towards it
continue;
}
}
} else
{
insert_parent = insert_point;
++insert_point;
if (k < insert_parent->k)
continue;
else
insert_point = skip_children(insert_point);
}
}
nodes.insert(insert_point, {std::move(k), std::move(v)});
}
void print(std::ostream& out, bool pretty_print)
{
std::stack<blt::size_t> left;
blt::size_t indent = 0;
for (auto& v : nodes)
{
if (v.children > 0)
{
create_indent(out, indent, pretty_print) << "(";
indent++;
left.emplace(v.children);
out << v.k << ": " << v.v << end_indent(pretty_print);
} else
create_indent(out, indent, pretty_print) << v.k << ": " << v.v << end_indent(pretty_print);
while (!left.empty())
{
auto top = left.top();
left.pop();
if (top == 0)
{
indent--;
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
continue;
} else
{
if (!pretty_print)
out << " ";
left.push(top - 1);
break;
}
}
}
while (!left.empty())
{
auto top = left.top();
left.pop();
if (top == 0)
{
indent--;
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
continue;
} else
{
out << "TREE MISMATCH";
break;
}
}
out << '\n';
}
std::optional<V> search(const K& k)
{
auto point = nodes.begin();
while (point != nodes.end())
{
if (k == point->k)
return point->v;
if (point->children == 0)
return {};
auto parent = point;
++point;
auto min = point->k < parent->k;
if (k >= parent->k)
{
if (min)
point = skip_children(point);
}
}
return {};
}
private:
auto skip_children(typename std::vector<node_t>::iterator begin)
{
blt::i64 children_left = 0;
do
{
if (children_left != 0)
children_left--;
if (begin->children > 0)
children_left += begin->children;
++begin;
} while (children_left > 0);
return begin;
}
std::ostream& create_indent(std::ostream& out, blt::size_t amount, bool pretty_print)
{
if (!pretty_print)
return out;
for (blt::size_t i = 0; i < amount; i++)
out << '\t';
return out;
}
std::string_view end_indent(bool pretty_print)
{
return pretty_print ? "\n" : "";
}
std::vector<node_t> nodes;
};
} }
#endif //BLT_BINARY_TREE_H #endif //BLT_BINARY_TREE_H

View File

@ -29,7 +29,7 @@
#include <blt/std/mmap.h> #include <blt/std/mmap.h>
#include <blt/compatibility.h> #include <blt/compatibility.h>
#include <stdexcept> #include <stdexcept>
#include "logging.h" #include "blt/logging/logging.h"
#include <cstdlib> #include <cstdlib>
#include <atomic> #include <atomic>

27
include/blt/std/defines.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_DEFINES_H
#define BLT_DEFINES_H
#if defined(BLT_DEBUG_OTEL) && defined(__has_include) &&__has_include(<opentelemetry/version.h>)
#define BLT_DEBUG_OTEL_ENABLED 1
#endif
#endif //BLT_DEFINES_H

View File

@ -375,12 +375,12 @@ namespace blt
}; };
template<typename T, typename E> template<typename T, typename E>
class expected<T, E, false> class expected<T, E, false> : public expected<T, E, true>
{ {
public: public:
using expected<T, E, true>::expected; using expected<T, E, true>::expected;
constexpr expected(const expected<T, E, false>& copy) = delete; constexpr expected(const expected& copy) = delete;
expected& operator=(const expected& copy) = delete; expected& operator=(const expected& copy) = delete;
}; };

View File

@ -1,780 +0,0 @@
/*
* Created by Brett on 20/07/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef BLT_TESTS_LOGGING2_H
#define BLT_TESTS_LOGGING2_H
#include <string>
#include <type_traits>
#include <functional>
#include <sstream>
#include <blt/config.h>
#include <iostream>
#include <cstdarg>
namespace blt::logging
{
enum class log_level
{
// default
NONE,
// low level
TRACE0, TRACE1, TRACE2, TRACE3,
// normal
TRACE, DEBUG, INFO,
// errors
WARN, ERROR, FATAL,
};
struct tag_func_param
{
blt::logging::log_level level;
const std::string& file, line, raw_string, formatted_string;
};
struct tag
{
// tag without the ${{ or }}
std::string tag;
// function to run: log level, file, line, and raw user input string are provided
std::function<std::string(const tag_func_param&)> func;
};
struct log_format
{
/**
* the log output format is the string which will be used to create the log output string
*
* Available tags:
* - ${{YEAR}} // current year
* - ${{MONTH}} // current month
* - ${{DAY}} // current day
* - ${{HOUR}} // current hour
* - ${{MINUTE}} // current minute
* - ${{SECOND}} // current second
* - ${{MS}} // current unix time
* - ${{NS}} // current ns from the high resolution system timer
* - ${{ISO_YEAR}} // ISO formatted 'year-month-day' in a single variable
* - ${{TIME}} // 'hour:minute:second' formatted string in a single variable
* - ${{FULL_TIME}} // 'year-month-day hour:minute:second' in a single variable
* - ${{LF}} // log level color (ANSI color code)
* - ${{ER}} // Error red
* - ${{CNR}} // conditional error red (only outputs if log level is an error!)
* - ${{RC}} // ANSI color reset
* - ${{LOG_LEVEL}} // current log level
* - ${{THREAD_NAME}} // current thread name, NOTE: thread names must be set by calling "setThreadName()" from the thread in question!
* - ${{FILE}} // file from which the macro is invoked
* - ${{LINE}} // line in the from which the macro is invoked
* - ${{RAW_STR}} // raw user string without formatting applied (NOTE: format args are not provided!)
* - ${{STR}} // the user supplied string (format applied!)
*/
std::string logOutputFormat = "\033[94m[${{TIME}}]${{RC}} ${{LF}}[${{LOG_LEVEL}}]${{RC}} \033[35m(${{FILE}}:${{LINE}})${{RC}} ${{CNR}}${{STR}}${{RC}}\n";
std::string levelNames[11] = {"STDOUT", "TRACE0", "TRACE1", "TRACE2", "TRACE3", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
std::string levelColors[11] = {"\033[0m", "\033[22;97m", "\033[97m", "\033[97m", "\033[97m", "\033[97m", "\033[36m", "\033[92m", "\033[93m",
"\033[91m", "\033[97;41m"};
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
bool printFullFileName = false;
// the logging lib will keep track of the largest line found so far and try to keep the spacing accordingly
// this is not thread safe!
bool ensureAlignment = false;
// should we log to file?
bool logToFile = false;
// should we log to console?
bool logToConsole = true;
// where should we log? (empty for current binary directory) should end in a / if not empty!
std::string logFilePath;
// logs to a file called $fileName_$count.log where count is the number of rollover files
// this accepts any of the macros above, using level names and colors should work but isn't supported.
std::string logFileName = "${{ISO_YEAR}}";
// default limit on file size: 10mb;
size_t logMaxFileSize = 1024 * 1024 * 10;
/**
* Variables below this line should never be changed by the user!
*/
// the current alignment width found (you shouldn't chance this variable!)
size_t currentWidth = 0;
// current number of file roll-overs. you shouldn't change this either.
size_t currentRollover = 0;
std::string lastFile;
};
struct logger
{
log_level level;
const char* file;
int line;
};
struct empty_logger
{
};
void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args);
void log_stream_internal(const std::string& str, const logger& logger);
template<typename T>
inline static void log_stream(const T& t, const logger& logger)
{
if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
{
log_stream_internal(std::to_string(t), logger);
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char*>)
{
log_stream_internal(t, logger);
} else
{
std::stringstream stream;
stream << t;
log_stream_internal(stream.str(), logger);
}
}
template<typename T>
inline void log(T t, log_level level, const char* file, int line, ...)
{
std::va_list args;
va_start(args, line);
if constexpr (std::is_arithmetic_v<T>)
{
log_internal(std::to_string(t), level, file, line, args);
} else if constexpr (std::is_same_v<T, std::string>)
{
log_internal(t, level, file, line, args);
} else if constexpr (std::is_same_v<T, const char*>){
log_internal(std::string(t), level, file, line, args);
} else
{
std::stringstream stream;
stream << t;
log_internal(stream.str(), level, file, line, args);
}
va_end(args);
}
template<typename T>
static inline const blt::logging::logger& operator<<(const blt::logging::logger& out, const T& t)
{
log_stream(t, out);
return out;
}
template<typename T>
static inline const blt::logging::empty_logger& operator<<(const blt::logging::empty_logger& out, const T&)
{
return out;
}
void flush();
void newline();
void setThreadName(const std::string& name);
void setLogFormat(const log_format& format);
void setLogColor(log_level level, const std::string& newFormat);
void setLogName(log_level level, const std::string& newFormat);
void setLogOutputFormat(const std::string& newFormat);
void setLogToFile(bool shouldLogToFile);
void setLogToConsole(bool shouldLogToConsole);
void setLogPath(const std::string& path);
void setLogFileName(const std::string& fileName);
void setMaxFileSize(size_t fileSize);
}
//#define BLT_LOGGING_IMPLEMENTATION
#ifdef BLT_LOGGING_IMPLEMENTATION
#include <iostream>
#include <chrono>
#include <ctime>
#include <unordered_map>
#include <thread>
#include <cstdarg>
#include <iostream>
#include <vector>
#if defined(CXX17_FILESYSTEM) || defined (CXX17_FILESYSTEM_LIBFS)
#include <filesystem>
#elif defined(CXX11_EXP_FILESYSTEM) || defined (CXX11_EXP_FILESYSTEM_LIBFS)
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
#include <ios>
#include <fstream>
template<typename K, typename V>
using hashmap = std::unordered_map<K, V>;
namespace blt::logging {
/**
* Used to store fast associations between built in tags and their respective values
*/
class tag_map {
private:
tag* tags;
size_t size;
[[nodiscard]] static inline size_t hash(const tag& t) {
size_t h = t.tag[1] * 3 - t.tag[0];
return h - 100;
}
// TODO: fix
void expand() {
auto newSize = size * 2;
auto newTags = new tag[newSize];
for (size_t i = 0; i < size; i++)
newTags[i] = tags[i];
delete[] tags;
tags = newTags;
size = newSize;
}
public:
tag_map(std::initializer_list<tag> initial_tags){
size_t max = 0;
for (const auto& t : initial_tags)
max = std::max(max, hash(t));
tags = new tag[(size = max+1)];
for (const auto& t : initial_tags)
insert(t);
}
tag_map(const tag_map& copy) {
tags = new tag[(size = copy.size)];
for (size_t i = 0; i < size; i++)
tags[i] = copy.tags[i];
}
void insert(const tag& t) {
auto h = hash(t);
//if (h > size)
// expand();
if (!tags[h].tag.empty())
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
tags[h] = t;
}
tag& operator[](const std::string& name) const {
auto h = hash(tag{name, nullptr});
if (h > size)
std::cerr << "Tag out of bounds";
return tags[h];
}
~tag_map(){
delete[] tags;
tags = nullptr;
}
};
class LogFileWriter {
private:
std::string m_path;
std::fstream* output = nullptr;
public:
explicit LogFileWriter() = default;
void writeLine(const std::string& path, const std::string& line){
if (path != m_path || output == nullptr){
clear();
delete output;
output = new std::fstream(path, std::ios::out | std::ios::app);
if (!output->good()){
throw std::runtime_error("Unable to open console filestream!\n");
}
}
if (!output->good()){
std::cerr << "There has been an error in the logging file stream!\n";
output->clear();
}
*output << line;
}
void clear(){
if (output != nullptr) {
try {
output->flush();
output->close();
} catch (std::exception& e){
std::cerr << e.what() << "\n";
}
}
}
~LogFileWriter() {
clear();
delete(output);
}
};
#ifdef WIN32
#define BLT_NOW() auto t = std::time(nullptr); tm now{}; localtime_s(&now, &t)
#else
#define BLT_NOW() auto t = std::time(nullptr); auto now_ptr = std::localtime(&t); auto& now = *now_ptr
#endif
//#define BLT_NOW() auto t = std::time(nullptr); tm now; localtime_s(&now, &t); //auto now = std::localtime(&t)
#define BLT_ISO_YEAR(S) auto S = std::to_string(now.tm_year + 1900); \
S += '-'; \
S += ensureHasDigits(now.tm_mon+1, 2); \
S += '-'; \
S += ensureHasDigits(now.tm_mday, 2);
#define BLT_CUR_TIME(S) auto S = ensureHasDigits(now.tm_hour, 2); \
S += ':'; \
S += ensureHasDigits(now.tm_min, 2); \
S += ':'; \
S += ensureHasDigits(now.tm_sec, 2);
static inline std::string ensureHasDigits(int current, int digits) {
std::string asString = std::to_string(current);
auto length = digits - asString.length();
if (length <= 0)
return asString;
std::string zeros;
zeros.reserve(length);
for (unsigned int i = 0; i < length; i++){
zeros += '0';
}
return zeros + asString;
}
log_format loggingFormat {};
hashmap<std::thread::id, std::string> loggingThreadNames;
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
LogFileWriter writer;
const std::unique_ptr<tag_map> tagMap = std::make_unique<tag_map>(tag_map{
{"YEAR", [](const tag_func_param&) -> std::string {
BLT_NOW();
return std::to_string(now.tm_year);
}},
{"MONTH", [](const tag_func_param&) -> std::string {
BLT_NOW();
return ensureHasDigits(now.tm_mon+1, 2);
}},
{"DAY", [](const tag_func_param&) -> std::string {
BLT_NOW();
return ensureHasDigits(now.tm_mday, 2);
}},
{"HOUR", [](const tag_func_param&) -> std::string {
BLT_NOW();
return ensureHasDigits(now.tm_hour, 2);
}},
{"MINUTE", [](const tag_func_param&) -> std::string {
BLT_NOW();
return ensureHasDigits(now.tm_min, 2);
}},
{"SECOND", [](const tag_func_param&) -> std::string {
BLT_NOW();
return ensureHasDigits(now.tm_sec, 2);
}},
{"MS", [](const tag_func_param&) -> std::string {
return std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
);
}},
{"NS", [](const tag_func_param&) -> std::string {
return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
);
}},
{"ISO_YEAR", [](const tag_func_param&) -> std::string {
BLT_NOW();
BLT_ISO_YEAR(returnStr);
return returnStr;
}},
{"TIME", [](const tag_func_param&) -> std::string {
BLT_NOW();
BLT_CUR_TIME(returnStr);
return returnStr;
}},
{"FULL_TIME", [](const tag_func_param&) -> std::string {
BLT_NOW();
BLT_ISO_YEAR(ISO);
BLT_CUR_TIME(TIME);
ISO += ' ';
ISO += TIME;
return ISO;
}},
{"LF", [](const tag_func_param& f) -> std::string {
return loggingFormat.levelColors[(int)f.level];
}},
{"ER", [](const tag_func_param&) -> std::string {
return loggingFormat.levelColors[(int)log_level::ERROR];
}},
{"CNR", [](const tag_func_param& f) -> std::string {
return f.level >= log_level::ERROR ? loggingFormat.levelColors[(int)log_level::ERROR] : "";
}},
{"RC", [](const tag_func_param&) -> std::string {
return "\033[0m";
}},
{"LOG_LEVEL", [](const tag_func_param& f) -> std::string {
return loggingFormat.levelNames[(int)f.level];
}},
{"THREAD_NAME", [](const tag_func_param&) -> std::string {
if (loggingThreadNames.find(std::this_thread::get_id()) == loggingThreadNames.end())
return "UNKNOWN";
return loggingThreadNames[std::this_thread::get_id()];
}},
{"FILE", [](const tag_func_param& f) -> std::string {
return f.file;
}},
{"LINE", [](const tag_func_param& f) -> std::string {
return f.line;
}},
{"RAW_STR", [](const tag_func_param& f) -> std::string {
return f.raw_string;
}},
{"STR", [](const tag_func_param& f) -> std::string {
return f.formatted_string;
}}
});
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
size_t pos = 0;
std::vector<std::string> tokens;
while ((pos = s.find(delim)) != std::string::npos) {
auto token = s.substr(0, pos);
tokens.push_back(token);
s.erase(0, pos + delim.length());
}
tokens.push_back(s);
return tokens;
}
inline std::string filename(const std::string& path){
if (loggingFormat.printFullFileName)
return path;
auto paths = split(path, "/");
auto final = paths[paths.size()-1];
if (final == "/")
return paths[paths.size()-2];
return final;
}
class string_parser {
private:
std::string _str;
size_t _pos;
public:
explicit string_parser(std::string str): _str(std::move(str)), _pos(0) {}
inline char next(){
return _str[_pos++];
}
[[nodiscard]] inline bool has_next() const {
return _pos < _str.size();
}
};
std::string stripANSI(const std::string& str){
string_parser parser(str);
std::string out;
while (parser.has_next()){
char c = parser.next();
if (c == '\033'){
while (parser.has_next() && parser.next() != 'm');
} else
out += c;
}
return out;
}
void applyCFormatting(const std::string& format, std::string& output, std::va_list& args){
// args must be copied because they will be consumed by the first vsnprintf
va_list args_copy;
va_copy(args_copy, args);
auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1;
auto* buffer = new char[static_cast<unsigned long>(buffer_size)];
vsnprintf(buffer, buffer_size, format.c_str(), args);
output = std::string(buffer);
delete[] buffer;
va_end(args_copy);
}
/**
* Checks if the next character in the parser is a tag opening, if not output the buffer to the out string
*/
inline bool tagOpening(string_parser& parser, std::string& out){
char c = ' ';
if (parser.has_next() && (c = parser.next()) == '{')
if (parser.has_next() && (c = parser.next()) == '{')
return true;
else
out += c;
else
out += c;
return false;
}
void parseString(string_parser& parser, std::string& out, const std::string& userStr, log_level level, const char* file, int line){
while (parser.has_next()){
char c = parser.next();
std::string nonTag;
if (c == '$' && tagOpening(parser, nonTag)){
std::string tag;
while (parser.has_next()){
c = parser.next();
if (c == '}')
break;
tag += c;
}
c = parser.next();
if (parser.has_next() && c != '}') {
std::cerr << "Error processing tag, is not closed with two '}'!\n";
break;
}
if (loggingFormat.ensureAlignment && tag == "STR") {
auto currentOutputWidth = out.size();
auto& longestWidth = loggingFormat.currentWidth;
longestWidth = std::max(longestWidth, currentOutputWidth);
// pad with spaces
if (currentOutputWidth != longestWidth){
for (size_t i = currentOutputWidth; i < longestWidth; i++)
out += ' ';
}
}
tag_func_param param{
level, filename({file}), std::to_string(line), userStr, userStr
};
out += (*tagMap)[tag].func(param);
} else {
out += c;
out += nonTag;
}
}
}
std::string applyFormatString(const std::string& str, log_level level, const char* file, int line){
// this can be speedup by preprocessing the string into an easily callable class
// where all the variables are ready to be substituted in one step
// and all static information already entered
string_parser parser(loggingFormat.logOutputFormat);
std::string out;
parseString(parser, out, str, level, file, line);
return out;
}
void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args) {
std::string withoutLn = format;
auto len = withoutLn.length();
if (len > 0 && withoutLn[len - 1] == '\n')
withoutLn = withoutLn.substr(0, len-1);
std::string out;
applyCFormatting(withoutLn, out, args);
if (level == log_level::NONE){
std::cout << out << std::endl;
return;
}
std::string finalFormattedOutput = applyFormatString(out, level, file, line);
if (loggingFormat.logToConsole)
std::cout << finalFormattedOutput;
if (loggingFormat.logToFile){
string_parser parser(loggingFormat.logFileName);
std::string fileName;
parseString(parser, fileName, withoutLn, level, file, line);
auto path = loggingFormat.logFilePath;
if (!path.empty() && path[path.length()-1] != '/')
path += '/';
// if the file has changed (new day in default case) we should reset the rollover count
if (loggingFormat.lastFile != fileName){
loggingFormat.currentRollover = 0;
loggingFormat.lastFile = fileName;
}
path += fileName;
path += '-';
path += std::to_string(loggingFormat.currentRollover);
path += ".log";
if (std::filesystem::exists(path)) {
auto fileSize = std::filesystem::file_size(path);
// will start on next file
if (fileSize > loggingFormat.logMaxFileSize)
loggingFormat.currentRollover++;
}
writer.writeLine(path, stripANSI(finalFormattedOutput));
}
//std::cout.flush();
}
void log_stream_internal(const std::string& str, const logger& logger) {
auto& s = loggingStreamLines[std::this_thread::get_id()][logger.level];
// s += str;
for (char c : str){
s += c;
if (c == '\n'){
log(s, logger.level, logger.file, logger.line);
s = "";
}
}
}
void setThreadName(const std::string& name) {
loggingThreadNames[std::this_thread::get_id()] = name;
}
void setLogFormat(const log_format& format){
loggingFormat = format;
}
void setLogColor(log_level level, const std::string& newFormat){
loggingFormat.levelColors[(int)level] = newFormat;
}
void setLogName(log_level level, const std::string& newFormat){
loggingFormat.levelNames[(int)level] = newFormat;
}
void setLogOutputFormat(const std::string& newFormat){
loggingFormat.logOutputFormat = newFormat;
}
void setLogToFile(bool shouldLogToFile){
loggingFormat.logToFile = shouldLogToFile;
}
void setLogToConsole(bool shouldLogToConsole){
loggingFormat.logToConsole = shouldLogToConsole;
}
void setLogPath(const std::string& path){
loggingFormat.logFilePath = path;
}
void setLogFileName(const std::string& fileName){
loggingFormat.logFileName = fileName;
}
void setMaxFileSize(size_t fileSize) {
loggingFormat.logMaxFileSize = fileSize;
}
void flush() {
std::cerr.flush();
std::cout.flush();
}
void newline()
{
std::cout << std::endl;
}
}
#endif
#if defined(__clang__) || defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#ifdef BLT_DISABLE_LOGGING
#define BLT_LOG(format, level, ...)
#define BLT_LOG_STREAM(level)
#define BLT_TRACE0_STREAM
#define BLT_TRACE1_STREAM
#define BLT_TRACE2_STREAM
#define BLT_TRACE3_STREAM
#define BLT_TRACE_STREAM
#define BLT_DEBUG_STREAM
#define BLT_INFO_STREAM
#define BLT_WARN_STREAM
#define BLT_ERROR_STREAM
#define BLT_FATAL_STREAM
#define BLT_TRACE(format, ...)
#define BLT_DEBUG(format, ...)
#define BLT_INFO(format, ...)
#define BLT_WARN(format, ...)
#define BLT_ERROR(format, ...)
#define BLT_FATAL(format, ...)
#else
#define BLT_NEWLINE() blt::logging::newline()
#define BLT_LOG(format, level, ...) blt::logging::log(format, level, __FILE__, __LINE__, ##__VA_ARGS__)
#define BLT_LOG_STREAM(level) blt::logging::logger{level, __FILE__, __LINE__}
#ifdef BLT_DISABLE_TRACE
#define BLT_TRACE(format, ...)
#define BLT_TRACE0_STREAM blt::logging::empty_logger{}
#define BLT_TRACE1_STREAM blt::logging::empty_logger{}
#define BLT_TRACE2_STREAM blt::logging::empty_logger{}
#define BLT_TRACE3_STREAM blt::logging::empty_logger{}
#define BLT_TRACE_STREAM blt::logging::empty_logger{}
#else
#define BLT_TRACE(format, ...) BLT_LOG(format, blt::logging::log_level::TRACE, ##__VA_ARGS__)
#define BLT_TRACE0_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE0)
#define BLT_TRACE1_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE1)
#define BLT_TRACE2_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE2)
#define BLT_TRACE3_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE3)
#define BLT_TRACE_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE)
#endif
#ifdef BLT_DISABLE_DEBUG
#define BLT_DEBUG(format, ...)
#define BLT_DEBUG_STREAM blt::logging::empty_logger{}
#else
#define BLT_DEBUG(format, ...) BLT_LOG(format, blt::logging::log_level::DEBUG, ##__VA_ARGS__)
#define BLT_DEBUG_STREAM BLT_LOG_STREAM(blt::logging::log_level::DEBUG)
#endif
#ifdef BLT_DISABLE_INFO
#define BLT_INFO(format, ...)
#define BLT_INFO_STREAM blt::logging::empty_logger{}
#else
#define BLT_INFO(format, ...) BLT_LOG(format, blt::logging::log_level::INFO, ##__VA_ARGS__)
#define BLT_INFO_STREAM BLT_LOG_STREAM(blt::logging::log_level::INFO)
#endif
#ifdef BLT_DISABLE_WARN
#define BLT_WARN(format, ...)
#define BLT_WARN_STREAM blt::logging::empty_logger{}
#else
#define BLT_WARN(format, ...) BLT_LOG(format, blt::logging::log_level::WARN, ##__VA_ARGS__)
#define BLT_WARN_STREAM BLT_LOG_STREAM(blt::logging::log_level::WARN)
#endif
#ifdef BLT_DISABLE_ERROR
#define BLT_ERROR(format, ...)
#define BLT_ERROR_STREAM blt::logging::empty_logger{}
#else
#define BLT_ERROR(format, ...) BLT_LOG(format, blt::logging::log_level::ERROR, ##__VA_ARGS__)
#define BLT_ERROR_STREAM BLT_LOG_STREAM(blt::logging::log_level::ERROR)
#endif
#ifdef BLT_DISABLE_FATAL
#define BLT_FATAL(format, ...)
#define BLT_FATAL_STREAM blt::logging::empty_logger{}
#else
#define BLT_FATAL(format, ...) BLT_LOG(format, blt::logging::log_level::FATAL, ##__VA_ARGS__)
#define BLT_FATAL_STREAM BLT_LOG_STREAM(blt::logging::log_level::FATAL)
#endif
#endif
#if defined(__clang__) || defined(__llvm__)
#pragma clang diagnostic pop
#endif
#endif //BLT_TESTS_LOGGING2_H

View File

@ -11,13 +11,9 @@
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <cstring> #include <cstring>
#include "queue.h"
#include "utility.h"
#include <blt/std/assert.h> #include <blt/std/assert.h>
#include <cstdint>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <cstring>
namespace blt namespace blt
{ {
@ -42,6 +38,7 @@ namespace blt
using const_pointer = const T*; using const_pointer = const T*;
using reference = T&; using reference = T&;
using const_reference = const T&; using const_reference = const T&;
using iterator = ptr_iterator<T>; using iterator = ptr_iterator<T>;
using const_iterator = ptr_iterator<const T>; using const_iterator = ptr_iterator<const T>;
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;

View File

@ -19,38 +19,41 @@
#ifndef BLT_MEMORY_UTIL_H #ifndef BLT_MEMORY_UTIL_H
#define BLT_MEMORY_UTIL_H #define BLT_MEMORY_UTIL_H
#include <blt/std/types.h>
#include <type_traits> #include <type_traits>
#include <array> #include <array>
#include <string>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <climits>
#include <cmath>
#include <iostream>
#if defined(__clang__) || defined(__llvm__) || defined(__GNUC__) || defined(__GNUG__) #if defined(__clang__) || defined(__llvm__) || defined(__GNUC__) || defined(__GNUG__)
#if defined(__GNUC__) || defined(__GNUG__) #if (defined(__GNUC__) || defined(__GNUG__)) && !defined(WIN32)
#include <byteswap.h> #include <byteswap.h>
#define SWAP16(val) bswap_16(val) #define SWAP16(val) bswap_16(val)
#define SWAP32(val) bswap_32(val) #define SWAP32(val) bswap_32(val)
#define SWAP64(val) bswap_64(val) #define SWAP64(val) bswap_64(val)
#else #else
#define SWAP16(val) __builtin_bswap16(val) #define SWAP16(val) __builtin_bswap16(val)
#define SWAP32(val) __builtin_bswap32(val) #define SWAP32(val) __builtin_bswap32(val)
#define SWAP64(val) __builtin_bswap64(val) #define SWAP64(val) __builtin_bswap64(val)
#endif #endif
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
#include <bit> #include <bit>
#define ENDIAN_LOOKUP(little_endian) (std::endian::native == std::endian::little && !little_endian) || \ #define ENDIAN_LOOKUP(little_endian) (std::endian::native == std::endian::little && !little_endian) || \
(std::endian::native == std::endian::big && little_endian) (std::endian::native == std::endian::big && little_endian)
#else #else
#define ENDIAN_LOOKUP(little_endian) !little_endian #define ENDIAN_LOOKUP(little_endian) !little_endian
#endif #endif
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#include <intrin.h> #include <intrin.h>
#define SWAP16(val) _byteswap_ushort(val) #define SWAP16(val) _byteswap_ushort(val)
@ -61,13 +64,43 @@
namespace blt::mem namespace blt::mem
{ {
template <typename R, typename T>
static R type_cast(T type)
{
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable to be type casted!");
static_assert(sizeof(T) == sizeof(R));
R r;
std::memcpy(&r, &type, sizeof(type));
return r;
}
template <typename T>
void reverse(T& out)
{
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable to be reversible!");
// if we need to swap find the best way to do so
if constexpr (std::is_same_v<T, std::int16_t> || std::is_same_v<T, std::uint16_t>)
out = type_cast<T>(SWAP16(type_cast<std::uint16_t>(out)));
else if constexpr (std::is_same_v<T, std::int32_t> || std::is_same_v<T, std::uint32_t> || std::is_same_v<T, float>)
out = type_cast<T>(SWAP32(type_cast<std::uint32_t>(out)));
else if constexpr (std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t> || std::is_same_v<T, double>)
out = type_cast<T>(SWAP64(type_cast<std::uint64_t>(out)));
else
{
std::array<std::byte, sizeof(T)> data;
std::memcpy(data.data(), &out, sizeof(T));
std::reverse(data.begin(), data.end());
std::memcpy(&out, data.data(), sizeof(T));
}
}
// Used to grab the byte-data of any T element. Defaults to Big Endian, however can be configured to use little endian // Used to grab the byte-data of any T element. Defaults to Big Endian, however can be configured to use little endian
template<bool little_endian = false, typename BYTE_TYPE, typename T> template <bool little_endian = false, typename BYTE_TYPE, typename T>
inline static int toBytes(const T& in, BYTE_TYPE* out) int toBytes(const T& in, BYTE_TYPE* out)
{ {
if constexpr (!(std::is_same_v<BYTE_TYPE, std::int8_t> || std::is_same_v<BYTE_TYPE, std::uint8_t>)) if constexpr (!(std::is_same_v<BYTE_TYPE, std::int8_t> || std::is_same_v<BYTE_TYPE, std::uint8_t>))
static_assert("Must provide a signed/unsigned int8 type"); static_assert("Must provide a signed/unsigned int8 type");
std::memcpy(out, (void*) &in, sizeof(T)); std::memcpy(out, &in, sizeof(T));
if constexpr (ENDIAN_LOOKUP(little_endian)) if constexpr (ENDIAN_LOOKUP(little_endian))
{ {
@ -80,41 +113,28 @@ namespace blt::mem
} }
// Used to cast the binary data of any T object, into a T object. Assumes data is in big ending (configurable) // Used to cast the binary data of any T object, into a T object. Assumes data is in big ending (configurable)
template<bool little_endian = false, typename BYTE_TYPE, typename T> template <bool little_endian = false, typename BYTE_TYPE, typename T>
inline static int fromBytes(const BYTE_TYPE* in, T& out) int fromBytes(const BYTE_TYPE* in, T& out)
{ {
if constexpr (!(std::is_same_v<BYTE_TYPE, std::int8_t> || std::is_same_v<BYTE_TYPE, std::uint8_t>)) if constexpr (!(std::is_same_v<BYTE_TYPE, std::int8_t> || std::is_same_v<BYTE_TYPE, std::uint8_t>))
static_assert("Must provide a signed/unsigned int8 type"); static_assert("Must provide a signed/unsigned int8 type");
std::array<BYTE_TYPE, sizeof(T)> data; std::memcpy(&out, in, sizeof(T));
std::memcpy(data.data(), in, sizeof(T));
if constexpr (ENDIAN_LOOKUP(little_endian)) if constexpr (ENDIAN_LOOKUP(little_endian))
{ {
// if we need to swap find the best way to do so reverse(out);
if constexpr (std::is_same_v<T, int16_t> || std::is_same_v<T, uint16_t>)
out = SWAP16(*reinterpret_cast<T*>(data.data()));
else if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
out = SWAP32(*reinterpret_cast<T*>(data.data()));
else if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
out = SWAP64(*reinterpret_cast<T*>(data.data()));
else
{
std::reverse(data.begin(), data.end());
out = *reinterpret_cast<T*>(data.data());
}
} }
return 0; return 0;
} }
template<bool little_endian = false, typename BYTE_TYPE, typename T> template <bool little_endian = false, typename BYTE_TYPE, typename T>
inline static int fromBytes(const BYTE_TYPE* in, T* out) static int fromBytes(const BYTE_TYPE* in, T* out)
{ {
return fromBytes(in, *out); return fromBytes<little_endian>(in, *out);
} }
inline static size_t next_byte_allocation(size_t prev_size, size_t default_allocation_block = 8192, size_t default_size = 16) inline std::size_t next_byte_allocation(std::size_t prev_size, std::size_t default_allocation_block = 8192, std::size_t default_size = 16)
{ {
if (prev_size < default_size) if (prev_size < default_size)
return default_size; return default_size;
@ -123,138 +143,347 @@ namespace blt::mem
return prev_size + default_allocation_block; return prev_size + default_allocation_block;
} }
template<typename R, typename T> template <bool bits = true, typename OStream, typename Value>
inline static R type_cast(T type) void print_bytes(OStream& stream, const Value& value)
{ {
static_assert(sizeof(T) == sizeof(R)); constexpr auto size = sizeof(Value);
R r;
std::memcpy(&r, &type, sizeof(type)); std::string line;
return r; for (std::size_t i = 0; i < size; i++)
{
std::uint8_t byte;
std::memcpy(&byte, reinterpret_cast<const char*>(&value) + i, 1);
if constexpr (bits)
{
for (std::ptrdiff_t j = CHAR_BIT - 1; j >= 0; j--)
{
const auto bit = (byte >> j) & 1;
line += std::to_string(bit);
}
}
else
{
const auto byte_str = std::to_string(byte);
const auto amount = CHAR_BIT - byte_str.size();
for (std::size_t j = 0; j < static_cast<std::size_t>(std::ceil(static_cast<double>(amount) / 2.0)); j++)
line += ' ';
line += byte_str;
for (std::size_t j = 0; j < static_cast<std::size_t>(std::floor(static_cast<double>(amount) / 2.0)); j++)
line += ' ';
}
if (i != size - 1)
line += " : ";
}
for (std::size_t i = 0; i < size; i++)
{
auto index = std::to_string(i);
const auto amount = CHAR_BIT - index.size();
for (std::size_t j = 0; j < static_cast<std::size_t>(std::ceil(static_cast<double>(amount) / 2.0)); j++)
stream << ' ';
stream << index;
for (std::size_t j = 0; j < static_cast<std::size_t>(std::floor(static_cast<double>(amount) / 2.0)); j++)
stream << ' ';
if (i != size - 1)
stream << " | ";
}
stream << '\n';
stream << line;
stream << '\n';
} }
// TODO: check if the platform actually has enough room in their pointers for this. plus endianness
struct bit_storage
{
static constexpr std::size_t START_BIT = 48;
static constexpr std::size_t END_BIT = sizeof(std::uintptr_t) * CHAR_BIT;
static constexpr std::size_t AVAILABLE_BITS = END_BIT - START_BIT;
static constexpr std::size_t make_storage_ones()
{
#ifdef __EMSCRIPTEN__
return ~static_cast<size_t>(0);
#else
std::size_t result = 0;
for (std::size_t i = START_BIT; i < END_BIT; i++)
result |= 1ul << i;
return result;
#endif
}
static constexpr std::size_t make_ptr_ones()
{
#ifdef __EMSCRIPTEN__
return ~static_cast<size_t>(0);
#else
std::size_t result = 0;
for (std::size_t i = 0; i < START_BIT; i++)
result |= 1ul << i;
return result;
#endif
}
bit_storage(): bits(0)
{
}
explicit bit_storage(const std::uint16_t bits): bits(bits)
{
}
std::uint16_t bits : AVAILABLE_BITS;
};
template <typename Ptr>
struct pointer_storage
{
static constexpr std::size_t STORAGE_ALL_ONES = bit_storage::make_storage_ones();
static constexpr std::size_t PTR_ALL_ONES = bit_storage::make_ptr_ones();
explicit pointer_storage(Ptr* ptr): ptr_bits(reinterpret_cast<std::uintptr_t>(ptr))
{
}
explicit pointer_storage(Ptr* ptr, const bit_storage bits): ptr_bits(reinterpret_cast<std::uintptr_t>(ptr))
{
storage(bits);
}
template <typename T, std::enable_if_t<!std::is_same_v<T, bit_storage>, bool> = false>
explicit pointer_storage(Ptr* ptr, const T& type): ptr_bits(reinterpret_cast<std::uintptr_t>(ptr))
{
storage(type);
}
[[nodiscard]] bit_storage storage() const noexcept
{
bit_storage storage{};
storage.bits = (ptr_bits & STORAGE_ALL_ONES) >> bit_storage::START_BIT;
return storage;
}
[[nodiscard]] bool bit(const std::size_t index) const noexcept
{
if (index >= bit_storage::END_BIT)
return false;
return (ptr_bits >> (bit_storage::START_BIT + index)) & 1;
}
pointer_storage& bit(const std::size_t index, const bool b) noexcept
{
#ifndef __EMSCRIPTEN__
if (index >= bit_storage::END_BIT)
return *this;
ptr_bits &= ~(1ul << (bit_storage::START_BIT + index));
ptr_bits |= (static_cast<std::uintptr_t>(b) << (bit_storage::START_BIT + index));
#endif
return *this;
}
template<typename T, std::enable_if_t<!std::is_same_v<T, bit_storage>, bool> = false>
pointer_storage& storage(const T& type)
{
// TODO!! Emscript support!
#ifndef __EMSCRIPTEN__
static_assert(sizeof(T) <= sizeof(std::uintptr_t), "Type takes too many bits to be stored!");
static constexpr std::uintptr_t store_bits = (2 << (bit_storage::AVAILABLE_BITS - 1)) - 1;
std::uintptr_t bit_store = 0;
bit_storage store{};
std::memcpy(&bit_store, &type, sizeof(T));
store.bits |= bit_store & store_bits;
storage(store);
#endif
return *this;
}
pointer_storage& storage(const bit_storage bits) noexcept
{
#ifndef __EMSCRIPTEN__
ptr_bits = ((ptr_bits & PTR_ALL_ONES) | (static_cast<std::uintptr_t>(bits.bits) << bit_storage::START_BIT));
#endif
return *this;
}
pointer_storage& operator=(const bit_storage bits) noexcept
{
storage(bits);
return *this;
}
template<typename T, std::enable_if_t<!std::is_same_v<T, bit_storage>, bool> = false>
pointer_storage& operator=(const T& type)
{
storage(type);
return *this;
}
pointer_storage& clear_storage() noexcept
{
ptr_bits &= PTR_ALL_ONES;
return *this;
}
// changes pointer without changing the tag bits
pointer_storage& pointer(Ptr* ptr) noexcept
{
const bit_storage old_storage = storage();
ptr_bits = (reinterpret_cast<std::uintptr_t>(ptr) & PTR_ALL_ONES) | old_storage.bits;
return *this;
}
pointer_storage& operator=(Ptr* ptr) noexcept
{
pointer(ptr);
return *this;
}
pointer_storage& clear_pointer() noexcept
{
ptr_bits &= STORAGE_ALL_ONES;
return *this;
}
Ptr* get() const noexcept
{
return reinterpret_cast<Ptr*>(ptr_bits & PTR_ALL_ONES);
}
Ptr& operator*() const noexcept
{
return *get();
}
Ptr* operator->() const noexcept
{
return get();
}
private:
std::uintptr_t ptr_bits;
};
} }
namespace blt namespace blt
{ {
template<typename V> template <typename V>
struct ptr_iterator struct ptr_iterator
{ {
public: using iterator_category = std::random_access_iterator_tag;
using iterator_category = std::random_access_iterator_tag; using difference_type = std::ptrdiff_t;
using difference_type = blt::ptrdiff_t; using value_type = V;
using value_type = V; using pointer = value_type*;
using pointer = value_type*; using reference = value_type&;
using reference = value_type&; using iter_reference = ptr_iterator&;
using iter_reference = ptr_iterator&;
explicit ptr_iterator(V* v): _v(v) explicit ptr_iterator(V* v): _v(v)
{} {
}
reference operator*() const reference operator*() const
{ {
return *_v; return *_v;
} }
pointer operator->() pointer operator->()
{ {
return _v; return _v;
} }
ptr_iterator& operator++() ptr_iterator& operator++()
{ {
_v++; _v++;
return *this; return *this;
} }
ptr_iterator& operator--() ptr_iterator& operator--()
{ {
_v--; _v--;
return *this; return *this;
} }
ptr_iterator operator++(int) ptr_iterator operator++(int)
{ {
auto tmp = *this; auto tmp = *this;
++(*this); ++(*this);
return tmp; return tmp;
} }
ptr_iterator operator--(int) ptr_iterator operator--(int)
{ {
auto tmp = *this; auto tmp = *this;
--(*this); --(*this);
return tmp; return tmp;
} }
iter_reference operator+=(difference_type amount) iter_reference operator+=(difference_type amount)
{ {
_v += amount; _v += amount;
return *this; return *this;
} }
iter_reference operator-=(difference_type amount) iter_reference operator-=(difference_type amount)
{ {
_v -= amount; _v -= amount;
return *this; return *this;
} }
reference operator[](difference_type index) reference operator[](difference_type index)
{ {
return *(_v + index); return *(_v + index);
} }
reference operator[](blt::size_t index) reference operator[](std::size_t index)
{ {
return *(_v + index); return *(_v + index);
} }
friend bool operator<(const ptr_iterator& a, const ptr_iterator& b) friend bool operator<(const ptr_iterator& a, const ptr_iterator& b)
{ {
return b._v - a._v > 0; return b._v - a._v > 0;
} }
friend bool operator>(const ptr_iterator& a, const ptr_iterator& b) friend bool operator>(const ptr_iterator& a, const ptr_iterator& b)
{ {
return a._v - b._v > 0; return a._v - b._v > 0;
} }
friend bool operator<=(const ptr_iterator& a, const ptr_iterator& b) friend bool operator<=(const ptr_iterator& a, const ptr_iterator& b)
{ {
return b._v - a._v >= 0; return b._v - a._v >= 0;
} }
friend bool operator>=(const ptr_iterator& a, const ptr_iterator& b) friend bool operator>=(const ptr_iterator& a, const ptr_iterator& b)
{ {
return a._v - b._v >= 0; return a._v - b._v >= 0;
} }
friend difference_type operator-(const ptr_iterator& a, const ptr_iterator& b) friend difference_type operator-(const ptr_iterator& a, const ptr_iterator& b)
{ {
return a._v - b._v; return a._v - b._v;
} }
friend ptr_iterator operator+(const ptr_iterator& a, difference_type n) friend ptr_iterator operator+(const ptr_iterator& a, difference_type n)
{ {
return ptr_iterator(a._v + n); return ptr_iterator(a._v + n);
} }
friend ptr_iterator operator+(difference_type n, const ptr_iterator& a) friend ptr_iterator operator+(difference_type n, const ptr_iterator& a)
{ {
return ptr_iterator(a._v + n); return ptr_iterator(a._v + n);
} }
friend bool operator==(const ptr_iterator& a, const ptr_iterator& b) friend bool operator==(const ptr_iterator& a, const ptr_iterator& b)
{ {
return a._v == b._v; return a._v == b._v;
} }
friend bool operator!=(const ptr_iterator& a, const ptr_iterator& b) friend bool operator!=(const ptr_iterator& a, const ptr_iterator& b)
{ {
return a._v != b._v; return a._v != b._v;
} }
private: private:
V* _v; V* _v;
}; };
} }

View File

@ -19,9 +19,11 @@
#ifndef BLT_MMAP_H #ifndef BLT_MMAP_H
#define BLT_MMAP_H #define BLT_MMAP_H
#include <blt/std/logging.h>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <cstdlib> #include <cstdlib>
#include <exception>
#include <string>
#include <string_view>
// size of 2mb in bytes // size of 2mb in bytes
inline constexpr blt::size_t BLT_2MB_SIZE = 2048 * 1024; inline constexpr blt::size_t BLT_2MB_SIZE = 2048 * 1024;
@ -41,7 +43,7 @@ namespace blt
public: public:
bad_alloc_t() = default; bad_alloc_t() = default;
explicit bad_alloc_t(std::string_view str): str(str) explicit bad_alloc_t(const std::string_view str): str(str)
{} {}
explicit bad_alloc_t(std::string str): str(std::move(str)) explicit bad_alloc_t(std::string str): str(std::move(str))
@ -95,7 +97,11 @@ namespace blt
public: public:
void* allocate(blt::size_t bytes) // NOLINT void* allocate(blt::size_t bytes) // NOLINT
{ {
#ifdef WIN32
return _aligned_malloc(bytes, BLT_2MB_SIZE);
#else
return std::aligned_alloc(BLT_2MB_SIZE, bytes); return std::aligned_alloc(BLT_2MB_SIZE, bytes);
#endif
} }
void deallocate(void* ptr, blt::size_t) // NOLINT void deallocate(void* ptr, blt::size_t) // NOLINT

View File

@ -191,13 +191,7 @@ namespace blt::random
} }
template<typename Container> template<typename Container>
constexpr auto& select(Container& container) constexpr decltype(auto) select(Container& container)
{
return container[get_u64(0, container.size())];
}
template<typename Container>
constexpr const auto& select(const Container& container)
{ {
return container[get_u64(0, container.size())]; return container[get_u64(0, container.size())];
} }

View File

@ -213,25 +213,28 @@ namespace blt
{} {}
template<class R, class RCV = std::remove_cv_t<std::remove_reference_t<R>>, typename std::enable_if_t< template<class R, class RCV = std::remove_cv_t<std::remove_reference_t<R>>, typename std::enable_if_t<
extent != dynamic_extent && span_detail::is_cont_v<RCV> && extent != dynamic_extent && span_detail::is_cont_v<RCV>, bool> = true>
std::is_convertible_v<std::remove_pointer_t<decltype(std::data(std::declval<R>()))>(*)[], T(*)[]>, bool> = true>
explicit constexpr span(R&& range): size_(std::size(range)), data_(std::data(range)) explicit constexpr span(R&& range): size_(std::size(range)), data_(std::data(range))
{} {}
template<class R, class RCV = std::remove_cv_t<std::remove_reference_t<R>>, typename std::enable_if_t< template<class R, class RCV = std::remove_cv_t<std::remove_reference_t<R>>, typename std::enable_if_t<
extent == dynamic_extent && span_detail::is_cont_v<RCV> && extent == dynamic_extent && span_detail::is_cont_v<RCV>, bool> = true>
std::is_convertible_v<std::remove_pointer_t<decltype(std::data(std::declval<R>()))>(*)[], T(*)[]>, bool> = true> constexpr span(R& range): size_(std::size(range)), data_(range.data()) // NOLINT
constexpr span(R&& range): size_(std::size(range)), data_(std::data(range)) // NOLINT
{} {}
template<size_type SIZE, typename std::enable_if_t< template<class R, class RCV = std::remove_cv_t<std::remove_reference_t<R>>, typename std::enable_if_t<
extent == dynamic_extent && span_detail::is_cont_v<RCV>, bool> = true>
constexpr span(const R& range): size_(std::size(range)), data_(range.data()) // NOLINT
{}
template<blt::size_t SIZE, typename std::enable_if_t<
extent != dynamic_extent && SIZE == extent && std::is_const_v<element_type>, bool> = true> extent != dynamic_extent && SIZE == extent && std::is_const_v<element_type>, bool> = true>
explicit constexpr span(std::initializer_list<value_type> il) noexcept: size_(il.size()), data_(&il.begin()) // NOLINT explicit constexpr span(std::initializer_list<value_type> il) noexcept: size_(il.size()), data_(&il.begin()) // NOLINT
{} {}
template<size_type SIZE, typename std::enable_if_t< // template<blt::size_t SIZE, typename std::enable_if_t<
extent == dynamic_extent && SIZE == extent && std::is_const_v<element_type>, bool> = true> // extent == dynamic_extent && SIZE == extent && std::is_const_v<pointer>, bool> = true>
explicit span(std::initializer_list<value_type> il) noexcept: size_(il.size()), data_(&il.begin()) // NOLINT span(std::initializer_list<T> il) noexcept: size_(il.size()), data_(std::data(il)) // NOLINT
{} {}
template<class U, std::size_t N, typename std::enable_if_t< template<class U, std::size_t N, typename std::enable_if_t<
@ -355,20 +358,27 @@ namespace blt
}; };
template<class T, std::size_t N> template<typename T, std::size_t N>
span(T (&)[N]) -> span<T, N>; span(T (&)[N]) -> span<T, N>;
template<class T, std::size_t N> template<typename T, std::size_t N>
span(const T (&)[N]) -> span<T, N>;
template<typename T, std::size_t N>
span(std::array<T, N>&) -> span<T, N>; span(std::array<T, N>&) -> span<T, N>;
template<class T, std::size_t N> template<typename T, std::size_t N>
span(const std::array<T, N>&) -> span<const T, N>; span(const std::array<T, N>&) -> span<const T, N>;
template<class Cont> template<typename Cont>
span(Cont&) -> span<typename Cont::value_type>; span(Cont&) -> span<typename Cont::value_type>;
template<class Cont> template<typename Cont>
span(const Cont&) -> span<const typename Cont::value_type>; span(const Cont&) -> span<const typename Cont::value_type>;
template<typename T>
span(std::initializer_list<T>) -> span<const T>;
} }
#endif //BLT_RANGES_H #endif //BLT_RANGES_H

View File

@ -0,0 +1,31 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_STD_REQUESTS_H
#define BLT_STD_REQUESTS_H
#include <string>
namespace blt::requests
{
std::string send_get_request(const std::string& url);
}
#endif //BLT_STD_REQUESTS_H

View File

@ -252,6 +252,22 @@ namespace blt::string
#endif #endif
} }
inline std::string ensure_ends_with_path_separator(std::string_view string)
{
if (ends_with(string, '/'))
return std::string(string);
else
return std::string(string) += '/';
}
inline std::string ensure_ends_with_path_separator(std::string&& string)
{
if (ends_with(string, '/'))
return string;
else
return (std::move(string) + '/');
}
class match class match
{ {
private: private:

View File

@ -146,7 +146,7 @@ namespace blt::system
std::uint64_t dt; std::uint64_t dt;
}; };
#ifdef _MSC_VER #if defined(_MSC_VER) || defined(WIN32)
using suseconds_t = std::size_t; using suseconds_t = std::size_t;
#endif #endif

View File

@ -31,31 +31,31 @@
#include <mutex> #include <mutex>
#include <chrono> #include <chrono>
#include <optional> #include <optional>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <condition_variable> #include <condition_variable>
namespace blt namespace blt
{ {
class barrier class barrier_t
{ {
public: public:
explicit barrier(blt::size_t threads, std::optional<std::reference_wrapper<std::atomic_bool>> exit_cond = {}): explicit barrier_t(blt::size_t threads, std::optional<std::reference_wrapper<std::atomic_bool>> exit_cond = {}):
thread_count(threads), threads_waiting(0), use_count(0), exit_cond(exit_cond), count_mutex(), cv() thread_count(threads), threads_waiting(0), use_count(0), exit_cond(exit_cond), count_mutex(), cv()
{ {
if (threads == 0) if (threads == 0)
throw std::runtime_error("Barrier thread count cannot be 0"); throw std::runtime_error("Barrier thread count cannot be 0");
} }
barrier(const barrier& copy) = delete; barrier_t(const barrier_t& copy) = delete;
barrier(barrier&& move) = delete; barrier_t(barrier_t&& move) = delete;
barrier& operator=(const barrier& copy) = delete; barrier_t& operator=(const barrier_t& copy) = delete;
barrier& operator=(barrier&& move) = delete; barrier_t& operator=(barrier_t&& move) = delete;
~barrier() = default; ~barrier_t() = default;
void wait() void wait()
{ {
@ -66,7 +66,7 @@ namespace blt
if (++threads_waiting == thread_count) if (++threads_waiting == thread_count)
{ {
threads_waiting = 0; threads_waiting = 0;
use_count++; ++use_count;
cv.notify_all(); cv.notify_all();
} else } else
{ {
@ -101,7 +101,7 @@ namespace blt
// improves performance by not blocking the thread for n iterations of the loop. // improves performance by not blocking the thread for n iterations of the loop.
// If the condition is not met by the end of this loop we can block the thread. // If the condition is not met by the end of this loop we can block the thread.
static constexpr blt::size_t BUSY_LOOP_WAIT = 200; static constexpr size_t BUSY_LOOP_WAIT = 200;
}; };
/** /**

View File

@ -21,6 +21,7 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <blt/std/defines.h>
#ifndef NO_BLT_NAMESPACE_ON_TYPES #ifndef NO_BLT_NAMESPACE_ON_TYPES
namespace blt namespace blt

View File

@ -19,9 +19,6 @@
#ifndef BLT_UTILITY_H #ifndef BLT_UTILITY_H
#define BLT_UTILITY_H #define BLT_UTILITY_H
#include <blt/compatibility.h>
#include <blt/std/ranges.h>
#include <blt/std/expected.h>
#include <string> #include <string>
#include <utility> #include <utility>
@ -59,42 +56,17 @@ namespace blt
namespace blt namespace blt
{ {
template<typename T> template<typename T>
static BLT_CPP20_CONSTEXPR inline std::string type_string() static std::string type_string()
{ {
return demangle(typeid(T).name()); return demangle(typeid(T).name());
} }
template<typename T> template<typename T>
static BLT_CPP20_CONSTEXPR inline std::string type_string_raw() static std::string type_string_raw()
{ {
return typeid(T).name(); return typeid(T).name();
} }
//#define BLT_LAMBDA(type, var, code) [](const type& var) -> auto { return code; }
//#define BLT_LAMBDA(var, code) [](var) -> auto { return code; }
/*
* std::visit(blt::lambda_visitor{
* lambdas...
* }, data_variant);
*/
// TODO: WTF
template<class... TLambdas>
struct lambda_visitor : TLambdas ...
{
using TLambdas::operator()...;
};
#if __cplusplus < 202002L
// explicit deduction guide (not needed as of C++20)
template<class... TLambdas>
lambda_visitor(TLambdas...) -> lambda_visitor<TLambdas...>;
#endif
#if defined(__GNUC__) || defined(__llvm__) #if defined(__GNUC__) || defined(__llvm__)
#define BLT_UNREACHABLE __builtin_unreachable() #define BLT_UNREACHABLE __builtin_unreachable()
#define BLT_ATTRIB_NO_INLINE __attribute__ ((noinline)) #define BLT_ATTRIB_NO_INLINE __attribute__ ((noinline))
@ -137,6 +109,15 @@ namespace blt
(void) hell; (void) hell;
} }
template<typename T>
BLT_ATTRIB_NO_INLINE T& black_box_ret(T& val)
{
static volatile void* hell;
hell = (void*) &val;
(void) hell;
return val;
}
template<typename T> template<typename T>
BLT_ATTRIB_NO_INLINE const T& black_box_ret(const T& val) BLT_ATTRIB_NO_INLINE const T& black_box_ret(const T& val)
{ {

459
include/blt/std/variant.h Normal file
View File

@ -0,0 +1,459 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_STD_VARIANT_H
#define BLT_STD_VARIANT_H
#include <functional>
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>
#include <blt/meta/function.h>
#include <blt/meta/passthrough.h>
#include <blt/meta/tuple_filters.h>
#include <blt/std/types.h>
namespace blt
{
// TODO rewrite all of the metaprogramming related to this + the meta programming lib
template <typename... Types>
class variant_t;
namespace detail
{
template <typename Type, typename Func, typename... Args>
struct member_func_meta
{
using can_invoke = std::is_invocable<Func, Type, Args...>;
using return_type = typename std::conditional_t<can_invoke::value, std::invoke_result<Func, Type, Args...>, meta::passthrough<void>>::type
;
};
template <typename Func, typename ArgsTuple, typename... Types>
struct member_call_return_type;
template <typename Func, typename... Args, typename... Types>
struct member_call_return_type<Func, std::tuple<Args...>, Types...>
{
using result_types = std::tuple<member_func_meta<Types, Func, Args...>...>;
using non_void_result_types = meta::filter_void_t<typename member_func_meta<Types, Func, Args...>::return_type...>;
static constexpr bool all_void = std::tuple_size_v<non_void_result_types> == 0;
static constexpr bool some_void = std::tuple_size_v<non_void_result_types> != sizeof...(Types);
using first_return = std::conditional_t<all_void, void, std::tuple_element_t<0, non_void_result_types>>;
using return_type = std::conditional_t<all_void, void, std::conditional_t<some_void, std::optional<first_return>, first_return>>;
};
template <typename Func, typename... Types>
struct visit_func_meta
{
using result_tuple = std::tuple<typename std::conditional_t<
std::is_invocable_v<Func, Types>, std::invoke_result<Func, Types>, meta::passthrough<void>>::type...>;
using non_void_results = meta::filter_void_t<result_tuple>;
using return_type = std::tuple_element_t<
0, std::conditional_t<std::tuple_size_v<non_void_results> == 0, std::tuple<void>, non_void_results>>;
};
template <typename FuncTuple, typename TypesTuple>
struct visit_return_type;
template <typename... Funcs, typename... Types>
struct visit_return_type<std::tuple<Funcs...>, std::tuple<Types...>>
{
using return_tuple = std::tuple<typename visit_func_meta<Funcs, Types...>::return_type...>;
using non_void_returns = meta::unique_tuple_t<meta::filter_void_t<return_tuple>>;
using first_return = std::tuple_element_t<
0, std::conditional_t<std::tuple_size_v<non_void_returns> == 0, std::tuple<void>, non_void_returns>>;
static constexpr bool all_void = std::tuple_size_v<non_void_returns> == 0;
static constexpr bool some_void = std::tuple_size_v<non_void_returns> != std::tuple_size_v<return_tuple>;
using same_type = meta::filter_func_t<std::is_same, std::tuple<first_return>, non_void_returns>;
static constexpr bool all_same = std::tuple_size_v<same_type> == std::tuple_size_v<non_void_returns>;
using variant_type = typename meta::apply_tuple<variant_t, non_void_returns>::type;
using base_type = std::conditional_t<all_same, first_return, variant_type>;
using return_type = std::conditional_t<all_void, void, std::conditional_t<some_void, std::optional<base_type>, base_type>>;
};
}
/*
* std::visit(blt::lambda_visitor{
* lambdas...
* }, data_variant);
*/
template <typename... TLambdas>
struct lambda_visitor : TLambdas...
{
using TLambdas::operator()...;
};
#if __cplusplus < 202002L
// explicit deduction guide (not needed as of C++20)
template <typename... TLambdas>
lambda_visitor(TLambdas...) -> lambda_visitor<TLambdas...>;
#endif
template <typename... Types>
class variant_t
{
public:
using value_type = std::variant<Types...>;
size_t variant_size = sizeof...(Types);
constexpr variant_t(): m_variant()
{}
constexpr variant_t(const variant_t& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant.m_variant)
{}
constexpr variant_t(variant_t&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant.m_variant))
{}
explicit constexpr variant_t(const value_type& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant)
{}
explicit constexpr variant_t(value_type&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant))
{}
explicit constexpr variant_t(Types&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Types...>): m_variant(
std::forward<Types>(args)...)
{}
template <typename T, typename... C_Args>
explicit constexpr variant_t(std::in_place_type_t<T>, C_Args&&... args): m_variant(std::in_place_type<T>, std::forward<C_Args>(args)...)
{}
template <typename T, typename U, typename... C_Args>
constexpr explicit variant_t(std::in_place_type_t<T>, std::initializer_list<U> il, C_Args&&... args): m_variant(
std::in_place_type<T>, il, std::forward<C_Args>(args)...)
{}
template <size_t I, typename... C_Args>
explicit constexpr variant_t(std::in_place_index_t<I>, C_Args&&... args): m_variant(std::in_place_index<I>, std::forward<C_Args>(args)...)
{}
template <std::size_t I, typename U, typename... C_Args>
constexpr explicit variant_t(std::in_place_index_t<I>, std::initializer_list<U> il, C_Args&&... args): m_variant(
std::in_place_index<I>, il, std::forward<C_Args>(args)...)
{}
template <typename T, typename... Args>
T& emplace(Args&&... args)
{
return m_variant.template emplace<T>(std::forward<Args>(args)...);
}
template <typename T, typename U, typename... Args>
T& emplace(std::initializer_list<U> il, Args&&... args)
{
return m_variant.template emplace<T>(il, std::forward<Args>(args)...);
}
template <std::size_t I, typename... Args>
std::variant_alternative_t<I, value_type>& emplace(Args&&... args)
{
return m_variant.template emplace<I>(std::forward<Args>(args)...);
}
template <std::size_t I, typename U, typename... Args>
std::variant_alternative_t<I, value_type>& emplace(std::initializer_list<U> il, Args&&... args)
{
return m_variant.template emplace<I>(il, std::forward<Args>(args)...);
}
[[nodiscard]] constexpr std::size_t index() const noexcept
{
return m_variant.index();
}
[[nodiscard]] constexpr bool valueless_by_exception() const noexcept
{
return m_variant.valueless_by_exception();
}
template <typename... Visitee>
static constexpr auto make_visitor(Visitee&&... visitees)
{
// TODO: this is probably not the best way to handle these cases...
using meta_t = detail::visit_return_type<std::tuple<Visitee...>, std::tuple<Types...>>;
if constexpr (meta_t::all_same)
{
if constexpr (meta_t::some_void)
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value)));
}
}...
};
} else
{
return lambda_visitor{std::forward<Visitee>(visitees)...};
}
} else
{
if constexpr (meta_t::some_void)
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(
typename meta_t::base_type(std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))));
}
}...
};
} else
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
return typename meta_t::return_type{std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))};
}...
};
}
}
}
/**
* Automatic visitor generation
* @param visitees user lambdas
*/
template <typename... Visitee>
constexpr auto visit(Visitee&&... visitees) -> decltype(auto)
{
return std::visit(make_visitor(std::forward<Visitee>(visitees)...), m_variant);
}
/**
* Automatic visitor generation
* @param visitees user lambdas
*/
template <typename... Visitee>
constexpr auto visit(Visitee&&... visitees) const -> decltype(auto)
{
return std::visit(make_visitor(std::forward<Visitee>(visitees)...), m_variant);
}
template <typename Default, typename... Visitee>
constexpr auto visit_value(Default&& default_value, Visitee&&... visitees) -> decltype(auto)
{
return visit(std::forward<Visitee>(visitees)..., [default_value=std::forward<Default>(default_value)](auto&&) {
return std::forward<Default>(default_value);
});
}
template <typename MemberFunc, typename... Args>
constexpr auto call_member(const MemberFunc func, Args&&... args) -> decltype(auto)
{
using meta = detail::member_call_return_type<MemberFunc, std::tuple<Args...>, Types...>;
return visit([&](auto&& value) -> typename meta::return_type {
if constexpr (std::is_invocable_v<MemberFunc, decltype(value), Args...>)
return ((value).*(func))(std::forward<Args>(args)...);
else
return {};
});
}
template <size_t I>
[[nodiscard]] constexpr bool has_index() const noexcept
{
return m_variant.index() == I;
}
template <typename T>
[[nodiscard]] constexpr bool has_type() const noexcept
{
return std::holds_alternative<T>(m_variant);
}
template <typename T>
[[nodiscard]] constexpr auto get() -> decltype(auto)
{
return std::get<T>(m_variant);
}
template <typename T>
[[nodiscard]] constexpr auto get() const -> decltype(auto)
{
return std::get<T>(m_variant);
}
template <size_t I>
[[nodiscard]] constexpr auto get() -> decltype(auto)
{
return std::get<I>(m_variant);
}
template <size_t I>
[[nodiscard]] constexpr auto get() const -> decltype(auto)
{
return std::get<I>(m_variant);
}
template <size_t I>
constexpr std::add_pointer_t<std::variant_alternative_t<I, value_type>> get_if() noexcept
{
return std::get_if<I>(m_variant);
}
template <size_t I>
constexpr std::add_pointer_t<const std::variant_alternative_t<I, value_type>> get_if() noexcept
{
return std::get_if<I>(m_variant);
}
template <typename T>
constexpr std::add_pointer_t<T> get_if() noexcept
{
return std::get_if<T>(m_variant);
}
template <typename T>
constexpr std::add_pointer_t<const T> get_if() noexcept
{
return std::get_if<T>(m_variant);
}
template <typename T>
constexpr T value_or(T&& t) const
{
if (has_type<T>())
return get<T>();
return std::forward<T>(t);
}
template <size_t I>
constexpr std::variant_alternative_t<I, value_type> value_or(const std::variant_alternative_t<I, value_type>& t) const
{
if (has_type<std::variant_alternative_t<I, value_type>>())
return get<I>();
return t;
}
template <size_t I>
constexpr std::variant_alternative_t<I, value_type> value_or(std::variant_alternative_t<I, value_type>&& t) const
{
if (has_type<std::variant_alternative_t<I, value_type>>())
return get<I>();
return t;
}
template <size_t>
constexpr const value_type& variant() const
{
return m_variant;
}
constexpr value_type& variant()
{
return m_variant;
}
[[nodiscard]] constexpr size_t size() const
{
return variant_size;
}
friend bool operator==(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant == rhs.m_variant;
}
friend bool operator!=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant != rhs.m_variant;
}
friend bool operator<(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant<rhs.m_variant;
}
friend bool operator>(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant > rhs.m_variant;
}
friend bool operator<=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant <= rhs.m_variant;
}
friend bool operator>=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant >= rhs.m_variant;
}
private:
value_type m_variant;
};
namespace detail
{
template <typename>
class variant_is_base_of
{};
template <typename... Types>
class variant_is_base_of<variant_t<Types...>>
{
public:
using value_type = bool;
template <typename T>
static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
};
template <typename... Types>
class variant_is_base_of<std::variant<Types...>>
{
public:
using value_type = bool;
template <typename T>
static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
};
template <typename T>
static constexpr bool variant_is_base_of_v = variant_is_base_of<T>::value;
}
}
#endif //BLT_STD_VARIANT_H

View File

@ -1,71 +0,0 @@
/*
* Created by Brett on 16/01/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef BLT_WINDOW_H
#define BLT_WINDOW_H
#include <functional>
#include <vector>
#ifndef BLT_MAP_FUNC
#include <unordered_map>
#define BLT_MAP_FUNC std::unordered_map
#endif
#define KEY_MAP BLT_MAP_FUNC<int, bool>
namespace blt {
class window {
protected:
bool m_windowOpen = true;
int m_width = 800, m_height = 600;
std::vector<std::function<void(window*)>> renderFunctions{};
std::vector<std::function<void(window*, int, bool)>> keyListeners{};
std::vector<std::function<void(window*, int, bool)>> mouseListeners{};
KEY_MAP keysDown{};
KEY_MAP mouseDown{};
public:
window() = default;
window(int width_, int height_) {
m_width = width_;
m_height = height_;
}
virtual void createWindow() = 0;
virtual void startMainLoop() = 0;
virtual void destroyWindow() = 0;
virtual ~window() = 0;
virtual inline bool setResizeable(bool resizeEnabled) = 0;
virtual inline bool setWindowSize(int width_, int height_) = 0;
[[nodiscard]] inline int getWidth() const {return m_width;};
[[nodiscard]] inline int getHeight() const {return m_height;};
[[nodiscard]] virtual inline bool isWindowOpen() const {return m_windowOpen;};
virtual inline void closeWindow(){
m_windowOpen = false;
}
virtual inline void registerLoopFunction(const std::function<void(window*)>& func) {
renderFunctions.push_back(func);
}
virtual inline bool isKeyDown(int key) const { return keysDown.at(key); }
virtual inline bool isMouseDown(int button) const {return mouseDown.at(button);};
// Function signature is window pointer to this, key press, pressed/released (true/false)
virtual inline void registerKeyListener(const std::function<void(window*, int, bool)>& listener) {
keyListeners.push_back(listener);
}
// Function signature is window pointer to this, mouse button press, pressed/released (true/false)
virtual inline void registerMouseListener(const std::function<void(window*, int, bool)>& listener) {
mouseListeners.push_back(listener);
}
};
}
#endif

@ -1 +1 @@
Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5 Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3

0
py_commit_helper.sh Executable file → Normal file
View File

View File

@ -6,12 +6,13 @@
#include <blt/format/format.h> #include <blt/format/format.h>
#include <blt/std/string.h> #include <blt/std/string.h>
#include <cmath> #include <cmath>
#include "blt/std/logging.h" #include "blt/logging/logging.h"
#include "blt/std/assert.h" #include "blt/std/assert.h"
#include "blt/std/utility.h" #include "blt/std/utility.h"
#include <stack> #include <stack>
#include <queue> #include <queue>
#include <algorithm> #include <algorithm>
#include <blt/std/variant.h>
inline constexpr char SEPARATOR = '-'; inline constexpr char SEPARATOR = '-';
inline constexpr char CONNECTOR = '+'; inline constexpr char CONNECTOR = '+';

View File

@ -0,0 +1,42 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <blt/fs/cstdio_wrappers.h>
namespace blt::fs
{
i64 file_reader_t::read(char* buffer, const size_t bytes)
{
return fread(buffer, bytes, 1, static_cast<FILE*>(m_file));
}
i64 file_writer_t::write(const char* buffer, const size_t bytes)
{
return fwrite(buffer, bytes, 1, static_cast<FILE*>(m_file));
}
i64 file_writer_t::tell()
{
return ftell(static_cast<FILE*>(m_file));
}
void file_writer_t::seek(const i64 offset, const seek_origin origin)
{
fseek(static_cast<FILE*>(m_file), offset, static_cast<int>(origin));
}
}

158
src/blt/fs/file_writers.cpp Normal file
View File

@ -0,0 +1,158 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <cstring>
#include <ctime>
#include <stdexcept>
#include <utility>
#include <blt/fs/file_writers.h>
namespace blt::fs
{
i64 fwriter_t::write(const char* buffer, const size_t bytes)
{
return static_cast<i64>(std::fwrite(buffer, 1, bytes, m_file));
}
void fwriter_t::flush()
{
writer_t::flush();
std::fflush(m_file);
}
void fwriter_t::newfile(const std::string& new_name)
{
if (m_file != nullptr)
std::fclose(m_file);
m_file = std::fopen(new_name.c_str(), m_mode.c_str());
if (!m_file)
throw std::runtime_error("Failed to open file for writing");
}
buffered_writer::buffered_writer(const std::string& name, const size_t buffer_size): fwriter_t{name, "ab"}
{
m_buffer.resize(buffer_size);
}
buffered_writer::buffered_writer(const size_t buffer_size)
{
m_buffer.resize(buffer_size);
}
void buffered_writer::newfile(const std::string& new_name)
{
fwriter_t::newfile(new_name);
setvbuf(this->m_file, nullptr, _IONBF, 0);
}
i64 buffered_writer::write(const char* buffer, const size_t bytes)
{
if (bytes > m_buffer.size())
return fwriter_t::write(buffer, bytes);
if (bytes + m_current_pos > m_buffer.size())
flush();
std::memcpy(m_buffer.data() + m_current_pos, buffer, bytes);
m_current_pos += bytes;
return static_cast<i64>(bytes);
}
void buffered_writer::flush()
{
fwriter_t::write(m_buffer.data(), m_current_pos);
fwriter_t::flush();
m_current_pos = 0;
}
bounded_writer::bounded_writer(fwriter_t& writer, std::optional<std::string> base_name, const size_t max_size, naming_function_t naming_function):
m_writer(&writer), m_base_name(std::move(base_name)), m_max_size(max_size), m_naming_function(std::move(naming_function))
{
bounded_writer::newfile(m_base_name.value_or(""));
}
i64 bounded_writer::write(const char* buffer, const size_t bytes)
{
m_currently_written += bytes;
if (m_currently_written > m_max_size)
this->newfile(m_base_name.value_or(""));
return m_writer->write(buffer, bytes);
}
void bounded_writer::newfile(const std::string& new_name)
{
++m_current_invocation;
m_currently_written = 0;
m_writer->newfile(m_naming_function(m_current_invocation, new_name));
}
void bounded_writer::flush()
{
m_writer->flush();
}
rotating_writer::rotating_writer(fwriter_t& writer, const time_t period): m_writer{&writer}, m_period{period}
{
newfile();
}
i64 rotating_writer::write(const char* buffer, const size_t bytes)
{
check_for_time();
return m_writer->write(buffer, bytes);
}
void rotating_writer::flush()
{
check_for_time();
m_writer->flush();
}
void rotating_writer::newfile(const std::string& new_name)
{
m_writer->newfile(new_name);
}
void rotating_writer::newfile()
{
m_last_time = get_current_time();
std::string name;
name += std::to_string(m_last_time.year);
name += "-" + std::to_string(m_last_time.month);
name += "-" + std::to_string(m_last_time.day);
if (m_period.hour >= 0)
name += "-" + std::to_string(m_last_time.hour);
name += ".txt";
newfile(name);
}
void rotating_writer::check_for_time()
{
const auto current_time = get_current_time();
if ((m_period.hour > 0 && current_time.hour > m_last_time.hour + m_period.hour) ||
(m_period.day > 0 && current_time.day > m_last_time.day + m_period.day) ||
(m_period.month > 0 && current_time.month > m_last_time.month + m_period.month) ||
(m_period.year > 0 && current_time.year > m_last_time.year + m_period.year))
newfile();
}
time_t rotating_writer::get_current_time()
{
const std::time_t time = std::time(nullptr);
const auto current_time = std::localtime(&time);
return {current_time->tm_year + 1900, current_time->tm_mon + 1, current_time->tm_mday, current_time->tm_hour};
}
}

View File

@ -17,63 +17,4 @@
*/ */
#include <blt/fs/filesystem.h> #include <blt/fs/filesystem.h>
#include <cstring> #include <cstring>
#include <blt/std/logging.h>
namespace blt::fs
{
fstream_block_reader::fstream_block_reader(std::fstream& stream, size_t bufferSize):
block_reader(bufferSize), m_stream(stream), m_buffer(new char[bufferSize])
{
if (!m_stream.good() || m_stream.fail())
BLT_WARN("Provided std::fstream is not good! Clearing!");
m_stream.clear();
}
int fstream_block_reader::read(char* buffer, size_t bytes)
{
if (readIndex == 0)
m_stream.read(m_buffer, (long) m_bufferSize);
if (readIndex + bytes >= m_bufferSize)
{
// copy out all the data from the current buffer
auto bytesLeft = m_bufferSize - readIndex;
memcpy(buffer, m_buffer + readIndex, bytesLeft);
readIndex = 0;
// now to prevent large scale reading in small blocks, we should just read the entire thing into the buffer.
m_stream.read(buffer + bytesLeft, (long) (bytes - bytesLeft));
} else
{
// but in the case that the size of the data read is small, we should read in blocks and copy from that buffer
// that should be quicker since file operations are slow.
std::memcpy(buffer, m_buffer + readIndex, bytes);
readIndex += bytes;
}
return 0;
}
int fstream_block_writer::write(char* buffer, size_t bytes)
{
if (writeIndex + bytes >= m_bufferSize)
{
// in an attempt to stay efficient we write out the old buffer and the new buffer
// since there is a good chance there is more than a buffer's worth of data being written
// otherwise the buffer is almost full and can be written anyway. (this might be bad for performance especially if the FS wants round numbers)
m_stream.write(m_buffer, (long) writeIndex);
writeIndex = 0;
m_stream.write(buffer, (long) bytes);
} else
{
std::memcpy(m_buffer + writeIndex, buffer, bytes);
writeIndex += bytes;
}
return 0;
}
void fstream_block_writer::flush_internal()
{
m_stream.write(m_buffer, (long) writeIndex);
writeIndex = 0;
}
}

View File

@ -82,8 +82,8 @@ std::string blt::fs::getFile(std::string_view path)
file_contents = file_stream.str(); file_contents = file_stream.str();
} catch (std::ifstream::failure& e) } catch (std::ifstream::failure& e)
{ {
BLT_WARN("Unable to read file '%s'!\n", std::string(path).c_str()); BLT_WARN("Unable to read file '{}'!\n", std::string(path).c_str());
BLT_WARN("Exception: %s", e.what()); BLT_WARN("Exception: {}", e.what());
BLT_THROW(std::runtime_error("Failed to read file!\n")); BLT_THROW(std::runtime_error("Failed to read file!\n"));
} }
return file_contents; return file_contents;

View File

@ -4,19 +4,19 @@
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
#include <blt/fs/nbt.h> #include <blt/fs/nbt.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <cassert> #include <cassert>
#include <type_traits> #include <type_traits>
namespace blt::nbt { namespace blt::nbt {
void writeUTF8String(blt::fs::block_writer& stream, const std::string& str) { void writeUTF8String(blt::fs::writer_t& stream, const std::string& str) {
blt::string::utf8_string str8 = blt::string::createUTFString(str); blt::string::utf8_string str8 = blt::string::createUTFString(str);
stream.write(str8.characters, str8.size); stream.write(str8.characters, str8.size);
delete[] str8.characters; delete[] str8.characters;
} }
std::string readUTF8String(blt::fs::block_reader& stream) { std::string readUTF8String(blt::fs::reader_t& stream) {
int16_t utflen; int16_t utflen;
readData(stream, utflen); readData(stream, utflen);
@ -33,9 +33,10 @@ namespace blt::nbt {
} }
void NBTReader::read() { void NBTReader::read() {
char t = reader.get(); char t;
reader.read(&t, 1);
if (t != (char)nbt_tag::COMPOUND) { if (t != (char)nbt_tag::COMPOUND) {
BLT_WARN("Found %d", t); BLT_WARN("Found {:d}", t);
throw std::runtime_error("Incorrectly formatted NBT data! Root tag must be a compound tag!"); throw std::runtime_error("Incorrectly formatted NBT data! Root tag must be a compound tag!");
} }
root = new tag_compound; root = new tag_compound;

View File

@ -0,0 +1,67 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/fs/path_helper.h>
#include <blt/std/string.h>
#include <blt/compatibility.h>
namespace blt::fs
{
#ifdef BLT_WINDOWS
constexpr static char delim = '\\';
#else
constexpr static char delim = '/';
#endif
std::string base_name(const std::string& str)
{
return std::string(base_name_sv(str));
}
std::string_view base_name_sv(const std::string_view str)
{
const auto parts = string::split_sv(str, delim);
const auto file_parts = string::split_sv(parts.back(), '.');
return file_parts.front();
}
std::string filename(const std::string& str)
{
return std::string(filename_sv(str));
}
std::string_view filename_sv(const std::string_view str)
{
const auto parts = string::split_sv(str, delim);
return parts.back();
}
std::string extension(const std::string& str)
{
return std::string(extension_sv(str));
}
std::string_view extension_sv(const std::string_view str)
{
const auto parts = string::split_sv(str, delim);
const auto file_parts = parts.back().find_first_of('.');
return parts.back().substr(std::min(file_parts + 1, parts.back().size()));
}
}

View File

@ -0,0 +1,67 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <istream>
#include <ostream>
#include <blt/fs/stream_wrappers.h>
namespace blt::fs
{
fstream_reader_t::fstream_reader_t(std::istream& stream): m_stream{&stream}
{}
i64 fstream_reader_t::read(char* buffer, const size_t bytes)
{
m_stream->read(buffer, static_cast<std::streamsize>(bytes));
return m_stream->gcount();
}
fstream_writer_t::fstream_writer_t(std::ostream& stream): m_stream{&stream}
{}
i64 fstream_writer_t::write(const char* buffer, const size_t bytes)
{
m_stream->write(buffer, static_cast<std::streamsize>(bytes));
return static_cast<i64>(bytes);
}
void fstream_writer_t::flush()
{
m_stream->flush();
}
i64 fstream_writer_t::tell()
{
return m_stream->tellp();
}
void fstream_writer_t::seek(const i64 offset, const seek_origin origin)
{
switch (origin)
{
case seek_origin::seek_cur:
m_stream->seekp(offset, std::ios_base::cur);
break;
case seek_origin::seek_end:
m_stream->seekp(offset, std::ios_base::end);
break;
case seek_origin::seek_set:
m_stream->seekp(offset, std::ios_base::beg);
break;
}
}
}

View File

@ -0,0 +1,36 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/fs/threaded_writers.h>
namespace blt::fs
{
concurrent_file_writer::concurrent_file_writer(writer_t* writer): m_writer{writer}
{}
i64 concurrent_file_writer::write(const char* buffer, const size_t bytes)
{
std::scoped_lock lock{m_mutex};
return m_writer->write(buffer, bytes);
}
void concurrent_file_writer::flush()
{
std::scoped_lock lock{m_mutex};
m_writer->flush();
}
}

18
src/blt/logging/ansi.cpp Normal file
View File

@ -0,0 +1,18 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/logging/ansi.h>

View File

@ -0,0 +1,593 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <blt/logging/fmt_tokenizer.h>
namespace blt::logging
{
fmt_token_type fmt_tokenizer_t::get_type(const char c)
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return fmt_token_type::NUMBER;
case '+':
return fmt_token_type::PLUS;
case '-':
return fmt_token_type::MINUS;
case '.':
return fmt_token_type::DOT;
case ':':
return fmt_token_type::COLON;
case ' ':
return fmt_token_type::SPACE;
case '#':
return fmt_token_type::POUND;
case '<':
return fmt_token_type::LEFT_CHEVRON;
case '>':
return fmt_token_type::RIGHT_CHEVRON;
case '^':
return fmt_token_type::CARET;
case '{':
return fmt_token_type::OPEN_BRACKET;
case '}':
return fmt_token_type::CLOSE_BRACKET;
default:
return fmt_token_type::STRING;
}
}
std::optional<fmt_token_t> fmt_tokenizer_t::next()
{
if (m_pos >= m_fmt.size())
return {};
bool is_escaped = false;
if (m_fmt[m_pos] == '\\')
{
is_escaped = true;
++m_pos;
}
switch (const auto base_type = get_type(m_fmt[m_pos]))
{
case fmt_token_type::SPACE:
case fmt_token_type::PLUS:
case fmt_token_type::MINUS:
case fmt_token_type::DOT:
case fmt_token_type::COLON:
case fmt_token_type::POUND:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CARET:
case fmt_token_type::OPEN_BRACKET:
case fmt_token_type::CLOSE_BRACKET:
if (is_escaped)
return fmt_token_t{fmt_token_type::STRING, std::string_view{m_fmt.data() + m_pos++, 1}};
return fmt_token_t{base_type, std::string_view{m_fmt.data() + m_pos++, 1}};
default:
{
const auto begin = m_pos;
for (; m_pos < m_fmt.size() && get_type(m_fmt[m_pos]) == base_type; ++m_pos)
{
}
return fmt_token_t{base_type, std::string_view{m_fmt.data() + begin, m_pos - begin}};
}
}
}
std::vector<fmt_token_t> fmt_tokenizer_t::tokenize(const std::string_view fmt)
{
m_fmt = fmt;
m_pos = 0;
std::vector<fmt_token_t> tokens;
while (auto token = next())
tokens.push_back(*token);
return tokens;
}
const fmt_spec_t& fmt_parser_t::parse(const std::string_view fmt)
{
m_spec = {};
m_pos = 0;
m_tokens = m_tokenizer.tokenize(fmt);
parse_fmt_field();
return m_spec;
}
bool fmt_parser_t::is_align_t(const fmt_token_type type)
{
switch (type)
{
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CARET:
return true;
default:
return false;
}
}
void fmt_parser_t::parse_fmt_field()
{
if (!has_next())
throw std::runtime_error("Expected token when parsing format field");
const auto [type, value] = peek();
switch (type)
{
case fmt_token_type::NUMBER:
parse_arg_id();
if (has_next())
{
if (peek().type == fmt_token_type::COLON)
parse_fmt_spec();
else
throw std::runtime_error("Expected ':' when parsing format field after arg id!");
}
break;
case fmt_token_type::COLON:
parse_fmt_spec();
break;
default:
{
std::stringstream ss;
ss << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field";
throw std::runtime_error(ss.str());
}
}
if (has_next())
parse_type();
}
void fmt_parser_t::parse_arg_id()
{
const auto [type, value] = next();
if (type != fmt_token_type::NUMBER)
{
std::stringstream ss;
ss << "Expected number when parsing arg id, unexpected value '" << value << '\'';
throw std::runtime_error(ss.str());
}
m_spec.arg_id = std::stoll(std::string(value));
}
std::string fmt_parser_t::parse_arg_or_number()
{
auto [type, value] = next();
if (type == fmt_token_type::NUMBER)
return std::string(value);
if (type == fmt_token_type::OPEN_BRACKET)
{
auto [next_type, next_value] = next();
if (next_type != fmt_token_type::NUMBER)
throw std::runtime_error("Expected number when parsing arg or number, unexpected value '" + std::string(next_value) + '\'');
if (next().type != fmt_token_type::CLOSE_BRACKET)
throw std::runtime_error("Expected closing bracket when parsing arg or number, unexpected value '" + std::string(next_value) + '\'');
// TODO: this feels like an evil hack.
const auto arg_id = std::stoul(std::string(next_value));
if (arg_id >= m_handlers.size())
throw std::runtime_error("Invalid arg id " + std::to_string(arg_id) + ", max arg supported: " + std::to_string(m_handlers.size()));
std::stringstream ss;
m_handlers[arg_id](ss, fmt_spec_t{});
return ss.str();
}
throw std::runtime_error("Expected number when parsing arg or number, unexpected value '" + std::string(value) + '\'');
}
void fmt_parser_t::parse_fmt_spec()
{
// consume :
consume();
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
// if there is a token beyond this string, it is not a type string
if (has_next(1))
parse_fmt_spec_fill();
return;
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::CARET:
parse_fmt_spec_align();
break;
case fmt_token_type::COLON:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Begin)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::NUMBER:
case fmt_token_type::OPEN_BRACKET:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
case fmt_token_type::SPACE:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
parse_fmt_spec_sign();
break;
case fmt_token_type::POUND:
parse_fmt_spec_form();
break;
}
}
void fmt_parser_t::parse_fmt_spec_fill()
{
parse_fill();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::CARET:
parse_fmt_spec_align();
break;
case fmt_token_type::NUMBER:
case fmt_token_type::OPEN_BRACKET:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
case fmt_token_type::SPACE:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
parse_fmt_spec_sign();
break;
case fmt_token_type::POUND:
parse_fmt_spec_form();
break;
case fmt_token_type::STRING:
return;
case fmt_token_type::COLON:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Fill)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
}
}
void fmt_parser_t::parse_fmt_spec_align()
{
parse_align();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
return;
case fmt_token_type::NUMBER:
case fmt_token_type::OPEN_BRACKET:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
case fmt_token_type::SPACE:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
parse_fmt_spec_sign();
break;
case fmt_token_type::POUND:
parse_fmt_spec_form();
break;
case fmt_token_type::CARET:
case fmt_token_type::COLON:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Align)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
}
}
// handle start of fmt, with sign
void fmt_parser_t::parse_fmt_spec_sign()
{
parse_sign();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
return;
case fmt_token_type::SPACE:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
case fmt_token_type::COLON:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Sign)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::NUMBER:
case fmt_token_type::OPEN_BRACKET:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
case fmt_token_type::POUND:
parse_fmt_spec_form();
break;
}
}
void fmt_parser_t::parse_fmt_spec_form()
{
parse_form();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
return;
case fmt_token_type::SPACE:
case fmt_token_type::COLON:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
case fmt_token_type::POUND:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Form)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::NUMBER:
case fmt_token_type::OPEN_BRACKET:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
}
}
// handle width parsing
void fmt_parser_t::parse_fmt_spec_width()
{
parse_width();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
return;
case fmt_token_type::COLON:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
case fmt_token_type::SPACE:
case fmt_token_type::POUND:
case fmt_token_type::NUMBER:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::OPEN_BRACKET:
case fmt_token_type::CLOSE_BRACKET:
{
std::stringstream ss;
ss << "(Stage (Width)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
}
}
void fmt_parser_t::parse_fmt_spec_precision()
{
// consume .
consume();
parse_precision();
}
void fmt_parser_t::parse_fill()
{
auto [type, value] = next();
if (type != fmt_token_type::STRING)
{
std::stringstream ss;
ss << "Expected string when parsing fill, got " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
m_spec.prefix_char = value.front();
}
void fmt_parser_t::parse_align()
{
auto [type, value] = next();
switch (type)
{
case fmt_token_type::LEFT_CHEVRON:
m_spec.alignment = fmt_align_t::LEFT;
break;
case fmt_token_type::RIGHT_CHEVRON:
m_spec.alignment = fmt_align_t::RIGHT;
break;
case fmt_token_type::CARET:
m_spec.alignment = fmt_align_t::CENTER;
break;
default:
{
std::stringstream ss;
ss << "Invalid align type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
}
}
void fmt_parser_t::parse_sign()
{
auto [_, value] = next();
if (value.size() > 1)
{
std::stringstream ss;
ss << "Sign contains more than one character, we are not sure how to interpret this. Value '" << value << "'";
throw std::runtime_error(ss.str());
}
switch (value[0])
{
case '+':
m_spec.sign = fmt_sign_t::PLUS;
break;
case '-':
m_spec.sign = fmt_sign_t::MINUS;
break;
case ' ':
m_spec.sign = fmt_sign_t::SPACE;
break;
default:
{
std::stringstream ss;
ss << "Invalid sign " << value[0];
throw std::runtime_error(ss.str());
}
}
}
void fmt_parser_t::parse_form()
{
consume();
m_spec.alternate_form = true;
}
void fmt_parser_t::parse_width()
{
const auto value = parse_arg_or_number();
if (value.front() == '0' && !m_spec.prefix_char.has_value())
m_spec.prefix_char = '0';
m_spec.width = std::stoll(value);
}
void fmt_parser_t::parse_precision()
{
if (!has_next())
throw std::runtime_error("Missing token when parsing precision");
auto value = parse_arg_or_number();
m_spec.precision = std::stoll(std::string(value));
}
void fmt_parser_t::parse_type()
{
auto [_, value] = next();
if (value.size() != 1)
{
std::stringstream ss;
ss << "Type contains more than one character, we are not sure how to interpret this value '" << value << "'";
throw std::runtime_error(ss.str());
}
m_spec.uppercase = std::isupper(value.front());
switch (value.front())
{
case 'b':
case 'B':
m_spec.type = fmt_type_t::BINARY;
break;
case 'c':
m_spec.type = fmt_type_t::CHAR;
break;
case 'd':
m_spec.type = fmt_type_t::DECIMAL;
break;
case 'o':
m_spec.type = fmt_type_t::OCTAL;
break;
case 'x':
case 'X':
m_spec.type = fmt_type_t::HEX;
break;
case 'a':
case 'A':
m_spec.type = fmt_type_t::HEX_FLOAT;
break;
case 'e':
case 'E':
m_spec.type = fmt_type_t::EXPONENT;
break;
case 'f':
case 'F':
m_spec.type = fmt_type_t::FIXED_POINT;
break;
case 'g':
case 'G':
m_spec.type = fmt_type_t::GENERAL;
break;
case 't':
case 'T':
m_spec.type = fmt_type_t::TYPE;
break;
default:
std::stringstream ss;
ss << "Invalid type " << value;
ss << std::endl << std::endl;
ss << "Expected one of: " << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "b | B" << std::setw(6) << ' ' << "Print as binary output" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "c" << std::setw(6) << ' ' << "Print as character output" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "d" << std::setw(6) << ' ' << "Print as decimal (base 10) output" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "o" << std::setw(6) << ' ' << "Print as octal (base 8) output" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "x | X" << std::setw(6) << ' ' << "Print as hexadecimal (base 16) output" <<
std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "a | A" << std::setw(6) << ' ' << "Print floats as hexadecimal (base 16) output"
<< std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "e | E" << std::setw(6) << ' ' << "Print floats in scientific notation" <<
std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "f | F" << std::setw(6) << ' ' <<
"Print floats in fixed point output, useful for setting precision of output" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "g | G" << std::setw(6) << ' ' <<
"Print floats in general output, switching between fixed point and scientific notation based on magnitude" << std::endl;
ss << std::setw(4) << ' ' << std::left << std::setw(5) << "t | T" << std::setw(6) << ' ' << "Print the type as a string" << std::endl;
throw std::runtime_error(ss.str());
}
}
}

293
src/blt/logging/logging.cpp Normal file
View File

@ -0,0 +1,293 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
#include <thread>
#include <blt/logging/injector.h>
#include <blt/logging/logging.h>
#include <blt/std/hashmap.h>
#include <blt/std/types.h>
namespace blt::logging
{
struct global_context_t
{
logging_config_t global_config;
};
static global_context_t global_context;
#ifdef BLT_LOGGING_THREAD_SAFE
static std::mutex global_logging_mutex;
#endif
struct logging_thread_context_t
{
std::stringstream stream;
std::stringstream logging_stream;
std::string thread_name;
logger_t logger{stream};
};
logging_thread_context_t& get_thread_context()
{
thread_local logging_thread_context_t context;
return context;
}
std::string logger_t::to_string() const
{
return dynamic_cast<std::stringstream&>(m_stream).str();
}
size_t logger_t::find_ending_brace(size_t begin) const
{
size_t braces = 0;
for (; begin < m_fmt.size(); ++begin)
{
if (m_fmt[begin] == '{')
++braces;
else if (m_fmt[begin] == '}')
--braces;
if (braces == 0)
return begin;
}
return std::string::npos;
}
void logger_t::setup_stream(const fmt_spec_t& spec) const
{
if (spec.prefix_char)
m_stream << std::setfill(*spec.prefix_char);
else
m_stream << std::setfill(' ');
switch (spec.alignment)
{
case fmt_align_t::LEFT:
m_stream << std::left;
break;
case fmt_align_t::CENTER:
// TODO?
break;
case fmt_align_t::RIGHT:
m_stream << std::right;
break;
}
if (spec.width > 0)
m_stream << std::setw(static_cast<i32>(spec.width));
else
m_stream << std::setw(0);
if (spec.precision > 0)
m_stream << std::setprecision(static_cast<i32>(spec.precision));
else
m_stream << std::setprecision(16);
if (spec.alternate_form)
m_stream << std::showbase;
else
m_stream << std::noshowbase;
if (spec.uppercase)
m_stream << std::uppercase;
else
m_stream << std::nouppercase;
if (spec.sign == fmt_sign_t::PLUS)
m_stream << std::showpos;
else
m_stream << std::noshowpos;
}
std::string logger_t::process_string(const std::string_view str)
{
auto result = std::string(str);
size_t pos = 0;
while (pos = result.find('{', pos), pos != std::string::npos)
{
if (pos > 0 && result[pos - 1] == '\\')
{
auto before = result.substr(0, pos - 1);
auto after = result.substr(pos);
result = before + after;
} else
++pos;
}
return result;
}
void logger_t::process_strings()
{
auto spec_it = m_fmt_specs.begin();
auto str_it = m_string_sections.begin();
for (; spec_it != m_fmt_specs.end(); ++spec_it, ++str_it)
{
m_stream << process_string(*str_it);
auto arg_pos = spec_it->arg_id;
if (arg_pos == -1)
arg_pos = static_cast<i64>(m_arg_pos++);
setup_stream(*spec_it);
m_arg_print_funcs[arg_pos](m_stream, *spec_it);
}
m_stream << process_string(*str_it);
}
void logger_t::handle_type(std::ostream& stream, const fmt_spec_t& spec)
{
switch (spec.type)
{
case fmt_type_t::DECIMAL:
stream << std::noboolalpha;
stream << std::dec;
break;
case fmt_type_t::OCTAL:
stream << std::oct;
break;
case fmt_type_t::HEX:
stream << std::hex;
break;
case fmt_type_t::HEX_FLOAT:
stream << std::hexfloat;
break;
case fmt_type_t::EXPONENT:
stream << std::scientific;
break;
case fmt_type_t::FIXED_POINT:
stream << std::fixed;
break;
case fmt_type_t::GENERAL:
stream << std::defaultfloat;
break;
case fmt_type_t::UNSPECIFIED:
stream << std::boolalpha;
stream << std::dec;
break;
default:
break;
}
}
void logger_t::exponential(std::ostream& stream)
{
stream << std::scientific;
}
void logger_t::fixed(std::ostream& stream)
{
stream << std::fixed;
}
void logger_t::compile(std::string fmt)
{
m_fmt = std::move(fmt);
m_last_fmt_pos = 0;
m_arg_pos = 0;
auto& ss = dynamic_cast<std::stringstream&>(m_stream);
ss.str("");
m_stream.clear();
m_string_sections.clear();
m_fmt_specs.clear();
ptrdiff_t last_pos = 0;
while (auto pair = consume_to_next_fmt())
{
const auto [begin, end] = *pair;
m_string_sections.emplace_back(m_fmt.data() + last_pos, begin - last_pos);
if (end - begin > 1)
m_fmt_specs.push_back(m_parser.parse(std::string_view{m_fmt.data() + static_cast<ptrdiff_t>(begin) + 1, end - begin - 1}));
else
m_fmt_specs.emplace_back();
last_pos = static_cast<ptrdiff_t>(end) + 1;
}
m_string_sections.emplace_back(m_fmt.data() + last_pos, m_fmt.size() - last_pos);
m_last_fmt_pos = 0;
}
std::optional<std::pair<size_t, size_t>> logger_t::consume_to_next_fmt()
{
auto begin = m_fmt.find('{', m_last_fmt_pos);
while (begin != std::string::npos && begin > 0 && m_fmt[begin - 1] == '\\')
begin = m_fmt.find('{', begin + 1);;
if (begin == std::string::npos)
return {};
const auto end = find_ending_brace(begin);
if (end == std::string::npos)
{
std::stringstream ss;
ss << "Invalid format string, missing closing '}' near " << m_fmt.substr(std::min(static_cast<i64>(begin) - 5, static_cast<i64>(0)));
throw std::runtime_error(ss.str());
}
m_last_fmt_pos = end + 1;
return std::pair{begin, end};
}
logger_t& get_global_logger()
{
return get_thread_context().logger;
}
void print(std::string str)
{
#ifdef BLT_LOGGING_THREAD_SAFE
std::scoped_lock lock{global_logging_mutex};
#endif
const auto& config = get_global_config();
bool should_print = true;
if (!config.get_injectors().empty())
{
for (const auto& injector : config.get_injectors())
{
auto [new_logging_output, should_continue, should_log] = injector->inject(str);
if (!should_log)
should_print = false;
str = std::move(new_logging_output);
if (!should_continue)
break;
}
}
if (should_print)
std::cout << str;
}
void newline()
{
#ifdef BLT_LOGGING_THREAD_SAFE
std::scoped_lock lock{global_logging_mutex};
#endif
std::cout << std::endl;
}
logging_config_t& get_global_config()
{
return global_context.global_config;
}
void set_thread_name(const std::string& name)
{
get_thread_context().thread_name = name;
}
const std::string& get_thread_name()
{
return get_thread_context().thread_name;
}
std::ostream& get_local_stream()
{
auto& context = get_thread_context();
context.logging_stream.str("");
return context.logging_stream;
}
}

View File

@ -0,0 +1,333 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ctime>
#include <iostream>
#include <blt/fs/path_helper.h>
#include <blt/fs/stream_wrappers.h>
#include <blt/logging/ansi.h>
#include <blt/logging/logging_config.h>
#include <blt/std/hashmap.h>
#include <blt/std/system.h>
#include <blt/std/time.h>
namespace blt::logging
{
namespace tags::detail
{
hashmap_t<std::string_view, log_tag_token_t> make_map()
{
hashmap_t<std::string_view, log_tag_token_t> map{};
map[YEAR] = log_tag_token_t::YEAR;
map[MONTH] = log_tag_token_t::MONTH;
map[DAY] = log_tag_token_t::DAY;
map[HOUR] = log_tag_token_t::HOUR;
map[MINUTE] = log_tag_token_t::MINUTE;
map[SECOND] = log_tag_token_t::SECOND;
map[MILLISECOND] = log_tag_token_t::MS;
map[NANOSECOND] = log_tag_token_t::NS;
map[UNIX_TIME] = log_tag_token_t::UNIX;
map[UNIX_TIME_NANO] = log_tag_token_t::UNIX_NANO;
map[ISO_YEAR] = log_tag_token_t::ISO_YEAR;
map[TIME] = log_tag_token_t::TIME;
map[FULL_TIME] = log_tag_token_t::FULL_TIME;
map[LOG_COLOR] = log_tag_token_t::LC;
map[ERROR_COLOR] = log_tag_token_t::EC;
map[CONDITIONAL_ERROR_COLOR] = log_tag_token_t::CEC;
map[RESET] = log_tag_token_t::RESET;
map[LOG_LEVEL] = log_tag_token_t::LL;
map[THREAD_NAME] = log_tag_token_t::TN;
map[FILE] = log_tag_token_t::FILE;
map[LINE] = log_tag_token_t::LINE;
map[STR] = log_tag_token_t::STR;
return map;
}
}
void logging_config_t::compile()
{
static hashmap_t<std::string_view, tags::detail::log_tag_token_t> tag_map = tags::detail::make_map();
m_log_tag_content.clear();
m_log_tag_tokens.clear();
size_t i = 0;
for (; i < m_log_format.size(); ++i)
{
size_t start = i;
while (i < m_log_format.size() && m_log_format[i] != '{')
++i;
if (i == m_log_format.size() || (i < m_log_format.size() && (i - start) > 0))
{
m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + start, i - start));
m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
if (i == m_log_format.size())
break;
}
start = i;
while (i < m_log_format.size() && m_log_format[i] != '}')
++i;
const auto tag = std::string_view(m_log_format.data() + start, i - start + 1);
auto it = tag_map.find(tag);
if (it == tag_map.end())
throw std::runtime_error("Invalid log tag: " + std::string(tag));
m_log_tag_tokens.emplace_back(it->second);
}
if (i < m_log_format.size())
{
m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + i, m_log_format.size() - i));
m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
}
m_longest_name_length = 0;
for (const auto& name : m_log_level_names)
m_longest_name_length = std::max(m_longest_name_length, name.size());
}
std::string add_year(const tm* current_time)
{
return std::to_string(current_time->tm_year + 1900);
}
std::string add_month(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_mon + 1);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_day(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_mday);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_hour(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_hour);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_minute(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_min);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_second(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_sec);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::optional<std::string> logging_config_t::generate(const std::string& user_str, const std::string& thread_name, const log_level_t level,
const char* file, const i32 line) const
{
if (level < m_level)
return {};
std::string fmt;
const std::time_t time = std::time(nullptr);
const auto current_time = std::localtime(&time);
const auto millis_time = system::getCurrentTimeMilliseconds();
const auto nano_time = system::getCurrentTimeNanoseconds();
size_t content = 0;
for (const auto& log_tag_token : m_log_tag_tokens)
{
switch (log_tag_token)
{
case tags::detail::log_tag_token_t::YEAR:
fmt += add_year(current_time);
break;
case tags::detail::log_tag_token_t::MONTH:
fmt += add_month(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::DAY:
fmt += add_day(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::HOUR:
fmt += add_hour(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::MINUTE:
fmt += add_minute(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::SECOND:
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::MS:
{
auto str = std::to_string(millis_time % 1000);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 4; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::NS:
{
auto str = std::to_string(nano_time % 1000000000ul);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 9; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::UNIX:
{
fmt += std::to_string(millis_time);
break;
}
case tags::detail::log_tag_token_t::UNIX_NANO:
{
fmt += std::to_string(nano_time);
break;
}
case tags::detail::log_tag_token_t::ISO_YEAR:
{
fmt += add_year(current_time);
fmt += '-';
fmt += add_month(current_time, m_ensure_alignment);
fmt += '-';
fmt += add_day(current_time, m_ensure_alignment);
break;
}
case tags::detail::log_tag_token_t::TIME:
fmt += add_hour(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_minute(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::FULL_TIME:
fmt += add_year(current_time);
fmt += '-';
fmt += add_month(current_time, m_ensure_alignment);
fmt += '-';
fmt += add_day(current_time, m_ensure_alignment);
fmt += ' ';
fmt += add_hour(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_minute(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::LC:
if (!m_use_color)
break;
fmt += m_log_level_colors[static_cast<u8>(level)];
break;
case tags::detail::log_tag_token_t::EC:
if (!m_use_color)
break;
fmt += m_error_color;
break;
case tags::detail::log_tag_token_t::CEC:
if (!m_use_color)
break;
if (static_cast<u8>(level) >= static_cast<u8>(log_level_t::ERROR))
fmt += m_error_color;
break;
case tags::detail::log_tag_token_t::RESET:
if (!m_use_color)
break;
fmt += build(ansi::color::color_mode::RESET_ALL);
break;
case tags::detail::log_tag_token_t::LL:
fmt += m_log_level_names[static_cast<u8>(level)];
break;
case tags::detail::log_tag_token_t::TN:
fmt += thread_name;
break;
case tags::detail::log_tag_token_t::FILE:
if (m_print_full_name)
fmt += file;
else
fmt += fs::filename_sv(file);
break;
case tags::detail::log_tag_token_t::LINE:
fmt += std::to_string(line);
break;
case tags::detail::log_tag_token_t::STR:
fmt += user_str;
break;
case tags::detail::log_tag_token_t::CONTENT:
fmt += m_log_tag_content[content++];
break;
}
}
return fmt;
}
std::string logging_config_t::get_default_log_format()
{
return build(fg(ansi::color::color8_bright::BLUE)) + "[" + tags::FULL_TIME + "]" + tags::RESET + " " + tags::LOG_COLOR + "[" + tags::LOG_LEVEL
+ "]" + tags::RESET + " " + build(fg(ansi::color::color8::MAGENTA)) + "(" + tags::FILE + ":" + tags::LINE + ")" + tags::RESET + " " +
tags::CONDITIONAL_ERROR_COLOR + tags::STR + tags::RESET + "\n";
}
std::vector<fs::writer_t*> logging_config_t::get_default_log_outputs()
{
static fs::fstream_writer_t cout_writer{std::cout};
std::vector<fs::writer_t*> outputs{};
outputs.push_back(&cout_writer);
return outputs;
}
std::array<std::string, LOG_LEVEL_COUNT> logging_config_t::get_default_log_level_colors()
{
return {
// TRACE
build(fg(ansi::color::color8_bright::WHITE)),
// DEBUG
build(fg(ansi::color::color8::CYAN)),
// INFO
build(fg(ansi::color::color8_bright::GREEN)),
// WARN
build(fg(ansi::color::color8_bright::YELLOW)),
// ERROR
build(fg(ansi::color::color8_bright::RED)),
// FATAL
build(fg(ansi::color::color8_bright::WHITE), bg(ansi::color::color8_bright::RED)),
};
}
std::array<std::string, LOG_LEVEL_COUNT> logging_config_t::get_default_log_level_names()
{
return {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL",};
}
std::string logging_config_t::get_default_error_color()
{
return build(fg(ansi::color::color8_bright::RED));
}
}

225
src/blt/logging/status.cpp Normal file
View File

@ -0,0 +1,225 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <iostream>
#ifdef unix
#include <termios.h>
#include <unistd.h>
#endif
#include <blt/logging/ansi.h>
#include <blt/logging/fmt_tokenizer.h>
#include <blt/logging/logging.h>
#include <blt/logging/status.h>
#include <blt/math/vectors.h>
#include <blt/math/log_util.h>
namespace blt::logging
{
vec2i get_cursor_position()
{
#ifdef unix
termios save{}, raw{};
tcgetattr(0, &save);
cfmakeraw(&raw);
tcsetattr(0,TCSANOW, &raw);
char buf[32];
char cmd[] = "\033[6n";
int row = 0;
int col = 0;
if (isatty(fileno(stdin)))
{
ssize_t i = write(1, cmd, sizeof(cmd));
(void) i;
i = read(0, buf, sizeof(buf));
(void) i;
int sep = 0;
int end = 0;
for (int i = 2; i < 8; i++)
{
if (buf[i] == ';')
sep = i;
if (buf[i] == 'R')
{
end = i;
break;
}
}
row = std::stoi(std::string(buf + 2, buf + sep));
col = std::stoi(std::string(buf + sep + 1, buf + end));
}
tcsetattr(0,TCSANOW, &save);
return vec2i{row, col};
#else
return {0,0};
#endif
}
#define SIZE 100
vec2i get_screen_size()
{
#ifdef unix
char in[SIZE] = "";
int each = 0;
int ch = 0;
int rows = 0;
int cols = 0;
termios original, changed;
// change terminal settings
tcgetattr( STDIN_FILENO, &original);
changed = original;
changed.c_lflag &= ~( ICANON | ECHO);
changed.c_cc[VMIN] = 1;
changed.c_cc[VTIME] = 0;
tcsetattr( STDIN_FILENO, TCSANOW, &changed);
printf ( "\033[2J"); //clear screen
printf ( "\033[9999;9999H"); // cursor should move as far as it can
printf ( "\033[6n"); // ask for cursor position
while ( ( ch = getchar ()) != 'R') { // R terminates the response
if ( EOF == ch) {
break;
}
if ( isprint ( ch)) {
if ( each + 1 < SIZE) {
in[each] = ch;
each++;
in[each] = '\0';
}
}
}
printf ( "\033[1;1H"); // move to upper left corner
if ( 2 == sscanf ( in, "[%d;%d", &rows, &cols)) {
tcsetattr( STDIN_FILENO, TCSANOW, &original);
return {rows, cols};
}
throw std::runtime_error("Could not get screen size");
#else
return {0,0};
#endif
}
i32 get_size_no_ansi(const std::string& str)
{
i32 size = 0;
for (size_t i = 0; i < str.size(); i++)
{
if (str[i] == BLT_ANSI_ESCAPE[0])
{
while (i < str.size())
{
if (std::isalpha(str[i++]))
break;
}
}
++size;
}
return size - 1;
}
std::string status_progress_bar_t::print(const vec2i, const i32 max_printed_length) const
{
std::string output = "[";
output.reserve(max_printed_length);
const auto amount_filled = (max_printed_length - 2) * m_progress;
auto amount_filled_int = static_cast<i32>(amount_filled);
const auto frac = amount_filled - static_cast<double>(amount_filled_int);
for (i64 i = 0; i < amount_filled_int; i++)
output += '#';
if (frac >= 0.5)
{
output += '|';
++amount_filled_int;
}
for (i64 i = amount_filled_int; i < max_printed_length - 2; i++)
output += ' ';
output += ']';
return output;
}
void status_progress_bar_t::set_progress(const double progress)
{
if (std::isnan(progress) || progress < 0 || progress > 1 || std::isinf(progress))
throw std::invalid_argument("Progress must be between 0 and 1 (got: " + std::to_string(progress) + ")");
m_progress = progress;
// m_status->redraw();
}
status_bar_t::status_bar_t()
{
m_screen_size = get_screen_size();
std::cout << ansi::cursor::home << std::flush;
std::cout << ansi::erase::entire_screen << std::flush;
m_begin_position = m_last_log_position = get_cursor_position();
std::cout << ansi::cursor::hide_cursor << std::flush;
}
injector_output_t status_bar_t::inject(const std::string& input)
{
std::scoped_lock lock{m_print_mutex};
injector_output_t output{input, false, false};
if (output.new_logging_output.back() != '\n')
output.new_logging_output += '\n';
if (get_cursor_position() != m_begin_position)
{
for (int i = 0; i < m_status_size; i++)
std::cout << ansi::erase::entire_line << ansi::cursor::move_begin_up(1) << std::flush;
}
std::cout << ansi::erase::entire_line << std::flush;
std::cout << output.new_logging_output << std::flush;
m_max_printed_length = std::max(get_size_no_ansi(output.new_logging_output), m_max_printed_length);
m_last_log_position = get_cursor_position();
redraw();
return output;
}
void status_bar_t::compute_size()
{
m_status_size = 0;
for (const auto* ptr : m_status_items)
m_status_size += ptr->lines_used();
}
void status_bar_t::redraw() const
{
std::cout << ansi::cursor::move_to(m_last_log_position.x(), m_last_log_position.y());
for (const auto* ptr : m_status_items)
std::cout << ansi::erase::entire_line << ptr->print(m_screen_size, m_max_printed_length) << std::endl;
std::cout << std::flush;
}
status_bar_t::~status_bar_t()
{
std::cout << ansi::cursor::show_cursor << std::flush;
}
}

View File

@ -9,6 +9,7 @@
#include <blt/iterator/iterator.h> #include <blt/iterator/iterator.h>
#include <algorithm> #include <algorithm>
#include "blt/std/utility.h" #include "blt/std/utility.h"
#include <blt/std/variant.h>
namespace blt namespace blt
{ {
@ -196,7 +197,7 @@ namespace blt
printUsage(); printUsage();
std::cout << getProgramName() << ": error: flag '" << flag << "' expected " << properties.a_nargs.args std::cout << getProgramName() << ": error: flag '" << flag << "' expected " << properties.a_nargs.args
<< " argument(s) but found '" << tokenizer.get() << "' instead!\n"; << " argument(s) but found '" << tokenizer.get() << "' instead!\n";
//BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args); //BLT_WARN("Expected {:d} arguments, found flag instead!", properties.a_nargs.args);
return false; return false;
} }
// get the value and advance // get the value and advance

File diff suppressed because it is too large Load Diff

View File

@ -26,231 +26,231 @@
#include <charconv> #include <charconv>
#include "blt/std/assert.h" #include "blt/std/assert.h"
#include "blt/std/utility.h" #include "blt/std/utility.h"
#include <blt/std/logging.h> #include <blt/logging/logging.h>
namespace blt::parse namespace blt::parse
{ {
class char_tokenizer class char_tokenizer
{ {
private: private:
std::string_view string; std::string_view string;
std::size_t current_pos = 0; std::size_t current_pos = 0;
public:
explicit char_tokenizer(std::string_view view): string(view)
{}
inline char advance() public:
{ explicit char_tokenizer(std::string_view view): string(view)
return string[current_pos++]; {}
}
inline bool has_next(size_t offset = 0) inline char advance()
{ {
return current_pos + offset < string.size(); return string[current_pos++];
} }
inline std::string_view read_fully() inline bool has_next(size_t offset = 0)
{ {
return blt::string::trim(string.substr(current_pos)); return current_pos + offset < string.size();
} }
};
template<typename T = float> inline std::string_view read_fully()
T get(std::string_view str) {
{ return blt::string::trim(string.substr(current_pos));
T x; }
// TODO: GCC version. C++17 supports from_chars but GCC8.5 doesn't have floating point. };
#if __cplusplus >= BLT_CPP20
template <typename T = float>
T get(std::string_view str)
{
T x;
// TODO: GCC version. C++17 supports from_chars but GCC8.5 doesn't have floating point.
#if __cplusplus >= BLT_CPP20
const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), x); const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), x);
#else #else
auto ec = std::errc(); auto ec = std::errc();
if constexpr (std::is_floating_point_v<T>) if constexpr (std::is_floating_point_v<T>)
{ {
x = static_cast<T>(std::stod(std::string(str))); x = static_cast<T>(std::stod(std::string(str)));
} else if constexpr (std::is_integral_v<T>) } else if constexpr (std::is_integral_v<T>)
{ {
x = static_cast<T>(std::stoll(std::string(str))); x = static_cast<T>(std::stoll(std::string(str)));
} else } else
static_assert( static_assert(
"You are using a c++ version which does not support the required std::from_chars, manual conversion has failed to find a type!"); "You are using a c++ version which does not support the required std::from_chars, manual conversion has failed to find a type!");
#endif #endif
// probably not needed. // probably not needed.
if (ec != std::errc()) if (ec != std::errc())
{ {
// int i; // int i;
// const auto [ptr2, ec2] = std::from_chars(str.data(), str.data() + str.size(), i); // const auto [ptr2, ec2] = std::from_chars(str.data(), str.data() + str.size(), i);
// if (ec2 == std::errc()) // if (ec2 == std::errc())
// { // {
// x = static_cast<float>(i); // x = static_cast<float>(i);
// } else // } else
// { // {
BLT_WARN("Unable to parse string '%s' into number!", std::string(str).c_str()); BLT_WARN("Unable to parse string '{}' into number!", std::string(str).c_str());
x = 0; x = 0;
// } // }
} }
return x; return x;
} }
void obj_loader::parse_vertex_line(char_tokenizer& tokenizer) void obj_loader::parse_vertex_line(char_tokenizer& tokenizer)
{ {
char type = tokenizer.advance(); char type = tokenizer.advance();
if (type == 'p') if (type == 'p')
{ {
BLT_WARN("Unexpected type '%c' (not supported)", type); BLT_WARN("Unexpected type '{:c}' (not supported)", type);
return; return;
} }
auto elements = blt::string::split(std::string(tokenizer.read_fully()), " "); auto elements = string::split(tokenizer.read_fully(), " ");
BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!"); BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!");
float x = get(elements[0]), y = get(elements[1]); float x = get(elements[0]), y = get(elements[1]);
BLT_DEBUG_STREAM << "Loaded value of (" << x << ", " << y << ")"; BLT_DEBUG("Loaded value of ({}, {})", x, y);
if (elements.size() < 3) if (elements.size() < 3)
{ {
if (type == 't') if (type == 't')
uvs.push_back(uv_t{x, y}); uvs.push_back(uv_t{x, y});
else else
BLT_ERROR("Unable to parse line '%s' type '%c' not recognized for arg count", std::string(tokenizer.read_fully()).c_str(), type); BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized for arg count", tokenizer.read_fully(), type);
} else } else
{ {
float z = get(elements[2]); float z = get(elements[2]);
BLT_DEBUG_STREAM << " with z: " << z; BLT_DEBUG(" with z: {}", z);
if (!handle_vertex_and_normals(x, y, z, type)) if (!handle_vertex_and_normals(x, y, z, type))
BLT_ERROR("Unable to parse line '%s' type '%c' not recognized", std::string(tokenizer.read_fully()).c_str(), type); BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized", tokenizer.read_fully(), type);
} }
BLT_DEBUG_STREAM << "\n"; }
}
bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type) bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type)
{ {
if (std::isspace(type)) if (std::isspace(type))
{ {
vertices.push_back(vertex_t{x, y, z}); vertices.push_back(vertex_t{x, y, z});
} else if (type == 'n') } else if (type == 'n')
{ {
normals.push_back(normal_t{x, y, z}); normals.push_back(normal_t{x, y, z});
} else } else
return false; return false;
return true; return true;
} }
obj_model_t quick_load(std::string_view file) obj_model_t quick_load(std::string_view file)
{ {
return obj_loader().parseFile(file); return obj_loader().parseFile(file);
} }
obj_model_t obj_loader::parseFile(std::string_view file) obj_model_t obj_loader::parseFile(std::string_view file)
{ {
auto lines = blt::fs::getLinesFromFile(std::string(file)); auto lines = blt::fs::getLinesFromFile(std::string(file));
for (const auto& [index, line] : blt::enumerate(lines)) for (const auto& [index, line] : blt::enumerate(lines))
{ {
current_line = index; current_line = index;
char_tokenizer token(line); char_tokenizer token(line);
if (!token.has_next() || token.read_fully().empty()) if (!token.has_next() || token.read_fully().empty())
continue; continue;
switch (token.advance()) switch (token.advance())
{ {
case '#': case '#':
continue; continue;
case 'f': case 'f':
parse_face(token); parse_face(token);
break; break;
case 'v': case 'v':
parse_vertex_line(token); parse_vertex_line(token);
break; break;
case 'o': case 'o':
{ {
current_object.object_names.emplace_back(token.read_fully()); current_object.object_names.emplace_back(token.read_fully());
BLT_TRACE("Setting object '%s'", std::string(current_object.object_name).c_str()); BLT_TRACE("Setting object '{}'", std::string(current_object.object_name).c_str());
break; break;
} }
case 'm': case 'm':
{ {
while (token.has_next() && token.advance() != ' ') while (token.has_next() && token.advance() != ' ')
{} {}
BLT_WARN("Material '%s' needs to be loaded!", std::string(token.read_fully()).c_str()); BLT_WARN("Material '{}' needs to be loaded!", std::string(token.read_fully()).c_str());
break; break;
} }
case 'u': case 'u':
{ {
if (!current_object.indices.empty()) if (!current_object.indices.empty())
data.push_back(current_object); data.push_back(current_object);
current_object = {}; current_object = {};
while (token.has_next() && token.advance() != ' ') while (token.has_next() && token.advance() != ' ')
{} {}
current_object.material = token.read_fully(); current_object.material = token.read_fully();
//BLT_WARN("Using material '%s'", std::string(token.read_fully()).c_str()); //BLT_WARN("Using material '{}'", std::string(token.read_fully()).c_str());
break; break;
} }
case 's': case 's':
//BLT_WARN("Using shading: %s", std::string(token.read_fully()).c_str()); //BLT_WARN("Using shading: {}", std::string(token.read_fully()).c_str());
break; break;
} }
} }
data.push_back(current_object); data.push_back(current_object);
return {std::move(vertex_data), std::move(data), std::move(materials)}; return {std::move(vertex_data), std::move(data), std::move(materials)};
} }
void obj_loader::parse_face(char_tokenizer& tokenizer) void obj_loader::parse_face(char_tokenizer& tokenizer)
{ {
auto faces = blt::string::split(std::string(tokenizer.read_fully()), ' '); auto faces = blt::string::split(std::string(tokenizer.read_fully()), ' ');
if (faces.size() == 3) if (faces.size() == 3)
{ {
triangle_t triangle{}; triangle_t triangle{};
handle_face_vertex(faces, triangle.v); handle_face_vertex(faces, triangle.v);
current_object.indices.push_back(triangle); current_object.indices.push_back(triangle);
} else if (faces.size() == 4) } else if (faces.size() == 4)
{ {
quad_t quad{}; quad_t quad{};
handle_face_vertex(faces, quad.v); handle_face_vertex(faces, quad.v);
triangle_t t1{}; triangle_t t1{};
triangle_t t2{}; triangle_t t2{};
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
t1.v[i] = quad.v[i]; t1.v[i] = quad.v[i];
t2.v[0] = quad.v[0]; t2.v[0] = quad.v[0];
t2.v[1] = quad.v[2]; t2.v[1] = quad.v[2];
t2.v[2] = quad.v[3]; t2.v[2] = quad.v[3];
current_object.indices.push_back(t1); current_object.indices.push_back(t1);
current_object.indices.push_back(t2); current_object.indices.push_back(t2);
} else } else
BLT_WARN("Unsupported face vertex count of %d on line %d!", faces.size(), current_line); BLT_WARN("Unsupported face vertex count of {:d} on line {:d}!", faces.size(), current_line);
} }
void obj_loader::handle_face_vertex(const std::vector<std::string>& face_list, int32_t* arr) void obj_loader::handle_face_vertex(const std::vector<std::string>& face_list, int32_t* arr)
{ {
for (const auto& [e_index, value] : blt::enumerate(face_list)) for (const auto& [e_index, value] : blt::enumerate(face_list))
{ {
auto indices = blt::string::split(value, '/'); auto indices = blt::string::split(value, '/');
BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!"); BLT_ASSERT(indices.size() == 3 && "Must have vertex, uv, and normal indices!!");
auto vi = get<std::int32_t>(indices[0]) - 1; auto vi = get<std::int32_t>(indices[0]) - 1;
auto ui = get<std::int32_t>(indices[1]) - 1; auto ui = get<std::int32_t>(indices[1]) - 1;
auto ni = get<std::int32_t>(indices[2]) - 1; auto ni = get<std::int32_t>(indices[2]) - 1;
BLT_DEBUG("Found vertex: %d, UV: %d, and normal: %d", vi, ui, ni); BLT_DEBUG("Found vertex: {:d}, UV: {:d}, and normal: {:d}", vi, ui, ni);
face_t face{vi, ui, ni}; face_t face{vi, ui, ni};
auto loc = vertex_map.find(face); auto loc = vertex_map.find(face);
if (loc == vertex_map.end()) if (loc == vertex_map.end())
{ {
BLT_DEBUG("DID NOT FIND FACE!"); BLT_DEBUG("{}", "DID NOT FIND FACE!");
auto index = static_cast<std::int32_t>(vertex_data.size()); auto index = static_cast<std::int32_t>(vertex_data.size());
vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]}); vertex_data.push_back({vertices[vi], uvs[ui], normals[ni]});
BLT_DEBUG("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", vertices[vi].x(), vertices[vi].y(), vertices[vi].z(), BLT_DEBUG("Vertex: ({.4f}, {.4f}, {.4f}), UV: ({.4f}, {.4f}), Normal: ({.4f}, {.4f}, {:.4f})", vertices[vi].x(), vertices[vi].y(),
uvs[ui].x(), uvs[ui].y(), normals[ni].x(), normals[ni].y(), normals[ni].z()); vertices[vi].z(), uvs[ui].x(), uvs[ui].y(), normals[ni].x(), normals[ni].y(), normals[ni].z());
vertex_map.insert({face, index}); vertex_map.insert({face, index});
arr[e_index] = index; arr[e_index] = index;
} else } else
{ {
BLT_TRACE("Using cached data; %d; map size: %d", loc->second, vertex_data.size()); BLT_TRACE("Using cached data; {:d}; map size: {:d}", loc->second, vertex_data.size());
//const auto& d = vertex_data[loc->second]; //const auto& d = vertex_data[loc->second];
BLT_TRACE("Vertex: (%f, %f, %f), UV: (%f, %f), Normal: (%f, %f, %f)", d.vertex.x(), d.vertex.y(), d.vertex.z(), BLT_TRACE("Vertex: ({.4f}, {.4f}, {.4f}), UV: ({.4f}, {.4f}), Normal: ({.4f}, {.4f}, {:.4f})", d.vertex.x(), d.vertex.y(),
d.uv.x(), d.uv.y(), d.normal.x(), d.normal.y(), d.normal.z()); d.vertex.z(), d.uv.x(), d.uv.y(), d.normal.x(), d.normal.y(), d.normal.z());
arr[e_index] = loc->second; arr[e_index] = loc->second;
} }
} }
} }
} }

View File

@ -18,7 +18,7 @@
#include <blt/parse/templating.h> #include <blt/parse/templating.h>
#include <blt/std/string.h> #include <blt/std/string.h>
#include <cctype> #include <cctype>
#include "blt/std/logging.h" #include "blt/logging/logging.h"
namespace blt namespace blt
{ {
@ -275,7 +275,7 @@ namespace blt
values.push_back(b1 ^ b2); values.push_back(b1 ^ b2);
break; break;
default: default:
BLT_WARN("Unexpected token '%s'", std::string(next.token).c_str()); BLT_WARN("Unexpected token '{}'", std::string(next.token).c_str());
return blt::unexpected(template_parser_failure_t::BOOL_TYPE_NOT_FOUND); return blt::unexpected(template_parser_failure_t::BOOL_TYPE_NOT_FOUND);
} }
} }

View File

@ -7,7 +7,7 @@
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <blt/std/time.h> #include <blt/std/time.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <blt/format/format.h> #include <blt/format/format.h>
@ -32,9 +32,9 @@ namespace blt::profiling {
difference(difference), name(std::move(name)), total(total) {} difference(difference), name(std::move(name)), total(total) {}
}; };
inline void println(const std::vector<std::string>&& lines, logging::log_level level) { inline void println(const std::vector<std::string>&& lines, const logging::log_level_t level) {
for (const auto& line : lines) for (const auto& line : lines)
BLT_LOG_STREAM(level) << line << "\n"; BLT_LOG(level, "{}", line);
// auto& logger = logging::getLoggerFromLevel(level); // auto& logger = logging::getLoggerFromLevel(level);
// for (const auto& line : lines) // for (const auto& line : lines)
// logger << line << "\n"; // logger << line << "\n";
@ -108,7 +108,7 @@ namespace blt::profiling {
} }
void printProfile( void printProfile(
const std::string& profileName, logging::log_level loggingLevel, bool averageHistory const std::string& profileName, const logging::log_level_t loggingLevel, const bool averageHistory
) { ) {
auto& profile = profiles[profileName]; auto& profile = profiles[profileName];
const auto& intervals = profile.intervals; const auto& intervals = profile.intervals;

View File

@ -89,11 +89,6 @@ namespace blt
profiler.cycle_intervals.clear(); profiler.cycle_intervals.clear();
} }
void writeProfile(std::ifstream&, const profile_t&)
{
BLT_WARN("Write profile for V2 is currently a TODO");
}
void sort_intervals(std::vector<interval_t*>& intervals, sort_by sort, bool) void sort_intervals(std::vector<interval_t*>& intervals, sort_by sort, bool)
{ {
std::function<bool(const interval_t* a, const interval_t* b)> sort_func; std::function<bool(const interval_t* a, const interval_t* b)> sort_func;
@ -137,12 +132,7 @@ namespace blt
return container; return container;
} }
inline void println(const std::vector<std::string>&& lines, logging::log_level level) { void writeProfile(std::ostream& stream, profile_t& profiler, std::uint32_t flags, sort_by sort)
for (const auto& line : lines)
BLT_LOG_STREAM(level) << line << "\n";
}
void printProfile(profile_t& profiler, std::uint32_t flags, sort_by sort, blt::logging::log_level log_level)
{ {
bool printHistory = flags & AVERAGE_HISTORY; bool printHistory = flags & AVERAGE_HISTORY;
bool printCycles = flags & PRINT_CYCLES; bool printCycles = flags & PRINT_CYCLES;
@ -192,7 +182,16 @@ namespace blt
formatter.addRow(row); formatter.addRow(row);
} }
println(formatter.createTable(true, true), log_level); auto lines = formatter.createTable(true, true);
for (const auto& line : lines)
stream << line << "\n";
}
void printProfile(profile_t& profiler, const std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level)
{
std::stringstream stream;
writeProfile(stream, profiler, flags, sort);
BLT_LOG(log_level, "{}", stream.str());
} }
profile_t::~profile_t() profile_t::~profile_t()
@ -226,7 +225,7 @@ namespace blt
blt::endInterval(profiles[profile_name].at(interval_name)); blt::endInterval(profiles[profile_name].at(interval_name));
} }
void _internal::writeProfile(std::ifstream& stream, const std::string& profile_name) void _internal::writeProfile(std::ostream& stream, const std::string& profile_name, std::uint32_t flags, sort_by sort)
{ {
if (profiles.find(profile_name) == profiles.end()) if (profiles.find(profile_name) == profiles.end())
return; return;
@ -234,11 +233,11 @@ namespace blt
profile_t profile{profile_name}; profile_t profile{profile_name};
for (const auto& i : pref) for (const auto& i : pref)
profile.intervals.push_back(i.second); profile.intervals.push_back(i.second);
blt::writeProfile(stream, profile); blt::writeProfile(stream, profile, flags, sort);
profiles.erase(profile_name); profiles.erase(profile_name);
} }
void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level log_level) void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level)
{ {
if (profiles.find(profile_name) == profiles.end()) if (profiles.find(profile_name) == profiles.end())
return; return;

View File

@ -3,17 +3,17 @@
* Licensed under GNU General Public License V3.0 * Licensed under GNU General Public License V3.0
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
#include <blt/std/assert.h>
#include <blt/std/utility.h>
#include <blt/std/logging.h>
#include <blt/std/string.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <exception>
#include <cstring> #include <cstring>
#include <exception>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <blt/logging/logging.h>
#include <blt/std/assert.h>
#include <blt/std/string.h>
#include <blt/std/utility.h>
struct abort_exception : public std::exception struct abort_exception final : public std::exception
{ {
public: public:
explicit abort_exception(const char* what) explicit abort_exception(const char* what)
@ -42,14 +42,18 @@ struct abort_exception : public std::exception
char* error{nullptr}; char* error{nullptr};
}; };
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #if defined(__GNUC__) && !defined(__EMSCRIPTEN__) && !defined(WIN32)
#define IS_GNU_BACKTRACE
#endif
#ifdef IS_GNU_BACKTRACE
#include <execinfo.h>
#include <cstdlib> #include <cstdlib>
#include <execinfo.h>
#endif #endif
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
#define BLT_STACK_TRACE(number) void* ptrs[number]; \ #define BLT_STACK_TRACE(number) void* ptrs[number]; \
int size = backtrace(ptrs, number); \ int size = backtrace(ptrs, number); \
char** messages = backtrace_symbols(ptrs, size); char** messages = backtrace_symbols(ptrs, size);
@ -64,7 +68,7 @@ struct abort_exception : public std::exception
namespace blt namespace blt
{ {
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
static inline std::string _macro_filename(const std::string& path) static inline std::string _macro_filename(const std::string& path)
{ {
@ -79,10 +83,10 @@ namespace blt
void b_throw(const char* what, const char* path, int line) void b_throw(const char* what, const char* path, int line)
{ {
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
BLT_STACK_TRACE(50); BLT_STACK_TRACE(50);
BLT_ERROR("An exception '%s' has occurred in file '%s:%d'", what, path, line); BLT_ERROR("An exception '{}' has occurred in file '{}:{:d}'", what, path, line);
BLT_ERROR("Stack Trace:"); BLT_ERROR("Stack Trace:");
printStacktrace(messages, size, path, line); printStacktrace(messages, size, path, line);
@ -96,10 +100,10 @@ namespace blt
void b_assert_failed(const char* expression, const char* msg, const char* path, int line) void b_assert_failed(const char* expression, const char* msg, const char* path, int line)
{ {
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
BLT_STACK_TRACE(50); BLT_STACK_TRACE(50);
BLT_ERROR("The assertion '%s' has failed in file '%s:%d'", expression, path, line); BLT_ERROR("The assertion '{}' has failed in file '{}:{:d}'", expression, path, line);
if (msg != nullptr) if (msg != nullptr)
BLT_ERROR(msg); BLT_ERROR(msg);
BLT_ERROR("Stack Trace:"); BLT_ERROR("Stack Trace:");
@ -122,7 +126,7 @@ namespace blt
{ {
if (messages == nullptr) if (messages == nullptr)
return; return;
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
{ {
int tabs = i - 1; int tabs = i - 1;
@ -167,13 +171,13 @@ namespace blt
void b_abort(const char* what, const char* path, int line) void b_abort(const char* what, const char* path, int line)
{ {
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
BLT_STACK_TRACE(50); BLT_STACK_TRACE(50);
#endif #endif
BLT_FATAL("----{BLT ABORT}----"); BLT_FATAL("----{BLT ABORT}----");
BLT_FATAL("\tWhat: %s", what); BLT_FATAL("\tWhat: {}", what);
BLT_FATAL("\tcalled from %s:%d", path, line); BLT_FATAL("\tCalled from {}:{:d}", path, line);
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) #ifdef IS_GNU_BACKTRACE
printStacktrace(messages, size, path, line); printStacktrace(messages, size, path, line);
BLT_FREE_STACK_TRACE(); BLT_FREE_STACK_TRACE();

View File

@ -15,68 +15,52 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <blt/std/error.h> #include <blt/std/error.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
namespace blt::error namespace blt::error
{ {
void print_socket_error() void print_socket_error()
{ {
switch (errno) switch (errno)
{ {
case EINVAL: case EINVAL: BLT_WARN("Invalid argument");
BLT_WARN("Invalid argument"); break;
break; case EACCES: BLT_WARN("Permission denied");
case EACCES: break;
BLT_WARN("Permission denied"); case EPERM: BLT_WARN("Operation not permitted");
break; break;
case EPERM: case EADDRINUSE: BLT_WARN("Address already in use");
BLT_WARN("Operation not permitted"); break;
break; case EADDRNOTAVAIL: BLT_WARN("Cannot copy_fast requested address");
case EADDRINUSE: break;
BLT_WARN("Address already in use"); case EAFNOSUPPORT: BLT_WARN("Address family not supported by protocol");
break; break;
case EADDRNOTAVAIL: case EAGAIN: BLT_WARN("Try again");
BLT_WARN("Cannot copy_fast requested address"); break;
break; case EALREADY: BLT_WARN("Operation already in progress");
case EAFNOSUPPORT: break;
BLT_WARN("Address family not supported by protocol"); case EBADF: BLT_WARN("Bad file number");
break; break;
case EAGAIN: case ECONNREFUSED: BLT_WARN("Connection refused");
BLT_WARN("Try again"); break;
break; case EFAULT: BLT_WARN("Bad address");
case EALREADY: break;
BLT_WARN("Operation already in progress"); case EINPROGRESS: BLT_WARN("Operation now in progress");
break; break;
case EBADF: case EINTR: BLT_WARN("Interrupted system call");
BLT_WARN("Bad file number"); break;
break; case EISCONN: BLT_WARN("Transport endpoint is already connected");
case ECONNREFUSED: break;
BLT_WARN("Connection refused"); case ENETUNREACH: BLT_WARN("Network is unreachable");
break; break;
case EFAULT: case ENOTSOCK: BLT_WARN("Socket operation_t on non-socket");
BLT_WARN("Bad address"); break;
break; case EPROTOTYPE: BLT_WARN("Protocol wrong type for socket");
case EINPROGRESS: break;
BLT_WARN("Operation now in progress"); case ETIMEDOUT: BLT_WARN("Connection timed out");
break; break;
case EINTR: default:
BLT_WARN("Interrupted system call"); break;
break; }
case EISCONN: }
BLT_WARN("Transport endpoint is already connected");
break;
case ENETUNREACH:
BLT_WARN("Network is unreachable");
break;
case ENOTSOCK:
BLT_WARN("Socket operation_t on non-socket");
break;
case EPROTOTYPE:
BLT_WARN("Protocol wrong type for socket");
break;
case ETIMEDOUT:
BLT_WARN("Connection timed out");
break;
}
}
} }

View File

@ -1,7 +0,0 @@
/*
* Created by Brett on 20/07/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#define BLT_LOGGING_IMPLEMENTATION
#include <blt/std/logging.h>

View File

@ -15,12 +15,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <blt/logging/logging.h>
#include <blt/std/mmap.h> #include <blt/std/mmap.h>
#ifdef __unix__ #ifdef __unix__
#include <sys/mman.h> #include <sys/mman.h>
#else
#include <blt/std/assert.h>
#endif #endif
namespace blt namespace blt
@ -99,16 +102,23 @@ namespace blt
} }
return buffer; return buffer;
#else #else
(void)page_type;
(void)bytes;
BLT_ABORT("Platform not supported for huge page allocation!"); BLT_ABORT("Platform not supported for huge page allocation!");
#endif #endif
} }
void mmap_free(void* ptr, blt::size_t bytes) void mmap_free(void* ptr, blt::size_t bytes)
{ {
#ifdef __unix__
if (munmap(ptr, bytes)) if (munmap(ptr, bytes))
{ {
BLT_ERROR_STREAM << "Failed to deallocate\n"; BLT_ERROR("Failed to deallocate");
throw bad_alloc_t(handle_mmap_error()); throw bad_alloc_t(handle_mmap_error());
} }
#else
(void)ptr;
(void)bytes;
#endif
} }
} }

121
src/blt/std/requests.cpp Normal file
View File

@ -0,0 +1,121 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#include <blt/std/requests.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#if defined(__has_include) && __has_include(<curl/curl.h>)
#include <curl/curl.h>
#define BLT_HAS_CURL
#endif
namespace blt::requests
{
#ifdef BLT_HAS_CURL
struct curl_init_t
{
curl_init_t()
{
const auto version_data = curl_version_info(CURLVERSION_NOW);
if (!(version_data->features & CURL_VERSION_THREADSAFE))
{
thread_safe = false;
}
curl_global_init(CURL_GLOBAL_ALL);
}
~curl_init_t()
{
curl_global_cleanup();
}
bool thread_safe = true;
};
struct curl_easy_init_t
{
curl_easy_init_t(): curl(curl_easy_init())
{
}
~curl_easy_init_t()
{
curl_easy_cleanup(curl);
}
CURL* curl;
};
void init()
{
static curl_init_t curl_init_obj;
}
CURL* easy_init()
{
thread_local curl_easy_init_t curl_easy_init_obj;
return curl_easy_init_obj.curl;
}
size_t write_to_string_func(const void* data, const size_t size, const size_t nmemb, void* user_data)
{
auto& str = *static_cast<std::string*>(user_data);
str.append(static_cast<const char*>(data), size * nmemb);
return size * nmemb;
};
#endif
std::string send_get_request(const std::string& url)
{
#ifdef __EMSCRIPTEN__
auto* str = static_cast<char*>(EM_ASM_PTR({
var xhr = new XMLHttpRequest();
xhr.open("GET", $0);
xhr.send();
return stringToNewUTF8(xhr.responseText);
}, url.c_str()));
std::string str_obj{str};
free(str);
return str_obj;
#else
#ifdef BLT_HAS_CURL
init();
auto curl = easy_init();
if (!curl)
throw std::runtime_error("Failed to initialize curl");
std::string response_string;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(&response_string));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_to_string_func);
const auto res = curl_easy_perform(curl);
if (res != CURLE_OK)
throw std::runtime_error(curl_easy_strerror(res));
return response_string;
#else
return "Missing cURL! Unable to fetch URL: '" + url + "'";
#endif
#endif
}
}

View File

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

View File

@ -130,10 +130,13 @@ namespace blt
{ {
auto size = pos - from; auto size = pos - from;
auto token = s.substr(from, size); auto token = s.substr(from, size);
tokens.emplace_back(token); if (!token.empty())
tokens.emplace_back(token);
from += size + delim.length(); from += size + delim.length();
} }
tokens.emplace_back(s.substr(from)); auto str = s.substr(from);
if (!str.empty())
tokens.emplace_back(str);
return tokens; return tokens;
} }
@ -146,10 +149,13 @@ namespace blt
{ {
auto size = pos - from; auto size = pos - from;
auto token = s.substr(from, size); auto token = s.substr(from, size);
tokens.emplace_back(token); if (!token.empty())
tokens.emplace_back(token);
from += size + 1; from += size + 1;
} }
tokens.emplace_back(s.substr(from)); auto str = s.substr(from);
if (!str.empty())
tokens.emplace_back(str);
return tokens; return tokens;
} }
@ -162,10 +168,13 @@ namespace blt
{ {
auto size = pos - from; auto size = pos - from;
auto token = s.substr(from, size); auto token = s.substr(from, size);
tokens.push_back(token); if (!token.empty())
tokens.push_back(token);
from += size + delim.length(); from += size + delim.length();
} }
tokens.push_back(s.substr(from)); auto str = s.substr(from);
if (!str.empty())
tokens.push_back(str);
return tokens; return tokens;
} }
@ -178,10 +187,13 @@ namespace blt
{ {
auto size = pos - from; auto size = pos - from;
auto token = s.substr(from, size); auto token = s.substr(from, size);
tokens.push_back(token); if (!token.empty())
tokens.push_back(token);
from += size + 1; from += size + 1;
} }
tokens.push_back(s.substr(from)); auto str = s.substr(from);
if (!str.empty())
tokens.push_back(str);
return tokens; return tokens;
} }

View File

@ -4,9 +4,9 @@
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
#include <blt/std/system.h> #include <blt/std/system.h>
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#ifndef _MSC_VER #if !defined(_MSC_VER) && !defined(WIN32)
#include <sys/time.h> /* for struct timeval */ #include <sys/time.h> /* for struct timeval */
#include <sys/resource.h> #include <sys/resource.h>
#else #else
@ -94,7 +94,7 @@ namespace blt
if (GetProcessTimes(GetCurrentProcess(), if (GetProcessTimes(GetCurrentProcess(),
&starttime, &exittime, &kerneltime, &usertime) == 0) &starttime, &exittime, &kerneltime, &usertime) == 0)
{ {
BLT_WARN("Unable to get process resource usage, error: %d", GetLastError()); BLT_WARN("Unable to get process resource usage, error: {:d}", GetLastError());
return {}; return {};
} }
@ -111,7 +111,7 @@ namespace blt
#else #else
if (getrusage(who, (struct rusage*) &usage) != 0) if (getrusage(who, (struct rusage*) &usage) != 0)
{ {
BLT_ERROR("Failed to get rusage %d", errno); BLT_ERROR("Failed to get rusage {:d}", errno);
return {}; return {};
} }
#endif #endif

3
test.txt Normal file
View File

@ -0,0 +1,3 @@
This is a println with a stream
This is a mixed print 25 with multiple types 34.233400
What about just a new line character?

23
tests/argparse_tests.cpp Normal file
View File

@ -0,0 +1,23 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/parse/argparse_v2.h>
int main(){
blt::argparse::detail::test();
return 0;
}

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <blt/std/logging.h> #include <blt/logging/logging.h>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <blt/std/assert.h> #include <blt/std/assert.h>
#include <blt/math/vectors.h> #include <blt/math/vectors.h>
@ -64,18 +64,18 @@ void test_enumerate()
{ {
blt::log_box_t box(std::cout, "Enumerate Tests", 25); blt::log_box_t box(std::cout, "Enumerate Tests", 25);
for (const auto& [index, item] : blt::enumerate(array_1)) for (const auto& [index, item] : blt::enumerate(array_1))
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_TRACE(""); BLT_TRACE("");
for (const auto& [index, item] : blt::enumerate(array_1).rev()) for (const auto& [index, item] : blt::enumerate(array_1).rev())
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_TRACE(""); BLT_TRACE("");
for (const auto& [index, item] : blt::enumerate(array_1).take(3)) for (const auto& [index, item] : blt::enumerate(array_1).take(3))
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index < 3); BLT_ASSERT(index < 3);
} }
@ -83,7 +83,7 @@ void test_enumerate()
for (const auto& [index, item] : blt::enumerate(array_1).take(3).rev()) for (const auto& [index, item] : blt::enumerate(array_1).take(3).rev())
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index < 3); BLT_ASSERT(index < 3);
} }
@ -91,7 +91,7 @@ void test_enumerate()
for (const auto& [index, item] : blt::enumerate(array_1).skip(3)) for (const auto& [index, item] : blt::enumerate(array_1).skip(3))
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index >= 3); BLT_ASSERT(index >= 3);
} }
@ -99,7 +99,7 @@ void test_enumerate()
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev()) for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev())
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index >= 3); BLT_ASSERT(index >= 3);
} }
@ -107,7 +107,7 @@ void test_enumerate()
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).take(5)) for (const auto& [index, item] : blt::enumerate(array_1).skip(3).take(5))
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index >= 3 && index < (array_1.size() - 5) + 3); BLT_ASSERT(index >= 3 && index < (array_1.size() - 5) + 3);
} }
@ -115,7 +115,7 @@ void test_enumerate()
for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev().take(5)) for (const auto& [index, item] : blt::enumerate(array_1).skip(3).rev().take(5))
{ {
BLT_TRACE_STREAM << index << " : " << item << "\n"; BLT_TRACE("{}, : {}", index, item);
BLT_ASSERT(index >= 5); BLT_ASSERT(index >= 5);
} }
} }
@ -125,7 +125,7 @@ void test_pairs()
blt::log_box_t box(std::cout, "Pairs Tests", 25); blt::log_box_t box(std::cout, "Pairs Tests", 25);
for (auto [a1, a2] : blt::in_pairs(array_1, array_2)) for (auto [a1, a2] : blt::in_pairs(array_1, array_2))
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << "\n"; BLT_TRACE("{}, : {}", a1, a2);
} }
} }
@ -134,32 +134,32 @@ void test_zip()
blt::log_box_t box(std::cout, "Zip Tests", 25); blt::log_box_t box(std::cout, "Zip Tests", 25);
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1)) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1))
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1).take(3)) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, list_1).take(3))
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take(3).rev()) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take(3).rev())
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take_or(13)) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).take_or(13))
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).rev().take(3)) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).rev().take(3))
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).skip(2).rev()) for (auto [a1, a2, a3] : blt::zip(array_1, array_2, array_3).skip(2).rev())
{ {
BLT_TRACE_STREAM << a1 << " : " << a2 << " : " << a3 << "\n"; BLT_TRACE("{:.4} : {:.4} : {:.4}", a1, a2, a3);
} }
} }
@ -168,34 +168,86 @@ void test_iterate()
blt::log_box_t box(std::cout, "Iterate Tests", 25); blt::log_box_t box(std::cout, "Iterate Tests", 25);
for (auto v : blt::iterate(array_1)) for (auto v : blt::iterate(array_1))
{ {
BLT_TRACE_STREAM << "Element: " << v << "\n"; BLT_TRACE("Element: {:.4f}", v);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto v : blt::iterate(array_1).skip(5)) for (auto v : blt::iterate(array_1).skip(5))
{ {
BLT_TRACE_STREAM << "Element: " << v << "\n"; BLT_TRACE("Element: {:.4f}", v);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto v : blt::iterate(array_1).take(5)) for (auto v : blt::iterate(array_1).take(5))
{ {
BLT_TRACE_STREAM << "Element: " << v << "\n"; BLT_TRACE("Element: {:.4f}", v);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto v : blt::iterate(array_1).rev()) for (auto v : blt::iterate(array_1).rev())
{ {
BLT_TRACE_STREAM << "Element: " << v << "\n"; BLT_TRACE("Element: {:.4f}", v);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [a, b] : blt::iterate(array_1).zip(list_1)) for (auto [a, b] : blt::iterate(array_1).zip(list_1))
{ {
BLT_TRACE_STREAM << "Zip: " << a << " " << b << "\n"; BLT_TRACE("Zip: {:.4f} {:.4f}", a, b);
} }
BLT_TRACE("================================"); BLT_TRACE("================================");
for (auto [i, data] : blt::iterate(array_1).zip(list_1).skip(3).take(4).enumerate()) for (auto [i, data] : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); }).zip(list_1).skip(3).take(4).enumerate())
{ {
auto [a, b] = data; auto [a, b] = data;
BLT_TRACE_STREAM << "Zip (" << i << "): " << a << " " << b << "\n"; BLT_TRACE("Map + Zip + Skip + Take + Enumerate (Index: {})> {:.4f} {:.4f}", i, a, b);
} }
BLT_TRACE("================================");
for (auto [i, data] : blt::iterate(array_1).map(
[](const blt::vec2& in) {
return in.normalize();
}).zip(list_1).skip(3).take(4).enumerate())
{
auto [a, b] = data;
BLT_TRACE("Map + Zip + Skip + Take + Enumerate (Index: {})> {:.4f} {:.4f}", i, a, b);
}
BLT_TRACE("================================");
for (auto a : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); })
.filter([](const blt::vec2& f) { return f.x() > 0.5; }))
{
if (!a)
continue;
auto v = *a;
BLT_TRACE(" So this one works? {:.4f}", v);
}
BLT_TRACE("================================");
for (auto a : blt::iterate(array_1).map([](const blt::vec2& in) { return in.normalize(); })
.enumerate().filter([](const auto& f) { return f.second.x() > 0.5; }))
{
if (!a)
continue;
auto [index, v] = *a;
BLT_TRACE(" So this one works? ({}) {:.4f}", index, v);
}
BLT_TRACE("================================");
// for (auto a : blt::iterate(array_1).filter([](const auto& f) { return f.x() > 3 && f.y() < 6; }).take(2))
// {
// if (!a)
// continue;
// auto v = *a;
// BLT_TRACE_STREAM << " How about this one?? " << v.get() << "\n";
// }
for (auto a : blt::iterate(array_1).map([](const auto& f) { return f.x() > 3 && f.y() < 6; }))
{
BLT_TRACE(" How about this one?? ({}) {:.4f}", a);
}
// for (auto [value, a] : blt::iterate(array_1).map(
// [](const blt::vec2& in) {
// return in.normalize();
// }).enumerate().filter([](const auto& v) {
// return v.index % 2 == 0;
// }).zip(array_2).skip(3))
// {
// if (!value)
// continue;
// auto [i, v] = *value;
// BLT_TRACE_STREAM << "Enumerate Filter (Index: " << i << ")> " << v << "\n";
// }
} }
int main() int main()

229
tests/logger_tests.cpp Normal file
View File

@ -0,0 +1,229 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <chrono>
#include <fstream>
#include <iostream>
#include <sstream>
#include <thread>
#include <blt/fs/filesystem.h>
#include <blt/logging/ansi.h>
#include <blt/logging/logging.h>
#include <blt/logging/status.h>
#include <blt/std/assert.h>
#include <blt/std/utility.h>
struct some_silly_type_t
{};
auto expected_str = std::string(R"(This is a println!
This is a println with args '42'
This is a println with multiple args '42' '32.342311859130859375' 'Hello World!'
This is a 'Well so am I except cooler :3' fmt string with positionals 'I am a string!'
This is a println with a sign +4120
This is a println with a sign -4120
This is a println with a space 4120
This is a println with a space -4120
This is a println with a minus 4120
This is a println with a minus -4120
This is a println with a with 4120
This is a println with a with leading zeros 0000004120
This is a println with a precision 42.2323423490
This is a println with hex 109a
This is a println with hex with leading 0x109a
This is a println with binary 0b00110010000110100101011000000000
This is a println with binary with space 0b10110010 00011010 01010110 00000000
This is a println with binary with space 10100010 00000000 00000000 00000000
This is a println with octal 015015
This is a println with hexfloat 0x1.926e978d4fdf4p+8
This is a println with exponent 4.4320902431999996e+07
This is a println with exponent 9.5324342340423400e+15
This is a println with general 953243.49
This is a println with general 9.532433240234033e+17
This is a println with a char B
This is a println with type some_silly_type_t
This is a println with boolean true
This is a println with boolean as int 0
This is a println with boolean as hex 0x1
This is a println with boolean as octal 1
This is a println with alignment left 64 end value
This is a println with alignment right 46 end value
This is a println with alignment left (fill) 46******** end value
This is a println with alignment right (fill) ********46 end value
This is a println with alignment right (fill with reserved character) ^^^^^^^^46 end value
This is a println with fill no alignment %%%%%%%%%%%%%%%%%%46 end value
This is a println with arg reference 46.02
This is a println with arg reference &&&&&&&&&&&&&&&&&&&&
)");
std::pair<bool, std::string> compare_strings(const std::string& s1, const std::string& s2)
{
const auto size = std::min(s1.size(), s2.size());
size_t index = 0;
for (; index < size; ++index)
{
if (s1[index] != s2[index])
{
std::stringstream ss;
const auto i1 = std::max(static_cast<blt::i64>(index) - 32, 0l);
const auto l1 = std::min(static_cast<blt::i64>(size) - i1, 65l);
ss << "Strings differ at index " << index << "!\n";
ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl;
return {false, ss.str()};
}
}
if (s1.size() != s2.size())
return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"};
return {true, ""};
}
int main()
{
std::stringstream ss;
blt::logging::println(ss, "This is a println!");
blt::logging::println(ss, "This is a println with args '{}'", 42);
blt::logging::println(ss, "This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!");
blt::logging::println(ss, "This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
blt::logging::println(ss, "This is a println with a sign {:+}", 4120);
blt::logging::println(ss, "This is a println with a sign {:+}", -4120);
blt::logging::println(ss, "This is a println with a space {: }", 4120);
blt::logging::println(ss, "This is a println with a space {: }", -4120);
blt::logging::println(ss, "This is a println with a minus {:-}", 4120);
blt::logging::println(ss, "This is a println with a minus {:-}", -4120);
blt::logging::println(ss, "This is a println with a with {:10}", 4120);
blt::logging::println(ss, "This is a println with a with leading zeros {:010}", 4120);
blt::logging::println(ss, "This is a println with a precision {:.10f}", 42.232342349);
blt::logging::println(ss, "This is a println with hex {:.10x}", 4250);
blt::logging::println(ss, "This is a println with hex with leading {:#.10x}", 4250);
blt::logging::println(ss, "This is a println with binary {:#b}", 6969420);
blt::logging::println(ss, "This is a println with binary with space {: #b}", 6969421);
blt::logging::println(ss, "This is a println with binary with space {: b}", 69);
blt::logging::println(ss, "This is a println with octal {:#o}", 6669);
blt::logging::println(ss, "This is a println with hexfloat {:a}", 402.4320);
blt::logging::println(ss, "This is a println with exponent {:e}", 44320902.4320);
blt::logging::println(ss, "This is a println with exponent {:e}", 9532434234042340.0);
blt::logging::println(ss, "This is a println with general {:g}", 953243.49);
blt::logging::println(ss, "This is a println with general {:g}", 953243324023403240.49);
blt::logging::println(ss, "This is a println with a char {:c}", 66);
blt::logging::println(ss, "This is a println with type {:t}", some_silly_type_t{});
blt::logging::println(ss, "This is a println with boolean {}", true);
blt::logging::println(ss, "This is a println with boolean as int {:d}", false);
blt::logging::println(ss, "This is a println with boolean as hex {:#x}", true);
blt::logging::println(ss, "This is a println with boolean as octal {:o}", true);
blt::logging::println(ss, "This is a println with alignment left {:<10} end value", 64);
blt::logging::println(ss, "This is a println with alignment right {:>10} end value", 46);
blt::logging::println(ss, "This is a println with alignment left (fill) {:*<10} end value", 46);
blt::logging::println(ss, "This is a println with alignment right (fill) {:*>10} end value", 46);
blt::logging::println(ss, "This is a println with alignment right (fill with reserved character) {:\\^>10} end value", 46);
blt::logging::println(ss, "This is a println with fill no alignment {:%20} end value", 46);
blt::logging::println(ss, "This is a println with arg reference {0:{1}.{2}f}", 46.0232, 20, 2);
blt::logging::println(ss, "This is a println with arg reference {0:&{1}}", "", 20);
// blt::logging::print(ss.str());
auto [passed, error_msg] = compare_strings(expected_str, ss.str());
BLT_ASSERT_MSG(passed && "Logger logged string doesn't match precomputed expected string!", error_msg.c_str());
namespace ansi = blt::logging::ansi;
namespace color = ansi::color;
// for (blt::u8 r = 0; r < 6; r++)
// {
// for (blt::u8 g = 0; g < 6; g++)
// {
// for (blt::u8 b = 0; b < 6; b++)
// {
// blt::logging::println("{}This is a println with a color {:#3x} {:#3x} {:#3x}{}",
// build(fg(color::color256{r, g, b}), bg(color::color256{
// static_cast<unsigned char>(5 - r),
// static_cast<unsigned char>(5 - g),
// static_cast<unsigned char>(5 - b)
// })), r, g, b, build(color::color_mode::RESET_ALL));
// }
// }
// }
// blt::logging::println("{}This is a color now with background{}",
// build(color::color_mode::BOLD, fg(color::color8::RED), color::color_mode::DIM, bg(color::color_rgb(0, 100, 255))),
// build(color::color_mode::RESET_ALL));
std::ofstream os("test.txt");
blt::fs::fstream_writer_t wtr(os);
blt::fs::writer_string_wrapper_t writer(wtr);
writer.write("This is a println with a stream\n");
writer.write("This is a mixed print ");
writer.write(std::to_string(25));
writer.write(" with multiple types ");
writer.write(std::to_string(34.23340));
writer.write('\n');
writer.write("What about just a new line character?\n");
// blt::logging::println("Logged {} characters", charCount);
//
// BLT_TRACE("Hello this is am empty trace!");
// BLT_TRACE("This is a trace with data {} {} {}", "bad at code", 413, "boy");
//
// BLT_DEBUG("This is complete? {}", "this is working!");
// BLT_INFO("Hello there!");
// BLT_WARN("This is a warning!");
// BLT_ERROR("This is an error!");
// BLT_FATAL("This is a fatal error!");
// BLT_TRACE("This is a pointer {:f}", &charCount);
//
// BLT_TRACE("Now time to test the logger status box");
blt::logging::status_progress_bar_t progress;
blt::logging::status_bar_t status;
status.add(progress);
blt::logging::get_global_config().add_injector(status);
progress.set_progress(1.0 / 103.0);
BLT_TRACE("Hello There!");
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
progress.set_progress(2.0 / 103.0);
BLT_TRACE("I am printing stuff!");
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
progress.set_progress(3.0 / 103.0);
BLT_TRACE("How are you!?");
for (int i = 0; i < 100; i++)
{
progress.set_progress((4.0 + i) / 103.0);
BLT_INFO("I am printing some output {} times!", i + 1);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
/*std::cout << "\033[2J";
constexpr int totalRows = 24;
// std::cout << "\033[1;" << (totalRows - 1) << "r";
std::cout << use_mode(ansi::mode::color80x25_text);
for (int i = 1; i <= 10; ++i)
{
std::cout << "\033[1;1H";
std::cout << "printed line " << i << std::endl;
std::cout << "\033[" << totalRows << ";1H";
std::cout << "[----status----]" << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "\033[r";*/
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
}

Some files were not shown because too many files have changed in this diff Show More