From 8fc3192da5ae3ac24511ad32088d669c799b6ddb Mon Sep 17 00:00:00 2001 From: rtk0c Date: Wed, 25 May 2022 22:30:59 -0700 Subject: Changeset: 39 Move more things to source-common/ --- source-common/Enum.hpp | 103 ++++ source-common/PodVector.hpp | 297 +++++++++ source-common/RcPtr.hpp | 120 ++++ source-common/Rect.hpp | 164 +++++ source-common/SmallVector.cpp | 145 +++++ source-common/SmallVector.hpp | 1332 +++++++++++++++++++++++++++++++++++++++++ source-common/TypeTraits.hpp | 19 + source-common/Uid.cpp | 58 ++ source-common/Uid.hpp | 42 ++ source/CMakeLists.txt | 2 - source/Enum.hpp | 103 ---- source/PodVector.hpp | 297 --------- source/RcPtr.hpp | 120 ---- source/Rect.hpp | 164 ----- source/SmallVector.cpp | 145 ----- source/SmallVector.hpp | 1332 ----------------------------------------- source/TypeTraits.hpp | 19 - source/Uid.cpp | 58 -- source/Uid.hpp | 42 -- 19 files changed, 2280 insertions(+), 2282 deletions(-) create mode 100644 source-common/Enum.hpp create mode 100644 source-common/PodVector.hpp create mode 100644 source-common/RcPtr.hpp create mode 100644 source-common/Rect.hpp create mode 100644 source-common/SmallVector.cpp create mode 100644 source-common/SmallVector.hpp create mode 100644 source-common/TypeTraits.hpp create mode 100644 source-common/Uid.cpp create mode 100644 source-common/Uid.hpp delete mode 100644 source/Enum.hpp delete mode 100644 source/PodVector.hpp delete mode 100644 source/RcPtr.hpp delete mode 100644 source/Rect.hpp delete mode 100644 source/SmallVector.cpp delete mode 100644 source/SmallVector.hpp delete mode 100644 source/TypeTraits.hpp delete mode 100644 source/Uid.cpp delete mode 100644 source/Uid.hpp diff --git a/source-common/Enum.hpp b/source-common/Enum.hpp new file mode 100644 index 0000000..5e106fe --- /dev/null +++ b/source-common/Enum.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +template +class EnumFlags { +public: + using Enum = TEnum; + using Underlying = std::underlying_type_t; + +private: + Underlying mValue; + +public: + EnumFlags() + : mValue{ 0 } { + } + + EnumFlags(TEnum e) + : mValue{ 1 << static_cast(e) } { + } + + bool IsSet(EnumFlags mask) const { + return (mValue & mask.mValue) == mask.mValue; + } + + bool IsSet(std::initializer_list enums) { + EnumFlags flags; + for (auto& e : enums) { + flags.mValue |= static_cast(e); + } + return IsSet(flags); + } + + bool IsSetExclusive(EnumFlags mask) const { + return mValue == mask.mValue; + } + + bool IsSetExclusive(std::initializer_list enums) { + EnumFlags flags; + for (auto& e : enums) { + flags.mValue |= static_cast(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) const { + mValue |= that.mValue; + return *this; + } + + EnumFlags& operator&=(EnumFlags that) const { + mValue &= that.mValue; + return *this; + } + + EnumFlags& operator^=(EnumFlags that) const { + mValue ^= that.mValue; + return *this; + } + + EnumFlags& operator|=(TEnum e) const { + mValue |= 1 << static_cast(e); + return *this; + } + + EnumFlags& operator&=(TEnum e) const { + mValue &= 1 << static_cast(e); + return *this; + } + + EnumFlags& operator^=(TEnum e) const { + mValue ^= 1 << static_cast(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(e)); } + EnumFlags operator&(TEnum e) const { return EnumFlags(mValue & 1 << static_cast(e)); } + EnumFlags operator^(TEnum e) const { return EnumFlags(mValue ^ 1 << static_cast(e)); } + + EnumFlags operator~() const { return EnumFlags(~mValue); } +}; diff --git a/source-common/PodVector.hpp b/source-common/PodVector.hpp new file mode 100644 index 0000000..74e99d6 --- /dev/null +++ b/source-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 +#include +#include +#include +#include +#include + +template +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& src) { + mSize = mCapacity = 0; + mData = nullptr; + operator=(src); + } + + PodVector& operator=(const PodVector& 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& 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 as_span() { return { mData, (size_t)mSize }; } + std::span as_data_span() { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } + std::span as_span() const { return { mData, (size_t)mSize }; } + std::span as_data_span() const { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } +}; diff --git a/source-common/RcPtr.hpp b/source-common/RcPtr.hpp new file mode 100644 index 0000000..130b2b2 --- /dev/null +++ b/source-common/RcPtr.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include "Macros.hpp" +#include "TypeTraits.hpp" + +#include +#include +#include +#include + +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 RcPtr : TDeleter { +private: + static_assert(std::is_base_of_v); + 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 + requires std::is_base_of_v + operator RcPtr() const { + return RcPtr(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 + bool operator==(const RcPtr& 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-common/Rect.hpp b/source-common/Rect.hpp new file mode 100644 index 0000000..89d9b01 --- /dev/null +++ b/source-common/Rect.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include + +/// 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 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 + // 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&, const Rect&) = 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 + Rect Cast() const { + return { + static_cast(x), + static_cast(y), + static_cast(width), + static_cast(height), + }; + } +}; diff --git a/source-common/SmallVector.cpp b/source-common/SmallVector.cpp new file mode 100644 index 0000000..c38e8a7 --- /dev/null +++ b/source-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 +#include +#include + +// 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) == + sizeof(unsigned) * 2 + sizeof(void*), + "wasted space in SmallVector size 0"); +static_assert(alignof(SmallVector) >= alignof(Struct16B), + "wrong alignment for 16-byte aligned T"); +static_assert(alignof(SmallVector) >= alignof(Struct32B), + "wrong alignment for 32-byte aligned T"); +static_assert(sizeof(SmallVector) >= alignof(Struct16B), + "missing padding for 16-byte aligned T"); +static_assert(sizeof(SmallVector) >= alignof(Struct32B), + "missing padding for 32-byte aligned T"); +static_assert(sizeof(SmallVector) == + sizeof(unsigned) * 2 + sizeof(void*) * 2, + "wasted space in SmallVector size 1"); + +static_assert(sizeof(SmallVector) == + 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 +static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { + constexpr size_t MaxSize = std::numeric_limits::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 +void* SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity) { + NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); + return malloc(NewCapacity * TSize); +} + +// Note: Moving this function into the header may cause performance regression. +template +void SmallVectorBase::grow_pod(void* FirstEl, size_t MinSize, size_t TSize) { + size_t NewCapacity = getNewCapacity(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; + +// 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; + +// Assertions to ensure this #if stays in sync with SmallVectorSizeType. +static_assert(sizeof(SmallVectorSizeType) == sizeof(uint64_t), + "Expected SmallVectorBase variant to be in use."); +#else +static_assert(sizeof(SmallVectorSizeType) == sizeof(uint32_t), + "Expected SmallVectorBase variant to be in use."); +#endif diff --git a/source-common/SmallVector.hpp b/source-common/SmallVector.hpp new file mode 100644 index 0000000..e33a25d --- /dev/null +++ b/source-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +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, where a +/// 32 bit size would limit the vector to ~4GB. SmallVectors are used for +/// buffering bitcode output - which can exceed 4GB. +template +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::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 +using SmallVectorSizeType = + typename std::conditional= 8, uint64_t, uint32_t>::type; + +/// Figure out the offset of the first element. +template +struct SmallVectorAlignmentAndSize { + alignas(SmallVectorBase>) char Base[sizeof( + SmallVectorBase>)]; + 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 +class SmallVectorTemplateCommon + : public SmallVectorBase> { + using Base = SmallVectorBase>; + + /// 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(reinterpret_cast( + reinterpret_cast(this) + + offsetof(SmallVectorAlignmentAndSize, 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, 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, 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 + 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; + using reverse_iterator = std::reverse_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 - 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, which is not +/// trivially assignable. +template ::value) && (std::is_trivially_move_constructible::value) && std::is_trivially_destructible::value> +class SmallVectorTemplateBase : public SmallVectorTemplateCommon { + friend class SmallVectorTemplateCommon; + +protected: + static constexpr bool TakesParamByValue = false; + using ValueParamT = const T&; + + SmallVectorTemplateBase(size_t Size) + : SmallVectorTemplateCommon(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 + 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 + 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( + SmallVectorBase>::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( + 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 + 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(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 +void SmallVectorTemplateBase::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 +void SmallVectorTemplateBase::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 +void SmallVectorTemplateBase::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 - 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 +class SmallVectorTemplateBase : public SmallVectorTemplateCommon { + friend class SmallVectorTemplateCommon; + +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::type; + + SmallVectorTemplateBase(size_t Size) + : SmallVectorTemplateCommon(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 + 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 + 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 + static void uninitialized_copy( + T1* I, T1* E, T2* Dest, std::enable_if_t::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(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( + 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 + 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(Args)...)); + return this->back(); + } + +public: + void push_back(ValueParamT Elt) { + const T* EltPtr = reserveForParamAndGetAddress(Elt); + memcpy(reinterpret_cast(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 +class SmallVectorImpl : public SmallVectorTemplateBase { + using SuperClass = SmallVectorTemplateBase; + +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::TakesParamByValue; + using ValueParamT = typename SuperClass::ValueParamT; + + // Default ctor - Initialize to empty. + explicit SmallVectorImpl(unsigned N) + : SmallVectorTemplateBase(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 + 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(N); } + + /// Like resize, but \ref T is POD, the new values won't be initialized. + void resize_for_overwrite(size_type N) { resizeImpl(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 ::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 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 ::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 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(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(CS); + iterator E = const_cast(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 + iterator insert_one_impl(iterator I, ArgType&& Elt) { + // Callers ensure that ArgType is derived from T. + static_assert( + std::is_same>, + T>::value, + "ArgType must be derived from T!"); + + if (I == this->end()) { // Important special case for empty vector. + this->push_back(::std::forward(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* 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::value, + "ArgType must be 'T' when taking by value!"); + if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end())) + ++EltPtr; + + *I = ::std::forward(*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(this->end() - NumToInsert), + std::move_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 ::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(this->end() - NumToInsert), + std::move_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 IL) { + insert(I, IL.begin(), IL.end()); + } + + template + reference emplace_back(ArgTypes&&... Args) { + if (LLVM_UNLIKELY(this->size() >= this->capacity())) + return this->growAndEmplaceBack(std::forward(Args)...); + + ::new ((void*)this->end()) T(std::forward(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 +void SmallVectorImpl::swap(SmallVectorImpl& 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 +SmallVectorImpl& SmallVectorImpl:: +operator=(const SmallVectorImpl& 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 +SmallVectorImpl& SmallVectorImpl::operator=(SmallVectorImpl&& 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 +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 +struct alignas(T) SmallVectorStorage {}; + +/// Forward declaration of SmallVector so that +/// calculateSmallVectorDefaultInlinedElements can reference +/// `sizeof(SmallVector)`. +template +class SmallVector; + +/// Helper class for calculating the default number of inline elements for +/// `SmallVector`. +/// +/// This should be migrated to a constexpr function when our minimum +/// compiler support is enough for multi-statement constexpr functions. +template +struct CalculateSmallVectorDefaultInlinedElements { + // Parameter controlling the default number of inlined elements + // for `SmallVector`. + // + // The default number of inlined elements ensures that + // 1. There is at least one inlined element. + // 2. `sizeof(SmallVector) <= 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>` 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` but `sizeof(T)` is really big! Please use an " + "explicit number of inlined elements with `SmallVector` 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); + 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 (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) around 64 bytes). +/// +/// \warning This does not attempt to be exception safe. +/// +/// \see https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h +template ::value> +class SmallVector : public SmallVectorImpl, + SmallVectorStorage { +public: + SmallVector() + : SmallVectorImpl(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(N) { + this->assign(Size, Value); + } + + template ::iterator_category, + std::input_iterator_tag>::value>> + SmallVector(ItTy S, ItTy E) + : SmallVectorImpl(N) { + this->append(S, E); + } + + template + explicit SmallVector(const iterator_range& R) + : SmallVectorImpl(N) { + this->append(R.begin(), R.end()); + } + + SmallVector(std::initializer_list IL) + : SmallVectorImpl(N) { + this->assign(IL); + } + + SmallVector(const SmallVector& RHS) + : SmallVectorImpl(N) { + if (!RHS.empty()) + SmallVectorImpl::operator=(RHS); + } + + SmallVector& operator=(const SmallVector& RHS) { + SmallVectorImpl::operator=(RHS); + return *this; + } + + SmallVector(SmallVector&& RHS) + : SmallVectorImpl(N) { + if (!RHS.empty()) + SmallVectorImpl::operator=(::std::move(RHS)); + } + + SmallVector(SmallVectorImpl&& RHS) + : SmallVectorImpl(N) { + if (!RHS.empty()) + SmallVectorImpl::operator=(::std::move(RHS)); + } + + SmallVector& operator=(SmallVector&& RHS) { + if (N) { + SmallVectorImpl::operator=(::std::move(RHS)); + return *this; + } + // SmallVectorImpl::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&& RHS) { + SmallVectorImpl::operator=(::std::move(RHS)); + return *this; + } + + SmallVector& operator=(std::initializer_list IL) { + this->assign(IL); + return *this; + } +}; + +template +inline size_t capacity_in_bytes(const SmallVector& X) { + return X.capacity_in_bytes(); +} + +template +using ValueTypeFromRangeType = + typename std::remove_const()))>::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 +SmallVector, Size> to_vector(R&& Range) { + return { std::begin(Range), std::end(Range) }; +} +template +SmallVector, + CalculateSmallVectorDefaultInlinedElements< + ValueTypeFromRangeType>::value> +to_vector(R&& Range) { + return { std::begin(Range), std::end(Range) }; +} + +namespace std { + +/// Implement std::swap in terms of SmallVector swap. +template +inline void swap(SmallVectorImpl& LHS, SmallVectorImpl& RHS) { + LHS.swap(RHS); +} + +/// Implement std::swap in terms of SmallVector swap. +template +inline void swap(SmallVector& LHS, SmallVector& RHS) { + LHS.swap(RHS); +} + +} // namespace std + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/source-common/TypeTraits.hpp b/source-common/TypeTraits.hpp new file mode 100644 index 0000000..cca9a1f --- /dev/null +++ b/source-common/TypeTraits.hpp @@ -0,0 +1,19 @@ +#pragma once + +template +struct DefaultDeleter { + void operator()(T* ptr) const { + delete ptr; + } +}; + +template +struct RemoveMemberPtrImpl {}; + +template +struct RemoveMemberPtrImpl { + using Type = U; +}; + +template +using RemoveMemberPtr = typename RemoveMemberPtrImpl::Type; diff --git a/source-common/Uid.cpp b/source-common/Uid.cpp new file mode 100644 index 0000000..1930cd8 --- /dev/null +++ b/source-common/Uid.cpp @@ -0,0 +1,58 @@ +#include "Uid.hpp" + +#include "RapidJsonHelper.hpp" + +#include +#include +#include + +Uid Uid::Create() { + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dist( + std::numeric_limits::min(), + std::numeric_limits::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-common/Uid.hpp b/source-common/Uid.hpp new file mode 100644 index 0000000..f58129c --- /dev/null +++ b/source-common/Uid.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Utils.hpp" + +#include +#include +#include +#include +#include + +#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 { + 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/CMakeLists.txt b/source/CMakeLists.txt index b5de147..abb724b 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -22,10 +22,8 @@ PRIVATE Renderer.cpp SceneThings.cpp Shader.cpp - SmallVector.cpp Sprite.cpp Texture.cpp - Uid.cpp VertexIndex.cpp World.cpp ) diff --git a/source/Enum.hpp b/source/Enum.hpp deleted file mode 100644 index 5e106fe..0000000 --- a/source/Enum.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include - -template -class EnumFlags { -public: - using Enum = TEnum; - using Underlying = std::underlying_type_t; - -private: - Underlying mValue; - -public: - EnumFlags() - : mValue{ 0 } { - } - - EnumFlags(TEnum e) - : mValue{ 1 << static_cast(e) } { - } - - bool IsSet(EnumFlags mask) const { - return (mValue & mask.mValue) == mask.mValue; - } - - bool IsSet(std::initializer_list enums) { - EnumFlags flags; - for (auto& e : enums) { - flags.mValue |= static_cast(e); - } - return IsSet(flags); - } - - bool IsSetExclusive(EnumFlags mask) const { - return mValue == mask.mValue; - } - - bool IsSetExclusive(std::initializer_list enums) { - EnumFlags flags; - for (auto& e : enums) { - flags.mValue |= static_cast(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) const { - mValue |= that.mValue; - return *this; - } - - EnumFlags& operator&=(EnumFlags that) const { - mValue &= that.mValue; - return *this; - } - - EnumFlags& operator^=(EnumFlags that) const { - mValue ^= that.mValue; - return *this; - } - - EnumFlags& operator|=(TEnum e) const { - mValue |= 1 << static_cast(e); - return *this; - } - - EnumFlags& operator&=(TEnum e) const { - mValue &= 1 << static_cast(e); - return *this; - } - - EnumFlags& operator^=(TEnum e) const { - mValue ^= 1 << static_cast(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(e)); } - EnumFlags operator&(TEnum e) const { return EnumFlags(mValue & 1 << static_cast(e)); } - EnumFlags operator^(TEnum e) const { return EnumFlags(mValue ^ 1 << static_cast(e)); } - - EnumFlags operator~() const { return EnumFlags(~mValue); } -}; diff --git a/source/PodVector.hpp b/source/PodVector.hpp deleted file mode 100644 index 74e99d6..0000000 --- a/source/PodVector.hpp +++ /dev/null @@ -1,297 +0,0 @@ -// File adapted from dear-imgui's ImVector, implemented in https://github.com/ocornut/imgUI/blob/master/imgui.h -#pragma once - -#include -#include -#include -#include -#include -#include - -template -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& src) { - mSize = mCapacity = 0; - mData = nullptr; - operator=(src); - } - - PodVector& operator=(const PodVector& 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& 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 as_span() { return { mData, (size_t)mSize }; } - std::span as_data_span() { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } - std::span as_span() const { return { mData, (size_t)mSize }; } - std::span as_data_span() const { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; } -}; diff --git a/source/RcPtr.hpp b/source/RcPtr.hpp deleted file mode 100644 index 130b2b2..0000000 --- a/source/RcPtr.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include "Macros.hpp" -#include "TypeTraits.hpp" - -#include -#include -#include -#include - -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 RcPtr : TDeleter { -private: - static_assert(std::is_base_of_v); - 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 - requires std::is_base_of_v - operator RcPtr() const { - return RcPtr(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 - bool operator==(const RcPtr& 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/Rect.hpp b/source/Rect.hpp deleted file mode 100644 index 89d9b01..0000000 --- a/source/Rect.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#include - -/// 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 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 - // 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&, const Rect&) = 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 - Rect Cast() const { - return { - static_cast(x), - static_cast(y), - static_cast(width), - static_cast(height), - }; - } -}; diff --git a/source/SmallVector.cpp b/source/SmallVector.cpp deleted file mode 100644 index c38e8a7..0000000 --- a/source/SmallVector.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// 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 -#include -#include - -// 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) == - sizeof(unsigned) * 2 + sizeof(void*), - "wasted space in SmallVector size 0"); -static_assert(alignof(SmallVector) >= alignof(Struct16B), - "wrong alignment for 16-byte aligned T"); -static_assert(alignof(SmallVector) >= alignof(Struct32B), - "wrong alignment for 32-byte aligned T"); -static_assert(sizeof(SmallVector) >= alignof(Struct16B), - "missing padding for 16-byte aligned T"); -static_assert(sizeof(SmallVector) >= alignof(Struct32B), - "missing padding for 32-byte aligned T"); -static_assert(sizeof(SmallVector) == - sizeof(unsigned) * 2 + sizeof(void*) * 2, - "wasted space in SmallVector size 1"); - -static_assert(sizeof(SmallVector) == - 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 -static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { - constexpr size_t MaxSize = std::numeric_limits::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 -void* SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity) { - NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); - return malloc(NewCapacity * TSize); -} - -// Note: Moving this function into the header may cause performance regression. -template -void SmallVectorBase::grow_pod(void* FirstEl, size_t MinSize, size_t TSize) { - size_t NewCapacity = getNewCapacity(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; - -// 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; - -// Assertions to ensure this #if stays in sync with SmallVectorSizeType. -static_assert(sizeof(SmallVectorSizeType) == sizeof(uint64_t), - "Expected SmallVectorBase variant to be in use."); -#else -static_assert(sizeof(SmallVectorSizeType) == sizeof(uint32_t), - "Expected SmallVectorBase variant to be in use."); -#endif diff --git a/source/SmallVector.hpp b/source/SmallVector.hpp deleted file mode 100644 index e33a25d..0000000 --- a/source/SmallVector.hpp +++ /dev/null @@ -1,1332 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 -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, where a -/// 32 bit size would limit the vector to ~4GB. SmallVectors are used for -/// buffering bitcode output - which can exceed 4GB. -template -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::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 -using SmallVectorSizeType = - typename std::conditional= 8, uint64_t, uint32_t>::type; - -/// Figure out the offset of the first element. -template -struct SmallVectorAlignmentAndSize { - alignas(SmallVectorBase>) char Base[sizeof( - SmallVectorBase>)]; - 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 -class SmallVectorTemplateCommon - : public SmallVectorBase> { - using Base = SmallVectorBase>; - - /// 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(reinterpret_cast( - reinterpret_cast(this) + - offsetof(SmallVectorAlignmentAndSize, 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, 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, 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 - 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; - using reverse_iterator = std::reverse_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 - 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, which is not -/// trivially assignable. -template ::value) && (std::is_trivially_move_constructible::value) && std::is_trivially_destructible::value> -class SmallVectorTemplateBase : public SmallVectorTemplateCommon { - friend class SmallVectorTemplateCommon; - -protected: - static constexpr bool TakesParamByValue = false; - using ValueParamT = const T&; - - SmallVectorTemplateBase(size_t Size) - : SmallVectorTemplateCommon(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 - 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 - 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( - SmallVectorBase>::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( - 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 - 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(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 -void SmallVectorTemplateBase::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 -void SmallVectorTemplateBase::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 -void SmallVectorTemplateBase::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 - 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 -class SmallVectorTemplateBase : public SmallVectorTemplateCommon { - friend class SmallVectorTemplateCommon; - -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::type; - - SmallVectorTemplateBase(size_t Size) - : SmallVectorTemplateCommon(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 - 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 - 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 - static void uninitialized_copy( - T1* I, T1* E, T2* Dest, std::enable_if_t::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(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( - 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 - 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(Args)...)); - return this->back(); - } - -public: - void push_back(ValueParamT Elt) { - const T* EltPtr = reserveForParamAndGetAddress(Elt); - memcpy(reinterpret_cast(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 -class SmallVectorImpl : public SmallVectorTemplateBase { - using SuperClass = SmallVectorTemplateBase; - -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::TakesParamByValue; - using ValueParamT = typename SuperClass::ValueParamT; - - // Default ctor - Initialize to empty. - explicit SmallVectorImpl(unsigned N) - : SmallVectorTemplateBase(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 - 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(N); } - - /// Like resize, but \ref T is POD, the new values won't be initialized. - void resize_for_overwrite(size_type N) { resizeImpl(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 ::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 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 ::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 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(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(CS); - iterator E = const_cast(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 - iterator insert_one_impl(iterator I, ArgType&& Elt) { - // Callers ensure that ArgType is derived from T. - static_assert( - std::is_same>, - T>::value, - "ArgType must be derived from T!"); - - if (I == this->end()) { // Important special case for empty vector. - this->push_back(::std::forward(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* 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::value, - "ArgType must be 'T' when taking by value!"); - if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end())) - ++EltPtr; - - *I = ::std::forward(*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(this->end() - NumToInsert), - std::move_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 ::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(this->end() - NumToInsert), - std::move_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 IL) { - insert(I, IL.begin(), IL.end()); - } - - template - reference emplace_back(ArgTypes&&... Args) { - if (LLVM_UNLIKELY(this->size() >= this->capacity())) - return this->growAndEmplaceBack(std::forward(Args)...); - - ::new ((void*)this->end()) T(std::forward(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 -void SmallVectorImpl::swap(SmallVectorImpl& 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 -SmallVectorImpl& SmallVectorImpl:: -operator=(const SmallVectorImpl& 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 -SmallVectorImpl& SmallVectorImpl::operator=(SmallVectorImpl&& 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 -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 -struct alignas(T) SmallVectorStorage {}; - -/// Forward declaration of SmallVector so that -/// calculateSmallVectorDefaultInlinedElements can reference -/// `sizeof(SmallVector)`. -template -class SmallVector; - -/// Helper class for calculating the default number of inline elements for -/// `SmallVector`. -/// -/// This should be migrated to a constexpr function when our minimum -/// compiler support is enough for multi-statement constexpr functions. -template -struct CalculateSmallVectorDefaultInlinedElements { - // Parameter controlling the default number of inlined elements - // for `SmallVector`. - // - // The default number of inlined elements ensures that - // 1. There is at least one inlined element. - // 2. `sizeof(SmallVector) <= 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>` 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` but `sizeof(T)` is really big! Please use an " - "explicit number of inlined elements with `SmallVector` 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); - 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 (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) around 64 bytes). -/// -/// \warning This does not attempt to be exception safe. -/// -/// \see https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h -template ::value> -class SmallVector : public SmallVectorImpl, - SmallVectorStorage { -public: - SmallVector() - : SmallVectorImpl(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(N) { - this->assign(Size, Value); - } - - template ::iterator_category, - std::input_iterator_tag>::value>> - SmallVector(ItTy S, ItTy E) - : SmallVectorImpl(N) { - this->append(S, E); - } - - template - explicit SmallVector(const iterator_range& R) - : SmallVectorImpl(N) { - this->append(R.begin(), R.end()); - } - - SmallVector(std::initializer_list IL) - : SmallVectorImpl(N) { - this->assign(IL); - } - - SmallVector(const SmallVector& RHS) - : SmallVectorImpl(N) { - if (!RHS.empty()) - SmallVectorImpl::operator=(RHS); - } - - SmallVector& operator=(const SmallVector& RHS) { - SmallVectorImpl::operator=(RHS); - return *this; - } - - SmallVector(SmallVector&& RHS) - : SmallVectorImpl(N) { - if (!RHS.empty()) - SmallVectorImpl::operator=(::std::move(RHS)); - } - - SmallVector(SmallVectorImpl&& RHS) - : SmallVectorImpl(N) { - if (!RHS.empty()) - SmallVectorImpl::operator=(::std::move(RHS)); - } - - SmallVector& operator=(SmallVector&& RHS) { - if (N) { - SmallVectorImpl::operator=(::std::move(RHS)); - return *this; - } - // SmallVectorImpl::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&& RHS) { - SmallVectorImpl::operator=(::std::move(RHS)); - return *this; - } - - SmallVector& operator=(std::initializer_list IL) { - this->assign(IL); - return *this; - } -}; - -template -inline size_t capacity_in_bytes(const SmallVector& X) { - return X.capacity_in_bytes(); -} - -template -using ValueTypeFromRangeType = - typename std::remove_const()))>::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 -SmallVector, Size> to_vector(R&& Range) { - return { std::begin(Range), std::end(Range) }; -} -template -SmallVector, - CalculateSmallVectorDefaultInlinedElements< - ValueTypeFromRangeType>::value> -to_vector(R&& Range) { - return { std::begin(Range), std::end(Range) }; -} - -namespace std { - -/// Implement std::swap in terms of SmallVector swap. -template -inline void swap(SmallVectorImpl& LHS, SmallVectorImpl& RHS) { - LHS.swap(RHS); -} - -/// Implement std::swap in terms of SmallVector swap. -template -inline void swap(SmallVector& LHS, SmallVector& RHS) { - LHS.swap(RHS); -} - -} // namespace std - -#ifdef _MSC_VER -# pragma warning(pop) -#endif diff --git a/source/TypeTraits.hpp b/source/TypeTraits.hpp deleted file mode 100644 index cca9a1f..0000000 --- a/source/TypeTraits.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -template -struct DefaultDeleter { - void operator()(T* ptr) const { - delete ptr; - } -}; - -template -struct RemoveMemberPtrImpl {}; - -template -struct RemoveMemberPtrImpl { - using Type = U; -}; - -template -using RemoveMemberPtr = typename RemoveMemberPtrImpl::Type; diff --git a/source/Uid.cpp b/source/Uid.cpp deleted file mode 100644 index 1930cd8..0000000 --- a/source/Uid.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "Uid.hpp" - -#include "RapidJsonHelper.hpp" - -#include -#include -#include - -Uid Uid::Create() { - std::random_device rd; - std::mt19937_64 gen(rd()); - std::uniform_int_distribution dist( - std::numeric_limits::min(), - std::numeric_limits::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/Uid.hpp b/source/Uid.hpp deleted file mode 100644 index f58129c..0000000 --- a/source/Uid.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "Utils.hpp" - -#include -#include -#include -#include -#include - -#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 { - size_t operator()(const Uid& uid) const { - size_t hash = 0; - Utils::HashCombine(hash, uid.upper); - Utils::HashCombine(hash, uid.lower); - return hash; - } -}; -- cgit v1.2.3-70-g09d2