diff options
author | rtk0c <[email protected]> | 2022-05-30 17:03:20 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-05-30 17:03:20 -0700 |
commit | e66286ebe30afc9acc4531fc2bea29b7fb924f93 (patch) | |
tree | fa6b76554c3eb88bc8f088fbab68e20c40118ca7 /source | |
parent | 366ef5a5450c6e0e680c924c3454943a9ae9814d (diff) |
Changeset: 56 Buildsystem cleanup: change to layered structure for different targets
Diffstat (limited to 'source')
-rw-r--r-- | source/10-common/Color.hpp (renamed from source/Color.hpp) | 0 | ||||
-rw-r--r-- | source/10-common/Enum.hpp | 103 | ||||
-rw-r--r-- | source/10-common/LookupTable.hpp | 53 | ||||
-rw-r--r-- | source/10-common/Macros.hpp | 31 | ||||
-rw-r--r-- | source/10-common/PodVector.hpp | 297 | ||||
-rw-r--r-- | source/10-common/RapidJsonHelper.hpp | 110 | ||||
-rw-r--r-- | source/10-common/RcPtr.hpp | 120 | ||||
-rw-r--r-- | source/10-common/Rect.hpp | 164 | ||||
-rw-r--r-- | source/10-common/ScopeGuard.hpp | 60 | ||||
-rw-r--r-- | source/10-common/SmallVector.cpp | 145 | ||||
-rw-r--r-- | source/10-common/SmallVector.hpp | 1332 | ||||
-rw-r--r-- | source/10-common/StbImplementations.c | 14 | ||||
-rw-r--r-- | source/10-common/TypeTraits.hpp | 19 | ||||
-rw-r--r-- | source/10-common/Uid.cpp | 58 | ||||
-rw-r--r-- | source/10-common/Uid.hpp | 42 | ||||
-rw-r--r-- | source/10-common/Utils.cpp | 87 | ||||
-rw-r--r-- | source/10-common/Utils.hpp | 61 | ||||
-rw-r--r-- | source/10-common/YCombinator.hpp | 14 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenConfig.hpp | 11 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenDecl.cpp | 49 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenDecl.hpp | 74 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenInput.inl | 69 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenMacros.hpp | 30 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenOutput.inl | 76 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenUtils.inl | 106 | ||||
-rw-r--r-- | source/20-codegen-compiler/main.cpp | 761 | ||||
-rw-r--r-- | source/20-codegen-compiler/test/examples/TestEnum.hpp.txt | 43 | ||||
-rw-r--r-- | source/20-codegen-runtime/MacrosCodegen.hpp | 7 | ||||
-rw-r--r-- | source/20-codegen-runtime/Metadata.cpp | 1 | ||||
-rw-r--r-- | source/20-codegen-runtime/Metadata.hpp | 4 | ||||
-rw-r--r-- | source/20-codegen-runtime/MetadataBase.cpp | 1 | ||||
-rw-r--r-- | source/20-codegen-runtime/MetadataBase.hpp | 14 | ||||
-rw-r--r-- | source/30-game/App.cpp (renamed from source/App.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/App.hpp (renamed from source/App.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/AppConfig.hpp (renamed from source/AppConfig.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Camera.cpp (renamed from source/Camera.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Camera.hpp (renamed from source/Camera.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/CommonVertexIndex.cpp (renamed from source/CommonVertexIndex.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/CommonVertexIndex.hpp (renamed from source/CommonVertexIndex.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorAccessories.cpp (renamed from source/EditorAccessories.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorAccessories.hpp (renamed from source/EditorAccessories.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorAttachment.hpp (renamed from source/EditorAttachment.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorAttachmentImpl.cpp (renamed from source/EditorAttachmentImpl.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorAttachmentImpl.hpp (renamed from source/EditorAttachmentImpl.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorCommandPalette.cpp (renamed from source/EditorCommandPalette.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorCommandPalette.hpp (renamed from source/EditorCommandPalette.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorCore.hpp (renamed from source/EditorCore.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorCorePrivate.cpp (renamed from source/EditorCorePrivate.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorCorePrivate.hpp (renamed from source/EditorCorePrivate.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorGuizmo.cpp (renamed from source/EditorGuizmo.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorGuizmo.hpp (renamed from source/EditorGuizmo.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorNotification.cpp (renamed from source/EditorNotification.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorNotification.hpp (renamed from source/EditorNotification.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorUtils.cpp (renamed from source/EditorUtils.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/EditorUtils.hpp (renamed from source/EditorUtils.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/FuzzyMatch.cpp (renamed from source/FuzzyMatch.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/FuzzyMatch.hpp (renamed from source/FuzzyMatch.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/GameObject.cpp (renamed from source/GameObject.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/GameObject.hpp (renamed from source/GameObject.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/GraphicsTags.cpp (renamed from source/GraphicsTags.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/GraphicsTags.hpp (renamed from source/GraphicsTags.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Image.cpp (renamed from source/Image.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Image.hpp (renamed from source/Image.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Ires.cpp (renamed from source/Ires.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Ires.hpp (renamed from source/Ires.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Level.cpp (renamed from source/Level.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Level.hpp (renamed from source/Level.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Material.cpp (renamed from source/Material.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Material.hpp (renamed from source/Material.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Mesh.cpp (renamed from source/Mesh.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Mesh.hpp (renamed from source/Mesh.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Player.cpp (renamed from source/Player.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Player.hpp (renamed from source/Player.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Renderer.cpp (renamed from source/Renderer.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Renderer.hpp (renamed from source/Renderer.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/SceneThings.cpp (renamed from source/SceneThings.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/SceneThings.hpp (renamed from source/SceneThings.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Shader.cpp (renamed from source/Shader.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Shader.hpp (renamed from source/Shader.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Sprite.cpp (renamed from source/Sprite.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Sprite.hpp (renamed from source/Sprite.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/Texture.cpp (renamed from source/Texture.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/Texture.hpp (renamed from source/Texture.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/VertexIndex.cpp (renamed from source/VertexIndex.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/VertexIndex.hpp (renamed from source/VertexIndex.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/World.cpp (renamed from source/World.cpp) | 0 | ||||
-rw-r--r-- | source/30-game/World.hpp (renamed from source/World.hpp) | 0 | ||||
-rw-r--r-- | source/30-game/main.cpp (renamed from source/main.cpp) | 0 |
88 files changed, 3956 insertions, 0 deletions
diff --git a/source/Color.hpp b/source/10-common/Color.hpp index ef0c5a9..ef0c5a9 100644 --- a/source/Color.hpp +++ b/source/10-common/Color.hpp diff --git a/source/10-common/Enum.hpp b/source/10-common/Enum.hpp new file mode 100644 index 0000000..8ad75ba --- /dev/null +++ b/source/10-common/Enum.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include <initializer_list> +#include <type_traits> + +template <class TEnum> +class EnumFlags { +public: + using Enum = TEnum; + using Underlying = std::underlying_type_t<TEnum>; + +private: + Underlying mValue; + +public: + EnumFlags() + : mValue{ 0 } { + } + + EnumFlags(TEnum e) + : mValue{ static_cast<Underlying>(1) << static_cast<Underlying>(e) } { + } + + bool IsSet(EnumFlags mask) const { + return (mValue & mask.mValue) == mask.mValue; + } + + bool IsSet(std::initializer_list<TEnum> enums) { + EnumFlags flags; + for (auto& e : enums) { + flags.mValue |= static_cast<Underlying>(e); + } + return IsSet(flags); + } + + bool IsSetExclusive(EnumFlags mask) const { + return mValue == mask.mValue; + } + + bool IsSetExclusive(std::initializer_list<TEnum> enums) { + EnumFlags flags; + for (auto& e : enums) { + flags.mValue |= static_cast<Underlying>(e); + } + return IsSetExclusive(flags); + } + + void SetOn(EnumFlags mask) { + mValue |= mask.mValue; + } + + void SetOff(EnumFlags mask) { + mValue &= ~mask.mValue; + } + + void Set(EnumFlags mask, bool enabled) { + if (enabled) { + SetOn(mask); + } else { + SetOff(mask); + } + } + + EnumFlags& operator|=(EnumFlags that) { + mValue |= that.mValue; + return *this; + } + + EnumFlags& operator&=(EnumFlags that) { + mValue &= that.mValue; + return *this; + } + + EnumFlags& operator^=(EnumFlags that) { + mValue ^= that.mValue; + return *this; + } + + EnumFlags& operator|=(TEnum e) { + mValue |= 1 << static_cast<Underlying>(e); + return *this; + } + + EnumFlags& operator&=(TEnum e) { + mValue &= 1 << static_cast<Underlying>(e); + return *this; + } + + EnumFlags& operator^=(TEnum e) { + mValue ^= 1 << static_cast<Underlying>(e); + return *this; + } + + EnumFlags operator|(EnumFlags that) const { return EnumFlags(mValue | that.mValue); } + EnumFlags operator&(EnumFlags that) const { return EnumFlags(mValue & that.mValue); } + EnumFlags operator^(EnumFlags that) const { return EnumFlags(mValue ^ that.mValue); } + + EnumFlags operator|(TEnum e) const { return EnumFlags(mValue | 1 << static_cast<Underlying>(e)); } + EnumFlags operator&(TEnum e) const { return EnumFlags(mValue & 1 << static_cast<Underlying>(e)); } + EnumFlags operator^(TEnum e) const { return EnumFlags(mValue ^ 1 << static_cast<Underlying>(e)); } + + EnumFlags operator~() const { return EnumFlags(~mValue); } +}; diff --git a/source/10-common/LookupTable.hpp b/source/10-common/LookupTable.hpp new file mode 100644 index 0000000..c50e28e --- /dev/null +++ b/source/10-common/LookupTable.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <robin_hood.h> +#include <string_view> + +// BIDI stands for bi-directional +#define BIDI_LUT_DECL(name, aType, aCount, bType, bCount) \ + int gLut_##name##_A2B[aCount]; \ + int gLut_##name##_B2A[bCount]; \ + using name##AType = aType; \ + using name##BType = bType; \ + void InitializeLut##name() +#define BIDI_LUT_MAP_FOR(name) \ + int* lutMappingA2B = gLut_##name##_A2B; \ + int* lutMappingB2A = gLut_##name##_B2A +#define BIDI_LUT_MAP(from, to) \ + lutMappingA2B[from] = to; \ + lutMappingB2A[to] = from +#define BIDI_LUT_INIT(name) InitializeLut##name() +#define BIDI_LUT_A2B_LOOKUP(name, from) (name##BType)(gLut_##name##_A2B[from]) +#define BIDI_LUT_B2A_LOOKUP(name, to) (name##AType)(gLut_##name##_B2A[to]) + +#define STR_LUT_DECL(name, enumMinValue, enumMaxValue) \ + constexpr int kLutMinVal_##name = enumMinValue; \ + const char* gLut_##name[(int)enumMaxValue - (int)enumMinValue]; \ + void InitializeLut##name() +#define STR_LUT_MAP_FOR(name) \ + const char** lutMapping = gLut_##name; \ + int lutMappingMinValue = kLutMinVal_##name +#define STR_LUT_MAP(value, text) lutMapping[value - lutMappingMinValue] = text +#define STR_LUT_MAP_ENUM(enumValue) STR_LUT_MAP(enumValue, #enumValue) +#define STR_LUT_LOOKUP(name, enumValue) gLut_##name[enumValue - kLutMinVal_##name] +#define STR_LUT_INIT(name) InitializeLut##name() + +// BSTR stands for bi-directional string +#define BSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \ + constexpr int kLutMinVal_##name = enumMinValue; \ + const char* gLut_##name##_V2S[(int)enumMaxValue - (int)enumMinValue]; \ + robin_hood::unordered_flat_map<std::string_view, decltype(enumMaxValue)> gLut_##name##_S2V; \ + void InitializeLut##name() +#define BSTR_LUT_MAP_FOR(name) \ + const char** lutMappingV2S = gLut_##name##_V2S; \ + auto& lutMappingS2V = gLut_##name##_S2V; \ + int lutMappingMinValue = kLutMinVal_##name +#define BSTR_LUT_MAP(value, text) \ + lutMappingV2S[value - lutMappingMinValue] = text; \ + lutMappingS2V.insert_or_assign(std::string_view(text), value); +#define BSTR_LUT_MAP_ENUM(enumValue) BSTR_LUT_MAP(enumValue, #enumValue) +#define BSTR_LUT_V2S(name) gLut_##name##_V2S +#define BSTR_LUT_S2V(name) gLut_##name##_S2V +#define BSTR_LUT_V2S_LOOKUP(name, enumValue) gLut_##name##_V2S[enumValue - kLutMinVal_##name] +#define BSTR_LUT_S2V_LOOKUP(name, string) gLut_##name##_S2V.find(std::string_view(text)) +#define BSTR_LUT_INIT(name) InitializeLut##name() diff --git a/source/10-common/Macros.hpp b/source/10-common/Macros.hpp new file mode 100644 index 0000000..a255ada --- /dev/null +++ b/source/10-common/Macros.hpp @@ -0,0 +1,31 @@ +#pragma once + +#define STRINGIFY_IMPL(text) #text +#define STRINGIFY(text) STRINGIFY_IMPL(text) + +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) +#define CONCAT_3(a, b, c) CONCAT(a, CONCAT(b, c)) +#define CONCAT_4(a, b, c, d) CONCAT(CONCAT(a, b), CONCAT(c, d)) + +#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__) +#define UNIQUE_NAME_LINE(prefix) CONCAT(prefix, __LINE__) +#define DISCARD UNIQUE_NAME(_discard) + +#define UNUSED(x) (void)x; + +#define PRINTF_STRING_VIEW(s) (int)s.size(), s.data() + +#if defined(_MSC_VER) +# define UNREACHABLE __assume(0) +#elif defined(__GNUC__) || defined(__clang__) +# define UNREACHABLE __builtin_unreachable() +#else +# define UNREACHABLE +#endif + +#if _WIN32 +# define PLATFORM_PATH_STR "%ls" +#else +# define PLATFORM_PATH_STR "%s" +#endif diff --git a/source/10-common/PodVector.hpp b/source/10-common/PodVector.hpp new file mode 100644 index 0000000..74e99d6 --- /dev/null +++ b/source/10-common/PodVector.hpp @@ -0,0 +1,297 @@ +// File adapted from dear-imgui's ImVector, implemented in https://github.com/ocornut/imgUI/blob/master/imgui.h +#pragma once + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <span> + +template <class T> +class PodVector { +public: + using value_type = T; + using iterator = value_type*; + using const_iterator = const value_type*; + +private: + int mSize; + int mCapacity; + T* mData; + +public: + PodVector() { + mSize = mCapacity = 0; + mData = nullptr; + } + + PodVector(const PodVector<T>& src) { + mSize = mCapacity = 0; + mData = nullptr; + operator=(src); + } + + PodVector<T>& operator=(const PodVector<T>& src) { + clear(); + resize(src.mSize); + std::memcpy(mData, src.mData, (size_t)mSize * sizeof(T)); + return *this; + } + + PodVector(PodVector&& src) { + mSize = src.mSize; + mCapacity = src.mCapacity; + mData = src.mData; + + src.mSize = src.mCapacity = 0; + src.mData = nullptr; + } + + PodVector& operator=(PodVector&& src) { + if (this != &src) { + std::free(mData); + + mSize = src.mSize; + mCapacity = src.mCapacity; + mData = src.mData; + + src.mSize = src.mCapacity = 0; + src.mData = nullptr; + } + return *this; + } + + ~PodVector() { + std::free(mData); + } + + bool empty() const { return mSize == 0; } + int size() const { return mSize; } + int size_in_bytes() const { return mSize * (int)sizeof(T); } + int max_size() const { return 0x7FFFFFFF / (int)sizeof(T); } + int capacity() const { return mCapacity; } + + T& operator[](int i) { + assert(i >= 0 && i < mSize); + return mData[i]; + } + + const T& operator[](int i) const { + assert(i >= 0 && i < mSize); + return mData[i]; + } + + void clear() { + if (mData) { + mSize = mCapacity = 0; + std::free(mData); + mData = nullptr; + } + } + + T* begin() { return mData; } + const T* begin() const { return mData; } + T* end() { return mData + mSize; } + const T* end() const { return mData + mSize; } + + T* data() { return mData; } + + T& front() { + assert(mSize > 0); + return mData[0]; + } + + const T& front() const { + assert(mSize > 0); + return mData[0]; + } + + T& back() { + assert(mSize > 0); + return mData[mSize - 1]; + } + + const T& back() const { + assert(mSize > 0); + return mData[mSize - 1]; + } + + void swap(PodVector<T>& rhs) { + int rhs_size = rhs.mSize; + rhs.mSize = mSize; + mSize = rhs_size; + int rhs_cap = rhs.mCapacity; + rhs.mCapacity = mCapacity; + mCapacity = rhs_cap; + T* rhs_mDataTmp = rhs.mData; + rhs.mData = mData; + mData = rhs_mDataTmp; + } + + int grow_capacity(int sz) const { + int newCapacity = mCapacity ? (mCapacity + mCapacity / 2) : 8; + return newCapacity > sz ? newCapacity : sz; + } + + void resize(int new_size) { + if (new_size > mCapacity) reserve(grow_capacity(new_size)); + mSize = new_size; + } + + void resize_more(int size) { + resize(mSize + size); + } + + void resize(int new_size, const T& v) { + if (new_size > mCapacity) reserve(grow_capacity(new_size)); + if (new_size > mSize) { + for (int n = mSize; n < new_size; n++) { + std::memcpy(&mData[n], &v, sizeof(v)); + } + } + mSize = new_size; + } + + void resize_more(int size, const T& v) { + resize(mSize + size, v); + } + + void shrink(int new_size) { + assert(new_size <= mSize); + mSize = new_size; + } + + /// Resize a vector to a smaller mSize, guaranteed not to cause a reallocation + void reserve(int newCapacity) { + if (newCapacity <= mCapacity) return; + auto tmp = (T*)std::malloc((size_t)newCapacity * sizeof(T)); + if (mData) { + std::memcpy(tmp, mData, (size_t)mSize * sizeof(T)); + std::free(mData); + } + mData = tmp; + mCapacity = newCapacity; + } + + void reserve_more(int size) { + reserve(mSize + size); + } + + /// NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the PodVector data itself! e.g. v.push_back(v[10]) is forbidden. + void push_back(const T& v) { + if (mSize == mCapacity) reserve(grow_capacity(mSize + 1)); + std::memcpy(&mData[mSize], &v, sizeof(v)); + mSize++; + } + + void pop_back() { + assert(mSize > 0); + mSize--; + } + + void push_front(const T& v) { + if (mSize == 0) { + push_back(v); + } else { + insert(mData, v); + } + } + + T* erase(const T* it) { + assert(it >= mData && it < mData + mSize); + const ptrdiff_t off = it - mData; + std::memmove(mData + off, mData + off + 1, ((size_t)mSize - (size_t)off - 1) * sizeof(T)); + mSize--; + return mData + off; + } + + T* erase(const T* it, const T* it_last) { + assert(it >= mData && it < mData + mSize && it_last > it && it_last <= mData + mSize); + const ptrdiff_t count = it_last - it; + const ptrdiff_t off = it - mData; + std::memmove(mData + off, mData + off + count, ((size_t)mSize - (size_t)off - count) * sizeof(T)); + mSize -= (int)count; + return mData + off; + } + + T* erase_unsorted(const T* it) { + assert(it >= mData && it < mData + mSize); + const ptrdiff_t off = it - mData; + if (it < mData + mSize - 1) std::memcpy(mData + off, mData + mSize - 1, sizeof(T)); + mSize--; + return mData + off; + } + + T* insert(const T* it, const T& v) { + assert(it >= mData && it <= mData + mSize); + const ptrdiff_t off = it - mData; + if (mSize == mCapacity) reserve(grow_capacity(mSize + 1)); + if (off < (int)mSize) std::memmove(mData + off + 1, mData + off, ((size_t)mSize - (size_t)off) * sizeof(T)); + std::memcpy(&mData[off], &v, sizeof(v)); + mSize++; + return mData + off; + } + + bool contains(const T& v) const { + const T* data = mData; + const T* dataEnd = mData + mSize; + while (data < dataEnd) { + if (*data++ == v) return true; + } + return false; + } + + T* find(const T& v) { + T* data = mData; + const T* dataEnd = mData + mSize; + while (data < dataEnd) + if (*data == v) + break; + else + ++data; + return data; + } + + const T* find(const T& v) const { + const T* data = mData; + const T* dataEnd = mData + mSize; + while (data < dataEnd) + if (*data == v) + break; + else + ++data; + return data; + } + + bool find_erase(const T& v) { + const T* it = find(v); + if (it < mData + mSize) { + erase(it); + return true; + } + return false; + } + + bool find_erase_unsorted(const T& v) { + const T* it = find(v); + if (it < mData + mSize) { + erase_unsorted(it); + return true; + } + return false; + } + + int index_from_ptr(const T* it) const { + assert(it >= mData && it < mData + mSize); + const ptrdiff_t off = it - mData; + return (int)off; + } + + // Custom utility functions + + std::span<T> as_span() { return { mData, (size_t)mSize }; } + std::span<uint8_t> as_data_span() { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } + std::span<const T> as_span() const { return { mData, (size_t)mSize }; } + std::span<const uint8_t> as_data_span() const { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } +}; diff --git a/source/10-common/RapidJsonHelper.hpp b/source/10-common/RapidJsonHelper.hpp new file mode 100644 index 0000000..75cd93a --- /dev/null +++ b/source/10-common/RapidJsonHelper.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include <rapidjson/document.h> +#include <cstring> +#include <string> +#include <string_view> + +#define BRUSSEL_JSON_GET(object, name, type, out, failAction) \ + { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) failAction; \ + auto& value = it->value; \ + if (!value.Is<type>()) failAction; \ + (out) = value.Get<type>(); \ + } + +#define BRUSSEL_JSON_GET_DEFAULT(object, name, type, out, theDefault) \ + do { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) { \ + (out) = theDefault; \ + break; \ + } \ + auto& value = it->value; \ + if (!value.Is<type>()) { \ + (out) = theDefault; \ + break; \ + } \ + (out) = value.Get<type>(); \ + } while (0); + +namespace rapidjson { + +inline const Value* GetProperty(const Value& value, std::string_view name) { + for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { + if (it->name.GetStringLength() != name.size()) continue; + if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; + + return &it->value; + } + return nullptr; +} + +inline const Value* GetProperty(const Value& value, Type type, std::string_view name) { + for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { + if (it->name.GetStringLength() != name.size()) continue; + if (it->value.GetType() != type) continue; + if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; + + return &it->value; + } + return nullptr; +} + +inline std::string_view AsStringView(const Value& value) { + return std::string_view(value.GetString(), value.GetStringLength()); +} + +inline std::string_view AsStringView(const GenericStringRef<char>& strRef) { + return std::string_view(strRef.s, strRef.length); +} + +inline std::string AsString(const Value& value) { + return std::string(value.GetString(), value.GetStringLength()); +} + +inline std::string AsString(const GenericStringRef<char>& strRef) { + return std::string(strRef.s, strRef.length); +} + +// RapidJson itself already provides std::string and const char* overloads +inline GenericStringRef<char> StringRef(std::string_view str) { + return GenericStringRef<char>( + str.data() ? str.data() : "", + str.size()); +} + +template <class TIter, class TSentienl> +rapidjson::Value WriteVectorPrimitives(rapidjson::Document& root, TIter begin, TSentienl end) { + using TElement = typename TIter::value_type; + + rapidjson::Value list; + while (begin != end) { + if constexpr (std::is_same_v<TElement, std::string>) { + auto& elm = *begin; + list.PushBack(rapidjson::Value(elm.c_str(), elm.size()), root.GetAllocator()); + } else { + list.PushBack(*begin, root.GetAllocator()); + } + ++begin; + } + return list; +} + +template <class TContainer> +bool ReadVectorPrimitives(const rapidjson::Value& value, TContainer& list) { + using TElement = typename TContainer::value_type; + + if (!value.IsArray()) return false; + + list.reserve(value.Size()); + for (auto& elm : value.GetArray()) { + if (!elm.Is<TElement>()) return {}; + list.push_back(elm.Get<TElement>()); + } + + return true; +} + +} // namespace rapidjson diff --git a/source/10-common/RcPtr.hpp b/source/10-common/RcPtr.hpp new file mode 100644 index 0000000..130b2b2 --- /dev/null +++ b/source/10-common/RcPtr.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include "Macros.hpp" +#include "TypeTraits.hpp" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <type_traits> + +class RefCounted { +public: + // DO NOT MODIFY this field, unless explicitly documented the use + uint32_t refCount = 0; + uint32_t weakCount = 0; // TODO implement +}; + +template <class T, class TDeleter = DefaultDeleter<T>> +class RcPtr : TDeleter { +private: + static_assert(std::is_base_of_v<RefCounted, T>); + T* mPtr; + +public: + RcPtr() + : mPtr{ nullptr } { + } + + explicit RcPtr(T* ptr) + : mPtr{ ptr } { + if (ptr) { + ++ptr->RefCounted::refCount; + } + } + + ~RcPtr() { + CleanUp(); + } + + void Attach(T* ptr) { + CleanUp(); + mPtr = ptr; + if (ptr) { + ++ptr->RefCounted::refCount; + } + } + + void Detatch() { + CleanUp(); + mPtr = nullptr; + } + + RcPtr(const RcPtr& that) + : mPtr{ that.mPtr } { + if (mPtr) { + ++mPtr->RefCounted::refCount; + } + } + + RcPtr& operator=(const RcPtr& that) { + CleanUp(); + mPtr = that.mPtr; + if (mPtr) { + ++mPtr->RefCounted::refCount; + } + return *this; + } + + RcPtr(RcPtr&& that) + : mPtr{ that.mPtr } { + that.mPtr = nullptr; + } + + RcPtr& operator=(RcPtr&& that) { + CleanUp(); + mPtr = that.mPtr; + that.mPtr = nullptr; + return *this; + } + + template <class TBase> + requires std::is_base_of_v<TBase, T> + operator RcPtr<TBase>() const { + return RcPtr<TBase>(mPtr); + } + + bool operator==(std::nullptr_t ptr) const { + return mPtr == nullptr; + } + + bool operator==(const T* ptr) const { + return mPtr == ptr; + } + + bool operator==(T* ptr) const { + return mPtr == ptr; + } + + template <class TThat> + bool operator==(const RcPtr<TThat>& ptr) const { + return mPtr == ptr.Get(); + } + + T* Get() const { + return mPtr; + } + + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + +private: + void CleanUp() { + if (mPtr) { + --mPtr->RefCounted::refCount; + if (mPtr->RefCounted::refCount == 0) { + TDeleter::operator()(mPtr); + } + } + } +}; diff --git a/source/10-common/Rect.hpp b/source/10-common/Rect.hpp new file mode 100644 index 0000000..89d9b01 --- /dev/null +++ b/source/10-common/Rect.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include <glm/glm.hpp> + +/// Rect is a rectangle representation based on a point and a dimensions, in television coordinate space +/// (x increases from left to right, y increases from top to bottom). +template <class T> +class Rect { +public: + using ScalarType = T; + using VectorType = glm::vec<2, T>; + +public: + T x; + T y; + T width; + T height; + +public: + Rect() + : x{ 0 }, y{ 0 }, width{ 0 }, height{ 0 } { + } + + Rect(T x, T y, T width, T height) + : x{ x }, y{ y }, width{ width }, height{ height } { + } + + Rect(VectorType pos, VectorType size) + : x{ pos.x } + , y{ pos.y } + , width{ size.x } + , height{ size.y } { + } + + T x0() const { return x; } + T y0() const { return y; } + T x1() const { return x + width; } + T y1() const { return y + height; } + + VectorType TopLeft() const { + return VectorType{ x, y }; + } + + VectorType TopRight() const { + return VectorType{ x + width, y }; + } + + VectorType BottomLeft() const { + return VectorType{ x, y + height }; + } + + VectorType BottomRight() const { + return VectorType{ x + width, y + height }; + } + + VectorType Center() const { + return TopLeft() + VectorType{ width / 2, height / 2 }; + } + + VectorType Dimensions() const { + return VectorType{ width, height }; + } + + VectorType Extents() const { + return VectorType{ width / 2, height / 2 }; + } + + /// Assumes `bySize * 2` is smaller than both `width` and `height` (does not produce a negative-dimension rectangle). + Rect Shrink(T bySize) const { + T two = bySize * 2; + return Rect{ x + bySize, y + bySize, width - two, height - two }; + } + + Rect Shrink(T left, T top, T right, T bottom) const { + return Rect{ + x + left, + y + top, + width - left - right, + height - top - bottom, + }; + } + + Rect Expand(T bySize) const { + T two = bySize * 2; + return Rect{ x - bySize, y - bySize, width + two, height + two }; + } + + Rect Expand(T left, T top, T right, T bottom) const { + return Rect{ + x - left, + y - top, + width + left + right, + height + top + bottom, + }; + } + + bool Contains(VectorType point) const { + return point.x >= x && + point.y >= y && + point.x < x + width && + point.y < y + height; + } + + bool Intersects(const Rect& that) const { + bool xBetween = x > that.x0() && x < that.x1(); + bool yBetween = y > that.y0() && y < that.y1(); + return xBetween && yBetween; + } + + // Write min()/max() tenary by hand so that we don't have to include <algorithm> + // This file is practically going to be included in every file in this project + + static Rect Intersection(const Rect& a, const Rect& b) { + auto x0 = a.x0() > b.x0() ? a.x0() : b.x0(); // Max + auto y0 = a.y0() > b.y0() ? a.y0() : b.y0(); // Max + auto x1 = a.x1() < b.x1() ? a.x1() : b.x1(); // Min + auto y1 = a.y1() < b.y1() ? a.y1() : b.y1(); // Min + auto width = x1 - x0; + auto height = y1 - x0; + return Rect{ x0, y0, width, height }; + } + + static Rect Union(const Rect& a, const Rect& b) { + auto x0 = a.x0() < b.x0() ? a.x0() : b.x0(); // Min + auto y0 = a.y0() < b.y0() ? a.y0() : b.y0(); // Min + auto x1 = a.x1() > b.x1() ? a.x1() : b.x1(); // Max + auto y1 = a.y1() > b.y1() ? a.y1() : b.y1(); // Max + auto width = x1 - x0; + auto height = y1 - x0; + return Rect{ x0, y0, width, height }; + } + + friend bool operator==(const Rect<T>&, const Rect<T>&) = default; + + Rect operator+(glm::vec<2, T> offset) const { + return { x + offset.x, y + offset.y, width, height }; + } + + Rect operator-(glm::vec<2, T> offset) const { + return { x - offset.x, y - offset.y, width, height }; + } + + Rect& operator+=(glm::vec<2, T> offset) { + x += offset.x; + y += offset.y; + return *this; + } + + Rect& operator-=(glm::vec<2, T> offset) { + x -= offset.x; + y -= offset.y; + return *this; + } + + template <class TTarget> + Rect<TTarget> Cast() const { + return { + static_cast<TTarget>(x), + static_cast<TTarget>(y), + static_cast<TTarget>(width), + static_cast<TTarget>(height), + }; + } +}; diff --git a/source/10-common/ScopeGuard.hpp b/source/10-common/ScopeGuard.hpp new file mode 100644 index 0000000..28f3385 --- /dev/null +++ b/source/10-common/ScopeGuard.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "Macros.hpp" + +#include <utility> + +template <class TCleanupFunc> +class ScopeGuard { +private: + TCleanupFunc mFunc; + bool mDismissed = false; + +public: + /// Specifically left this implicit so that constructs like + /// \code + /// ScopeGuard sg = [&]() { res.Cleanup(); }; + /// \endcode + /// would work. It is highly discourage and unlikely that one would want to use ScopeGuard as a function + /// parameter, so the normal argument that implicit conversion are harmful doesn't really apply here. + // Deliberately not explicit to allow usages like: ScopeGuard var = lambda; + ScopeGuard(TCleanupFunc&& function) noexcept + : mFunc{ std::move(function) } { + } + + ~ScopeGuard() noexcept { + if (!mDismissed) { + mFunc(); + } + } + + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + + ScopeGuard(ScopeGuard&& that) noexcept + : mFunc{ std::move(that.mFunc) } { + that.Cancel(); + } + + ScopeGuard& operator=(ScopeGuard&& that) noexcept { + if (!mDismissed) { + mFunc(); + } + this->mFunc = std::move(that.mFunc); + this->cancelled = std::exchange(that.cancelled, true); + } + + void Dismiss() noexcept { + mDismissed = true; + } +}; + +template <class T> +auto GuardDeletion(T* ptr) { + return ScopeGuard([ptr]() { + delete ptr; + }); +} + +#define SCOPE_GUARD(name) ScopeGuard name = [&]() +#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]() diff --git a/source/10-common/SmallVector.cpp b/source/10-common/SmallVector.cpp new file mode 100644 index 0000000..c38e8a7 --- /dev/null +++ b/source/10-common/SmallVector.cpp @@ -0,0 +1,145 @@ +// Obtained from https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/SmallVector.cpp +// commit 4b82bb6d82f65f98f23d0e4c2cd5297dc162864c +// adapted in code style and utilities to fix this project + +//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the SmallVector class. +// +//===----------------------------------------------------------------------===// + +#include "SmallVector.hpp" + +#include <cstdlib> +#include <stdexcept> +#include <string> + +// Check that no bytes are wasted and everything is well-aligned. +namespace { +// These structures may cause binary compat warnings on AIX. Suppress the +// warning since we are only using these types for the static assertions below. +#if defined(_AIX) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waix-compat" +#endif +struct Struct16B { + alignas(16) void* X; +}; +struct Struct32B { + alignas(32) void* X; +}; +#if defined(_AIX) +# pragma GCC diagnostic pop +#endif +} // namespace +static_assert(sizeof(SmallVector<void*, 0>) == + sizeof(unsigned) * 2 + sizeof(void*), + "wasted space in SmallVector size 0"); +static_assert(alignof(SmallVector<Struct16B, 0>) >= alignof(Struct16B), + "wrong alignment for 16-byte aligned T"); +static_assert(alignof(SmallVector<Struct32B, 0>) >= alignof(Struct32B), + "wrong alignment for 32-byte aligned T"); +static_assert(sizeof(SmallVector<Struct16B, 0>) >= alignof(Struct16B), + "missing padding for 16-byte aligned T"); +static_assert(sizeof(SmallVector<Struct32B, 0>) >= alignof(Struct32B), + "missing padding for 32-byte aligned T"); +static_assert(sizeof(SmallVector<void*, 1>) == + sizeof(unsigned) * 2 + sizeof(void*) * 2, + "wasted space in SmallVector size 1"); + +static_assert(sizeof(SmallVector<char, 0>) == + sizeof(void*) * 2 + sizeof(void*), + "1 byte elements have word-sized type for size and capacity"); + +/// Report that MinSize doesn't fit into this vector's size type. Throws +/// std::length_error or calls report_fatal_error. +[[noreturn]] static void report_size_overflow(size_t MinSize, size_t MaxSize); +static void report_size_overflow(size_t MinSize, size_t MaxSize) { + std::string Reason = "SmallVector unable to grow. Requested capacity (" + + std::to_string(MinSize) + + ") is larger than maximum value for size type (" + + std::to_string(MaxSize) + ")"; + throw std::length_error(Reason); +} + +/// Report that this vector is already at maximum capacity. Throws +/// std::length_error or calls report_fatal_error. +[[noreturn]] static void report_at_maximum_capacity(size_t MaxSize); +static void report_at_maximum_capacity(size_t MaxSize) { + std::string Reason = + "SmallVector capacity unable to grow. Already at maximum size " + + std::to_string(MaxSize); + throw std::length_error(Reason); +} + +// Note: Moving this function into the header may cause performance regression. +template <class Size_T> +static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { + constexpr size_t MaxSize = std::numeric_limits<Size_T>::max(); + + // Ensure we can fit the new capacity. + // This is only going to be applicable when the capacity is 32 bit. + if (MinSize > MaxSize) + report_size_overflow(MinSize, MaxSize); + + // Ensure we can meet the guarantee of space for at least one more element. + // The above check alone will not catch the case where grow is called with a + // default MinSize of 0, but the current capacity cannot be increased. + // This is only going to be applicable when the capacity is 32 bit. + if (OldCapacity == MaxSize) + report_at_maximum_capacity(MaxSize); + + // In theory 2*capacity can overflow if the capacity is 64 bit, but the + // original capacity would never be large enough for this to be a problem. + size_t NewCapacity = 2 * OldCapacity + 1; // Always grow. + return std::min(std::max(NewCapacity, MinSize), MaxSize); +} + +// Note: Moving this function into the header may cause performance regression. +template <class Size_T> +void* SmallVectorBase<Size_T>::mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity) { + NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity()); + return malloc(NewCapacity * TSize); +} + +// Note: Moving this function into the header may cause performance regression. +template <class Size_T> +void SmallVectorBase<Size_T>::grow_pod(void* FirstEl, size_t MinSize, size_t TSize) { + size_t NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity()); + void* NewElts; + if (BeginX == FirstEl) { + NewElts = malloc(NewCapacity * TSize); + + // Copy the elements over. No need to run dtors on PODs. + memcpy(NewElts, this->BeginX, size() * TSize); + } else { + // If this wasn't grown from the inline copy, grow the allocated space. + NewElts = realloc(this->BeginX, NewCapacity * TSize); + } + + this->BeginX = NewElts; + this->Capacity = NewCapacity; +} + +template class SmallVectorBase<uint32_t>; + +// Disable the uint64_t instantiation for 32-bit builds. +// Both uint32_t and uint64_t instantiations are needed for 64-bit builds. +// This instantiation will never be used in 32-bit builds, and will cause +// warnings when sizeof(Size_T) > sizeof(size_t). +#if SIZE_MAX > UINT32_MAX +template class SmallVectorBase<uint64_t>; + +// Assertions to ensure this #if stays in sync with SmallVectorSizeType. +static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t), + "Expected SmallVectorBase<uint64_t> variant to be in use."); +#else +static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t), + "Expected SmallVectorBase<uint32_t> variant to be in use."); +#endif diff --git a/source/10-common/SmallVector.hpp b/source/10-common/SmallVector.hpp new file mode 100644 index 0000000..e33a25d --- /dev/null +++ b/source/10-common/SmallVector.hpp @@ -0,0 +1,1332 @@ +// Obtained from https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/ADT/SmallVector.h +// commit 4b82bb6d82f65f98f23d0e4c2cd5297dc162864c +// adapted in code style and utilities to fix this project + +//===- llvm/ADT/SmallVector.h - 'Normally small' vectors --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the SmallVector class. +/// +//===----------------------------------------------------------------------===// + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <initializer_list> +#include <iterator> +#include <limits> +#include <memory> +#include <new> +#include <type_traits> +#include <utility> + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4267) // The compiler detected a conversion from size_t to a smaller type. +#endif + +#if __has_builtin(__builtin_expect) || defined(__GNUC__) +# define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +# define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +#else +# define LLVM_LIKELY(EXPR) (EXPR) +# define LLVM_UNLIKELY(EXPR) (EXPR) +#endif + +template <typename IteratorT> +class iterator_range; + +/// This is all the stuff common to all SmallVectors. +/// +/// The template parameter specifies the type which should be used to hold the +/// Size and Capacity of the SmallVector, so it can be adjusted. +/// Using 32 bit size is desirable to shrink the size of the SmallVector. +/// Using 64 bit size is desirable for cases like SmallVector<char>, where a +/// 32 bit size would limit the vector to ~4GB. SmallVectors are used for +/// buffering bitcode output - which can exceed 4GB. +template <class Size_T> +class SmallVectorBase { +protected: + void* BeginX; + Size_T Size = 0, Capacity; + + /// The maximum value of the Size_T used. + static constexpr size_t SizeTypeMax() { + return std::numeric_limits<Size_T>::max(); + } + + SmallVectorBase() = delete; + SmallVectorBase(void* FirstEl, size_t TotalCapacity) + : BeginX(FirstEl), Capacity(TotalCapacity) {} + + /// This is a helper for \a grow() that's out of line to reduce code + /// duplication. This function will report a fatal error if it can't grow at + /// least to \p MinSize. + void* mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity); + + /// This is an implementation of the grow() method which only works + /// on POD-like data types and is out of line to reduce code duplication. + /// This function will report a fatal error if it cannot increase capacity. + void grow_pod(void* FirstEl, size_t MinSize, size_t TSize); + +public: + size_t size() const { return Size; } + size_t capacity() const { return Capacity; } + + [[nodiscard]] bool empty() const { return !Size; } + +protected: + /// Set the array size to \p N, which the current array must have enough + /// capacity for. + /// + /// This does not construct or destroy any elements in the vector. + void set_size(size_t N) { + assert(N <= capacity()); + Size = N; + } +}; + +template <class T> +using SmallVectorSizeType = + typename std::conditional<sizeof(T) < 4 && sizeof(void*) >= 8, uint64_t, uint32_t>::type; + +/// Figure out the offset of the first element. +template <class T, typename = void> +struct SmallVectorAlignmentAndSize { + alignas(SmallVectorBase<SmallVectorSizeType<T>>) char Base[sizeof( + SmallVectorBase<SmallVectorSizeType<T>>)]; + alignas(T) char FirstEl[sizeof(T)]; +}; + +/// This is the part of SmallVectorTemplateBase which does not depend on whether +/// the type T is a POD. The extra dummy template argument is used by ArrayRef +/// to avoid unnecessarily requiring T to be complete. +template <typename T, typename = void> +class SmallVectorTemplateCommon + : public SmallVectorBase<SmallVectorSizeType<T>> { + using Base = SmallVectorBase<SmallVectorSizeType<T>>; + + /// Find the address of the first element. For this pointer math to be valid + /// with small-size of 0 for T with lots of alignment, it's important that + /// SmallVectorStorage is properly-aligned even for small-size of 0. + void* getFirstEl() const { + return const_cast<void*>(reinterpret_cast<const void*>( + reinterpret_cast<const char*>(this) + + offsetof(SmallVectorAlignmentAndSize<T>, FirstEl))); + } + // Space after 'FirstEl' is clobbered, do not add any instance vars after it. + +protected: + SmallVectorTemplateCommon(size_t Size) + : Base(getFirstEl(), Size) {} + + void grow_pod(size_t MinSize, size_t TSize) { + Base::grow_pod(getFirstEl(), MinSize, TSize); + } + + /// Return true if this is a smallvector which has not had dynamic + /// memory allocated for it. + bool isSmall() const { return this->BeginX == getFirstEl(); } + + /// Put this vector in a state of being small. + void resetToSmall() { + this->BeginX = getFirstEl(); + this->Size = this->Capacity = 0; // FIXME: Setting Capacity to 0 is suspect. + } + + /// Return true if V is an internal reference to the given range. + bool isReferenceToRange(const void* V, const void* First, const void* Last) const { + // Use std::less to avoid UB. + std::less<> LessThan; + return !LessThan(V, First) && LessThan(V, Last); + } + + /// Return true if V is an internal reference to this vector. + bool isReferenceToStorage(const void* V) const { + return isReferenceToRange(V, this->begin(), this->end()); + } + + /// Return true if First and Last form a valid (possibly empty) range in this + /// vector's storage. + bool isRangeInStorage(const void* First, const void* Last) const { + // Use std::less to avoid UB. + std::less<> LessThan; + return !LessThan(First, this->begin()) && !LessThan(Last, First) && + !LessThan(this->end(), Last); + } + + /// Return true unless Elt will be invalidated by resizing the vector to + /// NewSize. + bool isSafeToReferenceAfterResize(const void* Elt, size_t NewSize) { + // Past the end. + if (LLVM_LIKELY(!isReferenceToStorage(Elt))) + return true; + + // Return false if Elt will be destroyed by shrinking. + if (NewSize <= this->size()) + return Elt < this->begin() + NewSize; + + // Return false if we need to grow. + return NewSize <= this->capacity(); + } + + /// Check whether Elt will be invalidated by resizing the vector to NewSize. + void assertSafeToReferenceAfterResize(const void* Elt, size_t NewSize) { + assert(isSafeToReferenceAfterResize(Elt, NewSize) && + "Attempting to reference an element of the vector in an operation " + "that invalidates it"); + } + + /// Check whether Elt will be invalidated by increasing the size of the + /// vector by N. + void assertSafeToAdd(const void* Elt, size_t N = 1) { + this->assertSafeToReferenceAfterResize(Elt, this->size() + N); + } + + /// Check whether any part of the range will be invalidated by clearing. + void assertSafeToReferenceAfterClear(const T* From, const T* To) { + if (From == To) + return; + this->assertSafeToReferenceAfterResize(From, 0); + this->assertSafeToReferenceAfterResize(To - 1, 0); + } + template < + class ItTy, + std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T*>::value, + bool> = false> + void assertSafeToReferenceAfterClear(ItTy, ItTy) {} + + /// Check whether any part of the range will be invalidated by growing. + void assertSafeToAddRange(const T* From, const T* To) { + if (From == To) + return; + this->assertSafeToAdd(From, To - From); + this->assertSafeToAdd(To - 1, To - From); + } + template < + class ItTy, + std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T*>::value, + bool> = false> + void assertSafeToAddRange(ItTy, ItTy) {} + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + template <class U> + static const T* reserveForParamAndGetAddressImpl(U* This, const T& Elt, size_t N) { + size_t NewSize = This->size() + N; + if (LLVM_LIKELY(NewSize <= This->capacity())) + return &Elt; + + bool ReferencesStorage = false; + int64_t Index = -1; + if (!U::TakesParamByValue) { + if (LLVM_UNLIKELY(This->isReferenceToStorage(&Elt))) { + ReferencesStorage = true; + Index = &Elt - This->begin(); + } + } + This->grow(NewSize); + return ReferencesStorage ? This->begin() + Index : &Elt; + } + +public: + using size_type = size_t; + using difference_type = ptrdiff_t; + using value_type = T; + using iterator = T*; + using const_iterator = const T*; + + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using reverse_iterator = std::reverse_iterator<iterator>; + + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + + using Base::capacity; + using Base::empty; + using Base::size; + + // forward iterator creation methods. + iterator begin() { return (iterator)this->BeginX; } + const_iterator begin() const { return (const_iterator)this->BeginX; } + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + // reverse iterator creation methods. + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + size_type size_in_bytes() const { return size() * sizeof(T); } + size_type max_size() const { + return std::min(this->SizeTypeMax(), size_type(-1) / sizeof(T)); + } + + size_t capacity_in_bytes() const { return capacity() * sizeof(T); } + + /// Return a pointer to the vector's buffer, even if empty(). + pointer data() { return pointer(begin()); } + /// Return a pointer to the vector's buffer, even if empty(). + const_pointer data() const { return const_pointer(begin()); } + + reference operator[](size_type idx) { + assert(idx < size()); + return begin()[idx]; + } + const_reference operator[](size_type idx) const { + assert(idx < size()); + return begin()[idx]; + } + + reference front() { + assert(!empty()); + return begin()[0]; + } + const_reference front() const { + assert(!empty()); + return begin()[0]; + } + + reference back() { + assert(!empty()); + return end()[-1]; + } + const_reference back() const { + assert(!empty()); + return end()[-1]; + } +}; + +/// SmallVectorTemplateBase<TriviallyCopyable = false> - This is where we put +/// method implementations that are designed to work with non-trivial T's. +/// +/// We approximate is_trivially_copyable with trivial move/copy construction and +/// trivial destruction. While the standard doesn't specify that you're allowed +/// copy these types with memcpy, there is no way for the type to observe this. +/// This catches the important case of std::pair<POD, POD>, which is not +/// trivially assignable. +template <typename T, bool = (std::is_trivially_copy_constructible<T>::value) && (std::is_trivially_move_constructible<T>::value) && std::is_trivially_destructible<T>::value> +class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> { + friend class SmallVectorTemplateCommon<T>; + +protected: + static constexpr bool TakesParamByValue = false; + using ValueParamT = const T&; + + SmallVectorTemplateBase(size_t Size) + : SmallVectorTemplateCommon<T>(Size) {} + + static void destroy_range(T* S, T* E) { + while (S != E) { + --E; + E->~T(); + } + } + + /// Move the range [I, E) into the uninitialized memory starting with "Dest", + /// constructing elements as needed. + template <typename It1, typename It2> + static void uninitialized_move(It1 I, It1 E, It2 Dest) { + std::uninitialized_copy(std::make_move_iterator(I), + std::make_move_iterator(E), + Dest); + } + + /// Copy the range [I, E) onto the uninitialized memory starting with "Dest", + /// constructing elements as needed. + template <typename It1, typename It2> + static void uninitialized_copy(It1 I, It1 E, It2 Dest) { + std::uninitialized_copy(I, E, Dest); + } + + /// Grow the allocated memory (without initializing new elements), doubling + /// the size of the allocated memory. Guarantees space for at least one more + /// element, or MinSize more elements if specified. + void grow(size_t MinSize = 0); + + /// Create a new allocation big enough for \p MinSize and pass back its size + /// in \p NewCapacity. This is the first section of \a grow(). + T* mallocForGrow(size_t MinSize, size_t& NewCapacity) { + return static_cast<T*>( + SmallVectorBase<SmallVectorSizeType<T>>::mallocForGrow( + MinSize, sizeof(T), NewCapacity)); + } + + /// Move existing elements over to the new allocation \p NewElts, the middle + /// section of \a grow(). + void moveElementsForGrow(T* NewElts); + + /// Transfer ownership of the allocation, finishing up \a grow(). + void takeAllocationForGrow(T* NewElts, size_t NewCapacity); + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + const T* reserveForParamAndGetAddress(const T& Elt, size_t N = 1) { + return this->reserveForParamAndGetAddressImpl(this, Elt, N); + } + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + T* reserveForParamAndGetAddress(T& Elt, size_t N = 1) { + return const_cast<T*>( + this->reserveForParamAndGetAddressImpl(this, Elt, N)); + } + + static T&& forward_value_param(T&& V) { return std::move(V); } + static const T& forward_value_param(const T& V) { return V; } + + void growAndAssign(size_t NumElts, const T& Elt) { + // Grow manually in case Elt is an internal reference. + size_t NewCapacity; + T* NewElts = mallocForGrow(NumElts, NewCapacity); + std::uninitialized_fill_n(NewElts, NumElts, Elt); + this->destroy_range(this->begin(), this->end()); + takeAllocationForGrow(NewElts, NewCapacity); + this->set_size(NumElts); + } + + template <typename... ArgTypes> + T& growAndEmplaceBack(ArgTypes&&... Args) { + // Grow manually in case one of Args is an internal reference. + size_t NewCapacity; + T* NewElts = mallocForGrow(0, NewCapacity); + ::new ((void*)(NewElts + this->size())) T(std::forward<ArgTypes>(Args)...); + moveElementsForGrow(NewElts); + takeAllocationForGrow(NewElts, NewCapacity); + this->set_size(this->size() + 1); + return this->back(); + } + +public: + void push_back(const T& Elt) { + const T* EltPtr = reserveForParamAndGetAddress(Elt); + ::new ((void*)this->end()) T(*EltPtr); + this->set_size(this->size() + 1); + } + + void push_back(T&& Elt) { + T* EltPtr = reserveForParamAndGetAddress(Elt); + ::new ((void*)this->end()) T(::std::move(*EltPtr)); + this->set_size(this->size() + 1); + } + + void pop_back() { + this->set_size(this->size() - 1); + this->end()->~T(); + } +}; + +// Define this out-of-line to dissuade the C++ compiler from inlining it. +template <typename T, bool TriviallyCopyable> +void SmallVectorTemplateBase<T, TriviallyCopyable>::grow(size_t MinSize) { + size_t NewCapacity; + T* NewElts = mallocForGrow(MinSize, NewCapacity); + moveElementsForGrow(NewElts); + takeAllocationForGrow(NewElts, NewCapacity); +} + +// Define this out-of-line to dissuade the C++ compiler from inlining it. +template <typename T, bool TriviallyCopyable> +void SmallVectorTemplateBase<T, TriviallyCopyable>::moveElementsForGrow( + T* NewElts) { + // Move the elements over. + this->uninitialized_move(this->begin(), this->end(), NewElts); + + // Destroy the original elements. + destroy_range(this->begin(), this->end()); +} + +// Define this out-of-line to dissuade the C++ compiler from inlining it. +template <typename T, bool TriviallyCopyable> +void SmallVectorTemplateBase<T, TriviallyCopyable>::takeAllocationForGrow( + T* NewElts, size_t NewCapacity) { + // If this wasn't grown from the inline copy, deallocate the old space. + if (!this->isSmall()) + free(this->begin()); + + this->BeginX = NewElts; + this->Capacity = NewCapacity; +} + +/// SmallVectorTemplateBase<TriviallyCopyable = true> - This is where we put +/// method implementations that are designed to work with trivially copyable +/// T's. This allows using memcpy in place of copy/move construction and +/// skipping destruction. +template <typename T> +class SmallVectorTemplateBase<T, true> : public SmallVectorTemplateCommon<T> { + friend class SmallVectorTemplateCommon<T>; + +protected: + /// True if it's cheap enough to take parameters by value. Doing so avoids + /// overhead related to mitigations for reference invalidation. + static constexpr bool TakesParamByValue = sizeof(T) <= 2 * sizeof(void*); + + /// Either const T& or T, depending on whether it's cheap enough to take + /// parameters by value. + using ValueParamT = + typename std::conditional<TakesParamByValue, T, const T&>::type; + + SmallVectorTemplateBase(size_t Size) + : SmallVectorTemplateCommon<T>(Size) {} + + // No need to do a destroy loop for POD's. + static void destroy_range(T*, T*) {} + + /// Move the range [I, E) onto the uninitialized memory + /// starting with "Dest", constructing elements into it as needed. + template <typename It1, typename It2> + static void uninitialized_move(It1 I, It1 E, It2 Dest) { + // Just do a copy. + uninitialized_copy(I, E, Dest); + } + + /// Copy the range [I, E) onto the uninitialized memory + /// starting with "Dest", constructing elements into it as needed. + template <typename It1, typename It2> + static void uninitialized_copy(It1 I, It1 E, It2 Dest) { + // Arbitrary iterator types; just use the basic implementation. + std::uninitialized_copy(I, E, Dest); + } + + /// Copy the range [I, E) onto the uninitialized memory + /// starting with "Dest", constructing elements into it as needed. + template <typename T1, typename T2> + static void uninitialized_copy( + T1* I, T1* E, T2* Dest, std::enable_if_t<std::is_same<typename std::remove_const<T1>::type, T2>::value>* = nullptr) { + // Use memcpy for PODs iterated by pointers (which includes SmallVector + // iterators): std::uninitialized_copy optimizes to memmove, but we can + // use memcpy here. Note that I and E are iterators and thus might be + // invalid for memcpy if they are equal. + if (I != E) + memcpy(reinterpret_cast<void*>(Dest), I, (E - I) * sizeof(T)); + } + + /// Double the size of the allocated memory, guaranteeing space for at + /// least one more element or MinSize if specified. + void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); } + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + const T* reserveForParamAndGetAddress(const T& Elt, size_t N = 1) { + return this->reserveForParamAndGetAddressImpl(this, Elt, N); + } + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + T* reserveForParamAndGetAddress(T& Elt, size_t N = 1) { + return const_cast<T*>( + this->reserveForParamAndGetAddressImpl(this, Elt, N)); + } + + /// Copy \p V or return a reference, depending on \a ValueParamT. + static ValueParamT forward_value_param(ValueParamT V) { return V; } + + void growAndAssign(size_t NumElts, T Elt) { + // Elt has been copied in case it's an internal reference, side-stepping + // reference invalidation problems without losing the realloc optimization. + this->set_size(0); + this->grow(NumElts); + std::uninitialized_fill_n(this->begin(), NumElts, Elt); + this->set_size(NumElts); + } + + template <typename... ArgTypes> + T& growAndEmplaceBack(ArgTypes&&... Args) { + // Use push_back with a copy in case Args has an internal reference, + // side-stepping reference invalidation problems without losing the realloc + // optimization. + push_back(T(std::forward<ArgTypes>(Args)...)); + return this->back(); + } + +public: + void push_back(ValueParamT Elt) { + const T* EltPtr = reserveForParamAndGetAddress(Elt); + memcpy(reinterpret_cast<void*>(this->end()), EltPtr, sizeof(T)); + this->set_size(this->size() + 1); + } + + void pop_back() { this->set_size(this->size() - 1); } +}; + +/// This class consists of common code factored out of the SmallVector class to +/// reduce code duplication based on the SmallVector 'N' template parameter. +template <typename T> +class SmallVectorImpl : public SmallVectorTemplateBase<T> { + using SuperClass = SmallVectorTemplateBase<T>; + +public: + using iterator = typename SuperClass::iterator; + using const_iterator = typename SuperClass::const_iterator; + using reference = typename SuperClass::reference; + using size_type = typename SuperClass::size_type; + +protected: + using SmallVectorTemplateBase<T>::TakesParamByValue; + using ValueParamT = typename SuperClass::ValueParamT; + + // Default ctor - Initialize to empty. + explicit SmallVectorImpl(unsigned N) + : SmallVectorTemplateBase<T>(N) {} + + void assignRemote(SmallVectorImpl&& RHS) { + this->destroy_range(this->begin(), this->end()); + if (!this->isSmall()) + free(this->begin()); + this->BeginX = RHS.BeginX; + this->Size = RHS.Size; + this->Capacity = RHS.Capacity; + RHS.resetToSmall(); + } + +public: + SmallVectorImpl(const SmallVectorImpl&) = delete; + + ~SmallVectorImpl() { + // Subclass has already destructed this vector's elements. + // If this wasn't grown from the inline copy, deallocate the old space. + if (!this->isSmall()) + free(this->begin()); + } + + void clear() { + this->destroy_range(this->begin(), this->end()); + this->Size = 0; + } + +private: + // Make set_size() private to avoid misuse in subclasses. + using SuperClass::set_size; + + template <bool ForOverwrite> + void resizeImpl(size_type N) { + if (N == this->size()) + return; + + if (N < this->size()) { + this->truncate(N); + return; + } + + this->reserve(N); + for (auto I = this->end(), E = this->begin() + N; I != E; ++I) + if (ForOverwrite) + new (&*I) T; + else + new (&*I) T(); + this->set_size(N); + } + +public: + void resize(size_type N) { resizeImpl<false>(N); } + + /// Like resize, but \ref T is POD, the new values won't be initialized. + void resize_for_overwrite(size_type N) { resizeImpl<true>(N); } + + /// Like resize, but requires that \p N is less than \a size(). + void truncate(size_type N) { + assert(this->size() >= N && "Cannot increase size with truncate"); + this->destroy_range(this->begin() + N, this->end()); + this->set_size(N); + } + + void resize(size_type N, ValueParamT NV) { + if (N == this->size()) + return; + + if (N < this->size()) { + this->truncate(N); + return; + } + + // N > this->size(). Defer to append. + this->append(N - this->size(), NV); + } + + void reserve(size_type N) { + if (this->capacity() < N) + this->grow(N); + } + + void pop_back_n(size_type NumItems) { + assert(this->size() >= NumItems); + truncate(this->size() - NumItems); + } + + [[nodiscard]] T pop_back_val() { + T Result = ::std::move(this->back()); + this->pop_back(); + return Result; + } + + void swap(SmallVectorImpl& RHS); + + /// Add the specified range to the end of the SmallVector. + template <typename in_iter, + typename = std::enable_if_t<std::is_convertible< + typename std::iterator_traits<in_iter>::iterator_category, + std::input_iterator_tag>::value>> + void append(in_iter in_start, in_iter in_end) { + this->assertSafeToAddRange(in_start, in_end); + size_type NumInputs = std::distance(in_start, in_end); + this->reserve(this->size() + NumInputs); + this->uninitialized_copy(in_start, in_end, this->end()); + this->set_size(this->size() + NumInputs); + } + + /// Append \p NumInputs copies of \p Elt to the end. + void append(size_type NumInputs, ValueParamT Elt) { + const T* EltPtr = this->reserveForParamAndGetAddress(Elt, NumInputs); + std::uninitialized_fill_n(this->end(), NumInputs, *EltPtr); + this->set_size(this->size() + NumInputs); + } + + void append(std::initializer_list<T> IL) { + append(IL.begin(), IL.end()); + } + + void append(const SmallVectorImpl& RHS) { append(RHS.begin(), RHS.end()); } + + void assign(size_type NumElts, ValueParamT Elt) { + // Note that Elt could be an internal reference. + if (NumElts > this->capacity()) { + this->growAndAssign(NumElts, Elt); + return; + } + + // Assign over existing elements. + std::fill_n(this->begin(), std::min(NumElts, this->size()), Elt); + if (NumElts > this->size()) + std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt); + else if (NumElts < this->size()) + this->destroy_range(this->begin() + NumElts, this->end()); + this->set_size(NumElts); + } + + // FIXME: Consider assigning over existing elements, rather than clearing & + // re-initializing them - for all assign(...) variants. + + template <typename in_iter, + typename = std::enable_if_t<std::is_convertible< + typename std::iterator_traits<in_iter>::iterator_category, + std::input_iterator_tag>::value>> + void assign(in_iter in_start, in_iter in_end) { + this->assertSafeToReferenceAfterClear(in_start, in_end); + clear(); + append(in_start, in_end); + } + + void assign(std::initializer_list<T> IL) { + clear(); + append(IL); + } + + void assign(const SmallVectorImpl& RHS) { assign(RHS.begin(), RHS.end()); } + + iterator erase(const_iterator CI) { + // Just cast away constness because this is a non-const member function. + iterator I = const_cast<iterator>(CI); + + assert(this->isReferenceToStorage(CI) && "Iterator to erase is out of bounds."); + + iterator N = I; + // Shift all elts down one. + std::move(I + 1, this->end(), I); + // Drop the last elt. + this->pop_back(); + return (N); + } + + iterator erase(const_iterator CS, const_iterator CE) { + // Just cast away constness because this is a non-const member function. + iterator S = const_cast<iterator>(CS); + iterator E = const_cast<iterator>(CE); + + assert(this->isRangeInStorage(S, E) && "Range to erase is out of bounds."); + + iterator N = S; + // Shift all elts down. + iterator I = std::move(E, this->end(), S); + // Drop the last elts. + this->destroy_range(I, this->end()); + this->set_size(I - this->begin()); + return (N); + } + +private: + template <class ArgType> + iterator insert_one_impl(iterator I, ArgType&& Elt) { + // Callers ensure that ArgType is derived from T. + static_assert( + std::is_same<std::remove_const_t<std::remove_reference_t<ArgType>>, + T>::value, + "ArgType must be derived from T!"); + + if (I == this->end()) { // Important special case for empty vector. + this->push_back(::std::forward<ArgType>(Elt)); + return this->end() - 1; + } + + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); + + // Grow if necessary. + size_t Index = I - this->begin(); + std::remove_reference_t<ArgType>* EltPtr = + this->reserveForParamAndGetAddress(Elt); + I = this->begin() + Index; + + ::new ((void*)this->end()) T(::std::move(this->back())); + // Push everything else over. + std::move_backward(I, this->end() - 1, this->end()); + this->set_size(this->size() + 1); + + // If we just moved the element we're inserting, be sure to update + // the reference (never happens if TakesParamByValue). + static_assert(!TakesParamByValue || std::is_same<ArgType, T>::value, + "ArgType must be 'T' when taking by value!"); + if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end())) + ++EltPtr; + + *I = ::std::forward<ArgType>(*EltPtr); + return I; + } + +public: + iterator insert(iterator I, T&& Elt) { + return insert_one_impl(I, this->forward_value_param(std::move(Elt))); + } + + iterator insert(iterator I, const T& Elt) { + return insert_one_impl(I, this->forward_value_param(Elt)); + } + + iterator insert(iterator I, size_type NumToInsert, ValueParamT Elt) { + // Convert iterator to elt# to avoid invalidating iterator when we reserve() + size_t InsertElt = I - this->begin(); + + if (I == this->end()) { // Important special case for empty vector. + append(NumToInsert, Elt); + return this->begin() + InsertElt; + } + + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); + + // Ensure there is enough space, and get the (maybe updated) address of + // Elt. + const T* EltPtr = this->reserveForParamAndGetAddress(Elt, NumToInsert); + + // Uninvalidate the iterator. + I = this->begin() + InsertElt; + + // If there are more elements between the insertion point and the end of the + // range than there are being inserted, we can use a simple approach to + // insertion. Since we already reserved space, we know that this won't + // reallocate the vector. + if (size_t(this->end() - I) >= NumToInsert) { + T* OldEnd = this->end(); + append(std::move_iterator<iterator>(this->end() - NumToInsert), + std::move_iterator<iterator>(this->end())); + + // Copy the existing elements that get replaced. + std::move_backward(I, OldEnd - NumToInsert, OldEnd); + + // If we just moved the element we're inserting, be sure to update + // the reference (never happens if TakesParamByValue). + if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end()) + EltPtr += NumToInsert; + + std::fill_n(I, NumToInsert, *EltPtr); + return I; + } + + // Otherwise, we're inserting more elements than exist already, and we're + // not inserting at the end. + + // Move over the elements that we're about to overwrite. + T* OldEnd = this->end(); + this->set_size(this->size() + NumToInsert); + size_t NumOverwritten = OldEnd - I; + this->uninitialized_move(I, OldEnd, this->end() - NumOverwritten); + + // If we just moved the element we're inserting, be sure to update + // the reference (never happens if TakesParamByValue). + if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end()) + EltPtr += NumToInsert; + + // Replace the overwritten part. + std::fill_n(I, NumOverwritten, *EltPtr); + + // Insert the non-overwritten middle part. + std::uninitialized_fill_n(OldEnd, NumToInsert - NumOverwritten, *EltPtr); + return I; + } + + template <typename ItTy, + typename = std::enable_if_t<std::is_convertible< + typename std::iterator_traits<ItTy>::iterator_category, + std::input_iterator_tag>::value>> + iterator insert(iterator I, ItTy From, ItTy To) { + // Convert iterator to elt# to avoid invalidating iterator when we reserve() + size_t InsertElt = I - this->begin(); + + if (I == this->end()) { // Important special case for empty vector. + append(From, To); + return this->begin() + InsertElt; + } + + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); + + // Check that the reserve that follows doesn't invalidate the iterators. + this->assertSafeToAddRange(From, To); + + size_t NumToInsert = std::distance(From, To); + + // Ensure there is enough space. + reserve(this->size() + NumToInsert); + + // Uninvalidate the iterator. + I = this->begin() + InsertElt; + + // If there are more elements between the insertion point and the end of the + // range than there are being inserted, we can use a simple approach to + // insertion. Since we already reserved space, we know that this won't + // reallocate the vector. + if (size_t(this->end() - I) >= NumToInsert) { + T* OldEnd = this->end(); + append(std::move_iterator<iterator>(this->end() - NumToInsert), + std::move_iterator<iterator>(this->end())); + + // Copy the existing elements that get replaced. + std::move_backward(I, OldEnd - NumToInsert, OldEnd); + + std::copy(From, To, I); + return I; + } + + // Otherwise, we're inserting more elements than exist already, and we're + // not inserting at the end. + + // Move over the elements that we're about to overwrite. + T* OldEnd = this->end(); + this->set_size(this->size() + NumToInsert); + size_t NumOverwritten = OldEnd - I; + this->uninitialized_move(I, OldEnd, this->end() - NumOverwritten); + + // Replace the overwritten part. + for (T* J = I; NumOverwritten > 0; --NumOverwritten) { + *J = *From; + ++J; + ++From; + } + + // Insert the non-overwritten middle part. + this->uninitialized_copy(From, To, OldEnd); + return I; + } + + void insert(iterator I, std::initializer_list<T> IL) { + insert(I, IL.begin(), IL.end()); + } + + template <typename... ArgTypes> + reference emplace_back(ArgTypes&&... Args) { + if (LLVM_UNLIKELY(this->size() >= this->capacity())) + return this->growAndEmplaceBack(std::forward<ArgTypes>(Args)...); + + ::new ((void*)this->end()) T(std::forward<ArgTypes>(Args)...); + this->set_size(this->size() + 1); + return this->back(); + } + + SmallVectorImpl& operator=(const SmallVectorImpl& RHS); + + SmallVectorImpl& operator=(SmallVectorImpl&& RHS); + + bool operator==(const SmallVectorImpl& RHS) const { + if (this->size() != RHS.size()) return false; + return std::equal(this->begin(), this->end(), RHS.begin()); + } + bool operator!=(const SmallVectorImpl& RHS) const { + return !(*this == RHS); + } + + bool operator<(const SmallVectorImpl& RHS) const { + return std::lexicographical_compare(this->begin(), this->end(), RHS.begin(), RHS.end()); + } +}; + +template <typename T> +void SmallVectorImpl<T>::swap(SmallVectorImpl<T>& RHS) { + if (this == &RHS) return; + + // We can only avoid copying elements if neither vector is small. + if (!this->isSmall() && !RHS.isSmall()) { + std::swap(this->BeginX, RHS.BeginX); + std::swap(this->Size, RHS.Size); + std::swap(this->Capacity, RHS.Capacity); + return; + } + this->reserve(RHS.size()); + RHS.reserve(this->size()); + + // Swap the shared elements. + size_t NumShared = this->size(); + if (NumShared > RHS.size()) NumShared = RHS.size(); + for (size_type i = 0; i != NumShared; ++i) + std::swap((*this)[i], RHS[i]); + + // Copy over the extra elts. + if (this->size() > RHS.size()) { + size_t EltDiff = this->size() - RHS.size(); + this->uninitialized_copy(this->begin() + NumShared, this->end(), RHS.end()); + RHS.set_size(RHS.size() + EltDiff); + this->destroy_range(this->begin() + NumShared, this->end()); + this->set_size(NumShared); + } else if (RHS.size() > this->size()) { + size_t EltDiff = RHS.size() - this->size(); + this->uninitialized_copy(RHS.begin() + NumShared, RHS.end(), this->end()); + this->set_size(this->size() + EltDiff); + this->destroy_range(RHS.begin() + NumShared, RHS.end()); + RHS.set_size(NumShared); + } +} + +template <typename T> +SmallVectorImpl<T>& SmallVectorImpl<T>:: +operator=(const SmallVectorImpl<T>& RHS) { + // Avoid self-assignment. + if (this == &RHS) return *this; + + // If we already have sufficient space, assign the common elements, then + // destroy any excess. + size_t RHSSize = RHS.size(); + size_t CurSize = this->size(); + if (CurSize >= RHSSize) { + // Assign common elements. + iterator NewEnd; + if (RHSSize) + NewEnd = std::copy(RHS.begin(), RHS.begin() + RHSSize, this->begin()); + else + NewEnd = this->begin(); + + // Destroy excess elements. + this->destroy_range(NewEnd, this->end()); + + // Trim. + this->set_size(RHSSize); + return *this; + } + + // If we have to grow to have enough elements, destroy the current elements. + // This allows us to avoid copying them during the grow. + // FIXME: don't do this if they're efficiently moveable. + if (this->capacity() < RHSSize) { + // Destroy current elements. + this->clear(); + CurSize = 0; + this->grow(RHSSize); + } else if (CurSize) { + // Otherwise, use assignment for the already-constructed elements. + std::copy(RHS.begin(), RHS.begin() + CurSize, this->begin()); + } + + // Copy construct the new elements in place. + this->uninitialized_copy(RHS.begin() + CurSize, RHS.end(), this->begin() + CurSize); + + // Set end. + this->set_size(RHSSize); + return *this; +} + +template <typename T> +SmallVectorImpl<T>& SmallVectorImpl<T>::operator=(SmallVectorImpl<T>&& RHS) { + // Avoid self-assignment. + if (this == &RHS) return *this; + + // If the RHS isn't small, clear this vector and then steal its buffer. + if (!RHS.isSmall()) { + this->assignRemote(std::move(RHS)); + return *this; + } + + // If we already have sufficient space, assign the common elements, then + // destroy any excess. + size_t RHSSize = RHS.size(); + size_t CurSize = this->size(); + if (CurSize >= RHSSize) { + // Assign common elements. + iterator NewEnd = this->begin(); + if (RHSSize) + NewEnd = std::move(RHS.begin(), RHS.end(), NewEnd); + + // Destroy excess elements and trim the bounds. + this->destroy_range(NewEnd, this->end()); + this->set_size(RHSSize); + + // Clear the RHS. + RHS.clear(); + + return *this; + } + + // If we have to grow to have enough elements, destroy the current elements. + // This allows us to avoid copying them during the grow. + // FIXME: this may not actually make any sense if we can efficiently move + // elements. + if (this->capacity() < RHSSize) { + // Destroy current elements. + this->clear(); + CurSize = 0; + this->grow(RHSSize); + } else if (CurSize) { + // Otherwise, use assignment for the already-constructed elements. + std::move(RHS.begin(), RHS.begin() + CurSize, this->begin()); + } + + // Move-construct the new elements in place. + this->uninitialized_move(RHS.begin() + CurSize, RHS.end(), this->begin() + CurSize); + + // Set end. + this->set_size(RHSSize); + + RHS.clear(); + return *this; +} + +/// Storage for the SmallVector elements. This is specialized for the N=0 case +/// to avoid allocating unnecessary storage. +template <typename T, unsigned N> +struct SmallVectorStorage { + alignas(T) char InlineElts[N * sizeof(T)]; +}; + +/// We need the storage to be properly aligned even for small-size of 0 so that +/// the pointer math in \a SmallVectorTemplateCommon::getFirstEl() is +/// well-defined. +template <typename T> +struct alignas(T) SmallVectorStorage<T, 0> {}; + +/// Forward declaration of SmallVector so that +/// calculateSmallVectorDefaultInlinedElements can reference +/// `sizeof(SmallVector<T, 0>)`. +template <typename T, unsigned N> +class SmallVector; + +/// Helper class for calculating the default number of inline elements for +/// `SmallVector<T>`. +/// +/// This should be migrated to a constexpr function when our minimum +/// compiler support is enough for multi-statement constexpr functions. +template <typename T> +struct CalculateSmallVectorDefaultInlinedElements { + // Parameter controlling the default number of inlined elements + // for `SmallVector<T>`. + // + // The default number of inlined elements ensures that + // 1. There is at least one inlined element. + // 2. `sizeof(SmallVector<T>) <= kPreferredSmallVectorSizeof` unless + // it contradicts 1. + static constexpr size_t kPreferredSmallVectorSizeof = 64; + + // static_assert that sizeof(T) is not "too big". + // + // Because our policy guarantees at least one inlined element, it is possible + // for an arbitrarily large inlined element to allocate an arbitrarily large + // amount of inline storage. We generally consider it an antipattern for a + // SmallVector to allocate an excessive amount of inline storage, so we want + // to call attention to these cases and make sure that users are making an + // intentional decision if they request a lot of inline storage. + // + // We want this assertion to trigger in pathological cases, but otherwise + // not be too easy to hit. To accomplish that, the cutoff is actually somewhat + // larger than kPreferredSmallVectorSizeof (otherwise, + // `SmallVector<SmallVector<T>>` would be one easy way to trip it, and that + // pattern seems useful in practice). + // + // One wrinkle is that this assertion is in theory non-portable, since + // sizeof(T) is in general platform-dependent. However, we don't expect this + // to be much of an issue, because most LLVM development happens on 64-bit + // hosts, and therefore sizeof(T) is expected to *decrease* when compiled for + // 32-bit hosts, dodging the issue. The reverse situation, where development + // happens on a 32-bit host and then fails due to sizeof(T) *increasing* on a + // 64-bit host, is expected to be very rare. + static_assert( + sizeof(T) <= 256, + "You are trying to use a default number of inlined elements for " + "`SmallVector<T>` but `sizeof(T)` is really big! Please use an " + "explicit number of inlined elements with `SmallVector<T, N>` to make " + "sure you really want that much inline storage."); + + // Discount the size of the header itself when calculating the maximum inline + // bytes. + static constexpr size_t PreferredInlineBytes = + kPreferredSmallVectorSizeof - sizeof(SmallVector<T, 0>); + static constexpr size_t NumElementsThatFit = PreferredInlineBytes / sizeof(T); + static constexpr size_t value = + NumElementsThatFit == 0 ? 1 : NumElementsThatFit; +}; + +/// This is a 'vector' (really, a variable-sized array), optimized +/// for the case when the array is small. It contains some number of elements +/// in-place, which allows it to avoid heap allocation when the actual number of +/// elements is below that threshold. This allows normal "small" cases to be +/// fast without losing generality for large inputs. +/// +/// \note +/// In the absence of a well-motivated choice for the number of inlined +/// elements \p N, it is recommended to use \c SmallVector<T> (that is, +/// omitting the \p N). This will choose a default number of inlined elements +/// reasonable for allocation on the stack (for example, trying to keep \c +/// sizeof(SmallVector<T>) around 64 bytes). +/// +/// \warning This does not attempt to be exception safe. +/// +/// \see https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h +template <typename T, + unsigned N = CalculateSmallVectorDefaultInlinedElements<T>::value> +class SmallVector : public SmallVectorImpl<T>, + SmallVectorStorage<T, N> { +public: + SmallVector() + : SmallVectorImpl<T>(N) {} + + ~SmallVector() { + // Destroy the constructed elements in the vector. + this->destroy_range(this->begin(), this->end()); + } + + explicit SmallVector(size_t Size, const T& Value = T()) + : SmallVectorImpl<T>(N) { + this->assign(Size, Value); + } + + template <typename ItTy, + typename = std::enable_if_t<std::is_convertible< + typename std::iterator_traits<ItTy>::iterator_category, + std::input_iterator_tag>::value>> + SmallVector(ItTy S, ItTy E) + : SmallVectorImpl<T>(N) { + this->append(S, E); + } + + template <typename RangeTy> + explicit SmallVector(const iterator_range<RangeTy>& R) + : SmallVectorImpl<T>(N) { + this->append(R.begin(), R.end()); + } + + SmallVector(std::initializer_list<T> IL) + : SmallVectorImpl<T>(N) { + this->assign(IL); + } + + SmallVector(const SmallVector& RHS) + : SmallVectorImpl<T>(N) { + if (!RHS.empty()) + SmallVectorImpl<T>::operator=(RHS); + } + + SmallVector& operator=(const SmallVector& RHS) { + SmallVectorImpl<T>::operator=(RHS); + return *this; + } + + SmallVector(SmallVector&& RHS) + : SmallVectorImpl<T>(N) { + if (!RHS.empty()) + SmallVectorImpl<T>::operator=(::std::move(RHS)); + } + + SmallVector(SmallVectorImpl<T>&& RHS) + : SmallVectorImpl<T>(N) { + if (!RHS.empty()) + SmallVectorImpl<T>::operator=(::std::move(RHS)); + } + + SmallVector& operator=(SmallVector&& RHS) { + if (N) { + SmallVectorImpl<T>::operator=(::std::move(RHS)); + return *this; + } + // SmallVectorImpl<T>::operator= does not leverage N==0. Optimize the + // case. + if (this == &RHS) + return *this; + if (RHS.empty()) { + this->destroy_range(this->begin(), this->end()); + this->Size = 0; + } else { + this->assignRemote(std::move(RHS)); + } + return *this; + } + + SmallVector& operator=(SmallVectorImpl<T>&& RHS) { + SmallVectorImpl<T>::operator=(::std::move(RHS)); + return *this; + } + + SmallVector& operator=(std::initializer_list<T> IL) { + this->assign(IL); + return *this; + } +}; + +template <typename T, unsigned N> +inline size_t capacity_in_bytes(const SmallVector<T, N>& X) { + return X.capacity_in_bytes(); +} + +template <typename RangeType> +using ValueTypeFromRangeType = + typename std::remove_const<typename std::remove_reference< + decltype(*std::begin(std::declval<RangeType&>()))>::type>::type; + +/// Given a range of type R, iterate the entire range and return a +/// SmallVector with elements of the vector. This is useful, for example, +/// when you want to iterate a range and then sort the results. +template <unsigned Size, typename R> +SmallVector<ValueTypeFromRangeType<R>, Size> to_vector(R&& Range) { + return { std::begin(Range), std::end(Range) }; +} +template <typename R> +SmallVector<ValueTypeFromRangeType<R>, + CalculateSmallVectorDefaultInlinedElements< + ValueTypeFromRangeType<R>>::value> +to_vector(R&& Range) { + return { std::begin(Range), std::end(Range) }; +} + +namespace std { + +/// Implement std::swap in terms of SmallVector swap. +template <typename T> +inline void swap(SmallVectorImpl<T>& LHS, SmallVectorImpl<T>& RHS) { + LHS.swap(RHS); +} + +/// Implement std::swap in terms of SmallVector swap. +template <typename T, unsigned N> +inline void swap(SmallVector<T, N>& LHS, SmallVector<T, N>& RHS) { + LHS.swap(RHS); +} + +} // namespace std + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/source/10-common/StbImplementations.c b/source/10-common/StbImplementations.c new file mode 100644 index 0000000..73bbc2a --- /dev/null +++ b/source/10-common/StbImplementations.c @@ -0,0 +1,14 @@ +#define STB_RECT_PACK_IMPLEMENTATION +#include <stb_rect_pack.h> + +#define STB_TRUETYPE_IMPLEMENTATION +#include <stb_truetype.h> + +#define STB_IMAGE_IMPLEMENTATION +#include <stb_image.h> + +#define STB_SPRINTF_IMPLEMENTATION +#include <stb_sprintf.h> + +#define STB_C_LEXER_IMPLEMENTATION +#include <stb_c_lexer.h> diff --git a/source/10-common/TypeTraits.hpp b/source/10-common/TypeTraits.hpp new file mode 100644 index 0000000..cca9a1f --- /dev/null +++ b/source/10-common/TypeTraits.hpp @@ -0,0 +1,19 @@ +#pragma once + +template <class T> +struct DefaultDeleter { + void operator()(T* ptr) const { + delete ptr; + } +}; + +template <class> +struct RemoveMemberPtrImpl {}; + +template <class T, class U> +struct RemoveMemberPtrImpl<U T::*> { + using Type = U; +}; + +template <class T> +using RemoveMemberPtr = typename RemoveMemberPtrImpl<T>::Type; diff --git a/source/10-common/Uid.cpp b/source/10-common/Uid.cpp new file mode 100644 index 0000000..1930cd8 --- /dev/null +++ b/source/10-common/Uid.cpp @@ -0,0 +1,58 @@ +#include "Uid.hpp" + +#include "RapidJsonHelper.hpp" + +#include <rapidjson/document.h> +#include <cstring> +#include <random> + +Uid Uid::Create() { + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution<uint64_t> dist( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + + Uid uid; + uid.upper = dist(gen); + uid.lower = dist(gen); + return uid; +} + +bool Uid::IsNull() const { + return upper == 0 && lower == 0; +} + +void Uid::ReadString(std::string_view str) { + sscanf(str.data(), BRUSSEL_Uid_SCAN_STR, &upper, &lower); +} + +std::string Uid::WriteString() { + char buf[256]; + snprintf(buf, sizeof(buf), BRUSSEL_Uid_FORMAT_STR, upper, lower); + return std::string(buf); +} + +void Uid::Read(const rapidjson::Value& value) { + assert(value.IsArray()); + assert(value.Size() == 2); + auto& upper = value[0]; + assert(upper.IsUint64()); + auto& lower = value[1]; + assert(lower.IsUint64()); + + this->upper = upper.GetUint64(); + this->lower = lower.GetUint64(); +} + +void Uid::WriteInto(rapidjson::Value& value, rapidjson::Document& root) { + value.Reserve(2, root.GetAllocator()); + value.PushBack((uint64_t)upper, root.GetAllocator()); + value.PushBack((uint64_t)lower, root.GetAllocator()); +} + +rapidjson::Value Uid::Write(rapidjson::Document& root) { + rapidjson::Value result(rapidjson::kArrayType); + WriteInto(result, root); + return result; +} diff --git a/source/10-common/Uid.hpp b/source/10-common/Uid.hpp new file mode 100644 index 0000000..f58129c --- /dev/null +++ b/source/10-common/Uid.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Utils.hpp" + +#include <rapidjson/fwd.h> +#include <cinttypes> +#include <functional> +#include <string> +#include <string_view> + +#define BRUSSEL_Uid_SCAN_STR "%" PRIx64 "-%" PRIx64 +#define BRUSSEL_Uid_SCAN_EXPAND(uid) &((uid).upper), &((uid).upper) +#define BRUSSEL_Uid_FORMAT_STR "%016" PRIx64 "-%016" PRIx64 +#define BRUSSEL_Uid_FORMAT_EXPAND(uid) (uid).upper, (uid).lower + +struct Uid { + uint64_t upper = 0; + uint64_t lower = 0; + + static Uid Create(); + + bool IsNull() const; + + void ReadString(std::string_view str); + std::string WriteString(); + + void Read(const rapidjson::Value& value); + void WriteInto(rapidjson::Value& value, rapidjson::Document& root); + rapidjson::Value Write(rapidjson::Document& root); + + auto operator<=>(const Uid&) const = default; +}; + +template <> +struct std::hash<Uid> { + size_t operator()(const Uid& uid) const { + size_t hash = 0; + Utils::HashCombine(hash, uid.upper); + Utils::HashCombine(hash, uid.lower); + return hash; + } +}; diff --git a/source/10-common/Utils.cpp b/source/10-common/Utils.cpp new file mode 100644 index 0000000..53b3863 --- /dev/null +++ b/source/10-common/Utils.cpp @@ -0,0 +1,87 @@ +#include "Utils.hpp" + +#ifdef _WIN32 +# include <Windows.h> +#endif + +#ifdef _WIN32 +# define BRUSSEL_MODE_STRING(string) L##string +#else +# define BRUSSEL_MODE_STRING(string) string +#endif + +#if _WIN32 +using FopenModeString = const wchar_t*; +#else +using FopenModeString = const char*; +#endif + +static FopenModeString GetModeString(Utils::IoMode mode, bool binary) { + using namespace Utils; + if (binary) { + switch (mode) { + case Read: return BRUSSEL_MODE_STRING("rb"); + case WriteTruncate: return BRUSSEL_MODE_STRING("wb"); + case WriteAppend: return BRUSSEL_MODE_STRING("ab"); + } + } else { + switch (mode) { + case Read: return BRUSSEL_MODE_STRING("r"); + case WriteTruncate: return BRUSSEL_MODE_STRING("w"); + case WriteAppend: return BRUSSEL_MODE_STRING("a"); + } + } + return nullptr; +} + +FILE* Utils::OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary) { +#ifdef _WIN32 + // std::filesystem::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively + // NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen() + return _wfopen(path.c_str(), ::GetModeString(mode, binary)); +#else + return fopen(path.c_str(), ::GetModeString(mode, binary)); +#endif +} + +FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) { +#ifdef _WIN32 + // On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work + WCHAR platformPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) { + return nullptr; + } + return _wfopen(platformPath, ::GetModeString(mode, binary)); +#else + return fopen(path, ::GetModeString(mode, binary)); +#endif +} + +bool Utils::InRangeInclusive(int n, int lower, int upper) { + if (lower > upper) { + std::swap(lower, upper); + } + return n >= lower && n <= upper; +} + +bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) { + bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x); + bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y); + return verticalLine && horizontalLine; +} + +bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) { + return p1.x == p2.x || p1.y == p2.y; +} + +std::string Utils::MakeRandomNumberedName(const char* tag) { + int n = std::rand(); +#define RNG_NAME_PATTERN "Unnamed %s #%d", tag, n + // NOTE: does not include null-terminator + int size = snprintf(nullptr, 0, RNG_NAME_PATTERN); + std::string result; + result.resize(size); // std::string::resize handles storage for null-terminator alreaedy + snprintf(result.data(), size, RNG_NAME_PATTERN); +#undef RNG_NAME_PATTERN + return result; +} diff --git a/source/10-common/Utils.hpp b/source/10-common/Utils.hpp new file mode 100644 index 0000000..9f28aad --- /dev/null +++ b/source/10-common/Utils.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <robin_hood.h> +#include <cstdio> +#include <cstring> +#include <filesystem> +#include <glm/glm.hpp> + +namespace Utils { + +enum IoMode { + Read, + WriteTruncate, + WriteAppend, +}; + +FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary = false); +FILE* OpenCstdioFile(const char* path, IoMode mode, bool binary = false); + +constexpr float Abs(float v) noexcept { + return v < 0.0f ? -v : v; +} + +bool InRangeInclusive(int n, int lower, int upper); +bool LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate); + +bool IsColinear(glm::ivec2 p1, glm::ivec2 p2); + +template <class T> +void HashCombine(std::size_t& seed, const T& v) { + seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +std::string MakeRandomNumberedName(const char* tag); + +} // namespace Utils + +struct StringHash { + using is_transparent = void; + + std::size_t operator()(const std::string& key) const { return robin_hood::hash_bytes(key.c_str(), key.size()); } + std::size_t operator()(std::string_view key) const { return robin_hood::hash_bytes(key.data(), key.size()); } + std::size_t operator()(const char* key) const { return robin_hood::hash_bytes(key, std::strlen(key)); } +}; + +struct StringEqual { + using is_transparent = int; + + bool operator()(std::string_view lhs, const std::string& rhs) const { + const std::string_view view = rhs; + return lhs == view; + } + + bool operator()(const char* lhs, const std::string& rhs) const { + return std::strcmp(lhs, rhs.c_str()) == 0; + } + + bool operator()(const std::string& lhs, const std::string& rhs) const { + return lhs == rhs; + } +}; diff --git a/source/10-common/YCombinator.hpp b/source/10-common/YCombinator.hpp new file mode 100644 index 0000000..b1d2350 --- /dev/null +++ b/source/10-common/YCombinator.hpp @@ -0,0 +1,14 @@ +#pragma once + +template <class Func> +struct YCombinator { + // NOTE: implicit constructor allows initializing this + Func func; + + template <class... Ts> + decltype(auto) operator()(Ts&&... args) const { + // NOTE: static_cast<Ts>(args)... is equivalent to std::forward<Ts>(args)... + // written this way so that we don't have to include <utility>, as well as reducing template instanciations to help compile time + return func(*this, static_cast<Ts>(args)...); + } +}; diff --git a/source/20-codegen-compiler/CodegenConfig.hpp b/source/20-codegen-compiler/CodegenConfig.hpp new file mode 100644 index 0000000..b9dc56c --- /dev/null +++ b/source/20-codegen-compiler/CodegenConfig.hpp @@ -0,0 +1,11 @@ +#pragma once + +#ifndef CODEGEN_DEBUG_PRINT +# define CODEGEN_DEBUG_PRINT 0 +#endif + +#if CODEGEN_DEBUG_PRINT +# define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else +# define DEBUG_PRINTF(...) +#endif diff --git a/source/20-codegen-compiler/CodegenDecl.cpp b/source/20-codegen-compiler/CodegenDecl.cpp new file mode 100644 index 0000000..7cf21ce --- /dev/null +++ b/source/20-codegen-compiler/CodegenDecl.cpp @@ -0,0 +1,49 @@ +#include "CodegenDecl.hpp" + +#include <Utils.hpp> + +static EnumValuePattern NextPattern(EnumValuePattern val) { + return (EnumValuePattern)(val + 1); +} + +EnumValuePattern DeclEnum::CalcPattern() const { + if (elements.empty()) return EVP_Continuous; + + auto pattern = EVP_Continuous; +restart: + auto lastVal = elements[0].value; + for (size_t i = 1; i < elements.size(); ++i) { + auto currVal = elements[i].value; + switch (pattern) { + case EVP_Continuous: { + bool satisfy = lastVal + 1 == currVal; + if (!satisfy) { + pattern = NextPattern(pattern); + goto restart; + } + } break; + + case EVP_Bits: { + bool satisfy = (lastVal << 1) == currVal; + if (!satisfy) { + pattern = NextPattern(pattern); + goto restart; + } + } break; + + // A random pattern can match anything + case EVP_Random: + case EVP_COUNT: break; + } + lastVal = currVal; + } + + return pattern; +} + +EnumValuePattern DeclEnum::GetPattern() const { + if (pattern == EVP_COUNT) { + pattern = CalcPattern(); + } + return pattern; +} diff --git a/source/20-codegen-compiler/CodegenDecl.hpp b/source/20-codegen-compiler/CodegenDecl.hpp new file mode 100644 index 0000000..32d5445 --- /dev/null +++ b/source/20-codegen-compiler/CodegenDecl.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include <string> +#include <vector> + +struct DeclNamespace { + DeclNamespace* container = nullptr; + std::string name; + std::string_view fullname; // View into storage map key +}; + +// Structs or classes +struct DeclStruct { + DeclNamespace* container = nullptr; + std::string name; +}; + +enum EnumUnderlyingType { + EUT_Int8, + EUT_Int16, + EUT_Int32, + EUT_Int64, + EUT_Uint8, + EUT_Uint16, + EUT_Uint32, + EUT_Uint64, + EUT_COUNT, +}; + +enum EnumValuePattern { + // The numbers cover n..m with no gaps + EVP_Continuous, + // The numbers cover for i in n..m, 1 << i + // e.g. [0] = 1 << 0, + // [1] = 1 << 1. + // [2] = 1 << 2. etc. + EVP_Bits, + // The numbesr don't have a particular pattern + EVP_Random, + EVP_COUNT, +}; + +struct DeclEnumElement { + std::string name; + // TODO support int64_t, etc. enum underlying types + uint64_t value; +}; + +struct DeclEnum { + DeclNamespace* container = nullptr; + std::string name; + std::vector<DeclEnumElement> elements; + EnumUnderlyingType underlyingType; + // Start with invalid value, calculate on demand + mutable EnumValuePattern pattern = EVP_COUNT; + + EnumValuePattern CalcPattern() const; + EnumValuePattern GetPattern() const; +}; + +struct DeclFunctionArgument { + std::string type; + std::string name; +}; + +struct DeclFunction { + DeclNamespace* container = nullptr; + // Things like extern, static, etc. that gets written before the function return type + std::string prefix; + std::string name; + std::string returnType; + std::vector<DeclFunctionArgument> arguments; + std::string body; +}; diff --git a/source/20-codegen-compiler/CodegenInput.inl b/source/20-codegen-compiler/CodegenInput.inl new file mode 100644 index 0000000..0809e7f --- /dev/null +++ b/source/20-codegen-compiler/CodegenInput.inl @@ -0,0 +1,69 @@ +#pragma once + +#include "CodegenConfig.hpp" +#include "CodegenDecl.hpp" + +#include "CodegenUtils.inl" + +#include <Utils.hpp> + +#include <robin_hood.h> +#include <cinttypes> +#include <string> +#include <string_view> +#include <vector> + +using namespace std::literals; + +class CodegenInput { +private: + std::vector<DeclEnum> mEnums; + robin_hood::unordered_flat_map<std::string, size_t, StringHash, StringEqual> mDeclByName; + robin_hood::unordered_node_map<std::string, DeclNamespace, StringHash, StringEqual> mNamespaces; + +public: + void AddEnum(std::string fullname, DeclEnum decl) { +#if CODEGEN_DEBUG_PRINT + printf("Committed enum '%s'\n", decl.name.c_str()); + for (auto& elm : decl.elements) { + printf(" - element %s = %" PRId64 "\n", elm.name.c_str(), elm.value); + } +#endif + + mDeclByName.try_emplace(std::move(fullname), mEnums.size()); + mEnums.push_back(std::move(decl)); + } + + DeclNamespace* AddNamespace(DeclNamespace ns) { + auto path = Utils::MakeFullName(""sv, &ns); + auto [iter, success] = mNamespaces.try_emplace(std::move(path), std::move(ns)); + auto& nsRef = iter->second; + if (success) { + nsRef.fullname = iter->first; + } + return &nsRef; + } + + const DeclEnum* FindEnumByName(std::string_view name) const { + // TODO handle multiple kinds of decl + auto iter = mDeclByName.find(name); + if (iter != mDeclByName.end()) { + return &mEnums[iter->second]; + } else { + return nullptr; + } + } + + const DeclNamespace* FindNamespace(std::string_view fullname) const { + auto iter = mNamespaces.find(fullname); + if (iter != mNamespaces.end()) { + return &iter->second; + } else { + return nullptr; + } + } + + DeclNamespace* FindNamespace(std::string_view name) { + return const_cast<DeclNamespace*>(const_cast<const CodegenInput*>(this)->FindNamespace(name)); + } +}; diff --git a/source/20-codegen-compiler/CodegenMacros.hpp b/source/20-codegen-compiler/CodegenMacros.hpp new file mode 100644 index 0000000..84c9d09 --- /dev/null +++ b/source/20-codegen-compiler/CodegenMacros.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <algorithm> + +// I give up, hopefully nothing overflows this buffer +// TODO handle buffer sizing properly + +#define APPEND_LIT(out, str) out += str + +#define APPEND_FMT(out, format, ...) \ + { \ + char buffer[65536]; \ + snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \ + out += buffer; \ + } + +#define WRITE_LIT(file, str) fwrite(str, sizeof(char), sizeof(str) - 1, file) + +// NOTE: snprintf() returns the size written (given an infinite buffer) not including \0 +#define WRITE_FMT(file, format, ...) \ + { \ + char buffer[65536]; \ + int size = snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \ + fwrite(buffer, sizeof(char), std::min<int>(size, sizeof(buffer)), file); \ + } + +#define APPEND_LIT_LN(out, str) APPEND_LIT(out, (str "\n")) +#define APPEND_FMT_LN(out, format, ...) APPEND_FMT(out, (format "\n"), __VA_ARGS__) +#define WRITE_LIT_LN(out, str) WRITE_LIT(out, (str "\n")) +#define WRITE_FMT_LN(out, format, ...) WRITE_FMT(out, (format "\n"), __VA_ARGS__) diff --git a/source/20-codegen-compiler/CodegenOutput.inl b/source/20-codegen-compiler/CodegenOutput.inl new file mode 100644 index 0000000..ff7b912 --- /dev/null +++ b/source/20-codegen-compiler/CodegenOutput.inl @@ -0,0 +1,76 @@ +#pragma once + +#include "CodegenDecl.hpp" +#include "CodegenMacros.hpp" + +#include <Utils.hpp> + +#include <robin_hood.h> +#include <algorithm> +#include <cstdio> +#include <cstdlib> +#include <string> +#include <vector> + +// A generic "thing" (could be anything, comments, string-concated functionsm, etc.) to spit into the output file +struct CodegenOutputThing { + std::string text; +}; + +class CodegenOutput { +private: + robin_hood::unordered_set<std::string, StringHash, StringEqual> mRequestIncludes; + std::vector<CodegenOutputThing> mOutThings; + std::vector<DeclStruct> mOutStructs; + std::vector<DeclEnum> mOutEnums; + std::vector<DeclFunction> mOutFunctions; + +public: + std::string optionOutPrefix; + // Whether to add prefixes mOutPrefix to all global names or not + bool optionAutoAddPrefix : 1 = false; + +public: + void AddRequestInclude(std::string_view include) { + if (!mRequestIncludes.contains(include)) { + mRequestIncludes.insert(std::string(include)); + } + } + + void AddOutputThing(CodegenOutputThing thing) { + mOutThings.push_back(std::move(thing)); + } + + void MergeContents(CodegenOutput other) { + std::move(other.mOutThings.begin(), other.mOutThings.end(), std::back_inserter(this->mOutThings)); + std::move(other.mOutStructs.begin(), other.mOutStructs.end(), std::back_inserter(this->mOutStructs)); + std::move(other.mOutEnums.begin(), other.mOutEnums.end(), std::back_inserter(this->mOutEnums)); + std::move(other.mOutFunctions.begin(), other.mOutFunctions.end(), std::back_inserter(this->mOutFunctions)); + } + + void Write(FILE* file) const { + for (auto& include : mRequestIncludes) { + // TODO how to resolve to the correct include paths? + WRITE_FMT_LN(file, "#include <%s>", include.c_str()); + } + + for (auto& thing : mOutThings) { + fwrite(thing.text.c_str(), sizeof(char), thing.text.size(), file); + WRITE_LIT(file, "\n"); + } + + for (auto& declStruct : mOutStructs) { + WRITE_FMT_LN(file, "struct %s {", declStruct.name.c_str()); + // TODO + WRITE_LIT_LN(file, "};"); + } + + for (auto& declEnum : mOutEnums) { + // TODO + } + + for (auto& declFunc : mOutFunctions) { + // TODO + } + } +}; diff --git a/source/20-codegen-compiler/CodegenUtils.inl b/source/20-codegen-compiler/CodegenUtils.inl new file mode 100644 index 0000000..6feb654 --- /dev/null +++ b/source/20-codegen-compiler/CodegenUtils.inl @@ -0,0 +1,106 @@ +#pragma once + +#include "CodegenConfig.hpp" +#include "CodegenMacros.hpp" + +#include "CodegenOutput.inl" + +#include <Macros.hpp> +#include <ScopeGuard.hpp> + +#include <cstdio> +#include <cstdlib> +#include <filesystem> + +namespace Utils { + +std::string ReadFileAsString(const std::filesystem::path& path) { + auto file = Utils::OpenCstdioFile(path, Utils::Read); + if (!file) throw std::runtime_error("Failed to open source file."); + DEFER { fclose(file); }; + + fseek(file, 0, SEEK_END); + auto fileSize = ftell(file); + rewind(file); + + std::string result(fileSize, '\0'); + fread(result.data(), fileSize, 1, file); + + return result; +} + +bool WriteOutputFile(const CodegenOutput& output, std::string_view dir, std::string_view filename, std::string_view additionalSuffix = {}) { + char path[2048]; + snprintf(path, sizeof(path), "%.*s/%.*s%.*s", PRINTF_STRING_VIEW(dir), PRINTF_STRING_VIEW(filename), PRINTF_STRING_VIEW(additionalSuffix)); + + auto outputFile = Utils::OpenCstdioFile(path, Utils::WriteTruncate); + if (!outputFile) { + printf("[ERROR] unable to open output file %s\n", path); + return false; + } + DEFER { fclose(outputFile); }; + + DEBUG_PRINTF("Writing output %s\n", path); + output.Write(outputFile); + + return true; +} + +std::string MakeFullName(std::string_view name, DeclNamespace* ns = nullptr) { + size_t length = 0; + std::vector<std::string_view> components; + if (!name.empty()) { + components.push_back(name); + length += name.length(); + } + + DeclNamespace* currentNamespace = ns; + while (currentNamespace) { + components.push_back(currentNamespace->name); + length += currentNamespace->name.size() + /*::*/ 2; + currentNamespace = currentNamespace->container; + } + + std::string fullname; + fullname.reserve(length); + for (auto it = components.rbegin(); it != components.rend(); ++it) { + fullname += *it; + fullname += "::"; + } + // Get rid of the last "::" + fullname.pop_back(); + fullname.pop_back(); + + return fullname; +} + +void ProduceGeneratedHeaderFileHeader(CodegenOutput& output) { + output.AddOutputThing(CodegenOutputThing{ + .text = &R"""( +// This file is generated. Any changes will be overidden when building. +#pragma once + +#include <MetadataBase.hpp> + +#include <cstddef> +#include <cstdint> +)"""[1], + }); +} + +void ProduceGeneratedSourceFileHeader(CodegenOutput& output) { + output.AddOutputThing(CodegenOutputThing{ + // TODO we need to get the header name + .text = &R"""( +// This file is generated. Any changes will be overidden when building. + +#include <cstddef> +#include <cstdint> +#include <frozen/string.h> +#include <frozen/unordered_map.h> +using namespace std::literals; + )"""[1], + }); +} + +} // namespace Utils diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp new file mode 100644 index 0000000..b139515 --- /dev/null +++ b/source/20-codegen-compiler/main.cpp @@ -0,0 +1,761 @@ +#include "CodegenConfig.hpp" +#include "CodegenDecl.hpp" +#include "CodegenMacros.hpp" + +#include "CodegenInput.inl" +#include "CodegenOutput.inl" +#include "CodegenUtils.inl" + +#include <Enum.hpp> +#include <LookupTable.hpp> +#include <Macros.hpp> +#include <ScopeGuard.hpp> +#include <Utils.hpp> + +#include <frozen/string.h> +#include <frozen/unordered_map.h> +#include <robin_hood.h> +#include <stb_c_lexer.h> +#include <cinttypes> +#include <cstdlib> +#include <filesystem> +#include <memory> +#include <span> +#include <string> +#include <string_view> + +using namespace std::literals; +namespace fs = std::filesystem; + +// TODO handle namespace +// TODO support codegen target in .cpp files + +struct AppState { + std::string_view outputDir; + CodegenOutput mainHeaderOutput; + CodegenOutput mainSourceOutput; +}; + +enum { + CLEX_ext_single_char = CLEX_first_unused_token, + CLEX_ext_COUNT, +}; + +STR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) { + STR_LUT_MAP_FOR(ClexNames); + STR_LUT_MAP_ENUM(CLEX_intlit); + STR_LUT_MAP_ENUM(CLEX_floatlit); + STR_LUT_MAP_ENUM(CLEX_id); + STR_LUT_MAP_ENUM(CLEX_dqstring); + STR_LUT_MAP_ENUM(CLEX_sqstring); + STR_LUT_MAP_ENUM(CLEX_charlit); + STR_LUT_MAP_ENUM(CLEX_eq); + STR_LUT_MAP_ENUM(CLEX_noteq); + STR_LUT_MAP_ENUM(CLEX_lesseq); + STR_LUT_MAP_ENUM(CLEX_greatereq); + STR_LUT_MAP_ENUM(CLEX_andand); + STR_LUT_MAP_ENUM(CLEX_oror); + STR_LUT_MAP_ENUM(CLEX_shl); + STR_LUT_MAP_ENUM(CLEX_shr); + STR_LUT_MAP_ENUM(CLEX_plusplus); + STR_LUT_MAP_ENUM(CLEX_minusminus); + STR_LUT_MAP_ENUM(CLEX_pluseq); + STR_LUT_MAP_ENUM(CLEX_minuseq); + STR_LUT_MAP_ENUM(CLEX_muleq); + STR_LUT_MAP_ENUM(CLEX_diveq); + STR_LUT_MAP_ENUM(CLEX_modeq); + STR_LUT_MAP_ENUM(CLEX_andeq); + STR_LUT_MAP_ENUM(CLEX_oreq); + STR_LUT_MAP_ENUM(CLEX_xoreq); + STR_LUT_MAP_ENUM(CLEX_arrow); + STR_LUT_MAP_ENUM(CLEX_eqarrow); + STR_LUT_MAP_ENUM(CLEX_shleq); + STR_LUT_MAP_ENUM(CLEX_shreq); + STR_LUT_MAP_ENUM(CLEX_ext_single_char); +} + +enum CppKeyword { + CKw_Namespace, + CKw_Struct, + CKw_Class, + CKw_Enum, + CKw_COUNT, +}; + +BSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) { + BSTR_LUT_MAP_FOR(CppKeyword); + BSTR_LUT_MAP(CKw_Namespace, "namespace"); + BSTR_LUT_MAP(CKw_Struct, "struct"); + BSTR_LUT_MAP(CKw_Class, "class"); + BSTR_LUT_MAP(CKw_Enum, "enum"); +} + +enum CodegenDirective { + CD_ClassInfo, + CD_EnumInfo, + CD_COUNT, +}; + +BSTR_LUT_DECL(CodegenDirective, 0, CD_COUNT) { + BSTR_LUT_MAP_FOR(CodegenDirective); + BSTR_LUT_MAP(CD_ClassInfo, "BRUSSEL_CLASS"); + BSTR_LUT_MAP(CD_EnumInfo, "BRUSSEL_ENUM"); +} + +struct StbLexerToken { + std::string text; + // Can either be CLEX_* or CLEX_ext_* values + int type; +}; + +bool StbTokenIsSingleChar(int lexerToken) { + return lexerToken >= 0 && lexerToken < 256; +} + +bool StbTokenIsMultiChar(int lexerToken) { + return !StbTokenIsMultiChar(lexerToken); +} + +void CheckBraceDepth(int braceDpeth) { + if (braceDpeth < 0) { + printf("[WARNING] unbalanced brace\n"); + } +} + +const StbLexerToken* +PeekTokenOfTypeAt(const std::vector<StbLexerToken>& tokens, size_t idx, int type) { + auto& token = tokens[idx]; + if (token.type != type) { + return nullptr; + } + + return &token; +} + +std::pair<const StbLexerToken*, size_t> +PeekTokenOfType(const std::vector<StbLexerToken>& tokens, size_t current, int type) { + for (size_t i = current; i < tokens.size(); ++i) { + if (auto token = PeekTokenOfTypeAt(tokens, i, type)) { + return { token, i }; + } + } + return { nullptr, current }; +} + +std::pair<std::vector<std::vector<const StbLexerToken*>>, size_t> +PeekDirectiveArgumentList(const std::vector<StbLexerToken>& tokens, size_t current) { + std::vector<std::vector<const StbLexerToken*>> result; + decltype(result)::value_type currentArg; + + size_t i = current; + int parenDepth = 0; + for (; i < tokens.size(); ++i) { + auto& token = tokens[i]; + if (token.text[0] == '(') { + if (parenDepth > 0) { + currentArg.push_back(&token); + } + ++parenDepth; + } else if (token.text[0] == ')') { + --parenDepth; + if (parenDepth == 0) { + // End of argument list + break; + } + } else if (parenDepth > 0) { + // Parse these only if we are inside the argument list + if (token.text[0] == ',') { + result.push_back(std::move(currentArg)); + currentArg = {}; + } else { + currentArg.push_back(&token); + } + } + } + + if (!currentArg.empty()) { + result.push_back(std::move(currentArg)); + } + + return { result, i }; +} + +std::vector<StbLexerToken> RecordTokens(std::string_view source) { + stb_lexer lexer; + char stringStorage[65536]; + const char* srcBegin = source.data(); + const char* srcEnd = srcBegin + source.length(); + stb_c_lexer_init(&lexer, srcBegin, srcEnd, stringStorage, sizeof(stringStorage)); + + std::vector<StbLexerToken> tokens; + while (true) { + // See stb_c_lexer.h's comments, here are a few additinos that aren't made clear in the file: + // - `lexer->token` (noted as "token" below) after calling stb_c_lexer_get_token() contains either: + // 1. 0 <= token < 256: an ASCII character (more precisely a single char that the lexer ate; technically can be an incomplete code unit) + // 2. token < 0: an unknown token + // 3. One of the `CLEX_*` enums: a special, recognized token such as an operator + + int stbToken = stb_c_lexer_get_token(&lexer); + if (stbToken == 0) { + // EOF + break; + } + + if (lexer.token == CLEX_parse_error) { + printf("[ERROR] stb_c_lexer countered a parse error.\n"); + // TODO how to handle? + continue; + } + + StbLexerToken token; + if (StbTokenIsSingleChar(lexer.token)) { + token.type = CLEX_ext_single_char; + token.text = std::string(1, lexer.token); + } else { + token.type = lexer.token; + // WORKAROUND: use null terminated string, stb_c_lexer doens't set string_len properly when parsing identifiers + token.text = std::string(lexer.string); + } + tokens.push_back(std::move(token)); + token = {}; + } + return tokens; +} + +enum StructMetaGenOptions { + SMGO_InheritanceHiearchy, + SMGO_PublicFields, + SMGO_ProtectedFields, + SMGO_PrivateFields, + SMGO_COUNT, +}; + +BSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) { + BSTR_LUT_MAP_FOR(StructMetaGenOptions); + BSTR_LUT_MAP(SMGO_InheritanceHiearchy, "GenInheritanceHiearchy"); + BSTR_LUT_MAP(SMGO_PublicFields, "GenPublicFields"); + BSTR_LUT_MAP(SMGO_ProtectedFields, "GenProtectedFields"); + BSTR_LUT_MAP(SMGO_PrivateFields, "GenPrivateFields"); +} + +enum EnumMetaGenOptions { + EMGO_ToString, + EMGO_FromString, + EMGO_ExcludeUseHeuristics, + EMGO_COUNT, +}; + +BSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) { + BSTR_LUT_MAP_FOR(EnumMetaGenOptions); + BSTR_LUT_MAP(EMGO_ToString, "ToString"); + BSTR_LUT_MAP(EMGO_FromString, "FromString"); + BSTR_LUT_MAP(EMGO_ExcludeUseHeuristics, "ExcludeHeuristics"); +} + +std::string GenerateEnumStringArray(CodegenOutput& out, const DeclEnum& decl, bool useHeruistics) { + std::string arrayName; + APPEND_FMT(arrayName, "gCG_%s_Val2Str", decl.name.c_str()); + + CodegenOutputThing thing; + APPEND_FMT_LN(thing.text, "const char* %s[] = {", arrayName.c_str()); + for (auto& elm : decl.elements) { + if (useHeruistics && elm.name.ends_with("COUNT")) { + continue; + } + + APPEND_FMT_LN(thing.text, "\"%s\",", elm.name.c_str()); + } + APPEND_LIT_LN(thing.text, "};"); + out.AddOutputThing(std::move(thing)); + + return arrayName; +} + +std::string GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, bool useHeruistics) { + std::string mapName; + // TODO + + return mapName; +} + +void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const DeclEnum& decl, EnumFlags<EnumMetaGenOptions> options) { + char enumName[2048]; + if (decl.container) { + snprintf(enumName, sizeof(enumName), "%.*s::%s", PRINTF_STRING_VIEW(decl.container->fullname), decl.name.c_str()); + } else { + strncpy(enumName, decl.name.c_str(), sizeof(enumName)); + } + + auto useExcludeHeuristics = options.IsSet(EMGO_ExcludeUseHeuristics); + auto filteredElements = [&]() { + if (useExcludeHeuristics) { + decltype(decl.elements) result; + for (auto& elm : decl.elements) { + if (elm.name.ends_with("COUNT")) continue; + + result.push_back(elm); + } + return result; + } else { + return decl.elements; + } + }(); + + if (options.IsSet(EMGO_ToString)) { + // Generate value -> string lookup table and function + + switch (decl.GetPattern()) { + case EVP_Continuous: { + auto arrayName = GenerateEnumStringArray(sourceOut, decl, useExcludeHeuristics); + int minVal = filteredElements.empty() ? 0 : filteredElements.front().value; + int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value; + + CodegenOutputThing lookupFunctionDef; + { + auto& o = lookupFunctionDef.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName, enumName); + APPEND_FMT_LN(o, " if (value < %d || value > %d) return {};", minVal, maxVal); + APPEND_FMT_LN(o, " return %s[value - %d];", arrayName.c_str(), minVal); + APPEND_LIT_LN(o, "}"); + } + + sourceOut.AddOutputThing(std::move(lookupFunctionDef)); + } break; + + case EVP_Bits: { + auto arrayName = GenerateEnumStringArray(sourceOut, decl, useExcludeHeuristics); + // TODO + } break; + + case EVP_Random: { + auto mapName = GenerateEnumStringMap(sourceOut, decl, useExcludeHeuristics); + // TODO + } break; + + case EVP_COUNT: break; + } + } + + if (options.IsSet(EMGO_FromString)) { + // Generate string -> value lookup table + char mapName[1024]; + // TODO mangle to prevent name conflicts of enum in different namespaces + snprintf(mapName, sizeof(mapName), "gCG_%s_Str2Val", decl.name.c_str()); + + CodegenOutputThing lookupTable; + { + auto& o = lookupTable.text; + // TODO use correct underlying type + APPEND_FMT_LN(o, "constinit frozen::unordered_map<frozen::string, uint64_t, %" PRId64 "> %s = {", filteredElements.size(), mapName); + for (auto& elm : filteredElements) { + APPEND_FMT_LN(o, "{\"%s\", %" PRId64 "},", elm.name.c_str(), elm.value); + } + APPEND_LIT_LN(o, "};"); + } + + // Generate lookup function + CodegenOutputThing lookupFunctionDef; + { + auto& o = lookupFunctionDef.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", enumName, enumName); + APPEND_FMT_LN(o, " auto iter = %s.find(value);", mapName); + APPEND_FMT_LN(o, " if (iter != %s.end()) {", mapName); + APPEND_FMT_LN(o, " return (%s)iter->second;", enumName); + APPEND_LIT_LN(o, " } else {"); + APPEND_LIT_LN(o, " return {};"); + APPEND_LIT_LN(o, " }"); + APPEND_LIT_LN(o, "}"); + } + + sourceOut.AddOutputThing(std::move(lookupTable)); + sourceOut.AddOutputThing(std::move(lookupFunctionDef)); + } +} + +void HandleInputFile(AppState& state, std::string_view filenameStem, std::string_view source) { + auto tokens = RecordTokens(source); + size_t idx = 0; + +#if CODEGEN_DEBUG_PRINT + printf("BEGIN tokens\n"); + for (auto& token : tokens) { + printf(" token %-32s '%s'\n", STR_LUT_LOOKUP(ClexNames, token.type), token.text.c_str()); + } + printf("END tokens\n"); +#endif + + CodegenInput cgInput; + CodegenOutput cgHeaderOutput; + Utils::ProduceGeneratedHeaderFileHeader(cgHeaderOutput); + CodegenOutput cgSourceOutput; + Utils::ProduceGeneratedSourceFileHeader(cgSourceOutput); + + int currentBraceDepth = 0; + // The current effective namespace, see example + DeclNamespace* currentNamespace = nullptr; + + struct NamespaceStackframe { + // The current namespace that owns the brace level, see example + DeclNamespace* ns = nullptr; + // Brace depth `ns` was created at (e.g. [std::details].depth == 0) + int depth = 0; + }; + std::vector<NamespaceStackframe> nsStack; + + // Example: + // namespace std::details { + // /* [stack top].ns = std::details */ + // /* [stack top].depth = std */ + // } + // namespace foo { + // /* [stack top].ns = foo */ + // /* [stack top].depth = foo */ + // namespace details { + // /* [stack top].ns = foo::details */ + // /* [stack top].depth = foo::details */ + // } + // } + + while (idx < tokens.size()) { + auto& token = tokens[idx]; + + bool incrementTokenIdx = true; + + switch (token.type) { + case CLEX_id: { + CppKeyword keyword; + { + auto& map = BSTR_LUT_S2V(CppKeyword); + auto iter = map.find(token.text); + if (iter != map.end()) { + keyword = iter->second; + } else { + keyword = CKw_COUNT; // Skip keyword section + } + } + switch (keyword) { + case CKw_Namespace: { + ++idx; + incrementTokenIdx = false; + + while (true) { + if (tokens[idx].type != CLEX_id) { + // TODO better error recovery + printf("[ERROR] invalid syntax for namespace\n"); + break; + } + + currentNamespace = cgInput.AddNamespace(DeclNamespace{ + .container = currentNamespace, + .name = tokens[idx].text, + }); + + if (tokens[idx + 1].text[0] == ':' && + tokens[idx + 2].text[0] == ':') + { + // Skip the two ':' tokens, try parse the next identifier + idx += 3; + } else { + break; + } + } + + nsStack.push_back(NamespaceStackframe{ + .ns = currentNamespace, + .depth = currentBraceDepth, + }); + + goto endIdenCase; + } + + case CKw_Struct: + case CKw_Class: { + auto& idenTok = tokens[idx + 1]; // TODO handle end of list + DEBUG_PRINTF("[DEBUG] found struct named %s\n", idenTok.text.c_str()); + goto endIdenCase; + } + + case CKw_Enum: { + // Consume the "enum" keyword + ++idx; + incrementTokenIdx = false; + + DeclEnum enumDecl; + enumDecl.container = currentNamespace; + enumDecl.underlyingType = EUT_Int32; // TODO + + if (tokens[idx].text == "class") { + // Consume the "class" keyword + ++idx; + DEBUG_PRINTF("[DEBUG] found enum class named %s\n", tokens[idx].text.c_str()); + } else { + DEBUG_PRINTF("[DEBUG] found enum named %s\n", tokens[idx].text.c_str()); + } + + // Consume the enum name identifier + enumDecl.name = tokens[idx].text; + ++idx; + + int enumClosingBraceCount = 0; + int enumBraceDepth = 0; + while (enumClosingBraceCount == 0 && idx < tokens.size()) { + auto& token = tokens[idx]; + switch (token.type) { + case CLEX_id: { + auto& vec = enumDecl.elements; + // Set to the previous enum element's value + 1, or starting from 0 if this is the first + // Also overridden in the CLEX_intlit branch + auto value = vec.empty() ? 0 : vec.back().value + 1; + vec.push_back(DeclEnumElement{ + .name = token.text, + .value = value, + }); + } break; + + case CLEX_intlit: { + + } break; + + case CLEX_ext_single_char: { + switch (token.text[0]) { + case '{': { + ++enumBraceDepth; + } break; + + case '}': { + --enumBraceDepth; + ++enumClosingBraceCount; + } break; + } + } break; + } + + ++idx; + } + + auto fullname = Utils::MakeFullName(enumDecl.name, currentNamespace); + cgInput.AddEnum(std::move(fullname), std::move(enumDecl)); + goto endIdenCase; + } + + case CKw_COUNT: break; + } + + CodegenDirective directive; + { + auto& map = BSTR_LUT_S2V(CodegenDirective); + auto iter = map.find(token.text); + if (iter != map.end()) { + directive = iter->second; + } else { + directive = CD_COUNT; // Skip directive section + } + } + switch (directive) { + case CD_ClassInfo: { + // TODO + goto endIdenCase; + } + + case CD_EnumInfo: { + // Consume the directive + ++idx; + incrementTokenIdx = false; + + auto& optionsStrMap = BSTR_LUT_S2V(EnumMetaGenOptions); + auto [argList, newIdx] = PeekDirectiveArgumentList(tokens, idx); + if (argList.size() < 1) { + printf("[ERROR] invalid syntax for BRUSSEL_ENUM\n"); + break; // TODO handle this error case gracefully (advance to semicolon?) + } + + auto& enumName = argList[0][0]->text; + auto enumDecl = cgInput.FindEnumByName(Utils::MakeFullName(enumName, currentNamespace)); + if (!enumDecl) { + printf("[ERROR] BRUSSEL_ENUM: referring to non-existent enum '%s'\n", enumName.c_str()); + break; + } + + auto& directiveOptions = argList[1]; + EnumFlags<EnumMetaGenOptions> options; + for (auto optionTok : directiveOptions) { + auto iter = optionsStrMap.find(optionTok->text); + if (iter != optionsStrMap.end()) { + options |= iter->second; + } else { + printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionTok->text.c_str()); + } + } + + GenerateForEnum(cgHeaderOutput, cgSourceOutput, *enumDecl, options); + + idx = newIdx; + incrementTokenIdx = false; + goto endIdenCase; + } + + case CD_COUNT: break; + } + + endIdenCase: + break; + } + + case CLEX_ext_single_char: + switch (token.text[0]) { + case '{': { + currentBraceDepth++; + CheckBraceDepth(currentBraceDepth); + } break; + + case '}': { + currentBraceDepth--; + CheckBraceDepth(currentBraceDepth); + + if (!nsStack.empty()) { + auto& ns = nsStack.back(); + if (ns.depth == currentBraceDepth) { + nsStack.pop_back(); + + if (!nsStack.empty()) { + currentNamespace = nsStack.back().ns; + } else { + currentNamespace = nullptr; + } + } + } + } break; + } + break; + } + + if (incrementTokenIdx) { + ++idx; + } + } + + if (currentBraceDepth != 0) { + printf("[WARNING] unbalanced brace at end of file."); + } + + Utils::WriteOutputFile(cgHeaderOutput, state.outputDir, filenameStem, ".gh.inl"sv); + Utils::WriteOutputFile(cgSourceOutput, state.outputDir, filenameStem, ".gs.inl"sv); + + // TODO see CMakeLists.txt for rationale, clean this up to be a proper citizen + Utils::WriteOutputFile(CodegenOutput{}, state.outputDir, filenameStem, ".gs.cpp"sv); +} + +enum InputOpcode { + IOP_ProcessSingleFile, + IOP_ProcessRecursively, + IOP_COUNT, +}; + +void HandleArgument(AppState& state, InputOpcode opcode, std::string_view operand) { + switch (opcode) { + case IOP_ProcessSingleFile: { + DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand)); + + fs::path path(operand); + auto filenameStem = path.stem().string(); + auto source = Utils::ReadFileAsString(path); + HandleInputFile(state, filenameStem, source); + } break; + + case IOP_ProcessRecursively: { + DEBUG_PRINTF("Recursively processing folder %.*s\n", PRINTF_STRING_VIEW(operand)); + + fs::path startPath(operand); + for (auto& item : fs::recursive_directory_iterator(startPath)) { + if (!item.is_regular_file()) { + continue; + } + + auto& path = item.path(); + auto pathExt = path.extension(); + auto pathStem = path.stem(); + if (pathExt != ".h" && + pathExt != ".hpp") + { + continue; + } + + DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str()); + + auto filenameStem = pathStem.string(); + auto source = Utils::ReadFileAsString(path); + HandleInputFile(state, filenameStem, source); + } + } break; + + case IOP_COUNT: break; + } +} + +InputOpcode ParseInputOpcode(std::string_view text) { + if (text == "single"sv) { + return IOP_ProcessSingleFile; + } else if (text == "rec"sv) { + return IOP_ProcessRecursively; + } else { + DEBUG_PRINTF("Unknown input opcode %s\n", text.data()); + throw std::runtime_error("Unknown input opcode"); + } +} + +int main(int argc, char* argv[]) { + STR_LUT_INIT(ClexNames); + BSTR_LUT_INIT(CppKeyword); + BSTR_LUT_INIT(CodegenDirective); + BSTR_LUT_INIT(StructMetaGenOptions); + BSTR_LUT_INIT(EnumMetaGenOptions); + + // TODO better arg parser + // option 1: use cxxopts and positional arguments + // option 2: take one argument only, being a json objecet + + AppState state; + + Utils::ProduceGeneratedHeaderFileHeader(state.mainHeaderOutput); + Utils::ProduceGeneratedSourceFileHeader(state.mainSourceOutput); + + // If no cli is provided (argv[0] conventionally but not mandatorily the cli), this will do thing + // Otherwise, start with the 2nd element in the array, which is the 1st actual argument + if (argc < 2) { + // NOTE: keep in sync with various enum options and parser code + printf(&R"""( +USAGE: codegen.exe <output path> [<opcode>:<input path>]... +where <output path>: the directory to write generated contents to. This will NOT automatically create the directory. + <opcode> is one of: + "single" process this <input path> file only + "rec" starting at the given directory <input path>, recursively process all .h .hpp files +)"""[1]); + return -1; + } + + state.outputDir = std::string_view(argv[1]); + DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(state.outputDir)); + + for (int i = 2; i < argc; ++i) { + const char* argRaw = argv[i]; + std::string_view arg(argRaw); + DEBUG_PRINTF("Processing input command %s\n", argRaw); + + auto separatorLoc = arg.find(':'); + if (separatorLoc != std::string_view::npos) { + auto opcodeString = arg.substr(0, separatorLoc); + auto opcode = ParseInputOpcode(opcodeString); + auto operand = arg.substr(separatorLoc + 1); + + HandleArgument(state, opcode, operand); + } + } + + // TODO do we even need these? + // Utils::WriteOutputFile(state.mainHeaderOutput, state.outputDir, "GeneratedCode.hpp"sv); + // Utils::WriteOutputFile(state.mainSourceOutput, state.outputDir, "GeneratedCode.cpp"sv); + + return 0; +} diff --git a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt new file mode 100644 index 0000000..441d97c --- /dev/null +++ b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt @@ -0,0 +1,43 @@ +enum MyEnum { + EnumElement1, + EnumElement2, + EnumElement3, +}; +BRUSSEL_ENUM(MyEnum, ToString FromString); + +enum CountedEnumAll { + CEA_Foo, + CEA_Bar, + CEA_COUNT, +}; +BRUSSEL_ENUM(CountedEnumAll, ToString FromString); + +enum CountedEnum { + CE_Foo, + CE_Bar, + CE_FooBar, + CE_COUNT, +}; +BRUSSEL_ENUM(CountedEnum, ToString FromString ExcludeHeuristics); + +namespace MyNamespace { +enum MyNamespacedEnum { + MNE_Foo, + MNE_Bar, +}; +BRUSSEL_ENUM(MyNamespacedEnum, ToString FromString ExcludeHeuristics); + +namespace details { + enum MyNamespacedEnum { + MNE_Foo, + MNE_Bar, + }; + BRUSSEL_ENUM(MyNamespacedEnum, ToString FromString ExcludeHeuristics); +} +} + +namespace foo::details { +enum Enum { +}; +BRUSSEL_ENUM(Enum, ToString FromString ExcludeHeuristics); +} diff --git a/source/20-codegen-runtime/MacrosCodegen.hpp b/source/20-codegen-runtime/MacrosCodegen.hpp new file mode 100644 index 0000000..6803023 --- /dev/null +++ b/source/20-codegen-runtime/MacrosCodegen.hpp @@ -0,0 +1,7 @@ +// NOTE: contents of this file is coupled with buildtools/codegen/ +// when updating, change both sides at the same time + +#pragma once + +#define BRUSSEL_CLASS(name, options) +#define BRUSSEL_ENUM(name, options) diff --git a/source/20-codegen-runtime/Metadata.cpp b/source/20-codegen-runtime/Metadata.cpp new file mode 100644 index 0000000..ee32054 --- /dev/null +++ b/source/20-codegen-runtime/Metadata.cpp @@ -0,0 +1 @@ +#include "Metadata.hpp" diff --git a/source/20-codegen-runtime/Metadata.hpp b/source/20-codegen-runtime/Metadata.hpp new file mode 100644 index 0000000..a038c15 --- /dev/null +++ b/source/20-codegen-runtime/Metadata.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "MacrosCodegen.hpp" +#include "MetadataBase.hpp" diff --git a/source/20-codegen-runtime/MetadataBase.cpp b/source/20-codegen-runtime/MetadataBase.cpp new file mode 100644 index 0000000..3ccf870 --- /dev/null +++ b/source/20-codegen-runtime/MetadataBase.cpp @@ -0,0 +1 @@ +#include "MetadataBase.hpp" diff --git a/source/20-codegen-runtime/MetadataBase.hpp b/source/20-codegen-runtime/MetadataBase.hpp new file mode 100644 index 0000000..8be668d --- /dev/null +++ b/source/20-codegen-runtime/MetadataBase.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <optional> +#include <string_view> + +namespace Metadata { + +template <class TEnum> +std::string_view EnumToString(TEnum value); + +template <class TEnum> +std::optional<TEnum> EnumFromString(std::string_view str); + +} // namespace Metadata diff --git a/source/App.cpp b/source/30-game/App.cpp index 45a7545..45a7545 100644 --- a/source/App.cpp +++ b/source/30-game/App.cpp diff --git a/source/App.hpp b/source/30-game/App.hpp index c73c5a1..c73c5a1 100644 --- a/source/App.hpp +++ b/source/30-game/App.hpp diff --git a/source/AppConfig.hpp b/source/30-game/AppConfig.hpp index 794bee5..794bee5 100644 --- a/source/AppConfig.hpp +++ b/source/30-game/AppConfig.hpp diff --git a/source/Camera.cpp b/source/30-game/Camera.cpp index 39f0369..39f0369 100644 --- a/source/Camera.cpp +++ b/source/30-game/Camera.cpp diff --git a/source/Camera.hpp b/source/30-game/Camera.hpp index 7bf0a6c..7bf0a6c 100644 --- a/source/Camera.hpp +++ b/source/30-game/Camera.hpp diff --git a/source/CommonVertexIndex.cpp b/source/30-game/CommonVertexIndex.cpp index 786274e..786274e 100644 --- a/source/CommonVertexIndex.cpp +++ b/source/30-game/CommonVertexIndex.cpp diff --git a/source/CommonVertexIndex.hpp b/source/30-game/CommonVertexIndex.hpp index adb81b6..adb81b6 100644 --- a/source/CommonVertexIndex.hpp +++ b/source/30-game/CommonVertexIndex.hpp diff --git a/source/EditorAccessories.cpp b/source/30-game/EditorAccessories.cpp index 08d08ec..08d08ec 100644 --- a/source/EditorAccessories.cpp +++ b/source/30-game/EditorAccessories.cpp diff --git a/source/EditorAccessories.hpp b/source/30-game/EditorAccessories.hpp index 687b509..687b509 100644 --- a/source/EditorAccessories.hpp +++ b/source/30-game/EditorAccessories.hpp diff --git a/source/EditorAttachment.hpp b/source/30-game/EditorAttachment.hpp index 61b824b..61b824b 100644 --- a/source/EditorAttachment.hpp +++ b/source/30-game/EditorAttachment.hpp diff --git a/source/EditorAttachmentImpl.cpp b/source/30-game/EditorAttachmentImpl.cpp index 62d15eb..62d15eb 100644 --- a/source/EditorAttachmentImpl.cpp +++ b/source/30-game/EditorAttachmentImpl.cpp diff --git a/source/EditorAttachmentImpl.hpp b/source/30-game/EditorAttachmentImpl.hpp index 53bcd37..53bcd37 100644 --- a/source/EditorAttachmentImpl.hpp +++ b/source/30-game/EditorAttachmentImpl.hpp diff --git a/source/EditorCommandPalette.cpp b/source/30-game/EditorCommandPalette.cpp index 0e7b894..0e7b894 100644 --- a/source/EditorCommandPalette.cpp +++ b/source/30-game/EditorCommandPalette.cpp diff --git a/source/EditorCommandPalette.hpp b/source/30-game/EditorCommandPalette.hpp index 101344d..101344d 100644 --- a/source/EditorCommandPalette.hpp +++ b/source/30-game/EditorCommandPalette.hpp diff --git a/source/EditorCore.hpp b/source/30-game/EditorCore.hpp index 726f43e..726f43e 100644 --- a/source/EditorCore.hpp +++ b/source/30-game/EditorCore.hpp diff --git a/source/EditorCorePrivate.cpp b/source/30-game/EditorCorePrivate.cpp index 9fd6087..9fd6087 100644 --- a/source/EditorCorePrivate.cpp +++ b/source/30-game/EditorCorePrivate.cpp diff --git a/source/EditorCorePrivate.hpp b/source/30-game/EditorCorePrivate.hpp index 4fbfb72..4fbfb72 100644 --- a/source/EditorCorePrivate.hpp +++ b/source/30-game/EditorCorePrivate.hpp diff --git a/source/EditorGuizmo.cpp b/source/30-game/EditorGuizmo.cpp index 3e4f890..3e4f890 100644 --- a/source/EditorGuizmo.cpp +++ b/source/30-game/EditorGuizmo.cpp diff --git a/source/EditorGuizmo.hpp b/source/30-game/EditorGuizmo.hpp index 0560050..0560050 100644 --- a/source/EditorGuizmo.hpp +++ b/source/30-game/EditorGuizmo.hpp diff --git a/source/EditorNotification.cpp b/source/30-game/EditorNotification.cpp index e4a869e..e4a869e 100644 --- a/source/EditorNotification.cpp +++ b/source/30-game/EditorNotification.cpp diff --git a/source/EditorNotification.hpp b/source/30-game/EditorNotification.hpp index 3af8c2d..3af8c2d 100644 --- a/source/EditorNotification.hpp +++ b/source/30-game/EditorNotification.hpp diff --git a/source/EditorUtils.cpp b/source/30-game/EditorUtils.cpp index 20caef7..20caef7 100644 --- a/source/EditorUtils.cpp +++ b/source/30-game/EditorUtils.cpp diff --git a/source/EditorUtils.hpp b/source/30-game/EditorUtils.hpp index 99c522b..99c522b 100644 --- a/source/EditorUtils.hpp +++ b/source/30-game/EditorUtils.hpp diff --git a/source/FuzzyMatch.cpp b/source/30-game/FuzzyMatch.cpp index 0ab604d..0ab604d 100644 --- a/source/FuzzyMatch.cpp +++ b/source/30-game/FuzzyMatch.cpp diff --git a/source/FuzzyMatch.hpp b/source/30-game/FuzzyMatch.hpp index 7a26b7e..7a26b7e 100644 --- a/source/FuzzyMatch.hpp +++ b/source/30-game/FuzzyMatch.hpp diff --git a/source/GameObject.cpp b/source/30-game/GameObject.cpp index 8bb3ec7..8bb3ec7 100644 --- a/source/GameObject.cpp +++ b/source/30-game/GameObject.cpp diff --git a/source/GameObject.hpp b/source/30-game/GameObject.hpp index 77488b9..77488b9 100644 --- a/source/GameObject.hpp +++ b/source/30-game/GameObject.hpp diff --git a/source/GraphicsTags.cpp b/source/30-game/GraphicsTags.cpp index 522a58f..522a58f 100644 --- a/source/GraphicsTags.cpp +++ b/source/30-game/GraphicsTags.cpp diff --git a/source/GraphicsTags.hpp b/source/30-game/GraphicsTags.hpp index f83b99c..f83b99c 100644 --- a/source/GraphicsTags.hpp +++ b/source/30-game/GraphicsTags.hpp diff --git a/source/Image.cpp b/source/30-game/Image.cpp index 3673acc..3673acc 100644 --- a/source/Image.cpp +++ b/source/30-game/Image.cpp diff --git a/source/Image.hpp b/source/30-game/Image.hpp index c577c24..c577c24 100644 --- a/source/Image.hpp +++ b/source/30-game/Image.hpp diff --git a/source/Ires.cpp b/source/30-game/Ires.cpp index 10a6867..10a6867 100644 --- a/source/Ires.cpp +++ b/source/30-game/Ires.cpp diff --git a/source/Ires.hpp b/source/30-game/Ires.hpp index 83ca175..83ca175 100644 --- a/source/Ires.hpp +++ b/source/30-game/Ires.hpp diff --git a/source/Level.cpp b/source/30-game/Level.cpp index 5881084..5881084 100644 --- a/source/Level.cpp +++ b/source/30-game/Level.cpp diff --git a/source/Level.hpp b/source/30-game/Level.hpp index c1170a3..c1170a3 100644 --- a/source/Level.hpp +++ b/source/30-game/Level.hpp diff --git a/source/Material.cpp b/source/30-game/Material.cpp index e648970..e648970 100644 --- a/source/Material.cpp +++ b/source/30-game/Material.cpp diff --git a/source/Material.hpp b/source/30-game/Material.hpp index f1cd7dd..f1cd7dd 100644 --- a/source/Material.hpp +++ b/source/30-game/Material.hpp diff --git a/source/Mesh.cpp b/source/30-game/Mesh.cpp index 244e2e3..244e2e3 100644 --- a/source/Mesh.cpp +++ b/source/30-game/Mesh.cpp diff --git a/source/Mesh.hpp b/source/30-game/Mesh.hpp index f86fd55..f86fd55 100644 --- a/source/Mesh.hpp +++ b/source/30-game/Mesh.hpp diff --git a/source/Player.cpp b/source/30-game/Player.cpp index 34c4549..34c4549 100644 --- a/source/Player.cpp +++ b/source/30-game/Player.cpp diff --git a/source/Player.hpp b/source/30-game/Player.hpp index d003a25..d003a25 100644 --- a/source/Player.hpp +++ b/source/30-game/Player.hpp diff --git a/source/Renderer.cpp b/source/30-game/Renderer.cpp index 3497449..3497449 100644 --- a/source/Renderer.cpp +++ b/source/30-game/Renderer.cpp diff --git a/source/Renderer.hpp b/source/30-game/Renderer.hpp index 98a9f28..98a9f28 100644 --- a/source/Renderer.hpp +++ b/source/30-game/Renderer.hpp diff --git a/source/SceneThings.cpp b/source/30-game/SceneThings.cpp index 3fa0436..3fa0436 100644 --- a/source/SceneThings.cpp +++ b/source/30-game/SceneThings.cpp diff --git a/source/SceneThings.hpp b/source/30-game/SceneThings.hpp index c261fbb..c261fbb 100644 --- a/source/SceneThings.hpp +++ b/source/30-game/SceneThings.hpp diff --git a/source/Shader.cpp b/source/30-game/Shader.cpp index 48881f0..48881f0 100644 --- a/source/Shader.cpp +++ b/source/30-game/Shader.cpp diff --git a/source/Shader.hpp b/source/30-game/Shader.hpp index 707e6cc..707e6cc 100644 --- a/source/Shader.hpp +++ b/source/30-game/Shader.hpp diff --git a/source/Sprite.cpp b/source/30-game/Sprite.cpp index 2b4923c..2b4923c 100644 --- a/source/Sprite.cpp +++ b/source/30-game/Sprite.cpp diff --git a/source/Sprite.hpp b/source/30-game/Sprite.hpp index e163a01..e163a01 100644 --- a/source/Sprite.hpp +++ b/source/30-game/Sprite.hpp diff --git a/source/Texture.cpp b/source/30-game/Texture.cpp index 6fa7c8a..6fa7c8a 100644 --- a/source/Texture.cpp +++ b/source/30-game/Texture.cpp diff --git a/source/Texture.hpp b/source/30-game/Texture.hpp index 108dfa7..108dfa7 100644 --- a/source/Texture.hpp +++ b/source/30-game/Texture.hpp diff --git a/source/VertexIndex.cpp b/source/30-game/VertexIndex.cpp index ac68289..ac68289 100644 --- a/source/VertexIndex.cpp +++ b/source/30-game/VertexIndex.cpp diff --git a/source/VertexIndex.hpp b/source/30-game/VertexIndex.hpp index 2d65617..2d65617 100644 --- a/source/VertexIndex.hpp +++ b/source/30-game/VertexIndex.hpp diff --git a/source/World.cpp b/source/30-game/World.cpp index d4a8344..d4a8344 100644 --- a/source/World.cpp +++ b/source/30-game/World.cpp diff --git a/source/World.hpp b/source/30-game/World.hpp index 288142e..288142e 100644 --- a/source/World.hpp +++ b/source/30-game/World.hpp diff --git a/source/main.cpp b/source/30-game/main.cpp index c49fc0b..c49fc0b 100644 --- a/source/main.cpp +++ b/source/30-game/main.cpp |