mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
[upstream_utils] Upgrade to LLVM 20.1.7 (#8033)
Also removes xxhash, Hashing, and MapVector to reduce the size of the patches and to speed up compile times by a smidge.
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
|
||||
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
|
||||
wpi::allocate_buffer(size_t Size, size_t Alignment) {
|
||||
return ::operator new(Size
|
||||
void *Result = ::operator new(Size,
|
||||
#ifdef __cpp_aligned_new
|
||||
,
|
||||
std::align_val_t(Alignment)
|
||||
std::align_val_t(Alignment),
|
||||
#endif
|
||||
);
|
||||
std::nothrow);
|
||||
if (Result == nullptr) {
|
||||
report_bad_alloc_error("Buffer allocation failed");
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void wpi::deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {
|
||||
|
||||
@@ -62,7 +62,25 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
|
||||
return std::make_pair(Bucket, true);
|
||||
}
|
||||
|
||||
const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
|
||||
const void *const *SmallPtrSetImplBase::doFind(const void *Ptr) const {
|
||||
unsigned BucketNo =
|
||||
DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize - 1);
|
||||
unsigned ProbeAmt = 1;
|
||||
while (true) {
|
||||
const void *const *Bucket = CurArray + BucketNo;
|
||||
if (LLVM_LIKELY(*Bucket == Ptr))
|
||||
return Bucket;
|
||||
if (LLVM_LIKELY(*Bucket == getEmptyMarker()))
|
||||
return nullptr;
|
||||
|
||||
// Otherwise, it's a hash collision or a tombstone, continue quadratic
|
||||
// probing.
|
||||
BucketNo += ProbeAmt++;
|
||||
BucketNo &= CurArraySize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
const void *const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
|
||||
unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
|
||||
unsigned ArraySize = CurArraySize;
|
||||
unsigned ProbeAmt = 1;
|
||||
@@ -116,32 +134,33 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) {
|
||||
free(OldBuckets);
|
||||
NumNonEmpty -= NumTombstones;
|
||||
NumTombstones = 0;
|
||||
IsSmall = false;
|
||||
}
|
||||
|
||||
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &that) {
|
||||
SmallArray = SmallStorage;
|
||||
|
||||
// If we're becoming small, prepare to insert into our stack space
|
||||
if (that.isSmall()) {
|
||||
CurArray = SmallArray;
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
IsSmall = that.isSmall();
|
||||
if (IsSmall) {
|
||||
// If we're becoming small, prepare to insert into our stack space
|
||||
CurArray = SmallStorage;
|
||||
} else {
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
|
||||
}
|
||||
|
||||
// Copy over the that array.
|
||||
CopyHelper(that);
|
||||
copyHelper(that);
|
||||
}
|
||||
|
||||
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
|
||||
unsigned SmallSize,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &&that) {
|
||||
SmallArray = SmallStorage;
|
||||
MoveHelper(SmallSize, std::move(that));
|
||||
moveHelper(SmallStorage, SmallSize, RHSSmallStorage, std::move(that));
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
|
||||
void SmallPtrSetImplBase::copyFrom(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &RHS) {
|
||||
assert(&RHS != this && "Self-copy should be handled by the caller.");
|
||||
|
||||
if (isSmall() && RHS.isSmall())
|
||||
@@ -152,8 +171,9 @@ void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
|
||||
if (RHS.isSmall()) {
|
||||
if (!isSmall())
|
||||
free(CurArray);
|
||||
CurArray = SmallArray;
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
CurArray = SmallStorage;
|
||||
IsSmall = true;
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
} else if (CurArraySize != RHS.CurArraySize) {
|
||||
if (isSmall())
|
||||
CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
|
||||
@@ -162,12 +182,13 @@ void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
|
||||
sizeof(void*) * RHS.CurArraySize);
|
||||
CurArray = T;
|
||||
}
|
||||
IsSmall = false;
|
||||
}
|
||||
|
||||
CopyHelper(RHS);
|
||||
copyHelper(RHS);
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
|
||||
void SmallPtrSetImplBase::copyHelper(const SmallPtrSetImplBase &RHS) {
|
||||
// Copy over the new array size
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
|
||||
@@ -178,39 +199,46 @@ void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
|
||||
void SmallPtrSetImplBase::moveFrom(const void **SmallStorage,
|
||||
unsigned SmallSize,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &&RHS) {
|
||||
if (!isSmall())
|
||||
free(CurArray);
|
||||
MoveHelper(SmallSize, std::move(RHS));
|
||||
moveHelper(SmallStorage, SmallSize, RHSSmallStorage, std::move(RHS));
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
|
||||
void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
|
||||
unsigned SmallSize,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &&RHS) {
|
||||
assert(&RHS != this && "Self-move should be handled by the caller.");
|
||||
|
||||
if (RHS.isSmall()) {
|
||||
// Copy a small RHS rather than moving.
|
||||
CurArray = SmallArray;
|
||||
CurArray = SmallStorage;
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
|
||||
} else {
|
||||
CurArray = RHS.CurArray;
|
||||
RHS.CurArray = RHS.SmallArray;
|
||||
RHS.CurArray = RHSSmallStorage;
|
||||
}
|
||||
|
||||
// Copy the rest of the trivial members.
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
NumNonEmpty = RHS.NumNonEmpty;
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
IsSmall = RHS.IsSmall;
|
||||
|
||||
// Make the RHS small and empty.
|
||||
RHS.CurArraySize = SmallSize;
|
||||
assert(RHS.CurArray == RHS.SmallArray);
|
||||
RHS.NumNonEmpty = 0;
|
||||
RHS.NumTombstones = 0;
|
||||
RHS.IsSmall = true;
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
|
||||
void SmallPtrSetImplBase::swap(const void **SmallStorage,
|
||||
const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &RHS) {
|
||||
if (this == &RHS) return;
|
||||
|
||||
// We can only avoid copying elements if neither set is small.
|
||||
@@ -227,42 +255,42 @@ void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
|
||||
// If only RHS is small, copy the small elements into LHS and move the pointer
|
||||
// from LHS to RHS.
|
||||
if (!this->isSmall() && RHS.isSmall()) {
|
||||
assert(RHS.CurArray == RHS.SmallArray);
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
|
||||
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;
|
||||
this->CurArray = this->SmallArray;
|
||||
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()) {
|
||||
assert(this->CurArray == this->SmallArray);
|
||||
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
|
||||
RHS.SmallArray);
|
||||
RHSSmallStorage);
|
||||
std::swap(RHS.CurArraySize, this->CurArraySize);
|
||||
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
|
||||
std::swap(RHS.NumTombstones, this->NumTombstones);
|
||||
this->CurArray = RHS.CurArray;
|
||||
RHS.CurArray = RHS.SmallArray;
|
||||
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->SmallArray, this->SmallArray + MinNonEmpty,
|
||||
RHS.SmallArray);
|
||||
std::swap_ranges(this->CurArray, this->CurArray + MinNonEmpty, RHS.CurArray);
|
||||
if (this->NumNonEmpty > MinNonEmpty) {
|
||||
std::copy(this->SmallArray + MinNonEmpty,
|
||||
this->SmallArray + this->NumNonEmpty,
|
||||
RHS.SmallArray + MinNonEmpty);
|
||||
std::copy(this->CurArray + MinNonEmpty, this->CurArray + this->NumNonEmpty,
|
||||
RHS.CurArray + MinNonEmpty);
|
||||
} else {
|
||||
std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
|
||||
this->SmallArray + MinNonEmpty);
|
||||
std::copy(RHS.CurArray + MinNonEmpty, RHS.CurArray + RHS.NumNonEmpty,
|
||||
this->CurArray + MinNonEmpty);
|
||||
}
|
||||
assert(this->CurArraySize == RHS.CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
|
||||
@@ -102,9 +102,18 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
|
||||
return std::clamp(NewCapacity, MinSize, MaxSize);
|
||||
}
|
||||
|
||||
void *SmallVectorBase::replaceAllocation(void *NewElts, size_t TSize,
|
||||
size_t NewCapacity,
|
||||
size_t VSize) {
|
||||
/// If vector was first created with capacity 0, getFirstEl() points to the
|
||||
/// memory right after, an area unallocated. If a subsequent allocation,
|
||||
/// that grows the vector, happens to return the same pointer as getFirstEl(),
|
||||
/// get a new allocation, otherwise isSmall() will falsely return that no
|
||||
/// allocation was done (true) and the memory will not be freed in the
|
||||
/// destructor. If a VSize is given (vector size), also copy that many
|
||||
/// elements to the new allocation - used if realloca fails to increase
|
||||
/// space, and happens to allocate precisely at BeginX.
|
||||
/// This is unlikely to be called often, but resolves a memory leak when the
|
||||
/// situation does occur.
|
||||
static void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
|
||||
size_t VSize = 0) {
|
||||
void *NewEltsReplace = wpi::safe_malloc(NewCapacity * TSize);
|
||||
if (VSize)
|
||||
memcpy(NewEltsReplace, NewElts, VSize * TSize);
|
||||
|
||||
@@ -23,11 +23,9 @@
|
||||
|
||||
// mingw-w64 tends to define it as 0x0502 in its headers.
|
||||
#undef _WIN32_WINNT
|
||||
#undef _WIN32_IE
|
||||
|
||||
// Require at least Windows 7 API.
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
|
||||
@@ -70,14 +70,6 @@ constexpr raw_ostream::Colors raw_ostream::WHITE;
|
||||
constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
|
||||
constexpr raw_ostream::Colors raw_ostream::RESET;
|
||||
|
||||
namespace {
|
||||
// Find the length of an array.
|
||||
template <class T, std::size_t N>
|
||||
constexpr inline size_t array_lengthof(T (&)[N]) {
|
||||
return N;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
raw_ostream::~raw_ostream() {
|
||||
// raw_ostream's subclasses should take care to flush the buffer
|
||||
// in their destructors.
|
||||
@@ -575,6 +567,10 @@ size_t raw_fd_ostream::preferred_buffer_size() const {
|
||||
if (IsWindowsConsole)
|
||||
return 0;
|
||||
return raw_ostream::preferred_buffer_size();
|
||||
#elif defined(__MVS__)
|
||||
// The buffer size on z/OS is defined with macro BUFSIZ, which can be
|
||||
// retrieved by invoking function raw_ostream::preferred_buffer_size().
|
||||
return raw_ostream::preferred_buffer_size();
|
||||
#else
|
||||
assert(FD >= 0 && "File not yet open!");
|
||||
struct stat statbuf;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -755,7 +755,7 @@ template <class X, class Y> auto dyn_cast_if_present(Y *Val) {
|
||||
|
||||
// Forwards to dyn_cast_if_present to avoid breaking current users. This is
|
||||
// deprecated and will be removed in a future patch, use
|
||||
// cast_if_present instead.
|
||||
// dyn_cast_if_present instead.
|
||||
template <class X, class Y> auto dyn_cast_or_null(const Y &Val) {
|
||||
return dyn_cast_if_present<X>(Val);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
# define __has_include(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
|
||||
@@ -115,7 +111,8 @@
|
||||
/// this attribute will be made public and visible outside of any shared library
|
||||
/// they are linked in to.
|
||||
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(gnu::visibility)
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(gnu::visibility) && defined(__GNUC__) && \
|
||||
!defined(__clang__)
|
||||
#define LLVM_ATTRIBUTE_VISIBILITY_HIDDEN [[gnu::visibility("hidden")]]
|
||||
#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT [[gnu::visibility("default")]]
|
||||
#elif __has_attribute(visibility)
|
||||
@@ -126,18 +123,100 @@
|
||||
#define LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#endif
|
||||
|
||||
|
||||
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
|
||||
(defined(__MINGW32__) && defined(__clang__)))
|
||||
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
|
||||
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
|
||||
#define LLVM_EXTERNAL_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#else
|
||||
#define LLVM_EXTERNAL_VISIBILITY
|
||||
#endif
|
||||
|
||||
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
|
||||
(defined(__MINGW32__) && defined(__clang__)))
|
||||
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
|
||||
// Clang compilers older then 15 do not support gnu style attributes on
|
||||
// namespaces.
|
||||
#if defined(__clang__) && __clang_major__ < 15
|
||||
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE [[gnu::visibility("hidden")]]
|
||||
#else
|
||||
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
|
||||
#endif
|
||||
#define LLVM_ALWAYS_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#elif defined(_WIN32)
|
||||
#define LLVM_ALWAYS_EXPORT __declspec(dllexport)
|
||||
#define LLVM_LIBRARY_VISIBILITY
|
||||
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE
|
||||
#else
|
||||
#define LLVM_LIBRARY_VISIBILITY
|
||||
#define LLVM_EXTERNAL_VISIBILITY
|
||||
#define LLVM_ALWAYS_EXPORT
|
||||
#define LLVM_LIBRARY_VISIBILITY_NAMESPACE
|
||||
#endif
|
||||
|
||||
/// LLVM_ABI is the main export/visibility macro to mark something as explicitly
|
||||
/// exported when llvm is built as a shared library with everything else that is
|
||||
/// unannotated will have internal visibility.
|
||||
///
|
||||
/// LLVM_ABI_EXPORT is for the special case for things like plugin symbol
|
||||
/// declarations or definitions where we don't want the macro to be switching
|
||||
/// between dllexport and dllimport on windows based on what codebase is being
|
||||
/// built, it will only be dllexport. For non windows platforms this macro
|
||||
/// behaves the same as LLVM_ABI.
|
||||
///
|
||||
/// LLVM_EXPORT_TEMPLATE is used on explicit template instantiations in source
|
||||
/// files that were declared extern in a header. This macro is only set as a
|
||||
/// compiler export attribute on windows, on other platforms it does nothing.
|
||||
///
|
||||
/// LLVM_TEMPLATE_ABI is for annotating extern template declarations in headers
|
||||
/// 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.
|
||||
#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__)
|
||||
#if defined(LLVM_EXPORTS)
|
||||
#define LLVM_ABI __declspec(dllexport)
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE __declspec(dllexport)
|
||||
#else
|
||||
#define LLVM_ABI __declspec(dllimport)
|
||||
#define LLVM_TEMPLATE_ABI __declspec(dllimport)
|
||||
#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
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#elif defined(__MACH__) || defined(__WASM__) || defined(__EMSCRIPTEN__)
|
||||
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
|
||||
#endif
|
||||
#else
|
||||
#define LLVM_ABI
|
||||
#define LLVM_TEMPLATE_ABI
|
||||
#define LLVM_EXPORT_TEMPLATE
|
||||
#define LLVM_ABI_EXPORT
|
||||
#endif
|
||||
#define LLVM_C_ABI LLVM_ABI
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_PREFETCH
|
||||
@@ -357,6 +436,12 @@
|
||||
#define LLVM_GSL_POINTER
|
||||
#endif
|
||||
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
|
||||
#define LLVM_LIFETIME_BOUND [[clang::lifetimebound]]
|
||||
#else
|
||||
#define LLVM_LIFETIME_BOUND
|
||||
#endif
|
||||
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L
|
||||
#define LLVM_CTOR_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
|
||||
@@ -110,7 +110,8 @@ public:
|
||||
|
||||
void clear() {
|
||||
incrementEpoch();
|
||||
if (getNumEntries() == 0 && getNumTombstones() == 0) return;
|
||||
if (getNumEntries() == 0 && getNumTombstones() == 0)
|
||||
return;
|
||||
|
||||
// If the capacity of the array is huge, and the # elements used is small,
|
||||
// shrink the array.
|
||||
@@ -119,12 +120,13 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
|
||||
if (std::is_trivially_destructible<ValueT>::value) {
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
if constexpr (std::is_trivially_destructible_v<ValueT>) {
|
||||
// Use a simpler loop when values don't need destruction.
|
||||
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
|
||||
P->getFirst() = EmptyKey;
|
||||
} else {
|
||||
const KeyT TombstoneKey = getTombstoneKey();
|
||||
[[maybe_unused]] unsigned NumEntries = getNumEntries();
|
||||
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
|
||||
if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
|
||||
@@ -144,8 +146,7 @@ public:
|
||||
|
||||
/// Return true if the specified key is in the map, false otherwise.
|
||||
bool contains(const_arg_type_t<KeyT> Val) const {
|
||||
const BucketT *TheBucket;
|
||||
return LookupBucketFor(Val, TheBucket);
|
||||
return doFind(Val) != nullptr;
|
||||
}
|
||||
|
||||
/// Return 1 if the specified key is in the map, 0 otherwise.
|
||||
@@ -154,21 +155,17 @@ public:
|
||||
}
|
||||
|
||||
iterator find(const_arg_type_t<KeyT> Val) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Val, TheBucket))
|
||||
return makeIterator(TheBucket,
|
||||
shouldReverseIterate<KeyT>() ? getBuckets()
|
||||
: getBucketsEnd(),
|
||||
*this, true);
|
||||
if (BucketT *Bucket = doFind(Val))
|
||||
return makeIterator(
|
||||
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
|
||||
*this, true);
|
||||
return end();
|
||||
}
|
||||
const_iterator find(const_arg_type_t<KeyT> Val) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Val, TheBucket))
|
||||
return makeConstIterator(TheBucket,
|
||||
shouldReverseIterate<KeyT>() ? getBuckets()
|
||||
: getBucketsEnd(),
|
||||
*this, true);
|
||||
if (const BucketT *Bucket = doFind(Val))
|
||||
return makeConstIterator(
|
||||
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
|
||||
*this, true);
|
||||
return end();
|
||||
}
|
||||
|
||||
@@ -177,33 +174,27 @@ public:
|
||||
/// The DenseMapInfo is responsible for supplying methods
|
||||
/// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key
|
||||
/// type used.
|
||||
template<class LookupKeyT>
|
||||
iterator find_as(const LookupKeyT &Val) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Val, TheBucket))
|
||||
return makeIterator(TheBucket,
|
||||
shouldReverseIterate<KeyT>() ? getBuckets()
|
||||
: getBucketsEnd(),
|
||||
*this, true);
|
||||
template <class LookupKeyT> iterator find_as(const LookupKeyT &Val) {
|
||||
if (BucketT *Bucket = doFind(Val))
|
||||
return makeIterator(
|
||||
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
|
||||
*this, true);
|
||||
return end();
|
||||
}
|
||||
template<class LookupKeyT>
|
||||
template <class LookupKeyT>
|
||||
const_iterator find_as(const LookupKeyT &Val) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Val, TheBucket))
|
||||
return makeConstIterator(TheBucket,
|
||||
shouldReverseIterate<KeyT>() ? getBuckets()
|
||||
: getBucketsEnd(),
|
||||
*this, true);
|
||||
if (const BucketT *Bucket = doFind(Val))
|
||||
return makeConstIterator(
|
||||
Bucket, shouldReverseIterate<KeyT>() ? getBuckets() : getBucketsEnd(),
|
||||
*this, true);
|
||||
return end();
|
||||
}
|
||||
|
||||
/// lookup - Return the entry for the specified key, or a default
|
||||
/// constructed value if no such entry exists.
|
||||
ValueT lookup(const_arg_type_t<KeyT> Val) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Val, TheBucket))
|
||||
return TheBucket->getSecond();
|
||||
if (const BucketT *Bucket = doFind(Val))
|
||||
return Bucket->getSecond();
|
||||
return ValueT();
|
||||
}
|
||||
|
||||
@@ -233,7 +224,7 @@ public:
|
||||
// The value is constructed in-place if the key is not in the map, otherwise
|
||||
// it is not moved.
|
||||
template <typename... Ts>
|
||||
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&... Args) {
|
||||
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&...Args) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return std::make_pair(makeIterator(TheBucket,
|
||||
@@ -258,7 +249,7 @@ public:
|
||||
// The value is constructed in-place if the key is not in the map, otherwise
|
||||
// it is not moved.
|
||||
template <typename... Ts>
|
||||
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
|
||||
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&...Args) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return std::make_pair(makeIterator(TheBucket,
|
||||
@@ -307,8 +298,7 @@ public:
|
||||
}
|
||||
|
||||
/// insert - Range insertion of pairs.
|
||||
template<typename InputIt>
|
||||
void insert(InputIt I, InputIt E) {
|
||||
template <typename InputIt> void insert(InputIt I, InputIt E) {
|
||||
for (; I != E; ++I)
|
||||
insert(*I);
|
||||
}
|
||||
@@ -329,23 +319,9 @@ public:
|
||||
return Ret;
|
||||
}
|
||||
|
||||
/// Returns the value associated to the key in the map if it exists. If it
|
||||
/// does not exist, emplace a default value for the key and returns a
|
||||
/// reference to the newly created value.
|
||||
ValueT &getOrInsertDefault(KeyT &&Key) {
|
||||
return try_emplace(Key).first->second;
|
||||
}
|
||||
|
||||
/// Returns the value associated to the key in the map if it exists. If it
|
||||
/// does not exist, emplace a default value for the key and returns a
|
||||
/// reference to the newly created value.
|
||||
ValueT &getOrInsertDefault(const KeyT &Key) {
|
||||
return try_emplace(Key).first->second;
|
||||
}
|
||||
|
||||
bool erase(const KeyT &Val) {
|
||||
BucketT *TheBucket;
|
||||
if (!LookupBucketFor(Val, TheBucket))
|
||||
BucketT *TheBucket = doFind(Val);
|
||||
if (!TheBucket)
|
||||
return false; // not in map.
|
||||
|
||||
TheBucket->getSecond().~ValueT();
|
||||
@@ -362,28 +338,20 @@ public:
|
||||
incrementNumTombstones();
|
||||
}
|
||||
|
||||
value_type& FindAndConstruct(const KeyT &Key) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return *TheBucket;
|
||||
|
||||
return *InsertIntoBucket(TheBucket, Key);
|
||||
}
|
||||
|
||||
ValueT &operator[](const KeyT &Key) {
|
||||
return FindAndConstruct(Key).second;
|
||||
}
|
||||
|
||||
value_type& FindAndConstruct(KeyT &&Key) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return *TheBucket;
|
||||
return TheBucket->second;
|
||||
|
||||
return *InsertIntoBucket(TheBucket, std::move(Key));
|
||||
return InsertIntoBucket(TheBucket, Key)->second;
|
||||
}
|
||||
|
||||
ValueT &operator[](KeyT &&Key) {
|
||||
return FindAndConstruct(std::move(Key)).second;
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket->second;
|
||||
|
||||
return InsertIntoBucket(TheBucket, std::move(Key))->second;
|
||||
}
|
||||
|
||||
/// isPointerIntoBucketsArray - Return true if the specified pointer points
|
||||
@@ -418,7 +386,7 @@ protected:
|
||||
setNumEntries(0);
|
||||
setNumTombstones(0);
|
||||
|
||||
assert((getNumBuckets() & (getNumBuckets()-1)) == 0 &&
|
||||
assert((getNumBuckets() & (getNumBuckets() - 1)) == 0 &&
|
||||
"# initial buckets must be a power of two!");
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
for (BucketT *B = getBuckets(), *E = getBucketsEnd(); B != E; ++B)
|
||||
@@ -470,44 +438,45 @@ protected:
|
||||
setNumEntries(other.getNumEntries());
|
||||
setNumTombstones(other.getNumTombstones());
|
||||
|
||||
if (std::is_trivially_copyable<KeyT>::value &&
|
||||
std::is_trivially_copyable<ValueT>::value)
|
||||
memcpy(reinterpret_cast<void *>(getBuckets()), other.getBuckets(),
|
||||
getNumBuckets() * sizeof(BucketT));
|
||||
else
|
||||
for (size_t i = 0; i < getNumBuckets(); ++i) {
|
||||
::new (&getBuckets()[i].getFirst())
|
||||
KeyT(other.getBuckets()[i].getFirst());
|
||||
if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()) &&
|
||||
!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getTombstoneKey()))
|
||||
::new (&getBuckets()[i].getSecond())
|
||||
ValueT(other.getBuckets()[i].getSecond());
|
||||
BucketT *Buckets = getBuckets();
|
||||
const BucketT *OtherBuckets = other.getBuckets();
|
||||
const size_t NumBuckets = getNumBuckets();
|
||||
if constexpr (std::is_trivially_copyable_v<KeyT> &&
|
||||
std::is_trivially_copyable_v<ValueT>) {
|
||||
memcpy(reinterpret_cast<void *>(Buckets), OtherBuckets,
|
||||
NumBuckets * sizeof(BucketT));
|
||||
} else {
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
const KeyT TombstoneKey = getTombstoneKey();
|
||||
for (size_t I = 0; I < NumBuckets; ++I) {
|
||||
::new (&Buckets[I].getFirst()) KeyT(OtherBuckets[I].getFirst());
|
||||
if (!KeyInfoT::isEqual(Buckets[I].getFirst(), EmptyKey) &&
|
||||
!KeyInfoT::isEqual(Buckets[I].getFirst(), TombstoneKey))
|
||||
::new (&Buckets[I].getSecond()) ValueT(OtherBuckets[I].getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const KeyT &Val) {
|
||||
return KeyInfoT::getHashValue(Val);
|
||||
}
|
||||
|
||||
template<typename LookupKeyT>
|
||||
template <typename LookupKeyT>
|
||||
static unsigned getHashValue(const LookupKeyT &Val) {
|
||||
return KeyInfoT::getHashValue(Val);
|
||||
}
|
||||
|
||||
static const KeyT getEmptyKey() {
|
||||
static_assert(std::is_base_of<DenseMapBase, DerivedT>::value,
|
||||
static_assert(std::is_base_of_v<DenseMapBase, DerivedT>,
|
||||
"Must pass the derived type to this template!");
|
||||
return KeyInfoT::getEmptyKey();
|
||||
}
|
||||
|
||||
static const KeyT getTombstoneKey() {
|
||||
return KeyInfoT::getTombstoneKey();
|
||||
}
|
||||
static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); }
|
||||
|
||||
private:
|
||||
iterator makeIterator(BucketT *P, BucketT *E,
|
||||
DebugEpochBase &Epoch,
|
||||
bool NoAdvance=false) {
|
||||
iterator makeIterator(BucketT *P, BucketT *E, DebugEpochBase &Epoch,
|
||||
bool NoAdvance = false) {
|
||||
if (shouldReverseIterate<KeyT>()) {
|
||||
BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1;
|
||||
return iterator(B, E, Epoch, NoAdvance);
|
||||
@@ -517,7 +486,7 @@ private:
|
||||
|
||||
const_iterator makeConstIterator(const BucketT *P, const BucketT *E,
|
||||
const DebugEpochBase &Epoch,
|
||||
const bool NoAdvance=false) const {
|
||||
const bool NoAdvance = false) const {
|
||||
if (shouldReverseIterate<KeyT>()) {
|
||||
const BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1;
|
||||
return const_iterator(B, E, Epoch, NoAdvance);
|
||||
@@ -533,13 +502,9 @@ private:
|
||||
static_cast<DerivedT *>(this)->setNumEntries(Num);
|
||||
}
|
||||
|
||||
void incrementNumEntries() {
|
||||
setNumEntries(getNumEntries() + 1);
|
||||
}
|
||||
void incrementNumEntries() { setNumEntries(getNumEntries() + 1); }
|
||||
|
||||
void decrementNumEntries() {
|
||||
setNumEntries(getNumEntries() - 1);
|
||||
}
|
||||
void decrementNumEntries() { setNumEntries(getNumEntries() - 1); }
|
||||
|
||||
unsigned getNumTombstones() const {
|
||||
return static_cast<const DerivedT *>(this)->getNumTombstones();
|
||||
@@ -549,46 +514,34 @@ private:
|
||||
static_cast<DerivedT *>(this)->setNumTombstones(Num);
|
||||
}
|
||||
|
||||
void incrementNumTombstones() {
|
||||
setNumTombstones(getNumTombstones() + 1);
|
||||
}
|
||||
void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); }
|
||||
|
||||
void decrementNumTombstones() {
|
||||
setNumTombstones(getNumTombstones() - 1);
|
||||
}
|
||||
void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); }
|
||||
|
||||
const BucketT *getBuckets() const {
|
||||
return static_cast<const DerivedT *>(this)->getBuckets();
|
||||
}
|
||||
|
||||
BucketT *getBuckets() {
|
||||
return static_cast<DerivedT *>(this)->getBuckets();
|
||||
}
|
||||
BucketT *getBuckets() { return static_cast<DerivedT *>(this)->getBuckets(); }
|
||||
|
||||
unsigned getNumBuckets() const {
|
||||
return static_cast<const DerivedT *>(this)->getNumBuckets();
|
||||
}
|
||||
|
||||
BucketT *getBucketsEnd() {
|
||||
return getBuckets() + getNumBuckets();
|
||||
}
|
||||
BucketT *getBucketsEnd() { return getBuckets() + getNumBuckets(); }
|
||||
|
||||
const BucketT *getBucketsEnd() const {
|
||||
return getBuckets() + getNumBuckets();
|
||||
}
|
||||
|
||||
void grow(unsigned AtLeast) {
|
||||
static_cast<DerivedT *>(this)->grow(AtLeast);
|
||||
}
|
||||
void grow(unsigned AtLeast) { static_cast<DerivedT *>(this)->grow(AtLeast); }
|
||||
|
||||
void shrink_and_clear() {
|
||||
static_cast<DerivedT *>(this)->shrink_and_clear();
|
||||
}
|
||||
void shrink_and_clear() { static_cast<DerivedT *>(this)->shrink_and_clear(); }
|
||||
|
||||
template <typename KeyArg, typename... ValueArgs>
|
||||
BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key,
|
||||
ValueArgs &&... Values) {
|
||||
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
|
||||
ValueArgs &&...Values) {
|
||||
TheBucket = InsertIntoBucketImpl(Key, TheBucket);
|
||||
|
||||
TheBucket->getFirst() = std::forward<KeyArg>(Key);
|
||||
::new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...);
|
||||
@@ -598,7 +551,7 @@ private:
|
||||
template <typename LookupKeyT>
|
||||
BucketT *InsertIntoBucketWithLookup(BucketT *TheBucket, KeyT &&Key,
|
||||
ValueT &&Value, LookupKeyT &Lookup) {
|
||||
TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket);
|
||||
TheBucket = InsertIntoBucketImpl(Lookup, TheBucket);
|
||||
|
||||
TheBucket->getFirst() = std::move(Key);
|
||||
::new (&TheBucket->getSecond()) ValueT(std::move(Value));
|
||||
@@ -606,8 +559,7 @@ private:
|
||||
}
|
||||
|
||||
template <typename LookupKeyT>
|
||||
BucketT *InsertIntoBucketImpl(const KeyT &Key, const LookupKeyT &Lookup,
|
||||
BucketT *TheBucket) {
|
||||
BucketT *InsertIntoBucketImpl(const LookupKeyT &Lookup, BucketT *TheBucket) {
|
||||
incrementEpoch();
|
||||
|
||||
// If the load of the hash table is more than 3/4, or if fewer than 1/8 of
|
||||
@@ -625,8 +577,9 @@ private:
|
||||
this->grow(NumBuckets * 2);
|
||||
LookupBucketFor(Lookup, TheBucket);
|
||||
NumBuckets = getNumBuckets();
|
||||
} else if (LLVM_UNLIKELY(NumBuckets-(NewNumEntries+getNumTombstones()) <=
|
||||
NumBuckets/8)) {
|
||||
} else if (LLVM_UNLIKELY(NumBuckets -
|
||||
(NewNumEntries + getNumTombstones()) <=
|
||||
NumBuckets / 8)) {
|
||||
this->grow(NumBuckets);
|
||||
LookupBucketFor(Lookup, TheBucket);
|
||||
}
|
||||
@@ -644,14 +597,41 @@ private:
|
||||
return TheBucket;
|
||||
}
|
||||
|
||||
template <typename LookupKeyT> BucketT *doFind(const LookupKeyT &Val) {
|
||||
BucketT *BucketsPtr = getBuckets();
|
||||
const unsigned NumBuckets = getNumBuckets();
|
||||
if (NumBuckets == 0)
|
||||
return nullptr;
|
||||
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
|
||||
unsigned ProbeAmt = 1;
|
||||
while (true) {
|
||||
BucketT *Bucket = BucketsPtr + BucketNo;
|
||||
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, Bucket->getFirst())))
|
||||
return Bucket;
|
||||
if (LLVM_LIKELY(KeyInfoT::isEqual(Bucket->getFirst(), EmptyKey)))
|
||||
return nullptr;
|
||||
|
||||
// Otherwise, it's a hash collision or a tombstone, continue quadratic
|
||||
// probing.
|
||||
BucketNo += ProbeAmt++;
|
||||
BucketNo &= NumBuckets - 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LookupKeyT>
|
||||
const BucketT *doFind(const LookupKeyT &Val) const {
|
||||
return const_cast<DenseMapBase *>(this)->doFind(Val); // NOLINT
|
||||
}
|
||||
|
||||
/// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in
|
||||
/// FoundBucket. If the bucket contains the key and a value, this returns
|
||||
/// true, otherwise it returns a bucket with an empty marker or tombstone and
|
||||
/// returns false.
|
||||
template<typename LookupKeyT>
|
||||
bool LookupBucketFor(const LookupKeyT &Val,
|
||||
const BucketT *&FoundBucket) const {
|
||||
const BucketT *BucketsPtr = getBuckets();
|
||||
template <typename LookupKeyT>
|
||||
bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
|
||||
BucketT *BucketsPtr = getBuckets();
|
||||
const unsigned NumBuckets = getNumBuckets();
|
||||
|
||||
if (NumBuckets == 0) {
|
||||
@@ -660,17 +640,17 @@ private:
|
||||
}
|
||||
|
||||
// FoundTombstone - Keep track of whether we find a tombstone while probing.
|
||||
const BucketT *FoundTombstone = nullptr;
|
||||
BucketT *FoundTombstone = nullptr;
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
const KeyT TombstoneKey = getTombstoneKey();
|
||||
assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
|
||||
!KeyInfoT::isEqual(Val, TombstoneKey) &&
|
||||
"Empty/Tombstone value shouldn't be inserted into map!");
|
||||
|
||||
unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
|
||||
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
|
||||
unsigned ProbeAmt = 1;
|
||||
while (true) {
|
||||
const BucketT *ThisBucket = BucketsPtr + BucketNo;
|
||||
BucketT *ThisBucket = BucketsPtr + BucketNo;
|
||||
// Found Val's bucket? If so, return it.
|
||||
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
|
||||
FoundBucket = ThisBucket;
|
||||
@@ -690,32 +670,21 @@ private:
|
||||
// prefer to return it than something that would require more probing.
|
||||
if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
|
||||
!FoundTombstone)
|
||||
FoundTombstone = ThisBucket; // Remember the first tombstone found.
|
||||
FoundTombstone = ThisBucket; // Remember the first tombstone found.
|
||||
|
||||
// Otherwise, it's a hash collision or a tombstone, continue quadratic
|
||||
// probing.
|
||||
BucketNo += ProbeAmt++;
|
||||
BucketNo &= (NumBuckets-1);
|
||||
BucketNo &= (NumBuckets - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LookupKeyT>
|
||||
bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
|
||||
const BucketT *ConstFoundBucket;
|
||||
bool Result = const_cast<const DenseMapBase *>(this)
|
||||
->LookupBucketFor(Val, ConstFoundBucket);
|
||||
FoundBucket = const_cast<BucketT *>(ConstFoundBucket);
|
||||
return Result;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Return the approximate size (in bytes) of the actual map.
|
||||
/// This is just the raw memory used by DenseMap.
|
||||
/// If entries are pointers to objects, the size of the referenced objects
|
||||
/// are not included.
|
||||
size_t getMemorySize() const {
|
||||
return getNumBuckets() * sizeof(BucketT);
|
||||
}
|
||||
size_t getMemorySize() const { return getNumBuckets() * sizeof(BucketT); }
|
||||
};
|
||||
|
||||
/// Equality comparison for DenseMap.
|
||||
@@ -783,8 +752,7 @@ public:
|
||||
swap(other);
|
||||
}
|
||||
|
||||
template<typename InputIt>
|
||||
DenseMap(const InputIt &I, const InputIt &E) {
|
||||
template <typename InputIt> DenseMap(const InputIt &I, const InputIt &E) {
|
||||
init(std::distance(I, E));
|
||||
this->insert(I, E);
|
||||
}
|
||||
@@ -799,7 +767,7 @@ public:
|
||||
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
|
||||
}
|
||||
|
||||
void swap(DenseMap& RHS) {
|
||||
void swap(DenseMap &RHS) {
|
||||
this->incrementEpoch();
|
||||
RHS.incrementEpoch();
|
||||
std::swap(Buckets, RHS.Buckets);
|
||||
@@ -808,13 +776,13 @@ public:
|
||||
std::swap(NumBuckets, RHS.NumBuckets);
|
||||
}
|
||||
|
||||
DenseMap& operator=(const DenseMap& other) {
|
||||
DenseMap &operator=(const DenseMap &other) {
|
||||
if (&other != this)
|
||||
copyFrom(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseMap& operator=(DenseMap &&other) {
|
||||
DenseMap &operator=(DenseMap &&other) {
|
||||
this->destroyAll();
|
||||
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
|
||||
init(0);
|
||||
@@ -822,7 +790,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
void copyFrom(const DenseMap& other) {
|
||||
void copyFrom(const DenseMap &other) {
|
||||
this->destroyAll();
|
||||
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
|
||||
if (allocateBuckets(other.NumBuckets)) {
|
||||
@@ -847,14 +815,15 @@ public:
|
||||
unsigned OldNumBuckets = NumBuckets;
|
||||
BucketT *OldBuckets = Buckets;
|
||||
|
||||
allocateBuckets(std::max<unsigned>(64, static_cast<unsigned>(NextPowerOf2(AtLeast-1))));
|
||||
allocateBuckets(std::max<unsigned>(
|
||||
64, static_cast<unsigned>(NextPowerOf2(AtLeast - 1))));
|
||||
assert(Buckets);
|
||||
if (!OldBuckets) {
|
||||
this->BaseT::initEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
this->moveFromOldBuckets(OldBuckets, OldBuckets+OldNumBuckets);
|
||||
this->moveFromOldBuckets(OldBuckets, OldBuckets + OldNumBuckets);
|
||||
|
||||
// Free the old table.
|
||||
deallocate_buffer(OldBuckets, sizeof(BucketT) * OldNumBuckets,
|
||||
@@ -881,29 +850,17 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned getNumEntries() const {
|
||||
return NumEntries;
|
||||
}
|
||||
unsigned getNumEntries() const { return NumEntries; }
|
||||
|
||||
void setNumEntries(unsigned Num) {
|
||||
NumEntries = Num;
|
||||
}
|
||||
void setNumEntries(unsigned Num) { NumEntries = Num; }
|
||||
|
||||
unsigned getNumTombstones() const {
|
||||
return NumTombstones;
|
||||
}
|
||||
unsigned getNumTombstones() const { return NumTombstones; }
|
||||
|
||||
void setNumTombstones(unsigned Num) {
|
||||
NumTombstones = Num;
|
||||
}
|
||||
void setNumTombstones(unsigned Num) { NumTombstones = Num; }
|
||||
|
||||
BucketT *getBuckets() const {
|
||||
return Buckets;
|
||||
}
|
||||
BucketT *getBuckets() const { return Buckets; }
|
||||
|
||||
unsigned getNumBuckets() const {
|
||||
return NumBuckets;
|
||||
}
|
||||
unsigned getNumBuckets() const { return NumBuckets; }
|
||||
|
||||
bool allocateBuckets(unsigned Num) {
|
||||
NumBuckets = Num;
|
||||
@@ -964,7 +921,7 @@ public:
|
||||
swap(other);
|
||||
}
|
||||
|
||||
template<typename InputIt>
|
||||
template <typename InputIt>
|
||||
SmallDenseMap(const InputIt &I, const InputIt &E) {
|
||||
init(NextPowerOf2(std::distance(I, E)));
|
||||
this->insert(I, E);
|
||||
@@ -978,7 +935,7 @@ public:
|
||||
deallocateBuckets();
|
||||
}
|
||||
|
||||
void swap(SmallDenseMap& RHS) {
|
||||
void swap(SmallDenseMap &RHS) {
|
||||
unsigned TmpNumEntries = RHS.NumEntries;
|
||||
RHS.NumEntries = NumEntries;
|
||||
NumEntries = TmpNumEntries;
|
||||
@@ -1050,13 +1007,13 @@ public:
|
||||
new (SmallSide.getLargeRep()) LargeRep(std::move(TmpRep));
|
||||
}
|
||||
|
||||
SmallDenseMap& operator=(const SmallDenseMap& other) {
|
||||
SmallDenseMap &operator=(const SmallDenseMap &other) {
|
||||
if (&other != this)
|
||||
copyFrom(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SmallDenseMap& operator=(SmallDenseMap &&other) {
|
||||
SmallDenseMap &operator=(SmallDenseMap &&other) {
|
||||
this->destroyAll();
|
||||
deallocateBuckets();
|
||||
init(0);
|
||||
@@ -1064,7 +1021,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
void copyFrom(const SmallDenseMap& other) {
|
||||
void copyFrom(const SmallDenseMap &other) {
|
||||
this->destroyAll();
|
||||
deallocateBuckets();
|
||||
Small = true;
|
||||
@@ -1086,7 +1043,7 @@ public:
|
||||
|
||||
void grow(unsigned AtLeast) {
|
||||
if (AtLeast > InlineBuckets)
|
||||
AtLeast = std::max<unsigned>(64, NextPowerOf2(AtLeast-1));
|
||||
AtLeast = std::max<unsigned>(64, NextPowerOf2(AtLeast - 1));
|
||||
|
||||
if (Small) {
|
||||
// First move the inline buckets into a temporary storage.
|
||||
@@ -1130,7 +1087,8 @@ public:
|
||||
new (getLargeRep()) LargeRep(allocateBuckets(AtLeast));
|
||||
}
|
||||
|
||||
this->moveFromOldBuckets(OldRep.Buckets, OldRep.Buckets+OldRep.NumBuckets);
|
||||
this->moveFromOldBuckets(OldRep.Buckets,
|
||||
OldRep.Buckets + OldRep.NumBuckets);
|
||||
|
||||
// Free the old table.
|
||||
deallocate_buffer(OldRep.Buckets, sizeof(BucketT) * OldRep.NumBuckets,
|
||||
@@ -1159,9 +1117,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned getNumEntries() const {
|
||||
return NumEntries;
|
||||
}
|
||||
unsigned getNumEntries() const { return NumEntries; }
|
||||
|
||||
void setNumEntries(unsigned Num) {
|
||||
// NumEntries is hardcoded to be 31 bits wide.
|
||||
@@ -1169,13 +1125,9 @@ private:
|
||||
NumEntries = Num;
|
||||
}
|
||||
|
||||
unsigned getNumTombstones() const {
|
||||
return NumTombstones;
|
||||
}
|
||||
unsigned getNumTombstones() const { return NumTombstones; }
|
||||
|
||||
void setNumTombstones(unsigned Num) {
|
||||
NumTombstones = Num;
|
||||
}
|
||||
void setNumTombstones(unsigned Num) { NumTombstones = Num; }
|
||||
|
||||
const BucketT *getInlineBuckets() const {
|
||||
assert(Small);
|
||||
@@ -1187,7 +1139,7 @@ private:
|
||||
|
||||
BucketT *getInlineBuckets() {
|
||||
return const_cast<BucketT *>(
|
||||
const_cast<const SmallDenseMap *>(this)->getInlineBuckets());
|
||||
const_cast<const SmallDenseMap *>(this)->getInlineBuckets());
|
||||
}
|
||||
|
||||
const LargeRep *getLargeRep() const {
|
||||
@@ -1198,7 +1150,7 @@ private:
|
||||
|
||||
LargeRep *getLargeRep() {
|
||||
return const_cast<LargeRep *>(
|
||||
const_cast<const SmallDenseMap *>(this)->getLargeRep());
|
||||
const_cast<const SmallDenseMap *>(this)->getLargeRep());
|
||||
}
|
||||
|
||||
const BucketT *getBuckets() const {
|
||||
@@ -1207,7 +1159,7 @@ private:
|
||||
|
||||
BucketT *getBuckets() {
|
||||
return const_cast<BucketT *>(
|
||||
const_cast<const SmallDenseMap *>(this)->getBuckets());
|
||||
const_cast<const SmallDenseMap *>(this)->getBuckets());
|
||||
}
|
||||
|
||||
unsigned getNumBuckets() const {
|
||||
@@ -1258,7 +1210,8 @@ public:
|
||||
: DebugEpochBase::HandleBase(&Epoch), Ptr(Pos), End(E) {
|
||||
assert(isHandleInSync() && "invalid construction!");
|
||||
|
||||
if (NoAdvance) return;
|
||||
if (NoAdvance)
|
||||
return;
|
||||
if (shouldReverseIterate<KeyT>()) {
|
||||
RetreatPastEmptyBuckets();
|
||||
return;
|
||||
@@ -1304,7 +1257,7 @@ public:
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
inline DenseMapIterator& operator++() { // Preincrement
|
||||
inline DenseMapIterator &operator++() { // Preincrement
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
assert(Ptr != End && "incrementing end() iterator");
|
||||
if (shouldReverseIterate<KeyT>()) {
|
||||
@@ -1316,9 +1269,11 @@ public:
|
||||
AdvancePastEmptyBuckets();
|
||||
return *this;
|
||||
}
|
||||
DenseMapIterator operator++(int) { // Postincrement
|
||||
DenseMapIterator operator++(int) { // Postincrement
|
||||
assert(isHandleInSync() && "invalid iterator access!");
|
||||
DenseMapIterator tmp = *this; ++*this; return tmp;
|
||||
DenseMapIterator tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -60,7 +60,7 @@ template <typename value_type, std::size_t alignment = unaligned>
|
||||
[[nodiscard]] inline value_type read(const void *memory, endianness endian) {
|
||||
value_type ret;
|
||||
|
||||
memcpy(&ret,
|
||||
memcpy(static_cast<void *>(&ret),
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type));
|
||||
|
||||
@@ -87,6 +87,7 @@ using EnableIfCallable = std::enable_if_t<std::disjunction<
|
||||
template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
|
||||
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 {};
|
||||
@@ -168,7 +169,8 @@ protected:
|
||||
// provide four pointers worth of storage here.
|
||||
// This is mutable as an inlined `const unique_function<void() const>` may
|
||||
// still modify its own mutable members.
|
||||
alignas(void *) mutable std::byte InlineStorage[InlineStorageSize];
|
||||
alignas(InlineStorageAlign) mutable std::byte
|
||||
InlineStorage[InlineStorageSize];
|
||||
} StorageUnion;
|
||||
|
||||
// A compressed pointer to either our dispatching callback or our table of
|
||||
@@ -269,7 +271,7 @@ protected:
|
||||
bool IsInlineStorage = true;
|
||||
void *CallableAddr = getInlineStorage();
|
||||
if (sizeof(CallableT) > InlineStorageSize ||
|
||||
alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) {
|
||||
alignof(CallableT) > InlineStorageAlign) {
|
||||
IsInlineStorage = false;
|
||||
// Allocate out-of-line storage. FIXME: Use an explicit alignment
|
||||
// parameter in C++17 mode.
|
||||
@@ -319,6 +321,7 @@ protected:
|
||||
// Non-trivial move, so dispatch to a type-erased implementation.
|
||||
getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
|
||||
RHS.getInlineStorage());
|
||||
getNonTrivialCallbacks()->DestroyPtr(RHS.getInlineStorage());
|
||||
}
|
||||
|
||||
// Clear the old callback and inline flag to get back to as-if-null.
|
||||
|
||||
@@ -1,688 +0,0 @@
|
||||
//===-- llvm/ADT/Hashing.h - Utilities for hashing --------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the newly proposed standard C++ interfaces for hashing
|
||||
// arbitrary data and building hash functions for user-defined types. This
|
||||
// interface was originally proposed in N3333[1] and is currently under review
|
||||
// for inclusion in a future TR and/or standard.
|
||||
//
|
||||
// The primary interfaces provide are comprised of one type and three functions:
|
||||
//
|
||||
// -- 'hash_code' class is an opaque type representing the hash code for some
|
||||
// data. It is the intended product of hashing, and can be used to implement
|
||||
// hash tables, checksumming, and other common uses of hashes. It is not an
|
||||
// integer type (although it can be converted to one) because it is risky
|
||||
// to assume much about the internals of a hash_code. In particular, each
|
||||
// execution of the program has a high probability of producing a different
|
||||
// hash_code for a given input. Thus their values are not stable to save or
|
||||
// persist, and should only be used during the execution for the
|
||||
// construction of hashing datastructures.
|
||||
//
|
||||
// -- 'hash_value' is a function designed to be overloaded for each
|
||||
// user-defined type which wishes to be used within a hashing context. It
|
||||
// should be overloaded within the user-defined type's namespace and found
|
||||
// via ADL. Overloads for primitive types are provided by this library.
|
||||
//
|
||||
// -- 'hash_combine' and 'hash_combine_range' are functions designed to aid
|
||||
// programmers in easily and intuitively combining a set of data into
|
||||
// a single hash_code for their object. They should only logically be used
|
||||
// within the implementation of a 'hash_value' routine or similar context.
|
||||
//
|
||||
// Note that 'hash_combine_range' contains very special logic for hashing
|
||||
// a contiguous array of integers or pointers. This logic is *extremely* fast,
|
||||
// on a modern Intel "Gainestown" Xeon (Nehalem uarch) @2.2 GHz, these were
|
||||
// benchmarked at over 6.5 GiB/s for large keys, and <20 cycles/hash for keys
|
||||
// under 32-bytes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_HASHING_H
|
||||
#define WPIUTIL_WPI_HASHING_H
|
||||
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include "wpi/SwapByteOrder.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26495)
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
template <typename T, typename Enable> struct DenseMapInfo;
|
||||
|
||||
/// An opaque object representing a hash code.
|
||||
///
|
||||
/// This object represents the result of hashing some entity. It is intended to
|
||||
/// be used to implement hashtables or other hashing-based data structures.
|
||||
/// While it wraps and exposes a numeric value, this value should not be
|
||||
/// trusted to be stable or predictable across processes or executions.
|
||||
///
|
||||
/// In order to obtain the hash_code for an object 'x':
|
||||
/// \code
|
||||
/// using wpi::hash_value;
|
||||
/// wpi::hash_code code = hash_value(x);
|
||||
/// \endcode
|
||||
class hash_code {
|
||||
size_t value;
|
||||
|
||||
public:
|
||||
/// Default construct a hash_code.
|
||||
/// Note that this leaves the value uninitialized.
|
||||
hash_code() = default;
|
||||
|
||||
/// Form a hash code directly from a numerical value.
|
||||
hash_code(size_t value) : value(value) {}
|
||||
|
||||
/// Convert the hash code to its numerical value for use.
|
||||
/*explicit*/ operator size_t() const { return value; }
|
||||
|
||||
friend bool operator==(const hash_code &lhs, const hash_code &rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
friend bool operator!=(const hash_code &lhs, const hash_code &rhs) {
|
||||
return lhs.value != rhs.value;
|
||||
}
|
||||
|
||||
/// Allow a hash_code to be directly run through hash_value.
|
||||
friend size_t hash_value(const hash_code &code) { return code.value; }
|
||||
};
|
||||
|
||||
/// Compute a hash_code for any integer value.
|
||||
///
|
||||
/// Note that this function is intended to compute the same hash_code for
|
||||
/// a particular value without regard to the pre-promotion type. This is in
|
||||
/// contrast to hash_combine which may produce different hash_codes for
|
||||
/// differing argument types even if they would implicit promote to a common
|
||||
/// type without changing the value.
|
||||
template <typename T>
|
||||
std::enable_if_t<is_integral_or_enum<T>::value, hash_code> hash_value(T value);
|
||||
|
||||
/// Compute a hash_code for a pointer's address.
|
||||
///
|
||||
/// N.B.: This hashes the *address*. Not the value and not the type.
|
||||
template <typename T> hash_code hash_value(const T *ptr);
|
||||
|
||||
/// Compute a hash_code for a pair of objects.
|
||||
template <typename T, typename U>
|
||||
hash_code hash_value(const std::pair<T, U> &arg);
|
||||
|
||||
/// Compute a hash_code for a tuple.
|
||||
template <typename... Ts>
|
||||
hash_code hash_value(const std::tuple<Ts...> &arg);
|
||||
|
||||
/// Compute a hash_code for a standard string.
|
||||
template <typename T>
|
||||
hash_code hash_value(const std::basic_string<T> &arg);
|
||||
|
||||
/// Compute a hash_code for a standard string.
|
||||
template <typename T> hash_code hash_value(const std::optional<T> &arg);
|
||||
|
||||
// All of the implementation details of actually computing the various hash
|
||||
// code values are held within this namespace. These routines are included in
|
||||
// the header file mainly to allow inlining and constant propagation.
|
||||
namespace hashing {
|
||||
namespace detail {
|
||||
|
||||
inline uint64_t fetch64(const char *p) {
|
||||
uint64_t result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
if (sys::IsBigEndianHost)
|
||||
sys::swapByteOrder(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t fetch32(const char *p) {
|
||||
uint32_t result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
if (sys::IsBigEndianHost)
|
||||
sys::swapByteOrder(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Some primes between 2^63 and 2^64 for various uses.
|
||||
static constexpr uint64_t k0 = 0xc3a5c85c97cb3127ULL;
|
||||
static constexpr uint64_t k1 = 0xb492b66fbe98f273ULL;
|
||||
static constexpr uint64_t k2 = 0x9ae16a3b2f90404fULL;
|
||||
static constexpr uint64_t k3 = 0xc949d7c7509e6557ULL;
|
||||
|
||||
/// Bitwise right rotate.
|
||||
/// Normally this will compile to a single instruction, especially if the
|
||||
/// shift is a manifest constant.
|
||||
inline uint64_t rotate(uint64_t val, size_t shift) {
|
||||
// Avoid shifting by 64: doing so yields an undefined result.
|
||||
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
|
||||
}
|
||||
|
||||
inline uint64_t shift_mix(uint64_t val) {
|
||||
return val ^ (val >> 47);
|
||||
}
|
||||
|
||||
inline uint64_t hash_16_bytes(uint64_t low, uint64_t high) {
|
||||
// Murmur-inspired hashing.
|
||||
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||
uint64_t a = (low ^ high) * kMul;
|
||||
a ^= (a >> 47);
|
||||
uint64_t b = (high ^ a) * kMul;
|
||||
b ^= (b >> 47);
|
||||
b *= kMul;
|
||||
return b;
|
||||
}
|
||||
|
||||
inline uint64_t hash_1to3_bytes(const char *s, size_t len, uint64_t seed) {
|
||||
uint8_t a = s[0];
|
||||
uint8_t b = s[len >> 1];
|
||||
uint8_t c = s[len - 1];
|
||||
uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
|
||||
uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
|
||||
return shift_mix(y * k2 ^ z * k3 ^ seed) * k2;
|
||||
}
|
||||
|
||||
inline uint64_t hash_4to8_bytes(const char *s, size_t len, uint64_t seed) {
|
||||
uint64_t a = fetch32(s);
|
||||
return hash_16_bytes(len + (a << 3), seed ^ fetch32(s + len - 4));
|
||||
}
|
||||
|
||||
inline uint64_t hash_9to16_bytes(const char *s, size_t len, uint64_t seed) {
|
||||
uint64_t a = fetch64(s);
|
||||
uint64_t b = fetch64(s + len - 8);
|
||||
return hash_16_bytes(seed ^ a, rotate(b + len, len)) ^ b;
|
||||
}
|
||||
|
||||
inline uint64_t hash_17to32_bytes(const char *s, size_t len, uint64_t seed) {
|
||||
uint64_t a = fetch64(s) * k1;
|
||||
uint64_t b = fetch64(s + 8);
|
||||
uint64_t c = fetch64(s + len - 8) * k2;
|
||||
uint64_t d = fetch64(s + len - 16) * k0;
|
||||
return hash_16_bytes(std::rotr<uint64_t>(a - b, 43) +
|
||||
std::rotr<uint64_t>(c ^ seed, 30) + d,
|
||||
a + std::rotr<uint64_t>(b ^ k3, 20) - c + len + seed);
|
||||
}
|
||||
|
||||
inline uint64_t hash_33to64_bytes(const char *s, size_t len, uint64_t seed) {
|
||||
uint64_t z = fetch64(s + 24);
|
||||
uint64_t a = fetch64(s) + (len + fetch64(s + len - 16)) * k0;
|
||||
uint64_t b = std::rotr<uint64_t>(a + z, 52);
|
||||
uint64_t c = std::rotr<uint64_t>(a, 37);
|
||||
a += fetch64(s + 8);
|
||||
c += std::rotr<uint64_t>(a, 7);
|
||||
a += fetch64(s + 16);
|
||||
uint64_t vf = a + z;
|
||||
uint64_t vs = b + std::rotr<uint64_t>(a, 31) + c;
|
||||
a = fetch64(s + 16) + fetch64(s + len - 32);
|
||||
z = fetch64(s + len - 8);
|
||||
b = std::rotr<uint64_t>(a + z, 52);
|
||||
c = std::rotr<uint64_t>(a, 37);
|
||||
a += fetch64(s + len - 24);
|
||||
c += std::rotr<uint64_t>(a, 7);
|
||||
a += fetch64(s + len - 16);
|
||||
uint64_t wf = a + z;
|
||||
uint64_t ws = b + std::rotr<uint64_t>(a, 31) + c;
|
||||
uint64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0);
|
||||
return shift_mix((seed ^ (r * k0)) + vs) * k2;
|
||||
}
|
||||
|
||||
inline uint64_t hash_short(const char *s, size_t length, uint64_t seed) {
|
||||
if (length >= 4 && length <= 8)
|
||||
return hash_4to8_bytes(s, length, seed);
|
||||
if (length > 8 && length <= 16)
|
||||
return hash_9to16_bytes(s, length, seed);
|
||||
if (length > 16 && length <= 32)
|
||||
return hash_17to32_bytes(s, length, seed);
|
||||
if (length > 32)
|
||||
return hash_33to64_bytes(s, length, seed);
|
||||
if (length != 0)
|
||||
return hash_1to3_bytes(s, length, seed);
|
||||
|
||||
return k2 ^ seed;
|
||||
}
|
||||
|
||||
/// The intermediate state used during hashing.
|
||||
/// Currently, the algorithm for computing hash codes is based on CityHash and
|
||||
/// keeps 56 bytes of arbitrary state.
|
||||
struct hash_state {
|
||||
uint64_t h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0;
|
||||
|
||||
/// Create a new hash_state structure and initialize it based on the
|
||||
/// seed and the first 64-byte chunk.
|
||||
/// This effectively performs the initial mix.
|
||||
static hash_state create(const char *s, uint64_t seed) {
|
||||
hash_state state = {0,
|
||||
seed,
|
||||
hash_16_bytes(seed, k1),
|
||||
std::rotr<uint64_t>(seed ^ k1, 49),
|
||||
seed * k1,
|
||||
shift_mix(seed),
|
||||
0};
|
||||
state.h6 = hash_16_bytes(state.h4, state.h5);
|
||||
state.mix(s);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// Mix 32-bytes from the input sequence into the 16-bytes of 'a'
|
||||
/// and 'b', including whatever is already in 'a' and 'b'.
|
||||
static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) {
|
||||
a += fetch64(s);
|
||||
uint64_t c = fetch64(s + 24);
|
||||
b = std::rotr<uint64_t>(b + a + c, 21);
|
||||
uint64_t d = a;
|
||||
a += fetch64(s + 8) + fetch64(s + 16);
|
||||
b += std::rotr<uint64_t>(a, 44) + d;
|
||||
a += c;
|
||||
}
|
||||
|
||||
/// Mix in a 64-byte buffer of data.
|
||||
/// We mix all 64 bytes even when the chunk length is smaller, but we
|
||||
/// record the actual length.
|
||||
void mix(const char *s) {
|
||||
h0 = std::rotr<uint64_t>(h0 + h1 + h3 + fetch64(s + 8), 37) * k1;
|
||||
h1 = std::rotr<uint64_t>(h1 + h4 + fetch64(s + 48), 42) * k1;
|
||||
h0 ^= h6;
|
||||
h1 += h3 + fetch64(s + 40);
|
||||
h2 = std::rotr<uint64_t>(h2 + h5, 33) * k1;
|
||||
h3 = h4 * k1;
|
||||
h4 = h0 + h5;
|
||||
mix_32_bytes(s, h3, h4);
|
||||
h5 = h2 + h6;
|
||||
h6 = h1 + fetch64(s + 16);
|
||||
mix_32_bytes(s + 32, h5, h6);
|
||||
std::swap(h2, h0);
|
||||
}
|
||||
|
||||
/// Compute the final 64-bit hash code value based on the current
|
||||
/// state and the length of bytes hashed.
|
||||
uint64_t finalize(size_t length) {
|
||||
return hash_16_bytes(hash_16_bytes(h3, h5) + shift_mix(h1) * k1 + h2,
|
||||
hash_16_bytes(h4, h6) + shift_mix(length) * k1 + h0);
|
||||
}
|
||||
};
|
||||
|
||||
/// In LLVM_ENABLE_ABI_BREAKING_CHECKS builds, the seed is non-deterministic
|
||||
/// per process (address of a function in LLVMSupport) to prevent having users
|
||||
/// depend on the particular hash values. On platforms without ASLR, this is
|
||||
/// still likely non-deterministic per build.
|
||||
inline uint64_t get_execution_seed() {
|
||||
// Work around x86-64 negative offset folding for old Clang -fno-pic
|
||||
// https://reviews.llvm.org/D93931
|
||||
#if LLVM_ENABLE_ABI_BREAKING_CHECKS && \
|
||||
(!defined(__clang__) || __clang_major__ > 11)
|
||||
return static_cast<uint64_t>(
|
||||
reinterpret_cast<uintptr_t>(&install_fatal_error_handler));
|
||||
#else
|
||||
return 0xff51afd7ed558ccdULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Trait to indicate whether a type's bits can be hashed directly.
|
||||
///
|
||||
/// A type trait which is true if we want to combine values for hashing by
|
||||
/// reading the underlying data. It is false if values of this type must
|
||||
/// first be passed to hash_value, and the resulting hash_codes combined.
|
||||
//
|
||||
// FIXME: We want to replace is_integral_or_enum and is_pointer here with
|
||||
// a predicate which asserts that comparing the underlying storage of two
|
||||
// values of the type for equality is equivalent to comparing the two values
|
||||
// for equality. For all the platforms we care about, this holds for integers
|
||||
// and pointers, but there are platforms where it doesn't and we would like to
|
||||
// support user-defined types which happen to satisfy this property.
|
||||
template <typename T> struct is_hashable_data
|
||||
: std::integral_constant<bool, ((is_integral_or_enum<T>::value ||
|
||||
std::is_pointer<T>::value) &&
|
||||
64 % sizeof(T) == 0)> {};
|
||||
|
||||
// Special case std::pair to detect when both types are viable and when there
|
||||
// is no alignment-derived padding in the pair. This is a bit of a lie because
|
||||
// std::pair isn't truly POD, but it's close enough in all reasonable
|
||||
// implementations for our use case of hashing the underlying data.
|
||||
template <typename T, typename U> struct is_hashable_data<std::pair<T, U> >
|
||||
: std::integral_constant<bool, (is_hashable_data<T>::value &&
|
||||
is_hashable_data<U>::value &&
|
||||
(sizeof(T) + sizeof(U)) ==
|
||||
sizeof(std::pair<T, U>))> {};
|
||||
|
||||
/// Helper to get the hashable data representation for a type.
|
||||
/// This variant is enabled when the type itself can be used.
|
||||
template <typename T>
|
||||
std::enable_if_t<is_hashable_data<T>::value, T>
|
||||
get_hashable_data(const T &value) {
|
||||
return value;
|
||||
}
|
||||
/// Helper to get the hashable data representation for a type.
|
||||
/// This variant is enabled when we must first call hash_value and use the
|
||||
/// result as our data.
|
||||
template <typename T>
|
||||
std::enable_if_t<!is_hashable_data<T>::value, size_t>
|
||||
get_hashable_data(const T &value) {
|
||||
using ::wpi::hash_value;
|
||||
return hash_value(value);
|
||||
}
|
||||
|
||||
/// Helper to store data from a value into a buffer and advance the
|
||||
/// pointer into that buffer.
|
||||
///
|
||||
/// This routine first checks whether there is enough space in the provided
|
||||
/// buffer, and if not immediately returns false. If there is space, it
|
||||
/// copies the underlying bytes of value into the buffer, advances the
|
||||
/// buffer_ptr past the copied bytes, and returns true.
|
||||
template <typename T>
|
||||
bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value,
|
||||
size_t offset = 0) {
|
||||
size_t store_size = sizeof(value) - offset;
|
||||
if (buffer_ptr + store_size > buffer_end)
|
||||
return false;
|
||||
const char *value_data = reinterpret_cast<const char *>(&value);
|
||||
memcpy(buffer_ptr, value_data + offset, store_size);
|
||||
buffer_ptr += store_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Implement the combining of integral values into a hash_code.
|
||||
///
|
||||
/// This overload is selected when the value type of the iterator is
|
||||
/// integral. Rather than computing a hash_code for each object and then
|
||||
/// combining them, this (as an optimization) directly combines the integers.
|
||||
template <typename InputIteratorT>
|
||||
hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
|
||||
const uint64_t seed = get_execution_seed();
|
||||
char buffer[64], *buffer_ptr = buffer;
|
||||
char *const buffer_end = std::end(buffer);
|
||||
while (first != last && store_and_advance(buffer_ptr, buffer_end,
|
||||
get_hashable_data(*first)))
|
||||
++first;
|
||||
if (first == last)
|
||||
return hash_short(buffer, buffer_ptr - buffer, seed);
|
||||
assert(buffer_ptr == buffer_end);
|
||||
|
||||
hash_state state = state.create(buffer, seed);
|
||||
size_t length = 64;
|
||||
while (first != last) {
|
||||
// Fill up the buffer. We don't clear it, which re-mixes the last round
|
||||
// when only a partial 64-byte chunk is left.
|
||||
buffer_ptr = buffer;
|
||||
while (first != last && store_and_advance(buffer_ptr, buffer_end,
|
||||
get_hashable_data(*first)))
|
||||
++first;
|
||||
|
||||
// Rotate the buffer if we did a partial fill in order to simulate doing
|
||||
// a mix of the last 64-bytes. That is how the algorithm works when we
|
||||
// have a contiguous byte sequence, and we want to emulate that here.
|
||||
std::rotate(buffer, buffer_ptr, buffer_end);
|
||||
|
||||
// Mix this chunk into the current state.
|
||||
state.mix(buffer);
|
||||
length += buffer_ptr - buffer;
|
||||
};
|
||||
|
||||
return state.finalize(length);
|
||||
}
|
||||
|
||||
/// Implement the combining of integral values into a hash_code.
|
||||
///
|
||||
/// This overload is selected when the value type of the iterator is integral
|
||||
/// and when the input iterator is actually a pointer. Rather than computing
|
||||
/// a hash_code for each object and then combining them, this (as an
|
||||
/// optimization) directly combines the integers. Also, because the integers
|
||||
/// are stored in contiguous memory, this routine avoids copying each value
|
||||
/// and directly reads from the underlying memory.
|
||||
template <typename ValueT>
|
||||
std::enable_if_t<is_hashable_data<ValueT>::value, hash_code>
|
||||
hash_combine_range_impl(ValueT *first, ValueT *last) {
|
||||
const uint64_t seed = get_execution_seed();
|
||||
const char *s_begin = reinterpret_cast<const char *>(first);
|
||||
const char *s_end = reinterpret_cast<const char *>(last);
|
||||
const size_t length = std::distance(s_begin, s_end);
|
||||
if (length <= 64)
|
||||
return hash_short(s_begin, length, seed);
|
||||
|
||||
const char *s_aligned_end = s_begin + (length & ~63);
|
||||
hash_state state = state.create(s_begin, seed);
|
||||
s_begin += 64;
|
||||
while (s_begin != s_aligned_end) {
|
||||
state.mix(s_begin);
|
||||
s_begin += 64;
|
||||
}
|
||||
if (length & 63)
|
||||
state.mix(s_end - 64);
|
||||
|
||||
return state.finalize(length);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace hashing
|
||||
|
||||
|
||||
/// Compute a hash_code for a sequence of values.
|
||||
///
|
||||
/// This hashes a sequence of values. It produces the same hash_code as
|
||||
/// 'hash_combine(a, b, c, ...)', but can run over arbitrary sized sequences
|
||||
/// and is significantly faster given pointers and types which can be hashed as
|
||||
/// a sequence of bytes.
|
||||
template <typename InputIteratorT>
|
||||
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last) {
|
||||
return ::wpi::hashing::detail::hash_combine_range_impl(first, last);
|
||||
}
|
||||
|
||||
|
||||
// Implementation details for hash_combine.
|
||||
namespace hashing {
|
||||
namespace detail {
|
||||
|
||||
/// Helper class to manage the recursive combining of hash_combine
|
||||
/// arguments.
|
||||
///
|
||||
/// This class exists to manage the state and various calls involved in the
|
||||
/// recursive combining of arguments used in hash_combine. It is particularly
|
||||
/// useful at minimizing the code in the recursive calls to ease the pain
|
||||
/// caused by a lack of variadic functions.
|
||||
struct hash_combine_recursive_helper {
|
||||
char buffer[64] = {};
|
||||
hash_state state;
|
||||
const uint64_t seed;
|
||||
|
||||
public:
|
||||
/// Construct a recursive hash combining helper.
|
||||
///
|
||||
/// This sets up the state for a recursive hash combine, including getting
|
||||
/// the seed and buffer setup.
|
||||
hash_combine_recursive_helper()
|
||||
: seed(get_execution_seed()) {}
|
||||
|
||||
/// Combine one chunk of data into the current in-flight hash.
|
||||
///
|
||||
/// This merges one chunk of data into the hash. First it tries to buffer
|
||||
/// the data. If the buffer is full, it hashes the buffer into its
|
||||
/// hash_state, empties it, and then merges the new chunk in. This also
|
||||
/// handles cases where the data straddles the end of the buffer.
|
||||
template <typename T>
|
||||
char *combine_data(size_t &length, char *buffer_ptr, char *buffer_end, T data) {
|
||||
if (!store_and_advance(buffer_ptr, buffer_end, data)) {
|
||||
// Check for skew which prevents the buffer from being packed, and do
|
||||
// a partial store into the buffer to fill it. This is only a concern
|
||||
// with the variadic combine because that formation can have varying
|
||||
// argument types.
|
||||
size_t partial_store_size = buffer_end - buffer_ptr;
|
||||
memcpy(buffer_ptr, &data, partial_store_size);
|
||||
|
||||
// If the store fails, our buffer is full and ready to hash. We have to
|
||||
// either initialize the hash state (on the first full buffer) or mix
|
||||
// this buffer into the existing hash state. Length tracks the *hashed*
|
||||
// length, not the buffered length.
|
||||
if (length == 0) {
|
||||
state = state.create(buffer, seed);
|
||||
length = 64;
|
||||
} else {
|
||||
// Mix this chunk into the current state and bump length up by 64.
|
||||
state.mix(buffer);
|
||||
length += 64;
|
||||
}
|
||||
// Reset the buffer_ptr to the head of the buffer for the next chunk of
|
||||
// data.
|
||||
buffer_ptr = buffer;
|
||||
|
||||
// Try again to store into the buffer -- this cannot fail as we only
|
||||
// store types smaller than the buffer.
|
||||
if (!store_and_advance(buffer_ptr, buffer_end, data,
|
||||
partial_store_size))
|
||||
wpi_unreachable("buffer smaller than stored type");
|
||||
}
|
||||
return buffer_ptr;
|
||||
}
|
||||
|
||||
/// Recursive, variadic combining method.
|
||||
///
|
||||
/// This function recurses through each argument, combining that argument
|
||||
/// into a single hash.
|
||||
template <typename T, typename ...Ts>
|
||||
hash_code combine(size_t length, char *buffer_ptr, char *buffer_end,
|
||||
const T &arg, const Ts &...args) {
|
||||
buffer_ptr = combine_data(length, buffer_ptr, buffer_end, get_hashable_data(arg));
|
||||
|
||||
// Recurse to the next argument.
|
||||
return combine(length, buffer_ptr, buffer_end, args...);
|
||||
}
|
||||
|
||||
/// Base case for recursive, variadic combining.
|
||||
///
|
||||
/// The base case when combining arguments recursively is reached when all
|
||||
/// arguments have been handled. It flushes the remaining buffer and
|
||||
/// constructs a hash_code.
|
||||
hash_code combine(size_t length, char *buffer_ptr, char *buffer_end) {
|
||||
// Check whether the entire set of values fit in the buffer. If so, we'll
|
||||
// use the optimized short hashing routine and skip state entirely.
|
||||
if (length == 0)
|
||||
return hash_short(buffer, buffer_ptr - buffer, seed);
|
||||
|
||||
// Mix the final buffer, rotating it if we did a partial fill in order to
|
||||
// simulate doing a mix of the last 64-bytes. That is how the algorithm
|
||||
// works when we have a contiguous byte sequence, and we want to emulate
|
||||
// that here.
|
||||
std::rotate(buffer, buffer_ptr, buffer_end);
|
||||
|
||||
// Mix this chunk into the current state.
|
||||
state.mix(buffer);
|
||||
length += buffer_ptr - buffer;
|
||||
|
||||
return state.finalize(length);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace hashing
|
||||
|
||||
/// Combine values into a single hash_code.
|
||||
///
|
||||
/// This routine accepts a varying number of arguments of any type. It will
|
||||
/// attempt to combine them into a single hash_code. For user-defined types it
|
||||
/// attempts to call a \see hash_value overload (via ADL) for the type. For
|
||||
/// integer and pointer types it directly combines their data into the
|
||||
/// resulting hash_code.
|
||||
///
|
||||
/// The result is suitable for returning from a user's hash_value
|
||||
/// *implementation* for their user-defined type. Consumers of a type should
|
||||
/// *not* call this routine, they should instead call 'hash_value'.
|
||||
template <typename ...Ts> hash_code hash_combine(const Ts &...args) {
|
||||
// Recursively hash each argument using a helper class.
|
||||
::wpi::hashing::detail::hash_combine_recursive_helper helper;
|
||||
return helper.combine(0, helper.buffer, helper.buffer + 64, args...);
|
||||
}
|
||||
|
||||
// Implementation details for implementations of hash_value overloads provided
|
||||
// here.
|
||||
namespace hashing {
|
||||
namespace detail {
|
||||
|
||||
/// Helper to hash the value of a single integer.
|
||||
///
|
||||
/// Overloads for smaller integer types are not provided to ensure consistent
|
||||
/// behavior in the presence of integral promotions. Essentially,
|
||||
/// "hash_value('4')" and "hash_value('0' + 4)" should be the same.
|
||||
inline hash_code hash_integer_value(uint64_t value) {
|
||||
// Similar to hash_4to8_bytes but using a seed instead of length.
|
||||
const uint64_t seed = get_execution_seed();
|
||||
const char *s = reinterpret_cast<const char *>(&value);
|
||||
const uint64_t a = fetch32(s);
|
||||
return hash_16_bytes(seed + (a << 3), fetch32(s + 4));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace hashing
|
||||
|
||||
// Declared and documented above, but defined here so that any of the hashing
|
||||
// infrastructure is available.
|
||||
template <typename T>
|
||||
std::enable_if_t<is_integral_or_enum<T>::value, hash_code> hash_value(T value) {
|
||||
return ::wpi::hashing::detail::hash_integer_value(
|
||||
static_cast<uint64_t>(value));
|
||||
}
|
||||
|
||||
// Declared and documented above, but defined here so that any of the hashing
|
||||
// infrastructure is available.
|
||||
template <typename T> hash_code hash_value(const T *ptr) {
|
||||
return ::wpi::hashing::detail::hash_integer_value(
|
||||
reinterpret_cast<uintptr_t>(ptr));
|
||||
}
|
||||
|
||||
// Declared and documented above, but defined here so that any of the hashing
|
||||
// infrastructure is available.
|
||||
template <typename T, typename U>
|
||||
hash_code hash_value(const std::pair<T, U> &arg) {
|
||||
return hash_combine(arg.first, arg.second);
|
||||
}
|
||||
|
||||
template <typename... Ts> hash_code hash_value(const std::tuple<Ts...> &arg) {
|
||||
return std::apply([](const auto &...xs) { return hash_combine(xs...); }, arg);
|
||||
}
|
||||
|
||||
// Declared and documented above, but defined here so that any of the hashing
|
||||
// infrastructure is available.
|
||||
template <typename T>
|
||||
hash_code hash_value(const std::basic_string<T> &arg) {
|
||||
return hash_combine_range(arg.begin(), arg.end());
|
||||
}
|
||||
|
||||
template <typename T> hash_code hash_value(const std::optional<T> &arg) {
|
||||
return arg ? hash_combine(true, *arg) : hash_value(false);
|
||||
}
|
||||
|
||||
template <> struct DenseMapInfo<hash_code, void> {
|
||||
static inline hash_code getEmptyKey() { return hash_code(-1); }
|
||||
static inline hash_code getTombstoneKey() { return hash_code(-2); }
|
||||
static unsigned getHashValue(hash_code val) {
|
||||
return static_cast<unsigned>(size_t(val));
|
||||
}
|
||||
static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
/// Implement std::hash so that hash_code can be used in STL containers.
|
||||
namespace std {
|
||||
|
||||
template<>
|
||||
struct hash<wpi::hash_code> {
|
||||
size_t operator()(wpi::hash_code const& Val) const {
|
||||
return Val;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std;
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,259 +0,0 @@
|
||||
//===- llvm/ADT/MapVector.h - Map w/ deterministic value order --*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file implements a map that provides insertion order iteration. The
|
||||
/// interface is purposefully minimal. The key is assumed to be cheap to copy
|
||||
/// and 2 copies are kept, one for indexing in a DenseMap, one for iteration in
|
||||
/// a SmallVector.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_MAPVECTOR_H
|
||||
#define WPIUTIL_WPI_MAPVECTOR_H
|
||||
|
||||
#include "wpi/DenseMap.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// This class implements a map that also provides access to all stored values
|
||||
/// in a deterministic order. The values are kept in a SmallVector<*, 0> and the
|
||||
/// mapping is done with DenseMap from Keys to indexes in that vector.
|
||||
template <typename KeyT, typename ValueT,
|
||||
typename MapType = DenseMap<KeyT, unsigned>,
|
||||
typename VectorType = SmallVector<std::pair<KeyT, ValueT>, 0>>
|
||||
class MapVector {
|
||||
MapType Map;
|
||||
VectorType Vector;
|
||||
|
||||
static_assert(
|
||||
std::is_integral_v<typename MapType::mapped_type>,
|
||||
"The mapped_type of the specified Map must be an integral type");
|
||||
|
||||
public:
|
||||
using key_type = KeyT;
|
||||
using value_type = typename VectorType::value_type;
|
||||
using size_type = typename VectorType::size_type;
|
||||
|
||||
using iterator = typename VectorType::iterator;
|
||||
using const_iterator = typename VectorType::const_iterator;
|
||||
using reverse_iterator = typename VectorType::reverse_iterator;
|
||||
using const_reverse_iterator = typename VectorType::const_reverse_iterator;
|
||||
|
||||
/// Clear the MapVector and return the underlying vector.
|
||||
VectorType takeVector() {
|
||||
Map.clear();
|
||||
return std::move(Vector);
|
||||
}
|
||||
|
||||
size_type size() const { return Vector.size(); }
|
||||
|
||||
/// Grow the MapVector so that it can contain at least \p NumEntries items
|
||||
/// before resizing again.
|
||||
void reserve(size_type NumEntries) {
|
||||
Map.reserve(NumEntries);
|
||||
Vector.reserve(NumEntries);
|
||||
}
|
||||
|
||||
iterator begin() { return Vector.begin(); }
|
||||
const_iterator begin() const { return Vector.begin(); }
|
||||
iterator end() { return Vector.end(); }
|
||||
const_iterator end() const { return Vector.end(); }
|
||||
|
||||
reverse_iterator rbegin() { return Vector.rbegin(); }
|
||||
const_reverse_iterator rbegin() const { return Vector.rbegin(); }
|
||||
reverse_iterator rend() { return Vector.rend(); }
|
||||
const_reverse_iterator rend() const { return Vector.rend(); }
|
||||
|
||||
bool empty() const {
|
||||
return Vector.empty();
|
||||
}
|
||||
|
||||
std::pair<KeyT, ValueT> &front() { return Vector.front(); }
|
||||
const std::pair<KeyT, ValueT> &front() const { return Vector.front(); }
|
||||
std::pair<KeyT, ValueT> &back() { return Vector.back(); }
|
||||
const std::pair<KeyT, ValueT> &back() const { return Vector.back(); }
|
||||
|
||||
void clear() {
|
||||
Map.clear();
|
||||
Vector.clear();
|
||||
}
|
||||
|
||||
void swap(MapVector &RHS) {
|
||||
std::swap(Map, RHS.Map);
|
||||
std::swap(Vector, RHS.Vector);
|
||||
}
|
||||
|
||||
ValueT &operator[](const KeyT &Key) {
|
||||
std::pair<KeyT, typename MapType::mapped_type> Pair = std::make_pair(Key, 0);
|
||||
std::pair<typename MapType::iterator, bool> Result = Map.insert(Pair);
|
||||
auto &I = Result.first->second;
|
||||
if (Result.second) {
|
||||
Vector.push_back(std::make_pair(Key, ValueT()));
|
||||
I = Vector.size() - 1;
|
||||
}
|
||||
return Vector[I].second;
|
||||
}
|
||||
|
||||
// Returns a copy of the value. Only allowed if ValueT is copyable.
|
||||
ValueT lookup(const KeyT &Key) const {
|
||||
static_assert(std::is_copy_constructible_v<ValueT>,
|
||||
"Cannot call lookup() if ValueT is not copyable.");
|
||||
typename MapType::const_iterator Pos = Map.find(Key);
|
||||
return Pos == Map.end()? ValueT() : Vector[Pos->second].second;
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&...Args) {
|
||||
auto [It, Inserted] = Map.insert(std::make_pair(Key, 0));
|
||||
if (Inserted) {
|
||||
It->second = Vector.size();
|
||||
Vector.emplace_back(std::piecewise_construct, std::forward_as_tuple(Key),
|
||||
std::forward_as_tuple(std::forward<Ts>(Args)...));
|
||||
return std::make_pair(std::prev(end()), true);
|
||||
}
|
||||
return std::make_pair(begin() + It->second, false);
|
||||
}
|
||||
template <typename... Ts>
|
||||
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&...Args) {
|
||||
auto [It, Inserted] = Map.insert(std::make_pair(Key, 0));
|
||||
if (Inserted) {
|
||||
It->second = Vector.size();
|
||||
Vector.emplace_back(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::move(Key)),
|
||||
std::forward_as_tuple(std::forward<Ts>(Args)...));
|
||||
return std::make_pair(std::prev(end()), true);
|
||||
}
|
||||
return std::make_pair(begin() + It->second, false);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) {
|
||||
return try_emplace(KV.first, KV.second);
|
||||
}
|
||||
std::pair<iterator, bool> insert(std::pair<KeyT, ValueT> &&KV) {
|
||||
return try_emplace(std::move(KV.first), std::move(KV.second));
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
std::pair<iterator, bool> insert_or_assign(const KeyT &Key, V &&Val) {
|
||||
auto Ret = try_emplace(Key, std::forward<V>(Val));
|
||||
if (!Ret.second)
|
||||
Ret.first->second = std::forward<V>(Val);
|
||||
return Ret;
|
||||
}
|
||||
template <typename V>
|
||||
std::pair<iterator, bool> insert_or_assign(KeyT &&Key, V &&Val) {
|
||||
auto Ret = try_emplace(std::move(Key), std::forward<V>(Val));
|
||||
if (!Ret.second)
|
||||
Ret.first->second = std::forward<V>(Val);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
bool contains(const KeyT &Key) const { return Map.find(Key) != Map.end(); }
|
||||
|
||||
size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; }
|
||||
|
||||
iterator find(const KeyT &Key) {
|
||||
typename MapType::const_iterator Pos = Map.find(Key);
|
||||
return Pos == Map.end()? Vector.end() :
|
||||
(Vector.begin() + Pos->second);
|
||||
}
|
||||
|
||||
const_iterator find(const KeyT &Key) const {
|
||||
typename MapType::const_iterator Pos = Map.find(Key);
|
||||
return Pos == Map.end()? Vector.end() :
|
||||
(Vector.begin() + Pos->second);
|
||||
}
|
||||
|
||||
/// Remove the last element from the vector.
|
||||
void pop_back() {
|
||||
typename MapType::iterator Pos = Map.find(Vector.back().first);
|
||||
Map.erase(Pos);
|
||||
Vector.pop_back();
|
||||
}
|
||||
|
||||
/// Remove the element given by Iterator.
|
||||
///
|
||||
/// Returns an iterator to the element following the one which was removed,
|
||||
/// which may be end().
|
||||
///
|
||||
/// \note This is a deceivingly expensive operation (linear time). It's
|
||||
/// usually better to use \a remove_if() if possible.
|
||||
typename VectorType::iterator erase(typename VectorType::iterator Iterator) {
|
||||
Map.erase(Iterator->first);
|
||||
auto Next = Vector.erase(Iterator);
|
||||
if (Next == Vector.end())
|
||||
return Next;
|
||||
|
||||
// Update indices in the map.
|
||||
size_t Index = Next - Vector.begin();
|
||||
for (auto &I : Map) {
|
||||
assert(I.second != Index && "Index was already erased!");
|
||||
if (I.second > Index)
|
||||
--I.second;
|
||||
}
|
||||
return Next;
|
||||
}
|
||||
|
||||
/// Remove all elements with the key value Key.
|
||||
///
|
||||
/// Returns the number of elements removed.
|
||||
size_type erase(const KeyT &Key) {
|
||||
auto Iterator = find(Key);
|
||||
if (Iterator == end())
|
||||
return 0;
|
||||
erase(Iterator);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Remove the elements that match the predicate.
|
||||
///
|
||||
/// Erase all elements that match \c Pred in a single pass. Takes linear
|
||||
/// time.
|
||||
template <class Predicate> void remove_if(Predicate Pred);
|
||||
};
|
||||
|
||||
template <typename KeyT, typename ValueT, typename MapType, typename VectorType>
|
||||
template <class Function>
|
||||
void MapVector<KeyT, ValueT, MapType, VectorType>::remove_if(Function Pred) {
|
||||
auto O = Vector.begin();
|
||||
for (auto I = O, E = Vector.end(); I != E; ++I) {
|
||||
if (Pred(*I)) {
|
||||
// Erase from the map.
|
||||
Map.erase(I->first);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (I != O) {
|
||||
// Move the value and update the index in the map.
|
||||
*O = std::move(*I);
|
||||
Map[O->first] = O - Vector.begin();
|
||||
}
|
||||
++O;
|
||||
}
|
||||
// Erase trailing entries in the vector.
|
||||
Vector.erase(O, Vector.end());
|
||||
}
|
||||
|
||||
/// A MapVector that performs no allocations if smaller than a certain
|
||||
/// size.
|
||||
template <typename KeyT, typename ValueT, unsigned N>
|
||||
struct SmallMapVector
|
||||
: MapVector<KeyT, ValueT, SmallDenseMap<KeyT, unsigned, N>,
|
||||
SmallVector<std::pair<KeyT, ValueT>, N>> {
|
||||
};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_MAPVECTOR_H
|
||||
@@ -471,13 +471,21 @@ constexpr uint64_t alignTo(uint64_t Value, uint64_t Align) {
|
||||
return CeilDiv * Align;
|
||||
}
|
||||
|
||||
/// Will overflow only if result is not representable in T.
|
||||
template <typename U, typename V, typename T = common_uint<U, V>>
|
||||
constexpr T alignToPowerOf2(U Value, V Align) {
|
||||
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
|
||||
"Align must be a power of 2");
|
||||
T NegAlign = static_cast<T>(0) - Align;
|
||||
return (Value + (Align - 1)) & NegAlign;
|
||||
}
|
||||
|
||||
/// Fallback when arguments aren't integral.
|
||||
constexpr uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
|
||||
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
|
||||
"Align must be a power of 2");
|
||||
// Replace unary minus to avoid compilation error on Windows:
|
||||
// "unary minus operator applied to unsigned type, result still unsigned"
|
||||
uint64_t NegAlign = (~Align) + 1;
|
||||
return (Value + Align - 1) & NegAlign;
|
||||
uint64_t NegAlign = 0 - Align;
|
||||
return (Value + (Align - 1)) & NegAlign;
|
||||
}
|
||||
|
||||
/// If non-zero \p Skew is specified, the return value will be a minimal integer
|
||||
|
||||
@@ -192,12 +192,18 @@ public:
|
||||
// isa<T>, cast<T> and the wpi::dyn_cast<T>
|
||||
|
||||
/// Test if the Union currently holds the type matching T.
|
||||
template <typename T> inline bool is() const { return isa<T>(*this); }
|
||||
template <typename T>
|
||||
[[deprecated("Use isa instead")]]
|
||||
inline bool is() const {
|
||||
return isa<T>(*this);
|
||||
}
|
||||
|
||||
/// Returns the value of the specified pointer type.
|
||||
///
|
||||
/// If the specified pointer type is incorrect, assert.
|
||||
template <typename T> inline T get() const {
|
||||
template <typename T>
|
||||
[[deprecated("Use cast instead")]]
|
||||
inline T get() const {
|
||||
assert(isa<T>(*this) && "Invalid accessor called");
|
||||
return cast<T>(*this);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
#define WPIUTIL_WPI_SMALLPTRSET_H
|
||||
|
||||
#include "wpi/EpochTracker.h"
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/MathExtras.h"
|
||||
#include "wpi/ReverseIteration.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
@@ -52,10 +53,7 @@ class SmallPtrSetImplBase : public DebugEpochBase {
|
||||
friend class SmallPtrSetIteratorImpl;
|
||||
|
||||
protected:
|
||||
/// SmallArray - Points to a fixed size set of buckets, used in 'small mode'.
|
||||
const void **SmallArray;
|
||||
/// CurArray - This is the current set of buckets. If equal to SmallArray,
|
||||
/// then the set is in 'small mode'.
|
||||
/// The current set of buckets, in either small or big representation.
|
||||
const void **CurArray;
|
||||
/// CurArraySize - The allocated size of CurArray, always a power of two.
|
||||
unsigned CurArraySize;
|
||||
@@ -66,16 +64,18 @@ protected:
|
||||
unsigned NumNonEmpty;
|
||||
/// 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,
|
||||
SmallPtrSetImplBase &&that);
|
||||
const void **RHSSmallStorage, SmallPtrSetImplBase &&that);
|
||||
|
||||
explicit SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize)
|
||||
: SmallArray(SmallStorage), CurArray(SmallStorage),
|
||||
CurArraySize(SmallSize), NumNonEmpty(0), NumTombstones(0) {
|
||||
: CurArray(SmallStorage), CurArraySize(SmallSize), NumNonEmpty(0),
|
||||
NumTombstones(0), IsSmall(true) {
|
||||
assert(SmallSize && (SmallSize & (SmallSize-1)) == 0 &&
|
||||
"Initial size must be a power of two!");
|
||||
}
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||
size_type size() const { return NumNonEmpty - NumTombstones; }
|
||||
size_type capacity() const { return CurArraySize; }
|
||||
|
||||
void clear() {
|
||||
incrementEpoch();
|
||||
@@ -108,6 +109,27 @@ public:
|
||||
NumTombstones = 0;
|
||||
}
|
||||
|
||||
void reserve(size_type NumEntries) {
|
||||
incrementEpoch();
|
||||
// Do nothing if we're given zero as a reservation size.
|
||||
if (NumEntries == 0)
|
||||
return;
|
||||
// No need to expand if we're small and NumEntries will fit in the space.
|
||||
if (isSmall() && NumEntries <= 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))
|
||||
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);
|
||||
// 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); }
|
||||
|
||||
@@ -127,7 +149,7 @@ 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 = SmallArray, **E = SmallArray + NumNonEmpty;
|
||||
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr) {
|
||||
const void *Value = *APtr;
|
||||
if (Value == Ptr)
|
||||
@@ -136,9 +158,9 @@ protected:
|
||||
|
||||
// Nope, there isn't. If we stay small, just 'pushback' now.
|
||||
if (NumNonEmpty < CurArraySize) {
|
||||
SmallArray[NumNonEmpty++] = Ptr;
|
||||
CurArray[NumNonEmpty++] = Ptr;
|
||||
incrementEpoch();
|
||||
return std::make_pair(SmallArray + (NumNonEmpty - 1), true);
|
||||
return std::make_pair(CurArray + (NumNonEmpty - 1), true);
|
||||
}
|
||||
// Otherwise, hit the big set case, which will call grow.
|
||||
}
|
||||
@@ -151,10 +173,10 @@ protected:
|
||||
/// in.
|
||||
bool erase_imp(const void * Ptr) {
|
||||
if (isSmall()) {
|
||||
for (const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
|
||||
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr) {
|
||||
if (*APtr == Ptr) {
|
||||
*APtr = SmallArray[--NumNonEmpty];
|
||||
*APtr = CurArray[--NumNonEmpty];
|
||||
incrementEpoch();
|
||||
return true;
|
||||
}
|
||||
@@ -162,8 +184,8 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *Bucket = FindBucketFor(Ptr);
|
||||
if (*Bucket != Ptr)
|
||||
auto *Bucket = doFind(Ptr);
|
||||
if (!Bucket)
|
||||
return false;
|
||||
|
||||
*const_cast<const void **>(Bucket) = getTombstoneMarker();
|
||||
@@ -180,25 +202,40 @@ protected:
|
||||
const void *const * find_imp(const void * Ptr) const {
|
||||
if (isSmall()) {
|
||||
// Linear search for the item.
|
||||
for (const void *const *APtr = SmallArray,
|
||||
*const *E = SmallArray + NumNonEmpty; APtr != E; ++APtr)
|
||||
for (const void *const *APtr = CurArray, *const *E =
|
||||
CurArray + NumNonEmpty;
|
||||
APtr != E; ++APtr)
|
||||
if (*APtr == Ptr)
|
||||
return APtr;
|
||||
return EndPointer();
|
||||
}
|
||||
|
||||
// Big set case.
|
||||
auto *Bucket = FindBucketFor(Ptr);
|
||||
if (*Bucket == Ptr)
|
||||
if (auto *Bucket = doFind(Ptr))
|
||||
return Bucket;
|
||||
return EndPointer();
|
||||
}
|
||||
|
||||
bool isSmall() const { return CurArray == SmallArray; }
|
||||
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)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return doFind(Ptr) != nullptr;
|
||||
}
|
||||
|
||||
bool isSmall() const { return IsSmall; }
|
||||
|
||||
private:
|
||||
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();
|
||||
|
||||
@@ -208,16 +245,19 @@ private:
|
||||
protected:
|
||||
/// swap - Swaps the elements of two sets.
|
||||
/// Note: This method assumes that both sets have the same small size.
|
||||
void swap(SmallPtrSetImplBase &RHS);
|
||||
void swap(const void **SmallStorage, const void **RHSSmallStorage,
|
||||
SmallPtrSetImplBase &RHS);
|
||||
|
||||
void CopyFrom(const SmallPtrSetImplBase &RHS);
|
||||
void MoveFrom(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
|
||||
void copyFrom(const void **SmallStorage, const SmallPtrSetImplBase &RHS);
|
||||
void moveFrom(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
|
||||
|
||||
private:
|
||||
/// Code shared by MoveFrom() and move constructor.
|
||||
void MoveHelper(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
|
||||
/// Code shared by CopyFrom() and copy constructor.
|
||||
void CopyHelper(const SmallPtrSetImplBase &RHS);
|
||||
/// Code shared by moveFrom() and move constructor.
|
||||
void moveHelper(const void **SmallStorage, unsigned SmallSize,
|
||||
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
|
||||
/// Code shared by copyFrom() and copy constructor.
|
||||
void copyHelper(const SmallPtrSetImplBase &RHS);
|
||||
};
|
||||
|
||||
/// SmallPtrSetIteratorImpl - This is the common base class shared between all
|
||||
@@ -378,7 +418,7 @@ public:
|
||||
bool remove_if(UnaryPredicate P) {
|
||||
bool Removed = false;
|
||||
if (isSmall()) {
|
||||
const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
|
||||
const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
|
||||
while (APtr != E) {
|
||||
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(*APtr));
|
||||
if (P(Ptr)) {
|
||||
@@ -410,13 +450,13 @@ public:
|
||||
|
||||
/// count - Return 1 if the specified pointer is in the set, 0 otherwise.
|
||||
size_type count(ConstPtrType Ptr) const {
|
||||
return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
|
||||
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
|
||||
}
|
||||
iterator find(ConstPtrType Ptr) const {
|
||||
return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)));
|
||||
}
|
||||
bool contains(ConstPtrType Ptr) const {
|
||||
return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
|
||||
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
|
||||
}
|
||||
|
||||
template <typename IterT>
|
||||
@@ -503,7 +543,8 @@ public:
|
||||
SmallPtrSet() : BaseT(SmallStorage, SmallSizePowTwo) {}
|
||||
SmallPtrSet(const SmallPtrSet &that) : BaseT(SmallStorage, that) {}
|
||||
SmallPtrSet(SmallPtrSet &&that)
|
||||
: BaseT(SmallStorage, SmallSizePowTwo, std::move(that)) {}
|
||||
: BaseT(SmallStorage, SmallSizePowTwo, that.SmallStorage,
|
||||
std::move(that)) {}
|
||||
|
||||
template<typename It>
|
||||
SmallPtrSet(It I, It E) : BaseT(SmallStorage, SmallSizePowTwo) {
|
||||
@@ -518,14 +559,15 @@ public:
|
||||
SmallPtrSet<PtrType, SmallSize> &
|
||||
operator=(const SmallPtrSet<PtrType, SmallSize> &RHS) {
|
||||
if (&RHS != this)
|
||||
this->CopyFrom(RHS);
|
||||
this->copyFrom(SmallStorage, RHS);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SmallPtrSet<PtrType, SmallSize> &
|
||||
operator=(SmallPtrSet<PtrType, SmallSize> &&RHS) {
|
||||
if (&RHS != this)
|
||||
this->MoveFrom(SmallSizePowTwo, std::move(RHS));
|
||||
this->moveFrom(SmallStorage, SmallSizePowTwo, RHS.SmallStorage,
|
||||
std::move(RHS));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -538,7 +580,7 @@ public:
|
||||
|
||||
/// swap - Swaps the elements of two sets.
|
||||
void swap(SmallPtrSet<PtrType, SmallSize> &RHS) {
|
||||
SmallPtrSetImplBase::swap(RHS);
|
||||
SmallPtrSetImplBase::swap(SmallStorage, RHS.SmallStorage, RHS);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
#include "wpi/SmallPtrSet.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/iterator.h"
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <initializer_list>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi {
|
||||
@@ -46,24 +43,24 @@ private:
|
||||
VecIterTy VecIter;
|
||||
};
|
||||
|
||||
bool isSmall;
|
||||
bool IsSmall;
|
||||
|
||||
public:
|
||||
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), isSmall(false) {}
|
||||
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), IsSmall(false) {}
|
||||
|
||||
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), isSmall(true) {}
|
||||
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), IsSmall(true) {}
|
||||
|
||||
// Spell out destructor, copy/move constructor and assignment operators for
|
||||
// MSVC STL, where set<T>::const_iterator is not trivially copy constructible.
|
||||
~SmallSetIterator() {
|
||||
if (isSmall)
|
||||
if (IsSmall)
|
||||
VecIter.~VecIterTy();
|
||||
else
|
||||
SetIter.~SetIterTy();
|
||||
}
|
||||
|
||||
SmallSetIterator(const SmallSetIterator &Other) : isSmall(Other.isSmall) {
|
||||
if (isSmall)
|
||||
SmallSetIterator(const SmallSetIterator &Other) : IsSmall(Other.IsSmall) {
|
||||
if (IsSmall)
|
||||
VecIter = Other.VecIter;
|
||||
else
|
||||
// Use placement new, to make sure SetIter is properly constructed, even
|
||||
@@ -71,8 +68,8 @@ public:
|
||||
new (&SetIter) SetIterTy(Other.SetIter);
|
||||
}
|
||||
|
||||
SmallSetIterator(SmallSetIterator &&Other) : isSmall(Other.isSmall) {
|
||||
if (isSmall)
|
||||
SmallSetIterator(SmallSetIterator &&Other) : IsSmall(Other.IsSmall) {
|
||||
if (IsSmall)
|
||||
VecIter = std::move(Other.VecIter);
|
||||
else
|
||||
// Use placement new, to make sure SetIter is properly constructed, even
|
||||
@@ -83,11 +80,11 @@ public:
|
||||
SmallSetIterator& operator=(const SmallSetIterator& Other) {
|
||||
// Call destructor for SetIter, so it gets properly destroyed if it is
|
||||
// not trivially destructible in case we are setting VecIter.
|
||||
if (!isSmall)
|
||||
if (!IsSmall)
|
||||
SetIter.~SetIterTy();
|
||||
|
||||
isSmall = Other.isSmall;
|
||||
if (isSmall)
|
||||
IsSmall = Other.IsSmall;
|
||||
if (IsSmall)
|
||||
VecIter = Other.VecIter;
|
||||
else
|
||||
new (&SetIter) SetIterTy(Other.SetIter);
|
||||
@@ -97,11 +94,11 @@ public:
|
||||
SmallSetIterator& operator=(SmallSetIterator&& Other) {
|
||||
// Call destructor for SetIter, so it gets properly destroyed if it is
|
||||
// not trivially destructible in case we are setting VecIter.
|
||||
if (!isSmall)
|
||||
if (!IsSmall)
|
||||
SetIter.~SetIterTy();
|
||||
|
||||
isSmall = Other.isSmall;
|
||||
if (isSmall)
|
||||
IsSmall = Other.IsSmall;
|
||||
if (IsSmall)
|
||||
VecIter = std::move(Other.VecIter);
|
||||
else
|
||||
new (&SetIter) SetIterTy(std::move(Other.SetIter));
|
||||
@@ -109,22 +106,22 @@ public:
|
||||
}
|
||||
|
||||
bool operator==(const SmallSetIterator &RHS) const {
|
||||
if (isSmall != RHS.isSmall)
|
||||
if (IsSmall != RHS.IsSmall)
|
||||
return false;
|
||||
if (isSmall)
|
||||
if (IsSmall)
|
||||
return VecIter == RHS.VecIter;
|
||||
return SetIter == RHS.SetIter;
|
||||
}
|
||||
|
||||
SmallSetIterator &operator++() { // Preincrement
|
||||
if (isSmall)
|
||||
VecIter++;
|
||||
if (IsSmall)
|
||||
++VecIter;
|
||||
else
|
||||
SetIter++;
|
||||
++SetIter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T &operator*() const { return isSmall ? *VecIter : *SetIter; }
|
||||
const T &operator*() const { return IsSmall ? *VecIter : *SetIter; }
|
||||
};
|
||||
|
||||
/// SmallSet - This maintains a set of unique values, optimizing for the case
|
||||
@@ -139,10 +136,6 @@ class SmallSet {
|
||||
SmallVector<T, N> Vector;
|
||||
std::set<T, C> Set;
|
||||
|
||||
using VIterator = typename SmallVector<T, N>::const_iterator;
|
||||
using SIterator = typename std::set<T, C>::const_iterator;
|
||||
using mutable_iterator = typename SmallVector<T, N>::iterator;
|
||||
|
||||
// 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
|
||||
// DenseSet<> instead if you expect many elements in the set.
|
||||
@@ -155,6 +148,22 @@ public:
|
||||
using const_iterator = SmallSetIterator<T, N, C>;
|
||||
|
||||
SmallSet() = default;
|
||||
SmallSet(const SmallSet &) = default;
|
||||
SmallSet(SmallSet &&) = default;
|
||||
|
||||
template <typename IterT> SmallSet(IterT Begin, IterT End) {
|
||||
insert(Begin, End);
|
||||
}
|
||||
|
||||
template <typename RangeT>
|
||||
explicit SmallSet(const iterator_range<RangeT> &R) {
|
||||
insert(R.begin(), R.end());
|
||||
}
|
||||
|
||||
SmallSet(std::initializer_list<T> L) { insert(L.begin(), L.end()); }
|
||||
|
||||
SmallSet &operator=(const SmallSet &) = default;
|
||||
SmallSet &operator=(SmallSet &&) = default;
|
||||
|
||||
[[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
|
||||
|
||||
@@ -163,39 +172,16 @@ public:
|
||||
}
|
||||
|
||||
/// count - Return 1 if the element is in the set, 0 otherwise.
|
||||
size_type count(const T &V) const {
|
||||
if (isSmall()) {
|
||||
// Since the collection is small, just do a linear search.
|
||||
return vfind(V) == Vector.end() ? 0 : 1;
|
||||
} else {
|
||||
return Set.count(V);
|
||||
}
|
||||
}
|
||||
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
|
||||
/// element or the existing element in the set. The second value is true
|
||||
/// if the element is inserted (it was not in the set before).
|
||||
std::pair<const_iterator, bool> insert(const T &V) {
|
||||
if (!isSmall()) {
|
||||
auto [I, Inserted] = Set.insert(V);
|
||||
return std::make_pair(const_iterator(I), Inserted);
|
||||
}
|
||||
std::pair<const_iterator, bool> insert(const T &V) { return insertImpl(V); }
|
||||
|
||||
VIterator I = vfind(V);
|
||||
if (I != Vector.end()) // Don't reinsert if it already exists.
|
||||
return std::make_pair(const_iterator(I), false);
|
||||
if (Vector.size() < N) {
|
||||
Vector.push_back(V);
|
||||
return std::make_pair(const_iterator(std::prev(Vector.end())), true);
|
||||
}
|
||||
|
||||
// Otherwise, grow from vector to set.
|
||||
while (!Vector.empty()) {
|
||||
Set.insert(Vector.back());
|
||||
Vector.pop_back();
|
||||
}
|
||||
return std::make_pair(const_iterator(Set.insert(V).first), true);
|
||||
std::pair<const_iterator, bool> insert(T &&V) {
|
||||
return insertImpl(std::move(V));
|
||||
}
|
||||
|
||||
template <typename IterT>
|
||||
@@ -207,11 +193,11 @@ public:
|
||||
bool erase(const T &V) {
|
||||
if (!isSmall())
|
||||
return Set.erase(V);
|
||||
for (mutable_iterator I = Vector.begin(), E = Vector.end(); I != E; ++I)
|
||||
if (*I == V) {
|
||||
Vector.erase(I);
|
||||
return true;
|
||||
}
|
||||
auto I = vfind(V);
|
||||
if (I != Vector.end()) {
|
||||
Vector.erase(I);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -242,8 +228,33 @@ public:
|
||||
private:
|
||||
bool isSmall() const { return Set.empty(); }
|
||||
|
||||
VIterator vfind(const T &V) const {
|
||||
for (VIterator I = Vector.begin(), E = Vector.end(); I != E; ++I)
|
||||
template <typename ArgType>
|
||||
std::pair<const_iterator, bool> insertImpl(ArgType &&V) {
|
||||
static_assert(std::is_convertible_v<ArgType, T>,
|
||||
"ArgType must be convertible to T!");
|
||||
if (!isSmall()) {
|
||||
auto [I, Inserted] = Set.insert(std::forward<ArgType>(V));
|
||||
return {const_iterator(I), Inserted};
|
||||
}
|
||||
|
||||
auto I = vfind(V);
|
||||
if (I != Vector.end()) // Don't reinsert if it already exists.
|
||||
return {const_iterator(I), false};
|
||||
if (Vector.size() < N) {
|
||||
Vector.push_back(std::forward<ArgType>(V));
|
||||
return {const_iterator(std::prev(Vector.end())), true};
|
||||
}
|
||||
// Otherwise, grow from vector to set.
|
||||
Set.insert(std::make_move_iterator(Vector.begin()),
|
||||
std::make_move_iterator(Vector.end()));
|
||||
Vector.clear();
|
||||
return {const_iterator(Set.insert(std::forward<ArgType>(V)).first), true};
|
||||
}
|
||||
|
||||
// Handwritten linear search. The use of std::find might hurt performance as
|
||||
// its implementation may be optimized for larger containers.
|
||||
typename SmallVector<T, N>::const_iterator vfind(const T &V) const {
|
||||
for (auto I = Vector.begin(), E = Vector.end(); I != E; ++I)
|
||||
if (*I == V)
|
||||
return I;
|
||||
return Vector.end();
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#endif
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
@@ -82,19 +81,6 @@ protected:
|
||||
/// This function will report a fatal error if it cannot increase capacity.
|
||||
void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
|
||||
|
||||
/// If vector was first created with capacity 0, getFirstEl() points to the
|
||||
/// memory right after, an area unallocated. If a subsequent allocation,
|
||||
/// that grows the vector, happens to return the same pointer as getFirstEl(),
|
||||
/// get a new allocation, otherwise isSmall() will falsely return that no
|
||||
/// allocation was done (true) and the memory will not be freed in the
|
||||
/// destructor. If a VSize is given (vector size), also copy that many
|
||||
/// elements to the new allocation - used if realloca fails to increase
|
||||
/// space, and happens to allocate precisely at BeginX.
|
||||
/// This is unlikely to be called often, but resolves a memory leak when the
|
||||
/// situation does occur.
|
||||
void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
|
||||
size_t VSize = 0);
|
||||
|
||||
public:
|
||||
size_t size() const { return Size; }
|
||||
size_t capacity() const { return Capacity; }
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
#define WPIUTIL_WPI_VERSIONTUPLE_H
|
||||
|
||||
#include "wpi/DenseMapInfo.h"
|
||||
#include "wpi/Hashing.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace wpi {
|
||||
template <typename HasherT, wpi::endianness Endianness> class HashBuilder;
|
||||
class raw_ostream;
|
||||
|
||||
/// Represents a version number in the form major[.minor[.subminor[.build]]].
|
||||
|
||||
@@ -43,7 +43,8 @@ class iterator_range {
|
||||
IteratorT begin_iterator, end_iterator;
|
||||
|
||||
public:
|
||||
#if __GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4)
|
||||
#if defined(__GNUC__) && \
|
||||
(__GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4))
|
||||
// Be careful no to break gcc-7 and gcc-8 < 8.4 on the mlir target.
|
||||
// See https://github.com/llvm/llvm-project/issues/63843
|
||||
template <typename Container>
|
||||
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
/// So that the stream could keep at least tell() + ExtraSize bytes
|
||||
/// without re-allocations. reserveExtraSpace() does not change
|
||||
/// the size/data of the stream.
|
||||
virtual void reserveExtraSpace(uint64_t ExtraSize) {}
|
||||
virtual void reserveExtraSpace(uint64_t ExtraSize) { (void)ExtraSize; }
|
||||
|
||||
/// Set the stream to be buffered, with an automatically determined buffer
|
||||
/// size.
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
Header File
|
||||
Copyright (C) 2012-2016, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- xxHash source repository : https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
/* based on revision d2df04efcbef7d7f6886d345861e5dfda4edacc1 Removed
|
||||
* everything but a simple interface for computing XXh64. */
|
||||
|
||||
#ifndef WPIUTIL_WPI_XXHASH_H
|
||||
#define WPIUTIL_WPI_XXHASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
uint64_t xxHash64(std::string_view Data);
|
||||
uint64_t xxHash64(std::span<const uint8_t> Data);
|
||||
|
||||
uint64_t xxh3_64bits(std::span<const uint8_t> data);
|
||||
inline uint64_t xxh3_64bits(std::string_view data) {
|
||||
return xxh3_64bits(std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
|
||||
}
|
||||
|
||||
/*-**********************************************************************
|
||||
* XXH3 128-bit variant
|
||||
************************************************************************/
|
||||
|
||||
/*!
|
||||
* @brief The return value from 128-bit hashes.
|
||||
*
|
||||
* Stored in little endian order, although the fields themselves are in native
|
||||
* endianness.
|
||||
*/
|
||||
struct XXH128_hash_t {
|
||||
uint64_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */
|
||||
uint64_t high64; /*!< `value >> 64` */
|
||||
|
||||
/// Convenience equality check operator.
|
||||
bool operator==(const XXH128_hash_t rhs) const {
|
||||
return low64 == rhs.low64 && high64 == rhs.high64;
|
||||
}
|
||||
};
|
||||
|
||||
/// XXH3's 128-bit variant.
|
||||
XXH128_hash_t xxh3_128bits(std::span<const uint8_t> data);
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user