commit
eba9ecb9e5
|
@ -4,3 +4,6 @@ cmake-build-debug/
|
|||
/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)
|
||||
project(BLT VERSION 0.6.0)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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_PROFILING "Build the BLT profiler 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(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)
|
||||
|
@ -35,6 +39,17 @@ else()
|
|||
set(NBT_FILES "")
|
||||
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.
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
|
@ -52,18 +67,22 @@ message("Profiler Files ${PROFILING_FILES}")
|
|||
message("Source: ${CMAKE_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 ${CMAKE_CURRENT_BINARY_DIR}/config/)
|
||||
if(${ZLIB_FOUND})
|
||||
target_link_libraries(BLT PUBLIC ZLIB::ZLIB)
|
||||
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)
|
||||
#target_compile_options(BLT PRIVATE /W4)
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
@ -72,11 +91,6 @@ message("BLT ${CMAKE_PROJECT_VERSION} Successfully included!")
|
|||
if(${BUILD_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")
|
||||
|
||||
add_executable(BLT_TESTS ${TESTS_FILES})
|
||||
|
@ -92,5 +106,20 @@ if(${BUILD_TESTS})
|
|||
target_compile_options(BLT_TESTS PRIVATE -Wall -Wextra -Wpedantic)
|
||||
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!")
|
||||
endif()
|
||||
|
|
23
README.md
23
README.md
|
@ -1,5 +1,5 @@
|
|||
# **BLT v0.6.0a**
|
||||
A common utilities library I find useful
|
||||
# **BLT v0.8.0a**
|
||||
A C++20 common utilities library to make thing easy!
|
||||
|
||||
![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
|
||||
- Queue / Stack
|
||||
- faster than std::queue / std::stack
|
||||
- Binary Tree
|
||||
- Hashmap (TODO)
|
||||
- backed by a contiguous array
|
||||
- ## Utility
|
||||
- Simple Random Interface
|
||||
- No more worrying about min/max bounds!
|
||||
- Simple Random Wrapper Interface
|
||||
- Simple random functions based on the PCG Hash
|
||||
- ### String Functions
|
||||
- starts_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
|
||||
- trim
|
||||
- Logging
|
||||
- Trace / Debug / Info / Warn / Error / Fatal
|
||||
- Log to file
|
||||
- Log to console with color!
|
||||
- Easy to disable for release
|
||||
- define BLT_DISABLE_LOGGING before including the logging.h file.
|
||||
- See blt::logging section above
|
||||
- Time
|
||||
- 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
|
||||
- ## Profiling
|
||||
- Basic profiler
|
||||
- WIP
|
||||
- Basic profiler with history and formatted output
|
|
@ -20,6 +20,8 @@ namespace blt {
|
|||
return ((seed * (seed * seed * 15731 + 789221) + 1376312589) & 0x7fffffff);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
/**
|
||||
* fast inverse sqrt
|
||||
*/
|
||||
|
@ -36,6 +38,28 @@ namespace blt {
|
|||
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) {
|
||||
return out << "\rMatrix4x4{" << v.m00() << ", " << v.m01() << ", " << v.m02() << ", " << v.m03() << "} \n"\
|
||||
<< " {" << v.m10() << ", " << v.m11() << ", " << v.m12() << ", " << v.m13() << "} \n"\
|
||||
|
|
|
@ -7,53 +7,460 @@
|
|||
#ifndef 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/filesystem.h"
|
||||
#include "blt/std/logging.h"
|
||||
|
||||
#include <blt/std/hashmap.h>
|
||||
|
||||
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 {
|
||||
tag_end = 0,
|
||||
tag_byte = 1,
|
||||
tag_short = 2,
|
||||
tag_int = 3,
|
||||
tag_long = 4,
|
||||
tag_float = 5,
|
||||
tag_double = 6,
|
||||
tag_byte_array = 7,
|
||||
tag_string = 8,
|
||||
tag_list = 9,
|
||||
tag_compound = 10,
|
||||
tag_int_array = 11,
|
||||
tag_long_array = 12
|
||||
std::string readUTF8String(blt::fs::block_reader& stream);
|
||||
|
||||
// Used to grab the byte-data of any T element. Defaults to Big Endian, however can be configured to use little endian
|
||||
template <typename T>
|
||||
inline static int toBytes(const T& in, char* out) {
|
||||
std::memcpy(out, (void*) &in, sizeof(T));
|
||||
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
for (size_t i = 0; i < sizeof(T) / 2; i++)
|
||||
std::swap(out[i], out[sizeof(T) - 1 - i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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:
|
||||
virtual void readTag() = 0;
|
||||
virtual void writeTag() = 0;
|
||||
explicit tag_t(nbt_tag type): type(type) {};
|
||||
explicit tag_t(nbt_tag type, std::string name): type(type), name(std::move(name)) {}
|
||||
virtual void writePayload(blt::fs::block_writer& out) = 0;
|
||||
virtual void readPayload(blt::fs::block_reader& in) = 0;
|
||||
void writeName(blt::fs::block_writer& out) {
|
||||
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 {
|
||||
private:
|
||||
blt::fs::block_reader* m_reader;
|
||||
template<typename T>
|
||||
class tag : public tag_t {
|
||||
protected:
|
||||
T t;
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads the entire NBT file when the read() function is called.
|
||||
*/
|
||||
class tag_end : public tag<char> {
|
||||
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 {
|
||||
private:
|
||||
std::string m_file;
|
||||
|
||||
blt::fs::block_reader& reader;
|
||||
tag_compound* root = nullptr;
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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.
|
||||
|
@ -65,11 +75,10 @@ namespace blt::fs {
|
|||
class fstream_block_reader : public block_reader {
|
||||
private:
|
||||
std::fstream& m_stream;
|
||||
char* m_buffer;
|
||||
char* m_buffer = nullptr;
|
||||
size_t readIndex = 0;
|
||||
public:
|
||||
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize):
|
||||
block_reader(bufferSize), m_stream(stream), m_buffer(new char[bufferSize]) {}
|
||||
explicit fstream_block_reader(std::fstream& stream, size_t bufferSize = 131072);
|
||||
|
||||
explicit fstream_block_reader(fstream_block_reader& copy) = delete;
|
||||
|
||||
|
@ -91,8 +100,9 @@ namespace blt::fs {
|
|||
std::fstream& m_stream;
|
||||
char* m_buffer;
|
||||
size_t writeIndex = 0;
|
||||
void flush_internal();
|
||||
public:
|
||||
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize):
|
||||
explicit fstream_block_writer(std::fstream& stream, size_t bufferSize = 131072):
|
||||
block_writer(bufferSize), m_stream(stream), m_buffer(new char[bufferSize]) {}
|
||||
|
||||
explicit fstream_block_writer(fstream_block_writer& copy) = delete;
|
||||
|
@ -104,9 +114,12 @@ namespace blt::fs {
|
|||
fstream_block_writer& operator=(const fstream_block_writer&& move) = delete;
|
||||
|
||||
int write(char* buffer, size_t bytes) override;
|
||||
void flush() override;
|
||||
inline void flush() override {
|
||||
flush_internal();
|
||||
}
|
||||
|
||||
~fstream_block_writer() {
|
||||
flush_internal();
|
||||
delete[] m_buffer;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,27 +10,9 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <blt/math/math.h>
|
||||
|
||||
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){
|
||||
if (bytes > 1073741824) {
|
||||
|
@ -87,8 +69,7 @@ namespace blt::string {
|
|||
|
||||
// taken from java, adapted for c++.
|
||||
static inline utf8_string createUTFString(const std::string& str) {
|
||||
|
||||
const unsigned int strlen = (unsigned int) str.size();
|
||||
const auto strlen = (unsigned int) str.size();
|
||||
unsigned int utflen = strlen;
|
||||
|
||||
for (unsigned int i = 0; i < strlen; i++) {
|
||||
|
@ -105,8 +86,8 @@ namespace blt::string {
|
|||
chars.characters = new char[chars.size];
|
||||
|
||||
int count = 0;
|
||||
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
|
||||
chars.characters[count++] = (char) ((utflen >> 8) & 0xFF);
|
||||
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
|
||||
|
||||
unsigned int i = 0;
|
||||
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!
|
||||
bool ensureAlignment = false;
|
||||
// should we log to file?
|
||||
bool logToFile = true;
|
||||
bool logToFile = false;
|
||||
// should we log to console?
|
||||
bool logToConsole = true;
|
||||
// where should we log? (empty for current binary directory) should end in a / if not empty!
|
||||
|
@ -178,13 +178,14 @@ namespace blt::logging {
|
|||
size_t size;
|
||||
|
||||
[[nodiscard]] static inline size_t hash(const tag& t) {
|
||||
size_t h = t.tag[0]+ t.tag[1] * 3;
|
||||
return h;
|
||||
size_t h = t.tag[1] * 3 - t.tag[0];
|
||||
return h - 100;
|
||||
}
|
||||
|
||||
inline void expand(){
|
||||
// TODO: fix
|
||||
void expand() {
|
||||
auto newSize = size * 2;
|
||||
tag* newTags = new tag[newSize];
|
||||
auto newTags = new tag[newSize];
|
||||
for (size_t i = 0; i < size; i++)
|
||||
newTags[i] = tags[i];
|
||||
delete[] tags;
|
||||
|
@ -193,19 +194,26 @@ namespace blt::logging {
|
|||
}
|
||||
public:
|
||||
tag_map(std::initializer_list<tag> initial_tags){
|
||||
tags = new tag[(size = 16)];
|
||||
for (auto& t : initial_tags)
|
||||
size_t max = 0;
|
||||
for (const auto& t : initial_tags)
|
||||
max = std::max(max, hash(t));
|
||||
tags = new tag[(size = max+1)];
|
||||
for (const auto& t : initial_tags)
|
||||
insert(t);
|
||||
}
|
||||
tag_map(const tag_map& copy) {
|
||||
tags = new tag[(size = copy.size)];
|
||||
for (size_t i = 0; i < size; i++)
|
||||
tags[i] = copy.tags[i];
|
||||
}
|
||||
|
||||
tag_map& insert(const tag& t){
|
||||
void insert(const tag& t) {
|
||||
auto h = hash(t);
|
||||
if (h > size)
|
||||
expand();
|
||||
//if (h > size)
|
||||
// expand();
|
||||
if (!tags[h].tag.empty())
|
||||
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
|
||||
tags[h] = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tag& operator[](const std::string& name) const {
|
||||
|
@ -217,6 +225,7 @@ namespace blt::logging {
|
|||
|
||||
~tag_map(){
|
||||
delete[] tags;
|
||||
tags = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -290,7 +299,7 @@ namespace blt::logging {
|
|||
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
|
||||
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 {
|
||||
BLT_NOW();
|
||||
return std::to_string(now->tm_year);
|
||||
|
@ -374,8 +383,8 @@ namespace blt::logging {
|
|||
}},
|
||||
{"STR", [](const tag_func_param& f) -> std::string {
|
||||
return f.formatted_string;
|
||||
}},
|
||||
};
|
||||
}}
|
||||
});
|
||||
|
||||
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
|
||||
size_t pos = 0;
|
||||
|
@ -489,7 +498,7 @@ namespace blt::logging {
|
|||
tag_func_param param{
|
||||
level, filename({file}), std::to_string(line), userStr, userStr
|
||||
};
|
||||
out += tagMap[tag].func(param);
|
||||
out += (*tagMap)[tag].func(param);
|
||||
} else {
|
||||
out += c;
|
||||
out += nonTag;
|
||||
|
@ -522,6 +531,11 @@ namespace blt::logging {
|
|||
|
||||
applyCFormatting(withoutLn, out, args);
|
||||
|
||||
if (level == log_level::NONE){
|
||||
std::cout << out << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string finalFormattedOutput = applyFormatString(out, level, file, line);
|
||||
|
||||
if (loggingFormat.logToConsole)
|
||||
|
|
|
@ -58,38 +58,67 @@ namespace blt {
|
|||
* 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.
|
||||
* 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 & 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.
|
||||
* The operator * has been overloaded to return the internal buffer.
|
||||
* @tparam T type that is stored in buffer eg char
|
||||
*/
|
||||
template<typename T>
|
||||
struct scoped_buffer {
|
||||
T* buffer;
|
||||
unsigned long size;
|
||||
|
||||
explicit scoped_buffer(unsigned long size): size(size) {
|
||||
buffer = new T[size];
|
||||
private:
|
||||
T* _buffer;
|
||||
size_t _size;
|
||||
public:
|
||||
explicit scoped_buffer(size_t size): _size(size) {
|
||||
_buffer = new T[size];
|
||||
}
|
||||
|
||||
scoped_buffer(scoped_buffer& copy) = delete;
|
||||
scoped_buffer(const scoped_buffer& copy) = delete;
|
||||
|
||||
scoped_buffer(scoped_buffer&& move) = delete;
|
||||
scoped_buffer(scoped_buffer&& move) noexcept {
|
||||
_buffer = move._buffer;
|
||||
_size = move.size();
|
||||
move._buffer = nullptr;
|
||||
}
|
||||
|
||||
scoped_buffer operator=(scoped_buffer& copyAssignment) = delete;
|
||||
|
||||
scoped_buffer operator=(scoped_buffer&& moveAssignment) = delete;
|
||||
scoped_buffer& operator=(scoped_buffer&& moveAssignment) noexcept {
|
||||
_buffer = moveAssignment._buffer;
|
||||
_size = moveAssignment.size();
|
||||
moveAssignment._buffer = nullptr;
|
||||
|
||||
inline T& operator[](unsigned long index) const {
|
||||
return buffer[index];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline T& operator[](unsigned long index) {
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned long index) const {
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
inline T* operator*(){
|
||||
return 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;
|
||||
delete[] _buffer;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -168,6 +197,7 @@ namespace blt {
|
|||
delete[] _values;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_TESTS_MEMORY_H
|
||||
|
|
|
@ -9,7 +9,41 @@
|
|||
|
||||
#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
|
||||
* @tparam T numeric type
|
||||
|
@ -28,17 +62,19 @@ namespace blt {
|
|||
* @param max max value possible to generate. (default: 1)
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
T get(){
|
||||
T get() {
|
||||
return (*distribution)(gen);
|
||||
}
|
||||
~random(){
|
||||
|
||||
~random() {
|
||||
delete distribution;
|
||||
}
|
||||
};
|
||||
|
@ -52,6 +88,7 @@ namespace blt {
|
|||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_RANDOM_H
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <blt/std/logging.h>
|
||||
|
||||
namespace blt::string {
|
||||
|
||||
|
@ -140,6 +139,26 @@ namespace blt::string {
|
|||
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
|
||||
// would've preferred to use boost lib but instructions said to avoid external libs
|
||||
// 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();
|
||||
}
|
||||
|
||||
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:
|
||||
* Year-Month-Date Hour:Min:Second
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3
|
|
@ -4,18 +4,22 @@
|
|||
* See LICENSE file for license detail
|
||||
*/
|
||||
#include <blt/nbt/nbt.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
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);
|
||||
stream.write(str8.characters, str8.size);
|
||||
delete[] str8.characters;
|
||||
}
|
||||
|
||||
std::string readUTF8String(std::fstream& stream) {
|
||||
unsigned short utflen;
|
||||
std::string readUTF8String(blt::fs::block_reader& stream) {
|
||||
int16_t utflen;
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&utflen), sizeof(utflen));
|
||||
readData(stream, utflen);
|
||||
|
||||
blt::string::utf8_string str{};
|
||||
str.size = utflen;
|
||||
|
@ -23,8 +27,20 @@ namespace blt::nbt {
|
|||
|
||||
stream.read(str.characters, str.size);
|
||||
|
||||
auto strOut = std::move(blt::string::getStringFromUTF8(str));
|
||||
auto strOut = blt::string::getStringFromUTF8(str);
|
||||
delete[] str.characters;
|
||||
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 <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) {
|
||||
if (readIndex == 0)
|
||||
|
@ -40,7 +48,7 @@ int blt::fs::fstream_block_writer::write(char* buffer, size_t bytes) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void blt::fs::fstream_block_writer::flush() {
|
||||
void blt::fs::fstream_block_writer::flush_internal() {
|
||||
m_stream.write(m_buffer, (long) writeIndex);
|
||||
writeIndex = 0;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef 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(){
|
||||
|
||||
|
|
|
@ -1,61 +1,313 @@
|
|||
|
||||
#include <unordered_map>
|
||||
#include "binary_trees.h"
|
||||
#include "logging.h"
|
||||
//#include "binary_trees.h"
|
||||
//#include "logging.h"
|
||||
#include "profiling_tests.h"
|
||||
#include "nbt_tests.h"
|
||||
#include "queue_tests.h"
|
||||
#include "blt/math/vectors.h"
|
||||
#include "blt/math/matrix.h"
|
||||
#include <bitset>
|
||||
#include "hashmap_tests.h"
|
||||
#include "blt/parse/argparse.h"
|
||||
//#include "queue_tests.h"
|
||||
//#include "blt/math/vectors.h"
|
||||
//#include "blt/math/matrix.h"
|
||||
//#include <bitset>
|
||||
//#include "hashmap_tests.h"
|
||||
//#include <functional>
|
||||
|
||||
int main() {
|
||||
binaryTreeTest();
|
||||
std::function<int(int i)> test{
|
||||
[](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";
|
||||
BLT_TRACE1_STREAM << 5 << "\n";
|
||||
BLT_TRACE2_STREAM << 5 << "\n";
|
||||
BLT_TRACE3_STREAM << 5 << "\n";
|
||||
BLT_TRACE_STREAM << "TRACEY\n";
|
||||
class class_func : public super_func {
|
||||
public:
|
||||
int test(int i) override {
|
||||
int acc = 1;
|
||||
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};
|
||||
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);
|
||||
int main(int argc, const char** argv) {
|
||||
blt::arg_parse parser;
|
||||
parser.addArgument(blt::arg_builder({"--poo", "-p"}).build());
|
||||
parser.addArgument(blt::arg_builder("--foo").setAction(blt::arg_action_t::STORE_TRUE).setDefault(false).build());
|
||||
parser.addArgument(blt::arg_builder({"--goo", "-g"}).build());
|
||||
parser.addArgument(blt::arg_builder({"--oop", "-o"}).build());
|
||||
parser.addArgument(blt::arg_builder("Sexy_pos").setHelp("I am helpful!").build());
|
||||
|
||||
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!");
|
||||
return 1;
|
||||
{
|
||||
std::fstream nbtFile("super_file.nbt", std::ios::out | std::ios::binary);
|
||||
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!");
|
||||
BLT_DEBUG("Hello Debug!");
|
||||
BLT_INFO("Hello Info!");
|
||||
BLT_WARN("Hello Warn!");
|
||||
BLT_ERROR("Hello Error!");
|
||||
BLT_FATAL("Hello Fatal!");
|
||||
std::fstream nbtInputFile("super_file.nbt", std::ios::in | std::ios::binary);
|
||||
blt::fs::fstream_block_reader blockReader(nbtInputFile);
|
||||
blt::nbt::NBTReader nbtReader(blockReader);
|
||||
nbtReader.read();
|
||||
|
||||
auto shortTag = nbtReader.getTag<blt::nbt::tag_short>("shortTest");
|
||||
BLT_TRACE("Got short: %d", shortTag->get());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -14,12 +14,12 @@
|
|||
#include <blt/std/filesystem.h>
|
||||
|
||||
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);
|
||||
blt::fs::fstream_block_reader byteLargeBlockInputLarge(largeBlockInputLarge, bufferSize);
|
||||
byteLargeBlockInputLarge.read(read_buffer.buffer, bufferToCompare.size);
|
||||
for (unsigned int i = 0; i < bufferToCompare.size; i++) {
|
||||
if (read_buffer[i] != bufferToCompare.buffer[i])
|
||||
byteLargeBlockInputLarge.read(*read_buffer, bufferToCompare.size());
|
||||
for (unsigned int i = 0; i < bufferToCompare.size(); i++) {
|
||||
if (read_buffer[i] != bufferToCompare[i])
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
std::fstream largeBlockInput(file, std::ios::in | std::ios::binary);
|
||||
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;
|
||||
byteLargeBlockInput.read(&byte, 1);
|
||||
if (byte != bufferToCompare[i]) {
|
||||
|
@ -52,11 +52,11 @@ inline void nbt_read_tests(){
|
|||
bool fstream_large_correct = true;
|
||||
|
||||
for (int i = 0; i < bufferSize; i++)
|
||||
buffer.buffer[i] = i+1;
|
||||
buffer[i] = i + 1;
|
||||
|
||||
BLT_START_INTERVAL("nbt read", "Raw Write");
|
||||
std::fstream largeOutput("HeyThere.txt", std::ios::out | std::ios::binary);
|
||||
largeOutput.write(buffer.buffer, bufferSize);
|
||||
largeOutput.write(*buffer, bufferSize);
|
||||
largeOutput.flush();
|
||||
largeOutput.close();
|
||||
BLT_END_INTERVAL("nbt read", "Raw Write");
|
||||
|
@ -129,7 +129,7 @@ inline void nbt_write_tests(){
|
|||
blt::scoped_buffer<char> read_buffer{bufferSize};
|
||||
|
||||
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);
|
||||
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_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_START_INTERVAL("nbt write individual", profiler_string);
|
||||
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);
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ inline void nbt_write_tests(){
|
|||
auto size = (size_t) std::pow(2, 11 + i);
|
||||
auto size_str = std::to_string(size);
|
||||
bool results = true;
|
||||
fileInput.read(read_buffer.buffer, bufferSize);
|
||||
fileInput.read(*read_buffer, bufferSize);
|
||||
for (int j = 0; j < bufferSize; j++) {
|
||||
if (buffer[j] != read_buffer[j]) {
|
||||
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");
|
||||
|
||||
results = true;
|
||||
fileInput.read(read_buffer.buffer, bufferSize);
|
||||
fileInput.read(*read_buffer, bufferSize);
|
||||
for (int j = 0; j < bufferSize; j++) {
|
||||
if (buffer[j] != read_buffer[j]) {
|
||||
results = false;
|
||||
|
@ -193,7 +193,7 @@ inline void nbt_tests(){
|
|||
testOutput.write(testByte, 3);
|
||||
testOutput.write(reinterpret_cast<char*>(&testShort), sizeof(short));
|
||||
testOutput.write(reinterpret_cast<char*>(&testInt), sizeof(int));
|
||||
blt::nbt::writeUTF8String(testOutput, "HelloHowManyCanWeFit!");
|
||||
//blt::nbt::writeUTF8String(testOutput, "HelloHowManyCanWeFit!");
|
||||
|
||||
//testOutput.flush();
|
||||
testOutput.close();
|
||||
|
@ -207,10 +207,10 @@ inline void nbt_tests(){
|
|||
testInput.read(testByteIn, 3);
|
||||
testInput.read(reinterpret_cast<char*>(&testShortIn), sizeof(short));
|
||||
testInput.read(reinterpret_cast<char*>(&testIntIn), sizeof(int));
|
||||
std::string strIn = blt::nbt::readUTF8String(testInput);
|
||||
//std::string strIn = blt::nbt::readUTF8String(testInput);
|
||||
|
||||
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_write_tests();
|
||||
|
|
|
@ -129,7 +129,7 @@ static inline void random_access() {
|
|||
}
|
||||
|
||||
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){
|
||||
value = rand.get();
|
||||
|
|
Loading…
Reference in New Issue