commit
eba9ecb9e5
|
@ -4,3 +4,6 @@ cmake-build-debug/
|
||||||
/cmake-build-release/
|
/cmake-build-release/
|
||||||
./cmake-build-release/
|
./cmake-build-release/
|
||||||
cmake-build-release/
|
cmake-build-release/
|
||||||
|
cmake-build-relwithdebinfo/
|
||||||
|
cmake-build-reldebug-asan/
|
||||||
|
./cmake-build-relwithdebinfo/
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(BLT VERSION 0.6.0)
|
project(BLT VERSION 0.8.1)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
|
||||||
|
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
|
||||||
|
option(ENABLE_TSAN "Enable the thread data race sanitizer" 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_NBT "Build the BLT NBT + eNBT extension" ON)
|
option(BUILD_NBT "Build the BLT NBT + eNBT extension" ON)
|
||||||
|
option(BUILD_PARSE "Build the BLT parsers" ON)
|
||||||
option(BUILD_TESTS "Build the BLT test set" OFF)
|
option(BUILD_TESTS "Build the BLT test set" OFF)
|
||||||
option(BLT_DISABLE_LOGGING "Disable blt::logging (all macros and will safely disable logging function!)" OFF)
|
option(BLT_DISABLE_LOGGING "Disable blt::logging (all macros and will safely disable logging function!)" OFF)
|
||||||
option(BLT_DISABLE_TRACE "Disable blt::logging BLT_TRACE macro" OFF)
|
option(BLT_DISABLE_TRACE "Disable blt::logging BLT_TRACE macro" OFF)
|
||||||
|
@ -35,6 +39,17 @@ else()
|
||||||
set(NBT_FILES "")
|
set(NBT_FILES "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(${BUILD_PARSE})
|
||||||
|
file(GLOB_RECURSE PARSE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/parse/*.cpp")
|
||||||
|
else()
|
||||||
|
set(PARSE_FILES "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
|
||||||
|
message("Found Parallel Hashmaps")
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
|
||||||
|
endif()
|
||||||
|
|
||||||
#include zlib if the user has it.
|
#include zlib if the user has it.
|
||||||
find_package(ZLIB QUIET)
|
find_package(ZLIB QUIET)
|
||||||
|
|
||||||
|
@ -52,18 +67,22 @@ message("Profiler Files ${PROFILING_FILES}")
|
||||||
message("Source: ${CMAKE_SOURCE_DIR}")
|
message("Source: ${CMAKE_SOURCE_DIR}")
|
||||||
message("Current Source: ${CMAKE_CURRENT_SOURCE_DIR}")
|
message("Current Source: ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
add_library(BLT ${STD_FILES} ${PROFILING_FILES} ${NBT_FILES})
|
add_library(BLT ${STD_FILES} ${PROFILING_FILES} ${NBT_FILES} ${PARSE_FILES})
|
||||||
|
|
||||||
target_include_directories(BLT PUBLIC include/)
|
target_include_directories(BLT PUBLIC include/)
|
||||||
target_include_directories(BLT PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/config/)
|
target_include_directories(BLT PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/config/)
|
||||||
if(${ZLIB_FOUND})
|
if(${ZLIB_FOUND})
|
||||||
target_link_libraries(BLT PUBLIC ZLIB::ZLIB)
|
target_link_libraries(BLT PUBLIC ZLIB::ZLIB)
|
||||||
endif()
|
endif()
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
|
||||||
|
message("Including phmap")
|
||||||
|
target_include_directories(BLT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libraries/parallel-hashmap)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
#target_compile_options(BLT PRIVATE /W4)
|
#target_compile_options(BLT PRIVATE /W4)
|
||||||
else()
|
else()
|
||||||
# perhaps we should warn on usused variables, but BLT will have lots of them.
|
# perhaps we should warn on unused variables, but BLT will have lots of them.
|
||||||
target_compile_options(BLT PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(BLT PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -72,11 +91,6 @@ message("BLT ${CMAKE_PROJECT_VERSION} Successfully included!")
|
||||||
if(${BUILD_TESTS})
|
if(${BUILD_TESTS})
|
||||||
project(BLT_TESTS)
|
project(BLT_TESTS)
|
||||||
|
|
||||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug AND LINUX)
|
|
||||||
add_compile_options(-fsanitize=address)
|
|
||||||
add_link_options(-fsanitize=address)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(GLOB_RECURSE TESTS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/*.cpp")
|
file(GLOB_RECURSE TESTS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/*.cpp")
|
||||||
|
|
||||||
add_executable(BLT_TESTS ${TESTS_FILES})
|
add_executable(BLT_TESTS ${TESTS_FILES})
|
||||||
|
@ -92,5 +106,20 @@ if(${BUILD_TESTS})
|
||||||
target_compile_options(BLT_TESTS PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(BLT_TESTS PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||||
|
target_compile_options(BLT_TESTS PRIVATE -fsanitize=address)
|
||||||
|
target_link_options(BLT_TESTS PRIVATE -fsanitize=address)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (${ENABLE_UBSAN} MATCHES ON)
|
||||||
|
target_compile_options(BLT_TESTS PRIVATE -fsanitize=undefined)
|
||||||
|
target_link_options(BLT_TESTS PRIVATE -fsanitize=undefined)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (${ENABLE_TSAN} MATCHES ON)
|
||||||
|
target_compile_options(BLT_TESTS PRIVATE -fsanitize=thread)
|
||||||
|
target_link_options(BLT_TESTS PRIVATE -fsanitize=thread)
|
||||||
|
endif ()
|
||||||
|
|
||||||
message("BLT tests included!")
|
message("BLT tests included!")
|
||||||
endif()
|
endif()
|
||||||
|
|
23
README.md
23
README.md
|
@ -1,5 +1,5 @@
|
||||||
# **BLT v0.6.0a**
|
# **BLT v0.8.0a**
|
||||||
A common utilities library I find useful
|
A C++20 common utilities library to make thing easy!
|
||||||
|
|
||||||
![Icon](icon_large.png)
|
![Icon](icon_large.png)
|
||||||
|
|
||||||
|
@ -26,11 +26,10 @@ If you are using BLT as a CMake library (as you should!) this is done for you.
|
||||||
- ## Data Structures
|
- ## Data Structures
|
||||||
- Queue / Stack
|
- Queue / Stack
|
||||||
- faster than std::queue / std::stack
|
- faster than std::queue / std::stack
|
||||||
- Binary Tree
|
- backed by a contiguous array
|
||||||
- Hashmap (TODO)
|
|
||||||
- ## Utility
|
- ## Utility
|
||||||
- Simple Random Interface
|
- Simple Random Wrapper Interface
|
||||||
- No more worrying about min/max bounds!
|
- Simple random functions based on the PCG Hash
|
||||||
- ### String Functions
|
- ### String Functions
|
||||||
- starts_with
|
- starts_with
|
||||||
- ends_with
|
- ends_with
|
||||||
|
@ -40,14 +39,12 @@ If you are using BLT as a CMake library (as you should!) this is done for you.
|
||||||
- split
|
- split
|
||||||
- trim
|
- trim
|
||||||
- Logging
|
- Logging
|
||||||
- Trace / Debug / Info / Warn / Error / Fatal
|
- See blt::logging section above
|
||||||
- Log to file
|
|
||||||
- Log to console with color!
|
|
||||||
- Easy to disable for release
|
|
||||||
- define BLT_DISABLE_LOGGING before including the logging.h file.
|
|
||||||
- Time
|
- Time
|
||||||
- Current time in nanoseconds (without all the c++ gobbledygook)
|
- Current time in nanoseconds (without all the c++ gobbledygook)
|
||||||
|
- Java's currentTimeMilliseconds
|
||||||
|
- nanoTime as well
|
||||||
|
- `std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()` becomes `blt::system::nanoTime()`
|
||||||
- Formatted time string with year/month/date + current time
|
- Formatted time string with year/month/date + current time
|
||||||
- ## Profiling
|
- ## Profiling
|
||||||
- Basic profiler
|
- Basic profiler with history and formatted output
|
||||||
- WIP
|
|
|
@ -20,6 +20,8 @@ namespace blt {
|
||||||
return ((seed * (seed * seed * 15731 + 789221) + 1376312589) & 0x7fffffff);
|
return ((seed * (seed * seed * 15731 + 789221) + 1376312589) & 0x7fffffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||||
/**
|
/**
|
||||||
* fast inverse sqrt
|
* fast inverse sqrt
|
||||||
*/
|
*/
|
||||||
|
@ -36,6 +38,28 @@ namespace blt {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
||||||
|
static inline constexpr double pow(int b, int p) {
|
||||||
|
int collection = 1;
|
||||||
|
for (int i = 0; i < p; i++)
|
||||||
|
collection *= b;
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a fast rounding function and is not guaranteed to be 100% correct
|
||||||
|
* @tparam decimal_places
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template<int decimal_places>
|
||||||
|
static inline double round_up(double value) {
|
||||||
|
constexpr double multiplier = pow(10, decimal_places);
|
||||||
|
return ((int)(value * multiplier) + 1) / multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
/*inline std::ostream& operator<<(std::ostream& out, const mat4x4& v) {
|
/*inline std::ostream& operator<<(std::ostream& out, const mat4x4& v) {
|
||||||
return out << "\rMatrix4x4{" << v.m00() << ", " << v.m01() << ", " << v.m02() << ", " << v.m03() << "} \n"\
|
return out << "\rMatrix4x4{" << v.m00() << ", " << v.m01() << ", " << v.m02() << ", " << v.m03() << "} \n"\
|
||||||
<< " {" << v.m10() << ", " << v.m11() << ", " << v.m12() << ", " << v.m13() << "} \n"\
|
<< " {" << v.m10() << ", " << v.m11() << ", " << v.m12() << ", " << v.m13() << "} \n"\
|
||||||
|
|
|
@ -7,53 +7,460 @@
|
||||||
#ifndef BLT_TESTS_NBT_H
|
#ifndef BLT_TESTS_NBT_H
|
||||||
#define BLT_TESTS_NBT_H
|
#define BLT_TESTS_NBT_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <bit>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "blt/std/format.h"
|
#include "blt/std/format.h"
|
||||||
#include "blt/std/filesystem.h"
|
#include "blt/std/filesystem.h"
|
||||||
|
#include "blt/std/logging.h"
|
||||||
|
|
||||||
|
#include <blt/std/hashmap.h>
|
||||||
|
|
||||||
namespace blt::nbt {
|
namespace blt::nbt {
|
||||||
void writeUTF8String(std::fstream& stream, const std::string& str);
|
|
||||||
|
|
||||||
std::string readUTF8String(std::fstream& stream);
|
void writeUTF8String(blt::fs::block_writer& stream, const std::string& str);
|
||||||
|
|
||||||
enum nbt_type {
|
std::string readUTF8String(blt::fs::block_reader& stream);
|
||||||
tag_end = 0,
|
|
||||||
tag_byte = 1,
|
// Used to grab the byte-data of any T element. Defaults to Big Endian, however can be configured to use little endian
|
||||||
tag_short = 2,
|
template <typename T>
|
||||||
tag_int = 3,
|
inline static int toBytes(const T& in, char* out) {
|
||||||
tag_long = 4,
|
std::memcpy(out, (void*) &in, sizeof(T));
|
||||||
tag_float = 5,
|
|
||||||
tag_double = 6,
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
tag_byte_array = 7,
|
for (size_t i = 0; i < sizeof(T) / 2; i++)
|
||||||
tag_string = 8,
|
std::swap(out[i], out[sizeof(T) - 1 - i]);
|
||||||
tag_list = 9,
|
}
|
||||||
tag_compound = 10,
|
|
||||||
tag_int_array = 11,
|
return 0;
|
||||||
tag_long_array = 12
|
}
|
||||||
|
|
||||||
|
// Used to cast the binary data of any T object, into a T object.
|
||||||
|
template <typename T>
|
||||||
|
inline static int fromBytes(const char* in, T* out) {
|
||||||
|
memcpy(out, in, sizeof(T));
|
||||||
|
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
for (size_t i = 0; i < sizeof(T) / 2; i++)
|
||||||
|
std::swap(((char*) (out))[i], ((char*) (out))[sizeof(T) - 1 - i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline static void writeData(blt::fs::block_writer& out, const T& d){
|
||||||
|
char data[sizeof(T)];
|
||||||
|
toBytes(d, data);
|
||||||
|
out.write(data, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline static void readData(blt::fs::block_reader& in, T& d) {
|
||||||
|
char data[sizeof(T)];
|
||||||
|
in.read(data, sizeof(T));
|
||||||
|
fromBytes(data, &d);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class nbt_tag : char {
|
||||||
|
END = 0,
|
||||||
|
BYTE = 1,
|
||||||
|
SHORT = 2,
|
||||||
|
INT = 3,
|
||||||
|
LONG = 4,
|
||||||
|
FLOAT = 5,
|
||||||
|
DOUBLE = 6,
|
||||||
|
BYTE_ARRAY = 7,
|
||||||
|
STRING = 8,
|
||||||
|
LIST = 9,
|
||||||
|
COMPOUND = 10,
|
||||||
|
INT_ARRAY = 11,
|
||||||
|
LONG_ARRAY = 12
|
||||||
};
|
};
|
||||||
|
|
||||||
class nbt_tag {
|
class tag_t {
|
||||||
|
protected:
|
||||||
|
nbt_tag type;
|
||||||
|
std::string name;
|
||||||
public:
|
public:
|
||||||
virtual void readTag() = 0;
|
explicit tag_t(nbt_tag type): type(type) {};
|
||||||
virtual void writeTag() = 0;
|
explicit tag_t(nbt_tag type, std::string name): type(type), name(std::move(name)) {}
|
||||||
|
virtual void writePayload(blt::fs::block_writer& out) = 0;
|
||||||
|
virtual void readPayload(blt::fs::block_reader& in) = 0;
|
||||||
|
void writeName(blt::fs::block_writer& out) {
|
||||||
|
writeUTF8String(out, name);
|
||||||
|
}
|
||||||
|
void readName(blt::fs::block_reader& in) {
|
||||||
|
name = readUTF8String(in);
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline nbt_tag getType() const {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline const std::string& getName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
virtual ~tag_t() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NBTDecoder {
|
template<typename T>
|
||||||
private:
|
class tag : public tag_t {
|
||||||
blt::fs::block_reader* m_reader;
|
protected:
|
||||||
|
T t;
|
||||||
public:
|
public:
|
||||||
explicit NBTDecoder(blt::fs::block_reader* reader): m_reader(reader) {}
|
explicit tag(nbt_tag type): tag_t(type) {}
|
||||||
|
tag(nbt_tag type, std::string name, T t): tag_t(type, std::move(name)), t(std::move(t)) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) override {
|
||||||
|
if constexpr(std::is_arithmetic<T>::value)
|
||||||
|
writeData(out, t);
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) override {
|
||||||
|
if constexpr(std::is_arithmetic<T>::value)
|
||||||
|
readData(in, t);
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline const T& get() const {return t;}
|
||||||
|
inline T& get() {return t;}
|
||||||
|
~tag() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class tag_end : public tag<char> {
|
||||||
* Reads the entire NBT file when the read() function is called.
|
public:
|
||||||
*/
|
void writePayload(blt::fs::block_writer&) final {}
|
||||||
|
// nothing to read
|
||||||
|
void readPayload(blt::fs::block_reader&) final {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_byte : public tag<int8_t> {
|
||||||
|
public:
|
||||||
|
tag_byte(): tag(nbt_tag::BYTE) {}
|
||||||
|
tag_byte(const std::string& name, int8_t b): tag(nbt_tag::BYTE, name, b) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_short : public tag<int16_t> {
|
||||||
|
public:
|
||||||
|
tag_short(): tag(nbt_tag::SHORT) {}
|
||||||
|
tag_short(const std::string& name, int16_t s): tag(nbt_tag::SHORT, name, s) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_int : public tag<int32_t> {
|
||||||
|
public:
|
||||||
|
tag_int(): tag(nbt_tag::INT) {}
|
||||||
|
tag_int(const std::string& name, int32_t i): tag(nbt_tag::INT, name, i) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_long : public tag<int64_t> {
|
||||||
|
public:
|
||||||
|
tag_long(): tag(nbt_tag::LONG) {}
|
||||||
|
tag_long(const std::string& name, int64_t l): tag(nbt_tag::LONG, name, l) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_float : public tag<float> {
|
||||||
|
public:
|
||||||
|
tag_float(): tag(nbt_tag::FLOAT) {}
|
||||||
|
tag_float(const std::string& name, float f): tag(nbt_tag::FLOAT, name, f) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_double : public tag<double> {
|
||||||
|
public:
|
||||||
|
tag_double(): tag(nbt_tag::DOUBLE) {}
|
||||||
|
tag_double(const std::string& name, double d): tag(nbt_tag::DOUBLE, name, d) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_byte_array : public tag<std::vector<int8_t>> {
|
||||||
|
public:
|
||||||
|
tag_byte_array(): tag(nbt_tag::BYTE_ARRAY) {}
|
||||||
|
tag_byte_array(const std::string& name, const std::vector<int8_t>& v): tag(nbt_tag::BYTE_ARRAY, name, v) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
auto length = (int32_t) t.size();
|
||||||
|
writeData(out, length);
|
||||||
|
// TODO on the writer (remove need for cast + more std::fstream functions)
|
||||||
|
out.write(reinterpret_cast<char*>(t.data()), length);
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
int32_t length;
|
||||||
|
readData(in, length);
|
||||||
|
t.reserve(length);
|
||||||
|
in.read(reinterpret_cast<char*>(t.data()), length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_string : public tag<std::string> {
|
||||||
|
public:
|
||||||
|
tag_string(): tag(nbt_tag::STRING) {}
|
||||||
|
tag_string(const std::string& name, const std::string& s): tag(nbt_tag::STRING, name, s) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
writeUTF8String(out, t);
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
t = readUTF8String(in);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_int_array : public tag<std::vector<int32_t>> {
|
||||||
|
public:
|
||||||
|
tag_int_array(): tag(nbt_tag::INT_ARRAY) {}
|
||||||
|
tag_int_array(const std::string& name, const std::vector<int32_t>& v): tag(nbt_tag::INT_ARRAY, name, v) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
auto length = (int32_t) t.size();
|
||||||
|
writeData(out, length);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
writeData(out, t[i]);
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
int32_t length;
|
||||||
|
readData(in, length);
|
||||||
|
t.reserve(length);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
readData(in, t[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_long_array : public tag<std::vector<int64_t>> {
|
||||||
|
public:
|
||||||
|
tag_long_array(): tag(nbt_tag::LONG_ARRAY) {}
|
||||||
|
tag_long_array(const std::string& name, const std::vector<int64_t>& v): tag(nbt_tag::LONG_ARRAY, name, v) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
auto length = (int32_t) t.size();
|
||||||
|
writeData(out, length);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
writeData(out, t[i]);
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
int32_t length;
|
||||||
|
readData(in, length);
|
||||||
|
t.reserve(length);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
readData(in, t[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BLT_NBT_POPULATE_VEC(type, vec, length) for (int i = 0; i < length; i++) vec.push_back(type);
|
||||||
|
|
||||||
|
namespace _internal_ {
|
||||||
|
// EVIL HACK
|
||||||
|
static tag_t* newCompound();
|
||||||
|
static tag_t* newList();
|
||||||
|
static tag_t* toType(char id){
|
||||||
|
switch ((nbt_tag) id) {
|
||||||
|
case nbt_tag::END:
|
||||||
|
return nullptr;
|
||||||
|
break;
|
||||||
|
case nbt_tag::BYTE:
|
||||||
|
return new blt::nbt::tag_byte;
|
||||||
|
case nbt_tag::SHORT:
|
||||||
|
return new blt::nbt::tag_short;
|
||||||
|
case nbt_tag::INT:
|
||||||
|
return new blt::nbt::tag_int;
|
||||||
|
case nbt_tag::LONG:
|
||||||
|
return new blt::nbt::tag_long;
|
||||||
|
case nbt_tag::FLOAT:
|
||||||
|
return new blt::nbt::tag_float;
|
||||||
|
case nbt_tag::DOUBLE:
|
||||||
|
return new blt::nbt::tag_double;
|
||||||
|
case nbt_tag::BYTE_ARRAY:
|
||||||
|
return new blt::nbt::tag_byte_array;
|
||||||
|
case nbt_tag::STRING:
|
||||||
|
return new blt::nbt::tag_string;
|
||||||
|
case nbt_tag::LIST:
|
||||||
|
return _internal_::newList();
|
||||||
|
case nbt_tag::COMPOUND:
|
||||||
|
return _internal_::newCompound();
|
||||||
|
case nbt_tag::INT_ARRAY:
|
||||||
|
return new blt::nbt::tag_int_array;
|
||||||
|
case nbt_tag::LONG_ARRAY:
|
||||||
|
return new blt::nbt::tag_long_array;
|
||||||
|
}
|
||||||
|
BLT_WARN("Tag Type not found!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
static HASHMAP<std::string, tag_t*> toHashmap(const std::vector<tag_t*>& v){
|
||||||
|
HASHMAP<std::string, tag_t*> tags;
|
||||||
|
for (const auto& t : v)
|
||||||
|
tags[t->getName()] = t;
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class tag_list : public tag<std::vector<tag_t*>> {
|
||||||
|
public:
|
||||||
|
tag_list(): tag(nbt_tag::LIST) {}
|
||||||
|
tag_list(const std::string& name, const std::vector<tag_t*>& v): tag(nbt_tag::LIST, name, v) {}
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
if (t.empty())
|
||||||
|
writeData(out, (char)nbt_tag::END);
|
||||||
|
else
|
||||||
|
writeData(out, (char)t[0]->getType());
|
||||||
|
auto length = (int32_t) t.size();
|
||||||
|
writeData(out, length);
|
||||||
|
for (const auto& v : t)
|
||||||
|
v->writePayload(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
char id;
|
||||||
|
int32_t length;
|
||||||
|
readData(in, id);
|
||||||
|
readData(in, length);
|
||||||
|
if (length == 0 || id == 0)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
auto v = _internal_::toType(id);
|
||||||
|
v->readPayload(in);
|
||||||
|
t.push_back(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void put(tag_t* tag) {
|
||||||
|
t.push_back(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline tag_t*& operator[](size_t i){
|
||||||
|
return t[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] T* getTag(size_t i){
|
||||||
|
if constexpr (!std::is_base_of<tag_t, T>::value) {
|
||||||
|
static_assert("WARNING: provided type isn't of type tag. Cannot cast expression!");
|
||||||
|
BLT_WARN("You have requested an invalid type. Please use types of tag_t when using getTag");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& tag = t[i];
|
||||||
|
T t;
|
||||||
|
if (tag->getType() != t.getType()) {
|
||||||
|
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||||
|
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||||
|
}
|
||||||
|
return dynamic_cast<T*>(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline size_t size() const {
|
||||||
|
return t.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
~tag_list() override {
|
||||||
|
for (auto* p : t)
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tag_compound : public tag<HASHMAP<std::string, tag_t*>> {
|
||||||
|
public:
|
||||||
|
tag_compound(): tag(nbt_tag::COMPOUND) {}
|
||||||
|
tag_compound(const std::string& name, const std::vector<tag_t*>& v): tag(nbt_tag::COMPOUND, name, _internal_::toHashmap(v)) {}
|
||||||
|
tag_compound(const std::string& name, const std::initializer_list<tag_t*>& v): tag(nbt_tag::COMPOUND, name, _internal_::toHashmap(v)) {}
|
||||||
|
tag_compound(const std::string& name, const HASHMAP<std::string, tag_t*>& v): tag(nbt_tag::COMPOUND, name, v) {}
|
||||||
|
|
||||||
|
inline void put(tag_t* tag) {
|
||||||
|
t[tag->getName()] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] T* getTag(const std::string& name){
|
||||||
|
if constexpr (!std::is_base_of<tag_t, T>::value) {
|
||||||
|
static_assert("WARNING: provided type isn't of type tag. Cannot cast expression!");
|
||||||
|
BLT_WARN("You have requested an invalid type. Please use types of tag_t when using getTag");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& tag = t[name];
|
||||||
|
T t;
|
||||||
|
if (tag->getType() != t.getType()) {
|
||||||
|
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||||
|
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||||
|
}
|
||||||
|
return dynamic_cast<T*>(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline tag_t*& operator[](const std::string& name){
|
||||||
|
return t[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator()(tag_t* tag){
|
||||||
|
t[tag->getName()] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePayload(blt::fs::block_writer& out) final {
|
||||||
|
for (const auto& v : t){
|
||||||
|
auto tag = v.second;
|
||||||
|
out.put((char) tag->getType());
|
||||||
|
tag->writeName(out);
|
||||||
|
tag->writePayload(out);
|
||||||
|
}
|
||||||
|
out.put('\0');
|
||||||
|
}
|
||||||
|
void readPayload(blt::fs::block_reader& in) final {
|
||||||
|
char type;
|
||||||
|
while ((type = in.get()) != (char)nbt_tag::END){
|
||||||
|
auto* v = _internal_::toType(type);
|
||||||
|
v->readName(in);
|
||||||
|
v->readPayload(in);
|
||||||
|
t[v->getName()] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~tag_compound() override {
|
||||||
|
for (auto& v : t)
|
||||||
|
delete v.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static tag_t* _internal_::newCompound(){
|
||||||
|
return new blt::nbt::tag_compound;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tag_t* _internal_::newList() {
|
||||||
|
return new blt::nbt::tag_list;
|
||||||
|
}
|
||||||
|
|
||||||
class NBTReader {
|
class NBTReader {
|
||||||
private:
|
private:
|
||||||
std::string m_file;
|
blt::fs::block_reader& reader;
|
||||||
|
tag_compound* root = nullptr;
|
||||||
public:
|
public:
|
||||||
explicit NBTReader(std::string file): m_file(std::move(file)) {}
|
explicit NBTReader(blt::fs::block_reader& reader): reader(reader) {}
|
||||||
|
|
||||||
|
void read();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] T* getTag(const std::string& name){
|
||||||
|
if constexpr (!std::is_base_of<tag_t, T>::value) {
|
||||||
|
static_assert("WARNING: provided type isn't of type tag. Cannot cast expression!");
|
||||||
|
BLT_WARN("You have requested an invalid type. Please use types of tag_t when using getTag");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& tag = root->get()[name];
|
||||||
|
T t;
|
||||||
|
if (tag->getType() != t.getType()) {
|
||||||
|
BLT_WARN("Expected tag of type %d but got tag of type %d", (char)t.getType(), (char)tag->getType());
|
||||||
|
throw std::runtime_error("Requested Tag does not match stored type!");
|
||||||
|
}
|
||||||
|
return dynamic_cast<T*>(tag);
|
||||||
|
}
|
||||||
|
~NBTReader() {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NBTWriter {
|
||||||
|
private:
|
||||||
|
blt::fs::block_writer& writer;
|
||||||
|
public:
|
||||||
|
explicit NBTWriter(blt::fs::block_writer& writer): writer(writer) {}
|
||||||
|
/**
|
||||||
|
* Write a compound tag and then DELETES the tag. If you don't wish for the memory to be freed, please use the reference version!
|
||||||
|
* @param root root NBT tag to write, this function assumes ownership of this pointer.
|
||||||
|
*/
|
||||||
|
void write(tag_compound* root){
|
||||||
|
write(*root);
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
void write(tag_compound& root){
|
||||||
|
writer.put((char)nbt_tag::COMPOUND);
|
||||||
|
root.writeName(writer);
|
||||||
|
root.writePayload(writer);
|
||||||
|
}
|
||||||
|
~NBTWriter() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
* Created by Brett on 06/08/23.
|
||||||
|
* Licensed under GNU General Public License V3.0
|
||||||
|
* See LICENSE file for license detail
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_TESTS_ARGPARSE_H
|
||||||
|
#define BLT_TESTS_ARGPARSE_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <optional>
|
||||||
|
#include <blt/std/hashmap.h>
|
||||||
|
#include <variant>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace blt
|
||||||
|
{
|
||||||
|
typedef std::variant<std::string, bool, int32_t> arg_data_internal_t;
|
||||||
|
typedef std::vector<arg_data_internal_t> arg_data_vec_t;
|
||||||
|
typedef std::variant<arg_data_internal_t, arg_data_vec_t> arg_data_t;
|
||||||
|
|
||||||
|
enum class arg_action_t
|
||||||
|
{
|
||||||
|
STORE,
|
||||||
|
STORE_CONST,
|
||||||
|
STORE_TRUE,
|
||||||
|
STORE_FALSE,
|
||||||
|
APPEND,
|
||||||
|
APPEND_CONST,
|
||||||
|
COUNT,
|
||||||
|
HELP,
|
||||||
|
VERSION,
|
||||||
|
EXTEND
|
||||||
|
};
|
||||||
|
|
||||||
|
class arg_vector_t
|
||||||
|
{
|
||||||
|
friend class arg_parse;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> flags;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
void validateFlags();
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_vector_t(std::vector<std::string> flags): flags(std::move(flags))
|
||||||
|
{
|
||||||
|
validateFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_vector_t(std::initializer_list<std::string> flags): flags(flags)
|
||||||
|
{
|
||||||
|
validateFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_vector_t(const char* str);
|
||||||
|
|
||||||
|
arg_vector_t(const std::string& str);
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool isFlag() const
|
||||||
|
{
|
||||||
|
return !flags.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool contains(const std::string& str)
|
||||||
|
{
|
||||||
|
return std::any_of(
|
||||||
|
flags.begin(), flags.end(), [&str](const std::string& flag) -> bool {
|
||||||
|
return flag == str;
|
||||||
|
}
|
||||||
|
) || str == name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the first flag that starts with '--' otherwise return the first '-' flag
|
||||||
|
[[nodiscard]] std::string getFirstFullFlag();
|
||||||
|
};
|
||||||
|
|
||||||
|
class arg_nargs_t
|
||||||
|
{
|
||||||
|
friend class arg_parse;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int UNKNOWN = 0x1;
|
||||||
|
static constexpr int ALL = 0x2;
|
||||||
|
static constexpr int ALL_REQUIRED = 0x4;
|
||||||
|
// 0 means ignore
|
||||||
|
int args = 1;
|
||||||
|
// 0 indicates args is used
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
void decode(char c);
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_nargs_t() = default;
|
||||||
|
|
||||||
|
arg_nargs_t(int args): args(args)
|
||||||
|
{}
|
||||||
|
|
||||||
|
arg_nargs_t(char c);
|
||||||
|
|
||||||
|
arg_nargs_t(std::string s);
|
||||||
|
|
||||||
|
arg_nargs_t(const char* s);
|
||||||
|
|
||||||
|
[[nodiscard]] bool takesArgs() const
|
||||||
|
{
|
||||||
|
return args > 0 || flags > 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arg_properties_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
arg_vector_t a_flags;
|
||||||
|
arg_action_t a_action = arg_action_t::STORE;
|
||||||
|
arg_nargs_t a_nargs = 1;
|
||||||
|
std::string a_const{};
|
||||||
|
arg_data_internal_t a_default{};
|
||||||
|
std::string a_dest{};
|
||||||
|
std::string a_help{};
|
||||||
|
std::string a_version{};
|
||||||
|
std::string a_metavar{};
|
||||||
|
bool a_required = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class arg_builder
|
||||||
|
{
|
||||||
|
arg_properties_t properties;
|
||||||
|
public:
|
||||||
|
arg_builder(const arg_vector_t& flags): properties(flags)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline arg_properties_t build()
|
||||||
|
{
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setAction(arg_action_t action)
|
||||||
|
{
|
||||||
|
properties.a_action = action;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setNArgs(const arg_nargs_t& nargs)
|
||||||
|
{
|
||||||
|
properties.a_nargs = nargs;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setConst(const std::string& a_const)
|
||||||
|
{
|
||||||
|
properties.a_const = a_const;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setDefault(const arg_data_internal_t& def)
|
||||||
|
{
|
||||||
|
properties.a_default = def;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setDest(const std::string& dest)
|
||||||
|
{
|
||||||
|
properties.a_dest = dest;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setHelp(const std::string& help)
|
||||||
|
{
|
||||||
|
properties.a_help = help;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setVersion(const std::string& version)
|
||||||
|
{
|
||||||
|
properties.a_version = version;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setMetavar(const std::string& metavar)
|
||||||
|
{
|
||||||
|
properties.a_metavar = metavar;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline arg_builder& setRequired()
|
||||||
|
{
|
||||||
|
properties.a_required = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class arg_tokenizer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<std::string> args;
|
||||||
|
size_t currentIndex = 0;
|
||||||
|
public:
|
||||||
|
arg_tokenizer(std::vector<std::string> args): args(std::move(args))
|
||||||
|
{}
|
||||||
|
|
||||||
|
// returns the current arg
|
||||||
|
inline const std::string& get()
|
||||||
|
{
|
||||||
|
return args[currentIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns if we have next arg to process
|
||||||
|
inline bool hasNext()
|
||||||
|
{
|
||||||
|
return currentIndex + 1 < args.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hasCurrent()
|
||||||
|
{
|
||||||
|
return currentIndex < args.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the current arg is a flag
|
||||||
|
inline bool isFlag()
|
||||||
|
{
|
||||||
|
return args[currentIndex].starts_with('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if we have next and the next arg is a flag
|
||||||
|
inline bool isNextFlag()
|
||||||
|
{
|
||||||
|
return hasNext() && args[currentIndex + 1].starts_with('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
// advances to the next flag
|
||||||
|
inline size_t advance()
|
||||||
|
{
|
||||||
|
return currentIndex++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class arg_parse
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
friend arg_parse;
|
||||||
|
private:
|
||||||
|
std::vector<arg_properties_t*> arg_properties_storage;
|
||||||
|
size_t max_line_length = 80;
|
||||||
|
// TODO: grouping like git's help
|
||||||
|
// pre/postfix applied to the help message
|
||||||
|
std::string prefix;
|
||||||
|
std::string postfix;
|
||||||
|
public:
|
||||||
|
std::vector<arg_properties_t*> name_associations;
|
||||||
|
HASHMAP<std::string, arg_properties_t*> flag_associations;
|
||||||
|
} user_args;
|
||||||
|
|
||||||
|
struct arg_results
|
||||||
|
{
|
||||||
|
friend arg_parse;
|
||||||
|
private:
|
||||||
|
// stores dest value not the flag/name!
|
||||||
|
HASHSET<std::string> found_args;
|
||||||
|
std::vector<std::string> unrecognized_args;
|
||||||
|
public:
|
||||||
|
std::string program_name;
|
||||||
|
HASHMAP<std::string, arg_data_t> data;
|
||||||
|
|
||||||
|
inline arg_data_t& operator[](const std::string& key)
|
||||||
|
{
|
||||||
|
return data[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto begin()
|
||||||
|
{
|
||||||
|
return data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto end()
|
||||||
|
{
|
||||||
|
return data.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool contains(const std::string& key)
|
||||||
|
{
|
||||||
|
return data.find(key) != data.end();
|
||||||
|
}
|
||||||
|
} loaded_args;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string filename(const std::string& path);
|
||||||
|
static std::string getMetavar(const arg_properties_t* const& arg);
|
||||||
|
static std::string getFlagHelp(const arg_properties_t* const& arg);
|
||||||
|
|
||||||
|
static bool takesArgs(const arg_properties_t* const& arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prints out a new line if current line length is greater than max line length, using spacing to generate the next line's
|
||||||
|
* beginning spaces.
|
||||||
|
*/
|
||||||
|
void checkAndPrintFormattingLine(size_t& current_line_length, size_t spacing) const;
|
||||||
|
|
||||||
|
// expects that the current flag has already been consumed (advanced past), leaves tokenizer in a state where the next element is 'current'
|
||||||
|
bool consumeArguments(
|
||||||
|
arg_tokenizer& tokenizer, const std::string& flag, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out
|
||||||
|
) const;
|
||||||
|
|
||||||
|
void handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos);
|
||||||
|
|
||||||
|
void handleFlagArgument(arg_tokenizer& tokenizer);
|
||||||
|
|
||||||
|
void processFlag(arg_tokenizer& tokenizer, const std::string& flag);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline bool holds_alternative(const arg_data_t& v)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, arg_data_vec_t>)
|
||||||
|
return std::holds_alternative<T>(v);
|
||||||
|
else
|
||||||
|
return std::holds_alternative<arg_data_internal_t>(v) && std::holds_alternative<T>(std::get<arg_data_internal_t>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline T& get(arg_data_t& v)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, arg_data_vec_t>)
|
||||||
|
return std::get<arg_data_vec_t>(v);
|
||||||
|
else
|
||||||
|
return std::get<T>(std::get<arg_data_internal_t>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_parse(const std::string& helpMessage = "show this help menu and exit")
|
||||||
|
{
|
||||||
|
addArgument(arg_builder({"--help", "-h"}).setAction(arg_action_t::HELP).setHelp(helpMessage).build());
|
||||||
|
};
|
||||||
|
|
||||||
|
void addArgument(const arg_properties_t& args);
|
||||||
|
|
||||||
|
arg_results parse_args(int argc, const char** argv);
|
||||||
|
|
||||||
|
arg_results parse_args(const std::vector<std::string>& args);
|
||||||
|
|
||||||
|
void printUsage() const;
|
||||||
|
|
||||||
|
void printHelp() const;
|
||||||
|
|
||||||
|
inline void setHelpPrefix(const std::string& str)
|
||||||
|
{
|
||||||
|
user_args.prefix = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setHelpPostfix(const std::string& str)
|
||||||
|
{
|
||||||
|
user_args.postfix = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setMaxLineLength(size_t size)
|
||||||
|
{
|
||||||
|
user_args.max_line_length = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
~arg_parse()
|
||||||
|
{
|
||||||
|
for (auto* p : user_args.arg_properties_storage)
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(const blt::arg_data_t& v);
|
||||||
|
|
||||||
|
std::string to_string(const blt::arg_data_internal_t& v);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_TESTS_ARGPARSE_H
|
|
@ -34,6 +34,11 @@ namespace blt::fs {
|
||||||
* @return status code. non-zero return codes indicates a failure has occurred.
|
* @return status code. non-zero return codes indicates a failure has occurred.
|
||||||
*/
|
*/
|
||||||
virtual int read(char* buffer, size_t bytes) = 0;
|
virtual int read(char* buffer, size_t bytes) = 0;
|
||||||
|
virtual char get(){
|
||||||
|
char c[1];
|
||||||
|
read(c, 1);
|
||||||
|
return c[0];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +57,11 @@ namespace blt::fs {
|
||||||
* @return non-zero code if failure
|
* @return non-zero code if failure
|
||||||
*/
|
*/
|
||||||
virtual int write(char* buffer, size_t bytes) = 0;
|
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.
|
* Ensures that the internal buffer is written to the filesystem.
|
||||||
|
@ -65,11 +75,10 @@ namespace blt::fs {
|
||||||
class fstream_block_reader : public block_reader {
|
class fstream_block_reader : public block_reader {
|
||||||
private:
|
private:
|
||||||
std::fstream& m_stream;
|
std::fstream& m_stream;
|
||||||
char* m_buffer;
|
char* m_buffer = nullptr;
|
||||||
size_t readIndex = 0;
|
size_t readIndex = 0;
|
||||||
public:
|
public:
|
||||||
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize):
|
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize = 131072);
|
||||||
block_reader(bufferSize), m_stream(stream), m_buffer(new char[bufferSize]) {}
|
|
||||||
|
|
||||||
explicit fstream_block_reader(fstream_block_reader& copy) = delete;
|
explicit fstream_block_reader(fstream_block_reader& copy) = delete;
|
||||||
|
|
||||||
|
@ -91,8 +100,9 @@ namespace blt::fs {
|
||||||
std::fstream& m_stream;
|
std::fstream& m_stream;
|
||||||
char* m_buffer;
|
char* m_buffer;
|
||||||
size_t writeIndex = 0;
|
size_t writeIndex = 0;
|
||||||
|
void flush_internal();
|
||||||
public:
|
public:
|
||||||
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize):
|
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize = 131072):
|
||||||
block_writer(bufferSize), m_stream(stream), m_buffer(new char[bufferSize]) {}
|
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& copy) = delete;
|
||||||
|
@ -104,9 +114,12 @@ namespace blt::fs {
|
||||||
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
|
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
|
||||||
|
|
||||||
int write(char* buffer, size_t bytes) override;
|
int write(char* buffer, size_t bytes) override;
|
||||||
void flush() override;
|
inline void flush() override {
|
||||||
|
flush_internal();
|
||||||
|
}
|
||||||
|
|
||||||
~fstream_block_writer() {
|
~fstream_block_writer() {
|
||||||
|
flush_internal();
|
||||||
delete[] m_buffer;
|
delete[] m_buffer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,27 +10,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <blt/math/math.h>
|
||||||
|
|
||||||
namespace blt::string {
|
namespace blt::string {
|
||||||
static inline constexpr double _static_pow(int p) {
|
|
||||||
int collection = 1;
|
|
||||||
for (int i = 0; i < p; i++)
|
|
||||||
collection *= 10;
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a fast rounding function and is not guaranteed to be 100% correct
|
|
||||||
* @tparam decimal_places
|
|
||||||
* @param value
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
template<int decimal_places>
|
|
||||||
static inline double round_up(double value) {
|
|
||||||
constexpr double multiplier = _static_pow(decimal_places);
|
|
||||||
return ((int)(value * multiplier) + 1) / multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline std::string fromBytes(unsigned long bytes){
|
static inline std::string fromBytes(unsigned long bytes){
|
||||||
if (bytes > 1073741824) {
|
if (bytes > 1073741824) {
|
||||||
|
@ -87,8 +69,7 @@ namespace blt::string {
|
||||||
|
|
||||||
// taken from java, adapted for c++.
|
// taken from java, adapted for c++.
|
||||||
static inline utf8_string createUTFString(const std::string& str) {
|
static inline utf8_string createUTFString(const std::string& str) {
|
||||||
|
const auto strlen = (unsigned int) str.size();
|
||||||
const unsigned int strlen = (unsigned int) str.size();
|
|
||||||
unsigned int utflen = strlen;
|
unsigned int utflen = strlen;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < strlen; i++) {
|
for (unsigned int i = 0; i < strlen; i++) {
|
||||||
|
@ -105,8 +86,8 @@ namespace blt::string {
|
||||||
chars.characters = new char[chars.size];
|
chars.characters = new char[chars.size];
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
|
|
||||||
chars.characters[count++] = (char) ((utflen >> 8) & 0xFF);
|
chars.characters[count++] = (char) ((utflen >> 8) & 0xFF);
|
||||||
|
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
|
||||||
|
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
|
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
/*
|
|
||||||
* Created by Brett on 31/03/23.
|
|
||||||
* Licensed under GNU General Public License V3.0
|
|
||||||
* See LICENSE file for license detail
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BLT_HASH_MAP_H
|
|
||||||
#define BLT_HASH_MAP_H
|
|
||||||
|
|
||||||
|
|
||||||
#endif //BLT_HASH_MAP_H
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Created by Brett on 31/03/23.
|
||||||
|
* Licensed under GNU General Public License V3.0
|
||||||
|
* See LICENSE file for license detail
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_HASH_MAP_H
|
||||||
|
#define BLT_HASH_MAP_H
|
||||||
|
|
||||||
|
#ifndef HASHMAP
|
||||||
|
#if defined __has_include && __has_include(<parallel_hashmap/phmap.h>)
|
||||||
|
|
||||||
|
#include <parallel_hashmap/phmap.h>
|
||||||
|
#include <parallel_hashmap/phmap_fwd_decl.h>
|
||||||
|
|
||||||
|
template<class K, class V,
|
||||||
|
class Hash = phmap::priv::hash_default_hash<K>,
|
||||||
|
class Eq = phmap::priv::hash_default_eq<K>,
|
||||||
|
class Alloc = phmap::priv::Allocator<phmap::priv::Pair<const K, V>>>
|
||||||
|
using HASHMAP = phmap::flat_hash_map<K, V, Hash, Eq, Alloc>;
|
||||||
|
template<class T,
|
||||||
|
class Hash = phmap::priv::hash_default_hash<T>,
|
||||||
|
class Eq = phmap::priv::hash_default_eq<T>,
|
||||||
|
class Alloc = phmap::priv::Allocator<T>>
|
||||||
|
using HASHSET = phmap::flat_hash_set<T, Hash, Eq, Alloc>;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
template<typename K, typename V,
|
||||||
|
typename Hash = std::hash<K>,
|
||||||
|
typename Eq = std::equal_to<K>,
|
||||||
|
typename Alloc = std::allocator<std::pair<const K, V>>>
|
||||||
|
using HASHMAP = std::unordered_map<K, V, Hash, Eq, Alloc>;
|
||||||
|
|
||||||
|
template<typename K,
|
||||||
|
typename Hash = std::hash<K>,
|
||||||
|
typename Eq = std::equal_to<K>,
|
||||||
|
typename Alloc = std::allocator<K>>
|
||||||
|
using HASHSET = std::unordered_set<K, Hash, Eq, Alloc>;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //BLT_HASH_MAP_H
|
|
@ -76,7 +76,7 @@ namespace blt::logging {
|
||||||
// this is not thread safe!
|
// this is not thread safe!
|
||||||
bool ensureAlignment = false;
|
bool ensureAlignment = false;
|
||||||
// should we log to file?
|
// should we log to file?
|
||||||
bool logToFile = true;
|
bool logToFile = false;
|
||||||
// should we log to console?
|
// should we log to console?
|
||||||
bool logToConsole = true;
|
bool logToConsole = true;
|
||||||
// where should we log? (empty for current binary directory) should end in a / if not empty!
|
// where should we log? (empty for current binary directory) should end in a / if not empty!
|
||||||
|
@ -178,13 +178,14 @@ namespace blt::logging {
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
[[nodiscard]] static inline size_t hash(const tag& t) {
|
[[nodiscard]] static inline size_t hash(const tag& t) {
|
||||||
size_t h = t.tag[0]+ t.tag[1] * 3;
|
size_t h = t.tag[1] * 3 - t.tag[0];
|
||||||
return h;
|
return h - 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void expand(){
|
// TODO: fix
|
||||||
|
void expand() {
|
||||||
auto newSize = size * 2;
|
auto newSize = size * 2;
|
||||||
tag* newTags = new tag[newSize];
|
auto newTags = new tag[newSize];
|
||||||
for (size_t i = 0; i < size; i++)
|
for (size_t i = 0; i < size; i++)
|
||||||
newTags[i] = tags[i];
|
newTags[i] = tags[i];
|
||||||
delete[] tags;
|
delete[] tags;
|
||||||
|
@ -193,19 +194,26 @@ namespace blt::logging {
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
tag_map(std::initializer_list<tag> initial_tags){
|
tag_map(std::initializer_list<tag> initial_tags){
|
||||||
tags = new tag[(size = 16)];
|
size_t max = 0;
|
||||||
for (auto& t : initial_tags)
|
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);
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
tag_map& insert(const tag& t){
|
void insert(const tag& t) {
|
||||||
auto h = hash(t);
|
auto h = hash(t);
|
||||||
if (h > size)
|
//if (h > size)
|
||||||
expand();
|
// expand();
|
||||||
if (!tags[h].tag.empty())
|
if (!tags[h].tag.empty())
|
||||||
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
|
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
|
||||||
tags[h] = t;
|
tags[h] = t;
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tag& operator[](const std::string& name) const {
|
tag& operator[](const std::string& name) const {
|
||||||
|
@ -217,6 +225,7 @@ namespace blt::logging {
|
||||||
|
|
||||||
~tag_map(){
|
~tag_map(){
|
||||||
delete[] tags;
|
delete[] tags;
|
||||||
|
tags = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -290,7 +299,7 @@ namespace blt::logging {
|
||||||
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
|
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
|
||||||
LogFileWriter writer;
|
LogFileWriter writer;
|
||||||
|
|
||||||
const tag_map tagMap = {
|
const std::unique_ptr<tag_map> tagMap = std::make_unique<tag_map>(tag_map{
|
||||||
{"YEAR", [](const tag_func_param&) -> std::string {
|
{"YEAR", [](const tag_func_param&) -> std::string {
|
||||||
BLT_NOW();
|
BLT_NOW();
|
||||||
return std::to_string(now->tm_year);
|
return std::to_string(now->tm_year);
|
||||||
|
@ -374,8 +383,8 @@ namespace blt::logging {
|
||||||
}},
|
}},
|
||||||
{"STR", [](const tag_func_param& f) -> std::string {
|
{"STR", [](const tag_func_param& f) -> std::string {
|
||||||
return f.formatted_string;
|
return f.formatted_string;
|
||||||
}},
|
}}
|
||||||
};
|
});
|
||||||
|
|
||||||
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
|
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
@ -489,7 +498,7 @@ namespace blt::logging {
|
||||||
tag_func_param param{
|
tag_func_param param{
|
||||||
level, filename({file}), std::to_string(line), userStr, userStr
|
level, filename({file}), std::to_string(line), userStr, userStr
|
||||||
};
|
};
|
||||||
out += tagMap[tag].func(param);
|
out += (*tagMap)[tag].func(param);
|
||||||
} else {
|
} else {
|
||||||
out += c;
|
out += c;
|
||||||
out += nonTag;
|
out += nonTag;
|
||||||
|
@ -522,6 +531,11 @@ namespace blt::logging {
|
||||||
|
|
||||||
applyCFormatting(withoutLn, out, args);
|
applyCFormatting(withoutLn, out, args);
|
||||||
|
|
||||||
|
if (level == log_level::NONE){
|
||||||
|
std::cout << out << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string finalFormattedOutput = applyFormatString(out, level, file, line);
|
std::string finalFormattedOutput = applyFormatString(out, level, file, line);
|
||||||
|
|
||||||
if (loggingFormat.logToConsole)
|
if (loggingFormat.logToConsole)
|
||||||
|
|
|
@ -58,39 +58,68 @@ namespace blt {
|
||||||
* Creates an encapsulation of a T array which will be automatically deleted when this object goes out of scope.
|
* Creates an encapsulation of a T array which will be automatically deleted when this object goes out of scope.
|
||||||
* This is a simple buffer meant to be used only inside of a function and not moved around, with a few minor exceptions.
|
* This is a simple buffer meant to be used only inside of a function and not moved around, with a few minor exceptions.
|
||||||
* The internal buffer is allocated on the heap.
|
* The internal buffer is allocated on the heap.
|
||||||
* The operator * has been overloaded to return the internal buffer. (or just use scoped_buffer.buffer if you wish to be explicit)
|
* The operator * has been overloaded to return the internal buffer.
|
||||||
* The operator & was not used because I think it is stupid to do so.
|
|
||||||
* "*" on a reference is fine however since it isn't used to dereference in the case.
|
|
||||||
* @tparam T type that is stored in buffer eg char
|
* @tparam T type that is stored in buffer eg char
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct scoped_buffer {
|
struct scoped_buffer {
|
||||||
T* buffer;
|
private:
|
||||||
unsigned long size;
|
T* _buffer;
|
||||||
|
size_t _size;
|
||||||
|
public:
|
||||||
|
explicit scoped_buffer(size_t size): _size(size) {
|
||||||
|
_buffer = new T[size];
|
||||||
|
}
|
||||||
|
|
||||||
explicit scoped_buffer(unsigned long size): size(size) {
|
scoped_buffer(const scoped_buffer& copy) = delete;
|
||||||
buffer = new T[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_buffer(scoped_buffer& copy) = delete;
|
scoped_buffer(scoped_buffer&& move) noexcept {
|
||||||
|
_buffer = move._buffer;
|
||||||
|
_size = move.size();
|
||||||
|
move._buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
scoped_buffer(scoped_buffer&& move) = delete;
|
scoped_buffer operator=(scoped_buffer& copyAssignment) = delete;
|
||||||
|
|
||||||
scoped_buffer operator=(scoped_buffer& copyAssignment) = delete;
|
scoped_buffer& operator=(scoped_buffer&& moveAssignment) noexcept {
|
||||||
|
_buffer = moveAssignment._buffer;
|
||||||
|
_size = moveAssignment.size();
|
||||||
|
moveAssignment._buffer = nullptr;
|
||||||
|
|
||||||
scoped_buffer operator=(scoped_buffer&& moveAssignment) = delete;
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline T& operator[](unsigned long index) const {
|
inline T& operator[](unsigned long index) {
|
||||||
return buffer[index];
|
return _buffer[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T* operator*(){
|
inline const T& operator[](unsigned long index) const {
|
||||||
return buffer;
|
return _buffer[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
~scoped_buffer() {
|
inline T* operator*(){
|
||||||
delete[] buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline size_t size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T* ptr(){
|
||||||
|
return _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_iterator<T> begin(){
|
||||||
|
return ptr_iterator{_buffer};
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_iterator<T> end(){
|
||||||
|
return ptr_iterator{&_buffer[_size]};
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_buffer() {
|
||||||
|
delete[] _buffer;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -168,6 +197,7 @@ namespace blt {
|
||||||
delete[] _values;
|
delete[] _values;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_TESTS_MEMORY_H
|
#endif //BLT_TESTS_MEMORY_H
|
||||||
|
|
|
@ -9,7 +9,41 @@
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
namespace blt {
|
namespace blt::random {
|
||||||
|
|
||||||
|
static inline uint32_t PCG_Hash(uint32_t input) {
|
||||||
|
uint32_t state = input * 747796405u + 2891336453u;
|
||||||
|
uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
||||||
|
return (word >> 22u) ^
|
||||||
|
word;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float randomFloat(uint32_t& seed){
|
||||||
|
seed = PCG_Hash(seed);
|
||||||
|
return (float)seed / (float)std::numeric_limits<uint32_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return random float without changing seed
|
||||||
|
*/
|
||||||
|
static inline float randomFloat_c(uint32_t seed){
|
||||||
|
return randomFloat(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param seed seed for random
|
||||||
|
* @param min inclusive min
|
||||||
|
* @param max exclusive max
|
||||||
|
* @return random int between min (inclusive) and max (exclusive)
|
||||||
|
*/
|
||||||
|
static inline int randomInt(uint32_t& seed, int min = 0, int max = 1){
|
||||||
|
return (int)((randomFloat(seed) * (float)(max - min)) + (float)min);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int randomInt_c(uint32_t seed, int min = 0, int max = 1){
|
||||||
|
return randomInt(seed, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a container class for generating random number distributions
|
* Creates a container class for generating random number distributions
|
||||||
* @tparam T numeric type
|
* @tparam T numeric type
|
||||||
|
@ -28,17 +62,19 @@ namespace blt {
|
||||||
* @param max max value possible to generate. (default: 1)
|
* @param max max value possible to generate. (default: 1)
|
||||||
* @param seed seed to use in generating random values. (default: 0)
|
* @param seed seed to use in generating random values. (default: 0)
|
||||||
*/
|
*/
|
||||||
explicit random(T min = (T)0, T max = (T)1, long seed = 0): gen(std::mt19937(seed)){
|
explicit random(T min = (T) 0, T max = (T) 1, long seed = 0): gen(std::mt19937(seed)) {
|
||||||
distribution = new dist(min, max);
|
distribution = new dist(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note the min/max are inclusive and defaults to a **uniform** distribution.
|
* Note the min/max are inclusive and defaults to a **uniform** distribution.
|
||||||
* @return random number between the defined min/max or the default of [0,1].
|
* @return random number between the defined min/max or the default of [0,1].
|
||||||
*/
|
*/
|
||||||
T get(){
|
T get() {
|
||||||
return (*distribution)(gen);
|
return (*distribution)(gen);
|
||||||
}
|
}
|
||||||
~random(){
|
|
||||||
|
~random() {
|
||||||
delete distribution;
|
delete distribution;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -52,6 +88,7 @@ namespace blt {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_RANDOM_H
|
#endif //BLT_RANDOM_H
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <blt/std/logging.h>
|
|
||||||
|
|
||||||
namespace blt::string {
|
namespace blt::string {
|
||||||
|
|
||||||
|
@ -140,6 +139,26 @@ namespace blt::string {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
|
||||||
|
static bool replace(std::string& str, const std::string& from, const std::string& to) {
|
||||||
|
size_t start_pos = str.find(from);
|
||||||
|
if(start_pos == std::string::npos)
|
||||||
|
return false;
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||||
|
if(from.empty())
|
||||||
|
return;
|
||||||
|
size_t start_pos = 0;
|
||||||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
||||||
// would've preferred to use boost lib but instructions said to avoid external libs
|
// would've preferred to use boost lib but instructions said to avoid external libs
|
||||||
// trim from start (in place)
|
// trim from start (in place)
|
||||||
|
|
|
@ -29,6 +29,14 @@ namespace blt::system {
|
||||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline auto nanoTime() {
|
||||||
|
return getCurrentTimeNanoseconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline auto getCurrentTimeMilliseconds(){
|
||||||
|
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard time string is formatted as follows:
|
* Standard time string is formatted as follows:
|
||||||
* Year-Month-Date Hour:Min:Second
|
* Year-Month-Date Hour:Min:Second
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3
|
|
@ -4,18 +4,22 @@
|
||||||
* See LICENSE file for license detail
|
* See LICENSE file for license detail
|
||||||
*/
|
*/
|
||||||
#include <blt/nbt/nbt.h>
|
#include <blt/nbt/nbt.h>
|
||||||
|
#include <blt/std/logging.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace blt::nbt {
|
namespace blt::nbt {
|
||||||
void writeUTF8String(std::fstream& stream, const std::string& str) {
|
void writeUTF8String(blt::fs::block_writer& 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(std::fstream& stream) {
|
std::string readUTF8String(blt::fs::block_reader& stream) {
|
||||||
unsigned short utflen;
|
int16_t utflen;
|
||||||
|
|
||||||
stream.read(reinterpret_cast<char*>(&utflen), sizeof(utflen));
|
readData(stream, utflen);
|
||||||
|
|
||||||
blt::string::utf8_string str{};
|
blt::string::utf8_string str{};
|
||||||
str.size = utflen;
|
str.size = utflen;
|
||||||
|
@ -23,8 +27,20 @@ namespace blt::nbt {
|
||||||
|
|
||||||
stream.read(str.characters, str.size);
|
stream.read(str.characters, str.size);
|
||||||
|
|
||||||
auto strOut = std::move(blt::string::getStringFromUTF8(str));
|
auto strOut = blt::string::getStringFromUTF8(str);
|
||||||
delete[] str.characters;
|
delete[] str.characters;
|
||||||
return strOut;
|
return strOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NBTReader::read() {
|
||||||
|
char t = reader.get();
|
||||||
|
if (t != (char)nbt_tag::COMPOUND) {
|
||||||
|
BLT_WARN("Found %d", t);
|
||||||
|
throw std::runtime_error("Incorrectly formatted NBT data! Root tag must be a compound tag!");
|
||||||
|
}
|
||||||
|
root = new tag_compound;
|
||||||
|
assert(root != nullptr);
|
||||||
|
root->readName(reader);
|
||||||
|
root->readPayload(reader);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,572 @@
|
||||||
|
/*
|
||||||
|
* Created by Brett on 06/08/23.
|
||||||
|
* Licensed under GNU General Public License V3.0
|
||||||
|
* See LICENSE file for license detail
|
||||||
|
*/
|
||||||
|
#include <blt/parse/argparse.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <blt/std/string.h>
|
||||||
|
|
||||||
|
namespace blt
|
||||||
|
{
|
||||||
|
|
||||||
|
void arg_nargs_t::decode(char c)
|
||||||
|
{
|
||||||
|
if (c == '?')
|
||||||
|
flags = UNKNOWN;
|
||||||
|
else if (c == '+')
|
||||||
|
flags = ALL_REQUIRED;
|
||||||
|
else if (c == '*')
|
||||||
|
flags = ALL;
|
||||||
|
else
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_nargs_t::arg_nargs_t(char c)
|
||||||
|
{
|
||||||
|
decode(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_nargs_t::arg_nargs_t(std::string s)
|
||||||
|
{
|
||||||
|
decode(s[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_nargs_t::arg_nargs_t(const char* s)
|
||||||
|
{
|
||||||
|
decode(*s);
|
||||||
|
}
|
||||||
|
|
||||||
|
class invalid_argument_exception : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
invalid_argument_exception(const std::string& str): std::runtime_error(str)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
void arg_vector_t::validateFlags()
|
||||||
|
{
|
||||||
|
for (const auto& flag : flags)
|
||||||
|
if (!flag.starts_with('-'))
|
||||||
|
throw invalid_argument_exception("Flag '" + flag + "' must start with - or --");
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_vector_t::arg_vector_t(const char* str)
|
||||||
|
{
|
||||||
|
std::string as_string(str);
|
||||||
|
if (as_string.starts_with('-'))
|
||||||
|
flags.emplace_back(as_string);
|
||||||
|
else
|
||||||
|
name = as_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_vector_t::arg_vector_t(const std::string& str)
|
||||||
|
{
|
||||||
|
if (str.starts_with('-'))
|
||||||
|
flags.emplace_back(str);
|
||||||
|
else
|
||||||
|
name = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string arg_vector_t::getFirstFullFlag()
|
||||||
|
{
|
||||||
|
// assign flag so it always exists, will be first '-' flag if we fail to find a '--' flag
|
||||||
|
std::string flag = flags[0];
|
||||||
|
// search for first '--' flag
|
||||||
|
for (const auto& f : flags)
|
||||||
|
if (f.starts_with("--"))
|
||||||
|
{
|
||||||
|
flag = f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(const arg_data_t& v)
|
||||||
|
{
|
||||||
|
if (holds_alternative<arg_data_internal_t>(v))
|
||||||
|
return to_string(std::get<arg_data_internal_t>(v));
|
||||||
|
else if (std::holds_alternative<arg_data_vec_t>(v))
|
||||||
|
{
|
||||||
|
const auto& vec = std::get<arg_data_vec_t>(v);
|
||||||
|
if (vec.size() == 1)
|
||||||
|
return to_string(vec[0]);
|
||||||
|
if (vec.empty())
|
||||||
|
return "Empty Vector";
|
||||||
|
std::string str;
|
||||||
|
for (const auto& r : vec)
|
||||||
|
{
|
||||||
|
str += to_string(r);
|
||||||
|
str += ' ';
|
||||||
|
}
|
||||||
|
return "Vector of contents: " + str;
|
||||||
|
}
|
||||||
|
return "Empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(const arg_data_internal_t& v)
|
||||||
|
{
|
||||||
|
if (std::holds_alternative<std::string>(v))
|
||||||
|
{
|
||||||
|
return std::get<std::string>(v);
|
||||||
|
} else if (std::holds_alternative<bool>(v))
|
||||||
|
{
|
||||||
|
return std::get<bool>(v) ? "True" : "False";
|
||||||
|
}
|
||||||
|
return std::to_string(std::get<int32_t>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string arg_parse::filename(const std::string& path)
|
||||||
|
{
|
||||||
|
auto paths = blt::string::split(path, "/");
|
||||||
|
auto final = paths[paths.size() - 1];
|
||||||
|
if (final == "/")
|
||||||
|
return paths[paths.size() - 2];
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::addArgument(const arg_properties_t& args)
|
||||||
|
{
|
||||||
|
auto properties = new arg_properties_t(args);
|
||||||
|
|
||||||
|
// determine where to store the arg when parsing
|
||||||
|
if (properties->a_dest.empty())
|
||||||
|
{
|
||||||
|
if (properties->a_flags.isFlag())
|
||||||
|
properties->a_dest = properties->a_flags.getFirstFullFlag();
|
||||||
|
else
|
||||||
|
properties->a_dest = properties->a_flags.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties->a_dest.starts_with("--"))
|
||||||
|
properties->a_dest = properties->a_dest.substr(2);
|
||||||
|
else if (properties->a_dest.starts_with('-'))
|
||||||
|
properties->a_dest = properties->a_dest.substr(1);
|
||||||
|
|
||||||
|
// associate flags with their properties
|
||||||
|
for (const auto& flag : properties->a_flags.flags)
|
||||||
|
user_args.flag_associations[flag] = properties;
|
||||||
|
|
||||||
|
// positional args uses index (vector) to store the properties
|
||||||
|
if (!properties->a_flags.isFlag())
|
||||||
|
user_args.name_associations.push_back(properties);
|
||||||
|
|
||||||
|
user_args.arg_properties_storage.push_back(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arg_parse::consumeArguments(
|
||||||
|
arg_tokenizer& tokenizer, const std::string& flag, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
switch (properties.a_nargs.flags)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
for (int i = 0; i < properties.a_nargs.args; i++)
|
||||||
|
{
|
||||||
|
// if we don't have another arg to consume we have a problem!
|
||||||
|
if (!tokenizer.hasCurrent())
|
||||||
|
{
|
||||||
|
// TODO: S
|
||||||
|
printUsage();
|
||||||
|
std::cout << filename(loaded_args.program_name) << ": error: flag '" << flag << "' expected " << properties.a_nargs.args
|
||||||
|
<< " argument(s) got " << i << " argument(s) instead!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if we do have one, but it is a flag then we also have a problem!
|
||||||
|
if (tokenizer.isFlag())
|
||||||
|
{
|
||||||
|
printUsage();
|
||||||
|
std::cout << filename(loaded_args.program_name) << ": error: flag '" << flag << "' expected " << properties.a_nargs.args
|
||||||
|
<< " argument(s) but found '" << tokenizer.get() << "' instead!\n";
|
||||||
|
//BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// get the value and advance
|
||||||
|
v_out.emplace_back(tokenizer.get());
|
||||||
|
tokenizer.advance();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case arg_nargs_t::UNKNOWN:
|
||||||
|
// no arg next
|
||||||
|
if (!tokenizer.hasCurrent() || tokenizer.isFlag())
|
||||||
|
{
|
||||||
|
// python's default is to store const if around otherwise store default
|
||||||
|
if (!properties.a_const.empty())
|
||||||
|
v_out.emplace_back(properties.a_const);
|
||||||
|
else // this should no longer be required with the changes to how defaults are handled
|
||||||
|
v_out.emplace_back(properties.a_default);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
v_out.emplace_back(tokenizer.get());
|
||||||
|
tokenizer.advance();
|
||||||
|
return true;
|
||||||
|
case arg_nargs_t::ALL:
|
||||||
|
while (tokenizer.hasCurrent() && !tokenizer.isFlag())
|
||||||
|
{
|
||||||
|
v_out.emplace_back(tokenizer.get());
|
||||||
|
tokenizer.advance();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case arg_nargs_t::ALL_REQUIRED:
|
||||||
|
if (tokenizer.hasCurrent() && tokenizer.isFlag())
|
||||||
|
{
|
||||||
|
printUsage();
|
||||||
|
std::cout << loaded_args.program_name << ": at least one argument is required for '" << flag << "'\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (tokenizer.hasCurrent() && !tokenizer.isFlag())
|
||||||
|
{
|
||||||
|
v_out.emplace_back(tokenizer.get());
|
||||||
|
tokenizer.advance();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos)
|
||||||
|
{
|
||||||
|
auto index = last_pos++;
|
||||||
|
if (index >= user_args.name_associations.size())
|
||||||
|
loaded_args.unrecognized_args.push_back(tokenizer.get());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loaded_args.data[user_args.name_associations[index]->a_dest] = tokenizer.get();
|
||||||
|
loaded_args.found_args.insert(user_args.name_associations[index]->a_dest);
|
||||||
|
}
|
||||||
|
tokenizer.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::handleFlagArgument(arg_tokenizer& tokenizer)
|
||||||
|
{
|
||||||
|
auto flag = tokenizer.get();
|
||||||
|
tokenizer.advance();
|
||||||
|
|
||||||
|
// token is a flag, figure out how to handle it
|
||||||
|
if (flag.starts_with("--"))
|
||||||
|
processFlag(tokenizer, flag);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// handle special args like -vvv
|
||||||
|
if (!flag.starts_with('-'))
|
||||||
|
std::cerr << "Flag processed but does not start with '-' (this is a serious error!)\n";
|
||||||
|
// make sure the flag only contains the same character
|
||||||
|
auto type = flag[1];
|
||||||
|
for (char c : flag.substr(1))
|
||||||
|
{
|
||||||
|
if (c != type)
|
||||||
|
{
|
||||||
|
printUsage();
|
||||||
|
std::cout << "found different characters in flag '" << flag.c_str() << "' expected '" << type << "' but found '" << c << "'\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// size without -
|
||||||
|
auto len = flag.size() - 1;
|
||||||
|
// get flag type
|
||||||
|
std::string str = "- ";
|
||||||
|
str[1] = flag[1];
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
processFlag(tokenizer, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::processFlag(arg_tokenizer& tokenizer, const std::string& flag)
|
||||||
|
{
|
||||||
|
auto flag_itr = user_args.flag_associations.find(flag);
|
||||||
|
if (flag_itr == user_args.flag_associations.end())
|
||||||
|
{
|
||||||
|
loaded_args.unrecognized_args.push_back(flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* const properties = user_args.flag_associations.at(flag);
|
||||||
|
|
||||||
|
if (properties->a_dest.empty())
|
||||||
|
{
|
||||||
|
loaded_args.unrecognized_args.push_back(flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto dest = properties->a_dest;
|
||||||
|
|
||||||
|
loaded_args.found_args.insert(dest);
|
||||||
|
|
||||||
|
switch (properties->a_action)
|
||||||
|
{
|
||||||
|
case arg_action_t::HELP:
|
||||||
|
printUsage();
|
||||||
|
printHelp();
|
||||||
|
break;
|
||||||
|
case arg_action_t::STORE:
|
||||||
|
{
|
||||||
|
arg_data_t& data = loaded_args.data[dest];
|
||||||
|
arg_data_vec_t v;
|
||||||
|
if (!consumeArguments(tokenizer, flag, *properties, v))
|
||||||
|
{
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (v.empty())
|
||||||
|
data = "";
|
||||||
|
else if (v.size() == 1)
|
||||||
|
data = v[0];
|
||||||
|
else
|
||||||
|
data = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case arg_action_t::STORE_CONST:
|
||||||
|
loaded_args.data[dest] = properties->a_const;
|
||||||
|
break;
|
||||||
|
case arg_action_t::STORE_FALSE:
|
||||||
|
loaded_args.data[dest] = false;
|
||||||
|
break;
|
||||||
|
case arg_action_t::STORE_TRUE:
|
||||||
|
loaded_args.data[dest] = true;
|
||||||
|
break;
|
||||||
|
case arg_action_t::COUNT:
|
||||||
|
{
|
||||||
|
auto& data = loaded_args.data[dest];
|
||||||
|
if (!holds_alternative<int32_t>(data))
|
||||||
|
data = 0;
|
||||||
|
data = get<int32_t>(data) + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case arg_action_t::EXTEND:
|
||||||
|
{
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case arg_action_t::VERSION:
|
||||||
|
{
|
||||||
|
auto file = filename(loaded_args.program_name);
|
||||||
|
std::cout << file.c_str() << " " << properties->a_version << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case arg_action_t::APPEND_CONST:
|
||||||
|
{
|
||||||
|
auto& data = loaded_args.data[dest];
|
||||||
|
if (!std::holds_alternative<arg_data_vec_t>(data))
|
||||||
|
{
|
||||||
|
data = arg_data_vec_t();
|
||||||
|
}
|
||||||
|
auto& l = get<arg_data_vec_t>(data);
|
||||||
|
l.emplace_back(properties->a_const);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case arg_action_t::APPEND:
|
||||||
|
{
|
||||||
|
auto& data = loaded_args.data[dest];
|
||||||
|
if (!holds_alternative<arg_data_vec_t>(data))
|
||||||
|
data = arg_data_vec_t();
|
||||||
|
auto& l = get<arg_data_vec_t>(data);
|
||||||
|
consumeArguments(tokenizer, flag, *properties, l);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_parse::arg_results arg_parse::parse_args(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
args.reserve(argc);
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
args.emplace_back(argv[i]);
|
||||||
|
return parse_args(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_parse::arg_results arg_parse::parse_args(const std::vector<std::string>& args)
|
||||||
|
{
|
||||||
|
arg_tokenizer tokenizer(args);
|
||||||
|
loaded_args.program_name = tokenizer.get();
|
||||||
|
tokenizer.advance();
|
||||||
|
|
||||||
|
size_t last_positional = 0;
|
||||||
|
while (tokenizer.hasCurrent())
|
||||||
|
{
|
||||||
|
if (tokenizer.isFlag())
|
||||||
|
handleFlagArgument(tokenizer);
|
||||||
|
else
|
||||||
|
handlePositionalArgument(tokenizer, last_positional);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load defaults for args which were not found
|
||||||
|
for (const auto* arg : user_args.arg_properties_storage)
|
||||||
|
{
|
||||||
|
if (!to_string(arg->a_default).empty() && !loaded_args.contains(arg->a_dest))
|
||||||
|
loaded_args.data[arg->a_dest] = arg->a_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there was no problems processing then return the loaded args
|
||||||
|
if (loaded_args.unrecognized_args.empty())
|
||||||
|
return loaded_args;
|
||||||
|
|
||||||
|
// otherwise construct a string detailing the unrecognized args
|
||||||
|
std::string unrec;
|
||||||
|
for (const auto& r : loaded_args.unrecognized_args)
|
||||||
|
{
|
||||||
|
unrec += '\'';
|
||||||
|
unrec += r;
|
||||||
|
unrec += '\'';
|
||||||
|
unrec += ' ';
|
||||||
|
}
|
||||||
|
// remove the last space caused by the for loop
|
||||||
|
unrec = unrec.substr(0, unrec.size() - 1);
|
||||||
|
printUsage();
|
||||||
|
std::cout << loaded_args.program_name << ": error: unrecognized args: " << unrec << "\n";
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arg_parse::takesArgs(const arg_properties_t* const& arg)
|
||||||
|
{
|
||||||
|
switch (arg->a_action)
|
||||||
|
{
|
||||||
|
case arg_action_t::STORE_CONST:
|
||||||
|
case arg_action_t::STORE_TRUE:
|
||||||
|
case arg_action_t::STORE_FALSE:
|
||||||
|
case arg_action_t::APPEND_CONST:
|
||||||
|
case arg_action_t::COUNT:
|
||||||
|
case arg_action_t::HELP:
|
||||||
|
case arg_action_t::VERSION:
|
||||||
|
return false;
|
||||||
|
case arg_action_t::STORE:
|
||||||
|
case arg_action_t::APPEND:
|
||||||
|
case arg_action_t::EXTEND:
|
||||||
|
return arg->a_nargs.takesArgs();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::printHelp() const
|
||||||
|
{
|
||||||
|
if (!user_args.prefix.empty())
|
||||||
|
{
|
||||||
|
std::cout << "\n";
|
||||||
|
std::cout << user_args.prefix;
|
||||||
|
}
|
||||||
|
std::cout << "\npositional arguments:\n";
|
||||||
|
// spaces per tab
|
||||||
|
const size_t tab_size = 8;
|
||||||
|
size_t max_length = 0;
|
||||||
|
// search for longest pos arg length
|
||||||
|
for (const auto& arg : user_args.arg_properties_storage)
|
||||||
|
{
|
||||||
|
if (!arg->a_flags.isFlag())
|
||||||
|
max_length = std::max(arg->a_flags.name.size(), max_length);
|
||||||
|
else {
|
||||||
|
auto tmp = getFlagHelp(arg);
|
||||||
|
max_length = std::max(tmp.size(), max_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& arg : user_args.arg_properties_storage)
|
||||||
|
{
|
||||||
|
if (!arg->a_flags.isFlag())
|
||||||
|
{
|
||||||
|
const auto& name = arg->a_flags.name;
|
||||||
|
std::cout << name;
|
||||||
|
auto size = std::max(static_cast<int64_t>(max_length) - static_cast<int64_t>(name.size()), 0l);
|
||||||
|
size += tab_size;
|
||||||
|
for (int64_t i = 0; i < size; i++)
|
||||||
|
std::cout << " ";
|
||||||
|
std::cout << arg->a_help << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "\noptions:\n";
|
||||||
|
for (const auto& arg : user_args.arg_properties_storage)
|
||||||
|
{
|
||||||
|
if (arg->a_flags.isFlag())
|
||||||
|
{
|
||||||
|
const auto& name = getFlagHelp(arg);
|
||||||
|
std::cout << name;
|
||||||
|
auto size = std::max(static_cast<int64_t>(max_length) - static_cast<int64_t>(name.size()), 0l);
|
||||||
|
size += tab_size;
|
||||||
|
for (int64_t i = 0; i < size; i++)
|
||||||
|
std::cout << " ";
|
||||||
|
std::cout << arg->a_help << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!user_args.postfix.empty())
|
||||||
|
{
|
||||||
|
std::cout << user_args.postfix;
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::printUsage() const
|
||||||
|
{
|
||||||
|
std::string usage = "Usage: " + loaded_args.program_name + " ";
|
||||||
|
std::cout << usage;
|
||||||
|
size_t current_line_length = 0;
|
||||||
|
|
||||||
|
for (const auto& arg : user_args.arg_properties_storage)
|
||||||
|
{
|
||||||
|
auto meta = getMetavar(arg);
|
||||||
|
|
||||||
|
std::string str = "[";
|
||||||
|
if (arg->a_flags.isFlag())
|
||||||
|
{
|
||||||
|
str += arg->a_flags.getFirstFullFlag();
|
||||||
|
if (takesArgs(arg))
|
||||||
|
{
|
||||||
|
str += " ";
|
||||||
|
str += meta;
|
||||||
|
str += "";
|
||||||
|
}
|
||||||
|
str += "]";
|
||||||
|
str += ' ';
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
str += "<";
|
||||||
|
str += arg->a_flags.name;
|
||||||
|
str += ">] ";
|
||||||
|
}
|
||||||
|
|
||||||
|
current_line_length += str.size();
|
||||||
|
checkAndPrintFormattingLine(current_line_length, usage.size());
|
||||||
|
|
||||||
|
std::cout << str;
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void arg_parse::checkAndPrintFormattingLine(size_t& current_line_length, size_t spacing) const
|
||||||
|
{
|
||||||
|
if (current_line_length > user_args.max_line_length)
|
||||||
|
{
|
||||||
|
std::cout << "\n";
|
||||||
|
for (size_t i = 0; i < spacing; i++)
|
||||||
|
std::cout << " ";
|
||||||
|
current_line_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string arg_parse::getMetavar(const arg_properties_t* const& arg)
|
||||||
|
{
|
||||||
|
auto meta = arg->a_metavar;
|
||||||
|
|
||||||
|
if (meta.empty())
|
||||||
|
meta = blt::string::toUpperCase(arg->a_dest);
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string arg_parse::getFlagHelp(const arg_properties_t* const& arg)
|
||||||
|
{
|
||||||
|
auto meta = getMetavar(arg);
|
||||||
|
// bad that we have to create this twice!
|
||||||
|
std::string tmp;
|
||||||
|
for (const auto& flag : arg->a_flags.flags)
|
||||||
|
{
|
||||||
|
tmp += flag;
|
||||||
|
if (takesArgs(arg))
|
||||||
|
{
|
||||||
|
tmp += ' ';
|
||||||
|
tmp += meta;
|
||||||
|
}
|
||||||
|
tmp += ", ";
|
||||||
|
}
|
||||||
|
tmp = tmp.substr(0, tmp.size()-2);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,14 @@
|
||||||
*/
|
*/
|
||||||
#include <blt/std/filesystem.h>
|
#include <blt/std/filesystem.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include "blt/std/logging.h"
|
||||||
|
|
||||||
|
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 blt::fs::fstream_block_reader::read(char* buffer, size_t bytes) {
|
int blt::fs::fstream_block_reader::read(char* buffer, size_t bytes) {
|
||||||
if (readIndex == 0)
|
if (readIndex == 0)
|
||||||
|
@ -40,7 +48,7 @@ int blt::fs::fstream_block_writer::write(char* buffer, size_t bytes) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blt::fs::fstream_block_writer::flush() {
|
void blt::fs::fstream_block_writer::flush_internal() {
|
||||||
m_stream.write(m_buffer, (long) writeIndex);
|
m_stream.write(m_buffer, (long) writeIndex);
|
||||||
writeIndex = 0;
|
writeIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#ifndef BLT_TESTS_HASHMAP_TEXTS_H
|
#ifndef BLT_TESTS_HASHMAP_TEXTS_H
|
||||||
#define BLT_TESTS_HASHMAP_TEXTS_H
|
#define BLT_TESTS_HASHMAP_TEXTS_H
|
||||||
|
|
||||||
#include <blt/std/hash_map.h>
|
#include <blt/std/hashmap.h>
|
||||||
|
|
||||||
inline static int test_hashmaps(){
|
inline static int test_hashmaps(){
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,313 @@
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "binary_trees.h"
|
//#include "binary_trees.h"
|
||||||
#include "logging.h"
|
//#include "logging.h"
|
||||||
#include "profiling_tests.h"
|
#include "profiling_tests.h"
|
||||||
#include "nbt_tests.h"
|
#include "nbt_tests.h"
|
||||||
#include "queue_tests.h"
|
#include "blt/parse/argparse.h"
|
||||||
#include "blt/math/vectors.h"
|
//#include "queue_tests.h"
|
||||||
#include "blt/math/matrix.h"
|
//#include "blt/math/vectors.h"
|
||||||
#include <bitset>
|
//#include "blt/math/matrix.h"
|
||||||
#include "hashmap_tests.h"
|
//#include <bitset>
|
||||||
|
//#include "hashmap_tests.h"
|
||||||
|
//#include <functional>
|
||||||
|
|
||||||
int main() {
|
std::function<int(int i)> test{
|
||||||
binaryTreeTest();
|
[](int i) -> int {
|
||||||
|
int acc = 1;
|
||||||
|
for (int j = 0; j < i; j++){
|
||||||
|
acc += j * i;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
run_logging();
|
int test_as_func(int i){
|
||||||
|
int acc = 1;
|
||||||
|
for (int j = 0; j < i; j++){
|
||||||
|
acc += j * i;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
runProfilingAndTableTests();
|
inline int test_as_func_inline(int i){
|
||||||
|
int acc = 1;
|
||||||
|
for (int j = 0; j < i; j++){
|
||||||
|
acc += j * i;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
blt::logging::flush();
|
std::function<int(int i)> test_func_as_std(&test_as_func);
|
||||||
|
|
||||||
nbt_tests();
|
class super_func {
|
||||||
|
public:
|
||||||
|
virtual int test(int i) = 0;
|
||||||
|
virtual ~super_func() = default;
|
||||||
|
};
|
||||||
|
|
||||||
BLT_TRACE0_STREAM << "Test Output!\n";
|
class class_func : public super_func {
|
||||||
BLT_TRACE1_STREAM << 5 << "\n";
|
public:
|
||||||
BLT_TRACE2_STREAM << 5 << "\n";
|
int test(int i) override {
|
||||||
BLT_TRACE3_STREAM << 5 << "\n";
|
int acc = 1;
|
||||||
BLT_TRACE_STREAM << "TRACEY\n";
|
for (int j = 0; j < i; j++){
|
||||||
|
acc += j * i;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
blt::logging::flush();
|
int (*func_func)(int) = [](int i) -> int {
|
||||||
|
int acc = 1;
|
||||||
|
for (int j = 0; j < i; j++){
|
||||||
|
acc += j * i;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
};
|
||||||
|
|
||||||
test_queues();
|
int (*func_func_in)(int) = &test_as_func;
|
||||||
|
|
||||||
blt::vec4 v{2, 5, 1, 8};
|
int main(int argc, const char** argv) {
|
||||||
blt::mat4x4 m{};
|
blt::arg_parse parser;
|
||||||
m.m(0,0, 1);
|
parser.addArgument(blt::arg_builder({"--poo", "-p"}).build());
|
||||||
m.m(0,2, 2);
|
parser.addArgument(blt::arg_builder("--foo").setAction(blt::arg_action_t::STORE_TRUE).setDefault(false).build());
|
||||||
m.m(1, 1, 3);
|
parser.addArgument(blt::arg_builder({"--goo", "-g"}).build());
|
||||||
m.m(1, 3, 4);
|
parser.addArgument(blt::arg_builder({"--oop", "-o"}).build());
|
||||||
m.m(2, 2, 5);
|
parser.addArgument(blt::arg_builder("Sexy_pos").setHelp("I am helpful!").build());
|
||||||
m.m(3, 0, 6);
|
|
||||||
m.m(3, 3, 7);
|
|
||||||
|
|
||||||
auto result = m * v;
|
auto args = parser.parse_args(argc, argv);
|
||||||
|
std::vector<std::string> superArgs {
|
||||||
|
"BLT_TESTS",
|
||||||
|
"Sexy",
|
||||||
|
"-p", "I have poop",
|
||||||
|
"--help"
|
||||||
|
};
|
||||||
|
auto args2 = parser.parse_args(superArgs);
|
||||||
|
|
||||||
std::cout << result.x() << " " << result.y() << " " << result.z() << " " << result.w() << std::endl;
|
for (const auto& a : args2.data){
|
||||||
|
BLT_TRACE("['%s' = '%s']", a.first.c_str(), blt::to_string(a.second).c_str());
|
||||||
|
}
|
||||||
|
BLT_TRACE(args2.program_name);
|
||||||
|
//
|
||||||
|
// if (argc > 1 && std::string(argv[1]) == "--no_color") {
|
||||||
|
// for (int i = (int)blt::logging::log_level::NONE; i < (int)blt::logging::log_level::FATAL; i++) {
|
||||||
|
// blt::logging::setLogColor((blt::logging::log_level)i, "");
|
||||||
|
// }
|
||||||
|
// blt::logging::setLogOutputFormat("[${{TIME}}] [${{LOG_LEVEL}}] (${{FILE}}:${{LINE}}) ${{STR}}\n");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// auto* funy = new class_func;
|
||||||
|
// super_func* virtual_funy = new class_func;
|
||||||
|
//
|
||||||
|
// for (int _ = 0; _ < 10; _++ ) {
|
||||||
|
// int num_of_tests = 10000;
|
||||||
|
// int acc = 1;
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "std::function (lambda)");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += test(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "std::function (lambda)");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "std::function (normal)");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += test_func_as_std(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "std::function (normal)");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "normal function");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += test_as_func(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "normal function");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "(inline) normal function");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += test_as_func_inline(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "(inline) normal function");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "virtual class direct");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += funy->test(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "virtual class direct");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "virtual class");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += virtual_funy->test(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "virtual class");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "funcptr lambda");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += func_func(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "funcptr lambda");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
//
|
||||||
|
// BLT_START_INTERVAL("Functions Test", "c function ptr");
|
||||||
|
// acc = 1;
|
||||||
|
// for (int i = 0; i < num_of_tests; i++) {
|
||||||
|
// acc += func_func_in(i);
|
||||||
|
// }
|
||||||
|
// BLT_END_INTERVAL("Functions Test", "c function ptr");
|
||||||
|
// BLT_TRACE(acc);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// BLT_PRINT_PROFILE("Functions Test", blt::logging::log_level::NONE, true);
|
||||||
|
// delete virtual_funy;
|
||||||
|
// delete funy;
|
||||||
|
//
|
||||||
|
// binaryTreeTest();
|
||||||
|
//
|
||||||
|
// run_logging();
|
||||||
|
//
|
||||||
|
// runProfilingAndTableTests();
|
||||||
|
//
|
||||||
|
// blt::logging::flush();
|
||||||
|
//
|
||||||
|
// nbt_tests();
|
||||||
|
//
|
||||||
|
// BLT_TRACE0_STREAM << "Test Output!\n";
|
||||||
|
// BLT_TRACE1_STREAM << 5 << "\n";
|
||||||
|
// BLT_TRACE2_STREAM << 5 << "\n";
|
||||||
|
// BLT_TRACE3_STREAM << 5 << "\n";
|
||||||
|
// BLT_TRACE_STREAM << "TRACEY\n";
|
||||||
|
//
|
||||||
|
// blt::logging::flush();
|
||||||
|
//
|
||||||
|
// test_queues();
|
||||||
|
//
|
||||||
|
// blt::vec4 v{2, 5, 1, 8};
|
||||||
|
// blt::mat4x4 m{};
|
||||||
|
// m.m(0,0, 1);
|
||||||
|
// m.m(0,2, 2);
|
||||||
|
// m.m(1, 1, 3);
|
||||||
|
// m.m(1, 3, 4);
|
||||||
|
// m.m(2, 2, 5);
|
||||||
|
// m.m(3, 0, 6);
|
||||||
|
// m.m(3, 3, 7);
|
||||||
|
//
|
||||||
|
// auto result = m * v;
|
||||||
|
//
|
||||||
|
// std::cout << result.x() << " " << result.y() << " " << result.z() << " " << result.w() << std::endl;
|
||||||
|
//
|
||||||
|
// if (test_hashmaps()){
|
||||||
|
// BLT_FATAL("Hashmap test failed!");
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// BLT_TRACE("Hello Trace!");
|
||||||
|
// BLT_DEBUG("Hello Debug!");
|
||||||
|
// BLT_INFO("Hello Info!");
|
||||||
|
// BLT_WARN("Hello Warn!");
|
||||||
|
// BLT_ERROR("Hello Error!");
|
||||||
|
// BLT_FATAL("Hello Fatal!");
|
||||||
|
//
|
||||||
|
// constexpr int size = 100;
|
||||||
|
// uint32_t vals[size];
|
||||||
|
//
|
||||||
|
// for (uint32_t & val : vals)
|
||||||
|
// val = 0;
|
||||||
|
//
|
||||||
|
// uint32_t seed = 1023;
|
||||||
|
// for (int i = 0; i < 10000000; i++){
|
||||||
|
// seed = seed * i;
|
||||||
|
// vals[blt::random::randomInt(seed, 0, size)] += 1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// uint32_t mean = 0;
|
||||||
|
// for (uint32_t& val : vals)
|
||||||
|
// mean += val;
|
||||||
|
// mean /= size;
|
||||||
|
//
|
||||||
|
// uint32_t std = 0;
|
||||||
|
// for (uint32_t& val : vals) {
|
||||||
|
// auto e = (val - mean);
|
||||||
|
// std += e * e;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// auto stdev = sqrt((double)std / (double)size);
|
||||||
|
//
|
||||||
|
// BLT_INFO("STDDEV of # random values: %f", stdev);
|
||||||
|
|
||||||
if (test_hashmaps()){
|
{
|
||||||
BLT_FATAL("Hashmap test failed!");
|
std::fstream nbtFile("super_file.nbt", std::ios::out | std::ios::binary);
|
||||||
return 1;
|
blt::fs::fstream_block_writer blockWriter(nbtFile);
|
||||||
|
blt::nbt::NBTWriter nbtWriter(blockWriter);
|
||||||
|
nbtWriter.write(
|
||||||
|
new blt::nbt::tag_compound(
|
||||||
|
"root", {
|
||||||
|
new blt::nbt::tag_byte("super_byte", 8),
|
||||||
|
new blt::nbt::tag_short("shortTest", 32767),
|
||||||
|
new blt::nbt::tag_compound(
|
||||||
|
"SEXY_COMPOUND", {
|
||||||
|
new blt::nbt::tag_list(
|
||||||
|
"my list", {
|
||||||
|
new blt::nbt::tag_long("", 1230),
|
||||||
|
new blt::nbt::tag_long("", 2),
|
||||||
|
new blt::nbt::tag_long("", 50340535),
|
||||||
|
new blt::nbt::tag_long("", 55),
|
||||||
|
new blt::nbt::tag_long("", 256),
|
||||||
|
new blt::nbt::tag_long("", 512),
|
||||||
|
new blt::nbt::tag_long("", 9999999999),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new blt::nbt::tag_double("OMG IT'S A DOUBLE", 1320.04324),
|
||||||
|
new blt::nbt::tag_float("OMG IT'S A FLOAT", 12.04324),
|
||||||
|
new blt::nbt::tag_compound(
|
||||||
|
"Triple", {
|
||||||
|
new blt::nbt::tag_int("Test int", 32),
|
||||||
|
new blt::nbt::tag_byte_array(
|
||||||
|
"super array", {
|
||||||
|
51, 23, 12, 04, 33, 53, 11, 22, 3, 93, 120
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new blt::nbt::tag_string("I am a string", "I have stringy contents"),
|
||||||
|
new blt::nbt::tag_string("name", "Bananrama"),
|
||||||
|
new blt::nbt::tag_int_array(
|
||||||
|
"int array", {
|
||||||
|
1230, 234023, 21300, 2309230, 2340230, 2, 1, 32, 3265, 12, 53, 123, 7,
|
||||||
|
56, 12
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new blt::nbt::tag_long_array(
|
||||||
|
"valid", {
|
||||||
|
1230, 5320, 323200234402304, 230023, 23042034, 230230, 2301203,
|
||||||
|
123010230, 12300123
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
blockWriter.flush();
|
||||||
|
nbtFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
BLT_TRACE("Hello Trace!");
|
std::fstream nbtInputFile("super_file.nbt", std::ios::in | std::ios::binary);
|
||||||
BLT_DEBUG("Hello Debug!");
|
blt::fs::fstream_block_reader blockReader(nbtInputFile);
|
||||||
BLT_INFO("Hello Info!");
|
blt::nbt::NBTReader nbtReader(blockReader);
|
||||||
BLT_WARN("Hello Warn!");
|
nbtReader.read();
|
||||||
BLT_ERROR("Hello Error!");
|
|
||||||
BLT_FATAL("Hello Fatal!");
|
auto shortTag = nbtReader.getTag<blt::nbt::tag_short>("shortTest");
|
||||||
|
BLT_TRACE("Got short: %d", shortTag->get());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -14,12 +14,12 @@
|
||||||
#include <blt/std/filesystem.h>
|
#include <blt/std/filesystem.h>
|
||||||
|
|
||||||
inline bool readLargeBlockUsingNBTBufferedReader(const std::string& file, const blt::scoped_buffer<char>& bufferToCompare, size_t bufferSize) {
|
inline bool readLargeBlockUsingNBTBufferedReader(const std::string& file, const blt::scoped_buffer<char>& bufferToCompare, size_t bufferSize) {
|
||||||
blt::scoped_buffer<char> read_buffer{bufferToCompare.size};
|
blt::scoped_buffer<char> read_buffer{bufferToCompare.size()};
|
||||||
std::fstream largeBlockInputLarge(file, std::ios::in | std::ios::binary);
|
std::fstream largeBlockInputLarge(file, std::ios::in | std::ios::binary);
|
||||||
blt::fs::fstream_block_reader byteLargeBlockInputLarge(largeBlockInputLarge, bufferSize);
|
blt::fs::fstream_block_reader byteLargeBlockInputLarge(largeBlockInputLarge, bufferSize);
|
||||||
byteLargeBlockInputLarge.read(read_buffer.buffer, bufferToCompare.size);
|
byteLargeBlockInputLarge.read(*read_buffer, bufferToCompare.size());
|
||||||
for (unsigned int i = 0; i < bufferToCompare.size; i++) {
|
for (unsigned int i = 0; i < bufferToCompare.size(); i++) {
|
||||||
if (read_buffer[i] != bufferToCompare.buffer[i])
|
if (read_buffer[i] != bufferToCompare[i])
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
largeBlockInputLarge.close();
|
largeBlockInputLarge.close();
|
||||||
|
@ -29,7 +29,7 @@ inline bool readLargeBlockUsingNBTBufferedReader(const std::string& file, const
|
||||||
inline bool readIndividualUsingNBTBufferedReader(const std::string& file, const blt::scoped_buffer<char>& bufferToCompare, size_t bufferSize) {
|
inline bool readIndividualUsingNBTBufferedReader(const std::string& file, const blt::scoped_buffer<char>& bufferToCompare, size_t bufferSize) {
|
||||||
std::fstream largeBlockInput(file, std::ios::in | std::ios::binary);
|
std::fstream largeBlockInput(file, std::ios::in | std::ios::binary);
|
||||||
blt::fs::fstream_block_reader byteLargeBlockInput(largeBlockInput, bufferSize);
|
blt::fs::fstream_block_reader byteLargeBlockInput(largeBlockInput, bufferSize);
|
||||||
for (unsigned int i = 0; i < bufferToCompare.size; i++) {
|
for (unsigned int i = 0; i < bufferToCompare.size(); i++) {
|
||||||
char byte;
|
char byte;
|
||||||
byteLargeBlockInput.read(&byte, 1);
|
byteLargeBlockInput.read(&byte, 1);
|
||||||
if (byte != bufferToCompare[i]) {
|
if (byte != bufferToCompare[i]) {
|
||||||
|
@ -52,11 +52,11 @@ inline void nbt_read_tests(){
|
||||||
bool fstream_large_correct = true;
|
bool fstream_large_correct = true;
|
||||||
|
|
||||||
for (int i = 0; i < bufferSize; i++)
|
for (int i = 0; i < bufferSize; i++)
|
||||||
buffer.buffer[i] = i+1;
|
buffer[i] = i + 1;
|
||||||
|
|
||||||
BLT_START_INTERVAL("nbt read", "Raw Write");
|
BLT_START_INTERVAL("nbt read", "Raw Write");
|
||||||
std::fstream largeOutput("HeyThere.txt", std::ios::out | std::ios::binary);
|
std::fstream largeOutput("HeyThere.txt", std::ios::out | std::ios::binary);
|
||||||
largeOutput.write(buffer.buffer, bufferSize);
|
largeOutput.write(*buffer, bufferSize);
|
||||||
largeOutput.flush();
|
largeOutput.flush();
|
||||||
largeOutput.close();
|
largeOutput.close();
|
||||||
BLT_END_INTERVAL("nbt read", "Raw Write");
|
BLT_END_INTERVAL("nbt read", "Raw Write");
|
||||||
|
@ -129,7 +129,7 @@ inline void nbt_write_tests(){
|
||||||
blt::scoped_buffer<char> read_buffer{bufferSize};
|
blt::scoped_buffer<char> read_buffer{bufferSize};
|
||||||
|
|
||||||
for (int i = 0; i < bufferSize; i++)
|
for (int i = 0; i < bufferSize; i++)
|
||||||
buffer.buffer[i] = i+1;
|
buffer[i] = i + 1;
|
||||||
|
|
||||||
std::fstream fileOutput("IAmAFile.txt", std::ios::binary | std::ios::out);
|
std::fstream fileOutput("IAmAFile.txt", std::ios::binary | std::ios::out);
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
|
@ -139,11 +139,11 @@ inline void nbt_write_tests(){
|
||||||
blt::fs::fstream_block_writer writer(fileOutput, size);
|
blt::fs::fstream_block_writer writer(fileOutput, size);
|
||||||
|
|
||||||
BLT_START_INTERVAL("nbt write block", profiler_string);
|
BLT_START_INTERVAL("nbt write block", profiler_string);
|
||||||
writer.write(buffer.buffer, buffer.size);
|
writer.write(*buffer, buffer.size());
|
||||||
BLT_END_INTERVAL("nbt write block", profiler_string);
|
BLT_END_INTERVAL("nbt write block", profiler_string);
|
||||||
BLT_START_INTERVAL("nbt write individual", profiler_string);
|
BLT_START_INTERVAL("nbt write individual", profiler_string);
|
||||||
for (int j = 0; j < bufferSize; j++) {
|
for (int j = 0; j < bufferSize; j++) {
|
||||||
writer.write(&buffer.buffer[j], 1);
|
writer.write(&buffer[j], 1);
|
||||||
}
|
}
|
||||||
BLT_END_INTERVAL("nbt write individual", profiler_string);
|
BLT_END_INTERVAL("nbt write individual", profiler_string);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ inline void nbt_write_tests(){
|
||||||
auto size = (size_t) std::pow(2, 11 + i);
|
auto size = (size_t) std::pow(2, 11 + i);
|
||||||
auto size_str = std::to_string(size);
|
auto size_str = std::to_string(size);
|
||||||
bool results = true;
|
bool results = true;
|
||||||
fileInput.read(read_buffer.buffer, bufferSize);
|
fileInput.read(*read_buffer, bufferSize);
|
||||||
for (int j = 0; j < bufferSize; j++) {
|
for (int j = 0; j < bufferSize; j++) {
|
||||||
if (buffer[j] != read_buffer[j]) {
|
if (buffer[j] != read_buffer[j]) {
|
||||||
results = false;
|
results = false;
|
||||||
|
@ -166,7 +166,7 @@ inline void nbt_write_tests(){
|
||||||
BLT_INFO("NBT %s Block Write Correctly? %s;\n", size_str.c_str(), results ? "True" : "False");
|
BLT_INFO("NBT %s Block Write Correctly? %s;\n", size_str.c_str(), results ? "True" : "False");
|
||||||
|
|
||||||
results = true;
|
results = true;
|
||||||
fileInput.read(read_buffer.buffer, bufferSize);
|
fileInput.read(*read_buffer, bufferSize);
|
||||||
for (int j = 0; j < bufferSize; j++) {
|
for (int j = 0; j < bufferSize; j++) {
|
||||||
if (buffer[j] != read_buffer[j]) {
|
if (buffer[j] != read_buffer[j]) {
|
||||||
results = false;
|
results = false;
|
||||||
|
@ -193,7 +193,7 @@ inline void nbt_tests(){
|
||||||
testOutput.write(testByte, 3);
|
testOutput.write(testByte, 3);
|
||||||
testOutput.write(reinterpret_cast<char*>(&testShort), sizeof(short));
|
testOutput.write(reinterpret_cast<char*>(&testShort), sizeof(short));
|
||||||
testOutput.write(reinterpret_cast<char*>(&testInt), sizeof(int));
|
testOutput.write(reinterpret_cast<char*>(&testInt), sizeof(int));
|
||||||
blt::nbt::writeUTF8String(testOutput, "HelloHowManyCanWeFit!");
|
//blt::nbt::writeUTF8String(testOutput, "HelloHowManyCanWeFit!");
|
||||||
|
|
||||||
//testOutput.flush();
|
//testOutput.flush();
|
||||||
testOutput.close();
|
testOutput.close();
|
||||||
|
@ -207,10 +207,10 @@ inline void nbt_tests(){
|
||||||
testInput.read(testByteIn, 3);
|
testInput.read(testByteIn, 3);
|
||||||
testInput.read(reinterpret_cast<char*>(&testShortIn), sizeof(short));
|
testInput.read(reinterpret_cast<char*>(&testShortIn), sizeof(short));
|
||||||
testInput.read(reinterpret_cast<char*>(&testIntIn), sizeof(int));
|
testInput.read(reinterpret_cast<char*>(&testIntIn), sizeof(int));
|
||||||
std::string strIn = blt::nbt::readUTF8String(testInput);
|
//std::string strIn = blt::nbt::readUTF8String(testInput);
|
||||||
|
|
||||||
testInput.close();
|
testInput.close();
|
||||||
BLT_INFO("%d, %c, %d, %d, %d, %s", testByteIn[0], testByteIn[1], testByteIn[2], testShortIn, testIntIn, strIn.c_str());
|
//BLT_INFO("%d, %c, %d, %d, %d, %s", testByteIn[0], testByteIn[1], testByteIn[2], testShortIn, testIntIn, strIn.c_str());
|
||||||
|
|
||||||
nbt_read_tests();
|
nbt_read_tests();
|
||||||
nbt_write_tests();
|
nbt_write_tests();
|
||||||
|
|
|
@ -129,7 +129,7 @@ static inline void random_access() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void test_queues() {
|
static inline void test_queues() {
|
||||||
blt::random<int, std::uniform_int_distribution> rand{1, 100};
|
blt::random::random<int, std::uniform_int_distribution> rand{1, 100};
|
||||||
|
|
||||||
for (int& value : values){
|
for (int& value : values){
|
||||||
value = rand.get();
|
value = rand.get();
|
||||||
|
|
Loading…
Reference in New Issue