Merge branch 'main-dev'

Stupid git why tf is it not on a branch
v1
Brett 2023-08-09 21:51:33 -04:00
commit eba9ecb9e5
23 changed files with 2043 additions and 216 deletions

3
.gitignore vendored
View File

@ -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/

View File

@ -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()

View File

@ -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

View File

@ -19,7 +19,9 @@ namespace blt {
seed = (seed << 13) ^ seed; seed = (seed << 13) ^ seed;
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
*/ */
@ -35,6 +37,28 @@ namespace blt {
y = y * (1.5f - (x * y * y)); y = y * (1.5f - (x * y * y));
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"\

View File

@ -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,
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
};
class nbt_tag { // Used to grab the byte-data of any T element. Defaults to Big Endian, however can be configured to use little endian
public: template <typename T>
virtual void readTag() = 0; inline static int toBytes(const T& in, char* out) {
virtual void writeTag() = 0; std::memcpy(out, (void*) &in, sizeof(T));
};
class NBTDecoder {
private:
blt::fs::block_reader* m_reader;
public:
explicit NBTDecoder(blt::fs::block_reader* reader): m_reader(reader) {}
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 tag_t {
* Reads the entire NBT file when the read() function is called. protected:
*/ nbt_tag type;
std::string name;
public:
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;
};
template<typename T>
class tag : public tag_t {
protected:
T t;
public:
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> {
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;
}; };
} }

382
include/blt/parse/argparse.h Executable file
View File

@ -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

View File

@ -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;
} }
}; };

View File

@ -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

View File

@ -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

45
include/blt/std/hashmap.h Executable file
View File

@ -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

View File

@ -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)

View File

@ -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;
explicit scoped_buffer(unsigned long size): size(size) { public:
buffer = new T[size]; 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 {
scoped_buffer operator=(scoped_buffer& copyAssignment) = delete; _buffer = move._buffer;
_size = move.size();
scoped_buffer operator=(scoped_buffer&& moveAssignment) = delete; move._buffer = nullptr;
}
inline T& operator[](unsigned long index) const {
return buffer[index]; scoped_buffer operator=(scoped_buffer& copyAssignment) = delete;
}
scoped_buffer& operator=(scoped_buffer&& moveAssignment) noexcept {
inline T* operator*(){ _buffer = moveAssignment._buffer;
return buffer; _size = moveAssignment.size();
} moveAssignment._buffer = nullptr;
~scoped_buffer() { return *this;
delete[] buffer; }
}
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;
}
[[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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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);
}
} }

572
src/blt/parse/argparse.cpp Executable file
View File

@ -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;
}
}

View File

@ -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;
} }

View File

@ -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(){

View File

@ -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;
}
};
int test_as_func(int i){
int acc = 1;
for (int j = 0; j < i; j++){
acc += j * i;
}
return acc;
}
inline int test_as_func_inline(int i){
int acc = 1;
for (int j = 0; j < i; j++){
acc += j * i;
}
return acc;
}
std::function<int(int i)> test_func_as_std(&test_as_func);
class super_func {
public:
virtual int test(int i) = 0;
virtual ~super_func() = default;
};
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;
}
};
int (*func_func)(int) = [](int i) -> int {
int acc = 1;
for (int j = 0; j < i; j++){
acc += j * i;
}
return acc;
};
int (*func_func_in)(int) = &test_as_func;
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());
run_logging(); 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);
runProfilingAndTableTests(); 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);
blt::logging::flush(); {
std::fstream nbtFile("super_file.nbt", std::ios::out | std::ios::binary);
nbt_tests(); blt::fs::fstream_block_writer blockWriter(nbtFile);
blt::nbt::NBTWriter nbtWriter(blockWriter);
BLT_TRACE0_STREAM << "Test Output!\n"; nbtWriter.write(
BLT_TRACE1_STREAM << 5 << "\n"; new blt::nbt::tag_compound(
BLT_TRACE2_STREAM << 5 << "\n"; "root", {
BLT_TRACE3_STREAM << 5 << "\n"; new blt::nbt::tag_byte("super_byte", 8),
BLT_TRACE_STREAM << "TRACEY\n"; new blt::nbt::tag_short("shortTest", 32767),
new blt::nbt::tag_compound(
blt::logging::flush(); "SEXY_COMPOUND", {
new blt::nbt::tag_list(
test_queues(); "my list", {
new blt::nbt::tag_long("", 1230),
blt::vec4 v{2, 5, 1, 8}; new blt::nbt::tag_long("", 2),
blt::mat4x4 m{}; new blt::nbt::tag_long("", 50340535),
m.m(0,0, 1); new blt::nbt::tag_long("", 55),
m.m(0,2, 2); new blt::nbt::tag_long("", 256),
m.m(1, 1, 3); new blt::nbt::tag_long("", 512),
m.m(1, 3, 4); new blt::nbt::tag_long("", 9999999999),
m.m(2, 2, 5); }
m.m(3, 0, 6); ),
m.m(3, 3, 7); new blt::nbt::tag_double("OMG IT'S A DOUBLE", 1320.04324),
new blt::nbt::tag_float("OMG IT'S A FLOAT", 12.04324),
auto result = m * v; new blt::nbt::tag_compound(
"Triple", {
std::cout << result.x() << " " << result.y() << " " << result.z() << " " << result.w() << std::endl; new blt::nbt::tag_int("Test int", 32),
new blt::nbt::tag_byte_array(
if (test_hashmaps()){ "super array", {
BLT_FATAL("Hashmap test failed!"); 51, 23, 12, 04, 33, 53, 11, 22, 3, 93, 120
return 1; }
),
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;
} }

View File

@ -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();

View File

@ -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();