mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[upstream_utils] Upgrade to LLVM 22.1.6 (#8919)
This commit is contained in:
@@ -84,7 +84,7 @@
|
||||
# define ConvertUTF_RESTORE_WARNINGS \
|
||||
_Pragma("clang diagnostic pop")
|
||||
# endif
|
||||
#elif defined(__GNUC__) && __GNUC__ > 6
|
||||
#elif defined(__GNUC__)
|
||||
# define ConvertUTF_DISABLE_WARNINGS \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
||||
|
||||
@@ -305,5 +305,15 @@ bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSingleCodeUnitUTF8Codepoint(unsigned V) { return V <= 0x7F; }
|
||||
|
||||
bool IsSingleCodeUnitUTF16Codepoint(unsigned V) {
|
||||
return V <= 0xD7FF || (V >= 0xE000 && V <= 0xFFFF);
|
||||
}
|
||||
|
||||
bool IsSingleCodeUnitUTF32Codepoint(unsigned V) {
|
||||
return V <= 0xD7FF || (V >= 0xE000 && V <= 0x10FFFF);
|
||||
}
|
||||
|
||||
} // end namespace wpi::util
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "wpi/util/ErrorHandling.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/Errc.hpp"
|
||||
#include "wpi/util/Errno.hpp"
|
||||
#include "wpi/util/WindowsError.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
#include <cassert>
|
||||
@@ -50,6 +51,21 @@ static void *BadAllocErrorHandlerUserData = nullptr;
|
||||
static std::mutex ErrorHandlerMutex;
|
||||
static std::mutex BadAllocErrorHandlerMutex;
|
||||
|
||||
static bool write_retry(int fd, const char *buf, size_t count) {
|
||||
while (count > 0) {
|
||||
#ifdef _WIN32
|
||||
int written = sys::RetryAfterSignal(-1, ::_write, fd, buf, count);
|
||||
#else
|
||||
ssize_t written = sys::RetryAfterSignal(-1, ::write, fd, buf, count);
|
||||
#endif
|
||||
if (written <= 0)
|
||||
return false;
|
||||
buf += written;
|
||||
count -= written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void wpi::util::install_fatal_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data) {
|
||||
std::scoped_lock Lock(ErrorHandlerMutex);
|
||||
@@ -92,6 +108,25 @@ void wpi::util::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void wpi::util::reportFatalInternalError(const char *reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/true);
|
||||
}
|
||||
void wpi::util::reportFatalInternalError(const std::string& reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/true);
|
||||
}
|
||||
void wpi::util::reportFatalInternalError(std::string_view reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/true);
|
||||
}
|
||||
void wpi::util::reportFatalUsageError(const char *reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/false);
|
||||
}
|
||||
void wpi::util::reportFatalUsageError(const std::string& reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/false);
|
||||
}
|
||||
void wpi::util::reportFatalUsageError(std::string_view reason) {
|
||||
report_fatal_error(reason, /*GenCrashDiag=*/false);
|
||||
}
|
||||
|
||||
void wpi::util::install_bad_alloc_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data) {
|
||||
std::scoped_lock Lock(BadAllocErrorHandlerMutex);
|
||||
@@ -127,15 +162,9 @@ void wpi::util::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
|
||||
// an OOM to stderr and abort.
|
||||
const char *OOMMessage = "LLVM ERROR: out of memory\n";
|
||||
const char *Newline = "\n";
|
||||
#ifdef _WIN32
|
||||
(void)!::_write(2, OOMMessage, strlen(OOMMessage));
|
||||
(void)!::_write(2, Reason, strlen(Reason));
|
||||
(void)!::_write(2, Newline, strlen(Newline));
|
||||
#else
|
||||
(void)!::write(2, OOMMessage, strlen(OOMMessage));
|
||||
(void)!::write(2, Reason, strlen(Reason));
|
||||
(void)!::write(2, Newline, strlen(Newline));
|
||||
#endif
|
||||
write_retry(2, OOMMessage, strlen(OOMMessage));
|
||||
write_retry(2, Reason, strlen(Reason));
|
||||
write_retry(2, Newline, strlen(Newline));
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ void SmallPtrSetImplBase::shrink_and_clear() {
|
||||
// Reduce the number of buckets.
|
||||
unsigned Size = size();
|
||||
CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
|
||||
NumNonEmpty = NumTombstones = 0;
|
||||
NumEntries = NumTombstones = 0;
|
||||
|
||||
// Install the new array. Clear all the buckets to empty.
|
||||
CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
|
||||
@@ -41,7 +41,8 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
|
||||
if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
|
||||
// If more than 3/4 of the array is full, grow.
|
||||
Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
|
||||
} else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
|
||||
} else if (LLVM_UNLIKELY(CurArraySize - NumEntries - NumTombstones <
|
||||
CurArraySize / 8)) {
|
||||
// If fewer of 1/8 of the array is empty (meaning that many are filled with
|
||||
// tombstones), rehash.
|
||||
Grow(CurArraySize);
|
||||
@@ -50,16 +51,15 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
|
||||
// Okay, we know we have space. Find a hash bucket.
|
||||
const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
|
||||
if (*Bucket == Ptr)
|
||||
return std::make_pair(Bucket, false); // Already inserted, good.
|
||||
return {Bucket, false}; // Already inserted, good.
|
||||
|
||||
// Otherwise, insert it!
|
||||
if (*Bucket == getTombstoneMarker())
|
||||
--NumTombstones;
|
||||
else
|
||||
++NumNonEmpty; // Track density.
|
||||
++NumEntries;
|
||||
*Bucket = Ptr;
|
||||
incrementEpoch();
|
||||
return std::make_pair(Bucket, true);
|
||||
return {Bucket, true};
|
||||
}
|
||||
|
||||
const void *const *SmallPtrSetImplBase::doFind(const void *Ptr) const {
|
||||
@@ -110,8 +110,7 @@ const void *const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
|
||||
/// Grow - Allocate a larger backing store for the buckets and move it over.
|
||||
///
|
||||
void SmallPtrSetImplBase::Grow(unsigned NewSize) {
|
||||
const void **OldBuckets = CurArray;
|
||||
const void **OldEnd = EndPointer();
|
||||
auto OldBuckets = buckets();
|
||||
bool WasSmall = isSmall();
|
||||
|
||||
// Install the new array. Clear all the buckets to empty.
|
||||
@@ -123,16 +122,14 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) {
|
||||
memset(CurArray, -1, NewSize*sizeof(void*));
|
||||
|
||||
// Copy over all valid entries.
|
||||
for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
|
||||
for (const void *&Bucket : OldBuckets) {
|
||||
// Copy over the element if it is valid.
|
||||
const void *Elt = *BucketPtr;
|
||||
if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
|
||||
*const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
|
||||
if (Bucket != getTombstoneMarker() && Bucket != getEmptyMarker())
|
||||
*const_cast<void **>(FindBucketFor(Bucket)) = const_cast<void *>(Bucket);
|
||||
}
|
||||
|
||||
if (!WasSmall)
|
||||
free(OldBuckets);
|
||||
NumNonEmpty -= NumTombstones;
|
||||
free(OldBuckets.begin());
|
||||
NumTombstones = 0;
|
||||
IsSmall = false;
|
||||
}
|
||||
@@ -193,9 +190,9 @@ void SmallPtrSetImplBase::copyHelper(const SmallPtrSetImplBase &RHS) {
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
|
||||
// Copy over the contents from the other set
|
||||
std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
|
||||
std::ranges::copy(RHS.buckets(), CurArray);
|
||||
|
||||
NumNonEmpty = RHS.NumNonEmpty;
|
||||
NumEntries = RHS.NumEntries;
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
}
|
||||
|
||||
@@ -217,7 +214,7 @@ void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
|
||||
if (RHS.isSmall()) {
|
||||
// Copy a small RHS rather than moving.
|
||||
CurArray = SmallStorage;
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
|
||||
std::ranges::copy(RHS.small_buckets(), CurArray);
|
||||
} else {
|
||||
CurArray = RHS.CurArray;
|
||||
RHS.CurArray = RHSSmallStorage;
|
||||
@@ -225,13 +222,13 @@ void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
|
||||
|
||||
// Copy the rest of the trivial members.
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
NumNonEmpty = RHS.NumNonEmpty;
|
||||
NumEntries = RHS.NumEntries;
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
IsSmall = RHS.IsSmall;
|
||||
|
||||
// Make the RHS small and empty.
|
||||
RHS.CurArraySize = SmallSize;
|
||||
RHS.NumNonEmpty = 0;
|
||||
RHS.NumEntries = 0;
|
||||
RHS.NumTombstones = 0;
|
||||
RHS.IsSmall = true;
|
||||
}
|
||||
@@ -245,54 +242,42 @@ void SmallPtrSetImplBase::swap(const void **SmallStorage,
|
||||
if (!this->isSmall() && !RHS.isSmall()) {
|
||||
std::swap(this->CurArray, RHS.CurArray);
|
||||
std::swap(this->CurArraySize, RHS.CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumEntries, RHS.NumEntries);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: From here on we assume that both sets have the same small size.
|
||||
|
||||
// If only RHS is small, copy the small elements into LHS and move the pointer
|
||||
// from LHS to RHS.
|
||||
if (!this->isSmall() && RHS.isSmall()) {
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, SmallStorage);
|
||||
std::swap(RHS.CurArraySize, this->CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
RHS.CurArray = this->CurArray;
|
||||
RHS.IsSmall = false;
|
||||
this->CurArray = SmallStorage;
|
||||
this->IsSmall = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If only LHS is small, copy the small elements into RHS and move the pointer
|
||||
// from RHS to LHS.
|
||||
if (this->isSmall() && !RHS.isSmall()) {
|
||||
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
|
||||
RHSSmallStorage);
|
||||
std::swap(RHS.CurArraySize, this->CurArraySize);
|
||||
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
|
||||
std::swap(RHS.NumTombstones, this->NumTombstones);
|
||||
this->CurArray = RHS.CurArray;
|
||||
this->IsSmall = false;
|
||||
RHS.CurArray = RHSSmallStorage;
|
||||
RHS.IsSmall = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Both a small, just swap the small elements.
|
||||
assert(this->isSmall() && RHS.isSmall());
|
||||
unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap_ranges(this->CurArray, this->CurArray + MinNonEmpty, RHS.CurArray);
|
||||
if (this->NumNonEmpty > MinNonEmpty) {
|
||||
std::copy(this->CurArray + MinNonEmpty, this->CurArray + this->NumNonEmpty,
|
||||
RHS.CurArray + MinNonEmpty);
|
||||
} else {
|
||||
std::copy(RHS.CurArray + MinNonEmpty, RHS.CurArray + RHS.NumNonEmpty,
|
||||
this->CurArray + MinNonEmpty);
|
||||
if (this->isSmall() && RHS.isSmall()) {
|
||||
unsigned MinEntries = std::min(this->NumEntries, RHS.NumEntries);
|
||||
std::swap_ranges(this->CurArray, this->CurArray + MinEntries, RHS.CurArray);
|
||||
if (this->NumEntries > MinEntries) {
|
||||
std::copy(this->CurArray + MinEntries, this->CurArray + this->NumEntries,
|
||||
RHS.CurArray + MinEntries);
|
||||
} else {
|
||||
std::copy(RHS.CurArray + MinEntries, RHS.CurArray + RHS.NumEntries,
|
||||
this->CurArray + MinEntries);
|
||||
}
|
||||
assert(this->CurArraySize == RHS.CurArraySize);
|
||||
std::swap(this->NumEntries, RHS.NumEntries);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
return;
|
||||
}
|
||||
assert(this->CurArraySize == RHS.CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
|
||||
// If only one side is small, copy the small elements into the large side and
|
||||
// move the pointer from the large side to the small side.
|
||||
SmallPtrSetImplBase &SmallSide = this->isSmall() ? *this : RHS;
|
||||
SmallPtrSetImplBase &LargeSide = this->isSmall() ? RHS : *this;
|
||||
const void **LargeSideInlineStorage =
|
||||
this->isSmall() ? RHSSmallStorage : SmallStorage;
|
||||
std::ranges::copy(SmallSide.small_buckets(), LargeSideInlineStorage);
|
||||
std::swap(LargeSide.CurArraySize, SmallSide.CurArraySize);
|
||||
std::swap(LargeSide.NumEntries, SmallSide.NumEntries);
|
||||
std::swap(LargeSide.NumTombstones, SmallSide.NumTombstones);
|
||||
SmallSide.CurArray = LargeSide.CurArray;
|
||||
SmallSide.IsSmall = false;
|
||||
LargeSide.CurArray = LargeSideInlineStorage;
|
||||
LargeSide.IsSmall = true;
|
||||
}
|
||||
|
||||
@@ -82,15 +82,15 @@ inline bool RunningWindows8OrGreater() {
|
||||
}
|
||||
|
||||
/// Determines if the program is running on Windows 11 or Windows Server 2022.
|
||||
bool RunningWindows11OrGreater();
|
||||
LLVM_ABI bool RunningWindows11OrGreater();
|
||||
|
||||
/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
|
||||
/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
|
||||
/// GetVersionEx is deprecated, but this API exposes the build number which can
|
||||
/// be useful for working around certain kernel bugs.
|
||||
wpi::util::VersionTuple GetWindowsOSVersion();
|
||||
LLVM_ABI wpi::util::VersionTuple GetWindowsOSVersion();
|
||||
|
||||
bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
|
||||
LLVM_ABI bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
|
||||
|
||||
// Include GetLastError() in a fatal error message.
|
||||
[[noreturn]] inline void ReportLastErrorFatal(const char *Msg) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "wpi/util/ErrorHandling.hpp"
|
||||
#include "wpi/util/fs.hpp"
|
||||
#include "wpi/util/IOSandbox.hpp"
|
||||
#include "wpi/util/MathExtras.hpp"
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
@@ -59,17 +60,6 @@
|
||||
|
||||
using namespace wpi::util;
|
||||
|
||||
constexpr raw_ostream::Colors raw_ostream::BLACK;
|
||||
constexpr raw_ostream::Colors raw_ostream::RED;
|
||||
constexpr raw_ostream::Colors raw_ostream::GREEN;
|
||||
constexpr raw_ostream::Colors raw_ostream::YELLOW;
|
||||
constexpr raw_ostream::Colors raw_ostream::BLUE;
|
||||
constexpr raw_ostream::Colors raw_ostream::MAGENTA;
|
||||
constexpr raw_ostream::Colors raw_ostream::CYAN;
|
||||
constexpr raw_ostream::Colors raw_ostream::WHITE;
|
||||
constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
|
||||
constexpr raw_ostream::Colors raw_ostream::RESET;
|
||||
|
||||
raw_ostream::~raw_ostream() {
|
||||
// raw_ostream's subclasses should take care to flush the buffer
|
||||
// in their destructors.
|
||||
@@ -288,6 +278,9 @@ void raw_ostream::anchor() {}
|
||||
static int getFD(std::string_view Filename, std::error_code &EC,
|
||||
fs::CreationDisposition Disp, fs::FileAccess Access,
|
||||
fs::OpenFlags Flags) {
|
||||
// FIXME(sandboxing): Remove this by adopting `wpi::util::vfs::OutputBackend`.
|
||||
auto BypassSandbox = sys::sandbox::scopedDisable();
|
||||
|
||||
assert((Access & fs::FA_Write) &&
|
||||
"Cannot make a raw_ostream from a read-only descriptor!");
|
||||
|
||||
@@ -347,6 +340,9 @@ raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
|
||||
raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
|
||||
OStreamKind K)
|
||||
: raw_pwrite_stream(unbuffered, K), FD(fd), ShouldClose(shouldClose) {
|
||||
// FIXME(sandboxing): Remove this by adopting `wpi::util::vfs::OutputBackend`.
|
||||
auto BypassSandbox = sys::sandbox::scopedDisable();
|
||||
|
||||
if (FD < 0 ) {
|
||||
ShouldClose = false;
|
||||
return;
|
||||
@@ -402,8 +398,7 @@ raw_fd_ostream::~raw_fd_ostream() {
|
||||
// has_error() and clear the error flag with clear_error() before
|
||||
// destructing raw_ostream objects which may have errors.
|
||||
if (has_error())
|
||||
report_fatal_error("IO failure on output stream: " + error().message(),
|
||||
/*gen_crash_diag=*/false);
|
||||
reportFatalUsageError("IO failure on output stream: " + error().message());
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -596,6 +591,7 @@ void raw_fd_ostream::anchor() {}
|
||||
raw_fd_ostream &wpi::util::outs() {
|
||||
// Set buffer settings to model stdout behavior.
|
||||
std::error_code EC;
|
||||
|
||||
static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::OF_None);
|
||||
assert(!EC);
|
||||
return *S;
|
||||
|
||||
@@ -14,15 +14,16 @@
|
||||
#define WPIUTIL_WPI_ALIGNOF_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
/// A suitably aligned and sized character array member which can hold elements
|
||||
/// of any type.
|
||||
template <typename... Ts> struct AlignedCharArrayUnion {
|
||||
alignas((std::max)({alignof(Ts)...}))
|
||||
std::byte buffer[(std::max)({static_cast<size_t>(1), sizeof(Ts)...})];
|
||||
template <typename T, typename... Ts> struct AlignedCharArrayUnion {
|
||||
// Work around "internal compiler error: Segmentation fault" with GCC 7.5,
|
||||
// apparently caused by alignas(Ts...).
|
||||
static constexpr std::size_t Align = (std::max)({alignof(T), alignof(Ts)...});
|
||||
alignas(Align) char buffer[(std::max)({sizeof(T), sizeof(Ts)...})];
|
||||
};
|
||||
|
||||
} // end namespace wpi::util
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "wpi/util/MemAlloc.hpp"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
@@ -111,7 +112,7 @@ template <typename Alloc> class AllocatorHolder : Alloc {
|
||||
public:
|
||||
AllocatorHolder() = default;
|
||||
AllocatorHolder(const Alloc &A) : Alloc(A) {}
|
||||
AllocatorHolder(Alloc &&A) : Alloc(static_cast<Alloc &&>(A)) {}
|
||||
AllocatorHolder(Alloc &&A) : Alloc(std::move(A)) {}
|
||||
Alloc &getAllocator() { return *this; }
|
||||
const Alloc &getAllocator() const { return *this; }
|
||||
};
|
||||
|
||||
@@ -340,7 +340,7 @@ struct ValueFromPointerCast
|
||||
/// during the cast. It's also a good example of how to implement a move-only
|
||||
/// cast.
|
||||
template <typename To, typename From, typename Derived = void>
|
||||
struct UniquePtrCast : public CastIsPossible<To, From *> {
|
||||
struct UniquePtrCast : CastIsPossible<To, From *> {
|
||||
using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>;
|
||||
using CastResultType = std::unique_ptr<
|
||||
std::remove_reference_t<typename cast_retty<To, From>::ret_type>>;
|
||||
@@ -473,7 +473,7 @@ struct ForwardToPointerCast {
|
||||
// take advantage of the cast traits whenever possible!
|
||||
|
||||
template <typename To, typename From, typename Enable = void>
|
||||
struct CastInfo : public CastIsPossible<To, From> {
|
||||
struct CastInfo : CastIsPossible<To, From> {
|
||||
using Self = CastInfo<To, From, Enable>;
|
||||
|
||||
using CastReturnType = typename cast_retty<To, From>::ret_type;
|
||||
@@ -536,22 +536,16 @@ struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {};
|
||||
/// the input is std::optional<From> that the output can be std::optional<To>.
|
||||
/// If that's not the case, specialize CastInfo for your use case.
|
||||
template <typename To, typename From>
|
||||
struct CastInfo<To, std::optional<From>> : public OptionalValueCast<To, From> {
|
||||
};
|
||||
struct CastInfo<To, std::optional<From>> : OptionalValueCast<To, From> {};
|
||||
|
||||
/// isa<X> - Return true if the parameter to the template is an instance of one
|
||||
/// of the template type arguments. Used like this:
|
||||
///
|
||||
/// if (isa<Type>(myVal)) { ... }
|
||||
/// if (isa<Type0, Type1, Type2>(myVal)) { ... }
|
||||
template <typename To, typename From>
|
||||
template <typename... To, typename From>
|
||||
[[nodiscard]] inline bool isa(const From &Val) {
|
||||
return CastInfo<To, const From>::isPossible(Val);
|
||||
}
|
||||
|
||||
template <typename First, typename Second, typename... Rest, typename From>
|
||||
[[nodiscard]] inline bool isa(const From &Val) {
|
||||
return isa<First>(Val) || isa<Second, Rest...>(Val);
|
||||
return (CastInfo<To, const From>::isPossible(Val) || ...);
|
||||
}
|
||||
|
||||
/// cast<X> - Return the argument parameter cast to the specified type. This
|
||||
@@ -822,6 +816,42 @@ template <typename... Types> struct IsaAndPresentCheckPredicate {
|
||||
return isa_and_present<Types...>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Casting Function Objects
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Usable in generic algorithms like map_range
|
||||
template <typename U> struct StaticCastFunc {
|
||||
template <typename T> decltype(auto) operator()(T &&Val) const {
|
||||
return static_cast<U>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U> struct DynCastFunc {
|
||||
template <typename T> decltype(auto) operator()(T &&Val) const {
|
||||
return dyn_cast<U>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U> struct CastFunc {
|
||||
template <typename T> decltype(auto) operator()(T &&Val) const {
|
||||
return cast<U>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U> struct CastIfPresentFunc {
|
||||
template <typename T> decltype(auto) operator()(T &&Val) const {
|
||||
return cast_if_present<U>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U> struct DynCastIfPresentFunc {
|
||||
template <typename T> decltype(auto) operator()(T &&Val) const {
|
||||
return dyn_cast_if_present<U>(Val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Function object wrapper for the `wpi::util::isa` type check. The function call
|
||||
@@ -847,6 +877,20 @@ template <typename... Types>
|
||||
inline constexpr detail::IsaAndPresentCheckPredicate<Types...>
|
||||
IsaAndPresentPred{};
|
||||
|
||||
/// Function objects corresponding to the Cast types defined above.
|
||||
template <typename To>
|
||||
inline constexpr detail::StaticCastFunc<To> StaticCastTo{};
|
||||
|
||||
template <typename To> inline constexpr detail::CastFunc<To> CastTo{};
|
||||
|
||||
template <typename To>
|
||||
inline constexpr detail::CastIfPresentFunc<To> CastIfPresentTo{};
|
||||
|
||||
template <typename To>
|
||||
inline constexpr detail::DynCastIfPresentFunc<To> DynCastIfPresentTo{};
|
||||
|
||||
template <typename To> inline constexpr detail::DynCastFunc<To> DynCastTo{};
|
||||
|
||||
} // end namespace wpi::util
|
||||
|
||||
#endif // WPIUTIL_WPI_CASTING_H
|
||||
|
||||
@@ -76,8 +76,8 @@ toTimePoint(std::time_t T, uint32_t nsec) {
|
||||
|
||||
} // namespace sys
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
|
||||
raw_ostream &operator<<(raw_ostream &OS, sys::UtcTime<> TP);
|
||||
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
|
||||
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, sys::UtcTime<> TP);
|
||||
|
||||
} // namespace wpi::util
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_warning
|
||||
# define __has_warning(x) 0
|
||||
#endif
|
||||
|
||||
// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in
|
||||
// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid.
|
||||
#ifndef LLVM_HAS_CPP_ATTRIBUTE
|
||||
@@ -130,7 +134,7 @@
|
||||
#endif
|
||||
|
||||
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
|
||||
(defined(__MINGW32__) && defined(__clang__)))
|
||||
((defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__clang__)))
|
||||
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
|
||||
// Clang compilers older then 15 do not support gnu style attributes on
|
||||
// namespaces.
|
||||
@@ -168,26 +172,19 @@
|
||||
/// for both functions and classes. On windows its turned in to dllimport for
|
||||
/// library consumers, for other platforms its a default visibility attribute.
|
||||
///
|
||||
/// LLVM_C_ABI is used to annotated functions and data that need to be exported
|
||||
/// for the libllvm-c API. This used both for the llvm-c headers and for the
|
||||
/// functions declared in the different Target's c++ source files that don't
|
||||
/// include the header forward declaring them.
|
||||
/// LLVM_ABI_FOR_TEST is for annotating symbols that are only exported because
|
||||
/// they are imported from a test. These symbols are not technically part of the
|
||||
/// LLVM public interface and could be conditionally excluded when not building
|
||||
/// tests in the future.
|
||||
///
|
||||
#ifndef LLVM_ABI_GENERATING_ANNOTATIONS
|
||||
// Marker to add to classes or functions in public headers that should not have
|
||||
// export macros added to them by the clang tool
|
||||
#define LLVM_ABI_NOT_EXPORTED
|
||||
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) || \
|
||||
defined(LLVM_ENABLE_PLUGINS)
|
||||
// Some libraries like those for tablegen are linked in to tools that used
|
||||
// in the build so can't depend on the llvm shared library. If export macros
|
||||
// were left enabled when building these we would get duplicate or
|
||||
// missing symbol linker errors on windows.
|
||||
#if defined(LLVM_BUILD_STATIC)
|
||||
#define LLVM_ABI
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT
|
||||
#elif defined(_WIN32) && !defined(__MINGW32__)
|
||||
// TODO(https://github.com/llvm/llvm-project/issues/145406): eliminate need for
|
||||
// two preprocessor definitions to gate LLVM_ABI macro definitions.
|
||||
#if defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) && !defined(LLVM_BUILD_STATIC)
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
#if defined(LLVM_EXPORTS)
|
||||
#define LLVM_ABI __declspec(dllexport)
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
@@ -198,25 +195,28 @@
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#endif
|
||||
#define LLVM_ABI_EXPORT __declspec(dllexport)
|
||||
#elif defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \
|
||||
defined(__MVS__)
|
||||
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#define LLVM_TEMPLATE_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#elif __has_attribute(visibility)
|
||||
#if defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \
|
||||
defined(__MVS__) || defined(__CYGWIN__)
|
||||
#define LLVM_ABI __attribute__((visibility("default")))
|
||||
#define LLVM_TEMPLATE_ABI LLVM_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#define LLVM_ABI_EXPORT LLVM_ABI
|
||||
#elif defined(__MACH__) || defined(__WASM__) || defined(__EMSCRIPTEN__)
|
||||
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#define LLVM_ABI __attribute__((visibility("default")))
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#define LLVM_ABI_EXPORT LLVM_ABI
|
||||
#endif
|
||||
#else
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(LLVM_ABI)
|
||||
#define LLVM_ABI
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT
|
||||
#endif
|
||||
#define LLVM_C_ABI LLVM_ABI
|
||||
#define LLVM_ABI_FOR_TEST LLVM_ABI
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_PREFETCH
|
||||
@@ -227,6 +227,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __has_attribute(uninitialized)
|
||||
#define LLVM_ATTRIBUTE_UNINITIALIZED __attribute__((uninitialized))
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_UNINITIALIZED
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_ATTRIBUTE_USED
|
||||
#if __has_attribute(used)
|
||||
#define LLVM_ATTRIBUTE_USED __attribute__((__used__))
|
||||
@@ -235,6 +241,16 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Only enabled for clang:
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587
|
||||
// GCC may produce "warning: 'retain' attribute ignored" (despite
|
||||
// __has_attribute(retain) being 1).
|
||||
#if defined(__clang__) && __has_attribute(retain)
|
||||
#define LLVM_ATTRIBUTE_RETAIN __attribute__((__retain__))
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_RETAIN
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
|
||||
#else
|
||||
@@ -657,7 +673,8 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
|
||||
// FIXME: Move this to a private config.h as it's not usable in public headers.
|
||||
#ifndef LLVM_DUMP_METHOD
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
|
||||
#define LLVM_DUMP_METHOD \
|
||||
LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED LLVM_ATTRIBUTE_RETAIN
|
||||
#else
|
||||
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
@@ -724,4 +741,37 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
|
||||
#define LLVM_PREFERRED_TYPE(T)
|
||||
#endif
|
||||
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(clang::ptrauth_vtable_pointer) && \
|
||||
(defined(__PTRAUTH__) || __has_feature(ptrauth_calls))
|
||||
#define LLVM_MOVABLE_POLYMORPHIC_TYPE \
|
||||
[[clang::ptrauth_vtable_pointer(default_key, no_address_discrimination, \
|
||||
default_extra_discrimination)]]
|
||||
#else
|
||||
#define LLVM_MOVABLE_POLYMORPHIC_TYPE
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_VIRTUAL_ANCHOR_FUNCTION
|
||||
/// This macro is used to adhere to LLVM's policy that each class with a vtable
|
||||
/// must have at least one out-of-line virtual function. This macro allows us
|
||||
/// to declare such a function in `final` classes without triggering a warning.
|
||||
// clang-format off
|
||||
// Autoformatting makes this look awful.
|
||||
#if defined(__clang__)
|
||||
// Make sure this is only parsed if __clang__ is defined
|
||||
#if __has_warning("-Wunnecessary-virtual-specifier")
|
||||
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wunnecessary-virtual-specifier\"") \
|
||||
virtual void anchor() \
|
||||
_Pragma("clang diagnostic pop")
|
||||
#else // __has_warning
|
||||
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
|
||||
virtual void anchor()
|
||||
#endif
|
||||
#else // defined(__clang__)
|
||||
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
|
||||
virtual void anchor()
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Header file.
|
||||
|
||||
Several funtions are included here, forming a complete set of
|
||||
Several functions are included here, forming a complete set of
|
||||
conversions between the three formats. UTF-7 is not included
|
||||
here, but is handled in a separate source file.
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
#ifndef WPIUTIL_WPI_CONVERTUTF_H
|
||||
#define WPIUTIL_WPI_CONVERTUTF_H
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <span>
|
||||
@@ -124,10 +125,10 @@ namespace wpi::util {
|
||||
bit mask & shift operations.
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
typedef unsigned int UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
typedef bool Boolean; /* 0 or 1 */
|
||||
using UTF32 = unsigned int; /* at least 32 bits */
|
||||
using UTF16 = unsigned short; /* at least 16 bits */
|
||||
using UTF8 = unsigned char; /* typically 8 bits */
|
||||
using Boolean = bool; /* 0 or 1 */
|
||||
|
||||
/* Some fundamental constants */
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
@@ -144,61 +145,73 @@ typedef bool Boolean; /* 0 or 1 */
|
||||
#define UNI_UTF32_BYTE_ORDER_MARK_NATIVE 0x0000FEFF
|
||||
#define UNI_UTF32_BYTE_ORDER_MARK_SWAPPED 0xFFFE0000
|
||||
|
||||
typedef enum {
|
||||
conversionOK, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
} ConversionResult;
|
||||
enum ConversionResult {
|
||||
conversionOK, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
strictConversion = 0,
|
||||
lenientConversion
|
||||
} ConversionFlags;
|
||||
enum ConversionFlags { strictConversion = 0, lenientConversion };
|
||||
|
||||
ConversionResult ConvertUTF8toUTF16 (
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart,
|
||||
const UTF8 *sourceEnd,
|
||||
UTF16 **targetStart,
|
||||
UTF16 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
/**
|
||||
* Convert a partial UTF8 sequence to UTF32. If the sequence ends in an
|
||||
* incomplete code unit sequence, returns \c sourceExhausted.
|
||||
*/
|
||||
ConversionResult ConvertUTF8toUTF32Partial(
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
|
||||
const UTF8 *sourceEnd,
|
||||
UTF32 **targetStart,
|
||||
UTF32 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
/**
|
||||
* Convert a partial UTF8 sequence to UTF32. If the sequence ends in an
|
||||
* incomplete code unit sequence, returns \c sourceIllegal.
|
||||
*/
|
||||
ConversionResult ConvertUTF8toUTF32(
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
|
||||
const UTF8 *sourceEnd,
|
||||
UTF32 **targetStart,
|
||||
UTF32 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
ConversionResult ConvertUTF16toUTF8 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart,
|
||||
const UTF16 *sourceEnd,
|
||||
UTF8 **targetStart,
|
||||
UTF8 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
ConversionResult ConvertUTF32toUTF8 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart,
|
||||
const UTF32 *sourceEnd,
|
||||
UTF8 **targetStart,
|
||||
UTF8 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
ConversionResult ConvertUTF16toUTF32 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart,
|
||||
const UTF16 *sourceEnd,
|
||||
UTF32 **targetStart,
|
||||
UTF32 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
ConversionResult ConvertUTF32toUTF16 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
|
||||
LLVM_ABI ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart,
|
||||
const UTF32 *sourceEnd,
|
||||
UTF16 **targetStart,
|
||||
UTF16 *targetEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
|
||||
LLVM_ABI Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
|
||||
|
||||
Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
|
||||
LLVM_ABI Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
|
||||
|
||||
unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd);
|
||||
LLVM_ABI unsigned getUTF8SequenceSize(const UTF8 *source,
|
||||
const UTF8 *sourceEnd);
|
||||
|
||||
unsigned getNumBytesForUTF8(UTF8 firstByte);
|
||||
LLVM_ABI unsigned getNumBytesForUTF8(UTF8 firstByte);
|
||||
|
||||
/*************************************************************************/
|
||||
/* Below are LLVM-specific wrappers of the functions above. */
|
||||
@@ -214,27 +227,27 @@ template <typename T> class SmallVectorImpl;
|
||||
* the first character which could not be converted.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
|
||||
char *&ResultPtr, const UTF8 *&ErrorPtr);
|
||||
LLVM_ABI bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
|
||||
char *&ResultPtr, const UTF8 *&ErrorPtr);
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 string_view to a std::wstring.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
|
||||
LLVM_ABI bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 C-string to a std::wstring.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
|
||||
LLVM_ABI bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
|
||||
|
||||
/**
|
||||
* Converts a std::wstring to a UTF-8 encoded std::string.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result);
|
||||
|
||||
LLVM_ABI bool convertWideToUTF8(const std::wstring &Source,
|
||||
SmallVectorImpl<char> &Result);
|
||||
|
||||
/**
|
||||
* Convert an Unicode code point to UTF8 sequence.
|
||||
@@ -246,7 +259,7 @@ bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result
|
||||
*
|
||||
* \returns true on success.
|
||||
*/
|
||||
bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr);
|
||||
LLVM_ABI bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr);
|
||||
|
||||
/**
|
||||
* Convert the first UTF8 sequence in the given source buffer to a UTF32
|
||||
@@ -279,7 +292,7 @@ inline ConversionResult convertUTF8Sequence(const UTF8 **source,
|
||||
* Returns true if a blob of text starts with a UTF-16 big or little endian byte
|
||||
* order mark.
|
||||
*/
|
||||
bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
|
||||
LLVM_ABI bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
|
||||
|
||||
/**
|
||||
* Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string.
|
||||
@@ -288,7 +301,8 @@ bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
|
||||
* \param [out] Out Converted UTF-8 is stored here on success.
|
||||
* \returns true on success
|
||||
*/
|
||||
bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<char> &Out);
|
||||
LLVM_ABI bool convertUTF16ToUTF8String(std::span<const char> SrcBytes,
|
||||
SmallVectorImpl<char> &Out);
|
||||
|
||||
/**
|
||||
* Converts a UTF16 string into a UTF8 std::string.
|
||||
@@ -297,7 +311,8 @@ bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<ch
|
||||
* \param [out] Out Converted UTF-8 is stored here on success.
|
||||
* \returns true on success
|
||||
*/
|
||||
bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char> &Out);
|
||||
LLVM_ABI bool convertUTF16ToUTF8String(std::span<const UTF16> Src,
|
||||
SmallVectorImpl<char> &Out);
|
||||
|
||||
/**
|
||||
* Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string.
|
||||
@@ -306,7 +321,8 @@ bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char>
|
||||
* \param [out] Out Converted UTF-8 is stored here on success.
|
||||
* \returns true on success
|
||||
*/
|
||||
bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out);
|
||||
LLVM_ABI bool convertUTF32ToUTF8String(std::span<const char> SrcBytes,
|
||||
std::string &Out);
|
||||
|
||||
/**
|
||||
* Converts a UTF32 string into a UTF8 std::string.
|
||||
@@ -315,27 +331,33 @@ bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out);
|
||||
* \param [out] Out Converted UTF-8 is stored here on success.
|
||||
* \returns true on success
|
||||
*/
|
||||
bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out);
|
||||
LLVM_ABI bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out);
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 string into a UTF-16 string with native endianness.
|
||||
*
|
||||
* \returns true on success
|
||||
*/
|
||||
bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
|
||||
SmallVectorImpl<UTF16> &DstUTF16);
|
||||
LLVM_ABI bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
|
||||
SmallVectorImpl<UTF16> &DstUTF16);
|
||||
|
||||
LLVM_ABI bool IsSingleCodeUnitUTF8Codepoint(unsigned);
|
||||
LLVM_ABI bool IsSingleCodeUnitUTF16Codepoint(unsigned);
|
||||
LLVM_ABI bool IsSingleCodeUnitUTF32Codepoint(unsigned);
|
||||
|
||||
#if defined(_WIN32)
|
||||
namespace sys {
|
||||
namespace windows {
|
||||
std::error_code UTF8ToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||
LLVM_ABI std::error_code UTF8ToUTF16(std::string_view utf8,
|
||||
SmallVectorImpl<wchar_t> &utf16);
|
||||
/// Convert to UTF16 from the current code page used in the system
|
||||
std::error_code CurCPToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
LLVM_ABI std::error_code CurCPToUTF16(std::string_view utf8,
|
||||
SmallVectorImpl<wchar_t> &utf16);
|
||||
LLVM_ABI std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
/// Convert from UTF16 to the current code page used in the system
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
LLVM_ABI std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
} // namespace windows
|
||||
} // namespace sys
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,8 @@
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -50,10 +52,10 @@ inline unsigned combineHashValue(unsigned a, unsigned b) {
|
||||
/// just be `void`.
|
||||
template<typename T, typename Enable = void>
|
||||
struct DenseMapInfo {
|
||||
//static inline T getEmptyKey();
|
||||
//static inline T getTombstoneKey();
|
||||
//static unsigned getHashValue(const T &Val);
|
||||
//static bool isEqual(const T &LHS, const T &RHS);
|
||||
// static constexpr T getEmptyKey();
|
||||
// static constexpr T getTombstoneKey();
|
||||
// static unsigned getHashValue(const T &Val);
|
||||
// static bool isEqual(const T &LHS, const T &RHS);
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for all pointers. Come up with sentinel pointer values
|
||||
@@ -69,13 +71,13 @@ struct DenseMapInfo<T*> {
|
||||
// "Log2MaxAlign bits of alignment");
|
||||
static constexpr uintptr_t Log2MaxAlign = 12;
|
||||
|
||||
static inline T* getEmptyKey() {
|
||||
static constexpr T *getEmptyKey() {
|
||||
uintptr_t Val = static_cast<uintptr_t>(-1);
|
||||
Val <<= Log2MaxAlign;
|
||||
return reinterpret_cast<T*>(Val);
|
||||
}
|
||||
|
||||
static inline T* getTombstoneKey() {
|
||||
static constexpr T *getTombstoneKey() {
|
||||
uintptr_t Val = static_cast<uintptr_t>(-2);
|
||||
Val <<= Log2MaxAlign;
|
||||
return reinterpret_cast<T*>(Val);
|
||||
@@ -91,8 +93,8 @@ struct DenseMapInfo<T*> {
|
||||
|
||||
// Provide DenseMapInfo for chars.
|
||||
template<> struct DenseMapInfo<char> {
|
||||
static inline char getEmptyKey() { return ~0; }
|
||||
static inline char getTombstoneKey() { return ~0 - 1; }
|
||||
static constexpr char getEmptyKey() { return ~0; }
|
||||
static constexpr char getTombstoneKey() { return ~0 - 1; }
|
||||
static unsigned getHashValue(const char& Val) { return Val * 37U; }
|
||||
|
||||
static bool isEqual(const char &LHS, const char &RHS) {
|
||||
@@ -100,120 +102,33 @@ template<> struct DenseMapInfo<char> {
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for unsigned chars.
|
||||
template <> struct DenseMapInfo<unsigned char> {
|
||||
static inline unsigned char getEmptyKey() { return ~0; }
|
||||
static inline unsigned char getTombstoneKey() { return ~0 - 1; }
|
||||
static unsigned getHashValue(const unsigned char &Val) { return Val * 37U; }
|
||||
// Provide DenseMapInfo for all integral types except char.
|
||||
//
|
||||
// The "char" case is excluded because it uses ~0 as the empty key despite
|
||||
// "char" being a signed type. "std::is_same_v<T, char>" is included below
|
||||
// for clarity; technically, we do not need it because the explicit
|
||||
// specialization above "wins",
|
||||
template <typename T>
|
||||
struct DenseMapInfo<
|
||||
T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, char>>> {
|
||||
static constexpr T getEmptyKey() { return (std::numeric_limits<T>::max)(); }
|
||||
|
||||
static bool isEqual(const unsigned char &LHS, const unsigned char &RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for unsigned shorts.
|
||||
template <> struct DenseMapInfo<unsigned short> {
|
||||
static inline unsigned short getEmptyKey() { return 0xFFFF; }
|
||||
static inline unsigned short getTombstoneKey() { return 0xFFFF - 1; }
|
||||
static unsigned getHashValue(const unsigned short &Val) { return Val * 37U; }
|
||||
|
||||
static bool isEqual(const unsigned short &LHS, const unsigned short &RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for unsigned ints.
|
||||
template<> struct DenseMapInfo<unsigned> {
|
||||
static inline unsigned getEmptyKey() { return ~0U; }
|
||||
static inline unsigned getTombstoneKey() { return ~0U - 1; }
|
||||
static unsigned getHashValue(const unsigned& Val) { return Val * 37U; }
|
||||
|
||||
static bool isEqual(const unsigned& LHS, const unsigned& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for unsigned longs.
|
||||
template<> struct DenseMapInfo<unsigned long> {
|
||||
static inline unsigned long getEmptyKey() { return ~0UL; }
|
||||
static inline unsigned long getTombstoneKey() { return ~0UL - 1L; }
|
||||
|
||||
static unsigned getHashValue(const unsigned long& Val) {
|
||||
if constexpr (sizeof(Val) == 4)
|
||||
return DenseMapInfo<unsigned>::getHashValue(Val);
|
||||
static constexpr T getTombstoneKey() {
|
||||
if constexpr (std::is_unsigned_v<T> || std::is_same_v<T, long>)
|
||||
return (std::numeric_limits<T>::max)() - 1;
|
||||
else
|
||||
return (std::numeric_limits<T>::min)();
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const T &Val) {
|
||||
if constexpr (std::is_unsigned_v<T> && sizeof(T) > sizeof(unsigned))
|
||||
return densemap::detail::mix(Val);
|
||||
else
|
||||
return static_cast<unsigned>(Val *
|
||||
static_cast<std::make_unsigned_t<T>>(37U));
|
||||
}
|
||||
|
||||
static bool isEqual(const unsigned long& LHS, const unsigned long& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for unsigned long longs.
|
||||
template<> struct DenseMapInfo<unsigned long long> {
|
||||
static inline unsigned long long getEmptyKey() { return ~0ULL; }
|
||||
static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
|
||||
|
||||
static unsigned getHashValue(const unsigned long long& Val) {
|
||||
return densemap::detail::mix(Val);
|
||||
}
|
||||
|
||||
static bool isEqual(const unsigned long long& LHS,
|
||||
const unsigned long long& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for shorts.
|
||||
template <> struct DenseMapInfo<short> {
|
||||
static inline short getEmptyKey() { return 0x7FFF; }
|
||||
static inline short getTombstoneKey() { return -0x7FFF - 1; }
|
||||
static unsigned getHashValue(const short &Val) { return Val * 37U; }
|
||||
static bool isEqual(const short &LHS, const short &RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for ints.
|
||||
template<> struct DenseMapInfo<int> {
|
||||
static inline int getEmptyKey() { return 0x7fffffff; }
|
||||
static inline int getTombstoneKey() { return -0x7fffffff - 1; }
|
||||
static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37U); }
|
||||
|
||||
static bool isEqual(const int& LHS, const int& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for longs.
|
||||
template<> struct DenseMapInfo<long> {
|
||||
static inline long getEmptyKey() {
|
||||
return (1UL << (sizeof(long) * 8 - 1)) - 1UL;
|
||||
}
|
||||
|
||||
static inline long getTombstoneKey() { return getEmptyKey() - 1L; }
|
||||
|
||||
static unsigned getHashValue(const long& Val) {
|
||||
return (unsigned)(Val * 37UL);
|
||||
}
|
||||
|
||||
static bool isEqual(const long& LHS, const long& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for long longs.
|
||||
template<> struct DenseMapInfo<long long> {
|
||||
static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; }
|
||||
static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; }
|
||||
|
||||
static unsigned getHashValue(const long long& Val) {
|
||||
return (unsigned)(Val * 37ULL);
|
||||
}
|
||||
|
||||
static bool isEqual(const long long& LHS,
|
||||
const long long& RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
static bool isEqual(const T &LHS, const T &RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
// Provide DenseMapInfo for all pairs whose members have info.
|
||||
@@ -223,14 +138,12 @@ struct DenseMapInfo<std::pair<T, U>> {
|
||||
using FirstInfo = DenseMapInfo<T>;
|
||||
using SecondInfo = DenseMapInfo<U>;
|
||||
|
||||
static inline Pair getEmptyKey() {
|
||||
return std::make_pair(FirstInfo::getEmptyKey(),
|
||||
SecondInfo::getEmptyKey());
|
||||
static constexpr Pair getEmptyKey() {
|
||||
return {FirstInfo::getEmptyKey(), SecondInfo::getEmptyKey()};
|
||||
}
|
||||
|
||||
static inline Pair getTombstoneKey() {
|
||||
return std::make_pair(FirstInfo::getTombstoneKey(),
|
||||
SecondInfo::getTombstoneKey());
|
||||
static constexpr Pair getTombstoneKey() {
|
||||
return {FirstInfo::getTombstoneKey(), SecondInfo::getTombstoneKey()};
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const Pair& PairVal) {
|
||||
@@ -256,49 +169,39 @@ struct DenseMapInfo<std::pair<T, U>> {
|
||||
template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
|
||||
using Tuple = std::tuple<Ts...>;
|
||||
|
||||
static inline Tuple getEmptyKey() {
|
||||
static constexpr Tuple getEmptyKey() {
|
||||
return Tuple(DenseMapInfo<Ts>::getEmptyKey()...);
|
||||
}
|
||||
|
||||
static inline Tuple getTombstoneKey() {
|
||||
static constexpr Tuple getTombstoneKey() {
|
||||
return Tuple(DenseMapInfo<Ts>::getTombstoneKey()...);
|
||||
}
|
||||
|
||||
template <unsigned I>
|
||||
static unsigned getHashValueImpl(const Tuple &values, std::false_type) {
|
||||
using EltType = std::tuple_element_t<I, Tuple>;
|
||||
std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
|
||||
return detail::combineHashValue(
|
||||
DenseMapInfo<EltType>::getHashValue(std::get<I>(values)),
|
||||
getHashValueImpl<I + 1>(values, atEnd));
|
||||
}
|
||||
|
||||
template <unsigned I>
|
||||
static unsigned getHashValueImpl(const Tuple &, std::true_type) {
|
||||
return 0;
|
||||
template <unsigned I> static unsigned getHashValueImpl(const Tuple &values) {
|
||||
if constexpr (I == sizeof...(Ts)) {
|
||||
return 0;
|
||||
} else {
|
||||
using EltType = std::tuple_element_t<I, Tuple>;
|
||||
return detail::combineHashValue(
|
||||
DenseMapInfo<EltType>::getHashValue(std::get<I>(values)),
|
||||
getHashValueImpl<I + 1>(values));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const std::tuple<Ts...> &values) {
|
||||
std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd;
|
||||
return getHashValueImpl<0>(values, atEnd);
|
||||
return getHashValueImpl<0>(values);
|
||||
}
|
||||
|
||||
template <unsigned I>
|
||||
static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) {
|
||||
using EltType = std::tuple_element_t<I, Tuple>;
|
||||
std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
|
||||
return DenseMapInfo<EltType>::isEqual(std::get<I>(lhs), std::get<I>(rhs)) &&
|
||||
isEqualImpl<I + 1>(lhs, rhs, atEnd);
|
||||
}
|
||||
|
||||
template <unsigned I>
|
||||
static bool isEqualImpl(const Tuple &, const Tuple &, std::true_type) {
|
||||
return true;
|
||||
template <std::size_t... Is>
|
||||
static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs,
|
||||
std::index_sequence<Is...>) {
|
||||
return (DenseMapInfo<std::tuple_element_t<Is, Tuple>>::isEqual(
|
||||
std::get<Is>(lhs), std::get<Is>(rhs)) &&
|
||||
...);
|
||||
}
|
||||
|
||||
static bool isEqual(const Tuple &lhs, const Tuple &rhs) {
|
||||
std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd;
|
||||
return isEqualImpl<0>(lhs, rhs, atEnd);
|
||||
return isEqualImpl(lhs, rhs, std::index_sequence_for<Ts...>{});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -308,10 +211,22 @@ struct DenseMapInfo<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
|
||||
using UnderlyingType = std::underlying_type_t<Enum>;
|
||||
using Info = DenseMapInfo<UnderlyingType>;
|
||||
|
||||
static Enum getEmptyKey() { return static_cast<Enum>(Info::getEmptyKey()); }
|
||||
// If an enum does not have a "fixed" underlying type, it may be UB to cast
|
||||
// some values of the underlying type to the enum. We use an "extra" constexpr
|
||||
// local to ensure that such UB would trigger "static assertion expression is
|
||||
// not an integral constant expression", rather than runtime UB.
|
||||
//
|
||||
// If you hit this error, you can fix by switching to `enum class`, or adding
|
||||
// an explicit underlying type (e.g. `enum X : int`) to the enum's definition.
|
||||
|
||||
static Enum getTombstoneKey() {
|
||||
return static_cast<Enum>(Info::getTombstoneKey());
|
||||
static constexpr Enum getEmptyKey() {
|
||||
constexpr Enum V = static_cast<Enum>(Info::getEmptyKey());
|
||||
return V;
|
||||
}
|
||||
|
||||
static constexpr Enum getTombstoneKey() {
|
||||
constexpr Enum V = static_cast<Enum>(Info::getTombstoneKey());
|
||||
return V;
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const Enum &Val) {
|
||||
@@ -320,6 +235,30 @@ struct DenseMapInfo<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
|
||||
|
||||
static bool isEqual(const Enum &LHS, const Enum &RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
template <typename T> struct DenseMapInfo<std::optional<T>> {
|
||||
using Optional = std::optional<T>;
|
||||
using Info = DenseMapInfo<T>;
|
||||
|
||||
static constexpr Optional getEmptyKey() { return {Info::getEmptyKey()}; }
|
||||
|
||||
static constexpr Optional getTombstoneKey() {
|
||||
return {Info::getTombstoneKey()};
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const Optional &OptionalVal) {
|
||||
return detail::combineHashValue(
|
||||
OptionalVal.has_value(),
|
||||
Info::getHashValue(OptionalVal.value_or(Info::getEmptyKey())));
|
||||
}
|
||||
|
||||
static bool isEqual(const Optional &LHS, const Optional &RHS) {
|
||||
if (LHS && RHS) {
|
||||
return Info::isEqual(LHS.value(), RHS.value());
|
||||
}
|
||||
return !LHS && !RHS;
|
||||
}
|
||||
};
|
||||
} // end namespace wpi::util
|
||||
|
||||
#endif // WPIUTIL_WPI_DENSEMAPINFO_H
|
||||
|
||||
@@ -49,7 +49,9 @@ template <typename value_type>
|
||||
|
||||
/// Swap the bytes of value to match the given endianness.
|
||||
template <typename value_type, endianness endian>
|
||||
[[nodiscard]] inline value_type byte_swap(value_type value) {
|
||||
[[nodiscard]]
|
||||
LLVM_DEPRECATED("Pass endian as a function argument instead",
|
||||
"byte_swap") inline value_type byte_swap(value_type value) {
|
||||
if constexpr (endian != wpi::util::endianness::native)
|
||||
sys::swapByteOrder(value);
|
||||
return value;
|
||||
@@ -68,7 +70,9 @@ template <typename value_type, std::size_t alignment = unaligned>
|
||||
}
|
||||
|
||||
template <typename value_type, endianness endian, std::size_t alignment>
|
||||
[[nodiscard]] inline value_type read(const void *memory) {
|
||||
[[nodiscard]] LLVM_DEPRECATED("Pass endian as a function argument instead",
|
||||
"read") inline value_type
|
||||
read(const void *memory) {
|
||||
return read<value_type, alignment>(memory, endian);
|
||||
}
|
||||
|
||||
@@ -98,9 +102,8 @@ inline void write(void *memory, value_type value, endianness endian) {
|
||||
&value, sizeof(value_type));
|
||||
}
|
||||
|
||||
template<typename value_type,
|
||||
endianness endian,
|
||||
std::size_t alignment>
|
||||
template <typename value_type, endianness endian, std::size_t alignment>
|
||||
LLVM_DEPRECATED("Pass endian as a function argument instead", "write")
|
||||
inline void write(void *memory, value_type value) {
|
||||
write<value_type, alignment>(memory, value, endian);
|
||||
}
|
||||
@@ -130,7 +133,7 @@ template <typename value_type, endianness endian, std::size_t alignment>
|
||||
uint64_t startBit) {
|
||||
assert(startBit < 8);
|
||||
if (startBit == 0)
|
||||
return read<value_type, endian, alignment>(memory);
|
||||
return read<value_type, alignment>(memory, endian);
|
||||
else {
|
||||
// Read two values and compose the result from them.
|
||||
value_type val[2];
|
||||
@@ -138,8 +141,8 @@ template <typename value_type, endianness endian, std::size_t alignment>
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type) * 2);
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
val[0] = byte_swap<value_type>(val[0], endian);
|
||||
val[1] = byte_swap<value_type>(val[1], endian);
|
||||
|
||||
// Shift bits from the lower value into place.
|
||||
make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
|
||||
@@ -165,7 +168,7 @@ inline void writeAtBitAlignment(void *memory, value_type value,
|
||||
uint64_t startBit) {
|
||||
assert(startBit < 8);
|
||||
if (startBit == 0)
|
||||
write<value_type, endian, alignment>(memory, value);
|
||||
write<value_type, alignment>(memory, value, endian);
|
||||
else {
|
||||
// Read two values and shift the result into them.
|
||||
value_type val[2];
|
||||
@@ -173,8 +176,8 @@ inline void writeAtBitAlignment(void *memory, value_type value,
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type) * 2);
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
val[0] = byte_swap<value_type>(val[0], endian);
|
||||
val[1] = byte_swap<value_type>(val[1], endian);
|
||||
|
||||
// Mask off any existing bits in the upper part of the lower value that
|
||||
// we want to replace.
|
||||
@@ -202,8 +205,8 @@ inline void writeAtBitAlignment(void *memory, value_type value,
|
||||
val[1] |= upperVal;
|
||||
|
||||
// Finally, rewrite values.
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
val[0] = byte_swap<value_type>(val[0], endian);
|
||||
val[1] = byte_swap<value_type>(val[1], endian);
|
||||
memcpy(LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
&val[0], sizeof(value_type) * 2);
|
||||
@@ -225,14 +228,15 @@ struct packed_endian_specific_integral {
|
||||
|
||||
explicit packed_endian_specific_integral(value_type val) { *this = val; }
|
||||
|
||||
operator value_type() const {
|
||||
return endian::read<value_type, endian, alignment>(
|
||||
(const void*)Value.buffer);
|
||||
value_type value() const {
|
||||
return endian::read<value_type, alignment>((const void *)Value.buffer,
|
||||
endian);
|
||||
}
|
||||
operator value_type() const { return value(); }
|
||||
|
||||
void operator=(value_type newValue) {
|
||||
endian::write<value_type, endian, alignment>(
|
||||
(void*)Value.buffer, newValue);
|
||||
endian::write<value_type, alignment>((void *)Value.buffer, newValue,
|
||||
endian);
|
||||
}
|
||||
|
||||
packed_endian_specific_integral &operator+=(value_type newValue) {
|
||||
@@ -265,11 +269,11 @@ public:
|
||||
explicit ref(void *Ptr) : Ptr(Ptr) {}
|
||||
|
||||
operator value_type() const {
|
||||
return endian::read<value_type, endian, alignment>(Ptr);
|
||||
return endian::read<value_type, alignment>(Ptr, endian);
|
||||
}
|
||||
|
||||
void operator=(value_type NewValue) {
|
||||
endian::write<value_type, endian, alignment>(Ptr, NewValue);
|
||||
endian::write<value_type, alignment>(Ptr, NewValue, endian);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -279,6 +283,9 @@ public:
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
using ulittle8_t =
|
||||
detail::packed_endian_specific_integral<uint8_t, wpi::util::endianness::little,
|
||||
unaligned>;
|
||||
using ulittle16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, wpi::util::endianness::little,
|
||||
unaligned>;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef WPIUTIL_WPI_ERRNO_H
|
||||
#define WPIUTIL_WPI_ERRNO_H
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <cerrno>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -20,59 +20,81 @@
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
/// An error handler callback.
|
||||
typedef void (*fatal_error_handler_t)(void *user_data,
|
||||
const char *reason,
|
||||
bool gen_crash_diag);
|
||||
/// An error handler callback.
|
||||
using fatal_error_handler_t = void (*)(void *user_data, const char *reason,
|
||||
bool gen_crash_diag);
|
||||
|
||||
/// install_fatal_error_handler - Installs a new error handler to be used
|
||||
/// whenever a serious (non-recoverable) error is encountered by LLVM.
|
||||
///
|
||||
/// If no error handler is installed the default is to print the error message
|
||||
/// to stderr, and call exit(1). If an error handler is installed then it is
|
||||
/// the handler's responsibility to log the message, it will no longer be
|
||||
/// printed to stderr. If the error handler returns, then exit(1) will be
|
||||
/// called.
|
||||
///
|
||||
/// It is dangerous to naively use an error handler which throws an exception.
|
||||
/// Even though some applications desire to gracefully recover from arbitrary
|
||||
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
|
||||
/// achieve this.
|
||||
///
|
||||
/// \param user_data - An argument which will be passed to the install error
|
||||
/// handler.
|
||||
void install_fatal_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
|
||||
/// Restores default error handling behaviour.
|
||||
void remove_fatal_error_handler();
|
||||
|
||||
/// ScopedFatalErrorHandler - This is a simple helper class which just
|
||||
/// calls install_fatal_error_handler in its constructor and
|
||||
/// remove_fatal_error_handler in its destructor.
|
||||
struct ScopedFatalErrorHandler {
|
||||
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr) {
|
||||
install_fatal_error_handler(handler, user_data);
|
||||
}
|
||||
|
||||
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
|
||||
};
|
||||
|
||||
/// Reports a serious error, calling any installed error handler. These
|
||||
/// functions are intended to be used for error conditions which are outside
|
||||
/// the control of the compiler (I/O errors, invalid user input, etc.)
|
||||
/// install_fatal_error_handler - Installs a new error handler to be used
|
||||
/// whenever a serious (non-recoverable) error is encountered by LLVM.
|
||||
///
|
||||
/// If no error handler is installed the default is to print the message to
|
||||
/// standard error, followed by a newline.
|
||||
/// After the error handler is called this function will call abort(), it
|
||||
/// does not return.
|
||||
[[noreturn]] void report_fatal_error(const char *reason,
|
||||
bool gen_crash_diag = true);
|
||||
[[noreturn]] void report_fatal_error(const std::string &reason,
|
||||
bool gen_crash_diag = true);
|
||||
[[noreturn]] void report_fatal_error(std::string_view reason,
|
||||
bool gen_crash_diag = true);
|
||||
/// If no error handler is installed the default is to print the error message
|
||||
/// to stderr, and call exit(1). If an error handler is installed then it is
|
||||
/// the handler's responsibility to log the message, it will no longer be
|
||||
/// printed to stderr. If the error handler returns, then exit(1) will be
|
||||
/// called.
|
||||
///
|
||||
/// It is dangerous to naively use an error handler which throws an exception.
|
||||
/// Even though some applications desire to gracefully recover from arbitrary
|
||||
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
|
||||
/// achieve this.
|
||||
///
|
||||
/// \param user_data - An argument which will be passed to the install error
|
||||
/// handler.
|
||||
LLVM_ABI void install_fatal_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
|
||||
/// Restores default error handling behaviour.
|
||||
LLVM_ABI void remove_fatal_error_handler();
|
||||
|
||||
/// ScopedFatalErrorHandler - This is a simple helper class which just
|
||||
/// calls install_fatal_error_handler in its constructor and
|
||||
/// remove_fatal_error_handler in its destructor.
|
||||
struct ScopedFatalErrorHandler {
|
||||
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr) {
|
||||
install_fatal_error_handler(handler, user_data);
|
||||
}
|
||||
|
||||
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
|
||||
};
|
||||
|
||||
/// @deprecated Use reportFatalInternalError() or reportFatalUsageError()
|
||||
/// instead.
|
||||
[[noreturn]] LLVM_ABI void report_fatal_error(const char *reason,
|
||||
bool gen_crash_diag = true);
|
||||
[[noreturn]] LLVM_ABI void report_fatal_error(const std::string& reason,
|
||||
bool gen_crash_diag = true);
|
||||
[[noreturn]] LLVM_ABI void report_fatal_error(std::string_view reason,
|
||||
bool gen_crash_diag = true);
|
||||
|
||||
/// Report a fatal error that likely indicates a bug in LLVM. It serves a
|
||||
/// similar purpose as an assertion, but is always enabled, regardless of the
|
||||
/// value of NDEBUG.
|
||||
///
|
||||
/// This will call installed error handlers (or print the message by default)
|
||||
/// and then abort. This will produce a crash trace and *will* ask users to
|
||||
/// report an LLVM bug.
|
||||
[[noreturn]] LLVM_ABI void reportFatalInternalError(const char *reason);
|
||||
[[noreturn]] LLVM_ABI void reportFatalInternalError(const std::string& reason);
|
||||
[[noreturn]] LLVM_ABI void reportFatalInternalError(std::string_view reason);
|
||||
|
||||
/// Report a fatal error that does not indicate a bug in LLVM.
|
||||
///
|
||||
/// This can be used in contexts where a proper error reporting mechanism
|
||||
/// (such as Error/Expected or DiagnosticInfo) is currently not supported, and
|
||||
/// would be too involved to introduce at the moment.
|
||||
///
|
||||
/// Examples where this function should be used instead of
|
||||
/// reportFatalInternalError() include invalid inputs or options, but also
|
||||
/// environment error conditions outside LLVM's control. It should also be used
|
||||
/// for known unsupported/unimplemented functionality.
|
||||
///
|
||||
/// This will call installed error handlers (or print the message by default)
|
||||
/// and then exit with code 1. It will not produce a crash trace and will
|
||||
/// *not* ask users to report an LLVM bug.
|
||||
[[noreturn]] LLVM_ABI void reportFatalUsageError(const char *reason);
|
||||
[[noreturn]] LLVM_ABI void reportFatalUsageError(const std::string& reason);
|
||||
[[noreturn]] LLVM_ABI void reportFatalUsageError(std::string_view reason);
|
||||
|
||||
/// Installs a new bad alloc error handler that should be used whenever a
|
||||
/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM.
|
||||
@@ -90,13 +112,13 @@ namespace wpi::util {
|
||||
///
|
||||
/// \param user_data - An argument which will be passed to the installed error
|
||||
/// handler.
|
||||
void install_bad_alloc_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
LLVM_ABI void install_bad_alloc_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
|
||||
/// Restores default bad alloc error handling behavior.
|
||||
void remove_bad_alloc_error_handler();
|
||||
LLVM_ABI void remove_bad_alloc_error_handler();
|
||||
|
||||
void install_out_of_memory_new_handler();
|
||||
LLVM_ABI void install_out_of_memory_new_handler();
|
||||
|
||||
/// Reports a bad alloc error, calling any user defined bad alloc
|
||||
/// error handler. In contrast to the generic 'report_fatal_error'
|
||||
@@ -110,16 +132,16 @@ void install_out_of_memory_new_handler();
|
||||
/// If no error handler is installed (default), throws a bad_alloc exception
|
||||
/// if LLVM is compiled with exception support. Otherwise prints the error
|
||||
/// to standard error and calls abort().
|
||||
[[noreturn]] void report_bad_alloc_error(const char *Reason,
|
||||
bool GenCrashDiag = true);
|
||||
[[noreturn]] LLVM_ABI void report_bad_alloc_error(const char *Reason,
|
||||
bool GenCrashDiag = true);
|
||||
|
||||
/// This function calls abort(), and prints the optional message to stderr.
|
||||
/// Use the wpi_unreachable macro (that adds location info), instead of
|
||||
/// calling this function directly.
|
||||
[[noreturn]] void
|
||||
wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
|
||||
unsigned line = 0);
|
||||
}
|
||||
[[noreturn]] LLVM_ABI void wpi_unreachable_internal(const char *msg = nullptr,
|
||||
const char *file = nullptr,
|
||||
unsigned line = 0);
|
||||
} // namespace wpi::util
|
||||
|
||||
/// Marks that the current location is not supposed to be reachable.
|
||||
/// In !NDEBUG builds, prints the message and location info to stderr.
|
||||
@@ -139,7 +161,7 @@ wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
|
||||
/// diagnostics for unreachable code paths, and allows compilers to omit
|
||||
/// unnecessary code.
|
||||
#ifndef NDEBUG
|
||||
#define wpi_unreachable(msg) \
|
||||
#define wpi_unreachable(msg) \
|
||||
::wpi::util::wpi_unreachable_internal(msg, __FILE__, __LINE__)
|
||||
#elif !defined(LLVM_BUILTIN_UNREACHABLE)
|
||||
#define wpi_unreachable(msg) ::wpi::util::wpi_unreachable_internal()
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "wpi/util/MemAlloc.hpp"
|
||||
#include "wpi/util/type_traits.hpp"
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace wpi::util {
|
||||
@@ -65,10 +64,6 @@ template <typename FunctionT> class unique_function;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using EnableIfTrivial =
|
||||
std::enable_if_t<std::is_trivially_move_constructible<T>::value &&
|
||||
std::is_trivially_destructible<T>::value>;
|
||||
template <typename CallableT, typename ThisT>
|
||||
using EnableUnlessSameType =
|
||||
std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
|
||||
@@ -89,13 +84,6 @@ protected:
|
||||
static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
|
||||
static constexpr size_t InlineStorageAlign = alignof(void *);
|
||||
|
||||
template <typename T, class = void>
|
||||
struct IsSizeLessThanThresholdT : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsSizeLessThanThresholdT<
|
||||
T, std::enable_if_t<sizeof(T) <= 2 * sizeof(void *)>> : std::true_type {};
|
||||
|
||||
// Provide a type function to map parameters that won't observe extra copies
|
||||
// or moves and which are small enough to likely pass in register to values
|
||||
// and all other types to l-value reference types. We use this to compute the
|
||||
@@ -108,10 +96,12 @@ protected:
|
||||
template <typename T> struct AdjustedParamTBase {
|
||||
static_assert(!std::is_reference<T>::value,
|
||||
"references should be handled by template specialization");
|
||||
static constexpr bool IsSizeLessThanThreshold =
|
||||
sizeof(T) <= 2 * sizeof(void *);
|
||||
using type =
|
||||
std::conditional_t<std::is_trivially_copy_constructible<T>::value &&
|
||||
std::is_trivially_move_constructible<T>::value &&
|
||||
IsSizeLessThanThresholdT<T>::value,
|
||||
IsSizeLessThanThreshold,
|
||||
T, T &>;
|
||||
};
|
||||
|
||||
@@ -242,22 +232,22 @@ protected:
|
||||
|
||||
// The pointers to call/move/destroy functions are determined for each
|
||||
// callable type (and called-as type, which determines the overload chosen).
|
||||
// (definitions are out-of-line).
|
||||
|
||||
// By default, we need an object that contains all the different
|
||||
// type erased behaviors needed. Create a static instance of the struct type
|
||||
// here and each instance will contain a pointer to it.
|
||||
// Wrap in a struct to avoid https://gcc.gnu.org/PR71954
|
||||
template <typename CallableT, typename CalledAs, typename Enable = void>
|
||||
struct CallbacksHolder {
|
||||
static NonTrivialCallbacks Callbacks;
|
||||
};
|
||||
// See if we can create a trivial callback. We need the callable to be
|
||||
// trivially moved and trivially destroyed so that we don't have to store
|
||||
// type erased callbacks for those operations.
|
||||
template <typename CallableT, typename CalledAs>
|
||||
struct CallbacksHolder<CallableT, CalledAs, EnableIfTrivial<CallableT>> {
|
||||
static TrivialCallback Callbacks;
|
||||
template <typename CallableT, typename CalledAs> struct CallbacksHolder {
|
||||
inline static auto Callbacks = []() constexpr {
|
||||
// For trivial callables, we don't need to store move and destroy
|
||||
// callbacks.
|
||||
if constexpr (std::is_trivially_move_constructible_v<CallableT> &&
|
||||
std::is_trivially_destructible_v<CallableT>)
|
||||
return TrivialCallback{&CallImpl<CalledAs>};
|
||||
else
|
||||
return NonTrivialCallbacks{&CallImpl<CalledAs>, &MoveImpl<CallableT>,
|
||||
&DestroyImpl<CallableT>};
|
||||
}();
|
||||
};
|
||||
|
||||
// A simple tag type so the call-as type to be passed to the constructor.
|
||||
@@ -355,19 +345,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename R, typename... P>
|
||||
template <typename CallableT, typename CalledAsT, typename Enable>
|
||||
typename UniqueFunctionBase<R, P...>::NonTrivialCallbacks UniqueFunctionBase<
|
||||
R, P...>::CallbacksHolder<CallableT, CalledAsT, Enable>::Callbacks = {
|
||||
&CallImpl<CalledAsT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
|
||||
|
||||
template <typename R, typename... P>
|
||||
template <typename CallableT, typename CalledAsT>
|
||||
typename UniqueFunctionBase<R, P...>::TrivialCallback
|
||||
UniqueFunctionBase<R, P...>::CallbacksHolder<
|
||||
CallableT, CalledAsT, EnableIfTrivial<CallableT>>::Callbacks{
|
||||
&CallImpl<CalledAsT>};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename R, typename... P>
|
||||
|
||||
42
wpiutil/src/main/native/thirdparty/llvm/include/wpi/util/IOSandbox.hpp
vendored
Normal file
42
wpiutil/src/main/native/thirdparty/llvm/include/wpi/util/IOSandbox.hpp
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
//===- IOSandbox.h ----------------------------------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_IOSANDBOX_H
|
||||
#define WPIUTIL_WPI_IOSANDBOX_H
|
||||
|
||||
#if defined(LLVM_ENABLE_IO_SANDBOX) && LLVM_ENABLE_IO_SANDBOX
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "wpi/util/ErrorHandling.hpp"
|
||||
#include "wpi/util/SaveAndRestore.hpp"
|
||||
|
||||
namespace wpi::util::sys::sandbox {
|
||||
inline LLVM_THREAD_LOCAL bool Enabled = false;
|
||||
struct ScopedSetting {
|
||||
SaveAndRestore<bool> Impl;
|
||||
};
|
||||
inline ScopedSetting scopedEnable() { return {{Enabled, true}}; }
|
||||
inline ScopedSetting scopedDisable() { return {{Enabled, false}}; }
|
||||
inline void violationIfEnabled() {
|
||||
if (Enabled)
|
||||
reportFatalInternalError("IO sandbox violation");
|
||||
}
|
||||
} // namespace wpi::util::sys::sandbox
|
||||
|
||||
#else
|
||||
|
||||
namespace wpi::util::sys::sandbox {
|
||||
struct [[maybe_unused]] ScopedSetting {};
|
||||
inline ScopedSetting scopedEnable() { return {}; }
|
||||
inline ScopedSetting scopedDisable() { return {}; }
|
||||
inline void violationIfEnabled() {}
|
||||
} // namespace wpi::util::sys::sandbox
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef WPIUTIL_WPI_MATHEXTRAS_H
|
||||
#define WPIUTIL_WPI_MATHEXTRAS_H
|
||||
|
||||
#include "wpi/util/STLForwardCompat.hpp"
|
||||
#include "wpi/util/bit.hpp"
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <bit>
|
||||
@@ -43,7 +44,7 @@ using common_sint =
|
||||
|
||||
/// Create a bitmask with the N right-most bits set to 1, and all other
|
||||
/// bits set to 0. Only unsigned types are allowed.
|
||||
template <typename T> T maskTrailingOnes(unsigned N) {
|
||||
template <typename T> constexpr T maskTrailingOnes(unsigned N) {
|
||||
static_assert(std::is_unsigned_v<T>, "Invalid type!");
|
||||
const unsigned Bits = CHAR_BIT * sizeof(T);
|
||||
assert(N <= Bits && "Invalid bit index");
|
||||
@@ -54,19 +55,19 @@ template <typename T> T maskTrailingOnes(unsigned N) {
|
||||
|
||||
/// Create a bitmask with the N left-most bits set to 1, and all other
|
||||
/// bits set to 0. Only unsigned types are allowed.
|
||||
template <typename T> T maskLeadingOnes(unsigned N) {
|
||||
template <typename T> constexpr T maskLeadingOnes(unsigned N) {
|
||||
return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N right-most bits set to 0, and all other
|
||||
/// bits set to 1. Only unsigned types are allowed.
|
||||
template <typename T> T maskTrailingZeros(unsigned N) {
|
||||
template <typename T> constexpr T maskTrailingZeros(unsigned N) {
|
||||
return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N left-most bits set to 0, and all other
|
||||
/// bits set to 1. Only unsigned types are allowed.
|
||||
template <typename T> T maskLeadingZeros(unsigned N) {
|
||||
template <typename T> constexpr T maskLeadingZeros(unsigned N) {
|
||||
return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ static const unsigned char BitReverseTable256[256] = {
|
||||
};
|
||||
|
||||
/// Reverse the bits in \p Val.
|
||||
template <typename T> T reverseBits(T Val) {
|
||||
template <typename T> constexpr T reverseBits(T Val) {
|
||||
#if __has_builtin(__builtin_bitreverse8)
|
||||
if constexpr (std::is_same_v<T, uint8_t>)
|
||||
return __builtin_bitreverse8(Val);
|
||||
@@ -156,16 +157,8 @@ constexpr bool isShiftedInt(int64_t x) {
|
||||
|
||||
/// Checks if an unsigned integer fits into the given bit width.
|
||||
template <unsigned N> constexpr bool isUInt(uint64_t x) {
|
||||
if constexpr (N == 0)
|
||||
return 0 == x;
|
||||
if constexpr (N == 8)
|
||||
return static_cast<uint8_t>(x) == x;
|
||||
if constexpr (N == 16)
|
||||
return static_cast<uint16_t>(x) == x;
|
||||
if constexpr (N == 32)
|
||||
return static_cast<uint32_t>(x) == x;
|
||||
if constexpr (N < 64)
|
||||
return x < (UINT64_C(1) << (N));
|
||||
return (x >> N) == 0;
|
||||
(void)x; // MSVC v19.25 warns that x is unused.
|
||||
return true;
|
||||
}
|
||||
@@ -181,7 +174,7 @@ constexpr bool isShiftedUInt(uint64_t x) {
|
||||
}
|
||||
|
||||
/// Gets the maximum value for a N-bit unsigned integer.
|
||||
inline uint64_t maxUIntN(uint64_t N) {
|
||||
inline constexpr uint64_t maxUIntN(uint64_t N) {
|
||||
assert(N <= 64 && "integer width out of range");
|
||||
|
||||
// uint64_t(1) << 64 is undefined behavior, so we can't do
|
||||
@@ -202,12 +195,12 @@ inline uint64_t maxUIntN(uint64_t N) {
|
||||
#endif
|
||||
|
||||
/// Gets the minimum value for a N-bit signed integer.
|
||||
inline int64_t minIntN(int64_t N) {
|
||||
inline constexpr int64_t minIntN(int64_t N) {
|
||||
assert(N >= 0 && N <= 64 && "integer width out of range");
|
||||
|
||||
if (N == 0)
|
||||
return 0;
|
||||
return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
|
||||
return UINT64_MAX << (N - 1);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -215,7 +208,7 @@ inline int64_t minIntN(int64_t N) {
|
||||
#endif
|
||||
|
||||
/// Gets the maximum value for a N-bit signed integer.
|
||||
inline int64_t maxIntN(int64_t N) {
|
||||
inline constexpr int64_t maxIntN(int64_t N) {
|
||||
assert(N >= 0 && N <= 64 && "integer width out of range");
|
||||
|
||||
// This relies on two's complement wraparound when N == 64, so we convert to
|
||||
@@ -226,12 +219,12 @@ inline int64_t maxIntN(int64_t N) {
|
||||
}
|
||||
|
||||
/// Checks if an unsigned integer fits into the given (dynamic) bit width.
|
||||
inline bool isUIntN(unsigned N, uint64_t x) {
|
||||
return N >= 64 || x <= maxUIntN(N);
|
||||
inline constexpr bool isUIntN(unsigned N, uint64_t x) {
|
||||
return N >= 64 || (x >> N) == 0;
|
||||
}
|
||||
|
||||
/// Checks if an signed integer fits into the given (dynamic) bit width.
|
||||
inline bool isIntN(unsigned N, int64_t x) {
|
||||
inline constexpr bool isIntN(unsigned N, int64_t x) {
|
||||
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
|
||||
}
|
||||
|
||||
@@ -300,13 +293,16 @@ inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
|
||||
|
||||
/// Compile time Log2.
|
||||
/// Valid only for positive powers of two.
|
||||
template <size_t kValue> constexpr size_t CTLog2() {
|
||||
static_assert(kValue > 0 && wpi::util::isPowerOf2_64(kValue),
|
||||
"Value is not a valid power of 2");
|
||||
return 1 + CTLog2<kValue / 2>();
|
||||
template <size_t kValue> constexpr size_t ConstantLog2() {
|
||||
static_assert(wpi::util::isPowerOf2_64(kValue), "Value is not a valid power of 2");
|
||||
return std::countr_zero(kValue);
|
||||
}
|
||||
|
||||
template <> constexpr size_t CTLog2<1>() { return 0; }
|
||||
template <size_t kValue>
|
||||
LLVM_DEPRECATED("Use ConstantLog2 instead", "ConstantLog2")
|
||||
constexpr size_t CTLog2() {
|
||||
return ConstantLog2<kValue>();
|
||||
}
|
||||
|
||||
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
|
||||
/// (32 bit edition.)
|
||||
@@ -568,6 +564,15 @@ inline int64_t SignExtend64(uint64_t X, unsigned B) {
|
||||
return int64_t(X << (64 - B)) >> (64 - B);
|
||||
}
|
||||
|
||||
/// Return the absolute value of a signed integer, converted to the
|
||||
/// corresponding unsigned integer type. Avoids undefined behavior in std::abs
|
||||
/// when you pass it INT_MIN or similar.
|
||||
template <typename T, typename U = std::make_unsigned_t<T>>
|
||||
constexpr U AbsoluteValue(T X) {
|
||||
// If X is negative, cast it to the unsigned type _before_ negating it.
|
||||
return X < 0 ? -static_cast<U>(X) : X;
|
||||
}
|
||||
|
||||
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
|
||||
/// value of the result.
|
||||
template <typename U, typename V, typename T = common_uint<U, V>>
|
||||
@@ -667,7 +672,7 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
|
||||
}
|
||||
|
||||
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
|
||||
extern const float huge_valf;
|
||||
LLVM_ABI extern const float huge_valf;
|
||||
|
||||
/// Add two signed integers, computing the two's complement truncated result,
|
||||
/// returning true if overflow occurred.
|
||||
@@ -696,7 +701,7 @@ std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
|
||||
}
|
||||
|
||||
/// Subtract two signed integers, computing the two's complement truncated
|
||||
/// result, returning true if an overflow ocurred.
|
||||
/// result, returning true if an overflow occurred.
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
|
||||
#if __has_builtin(__builtin_sub_overflow)
|
||||
@@ -722,7 +727,7 @@ std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
|
||||
}
|
||||
|
||||
/// Multiply two signed integers, computing the two's complement truncated
|
||||
/// result, returning true if an overflow ocurred.
|
||||
/// result, returning true if an overflow occurred.
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
|
||||
#if __has_builtin(__builtin_mul_overflow)
|
||||
|
||||
@@ -79,7 +79,7 @@ LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) {
|
||||
/// like posix_memalign due to portability. It is mostly intended to allow
|
||||
/// compatibility with platforms that, after aligned allocation was added, use
|
||||
/// reduced default alignment.
|
||||
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
|
||||
LLVM_ABI LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
|
||||
allocate_buffer(size_t Size, size_t Alignment);
|
||||
|
||||
/// Deallocate a buffer of memory with the given size and alignment.
|
||||
@@ -89,7 +89,7 @@ allocate_buffer(size_t Size, size_t Alignment);
|
||||
///
|
||||
/// The pointer must have been allocated with the corresponding new operator,
|
||||
/// most likely using the above helper.
|
||||
void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
|
||||
LLVM_ABI void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
|
||||
|
||||
} // namespace wpi::util
|
||||
|
||||
|
||||
@@ -173,15 +173,14 @@ struct PointerIntPairInfo {
|
||||
"PointerIntPair with integer size too large for pointer");
|
||||
enum MaskAndShiftConstants : uintptr_t {
|
||||
/// PointerBitMask - The bits that come from the pointer.
|
||||
PointerBitMask =
|
||||
~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
|
||||
PointerBitMask = (~(uintptr_t)0) << PtrTraits::NumLowBitsAvailable,
|
||||
|
||||
/// IntShift - The number of low bits that we reserve for other uses, and
|
||||
/// keep zero.
|
||||
IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
|
||||
|
||||
/// IntMask - This is the unshifted mask for valid bits of the int type.
|
||||
IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
|
||||
IntMask = ((uintptr_t)1 << IntBits) - 1,
|
||||
|
||||
// ShiftedIntMask - This is the bits for the integer shifted in place.
|
||||
ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
|
||||
@@ -206,11 +205,10 @@ struct PointerIntPairInfo {
|
||||
}
|
||||
|
||||
static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) {
|
||||
intptr_t IntWord = static_cast<intptr_t>(Int);
|
||||
assert((IntWord & ~IntMask) == 0 && "Integer too large for field");
|
||||
assert((Int & ~IntMask) == 0 && "Integer too large for field");
|
||||
|
||||
// Preserve all bits other than the ones we are updating.
|
||||
return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift;
|
||||
return (OrigValue & ~ShiftedIntMask) | Int << IntShift;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#ifndef WPIUTIL_WPI_POINTERLIKETYPETRAITS_H
|
||||
#define WPIUTIL_WPI_POINTERLIKETYPETRAITS_H
|
||||
|
||||
#include "wpi/util/MathExtras.hpp"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
@@ -25,12 +25,6 @@ namespace wpi::util {
|
||||
template <typename T> struct PointerLikeTypeTraits;
|
||||
|
||||
namespace detail {
|
||||
/// A tiny meta function to compute the log2 of a compile time constant.
|
||||
template <size_t N>
|
||||
struct ConstantLog2
|
||||
: std::integral_constant<size_t, ConstantLog2<N / 2>::value + 1> {};
|
||||
template <> struct ConstantLog2<1> : std::integral_constant<size_t, 0> {};
|
||||
|
||||
// Provide a trait to check if T is pointer-like.
|
||||
template <typename T, typename U = void> struct HasPointerLikeTypeTraits {
|
||||
static const bool value = false;
|
||||
@@ -57,8 +51,7 @@ template <typename T> struct PointerLikeTypeTraits<T *> {
|
||||
static inline void *getAsVoidPointer(T *P) { return P; }
|
||||
static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); }
|
||||
|
||||
static constexpr int NumLowBitsAvailable =
|
||||
detail::ConstantLog2<alignof(T)>::value;
|
||||
static constexpr int NumLowBitsAvailable = ConstantLog2<alignof(T)>();
|
||||
};
|
||||
|
||||
template <> struct PointerLikeTypeTraits<void *> {
|
||||
@@ -77,7 +70,7 @@ template <> struct PointerLikeTypeTraits<void *> {
|
||||
|
||||
// Provide PointerLikeTypeTraits for const things.
|
||||
template <typename T> struct PointerLikeTypeTraits<const T> {
|
||||
typedef PointerLikeTypeTraits<T> NonConst;
|
||||
using NonConst = PointerLikeTypeTraits<T>;
|
||||
|
||||
static inline const void *getAsVoidPointer(const T P) {
|
||||
return NonConst::getAsVoidPointer(P);
|
||||
@@ -90,7 +83,7 @@ template <typename T> struct PointerLikeTypeTraits<const T> {
|
||||
|
||||
// Provide PointerLikeTypeTraits for const pointers.
|
||||
template <typename T> struct PointerLikeTypeTraits<const T *> {
|
||||
typedef PointerLikeTypeTraits<T *> NonConst;
|
||||
using NonConst = PointerLikeTypeTraits<T *>;
|
||||
|
||||
static inline const void *getAsVoidPointer(const T *P) {
|
||||
return NonConst::getAsVoidPointer(const_cast<T *>(P));
|
||||
@@ -123,8 +116,7 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
|
||||
/// potentially use alignment attributes on functions to satisfy that.
|
||||
template <int Alignment, typename FunctionPointerT>
|
||||
struct FunctionPointerLikeTypeTraits {
|
||||
static constexpr int NumLowBitsAvailable =
|
||||
detail::ConstantLog2<Alignment>::value;
|
||||
static constexpr int NumLowBitsAvailable = ConstantLog2<Alignment>();
|
||||
static inline void *getAsVoidPointer(FunctionPointerT P) {
|
||||
assert((reinterpret_cast<uintptr_t>(P) &
|
||||
~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&
|
||||
|
||||
@@ -76,18 +76,13 @@ namespace pointer_union_detail {
|
||||
/// Determine the number of bits required to store integers with values < n.
|
||||
/// This is ceil(log2(n)).
|
||||
constexpr int bitsRequired(unsigned n) {
|
||||
return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0;
|
||||
return n == 0 ? 0 : std::bit_width(n - 1);
|
||||
}
|
||||
|
||||
template <typename... Ts> constexpr int lowBitsAvailable() {
|
||||
return std::min<int>({PointerLikeTypeTraits<Ts>::NumLowBitsAvailable...});
|
||||
}
|
||||
|
||||
/// Find the first type in a list of types.
|
||||
template <typename T, typename...> struct GetFirstType {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
/// Provide PointerLikeTypeTraits for void* that is used by PointerUnion
|
||||
/// for the template arguments.
|
||||
template <typename ...PTs> class PointerUnionUIntTraits {
|
||||
@@ -133,9 +128,6 @@ namespace pointer_union_detail {
|
||||
};
|
||||
}
|
||||
|
||||
// This is a forward declaration of CastInfoPointerUnionImpl
|
||||
// Refer to its definition below for further details
|
||||
template <typename... PTs> struct CastInfoPointerUnionImpl;
|
||||
/// A discriminated union of two or more pointer types, with the discriminator
|
||||
/// in the low bit of the pointer.
|
||||
///
|
||||
@@ -171,10 +163,12 @@ class PointerUnion
|
||||
using First = TypeAtIndex<0, PTs...>;
|
||||
using Base = typename PointerUnion::PointerUnionMembers;
|
||||
|
||||
/// This is needed to give the CastInfo implementation below access
|
||||
/// to protected members.
|
||||
/// Refer to its definition for further details.
|
||||
friend struct CastInfoPointerUnionImpl<PTs...>;
|
||||
// Give the CastInfo specialization below access to protected members.
|
||||
//
|
||||
// This makes all of CastInfo a friend, which is more than strictly
|
||||
// necessary. It's a workaround for C++'s inability to friend a
|
||||
// partial template specialization.
|
||||
template <typename To, typename From, typename Enable> friend struct CastInfo;
|
||||
|
||||
public:
|
||||
PointerUnion() = default;
|
||||
@@ -264,42 +258,21 @@ bool operator<(PointerUnion<PTs...> lhs, PointerUnion<PTs...> rhs) {
|
||||
return lhs.getOpaqueValue() < rhs.getOpaqueValue();
|
||||
}
|
||||
|
||||
/// We can't (at least, at this moment with C++14) declare CastInfo
|
||||
/// as a friend of PointerUnion like this:
|
||||
/// ```
|
||||
/// template<typename To>
|
||||
/// friend struct CastInfo<To, PointerUnion<PTs...>>;
|
||||
/// ```
|
||||
/// The compiler complains 'Partial specialization cannot be declared as a
|
||||
/// friend'.
|
||||
/// So we define this struct to be a bridge between CastInfo and
|
||||
/// PointerUnion.
|
||||
template <typename... PTs> struct CastInfoPointerUnionImpl {
|
||||
using From = PointerUnion<PTs...>;
|
||||
|
||||
template <typename To> static inline bool isPossible(From &F) {
|
||||
return F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
|
||||
}
|
||||
|
||||
template <typename To> static To doCast(From &F) {
|
||||
assert(isPossible<To>(F) && "cast to an incompatible type!");
|
||||
return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of CastInfo for PointerUnion
|
||||
template <typename To, typename... PTs>
|
||||
struct CastInfo<To, PointerUnion<PTs...>>
|
||||
: public DefaultDoCastIfPossible<To, PointerUnion<PTs...>,
|
||||
CastInfo<To, PointerUnion<PTs...>>> {
|
||||
using From = PointerUnion<PTs...>;
|
||||
using Impl = CastInfoPointerUnionImpl<PTs...>;
|
||||
|
||||
static inline bool isPossible(From &f) {
|
||||
return Impl::template isPossible<To>(f);
|
||||
return f.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
|
||||
}
|
||||
|
||||
static To doCast(From &f) { return Impl::template doCast<To>(f); }
|
||||
static To doCast(From &f) {
|
||||
assert(isPossible(f) && "cast to an incompatible type!");
|
||||
return PointerLikeTypeTraits<To>::getFromVoidPointer(f.Val.getPointer());
|
||||
}
|
||||
|
||||
static inline To castFailed() { return To(); }
|
||||
};
|
||||
@@ -331,8 +304,7 @@ struct PointerLikeTypeTraits<PointerUnion<PTs...>> {
|
||||
// Teach DenseMap how to use PointerUnions as keys.
|
||||
template <typename ...PTs> struct DenseMapInfo<PointerUnion<PTs...>> {
|
||||
using Union = PointerUnion<PTs...>;
|
||||
using FirstInfo =
|
||||
DenseMapInfo<typename pointer_union_detail::GetFirstType<PTs...>::type>;
|
||||
using FirstInfo = DenseMapInfo<TypeAtIndex<0, PTs...>>;
|
||||
|
||||
static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); }
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
template<class T = void *>
|
||||
bool shouldReverseIterate() {
|
||||
template <class T = void *> constexpr bool shouldReverseIterate() {
|
||||
#if LLVM_ENABLE_REVERSE_ITERATION
|
||||
return detail::IsPointerLike<T>::value;
|
||||
#else
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
@@ -26,6 +27,54 @@ namespace wpi::util {
|
||||
// Features from C++20
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace numbers {
|
||||
// clang-format off
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T e_v = T(0x1.5bf0a8b145769P+1); // (2.7182818284590452354) https://oeis.org/A001113
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T egamma_v = T(0x1.2788cfc6fb619P-1); // (.57721566490153286061) https://oeis.org/A001620
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T ln2_v = T(0x1.62e42fefa39efP-1); // (.69314718055994530942) https://oeis.org/A002162
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T ln10_v = T(0x1.26bb1bbb55516P+1); // (2.3025850929940456840) https://oeis.org/A002392
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T log2e_v = T(0x1.71547652b82feP+0); // (1.4426950408889634074)
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T log10e_v = T(0x1.bcb7b1526e50eP-2); // (.43429448190325182765)
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T pi_v = T(0x1.921fb54442d18P+1); // (3.1415926535897932385) https://oeis.org/A000796
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T inv_pi_v = T(0x1.45f306dc9c883P-2); // (.31830988618379067154) https://oeis.org/A049541
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T inv_sqrtpi_v = T(0x1.20dd750429b6dP-1); // (.56418958354775628695) https://oeis.org/A087197
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T sqrt2_v = T(0x1.6a09e667f3bcdP+0); // (1.4142135623730950488) https://oeis.org/A00219
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T inv_sqrt2_v = T(0x1.6a09e667f3bcdP-1); // (.70710678118654752440)
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T sqrt3_v = T(0x1.bb67ae8584caaP+0); // (1.7320508075688772935) https://oeis.org/A002194
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T inv_sqrt3_v = T(0x1.279a74590331cP-1); // (.57735026918962576451)
|
||||
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
inline constexpr T phi_v = T(0x1.9e3779b97f4a8P+0); // (1.6180339887498948482) https://oeis.org/A001622
|
||||
|
||||
inline constexpr double e = e_v<double>;
|
||||
inline constexpr double egamma = egamma_v<double>;
|
||||
inline constexpr double ln2 = ln2_v<double>;
|
||||
inline constexpr double ln10 = ln10_v<double>;
|
||||
inline constexpr double log2e = log2e_v<double>;
|
||||
inline constexpr double log10e = log10e_v<double>;
|
||||
inline constexpr double pi = pi_v<double>;
|
||||
inline constexpr double inv_pi = inv_pi_v<double>;
|
||||
inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
|
||||
inline constexpr double sqrt2 = sqrt2_v<double>;
|
||||
inline constexpr double inv_sqrt2 = inv_sqrt2_v<double>;
|
||||
inline constexpr double sqrt3 = sqrt3_v<double>;
|
||||
inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
|
||||
inline constexpr double phi = phi_v<double>;
|
||||
// clang-format on
|
||||
} // namespace numbers
|
||||
|
||||
template <typename T>
|
||||
struct remove_cvref // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
@@ -36,27 +85,81 @@ template <typename T>
|
||||
using remove_cvref_t // NOLINT(readability-identifier-naming)
|
||||
= typename wpi::util::remove_cvref<T>::type;
|
||||
|
||||
// TODO: Remove this in favor of std::type_identity<T> once we switch to C++20.
|
||||
template <typename T>
|
||||
struct type_identity // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
// TODO: Remove this in favor of std::type_identity_t<T> once we switch to
|
||||
// C++20.
|
||||
template <typename T>
|
||||
using type_identity_t // NOLINT(readability-identifier-naming)
|
||||
= typename wpi::util::type_identity<T>::type;
|
||||
|
||||
namespace detail {
|
||||
template <class, template <class...> class Op, class... Args> struct detector {
|
||||
using value_t = std::false_type;
|
||||
};
|
||||
template <template <class...> class Op, class... Args>
|
||||
struct detector<std::void_t<Op<Args...>>, Op, Args...> {
|
||||
using value_t = std::true_type;
|
||||
};
|
||||
} // end namespace detail
|
||||
|
||||
/// Detects if a given trait holds for some set of arguments 'Args'.
|
||||
/// For example, the given trait could be used to detect if a given type
|
||||
/// has a copy assignment operator:
|
||||
/// template<class T>
|
||||
/// using has_copy_assign_t = decltype(std::declval<T&>()
|
||||
/// = std::declval<const T&>());
|
||||
/// bool fooHasCopyAssign = is_detected<has_copy_assign_t, FooClass>::value;
|
||||
///
|
||||
/// NOTE: The C++20 standard has adopted concepts and requires clauses as a
|
||||
/// superior alternative to std::is_detected.
|
||||
///
|
||||
/// This utility is placed in STLForwardCompat.h as a reminder
|
||||
/// to migrate usages of wpi::util::is_detected to concepts and 'requires'
|
||||
/// clauses when the codebase adopts C++20.
|
||||
template <template <class...> class Op, class... Args>
|
||||
using is_detected = typename detail::detector<void, Op, Args...>::value_t;
|
||||
|
||||
struct identity // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
template <typename T> constexpr T &&operator()(T &&self) const noexcept {
|
||||
return std::forward<T>(self);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns a raw pointer that represents the same address as the argument.
|
||||
///
|
||||
/// This implementation can be removed once we move to C++20 where it's defined
|
||||
/// as std::to_address().
|
||||
///
|
||||
/// The std::pointer_traits<>::to_address(p) variations of these overloads has
|
||||
/// not been implemented.
|
||||
template <class Ptr> auto to_address(const Ptr &P) { return P.operator->(); }
|
||||
template <class T> constexpr T *to_address(T *P) {
|
||||
static_assert(!std::is_function_v<T>);
|
||||
return P;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Features from C++23
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
|
||||
// C++23.
|
||||
template <typename T, typename Function>
|
||||
auto transformOptional(const std::optional<T> &O, const Function &F)
|
||||
-> std::optional<decltype(F(*O))> {
|
||||
if (O)
|
||||
return F(*O);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
|
||||
// C++23.
|
||||
template <typename T, typename Function>
|
||||
auto transformOptional(std::optional<T> &&O, const Function &F)
|
||||
-> std::optional<decltype(F(*std::move(O)))> {
|
||||
if (O)
|
||||
return F(*std::move(O));
|
||||
template <typename Optional, typename Function,
|
||||
typename Value = typename wpi::util::remove_cvref_t<Optional>::value_type>
|
||||
std::optional<std::invoke_result_t<Function, Value>>
|
||||
transformOptional(Optional &&O, Function &&F) {
|
||||
if (O) {
|
||||
return F(*std::forward<Optional>(O));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -67,6 +170,11 @@ template <typename Enum>
|
||||
return static_cast<std::underlying_type_t<Enum>>(E);
|
||||
}
|
||||
|
||||
// A tag for constructors accepting ranges.
|
||||
struct from_range_t {
|
||||
explicit from_range_t() = default;
|
||||
};
|
||||
inline constexpr from_range_t from_range{};
|
||||
} // namespace wpi::util
|
||||
|
||||
#endif // WPIUTIL_WPI_STLFORWARDCOMPAT_H
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
#ifndef WPIUTIL_WPI_SMALLPTRSET_H
|
||||
#define WPIUTIL_WPI_SMALLPTRSET_H
|
||||
|
||||
#include "wpi/util/ADL.hpp"
|
||||
#include "wpi/util/EpochTracker.hpp"
|
||||
#include "wpi/util/STLForwardCompat.hpp"
|
||||
#include "wpi/util/iterator_range.hpp"
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "wpi/util/MathExtras.hpp"
|
||||
#include "wpi/util/ReverseIteration.hpp"
|
||||
#include "wpi/util/type_traits.hpp"
|
||||
@@ -43,7 +47,7 @@ namespace wpi::util {
|
||||
/// sets are often small. In this case, no memory allocation is used, and only
|
||||
/// light-weight and cache-efficient scanning is used.
|
||||
///
|
||||
/// Large sets use a classic exponentially-probed hash table. Empty buckets are
|
||||
/// Large sets use a classic quadratically-probed hash table. Empty buckets are
|
||||
/// represented with an illegal pointer value (-1) to allow null pointers to be
|
||||
/// inserted. Tombstones are represented with another illegal pointer value
|
||||
/// (-2), to allow deletion. The hash table is resized when the table is 3/4 or
|
||||
@@ -58,25 +62,26 @@ protected:
|
||||
/// CurArraySize - The allocated size of CurArray, always a power of two.
|
||||
unsigned CurArraySize;
|
||||
|
||||
/// Number of elements in CurArray that contain a value or are a tombstone.
|
||||
/// Number of elements in CurArray that contain a value.
|
||||
/// If small, all these elements are at the beginning of CurArray and the rest
|
||||
/// is uninitialized.
|
||||
unsigned NumNonEmpty;
|
||||
unsigned NumEntries;
|
||||
/// Number of tombstones in CurArray.
|
||||
unsigned NumTombstones;
|
||||
/// Whether the set is in small representation.
|
||||
bool IsSmall;
|
||||
|
||||
// Helpers to copy and move construct a SmallPtrSet.
|
||||
SmallPtrSetImplBase(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &that);
|
||||
SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage, SmallPtrSetImplBase &&that);
|
||||
LLVM_ABI SmallPtrSetImplBase(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &that);
|
||||
LLVM_ABI SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &&that);
|
||||
|
||||
explicit SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize)
|
||||
: CurArray(SmallStorage), CurArraySize(SmallSize), NumNonEmpty(0),
|
||||
: CurArray(SmallStorage), CurArraySize(SmallSize), NumEntries(0),
|
||||
NumTombstones(0), IsSmall(true) {
|
||||
assert(SmallSize && (SmallSize & (SmallSize-1)) == 0 &&
|
||||
assert(std::has_single_bit(SmallSize) &&
|
||||
"Initial size must be a power of two!");
|
||||
}
|
||||
|
||||
@@ -91,8 +96,8 @@ public:
|
||||
SmallPtrSetImplBase &operator=(const SmallPtrSetImplBase &) = delete;
|
||||
|
||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||
size_type size() const { return NumNonEmpty - NumTombstones; }
|
||||
size_type capacity() const { return CurArraySize; }
|
||||
[[nodiscard]] size_type size() const { return NumEntries; }
|
||||
[[nodiscard]] size_type capacity() const { return CurArraySize; }
|
||||
|
||||
void clear() {
|
||||
incrementEpoch();
|
||||
@@ -105,42 +110,58 @@ public:
|
||||
memset(CurArray, -1, CurArraySize * sizeof(void *));
|
||||
}
|
||||
|
||||
NumNonEmpty = 0;
|
||||
NumEntries = 0;
|
||||
NumTombstones = 0;
|
||||
}
|
||||
|
||||
void reserve(size_type NumEntries) {
|
||||
void reserve(size_type NewNumEntries) {
|
||||
incrementEpoch();
|
||||
// Do nothing if we're given zero as a reservation size.
|
||||
if (NumEntries == 0)
|
||||
if (NewNumEntries == 0)
|
||||
return;
|
||||
// No need to expand if we're small and NumEntries will fit in the space.
|
||||
if (isSmall() && NumEntries <= CurArraySize)
|
||||
// No need to expand if we're small and NewNumEntries will fit in the space.
|
||||
if (isSmall() && NewNumEntries <= CurArraySize)
|
||||
return;
|
||||
// insert_imp_big will reallocate if stores is more than 75% full, on the
|
||||
// /final/ insertion.
|
||||
if (!isSmall() && ((NumEntries - 1) * 4) < (CurArraySize * 3))
|
||||
if (!isSmall() && ((NewNumEntries - 1) * 4) < (CurArraySize * 3))
|
||||
return;
|
||||
// We must Grow -- find the size where we'd be 75% full, then round up to
|
||||
// the next power of two.
|
||||
size_type NewSize = NumEntries + (NumEntries / 3);
|
||||
NewSize = 1 << (Log2_32_Ceil(NewSize) + 1);
|
||||
size_type NewSize = NewNumEntries + (NewNumEntries / 3);
|
||||
NewSize = std::bit_ceil(NewSize);
|
||||
// Like insert_imp_big, always allocate at least 128 elements.
|
||||
NewSize = (std::max)(128u, NewSize);
|
||||
Grow(NewSize);
|
||||
}
|
||||
|
||||
protected:
|
||||
static void *getTombstoneMarker() { return reinterpret_cast<void*>(-2); }
|
||||
static void *getTombstoneMarker() { return reinterpret_cast<void *>(-2); }
|
||||
|
||||
static void *getEmptyMarker() {
|
||||
// Note that -1 is chosen to make clear() efficiently implementable with
|
||||
// memset and because it's not a valid pointer value.
|
||||
return reinterpret_cast<void*>(-1);
|
||||
return reinterpret_cast<void *>(-1);
|
||||
}
|
||||
|
||||
const void **EndPointer() const {
|
||||
return isSmall() ? CurArray + NumNonEmpty : CurArray + CurArraySize;
|
||||
return isSmall() ? CurArray + NumEntries : CurArray + CurArraySize;
|
||||
}
|
||||
|
||||
iterator_range<const void **> small_buckets() {
|
||||
return make_range(CurArray, CurArray + NumEntries);
|
||||
}
|
||||
|
||||
iterator_range<const void *const *> small_buckets() const {
|
||||
return {CurArray, CurArray + NumEntries};
|
||||
}
|
||||
|
||||
iterator_range<const void **> buckets() {
|
||||
return make_range(CurArray, EndPointer());
|
||||
}
|
||||
|
||||
iterator_range<const void *const *> buckets() const {
|
||||
return make_range(CurArray, EndPointer());
|
||||
}
|
||||
|
||||
/// insert_imp - This returns true if the pointer was new to the set, false if
|
||||
@@ -149,18 +170,16 @@ protected:
|
||||
std::pair<const void *const *, bool> insert_imp(const void *Ptr) {
|
||||
if (isSmall()) {
|
||||
// Check to see if it is already in the set.
|
||||
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr) {
|
||||
const void *Value = *APtr;
|
||||
if (Value == Ptr)
|
||||
return std::make_pair(APtr, false);
|
||||
for (const void *&Bucket : small_buckets()) {
|
||||
if (Bucket == Ptr)
|
||||
return {&Bucket, false};
|
||||
}
|
||||
|
||||
// Nope, there isn't. If we stay small, just 'pushback' now.
|
||||
if (NumNonEmpty < CurArraySize) {
|
||||
CurArray[NumNonEmpty++] = Ptr;
|
||||
if (NumEntries < CurArraySize) {
|
||||
CurArray[NumEntries++] = Ptr;
|
||||
incrementEpoch();
|
||||
return std::make_pair(CurArray + (NumNonEmpty - 1), true);
|
||||
return {CurArray + (NumEntries - 1), true};
|
||||
}
|
||||
// Otherwise, hit the big set case, which will call grow.
|
||||
}
|
||||
@@ -171,12 +190,11 @@ protected:
|
||||
/// return true, otherwise return false. This is hidden from the client so
|
||||
/// that the derived class can check that the right type of pointer is passed
|
||||
/// in.
|
||||
bool erase_imp(const void * Ptr) {
|
||||
bool erase_imp(const void *Ptr) {
|
||||
if (isSmall()) {
|
||||
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr) {
|
||||
if (*APtr == Ptr) {
|
||||
*APtr = CurArray[--NumNonEmpty];
|
||||
for (const void *&Bucket : small_buckets()) {
|
||||
if (Bucket == Ptr) {
|
||||
Bucket = CurArray[--NumEntries];
|
||||
incrementEpoch();
|
||||
return true;
|
||||
}
|
||||
@@ -190,6 +208,7 @@ protected:
|
||||
|
||||
*const_cast<const void **>(Bucket) = getTombstoneMarker();
|
||||
NumTombstones++;
|
||||
--NumEntries;
|
||||
// Treat this consistently from an API perspective, even if we don't
|
||||
// actually invalidate iterators here.
|
||||
incrementEpoch();
|
||||
@@ -199,14 +218,12 @@ protected:
|
||||
/// Returns the raw pointer needed to construct an iterator. If element not
|
||||
/// found, this will be EndPointer. Otherwise, it will be a pointer to the
|
||||
/// slot which stores Ptr;
|
||||
const void *const * find_imp(const void * Ptr) const {
|
||||
const void *const *find_imp(const void *Ptr) const {
|
||||
if (isSmall()) {
|
||||
// Linear search for the item.
|
||||
for (const void *const *APtr = CurArray, *const *E =
|
||||
CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr)
|
||||
if (*APtr == Ptr)
|
||||
return APtr;
|
||||
for (const void *const &Bucket : small_buckets())
|
||||
if (Bucket == Ptr)
|
||||
return &Bucket;
|
||||
return EndPointer();
|
||||
}
|
||||
|
||||
@@ -219,10 +236,8 @@ protected:
|
||||
bool contains_imp(const void *Ptr) const {
|
||||
if (isSmall()) {
|
||||
// Linear search for the item.
|
||||
const void *const *APtr = CurArray;
|
||||
const void *const *E = CurArray + NumNonEmpty;
|
||||
for (; APtr != E; ++APtr)
|
||||
if (*APtr == Ptr)
|
||||
for (const void *const &Bucket : small_buckets())
|
||||
if (Bucket == Ptr)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -233,24 +248,26 @@ protected:
|
||||
bool isSmall() const { return IsSmall; }
|
||||
|
||||
private:
|
||||
std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
|
||||
LLVM_ABI std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
|
||||
|
||||
const void *const *doFind(const void *Ptr) const;
|
||||
const void * const *FindBucketFor(const void *Ptr) const;
|
||||
void shrink_and_clear();
|
||||
LLVM_ABI const void *const *doFind(const void *Ptr) const;
|
||||
const void *const *FindBucketFor(const void *Ptr) const;
|
||||
LLVM_ABI void shrink_and_clear();
|
||||
|
||||
/// Grow - Allocate a larger backing store for the buckets and move it over.
|
||||
void Grow(unsigned NewSize);
|
||||
LLVM_ABI void Grow(unsigned NewSize);
|
||||
|
||||
protected:
|
||||
/// swap - Swaps the elements of two sets.
|
||||
/// Note: This method assumes that both sets have the same small size.
|
||||
void swap(const void **SmallStorage, const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &RHS);
|
||||
LLVM_ABI void swap(const void **SmallStorage, const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &RHS);
|
||||
|
||||
void copyFrom(const void **SmallStorage, const SmallPtrSetImplBase &RHS);
|
||||
void moveFrom(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
|
||||
LLVM_ABI void copyFrom(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &RHS);
|
||||
LLVM_ABI void moveFrom(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &&RHS);
|
||||
|
||||
private:
|
||||
/// Code shared by moveFrom() and move constructor.
|
||||
@@ -262,18 +279,12 @@ private:
|
||||
|
||||
/// SmallPtrSetIteratorImpl - This is the common base class shared between all
|
||||
/// instances of SmallPtrSetIterator.
|
||||
class SmallPtrSetIteratorImpl {
|
||||
protected:
|
||||
const void *const *Bucket;
|
||||
const void *const *End;
|
||||
|
||||
class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIteratorImpl
|
||||
: public DebugEpochBase::HandleBase {
|
||||
public:
|
||||
explicit SmallPtrSetIteratorImpl(const void *const *BP, const void*const *E)
|
||||
: Bucket(BP), End(E) {
|
||||
if (shouldReverseIterate()) {
|
||||
RetreatIfNotValid();
|
||||
return;
|
||||
}
|
||||
explicit SmallPtrSetIteratorImpl(const void *const *BP, const void *const *E,
|
||||
const DebugEpochBase &Epoch)
|
||||
: DebugEpochBase::HandleBase(&Epoch), Bucket(BP), End(E) {
|
||||
AdvanceIfNotValid();
|
||||
}
|
||||
|
||||
@@ -285,6 +296,18 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void *dereference() const {
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
assert(Bucket < End);
|
||||
return const_cast<void *>(*Bucket);
|
||||
}
|
||||
void increment() {
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
++Bucket;
|
||||
AdvanceIfNotValid();
|
||||
}
|
||||
|
||||
private:
|
||||
/// AdvanceIfNotValid - If the current bucket isn't valid, advance to a bucket
|
||||
/// that is. This is guaranteed to stop because the end() bucket is marked
|
||||
/// valid.
|
||||
@@ -295,21 +318,19 @@ protected:
|
||||
*Bucket == SmallPtrSetImplBase::getTombstoneMarker()))
|
||||
++Bucket;
|
||||
}
|
||||
void RetreatIfNotValid() {
|
||||
assert(Bucket >= End);
|
||||
while (Bucket != End &&
|
||||
(Bucket[-1] == SmallPtrSetImplBase::getEmptyMarker() ||
|
||||
Bucket[-1] == SmallPtrSetImplBase::getTombstoneMarker())) {
|
||||
--Bucket;
|
||||
}
|
||||
}
|
||||
|
||||
using BucketItTy =
|
||||
std::conditional_t<shouldReverseIterate(),
|
||||
std::reverse_iterator<const void *const *>,
|
||||
const void *const *>;
|
||||
|
||||
BucketItTy Bucket;
|
||||
BucketItTy End;
|
||||
};
|
||||
|
||||
/// SmallPtrSetIterator - This implements a const_iterator for SmallPtrSet.
|
||||
template <typename PtrTy>
|
||||
class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
|
||||
: public SmallPtrSetIteratorImpl,
|
||||
DebugEpochBase::HandleBase {
|
||||
class SmallPtrSetIterator : public SmallPtrSetIteratorImpl {
|
||||
using PtrTraits = PointerLikeTypeTraits<PtrTy>;
|
||||
|
||||
public:
|
||||
@@ -319,37 +340,22 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
explicit SmallPtrSetIterator(const void *const *BP, const void *const *E,
|
||||
const DebugEpochBase &Epoch)
|
||||
: SmallPtrSetIteratorImpl(BP, E), DebugEpochBase::HandleBase(&Epoch) {}
|
||||
using SmallPtrSetIteratorImpl::SmallPtrSetIteratorImpl;
|
||||
|
||||
// Most methods are provided by the base class.
|
||||
|
||||
const PtrTy operator*() const {
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
if (shouldReverseIterate()) {
|
||||
assert(Bucket > End);
|
||||
return PtrTraits::getFromVoidPointer(const_cast<void *>(Bucket[-1]));
|
||||
}
|
||||
assert(Bucket < End);
|
||||
return PtrTraits::getFromVoidPointer(const_cast<void*>(*Bucket));
|
||||
[[nodiscard]] const PtrTy operator*() const {
|
||||
return PtrTraits::getFromVoidPointer(dereference());
|
||||
}
|
||||
|
||||
inline SmallPtrSetIterator& operator++() { // Preincrement
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
if (shouldReverseIterate()) {
|
||||
--Bucket;
|
||||
RetreatIfNotValid();
|
||||
return *this;
|
||||
}
|
||||
++Bucket;
|
||||
AdvanceIfNotValid();
|
||||
inline SmallPtrSetIterator &operator++() { // Preincrement
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SmallPtrSetIterator operator++(int) { // Postincrement
|
||||
SmallPtrSetIterator operator++(int) { // Postincrement
|
||||
SmallPtrSetIterator tmp = *this;
|
||||
++*this;
|
||||
increment();
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
@@ -359,8 +365,7 @@ public:
|
||||
///
|
||||
/// This is particularly useful for passing around between interface boundaries
|
||||
/// to avoid encoding a particular small size in the interface boundary.
|
||||
template <typename PtrType>
|
||||
class SmallPtrSetImpl : public SmallPtrSetImplBase {
|
||||
template <typename PtrType> class SmallPtrSetImpl : public SmallPtrSetImplBase {
|
||||
using ConstPtrType = typename add_const_past_pointer<PtrType>::type;
|
||||
using PtrTraits = PointerLikeTypeTraits<PtrType>;
|
||||
using ConstPtrTraits = PointerLikeTypeTraits<ConstPtrType>;
|
||||
@@ -383,15 +388,13 @@ public:
|
||||
/// the element equal to Ptr.
|
||||
std::pair<iterator, bool> insert(PtrType Ptr) {
|
||||
auto p = insert_imp(PtrTraits::getAsVoidPointer(Ptr));
|
||||
return std::make_pair(makeIterator(p.first), p.second);
|
||||
return {makeIterator(p.first), p.second};
|
||||
}
|
||||
|
||||
/// Insert the given pointer with an iterator hint that is ignored. This is
|
||||
/// identical to calling insert(Ptr), but allows SmallPtrSet to be used by
|
||||
/// std::insert_iterator and std::inserter().
|
||||
iterator insert(iterator, PtrType Ptr) {
|
||||
return insert(Ptr).first;
|
||||
}
|
||||
iterator insert(iterator, PtrType Ptr) { return insert(Ptr).first; }
|
||||
|
||||
/// Remove pointer from the set.
|
||||
///
|
||||
@@ -414,16 +417,16 @@ public:
|
||||
/// Returns whether anything was removed. It is safe to read the set inside
|
||||
/// the predicate function. However, the predicate must not modify the set
|
||||
/// itself, only indicate a removal by returning true.
|
||||
template <typename UnaryPredicate>
|
||||
bool remove_if(UnaryPredicate P) {
|
||||
template <typename UnaryPredicate> bool remove_if(UnaryPredicate P) {
|
||||
bool Removed = false;
|
||||
if (isSmall()) {
|
||||
const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
auto Buckets = small_buckets();
|
||||
const void **APtr = Buckets.begin(), **E = Buckets.end();
|
||||
while (APtr != E) {
|
||||
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(*APtr));
|
||||
if (P(Ptr)) {
|
||||
*APtr = *--E;
|
||||
--NumNonEmpty;
|
||||
--NumEntries;
|
||||
incrementEpoch();
|
||||
Removed = true;
|
||||
} else {
|
||||
@@ -433,14 +436,14 @@ public:
|
||||
return Removed;
|
||||
}
|
||||
|
||||
for (const void **APtr = CurArray, **E = EndPointer(); APtr != E; ++APtr) {
|
||||
const void *Value = *APtr;
|
||||
if (Value == getTombstoneMarker() || Value == getEmptyMarker())
|
||||
for (const void *&Bucket : buckets()) {
|
||||
if (Bucket == getTombstoneMarker() || Bucket == getEmptyMarker())
|
||||
continue;
|
||||
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Value));
|
||||
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Bucket));
|
||||
if (P(Ptr)) {
|
||||
*APtr = getTombstoneMarker();
|
||||
Bucket = getTombstoneMarker();
|
||||
++NumTombstones;
|
||||
--NumEntries;
|
||||
incrementEpoch();
|
||||
Removed = true;
|
||||
}
|
||||
@@ -449,18 +452,17 @@ public:
|
||||
}
|
||||
|
||||
/// count - Return 1 if the specified pointer is in the set, 0 otherwise.
|
||||
size_type count(ConstPtrType Ptr) const {
|
||||
[[nodiscard]] size_type count(ConstPtrType Ptr) const {
|
||||
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
|
||||
}
|
||||
iterator find(ConstPtrType Ptr) const {
|
||||
[[nodiscard]] iterator find(ConstPtrType Ptr) const {
|
||||
return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)));
|
||||
}
|
||||
bool contains(ConstPtrType Ptr) const {
|
||||
[[nodiscard]] bool contains(ConstPtrType Ptr) const {
|
||||
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
|
||||
}
|
||||
|
||||
template <typename IterT>
|
||||
void insert(IterT I, IterT E) {
|
||||
template <typename IterT> void insert(IterT I, IterT E) {
|
||||
for (; I != E; ++I)
|
||||
insert(*I);
|
||||
}
|
||||
@@ -469,19 +471,25 @@ public:
|
||||
insert(IL.begin(), IL.end());
|
||||
}
|
||||
|
||||
iterator begin() const {
|
||||
if (shouldReverseIterate())
|
||||
return makeIterator(EndPointer() - 1);
|
||||
return makeIterator(CurArray);
|
||||
template <typename Range> void insert_range(Range &&R) {
|
||||
insert(adl_begin(R), adl_end(R));
|
||||
}
|
||||
iterator end() const { return makeIterator(EndPointer()); }
|
||||
|
||||
[[nodiscard]] iterator begin() const {
|
||||
if constexpr (shouldReverseIterate())
|
||||
return makeIterator(EndPointer() - 1);
|
||||
else
|
||||
return makeIterator(CurArray);
|
||||
}
|
||||
[[nodiscard]] iterator end() const { return makeIterator(EndPointer()); }
|
||||
|
||||
private:
|
||||
/// Create an iterator that dereferences to same place as the given pointer.
|
||||
iterator makeIterator(const void *const *P) const {
|
||||
if (shouldReverseIterate())
|
||||
if constexpr (shouldReverseIterate())
|
||||
return iterator(P == EndPointer() ? CurArray : P + 1, CurArray, *this);
|
||||
return iterator(P, EndPointer(), *this);
|
||||
else
|
||||
return iterator(P, EndPointer(), *this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -490,8 +498,8 @@ private:
|
||||
/// Iterates over elements of LHS confirming that each value from LHS is also in
|
||||
/// RHS, and that no additional values are in RHS.
|
||||
template <typename PtrType>
|
||||
bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
const SmallPtrSetImpl<PtrType> &RHS) {
|
||||
[[nodiscard]] bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
const SmallPtrSetImpl<PtrType> &RHS) {
|
||||
if (LHS.size() != RHS.size())
|
||||
return false;
|
||||
|
||||
@@ -506,8 +514,8 @@ bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
///
|
||||
/// Equivalent to !(LHS == RHS).
|
||||
template <typename PtrType>
|
||||
bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
const SmallPtrSetImpl<PtrType> &RHS) {
|
||||
[[nodiscard]] bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
const SmallPtrSetImpl<PtrType> &RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
@@ -515,7 +523,7 @@ bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
|
||||
/// SmallSize or less elements. This internally rounds up SmallSize to the next
|
||||
/// power of two if it is not already a power of two. See the comments above
|
||||
/// SmallPtrSetImplBase for details of the algorithm.
|
||||
template<class PtrType, unsigned SmallSize>
|
||||
template <class PtrType, unsigned SmallSize>
|
||||
class SmallPtrSet : public SmallPtrSetImpl<PtrType> {
|
||||
// In small mode SmallPtrSet uses linear search for the elements, so it is
|
||||
// not a good idea to choose this value too high. You may consider using a
|
||||
@@ -524,18 +532,8 @@ class SmallPtrSet : public SmallPtrSetImpl<PtrType> {
|
||||
|
||||
using BaseT = SmallPtrSetImpl<PtrType>;
|
||||
|
||||
// A constexpr version of wpi::util::bit_ceil.
|
||||
// TODO: Replace this with std::bit_ceil once C++20 is available.
|
||||
static constexpr size_t RoundUpToPowerOfTwo(size_t X) {
|
||||
size_t C = 1;
|
||||
size_t CMax = C << (std::numeric_limits<size_t>::digits - 1);
|
||||
while (C < X && C < CMax)
|
||||
C <<= 1;
|
||||
return C;
|
||||
}
|
||||
|
||||
// Make sure that SmallSize is a power of two, round up if not.
|
||||
static constexpr size_t SmallSizePowTwo = RoundUpToPowerOfTwo(SmallSize);
|
||||
static constexpr size_t SmallSizePowTwo = std::bit_ceil(SmallSize);
|
||||
/// SmallStorage - Fixed size storage used in 'small mode'.
|
||||
const void *SmallStorage[SmallSizePowTwo];
|
||||
|
||||
@@ -546,11 +544,15 @@ public:
|
||||
: BaseT(SmallStorage, SmallSizePowTwo, that.SmallStorage,
|
||||
std::move(that)) {}
|
||||
|
||||
template<typename It>
|
||||
template <typename It>
|
||||
SmallPtrSet(It I, It E) : BaseT(SmallStorage, SmallSizePowTwo) {
|
||||
this->insert(I, E);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
SmallPtrSet(wpi::util::from_range_t, Range &&R)
|
||||
: SmallPtrSet(adl_begin(R), adl_end(R)) {}
|
||||
|
||||
SmallPtrSet(std::initializer_list<PtrType> IL)
|
||||
: BaseT(SmallStorage, SmallSizePowTwo) {
|
||||
this->insert(IL.begin(), IL.end());
|
||||
@@ -584,16 +586,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace wpi::util
|
||||
} // namespace wpi::util
|
||||
|
||||
namespace std {
|
||||
|
||||
/// Implement std::swap in terms of SmallPtrSet swap.
|
||||
template<class T, unsigned N>
|
||||
inline void swap(wpi::util::SmallPtrSet<T, N> &LHS, wpi::util::SmallPtrSet<T, N> &RHS) {
|
||||
LHS.swap(RHS);
|
||||
}
|
||||
/// Implement std::swap in terms of SmallPtrSet swap.
|
||||
template <class T, unsigned N>
|
||||
inline void swap(wpi::util::SmallPtrSet<T, N> &LHS, wpi::util::SmallPtrSet<T, N> &RHS) {
|
||||
LHS.swap(RHS);
|
||||
}
|
||||
|
||||
} // end namespace std
|
||||
} // namespace std
|
||||
|
||||
#endif // WPIUTIL_WPI_SMALLPTRSET_H
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#ifndef WPIUTIL_WPI_SMALLSET_H
|
||||
#define WPIUTIL_WPI_SMALLSET_H
|
||||
|
||||
#include "wpi/util/ADL.hpp"
|
||||
#include "wpi/util/STLForwardCompat.hpp"
|
||||
#include "wpi/util/SmallPtrSet.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/iterator.hpp"
|
||||
@@ -34,7 +36,6 @@ class SmallSetIterator
|
||||
private:
|
||||
using SetIterTy = typename std::set<T, C>::const_iterator;
|
||||
using VecIterTy = typename SmallVector<T, N>::const_iterator;
|
||||
using SelfTy = SmallSetIterator<T, N, C>;
|
||||
|
||||
/// Iterators to the parts of the SmallSet containing the data. They are set
|
||||
/// depending on isSmall.
|
||||
@@ -161,10 +162,9 @@ public:
|
||||
insert(Begin, End);
|
||||
}
|
||||
|
||||
template <typename RangeT>
|
||||
explicit SmallSet(const iterator_range<RangeT> &R) {
|
||||
insert(R.begin(), R.end());
|
||||
}
|
||||
template <typename Range>
|
||||
SmallSet(wpi::util::from_range_t, Range &&R)
|
||||
: SmallSet(adl_begin(R), adl_end(R)) {}
|
||||
|
||||
SmallSet(std::initializer_list<T> L) { insert(L.begin(), L.end()); }
|
||||
|
||||
@@ -173,12 +173,14 @@ public:
|
||||
|
||||
[[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
|
||||
|
||||
size_type size() const {
|
||||
[[nodiscard]] size_type size() const {
|
||||
return isSmall() ? Vector.size() : Set.size();
|
||||
}
|
||||
|
||||
/// count - Return 1 if the element is in the set, 0 otherwise.
|
||||
size_type count(const T &V) const { return contains(V) ? 1 : 0; }
|
||||
[[nodiscard]] size_type count(const T &V) const {
|
||||
return contains(V) ? 1 : 0;
|
||||
}
|
||||
|
||||
/// insert - Insert an element into the set if it isn't already there.
|
||||
/// Returns a pair. The first value of it is an iterator to the inserted
|
||||
@@ -196,6 +198,10 @@ public:
|
||||
insert(*I);
|
||||
}
|
||||
|
||||
template <typename Range> void insert_range(Range &&R) {
|
||||
insert(adl_begin(R), adl_end(R));
|
||||
}
|
||||
|
||||
bool erase(const T &V) {
|
||||
if (!isSmall())
|
||||
return Set.erase(V);
|
||||
@@ -212,20 +218,20 @@ public:
|
||||
Set.clear();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
[[nodiscard]] const_iterator begin() const {
|
||||
if (isSmall())
|
||||
return {Vector.begin()};
|
||||
return {Set.begin()};
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
[[nodiscard]] const_iterator end() const {
|
||||
if (isSmall())
|
||||
return {Vector.end()};
|
||||
return {Set.end()};
|
||||
}
|
||||
|
||||
/// Check if the SmallSet contains the given element.
|
||||
bool contains(const T &V) const {
|
||||
[[nodiscard]] bool contains(const T &V) const {
|
||||
if (isSmall())
|
||||
return vfind(V) != Vector.end();
|
||||
return Set.find(V) != Set.end();
|
||||
@@ -270,7 +276,7 @@ private:
|
||||
/// If this set is of pointer values, transparently switch over to using
|
||||
/// SmallPtrSet for performance.
|
||||
template <typename PointeeType, unsigned N>
|
||||
class SmallSet<PointeeType*, N> : public SmallPtrSet<PointeeType*, N> {};
|
||||
class SmallSet<PointeeType *, N> : public SmallPtrSet<PointeeType *, N> {};
|
||||
|
||||
/// Equality comparison for SmallSet.
|
||||
///
|
||||
@@ -281,7 +287,8 @@ class SmallSet<PointeeType*, N> : public SmallPtrSet<PointeeType*, N> {};
|
||||
/// For large-set mode amortized complexity is linear, worst case is O(N^2) (if
|
||||
/// every hash collides).
|
||||
template <typename T, unsigned LN, unsigned RN, typename C>
|
||||
bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
|
||||
[[nodiscard]] bool operator==(const SmallSet<T, LN, C> &LHS,
|
||||
const SmallSet<T, RN, C> &RHS) {
|
||||
if (LHS.size() != RHS.size())
|
||||
return false;
|
||||
|
||||
@@ -293,7 +300,8 @@ bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
|
||||
///
|
||||
/// Equivalent to !(LHS == RHS). See operator== for performance notes.
|
||||
template <typename T, unsigned LN, unsigned RN, typename C>
|
||||
bool operator!=(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
|
||||
[[nodiscard]] bool operator!=(const SmallSet<T, LN, C> &LHS,
|
||||
const SmallSet<T, RN, C> &RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#ifndef WPIUTIL_WPI_SMALLVECTOR_H
|
||||
#define WPIUTIL_WPI_SMALLVECTOR_H
|
||||
|
||||
#include "wpi/util/ADL.hpp"
|
||||
#include "wpi/util/DenseMapInfo.hpp"
|
||||
|
||||
// This file uses std::memcpy() to copy std::pair<unsigned int, unsigned int>.
|
||||
// That type is POD, but the standard doesn't guarantee that. GCC doesn't treat
|
||||
// the type as POD so it throws a warning. We want to consider this a warning
|
||||
@@ -73,13 +76,13 @@ protected:
|
||||
/// 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(void *FirstEl, size_t MinSize, size_t TSize,
|
||||
size_t &NewCapacity);
|
||||
LLVM_ABI void *mallocForGrow(void *FirstEl, 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);
|
||||
LLVM_ABI void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
|
||||
|
||||
public:
|
||||
size_t size() const { return Size; }
|
||||
@@ -200,30 +203,34 @@ protected:
|
||||
}
|
||||
|
||||
/// 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>
|
||||
void assertSafeToReferenceAfterClear(ItTy From, ItTy To) {
|
||||
if constexpr (std::is_pointer_v<ItTy> &&
|
||||
std::is_same_v<
|
||||
std::remove_const_t<std::remove_pointer_t<ItTy>>,
|
||||
std::remove_const_t<T>>) {
|
||||
if (From == To)
|
||||
return;
|
||||
this->assertSafeToReferenceAfterResize(From, 0);
|
||||
this->assertSafeToReferenceAfterResize(To - 1, 0);
|
||||
}
|
||||
(void)From;
|
||||
(void)To;
|
||||
}
|
||||
template <
|
||||
class ItTy,
|
||||
std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T *>::value,
|
||||
bool> = false>
|
||||
void assertSafeToReferenceAfterClear(ItTy, ItTy) {}
|
||||
|
||||
/// Check whether any part of the range will be invalidated by growing.
|
||||
void assertSafeToAddRange(const T *From, const T *To) {
|
||||
if (From == To)
|
||||
return;
|
||||
this->assertSafeToAdd(From, To - From);
|
||||
this->assertSafeToAdd(To - 1, To - From);
|
||||
template <class ItTy> void assertSafeToAddRange(ItTy From, ItTy To) {
|
||||
if constexpr (std::is_pointer_v<ItTy> &&
|
||||
std::is_same_v<std::remove_cv_t<std::remove_pointer_t<ItTy>>,
|
||||
T>) {
|
||||
if (From == To)
|
||||
return;
|
||||
this->assertSafeToAdd(From, To - From);
|
||||
this->assertSafeToAdd(To - 1, To - From);
|
||||
}
|
||||
(void)From;
|
||||
(void)To;
|
||||
}
|
||||
template <
|
||||
class ItTy,
|
||||
std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T *>::value,
|
||||
bool> = false>
|
||||
void assertSafeToAddRange(ItTy, ItTy) {}
|
||||
|
||||
/// Reserve enough space to add one element, and return the updated element
|
||||
/// pointer in case it was a reference to the storage.
|
||||
@@ -501,25 +508,22 @@ protected:
|
||||
|
||||
/// Copy the range [I, E) onto the uninitialized memory
|
||||
/// starting with "Dest", constructing elements into it as needed.
|
||||
template<typename It1, typename It2>
|
||||
template <typename It1, typename It2>
|
||||
static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
|
||||
// Arbitrary iterator types; just use the basic implementation.
|
||||
std::uninitialized_copy(I, E, Dest);
|
||||
}
|
||||
|
||||
/// Copy the range [I, E) onto the uninitialized memory
|
||||
/// starting with "Dest", constructing elements into it as needed.
|
||||
template <typename T1, typename T2>
|
||||
static void uninitialized_copy(
|
||||
T1 *I, T1 *E, T2 *Dest,
|
||||
std::enable_if_t<std::is_same<std::remove_const_t<T1>, T2>::value> * =
|
||||
nullptr) {
|
||||
// Use memcpy for PODs iterated by pointers (which includes SmallVector
|
||||
// iterators): std::uninitialized_copy optimizes to memmove, but we can
|
||||
// use memcpy here. Note that I and E are iterators and thus might be
|
||||
// invalid for memcpy if they are equal.
|
||||
if (I != E)
|
||||
memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
|
||||
if constexpr (std::is_pointer_v<It1> && std::is_pointer_v<It2> &&
|
||||
std::is_same_v<
|
||||
std::remove_const_t<std::remove_pointer_t<It1>>,
|
||||
std::remove_pointer_t<It2>>) {
|
||||
// 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)
|
||||
std::memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
|
||||
} else {
|
||||
// Arbitrary iterator types; just use the basic implementation.
|
||||
std::uninitialized_copy(I, E, Dest);
|
||||
}
|
||||
}
|
||||
|
||||
/// Double the size of the allocated memory, guaranteeing space for at
|
||||
@@ -562,7 +566,7 @@ protected:
|
||||
public:
|
||||
void push_back(ValueParamT Elt) {
|
||||
const T *EltPtr = reserveForParamAndGetAddress(Elt);
|
||||
memcpy(reinterpret_cast<void *>(this->end()), EltPtr, sizeof(T));
|
||||
std::memcpy(reinterpret_cast<void *>(this->end()), EltPtr, sizeof(T));
|
||||
this->set_size(this->size() + 1);
|
||||
}
|
||||
|
||||
@@ -736,6 +740,12 @@ public:
|
||||
|
||||
void assign(const SmallVectorImpl &RHS) { assign(RHS.begin(), RHS.end()); }
|
||||
|
||||
template <typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
void assign(std::span<const U> AR) {
|
||||
assign(AR.begin(), AR.end());
|
||||
}
|
||||
|
||||
iterator erase(const_iterator CI) {
|
||||
// Just cast away constness because this is a non-const member function.
|
||||
iterator I = const_cast<iterator>(CI);
|
||||
@@ -1230,7 +1240,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename U,
|
||||
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
explicit SmallVector(std::span<const U> A) : SmallVectorImpl<T>(N) {
|
||||
this->append(A.begin(), A.end());
|
||||
}
|
||||
@@ -1291,30 +1301,49 @@ inline size_t capacity_in_bytes(const SmallVector<T, N> &X) {
|
||||
|
||||
template <typename RangeType>
|
||||
using ValueTypeFromRangeType =
|
||||
std::remove_const_t<std::remove_reference_t<decltype(*std::begin(
|
||||
std::declval<RangeType &>()))>>;
|
||||
std::remove_const_t<detail::ValueOfRange<RangeType>>;
|
||||
|
||||
/// Given a range of type R, iterate the entire range and return a
|
||||
/// SmallVector with elements of the vector. This is useful, for example,
|
||||
/// when you want to iterate a range and then sort the results.
|
||||
template <unsigned Size, typename R>
|
||||
SmallVector<ValueTypeFromRangeType<R>, Size> to_vector(R &&Range) {
|
||||
return {std::begin(Range), std::end(Range)};
|
||||
return {adl_begin(Range), adl_end(Range)};
|
||||
}
|
||||
template <typename R>
|
||||
SmallVector<ValueTypeFromRangeType<R>> to_vector(R &&Range) {
|
||||
return {std::begin(Range), std::end(Range)};
|
||||
return {adl_begin(Range), adl_end(Range)};
|
||||
}
|
||||
|
||||
template <typename Out, unsigned Size, typename R>
|
||||
SmallVector<Out, Size> to_vector_of(R &&Range) {
|
||||
return {std::begin(Range), std::end(Range)};
|
||||
return {adl_begin(Range), adl_end(Range)};
|
||||
}
|
||||
|
||||
template <typename Out, typename R> SmallVector<Out> to_vector_of(R &&Range) {
|
||||
return {std::begin(Range), std::end(Range)};
|
||||
return {adl_begin(Range), adl_end(Range)};
|
||||
}
|
||||
|
||||
// Provide DenseMapInfo for SmallVector of a type which has info.
|
||||
template <typename T, unsigned N> struct DenseMapInfo<wpi::util::SmallVector<T, N>> {
|
||||
static SmallVector<T, N> getEmptyKey() {
|
||||
return {DenseMapInfo<T>::getEmptyKey()};
|
||||
}
|
||||
|
||||
static SmallVector<T, N> getTombstoneKey() {
|
||||
return {DenseMapInfo<T>::getTombstoneKey()};
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const SmallVector<T, N> &V) {
|
||||
return static_cast<unsigned>(hash_combine_range(V));
|
||||
}
|
||||
|
||||
static bool isEqual(const SmallVector<T, N> &LHS,
|
||||
const SmallVector<T, N> &RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Pred>
|
||||
typename SmallVectorImpl<T>::size_type erase_if(
|
||||
SmallVectorImpl<T>& c, Pred pred) {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace sys {
|
||||
constexpr bool IsBigEndianHost =
|
||||
wpi::util::endianness::native == wpi::util::endianness::big;
|
||||
|
||||
static const bool IsLittleEndianHost = !IsBigEndianHost;
|
||||
constexpr bool IsLittleEndianHost = !IsBigEndianHost;
|
||||
|
||||
inline unsigned char getSwappedBytes(unsigned char C) { return wpi::util::byteswap(C); }
|
||||
inline signed char getSwappedBytes( signed char C) { return wpi::util::byteswap(C); }
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define WPIUTIL_WPI_VERSIONTUPLE_H
|
||||
|
||||
#include "wpi/util/DenseMapInfo.hpp"
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -97,9 +98,7 @@ public:
|
||||
|
||||
/// Return a version tuple that contains a different major version but
|
||||
/// everything else is the same.
|
||||
VersionTuple withMajorReplaced(unsigned NewMajor) const {
|
||||
return VersionTuple(NewMajor, Minor, Subminor, Build);
|
||||
}
|
||||
LLVM_ABI VersionTuple withMajorReplaced(unsigned NewMajor) const;
|
||||
|
||||
/// Return a version tuple that contains only components that are non-zero.
|
||||
VersionTuple normalize() const {
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
#ifndef WPIUTIL_WPI_WINDOWSERROR_H
|
||||
#define WPIUTIL_WPI_WINDOWSERROR_H
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <system_error>
|
||||
|
||||
namespace wpi::util {
|
||||
std::error_code mapLastWindowsError();
|
||||
std::error_code mapWindowsError(unsigned EV);
|
||||
LLVM_ABI std::error_code mapLastWindowsError();
|
||||
LLVM_ABI std::error_code mapWindowsError(unsigned EV);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define WPIUTIL_WPI_BIT_H
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <cstddef> // for std::size_t
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -85,7 +85,9 @@ public:
|
||||
using pointer = PointerT;
|
||||
using reference = ReferenceT;
|
||||
|
||||
protected:
|
||||
// Note: These were previously protected, but MSVC has trouble with SFINAE
|
||||
// accessing protected members in derived class templates (specifically in
|
||||
// iterator_adaptor_base::operator-). Making them public fixes the build.
|
||||
enum {
|
||||
IsRandomAccess = std::is_base_of<std::random_access_iterator_tag,
|
||||
IteratorCategoryT>::value,
|
||||
@@ -93,6 +95,7 @@ protected:
|
||||
IteratorCategoryT>::value,
|
||||
};
|
||||
|
||||
protected:
|
||||
/// A proxy object for computing a reference via indirecting a copy of an
|
||||
/// iterator. This is used in APIs which need to produce a reference via
|
||||
/// indirection but for which the iterator object might be a temporary. The
|
||||
@@ -267,6 +270,8 @@ public:
|
||||
return *static_cast<DerivedT *>(this);
|
||||
}
|
||||
using BaseT::operator-;
|
||||
template <bool Enabled = BaseT::IsRandomAccess,
|
||||
typename = std::enable_if_t<Enabled>>
|
||||
difference_type operator-(const DerivedT &RHS) const {
|
||||
static_assert(
|
||||
BaseT::IsRandomAccess,
|
||||
|
||||
@@ -24,16 +24,6 @@
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
template <typename From, typename To, typename = void>
|
||||
struct explicitly_convertible : std::false_type {};
|
||||
|
||||
template <typename From, typename To>
|
||||
struct explicitly_convertible<
|
||||
From, To,
|
||||
std::void_t<decltype(static_cast<To>(
|
||||
std::declval<std::add_rvalue_reference_t<From>>()))>> : std::true_type {
|
||||
};
|
||||
|
||||
/// A range adaptor for a pair of iterators.
|
||||
///
|
||||
/// This just wraps two iterators into a range-compatible interface. Nothing
|
||||
@@ -42,6 +32,10 @@ template <typename IteratorT>
|
||||
class iterator_range {
|
||||
IteratorT begin_iterator, end_iterator;
|
||||
|
||||
template <typename From, typename To>
|
||||
using explicitly_converted_t = decltype(static_cast<To>(
|
||||
std::declval<std::add_rvalue_reference_t<From>>()));
|
||||
|
||||
public:
|
||||
#if defined(__GNUC__) && \
|
||||
(__GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4))
|
||||
@@ -49,10 +43,9 @@ public:
|
||||
// See https://github.com/llvm/llvm-project/issues/63843
|
||||
template <typename Container>
|
||||
#else
|
||||
template <
|
||||
typename Container,
|
||||
std::enable_if_t<explicitly_convertible<
|
||||
wpi::util::detail::IterOfRange<Container>, IteratorT>::value> * = nullptr>
|
||||
template <typename Container,
|
||||
std::void_t<explicitly_converted_t<
|
||||
wpi::util::detail::IterOfRange<Container>, IteratorT>> * = nullptr>
|
||||
#endif
|
||||
iterator_range(Container &&c)
|
||||
: begin_iterator(adl_begin(c)), end_iterator(adl_end(c)) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef WPIUTIL_WPI_RAW_OS_OSTREAM_H
|
||||
#define WPIUTIL_WPI_RAW_OS_OSTREAM_H
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "wpi/util/raw_ostream.hpp"
|
||||
#include <iosfwd>
|
||||
|
||||
@@ -21,7 +22,7 @@ namespace wpi::util {
|
||||
/// raw_os_ostream - A raw_ostream that writes to an std::ostream. This is a
|
||||
/// simple adaptor class. It does not check for output errors; clients should
|
||||
/// use the underlying stream to detect errors.
|
||||
class raw_os_ostream : public raw_ostream {
|
||||
class LLVM_ABI raw_os_ostream : public raw_ostream {
|
||||
std::ostream &OS;
|
||||
|
||||
/// write_impl - See raw_ostream::write_impl.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define WPIUTIL_WPI_RAW_OSTREAM_H
|
||||
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -40,7 +41,7 @@ namespace wpi::util {
|
||||
/// output to a stream. It does not support seeking, reopening, rewinding, line
|
||||
/// buffered disciplines etc. It is a simple buffer that outputs
|
||||
/// a chunk at a time.
|
||||
class raw_ostream {
|
||||
class LLVM_ABI raw_ostream {
|
||||
public:
|
||||
// Class kinds to support LLVM-style RTTI.
|
||||
enum class OStreamKind {
|
||||
@@ -409,7 +410,7 @@ operator<<(OStream &&OS, const T &Value) {
|
||||
/// An abstract base class for streams implementations that also support a
|
||||
/// pwrite operation. This is useful for code that can mostly stream out data,
|
||||
/// but needs to patch in a header that needs to know the output size.
|
||||
class raw_pwrite_stream : public raw_ostream {
|
||||
class LLVM_ABI raw_pwrite_stream : public raw_ostream {
|
||||
virtual void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) = 0;
|
||||
void anchor() override;
|
||||
|
||||
@@ -435,7 +436,7 @@ public:
|
||||
|
||||
/// A raw_ostream that writes to a file descriptor.
|
||||
///
|
||||
class raw_fd_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_fd_ostream : public raw_pwrite_stream {
|
||||
int FD;
|
||||
bool ShouldClose;
|
||||
bool SupportsSeeking = false;
|
||||
@@ -548,17 +549,17 @@ public:
|
||||
|
||||
/// This returns a reference to a raw_fd_ostream for standard output. Use it
|
||||
/// like: outs() << "foo" << "bar";
|
||||
raw_fd_ostream &outs();
|
||||
LLVM_ABI raw_fd_ostream &outs();
|
||||
|
||||
/// This returns a reference to a raw_ostream for standard error.
|
||||
/// Use it like: errs() << "foo" << "bar";
|
||||
/// By default, the stream is tied to stdout to ensure stdout is flushed before
|
||||
/// stderr is written, to ensure the error messages are written in their
|
||||
/// expected place.
|
||||
raw_fd_ostream &errs();
|
||||
LLVM_ABI raw_fd_ostream &errs();
|
||||
|
||||
/// This returns a reference to a raw_ostream which simply discards output.
|
||||
raw_ostream &nulls();
|
||||
LLVM_ABI raw_ostream &nulls();
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// File Streams
|
||||
@@ -571,10 +572,10 @@ public:
|
||||
/// Open the specified file for reading/writing/seeking. If an error occurs,
|
||||
/// information about the error is put into EC, and the stream should be
|
||||
/// immediately destroyed.
|
||||
raw_fd_stream(std::string_view Filename, std::error_code &EC);
|
||||
LLVM_ABI raw_fd_stream(std::string_view Filename, std::error_code &EC);
|
||||
|
||||
/// Check if \p OS is a pointer of type raw_fd_stream*.
|
||||
static bool classof(const raw_ostream *OS);
|
||||
LLVM_ABI static bool classof(const raw_ostream *OS);
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -586,7 +587,7 @@ public:
|
||||
/// raw_string_ostream operates without a buffer, delegating all memory
|
||||
/// management to the std::string. Thus the std::string is always up-to-date,
|
||||
/// may be used directly and there is no need to call flush().
|
||||
class raw_string_ostream : public raw_ostream {
|
||||
class LLVM_ABI raw_string_ostream : public raw_ostream {
|
||||
std::string &OS;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
@@ -616,7 +617,7 @@ public:
|
||||
/// raw_svector_ostream operates without a buffer, delegating all memory
|
||||
/// management to the SmallString. Thus the SmallString is always up-to-date,
|
||||
/// may be used directly and there is no need to call flush().
|
||||
class raw_svector_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_svector_ostream : public raw_pwrite_stream {
|
||||
SmallVectorImpl<char> &OS;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
@@ -660,7 +661,7 @@ public:
|
||||
/// raw_vector_ostream operates without a buffer, delegating all memory
|
||||
/// management to the vector. Thus the vector is always up-to-date,
|
||||
/// may be used directly and there is no need to call flush().
|
||||
class raw_vector_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_vector_ostream : public raw_pwrite_stream {
|
||||
std::vector<char> &OS;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
@@ -693,7 +694,7 @@ public:
|
||||
/// raw_svector_ostream operates without a buffer, delegating all memory
|
||||
/// management to the SmallString. Thus the SmallString is always up-to-date,
|
||||
/// may be used directly and there is no need to call flush().
|
||||
class raw_usvector_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_usvector_ostream : public raw_pwrite_stream {
|
||||
SmallVectorImpl<uint8_t> &OS;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
@@ -727,7 +728,7 @@ public:
|
||||
/// raw_vector_ostream operates without a buffer, delegating all memory
|
||||
/// management to the vector. Thus the vector is always up-to-date,
|
||||
/// may be used directly and there is no need to call flush().
|
||||
class raw_uvector_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_uvector_ostream : public raw_pwrite_stream {
|
||||
std::vector<uint8_t> &OS;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
@@ -758,7 +759,7 @@ public:
|
||||
|
||||
|
||||
/// A raw_ostream that discards all output.
|
||||
class raw_null_ostream : public raw_pwrite_stream {
|
||||
class LLVM_ABI raw_null_ostream : public raw_pwrite_stream {
|
||||
/// See raw_ostream::write_impl.
|
||||
void write_impl(const char *Ptr, size_t size) override;
|
||||
void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
|
||||
@@ -768,11 +769,11 @@ class raw_null_ostream : public raw_pwrite_stream {
|
||||
uint64_t current_pos() const override;
|
||||
|
||||
public:
|
||||
explicit raw_null_ostream() = default;
|
||||
explicit raw_null_ostream() : raw_pwrite_stream(/*Unbuffered=*/true) {}
|
||||
~raw_null_ostream() override;
|
||||
};
|
||||
|
||||
class buffer_ostream : public raw_svector_ostream {
|
||||
class LLVM_ABI buffer_ostream : public raw_svector_ostream {
|
||||
raw_ostream &OS;
|
||||
SmallVector<char, 0> Buffer;
|
||||
|
||||
@@ -783,7 +784,7 @@ public:
|
||||
~buffer_ostream() override { OS << str(); }
|
||||
};
|
||||
|
||||
class buffer_unique_ostream : public raw_svector_ostream {
|
||||
class LLVM_ABI buffer_unique_ostream : public raw_svector_ostream {
|
||||
std::unique_ptr<raw_ostream> OS;
|
||||
SmallVector<char, 0> Buffer;
|
||||
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
|
||||
/// Metafunction that determines whether the given type is either an
|
||||
/// integral type or an enumeration type, including enum classes.
|
||||
///
|
||||
@@ -40,47 +38,28 @@ public:
|
||||
};
|
||||
|
||||
/// If T is a pointer, just return it. If it is not, return T&.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_lvalue_reference_if_not_pointer { using type = T &; };
|
||||
|
||||
template <typename T>
|
||||
struct add_lvalue_reference_if_not_pointer<
|
||||
T, std::enable_if_t<std::is_pointer_v<T>>> {
|
||||
using type = T;
|
||||
template <typename T> struct add_lvalue_reference_if_not_pointer {
|
||||
using type = std::conditional_t<std::is_pointer_v<T>, T, T &>;
|
||||
};
|
||||
|
||||
/// If T is a pointer to X, return a pointer to const X. If it is not,
|
||||
/// return const T.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_const_past_pointer { using type = const T; };
|
||||
|
||||
template <typename T>
|
||||
struct add_const_past_pointer<T, std::enable_if_t<std::is_pointer_v<T>>> {
|
||||
using type = const std::remove_pointer_t<T> *;
|
||||
template <typename T> struct add_const_past_pointer {
|
||||
using type = std::conditional_t<std::is_pointer_v<T>,
|
||||
const std::remove_pointer_t<T> *, const T>;
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct const_pointer_or_const_ref {
|
||||
using type = const T &;
|
||||
template <typename T> struct const_pointer_or_const_ref {
|
||||
using type =
|
||||
std::conditional_t<std::is_pointer_v<T>,
|
||||
typename add_const_past_pointer<T>::type, const T &>;
|
||||
};
|
||||
template <typename T>
|
||||
struct const_pointer_or_const_ref<T, std::enable_if_t<std::is_pointer_v<T>>> {
|
||||
using type = typename add_const_past_pointer<T>::type;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<class T>
|
||||
union trivial_helper {
|
||||
T t;
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
// https://stackoverflow.com/questions/55288555/c-check-if-statement-can-be-evaluated-constexpr
|
||||
template<class Lambda, int=(Lambda{}(), 0)>
|
||||
constexpr bool is_constexpr(Lambda) { return true; }
|
||||
constexpr bool is_constexpr(...) { return false; }
|
||||
|
||||
} // end namespace wpi::util
|
||||
} // namespace wpi::util
|
||||
|
||||
#endif // WPIUTIL_WPI_TYPE_TRAITS_H
|
||||
|
||||
@@ -14,9 +14,12 @@
|
||||
#include "CountCopyAndMove.hpp"
|
||||
#include "wpi/util/DenseMapInfo.hpp"
|
||||
#include "wpi/util/DenseMapInfoVariant.hpp"
|
||||
#include "wpi/util/STLForwardCompat.hpp"
|
||||
#include "wpi/util/SmallSet.hpp"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -71,6 +74,16 @@ public:
|
||||
|
||||
int getValue() const { return Value; }
|
||||
bool operator==(const CtorTester &RHS) const { return Value == RHS.Value; }
|
||||
|
||||
// Return the number of live CtorTester objects, excluding the empty and
|
||||
// tombstone keys.
|
||||
static size_t getNumConstructed() {
|
||||
return std::count_if(Constructed.begin(), Constructed.end(),
|
||||
[](const CtorTester *Obj) {
|
||||
int V = Obj->getValue();
|
||||
return V != -1 && V != -2;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
std::set<CtorTester *> CtorTester::Constructed;
|
||||
@@ -89,6 +102,10 @@ struct CtorTesterMapInfo {
|
||||
CtorTester getTestKey(int i, CtorTester *) { return CtorTester(i); }
|
||||
CtorTester getTestValue(int i, CtorTester *) { return CtorTester(42 + i); }
|
||||
|
||||
std::optional<uint32_t> getTestKey(int i, std::optional<uint32_t> *) {
|
||||
return i;
|
||||
}
|
||||
|
||||
// Test fixture, with helper functions implemented by forwarding to global
|
||||
// function overloads selected by component types of the type parameter. This
|
||||
// allows all of the map implementations to be tested with shared
|
||||
@@ -116,16 +133,17 @@ typename T::mapped_type *const DenseMapTest<T>::dummy_value_ptr = nullptr;
|
||||
|
||||
// Register these types for testing.
|
||||
// clang-format off
|
||||
typedef ::testing::Types<DenseMap<uint32_t, uint32_t>,
|
||||
DenseMap<uint32_t *, uint32_t *>,
|
||||
DenseMap<CtorTester, CtorTester, CtorTesterMapInfo>,
|
||||
DenseMap<EnumClass, uint32_t>,
|
||||
SmallDenseMap<uint32_t, uint32_t>,
|
||||
SmallDenseMap<uint32_t *, uint32_t *>,
|
||||
SmallDenseMap<CtorTester, CtorTester, 4,
|
||||
CtorTesterMapInfo>,
|
||||
SmallDenseMap<EnumClass, uint32_t>
|
||||
> DenseMapTestTypes;
|
||||
using DenseMapTestTypes = ::testing::Types<
|
||||
DenseMap<uint32_t, uint32_t>,
|
||||
DenseMap<uint32_t *, uint32_t *>,
|
||||
DenseMap<CtorTester, CtorTester, CtorTesterMapInfo>,
|
||||
DenseMap<EnumClass, uint32_t>,
|
||||
DenseMap<std::optional<uint32_t>, uint32_t>,
|
||||
SmallDenseMap<uint32_t, uint32_t>,
|
||||
SmallDenseMap<uint32_t *, uint32_t *>,
|
||||
SmallDenseMap<CtorTester, CtorTester, 4, CtorTesterMapInfo>,
|
||||
SmallDenseMap<EnumClass, uint32_t>,
|
||||
SmallDenseMap<std::optional<uint32_t>, uint32_t>>;
|
||||
// clang-format on
|
||||
|
||||
TYPED_TEST_SUITE(DenseMapTest, DenseMapTestTypes, );
|
||||
@@ -186,6 +204,14 @@ TYPED_TEST(DenseMapTest, AtTest) {
|
||||
EXPECT_EQ(this->getValue(0), this->Map.at(this->getKey(0)));
|
||||
EXPECT_EQ(this->getValue(1), this->Map.at(this->getKey(1)));
|
||||
EXPECT_EQ(this->getValue(2), this->Map.at(this->getKey(2)));
|
||||
|
||||
this->Map.at(this->getKey(0)) = this->getValue(1);
|
||||
EXPECT_EQ(this->getValue(1), this->Map.at(this->getKey(0)));
|
||||
|
||||
const auto &ConstMap = this->Map;
|
||||
EXPECT_EQ(this->getValue(1), ConstMap.at(this->getKey(0)));
|
||||
EXPECT_EQ(this->getValue(1), ConstMap.at(this->getKey(1)));
|
||||
EXPECT_EQ(this->getValue(2), ConstMap.at(this->getKey(2)));
|
||||
}
|
||||
|
||||
// Test clear() method
|
||||
@@ -245,6 +271,25 @@ TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) {
|
||||
EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);
|
||||
}
|
||||
|
||||
// Test range constructors.
|
||||
TYPED_TEST(DenseMapTest, RangeConstructorTest) {
|
||||
using KeyAndValue =
|
||||
std::pair<typename TypeParam::key_type, typename TypeParam::mapped_type>;
|
||||
KeyAndValue PlainArray[] = {{this->getKey(0), this->getValue(0)},
|
||||
{this->getKey(1), this->getValue(1)}};
|
||||
|
||||
TypeParam MapFromRange(wpi::util::from_range, PlainArray);
|
||||
EXPECT_EQ(2u, MapFromRange.size());
|
||||
EXPECT_EQ(this->getValue(0), MapFromRange[this->getKey(0)]);
|
||||
EXPECT_EQ(this->getValue(1), MapFromRange[this->getKey(1)]);
|
||||
|
||||
TypeParam MapFromInitList({{this->getKey(0), this->getValue(1)},
|
||||
{this->getKey(1), this->getValue(2)}});
|
||||
EXPECT_EQ(2u, MapFromInitList.size());
|
||||
EXPECT_EQ(this->getValue(1), MapFromInitList[this->getKey(0)]);
|
||||
EXPECT_EQ(this->getValue(2), MapFromInitList[this->getKey(1)]);
|
||||
}
|
||||
|
||||
// Test copying from a default-constructed map.
|
||||
TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) {
|
||||
TypeParam copyMap(this->Map);
|
||||
@@ -363,6 +408,58 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) {
|
||||
EXPECT_TRUE(cit == cit2);
|
||||
}
|
||||
|
||||
// TYPED_TEST below cycles through different types. We define UniversalSmallSet
|
||||
// here so that we'll use SmallSet or SmallPtrSet depending on whether the
|
||||
// element type is a pointer.
|
||||
template <typename T, unsigned N>
|
||||
using UniversalSmallSet =
|
||||
std::conditional_t<std::is_pointer_v<T>, SmallPtrSet<T, N>, SmallSet<T, N>>;
|
||||
|
||||
TYPED_TEST(DenseMapTest, KeysValuesIterator) {
|
||||
UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
|
||||
UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
|
||||
for (int I = 0; I < 10; ++I) {
|
||||
auto K = this->getKey(I);
|
||||
auto V = this->getValue(I);
|
||||
Keys.insert(K);
|
||||
Values.insert(V);
|
||||
this->Map[K] = V;
|
||||
}
|
||||
|
||||
UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
|
||||
UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
|
||||
for (auto K : this->Map.keys())
|
||||
ActualKeys.insert(K);
|
||||
for (auto V : this->Map.values())
|
||||
ActualValues.insert(V);
|
||||
|
||||
EXPECT_EQ(Keys, ActualKeys);
|
||||
EXPECT_EQ(Values, ActualValues);
|
||||
}
|
||||
|
||||
TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) {
|
||||
UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
|
||||
UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
|
||||
for (int I = 0; I < 10; ++I) {
|
||||
auto K = this->getKey(I);
|
||||
auto V = this->getValue(I);
|
||||
Keys.insert(K);
|
||||
Values.insert(V);
|
||||
this->Map[K] = V;
|
||||
}
|
||||
|
||||
const TypeParam &ConstMap = this->Map;
|
||||
UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
|
||||
UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
|
||||
for (auto K : ConstMap.keys())
|
||||
ActualKeys.insert(K);
|
||||
for (auto V : ConstMap.values())
|
||||
ActualValues.insert(V);
|
||||
|
||||
EXPECT_EQ(Keys, ActualKeys);
|
||||
EXPECT_EQ(Values, ActualValues);
|
||||
}
|
||||
|
||||
// Test initializer list construction.
|
||||
TEST(DenseMapCustomTest, InitializerList) {
|
||||
DenseMap<int, int> M({{0, 0}, {0, 1}, {1, 2}});
|
||||
@@ -383,6 +480,28 @@ TEST(DenseMapCustomTest, EqualityComparison) {
|
||||
EXPECT_NE(M1, M3);
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, InsertRange) {
|
||||
DenseMap<int, int> M;
|
||||
|
||||
std::pair<int, int> InputVals[3] = {{0, 0}, {0, 1}, {1, 2}};
|
||||
M.insert_range(InputVals);
|
||||
|
||||
EXPECT_EQ(M.size(), 2u);
|
||||
EXPECT_THAT(M, testing::UnorderedElementsAre(testing::Pair(0, 0),
|
||||
testing::Pair(1, 2)));
|
||||
}
|
||||
|
||||
TEST(SmallDenseMapCustomTest, InsertRange) {
|
||||
SmallDenseMap<int, int> M;
|
||||
|
||||
std::pair<int, int> InputVals[3] = {{0, 0}, {0, 1}, {1, 2}};
|
||||
M.insert_range(InputVals);
|
||||
|
||||
EXPECT_EQ(M.size(), 2u);
|
||||
EXPECT_THAT(M, testing::UnorderedElementsAre(testing::Pair(0, 0),
|
||||
testing::Pair(1, 2)));
|
||||
}
|
||||
|
||||
// Test for the default minimum size of a DenseMap
|
||||
TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
|
||||
// IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and
|
||||
@@ -527,6 +646,68 @@ TEST(DenseMapCustomTest, InsertOrAssignTest) {
|
||||
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, EmplaceOrAssign) {
|
||||
DenseMap<int, CountCopyAndMove> Map;
|
||||
|
||||
CountCopyAndMove::ResetCounts();
|
||||
auto Try0 = Map.emplace_or_assign(3, 3);
|
||||
EXPECT_TRUE(Try0.second);
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalMoves());
|
||||
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
|
||||
|
||||
CountCopyAndMove::ResetCounts();
|
||||
auto Try1 = Map.emplace_or_assign(3, 4);
|
||||
EXPECT_FALSE(Try1.second);
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
|
||||
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
|
||||
EXPECT_EQ(0, CountCopyAndMove::MoveConstructions);
|
||||
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
|
||||
|
||||
int Key = 5;
|
||||
CountCopyAndMove::ResetCounts();
|
||||
auto Try2 = Map.emplace_or_assign(Key, 3);
|
||||
EXPECT_TRUE(Try2.second);
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalMoves());
|
||||
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
|
||||
|
||||
CountCopyAndMove::ResetCounts();
|
||||
auto Try3 = Map.emplace_or_assign(Key, 4);
|
||||
EXPECT_FALSE(Try3.second);
|
||||
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
|
||||
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
|
||||
EXPECT_EQ(0, CountCopyAndMove::MoveConstructions);
|
||||
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
|
||||
}
|
||||
|
||||
struct NonDefaultConstructible {
|
||||
unsigned V;
|
||||
NonDefaultConstructible(unsigned V) : V(V) {};
|
||||
bool operator==(const NonDefaultConstructible &Other) const {
|
||||
return V == Other.V;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(DenseMapCustomTest, LookupOr) {
|
||||
DenseMap<int, NonDefaultConstructible> M;
|
||||
|
||||
M.insert_or_assign(0, 3u);
|
||||
M.insert_or_assign(1, 2u);
|
||||
M.insert_or_assign(1, 0u);
|
||||
|
||||
EXPECT_EQ(M.lookup_or(0, 4u), 3u);
|
||||
EXPECT_EQ(M.lookup_or(1, 4u), 0u);
|
||||
EXPECT_EQ(M.lookup_or(2, 4u), 4u);
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, LookupOrConstness) {
|
||||
DenseMap<int, unsigned *> M;
|
||||
unsigned Default = 3u;
|
||||
unsigned *Ret = M.lookup_or(0, &Default);
|
||||
EXPECT_EQ(Ret, &Default);
|
||||
}
|
||||
|
||||
// Key traits that allows lookup with either an unsigned or char* key;
|
||||
// In the latter case, "a" == 0, "b" == 1 and so on.
|
||||
struct TestDenseMapInfo {
|
||||
@@ -568,6 +749,15 @@ TEST(DenseMapCustomTest, FindAsTest) {
|
||||
EXPECT_TRUE(map.find_as("d") == map.end());
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, SmallDenseMapFromRange) {
|
||||
std::pair<int, std::string_view> PlainArray[] = {{0, "0"}, {1, "1"}, {2, "2"}};
|
||||
SmallDenseMap<int, std::string_view> M(wpi::util::from_range, PlainArray);
|
||||
EXPECT_EQ(3u, M.size());
|
||||
using testing::Pair;
|
||||
EXPECT_THAT(M, testing::UnorderedElementsAre(Pair(0, "0"), Pair(1, "1"),
|
||||
Pair(2, "2")));
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, SmallDenseMapInitializerList) {
|
||||
SmallDenseMap<int, int> M = {{0, 0}, {0, 1}, {1, 2}};
|
||||
EXPECT_EQ(2u, M.size());
|
||||
@@ -761,4 +951,133 @@ TEST(DenseMapCustomTest, VariantSupport) {
|
||||
EXPECT_FALSE(DenseMapInfo<variant>::isEqual(Keys[2], Keys[2]));
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, InitSize) {
|
||||
constexpr unsigned ElemSize = sizeof(std::pair<int *, int>);
|
||||
|
||||
{
|
||||
DenseMap<int *, int> Map;
|
||||
EXPECT_EQ(ElemSize * 0U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
DenseMap<int *, int> Map(0);
|
||||
EXPECT_EQ(ElemSize * 0U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
DenseMap<int *, int> Map(1);
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
DenseMap<int *, int> Map(2);
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
DenseMap<int *, int> Map(3);
|
||||
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
int A, B;
|
||||
DenseMap<int *, int> Map = {{&A, 1}, {&B, 2}};
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
int A, B, C;
|
||||
DenseMap<int *, int> Map = {{&A, 1}, {&B, 2}, {&C, 3}};
|
||||
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SmallDenseMapCustomTest, InitSize) {
|
||||
constexpr unsigned ElemSize = sizeof(std::pair<int *, int>);
|
||||
{
|
||||
SmallDenseMap<int *, int> Map;
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
SmallDenseMap<int *, int> Map(0);
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
SmallDenseMap<int *, int> Map(1);
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
SmallDenseMap<int *, int> Map(2);
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
SmallDenseMap<int *, int> Map(3);
|
||||
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
int A, B;
|
||||
SmallDenseMap<int *, int> Map = {{&A, 1}, {&B, 2}};
|
||||
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
|
||||
}
|
||||
{
|
||||
int A, B, C;
|
||||
SmallDenseMap<int *, int> Map = {{&A, 1}, {&B, 2}, {&C, 3}};
|
||||
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, KeyDtor) {
|
||||
// This test relies on CtorTester being non-trivially destructible.
|
||||
static_assert(!std::is_trivially_destructible_v<CtorTester>,
|
||||
"CtorTester must not be trivially destructible");
|
||||
|
||||
// Test that keys are destructed on scope exit.
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
{
|
||||
DenseMap<CtorTester, int, CtorTesterMapInfo> Map;
|
||||
Map.try_emplace(CtorTester(0), 1);
|
||||
Map.try_emplace(CtorTester(1), 2);
|
||||
EXPECT_EQ(2u, CtorTester::getNumConstructed());
|
||||
}
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
|
||||
// Test that keys are destructed on erase and shrink_and_clear.
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
{
|
||||
DenseMap<CtorTester, int, CtorTesterMapInfo> Map;
|
||||
Map.try_emplace(CtorTester(0), 1);
|
||||
Map.try_emplace(CtorTester(1), 2);
|
||||
EXPECT_EQ(2u, CtorTester::getNumConstructed());
|
||||
Map.erase(CtorTester(1));
|
||||
EXPECT_EQ(1u, CtorTester::getNumConstructed());
|
||||
Map.shrink_and_clear();
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
}
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
}
|
||||
|
||||
TEST(DenseMapCustomTest, ValueDtor) {
|
||||
// This test relies on CtorTester being non-trivially destructible.
|
||||
static_assert(!std::is_trivially_destructible_v<CtorTester>,
|
||||
"CtorTester must not be trivially destructible");
|
||||
|
||||
// Test that values are destructed on scope exit.
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
{
|
||||
DenseMap<int, CtorTester> Map;
|
||||
Map.try_emplace(0, CtorTester(1));
|
||||
Map.try_emplace(1, CtorTester(2));
|
||||
EXPECT_EQ(2u, CtorTester::getNumConstructed());
|
||||
}
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
|
||||
// Test that values are destructed on erase and shrink_and_clear.
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
{
|
||||
DenseMap<int, CtorTester> Map;
|
||||
Map.try_emplace(0, CtorTester(1));
|
||||
Map.try_emplace(1, CtorTester(2));
|
||||
EXPECT_EQ(2u, CtorTester::getNumConstructed());
|
||||
Map.erase(1);
|
||||
EXPECT_EQ(1u, CtorTester::getNumConstructed());
|
||||
Map.shrink_and_clear();
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
}
|
||||
EXPECT_EQ(0u, CtorTester::getNumConstructed());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -23,16 +23,15 @@ TEST(Endian, Read) {
|
||||
unsigned char littleval[] = {0x00, 0x04, 0x03, 0x02, 0x01};
|
||||
int32_t BigAsHost = 0x00010203;
|
||||
EXPECT_EQ(BigAsHost,
|
||||
(endian::read<int32_t, wpi::util::endianness::big, unaligned>(bigval)));
|
||||
(endian::read<int32_t, unaligned>(bigval, wpi::util::endianness::big)));
|
||||
int32_t LittleAsHost = 0x02030400;
|
||||
EXPECT_EQ(
|
||||
LittleAsHost,
|
||||
(endian::read<int32_t, wpi::util::endianness::little, unaligned>(littleval)));
|
||||
EXPECT_EQ(LittleAsHost, (endian::read<int32_t, unaligned>(
|
||||
littleval, wpi::util::endianness::little)));
|
||||
|
||||
EXPECT_EQ(
|
||||
(endian::read<int32_t, wpi::util::endianness::big, unaligned>(bigval + 1)),
|
||||
(endian::read<int32_t, wpi::util::endianness::little, unaligned>(littleval +
|
||||
1)));
|
||||
(endian::read<int32_t, unaligned>(bigval + 1, wpi::util::endianness::big)),
|
||||
(endian::read<int32_t, unaligned>(littleval + 1,
|
||||
wpi::util::endianness::little)));
|
||||
}
|
||||
|
||||
TEST(Endian, WriteNext) {
|
||||
@@ -200,26 +199,26 @@ TEST(Endian, WriteBitAligned) {
|
||||
|
||||
TEST(Endian, Write) {
|
||||
unsigned char data[5];
|
||||
endian::write<int32_t, wpi::util::endianness::big, unaligned>(data, -1362446643);
|
||||
endian::write<int32_t, unaligned>(data, -1362446643, wpi::util::endianness::big);
|
||||
EXPECT_EQ(data[0], 0xAE);
|
||||
EXPECT_EQ(data[1], 0xCA);
|
||||
EXPECT_EQ(data[2], 0xB6);
|
||||
EXPECT_EQ(data[3], 0xCD);
|
||||
endian::write<int32_t, wpi::util::endianness::big, unaligned>(data + 1,
|
||||
-1362446643);
|
||||
endian::write<int32_t, unaligned>(data + 1, -1362446643,
|
||||
wpi::util::endianness::big);
|
||||
EXPECT_EQ(data[1], 0xAE);
|
||||
EXPECT_EQ(data[2], 0xCA);
|
||||
EXPECT_EQ(data[3], 0xB6);
|
||||
EXPECT_EQ(data[4], 0xCD);
|
||||
|
||||
endian::write<int32_t, wpi::util::endianness::little, unaligned>(data,
|
||||
-1362446643);
|
||||
endian::write<int32_t, unaligned>(data, -1362446643,
|
||||
wpi::util::endianness::little);
|
||||
EXPECT_EQ(data[0], 0xCD);
|
||||
EXPECT_EQ(data[1], 0xB6);
|
||||
EXPECT_EQ(data[2], 0xCA);
|
||||
EXPECT_EQ(data[3], 0xAE);
|
||||
endian::write<int32_t, wpi::util::endianness::little, unaligned>(data + 1,
|
||||
-1362446643);
|
||||
endian::write<int32_t, unaligned>(data + 1, -1362446643,
|
||||
wpi::util::endianness::little);
|
||||
EXPECT_EQ(data[1], 0xCD);
|
||||
EXPECT_EQ(data[2], 0xB6);
|
||||
EXPECT_EQ(data[3], 0xCA);
|
||||
@@ -236,6 +235,7 @@ TEST(Endian, PackedEndianSpecificIntegral) {
|
||||
reinterpret_cast<little32_t *>(little + 1);
|
||||
|
||||
EXPECT_EQ(*big_val, *little_val);
|
||||
EXPECT_EQ(big_val->value(), little_val->value());
|
||||
}
|
||||
|
||||
TEST(Endian, PacketEndianSpecificIntegralAsEnum) {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace wpi::util;
|
||||
|
||||
|
||||
@@ -152,23 +152,23 @@ TEST(MathExtras, PowerOf2Ceil) {
|
||||
EXPECT_EQ(8U, PowerOf2Ceil(7U));
|
||||
}
|
||||
|
||||
TEST(MathExtras, CTLog2) {
|
||||
EXPECT_EQ(CTLog2<1ULL << 0>(), 0U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 1>(), 1U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 2>(), 2U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 3>(), 3U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 4>(), 4U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 5>(), 5U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 6>(), 6U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 7>(), 7U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 8>(), 8U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 9>(), 9U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 10>(), 10U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 11>(), 11U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 12>(), 12U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 13>(), 13U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 14>(), 14U);
|
||||
EXPECT_EQ(CTLog2<1ULL << 15>(), 15U);
|
||||
TEST(MathExtras, ConstantLog2) {
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 0>(), 0U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 1>(), 1U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 2>(), 2U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 3>(), 3U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 4>(), 4U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 5>(), 5U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 6>(), 6U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 7>(), 7U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 8>(), 8U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 9>(), 9U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 10>(), 10U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 11>(), 11U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 12>(), 12U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 13>(), 13U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 14>(), 14U);
|
||||
EXPECT_EQ(ConstantLog2<1ULL << 15>(), 15U);
|
||||
}
|
||||
|
||||
TEST(MathExtras, MinAlign) {
|
||||
|
||||
@@ -12,9 +12,9 @@ using namespace wpi::util;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef PointerUnion<int *, float *> PU;
|
||||
typedef PointerUnion<int *, float *, long long *> PU3;
|
||||
typedef PointerUnion<int *, float *, long long *, double *> PU4;
|
||||
using PU = PointerUnion<int *, float *>;
|
||||
using PU3 = PointerUnion<int *, float *, long long *>;
|
||||
using PU4 = PointerUnion<int *, float *, long long *, double *>;
|
||||
|
||||
struct PointerUnionTest : public testing::Test {
|
||||
float f;
|
||||
@@ -116,9 +116,9 @@ TEST_F(PointerUnionTest, Get) {
|
||||
|
||||
template<int I> struct alignas(8) Aligned {};
|
||||
|
||||
typedef PointerUnion<Aligned<0> *, Aligned<1> *, Aligned<2> *, Aligned<3> *,
|
||||
Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>
|
||||
PU8;
|
||||
using PU8 =
|
||||
PointerUnion<Aligned<0> *, Aligned<1> *, Aligned<2> *, Aligned<3> *,
|
||||
Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>;
|
||||
|
||||
TEST_F(PointerUnionTest, ManyElements) {
|
||||
Aligned<0> a0;
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "CountCopyAndMove.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
@@ -45,6 +49,25 @@ TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRefT) {
|
||||
wpi::util::remove_cvref_t<From>>::value));
|
||||
}
|
||||
|
||||
template <typename T> class TypeIdentityTest : public ::testing::Test {
|
||||
public:
|
||||
using TypeIdentity = wpi::util::type_identity<T>;
|
||||
};
|
||||
|
||||
struct A {
|
||||
struct B {};
|
||||
};
|
||||
using TypeIdentityTestTypes =
|
||||
::testing::Types<int, volatile int, A, const A::B>;
|
||||
|
||||
TYPED_TEST_SUITE(TypeIdentityTest, TypeIdentityTestTypes, /*NameGenerator*/);
|
||||
|
||||
TYPED_TEST(TypeIdentityTest, Identity) {
|
||||
// TestFixture is the instantiated TypeIdentityTest.
|
||||
EXPECT_TRUE(
|
||||
(std::is_same_v<TypeParam, typename TestFixture::TypeIdentity::type>));
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformStd) {
|
||||
std::optional<int> A;
|
||||
|
||||
@@ -123,6 +146,26 @@ TEST(TransformTest, MoveTransformLlvm) {
|
||||
EXPECT_EQ(0, CountCopyAndMove::Destructions);
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformCategory) {
|
||||
struct StructA {
|
||||
int x;
|
||||
};
|
||||
struct StructB : StructA {
|
||||
StructB(StructA &&A) : StructA(std::move(A)) {}
|
||||
};
|
||||
|
||||
std::optional<StructA> A{StructA{}};
|
||||
wpi::util::transformOptional(A, [](auto &&s) {
|
||||
EXPECT_FALSE(std::is_rvalue_reference_v<decltype(s)>);
|
||||
return StructB{std::move(s)};
|
||||
});
|
||||
|
||||
wpi::util::transformOptional(std::move(A), [](auto &&s) {
|
||||
EXPECT_TRUE(std::is_rvalue_reference_v<decltype(s)>);
|
||||
return StructB{std::move(s)};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(TransformTest, ToUnderlying) {
|
||||
enum E { A1 = 0, B1 = -1 };
|
||||
static_assert(wpi::util::to_underlying(A1) == 0);
|
||||
@@ -140,4 +183,26 @@ TEST(TransformTest, ToUnderlying) {
|
||||
static_assert(wpi::util::to_underlying(E3::B3) == 0);
|
||||
}
|
||||
|
||||
TEST(STLForwardCompatTest, IdentityCxx20) {
|
||||
wpi::util::identity identity;
|
||||
|
||||
// Test with an lvalue.
|
||||
int X = 42;
|
||||
int &Y = identity(X);
|
||||
EXPECT_EQ(&X, &Y);
|
||||
|
||||
// Test with a const lvalue.
|
||||
const int CX = 10;
|
||||
const int &CY = identity(CX);
|
||||
EXPECT_EQ(&CX, &CY);
|
||||
|
||||
// Test with an rvalue.
|
||||
EXPECT_EQ(identity(123), 123);
|
||||
|
||||
// Test perfect forwarding.
|
||||
static_assert(std::is_same_v<int &, decltype(identity(X))>);
|
||||
static_assert(std::is_same_v<const int &, decltype(identity(CX))>);
|
||||
static_assert(std::is_same_v<int &&, decltype(identity(int(5)))>);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -58,7 +58,7 @@ TEST(SmallPtrSetTest, GrowthTest) {
|
||||
|
||||
|
||||
SmallPtrSet<int *, 4> s;
|
||||
typedef SmallPtrSet<int *, 4>::iterator iter;
|
||||
using iter = SmallPtrSet<int *, 4>::iterator;
|
||||
|
||||
s.insert(&buf[0]);
|
||||
s.insert(&buf[1]);
|
||||
@@ -412,6 +412,25 @@ TEST(SmallPtrSetTest, RemoveIf) {
|
||||
EXPECT_FALSE(Removed);
|
||||
}
|
||||
|
||||
TEST(SmallPtrSetTest, CtorRange) {
|
||||
int V0 = 0;
|
||||
int V1 = 1;
|
||||
int V2 = 2;
|
||||
int *Args[] = {&V2, &V0, &V1};
|
||||
SmallPtrSet<int *, 4> Set(wpi::util::from_range, Args);
|
||||
EXPECT_THAT(Set, UnorderedElementsAre(&V0, &V1, &V2));
|
||||
}
|
||||
|
||||
TEST(SmallPtrSetTest, InsertRange) {
|
||||
int V0 = 0;
|
||||
int V1 = 1;
|
||||
int V2 = 2;
|
||||
SmallPtrSet<int *, 4> Set;
|
||||
int *Args[] = {&V2, &V0, &V1};
|
||||
Set.insert_range(Args);
|
||||
EXPECT_THAT(Set, UnorderedElementsAre(&V0, &V1, &V2));
|
||||
}
|
||||
|
||||
TEST(SmallPtrSetTest, Reserve) {
|
||||
// Check that we don't do anything silly when using reserve().
|
||||
SmallPtrSet<int *, 4> Set;
|
||||
@@ -457,4 +476,8 @@ TEST(SmallPtrSetTest, Reserve) {
|
||||
EXPECT_EQ(Set.capacity(), 128u);
|
||||
EXPECT_EQ(Set.size(), 6u);
|
||||
EXPECT_THAT(Set, UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4], &Vals[5]));
|
||||
|
||||
// Reserving 192 should result in 256 buckets.
|
||||
Set.reserve(192);
|
||||
EXPECT_EQ(Set.capacity(), 256u);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,6 @@ TEST(SmallSetTest, ConstructorIteratorPair) {
|
||||
EXPECT_THAT(S, testing::UnorderedElementsAreArray(L));
|
||||
}
|
||||
|
||||
TEST(SmallSet, ConstructorRange) {
|
||||
std::initializer_list<int> L = {1, 2, 3, 4, 5};
|
||||
|
||||
SmallSet<int, 4> S(wpi::util::make_range(std::begin(L), std::end(L)));
|
||||
EXPECT_THAT(S, testing::UnorderedElementsAreArray(L));
|
||||
}
|
||||
|
||||
TEST(SmallSet, ConstructorInitializerList) {
|
||||
std::initializer_list<int> L = {1, 2, 3, 4, 5};
|
||||
SmallSet<int, 4> S = {1, 2, 3, 4, 5};
|
||||
@@ -127,6 +120,19 @@ TEST(SmallSetTest, InsertPerfectFwd) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SmallSetTest, CtorRange) {
|
||||
constexpr unsigned Args[] = {3, 1, 2};
|
||||
SmallSet<int, 4> s1(wpi::util::from_range, Args);
|
||||
EXPECT_THAT(s1, ::testing::UnorderedElementsAre(1, 2, 3));
|
||||
}
|
||||
|
||||
TEST(SmallSetTest, InsertRange) {
|
||||
SmallSet<int, 4> s1;
|
||||
constexpr unsigned Args[] = {3, 1, 2};
|
||||
s1.insert_range(Args);
|
||||
EXPECT_THAT(s1, ::testing::UnorderedElementsAre(1, 2, 3));
|
||||
}
|
||||
|
||||
TEST(SmallSetTest, Grow) {
|
||||
SmallSet<int, 4> s1;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace {
|
||||
// Test fixture class
|
||||
class SmallStringTest : public testing::Test {
|
||||
protected:
|
||||
typedef SmallString<40> StringType;
|
||||
using StringType = SmallString<40>;
|
||||
|
||||
StringType theString;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/Compiler.hpp"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <array>
|
||||
#include <list>
|
||||
@@ -132,24 +133,24 @@ public:
|
||||
return c0.getValue() == c1.getValue();
|
||||
}
|
||||
|
||||
friend bool LLVM_ATTRIBUTE_UNUSED operator!=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
[[maybe_unused]] friend bool operator!=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
return c0.getValue() != c1.getValue();
|
||||
}
|
||||
|
||||
friend bool operator<(const Constructable &c0, const Constructable &c1) {
|
||||
return c0.getValue() < c1.getValue();
|
||||
}
|
||||
friend bool LLVM_ATTRIBUTE_UNUSED operator<=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
[[maybe_unused]] friend bool operator<=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
return c0.getValue() <= c1.getValue();
|
||||
}
|
||||
friend bool LLVM_ATTRIBUTE_UNUSED operator>(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
[[maybe_unused]] friend bool operator>(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
return c0.getValue() > c1.getValue();
|
||||
}
|
||||
friend bool LLVM_ATTRIBUTE_UNUSED operator>=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
[[maybe_unused]] friend bool operator>=(const Constructable &c0,
|
||||
const Constructable &c1) {
|
||||
return c0.getValue() >= c1.getValue();
|
||||
}
|
||||
};
|
||||
@@ -163,7 +164,7 @@ int Constructable::numCopyAssignmentCalls;
|
||||
int Constructable::numMoveAssignmentCalls;
|
||||
|
||||
struct NonCopyable {
|
||||
NonCopyable() {}
|
||||
NonCopyable() = default;
|
||||
NonCopyable(NonCopyable &&) {}
|
||||
NonCopyable &operator=(NonCopyable &&) { return *this; }
|
||||
private:
|
||||
@@ -230,13 +231,10 @@ protected:
|
||||
VectorT otherVector;
|
||||
};
|
||||
|
||||
|
||||
typedef ::testing::Types<SmallVector<Constructable, 0>,
|
||||
SmallVector<Constructable, 1>,
|
||||
SmallVector<Constructable, 2>,
|
||||
SmallVector<Constructable, 4>,
|
||||
SmallVector<Constructable, 5>
|
||||
> SmallVectorTestTypes;
|
||||
using SmallVectorTestTypes = ::testing::Types<
|
||||
SmallVector<Constructable, 0>, SmallVector<Constructable, 1>,
|
||||
SmallVector<Constructable, 2>, SmallVector<Constructable, 4>,
|
||||
SmallVector<Constructable, 5>>;
|
||||
TYPED_TEST_SUITE(SmallVectorTest, SmallVectorTestTypes, );
|
||||
|
||||
// Constructor test.
|
||||
@@ -541,11 +539,11 @@ TYPED_TEST(SmallVectorTest, AppendNonIterTest) {
|
||||
}
|
||||
|
||||
struct output_iterator {
|
||||
typedef std::output_iterator_tag iterator_category;
|
||||
typedef int value_type;
|
||||
typedef int difference_type;
|
||||
typedef value_type *pointer;
|
||||
typedef value_type &reference;
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = int;
|
||||
using difference_type = int;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
operator int() { return 2; }
|
||||
operator Constructable() { return 7; }
|
||||
};
|
||||
@@ -604,6 +602,15 @@ TYPED_TEST(SmallVectorTest, AssignSmallVector) {
|
||||
assertValuesInOrder(V, 2u, 7, 7);
|
||||
}
|
||||
|
||||
TYPED_TEST(SmallVectorTest, AssignSpan) {
|
||||
SCOPED_TRACE("AssignSpan");
|
||||
auto &V = this->theVector;
|
||||
Constructable Other[] = {7, 8, 9};
|
||||
V.push_back(Constructable(1));
|
||||
V.assign(std::span<const Constructable>(Other));
|
||||
assertValuesInOrder(V, 3u, 7, 8, 9);
|
||||
}
|
||||
|
||||
// Move-assign test
|
||||
TYPED_TEST(SmallVectorTest, MoveAssignTest) {
|
||||
SCOPED_TRACE("MoveAssignTest");
|
||||
@@ -891,7 +898,7 @@ protected:
|
||||
VectorT2 otherVector;
|
||||
};
|
||||
|
||||
typedef ::testing::Types<
|
||||
using DualSmallVectorTestTypes = ::testing::Types<
|
||||
// Small mode -> Small mode.
|
||||
std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 4>>,
|
||||
// Small mode -> Big mode.
|
||||
@@ -899,8 +906,7 @@ typedef ::testing::Types<
|
||||
// Big mode -> Small mode.
|
||||
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 4>>,
|
||||
// Big mode -> Big mode.
|
||||
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>
|
||||
> DualSmallVectorTestTypes;
|
||||
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>>;
|
||||
|
||||
TYPED_TEST_SUITE(DualSmallVectorsTest, DualSmallVectorTestTypes, );
|
||||
|
||||
@@ -1134,6 +1140,17 @@ TEST(SmallVectorTest, DefaultInlinedElements) {
|
||||
EXPECT_EQ(NestedV[0][0][0], 42);
|
||||
}
|
||||
|
||||
namespace namespace_with_adl {
|
||||
struct MyVector {
|
||||
std::vector<int> data;
|
||||
};
|
||||
|
||||
std::vector<int>::const_iterator begin(const MyVector &V) {
|
||||
return V.data.begin();
|
||||
}
|
||||
std::vector<int>::const_iterator end(const MyVector &V) { return V.data.end(); }
|
||||
} // namespace namespace_with_adl
|
||||
|
||||
TEST(SmallVectorTest, ToVector) {
|
||||
{
|
||||
std::vector<char> v = {'a', 'b', 'c'};
|
||||
@@ -1151,6 +1168,15 @@ TEST(SmallVectorTest, ToVector) {
|
||||
for (size_t I = 0; I < v.size(); ++I)
|
||||
EXPECT_EQ(v[I], Vector[I]);
|
||||
}
|
||||
{
|
||||
// Check that to_vector and to_vector_of work with types that require ADL
|
||||
// for being/end iterators.
|
||||
namespace_with_adl::MyVector V = {{1, 2, 3}};
|
||||
auto IntVector = to_vector(V);
|
||||
EXPECT_THAT(IntVector, testing::ElementsAre(1, 2, 3));
|
||||
IntVector = to_vector<3>(V);
|
||||
EXPECT_THAT(IntVector, testing::ElementsAre(1, 2, 3));
|
||||
}
|
||||
}
|
||||
|
||||
struct To {
|
||||
@@ -1209,6 +1235,15 @@ TEST(SmallVectorTest, ToVectorOf) {
|
||||
for (size_t I = 0; I < StdVector.size(); ++I)
|
||||
EXPECT_EQ(StdVector[I], Vector[I]);
|
||||
}
|
||||
{
|
||||
// Check that to_vector works with types that require ADL for being/end
|
||||
// iterators.
|
||||
namespace_with_adl::MyVector V = {{1, 2, 3}};
|
||||
auto UnsignedVector = to_vector_of<unsigned>(V);
|
||||
EXPECT_THAT(UnsignedVector, testing::ElementsAre(1u, 2u, 3u));
|
||||
UnsignedVector = to_vector_of<unsigned, 3>(V);
|
||||
EXPECT_THAT(UnsignedVector, testing::ElementsAre(1u, 2u, 3u));
|
||||
}
|
||||
}
|
||||
|
||||
template <class VectorT>
|
||||
|
||||
Reference in New Issue
Block a user