[wpiutil] Replace LLVM StringMap impl with std::map

As string_view operations on std::map<std::string> won't be integrated
until C++26, placeholder implementations are used which are less efficient
in a couple of situations (e.g. insert with hint).
This commit is contained in:
Peter Johnson
2024-10-23 21:33:12 -07:00
parent 5f3cf517d3
commit f620141e0d
34 changed files with 944 additions and 2031 deletions

View File

@@ -130,7 +130,7 @@ void AprilTagDetector::RemoveFamily(std::string_view fam) {
apriltag_detector_remove_family(
static_cast<apriltag_detector_t*>(m_impl),
static_cast<apriltag_family_t*>(it->second));
DestroyFamily(it->getKey(), it->second);
DestroyFamily(it->first, it->second);
m_families.erase(it);
}
}
@@ -158,7 +158,7 @@ void AprilTagDetector::Destroy() {
void AprilTagDetector::DestroyFamilies() {
for (auto&& entry : m_families) {
DestroyFamily(entry.getKey(), entry.second);
DestroyFamily(entry.first, entry.second);
}
}

View File

@@ -53,7 +53,7 @@ static void WorkspaceInit() {
}
for (auto&& root : gContext->storageRoots) {
root.getValue()->Apply();
root.second->Apply();
}
}
@@ -176,7 +176,7 @@ static bool LoadStorageImpl(Context* ctx, std::string_view dir,
bool rv = true;
for (auto&& root : ctx->storageRoots) {
std::string filename;
auto rootName = root.getKey();
auto& rootName = root.first;
if (rootName.empty()) {
filename = (fs::path{dir} / fmt::format("{}.json", name)).string();
} else {
@@ -291,7 +291,7 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
if (exiting && wpi::gui::gContext->resetOnExit) {
fs::remove(dirPath / fmt::format("{}-window.json", name), ec);
for (auto&& root : ctx->storageRoots) {
auto rootName = root.getKey();
auto& rootName = root.first;
if (rootName.empty()) {
fs::remove(dirPath / fmt::format("{}.json", name), ec);
} else {
@@ -304,14 +304,14 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
(dirPath / fmt::format("{}-window.json", name)).string());
for (auto&& root : ctx->storageRoots) {
auto rootName = root.getKey();
auto& rootName = root.first;
std::string filename;
if (rootName.empty()) {
filename = (dirPath / fmt::format("{}.json", name)).string();
} else {
filename = (dirPath / fmt::format("{}-{}.json", name, rootName)).string();
}
if (!SaveStorageRootImpl(ctx, filename, *root.getValue())) {
if (!SaveStorageRootImpl(ctx, filename, *root.second)) {
rv = false;
}
}
@@ -320,8 +320,7 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
Context::Context()
: sourceNameStorage{storageRoots.insert({"", std::make_unique<Storage>()})
.first->getValue()
->GetChild("sourceNames")} {
.first->second->GetChild("sourceNames")} {
storageStack.emplace_back(storageRoots[""].get());
// override ImGui ini saving

View File

@@ -112,5 +112,5 @@ DataSource* DataSource::Find(std::string_view id) {
if (it == gContext->sources.end()) {
return nullptr;
}
return it->getValue();
return it->second;
}

View File

@@ -331,7 +331,7 @@ std::vector<std::unique_ptr<Storage>>& Storage::GetChildArray(
std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
auto it = m_values.find(key);
if (it != m_values.end()) {
auto rv = std::move(it->getValue());
auto rv = std::move(it->second);
m_values.erase(it);
return rv;
}
@@ -339,11 +339,9 @@ std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
}
void Storage::EraseChildren() {
for (auto&& kv : m_values) {
if (kv.getValue()->type == Value::kChild) {
m_values.remove(&kv);
}
}
std::erase_if(m_values, [](const auto& kv) {
return kv.second->type == Value::kChild;
});
}
static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr,
@@ -559,7 +557,7 @@ wpi::json Storage::ToJson() const {
wpi::json j = wpi::json::object();
for (auto&& kv : m_values) {
wpi::json jelem;
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
#define CASE(CapsName, LowerName) \
case Value::k##CapsName: \
@@ -602,7 +600,7 @@ wpi::json Storage::ToJson() const {
default:
continue;
}
j.emplace(kv.getKey(), std::move(jelem));
j.emplace(kv.first, std::move(jelem));
}
return j;
}
@@ -617,7 +615,7 @@ void Storage::Clear() {
void Storage::ClearValues() {
for (auto&& kv : m_values) {
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
case Value::kInt:
value.intVal = value.intDefault;
@@ -703,7 +701,7 @@ void Storage::Apply() {
void Storage::ApplyChildren() {
for (auto&& kv : m_values) {
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
case Value::kChild:
value.child->Apply();

View File

@@ -249,8 +249,7 @@ class ChildIterator {
public:
ChildIterator(IteratorType it, IteratorType end) noexcept
: anchor(it), end(end) {
while (anchor != end &&
anchor->getValue()->type != Storage::Value::kChild) {
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
++anchor;
}
}
@@ -261,8 +260,7 @@ class ChildIterator {
/// increment operator (needed for range-based for)
ChildIterator& operator++() {
++anchor;
while (anchor != end &&
anchor->getValue()->type != Storage::Value::kChild) {
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
++anchor;
}
return *this;
@@ -274,10 +272,10 @@ class ChildIterator {
}
/// return key of the iterator
std::string_view key() const { return anchor->getKey(); }
std::string_view key() const { return anchor->first; }
/// return value of the iterator
Storage& value() const { return *anchor->getValue()->child; }
Storage& value() const { return *anchor->second->child; }
};
} // namespace detail

View File

@@ -317,7 +317,7 @@ HAL_SimDeviceHandle SimDeviceData::GetDeviceHandle(const char* name) {
if (it == m_deviceMap.end()) {
return 0;
}
if (auto deviceImpl = it->getValue().lock()) {
if (auto deviceImpl = it->second.lock()) {
return deviceImpl->handle;
} else {
return 0;
@@ -459,10 +459,10 @@ HAL_SimValueHandle SimDeviceData::GetValueHandle(HAL_SimDeviceHandle device,
if (it == deviceImpl->valueMap.end()) {
return 0;
}
if (!it->getValue()) {
if (!it->second) {
return 0;
}
return it->getValue()->handle;
return it->second->handle;
}
void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,

View File

@@ -112,8 +112,8 @@ void HALSimWS::Start() {
// Print any filters we are using
if (m_useMsgFiltering) {
wpi::print("WS Message Filters:");
for (auto filter : m_msgFilters.keys()) {
wpi::print("* \"{}\"\n", filter);
for (auto&& filter : m_msgFilters) {
wpi::print("* \"{}\"\n", filter.first);
}
} else {
wpi::print("No WS Message Filters specified");

View File

@@ -54,7 +54,7 @@ void HALSimWSProviderSimDevice::CancelCallbacks() {
m_simValueCreatedCbKey = 0;
for (auto& kv : m_simValueChangedCbKeys) {
HALSIM_CancelSimValueChangedCallback(kv.getValue());
HALSIM_CancelSimValueChangedCallback(kv.second);
}
m_simValueChangedCbKeys.clear();

View File

@@ -40,7 +40,7 @@ class ProviderContainer {
void ForEach(IterFn fn) {
std::shared_lock lock(m_mutex);
for (auto& kv : m_providers) {
fn(kv.getValue());
fn(kv.second);
}
}

View File

@@ -123,8 +123,8 @@ void HALSimWeb::Start() {
// Print any filters we are using
if (m_useMsgFiltering) {
wpi::print("WS Message Filters:");
for (auto filter : m_msgFilters.keys()) {
wpi::print("* \"{}\"\n", filter);
for (auto&& filter : m_msgFilters) {
wpi::print("* \"{}\"\n", filter.first);
}
} else {
wpi::print("No WS Message Filters specified");

View File

@@ -90,8 +90,8 @@ static void CopyRawData(wpi::StringMap<MotorData>* dataset) {
auto& data = *dataset;
// Loads the Raw Data
for (auto& it : data) {
auto key = it.first();
auto& motorData = it.getValue();
auto& key = it.first;
auto& motorData = it.second;
if (!wpi::contains(key, "raw")) {
data[fmt::format("raw-{}", key)] = motorData;
@@ -126,7 +126,7 @@ void AnalysisManager::PrepareGeneralData() {
WPI_INFO(m_logger, "{}", "Converting raw data to PreparedData struct.");
// Convert data to PreparedData structs
for (auto& it : m_data.motorData) {
auto key = it.first();
auto key = it.first;
preparedData[key] = ConvertToPrepared(m_data.motorData[key]);
WPI_INFO(m_logger, "SAMPLES {}", preparedData[key].size());
}

View File

@@ -322,8 +322,8 @@ static units::second_t GetMaxStepTime(
wpi::StringMap<std::vector<PreparedData>>& data) {
auto maxStepTime = 0_s;
for (auto& it : data) {
auto key = it.first();
auto& dataset = it.getValue();
auto& key = it.first;
auto& dataset = it.second;
if (IsRaw(key) && wpi::contains(key, "dynamic")) {
if (!dataset.empty()) {
@@ -351,8 +351,8 @@ void sysid::InitialTrimAndFilter(
// Calculate Velocity Threshold if it hasn't been set yet
if (settings->velocityThreshold == std::numeric_limits<double>::infinity()) {
for (auto& it : preparedData) {
auto key = it.first();
auto& dataset = it.getValue();
auto& key = it.first;
auto& dataset = it.second;
if (wpi::contains(key, "quasistatic")) {
settings->velocityThreshold =
std::min(settings->velocityThreshold,
@@ -363,8 +363,8 @@ void sysid::InitialTrimAndFilter(
}
for (auto& it : preparedData) {
auto key = it.first();
auto& dataset = it.getValue();
auto& key = it.first;
auto& dataset = it.second;
// Trim quasistatic test data to remove all points where voltage is zero or
// velocity < velocity threshold.
@@ -424,7 +424,7 @@ void sysid::AccelFilter(wpi::StringMap<std::vector<PreparedData>>* data) {
// Remove points with acceleration = 0
for (auto& it : preparedData) {
auto& dataset = it.getValue();
auto& dataset = it.second;
for (size_t i = 0; i < dataset.size(); i++) {
if (dataset.at(i).acceleration == 0.0) {
@@ -436,7 +436,7 @@ void sysid::AccelFilter(wpi::StringMap<std::vector<PreparedData>>* data) {
// Confirm there's still data
if (std::any_of(preparedData.begin(), preparedData.end(),
[](const auto& it) { return it.getValue().empty(); })) {
[](const auto& it) { return it.second.empty(); })) {
throw sysid::InvalidDataError(
"Acceleration filtering has removed all data.");
}

View File

@@ -56,7 +56,7 @@ void DataSelector::Display() {
TestData data = m_testdataFuture.get();
for (auto&& motordata : data.motorData) {
m_testdataStats.emplace_back(
fmt::format("Test State: {}", motordata.first()));
fmt::format("Test State: {}", motordata.first));
int i = 0;
for (auto&& run : motordata.second.runs) {
m_testdataStats.emplace_back(fmt::format(

View File

@@ -16,6 +16,7 @@
#include <imgui.h>
#include <imgui_stdlib.h>
#include <portable-file-dialogs.h>
#include <wpi/SmallVector.h>
#include <wpi/SpanExtras.h>
#include <wpi/StringExtras.h>
#include <wpi/fs.h>

View File

@@ -8,8 +8,6 @@ Subject: [PATCH 01/38] Remove StringRef, ArrayRef, and Optional
llvm/include/llvm/ADT/SmallSet.h | 2 +-
llvm/include/llvm/ADT/SmallString.h | 103 ++++++++++--------
llvm/include/llvm/ADT/SmallVector.h | 7 +-
llvm/include/llvm/ADT/StringMap.h | 38 +++----
llvm/include/llvm/ADT/StringMapEntry.h | 20 ++--
llvm/include/llvm/Support/Chrono.h | 10 +-
llvm/include/llvm/Support/Compiler.h | 2 +-
llvm/include/llvm/Support/ConvertUTF.h | 31 +++---
@@ -22,7 +20,6 @@ Subject: [PATCH 01/38] Remove StringRef, ArrayRef, and Optional
llvm/lib/Support/ConvertUTFWrapper.cpp | 38 +++----
llvm/lib/Support/ErrorHandling.cpp | 13 +--
llvm/lib/Support/SmallVector.cpp | 5 +-
llvm/lib/Support/StringMap.cpp | 12 +-
llvm/lib/Support/raw_ostream.cpp | 25 ++---
llvm/lib/Support/xxhash.cpp | 10 +-
llvm/unittests/ADT/DenseMapTest.cpp | 29 +----
@@ -31,7 +28,6 @@ Subject: [PATCH 01/38] Remove StringRef, ArrayRef, and Optional
llvm/unittests/ADT/SmallPtrSetTest.cpp | 1 -
llvm/unittests/ADT/SmallStringTest.cpp | 50 ++++-----
llvm/unittests/ADT/SmallVectorTest.cpp | 30 +----
llvm/unittests/ADT/StringMapTest.cpp | 32 +++---
llvm/unittests/Support/ConvertUTFTest.cpp | 41 ++++---
llvm/unittests/Support/xxhashTest.cpp | 4 +-
30 files changed, 282 insertions(+), 323 deletions(-)
@@ -372,230 +368,6 @@ index 09676d792dfebd88e5c8eace666b3ab0044a962e..c96fd0e4956ee6d586f85dc79623de13
this->append(A.begin(), A.end());
}
diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
index 466f95254d102e98343290b211f317f749d7692b..34dfbf83c681f4e81a9dadd9382ddca6ef8d6c1d 100644
--- a/llvm/include/llvm/ADT/StringMap.h
+++ b/llvm/include/llvm/ADT/StringMap.h
@@ -60,12 +60,12 @@ protected:
/// specified bucket will be non-null. Otherwise, it will be null. In either
/// case, the FullHashValue field of the bucket will be set to the hash value
/// of the string.
- unsigned LookupBucketFor(StringRef Key);
+ unsigned LookupBucketFor(std::string_view Key);
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
- int FindKey(StringRef Key) const;
+ int FindKey(std::string_view Key) const;
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
/// delete it. This aborts if the value isn't in the table.
@@ -73,7 +73,7 @@ protected:
/// RemoveKey - Remove the StringMapEntry for the specified key from the
/// table, returning it. If the key is not in the table, this returns null.
- StringMapEntryBase *RemoveKey(StringRef Key);
+ StringMapEntryBase *RemoveKey(std::string_view Key);
/// Allocate the table with the specified number of buckets and otherwise
/// setup the map as empty.
@@ -127,7 +127,7 @@ public:
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
AllocTy(A) {}
- StringMap(std::initializer_list<std::pair<StringRef, ValueTy>> List)
+ StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
: StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
insert(List);
}
@@ -215,14 +215,14 @@ public:
StringMapKeyIterator<ValueTy>(end()));
}
- iterator find(StringRef Key) {
+ iterator find(std::string_view Key) {
int Bucket = FindKey(Key);
if (Bucket == -1)
return end();
return iterator(TheTable + Bucket, true);
}
- const_iterator find(StringRef Key) const {
+ const_iterator find(std::string_view Key) const {
int Bucket = FindKey(Key);
if (Bucket == -1)
return end();
@@ -231,7 +231,7 @@ public:
/// lookup - Return the entry for the specified key, or a default
/// constructed value if no such entry exists.
- ValueTy lookup(StringRef Key) const {
+ ValueTy lookup(std::string_view Key) const {
const_iterator Iter = find(Key);
if (Iter != end())
return Iter->second;
@@ -240,7 +240,7 @@ public:
/// at - Return the entry for the specified key, or abort if no such
/// entry exists.
- const ValueTy &at(StringRef Val) const {
+ const ValueTy &at(std::string_view Val) const {
auto Iter = this->find(std::move(Val));
assert(Iter != this->end() && "StringMap::at failed due to a missing key");
return Iter->second;
@@ -248,13 +248,13 @@ public:
/// Lookup the ValueTy for the \p Key, or create a default constructed value
/// if the key is not in the map.
- ValueTy &operator[](StringRef Key) { return try_emplace(Key).first->second; }
+ ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; }
/// contains - Return true if the element is in the map, false otherwise.
- bool contains(StringRef Key) const { return find(Key) != end(); }
+ bool contains(std::string_view Key) const { return find(Key) != end(); }
/// count - Return 1 if the element is in the map, 0 otherwise.
- size_type count(StringRef Key) const { return contains(Key) ? 1 : 0; }
+ size_type count(std::string_view Key) const { return contains(Key) ? 1 : 0; }
template <typename InputTy>
size_type count(const StringMapEntry<InputTy> &MapEntry) const {
@@ -304,7 +304,7 @@ public:
/// isn't already in the map. The bool component of the returned pair is true
/// if and only if the insertion takes place, and the iterator component of
/// the pair points to the element with key equivalent to the key of the pair.
- std::pair<iterator, bool> insert(std::pair<StringRef, ValueTy> KV) {
+ std::pair<iterator, bool> insert(std::pair<std::string_view, ValueTy> KV) {
return try_emplace(KV.first, std::move(KV.second));
}
@@ -319,14 +319,14 @@ public:
/// Inserts elements from initializer list ilist. If multiple elements in
/// the range have keys that compare equivalent, it is unspecified which
/// element is inserted
- void insert(std::initializer_list<std::pair<StringRef, ValueTy>> List) {
+ void insert(std::initializer_list<std::pair<std::string_view, ValueTy>> List) {
insert(List.begin(), List.end());
}
/// Inserts an element or assigns to the current element if the key already
/// exists. The return type is the same as try_emplace.
template <typename V>
- std::pair<iterator, bool> insert_or_assign(StringRef Key, V &&Val) {
+ std::pair<iterator, bool> insert_or_assign(std::string_view Key, V &&Val) {
auto Ret = try_emplace(Key, std::forward<V>(Val));
if (!Ret.second)
Ret.first->second = std::forward<V>(Val);
@@ -338,7 +338,7 @@ public:
/// if and only if the insertion takes place, and the iterator component of
/// the pair points to the element with key equivalent to the key of the pair.
template <typename... ArgsTy>
- std::pair<iterator, bool> try_emplace(StringRef Key, ArgsTy &&...Args) {
+ std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&...Args) {
unsigned BucketNo = LookupBucketFor(Key);
StringMapEntryBase *&Bucket = TheTable[BucketNo];
if (Bucket && Bucket != getTombstoneVal())
@@ -385,7 +385,7 @@ public:
V.Destroy(getAllocator());
}
- bool erase(StringRef Key) {
+ bool erase(std::string_view Key) {
iterator I = find(Key);
if (I == end())
return false;
@@ -482,17 +482,17 @@ template <typename ValueTy>
class StringMapKeyIterator
: public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
StringMapConstIterator<ValueTy>,
- std::forward_iterator_tag, StringRef> {
+ std::forward_iterator_tag, std::string_view> {
using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
StringMapConstIterator<ValueTy>,
- std::forward_iterator_tag, StringRef>;
+ std::forward_iterator_tag, std::string_view>;
public:
StringMapKeyIterator() = default;
explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
: base(std::move(Iter)) {}
- StringRef operator*() const { return this->wrapped()->getKey(); }
+ std::string_view operator*() const { return this->wrapped()->getKey(); }
};
} // end namespace llvm
diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
index 98b51cc1aebd59eba20076e6d8a4eebc0eebb982..388e81c361642113937f7d5680de73a50635b07d 100644
--- a/llvm/include/llvm/ADT/StringMapEntry.h
+++ b/llvm/include/llvm/ADT/StringMapEntry.h
@@ -16,8 +16,8 @@
#ifndef LLVM_ADT_STRINGMAPENTRY_H
#define LLVM_ADT_STRINGMAPENTRY_H
-#include "llvm/ADT/StringRef.h"
#include <optional>
+#include <string_view>
namespace llvm {
@@ -36,13 +36,13 @@ protected:
/// type-erase the allocator and put it in a source file.
template <typename AllocatorTy>
static void *allocateWithKey(size_t EntrySize, size_t EntryAlign,
- StringRef Key, AllocatorTy &Allocator);
+ std::string_view Key, AllocatorTy &Allocator);
};
// Define out-of-line to dissuade inlining.
template <typename AllocatorTy>
void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign,
- StringRef Key,
+ std::string_view Key,
AllocatorTy &Allocator) {
size_t KeyLength = Key.size();
@@ -105,8 +105,8 @@ public:
using ValueType = ValueTy;
- StringRef getKey() const {
- return StringRef(getKeyData(), this->getKeyLength());
+ std::string_view getKey() const {
+ return std::string_view(getKeyData(), this->getKeyLength());
}
/// getKeyData - Return the start of the string data that is the key for this
@@ -116,15 +116,15 @@ public:
return reinterpret_cast<const char *>(this + 1);
}
- StringRef first() const {
- return StringRef(getKeyData(), this->getKeyLength());
+ std::string_view first() const {
+ return std::string_view(getKeyData(), this->getKeyLength());
}
/// Create a StringMapEntry for the specified key construct the value using
/// \p InitiVals.
template <typename AllocatorTy, typename... InitTy>
- static StringMapEntry *create(StringRef key, AllocatorTy &allocator,
- InitTy &&...initVals) {
+ static StringMapEntry *create(std::string_view key, AllocatorTy &allocator,
+ InitTy &&... initVals) {
return new (StringMapEntryBase::allocateWithKey(
sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
StringMapEntry(key.size(), std::forward<InitTy>(initVals)...);
@@ -167,7 +167,7 @@ struct tuple_size<llvm::StringMapEntry<ValueTy>>
template <std::size_t I, typename ValueTy>
struct tuple_element<I, llvm::StringMapEntry<ValueTy>>
- : std::conditional<I == 0, llvm::StringRef, ValueTy> {};
+ : std::conditional<I == 0, std::string_view, ValueTy> {};
} // namespace std
#endif // LLVM_ADT_STRINGMAPENTRY_H
diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h
index 71859af7c7e4a595140475daf356744f52d14d24..9c9ba7002310eba5113c14957f769702c61f4326 100644
--- a/llvm/include/llvm/Support/Chrono.h
@@ -1228,63 +1000,6 @@ index b6ce37842040b36fc79770ca0296255f2bb42a1a..4f6fee18b659adcbfd79822832f91417
#endif
}
diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp
index 67c05a87959cf0c243d17646ae2f28f6c9f0d708..7be219323f6d76f32a9a841115f2f146141cdbab 100644
--- a/llvm/lib/Support/StringMap.cpp
+++ b/llvm/lib/Support/StringMap.cpp
@@ -81,7 +81,7 @@ void StringMapImpl::init(unsigned InitSize) {
/// specified bucket will be non-null. Otherwise, it will be null. In either
/// case, the FullHashValue field of the bucket will be set to the hash value
/// of the string.
-unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
+unsigned StringMapImpl::LookupBucketFor(std::string_view Name) {
// Hash table unallocated so far?
if (NumBuckets == 0)
init(16);
@@ -121,7 +121,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
// Do the comparison like this because Name isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
- if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) {
+ if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
@@ -139,7 +139,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
-int StringMapImpl::FindKey(StringRef Key) const {
+int StringMapImpl::FindKey(std::string_view Key) const {
if (NumBuckets == 0)
return -1; // Really empty table?
unsigned FullHashValue = xxh3_64bits(Key);
@@ -166,7 +166,7 @@ int StringMapImpl::FindKey(StringRef Key) const {
// Do the comparison like this because NameStart isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
- if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) {
+ if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
@@ -185,14 +185,14 @@ int StringMapImpl::FindKey(StringRef Key) const {
/// delete it. This aborts if the value isn't in the table.
void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
const char *VStr = (char *)V + ItemSize;
- StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength()));
+ StringMapEntryBase *V2 = RemoveKey(std::string_view(VStr, V->getKeyLength()));
(void)V2;
assert(V == V2 && "Didn't find key?");
}
/// RemoveKey - Remove the StringMapEntry for the specified key from the
/// table, returning it. If the key is not in the table, this returns null.
-StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
+StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) {
int Bucket = FindKey(Key);
if (Bucket == -1)
return nullptr;
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 3d3a564af51d120786b358b96a3c90e2b7fbf9f3..220d850b9bc69f8fc2fba7cd23629eca487cde23 100644
--- a/llvm/lib/Support/raw_ostream.cpp
@@ -1746,132 +1461,6 @@ index 137dd43b473068eae34b39edc4b9b8b9633bab95..7029038d18d433cef987bedbfa4fda26
{
llvm::SmallVector<To> Vector(Array);
diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
index f9b138e9a472137139397d9cae76823711594211..7f10b3d7d3a8894b1ab0ac660268d94a8b89e082 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Twine.h"
#include "llvm/Support/DataTypes.h"
#include "gtest/gtest.h"
#include <limits>
@@ -43,10 +41,10 @@ protected:
// Lookup tests
EXPECT_FALSE(testMap.contains(testKey));
EXPECT_EQ(0u, testMap.count(testKey));
- EXPECT_EQ(0u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
+ EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
EXPECT_EQ(0u, testMap.count(testKeyStr));
EXPECT_TRUE(testMap.find(testKey) == testMap.end());
- EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==
+ EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
testMap.end());
EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());
}
@@ -67,10 +65,10 @@ protected:
// Lookup tests
EXPECT_TRUE(testMap.contains(testKey));
EXPECT_EQ(1u, testMap.count(testKey));
- EXPECT_EQ(1u, testMap.count(StringRef(testKeyFirst, testKeyLength)));
+ EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
EXPECT_EQ(1u, testMap.count(testKeyStr));
EXPECT_TRUE(testMap.find(testKey) == testMap.begin());
- EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==
+ EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
testMap.begin());
EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());
}
@@ -108,10 +106,10 @@ TEST_F(StringMapTest, ConstEmptyMapTest) {
// Lookup tests
EXPECT_EQ(0u, constTestMap.count(testKey));
- EXPECT_EQ(0u, constTestMap.count(StringRef(testKeyFirst, testKeyLength)));
+ EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
EXPECT_EQ(0u, constTestMap.count(testKeyStr));
EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());
- EXPECT_TRUE(constTestMap.find(StringRef(testKeyFirst, testKeyLength)) ==
+ EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
constTestMap.end());
EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());
}
@@ -251,7 +249,7 @@ TEST_F(StringMapTest, StringMapEntryTest) {
MallocAllocator Allocator;
StringMap<uint32_t>::value_type *entry =
StringMap<uint32_t>::value_type::create(
- StringRef(testKeyFirst, testKeyLength), Allocator, 1u);
+ std::string_view(testKeyFirst, testKeyLength), Allocator, 1u);
EXPECT_STREQ(testKey, entry->first().data());
EXPECT_EQ(1u, entry->second);
entry->Destroy(Allocator);
@@ -261,7 +259,7 @@ TEST_F(StringMapTest, StringMapEntryTest) {
TEST_F(StringMapTest, InsertTest) {
SCOPED_TRACE("InsertTest");
testMap.insert(StringMap<uint32_t>::value_type::create(
- StringRef(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
+ std::string_view(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
assertSingleItemMap();
}
@@ -330,10 +328,10 @@ TEST_F(StringMapTest, IterMapKeysVector) {
Map["C"] = 3;
Map["D"] = 3;
- std::vector<StringRef> Keys{Map.keys().begin(), Map.keys().end()};
+ std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
llvm::sort(Keys);
- std::vector<StringRef> Expected{{"A", "B", "C", "D"}};
+ std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
EXPECT_EQ(Expected, Keys);
}
@@ -347,7 +345,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
auto Keys = to_vector<4>(Map.keys());
llvm::sort(Keys);
- SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};
+ SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
EXPECT_EQ(Expected, Keys);
}
@@ -389,13 +387,13 @@ private:
TEST_F(StringMapTest, MoveOnly) {
StringMap<MoveOnly> t;
t.insert(std::make_pair("Test", MoveOnly(42)));
- StringRef Key = "Test";
+ std::string_view Key = "Test";
StringMapEntry<MoveOnly>::create(Key, t.getAllocator(), MoveOnly(42))
->Destroy(t.getAllocator());
}
TEST_F(StringMapTest, CtorArg) {
- StringRef Key = "Test";
+ std::string_view Key = "Test";
MallocAllocator Allocator;
StringMapEntry<MoveOnly>::create(Key, Allocator, Immovable())
->Destroy(Allocator);
@@ -580,7 +578,7 @@ TEST(StringMapCustomTest, InitialSizeTest) {
CountCtorCopyAndMove::Copy = 0;
for (int i = 0; i < Size; ++i)
Map.insert(std::pair<std::string, CountCtorCopyAndMove>(
- std::piecewise_construct, std::forward_as_tuple(Twine(i).str()),
+ std::piecewise_construct, std::forward_as_tuple(std::to_string(i)),
std::forward_as_tuple(i)));
// After the initial move, the map will move the Elts in the Entry.
EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);
@@ -649,7 +647,7 @@ TEST(StringMapCustomTest, StringMapEntrySize) {
else
LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
StringMapEntry<int> LargeEntry(LargeValue);
- StringRef Key = LargeEntry.getKey();
+ std::string_view Key = LargeEntry.getKey();
EXPECT_EQ(LargeValue, Key.size());
// Test that the entry can hold at least max size_t.
diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
index 6e75fbae0969ba1bf0a76c4d79a123e405a8dae7..3b07d344f15a555f11ad5f8177a0a65b8a4fa472 100644
--- a/llvm/unittests/Support/ConvertUTFTest.cpp

View File

@@ -8,7 +8,6 @@ Subject: [PATCH 06/38] Explicitly use std::
llvm/lib/Support/ErrorHandling.cpp | 2 +-
llvm/unittests/ADT/SmallPtrSetTest.cpp | 2 +-
llvm/unittests/ADT/SmallSetTest.cpp | 10 +++++-----
llvm/unittests/ADT/StringMapTest.cpp | 2 +-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
@@ -100,16 +99,3 @@ index b50b368ae663614f050c220432c05b32c201db00..f9d84fa8a42a7feaaffa3aa080e84574
EXPECT_EQ(4u, s1.size());
EXPECT_EQ("str 0", V[0]);
EXPECT_EQ("str 1", V[1]);
diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
index 7f10b3d7d3a8894b1ab0ac660268d94a8b89e082..acd8b566f9c7a6efc2c9204624c01104dd34daf6 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -343,7 +343,7 @@ TEST_F(StringMapTest, IterMapKeysSmallVector) {
Map["D"] = 3;
auto Keys = to_vector<4>(Map.keys());
- llvm::sort(Keys);
+ std::sort(Keys.begin(), Keys.end());
SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
EXPECT_EQ(Expected, Keys);

View File

@@ -4,143 +4,9 @@ Date: Tue, 3 May 2022 22:16:10 -0400
Subject: [PATCH 12/38] Extra collections features
---
llvm/include/llvm/ADT/StringMap.h | 103 +++++++++++++++++++++++++++++-
llvm/lib/Support/raw_ostream.cpp | 8 +++
2 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
index 34dfbf83c681f4e81a9dadd9382ddca6ef8d6c1d..c133e84f9b2e3a225cdac782c011fadbf07adab2 100644
--- a/llvm/include/llvm/ADT/StringMap.h
+++ b/llvm/include/llvm/ADT/StringMap.h
@@ -42,7 +42,7 @@ protected:
protected:
explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {}
- StringMapImpl(StringMapImpl &&RHS)
+ StringMapImpl(StringMapImpl &&RHS) noexcept
: TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets),
NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones),
ItemSize(RHS.ItemSize) {
@@ -432,11 +432,27 @@ public:
return Tmp;
}
+ DerivedTy &operator--() { // Predecrement
+ --Ptr;
+ ReversePastEmptyBuckets();
+ return static_cast<DerivedTy &>(*this);
+ }
+
+ DerivedTy operator--(int) { // Post-decrement
+ DerivedTy Tmp(Ptr);
+ --*this;
+ return Tmp;
+ }
+
private:
void AdvancePastEmptyBuckets() {
while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
++Ptr;
}
+ void ReversePastEmptyBuckets() {
+ while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
+ --Ptr;
+ }
};
template <typename ValueTy>
@@ -495,6 +511,91 @@ public:
std::string_view operator*() const { return this->wrapped()->getKey(); }
};
+template <typename ValueTy>
+bool operator==(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
+ // same instance?
+ if (&lhs == &rhs) return true;
+
+ // first check that sizes are identical
+ if (lhs.size() != rhs.size()) return false;
+
+ // copy into vectors and sort by key
+ SmallVector<StringMapConstIterator<ValueTy>, 16> lhs_items;
+ lhs_items.reserve(lhs.size());
+ for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
+ lhs_items.push_back(i);
+ std::sort(lhs_items.begin(), lhs_items.end(),
+ [](const StringMapConstIterator<ValueTy>& a,
+ const StringMapConstIterator<ValueTy>& b) {
+ return a->getKey() < b->getKey();
+ });
+
+ SmallVector<StringMapConstIterator<ValueTy>, 16> rhs_items;
+ rhs_items.reserve(rhs.size());
+ for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
+ rhs_items.push_back(i);
+ std::sort(rhs_items.begin(), rhs_items.end(),
+ [](const StringMapConstIterator<ValueTy>& a,
+ const StringMapConstIterator<ValueTy>& b) {
+ return a->getKey() < b->getKey();
+ });
+
+ // compare vector keys and values
+ for (auto a = lhs_items.begin(), b = rhs_items.begin(),
+ aend = lhs_items.end(), bend = rhs_items.end();
+ a != aend && b != bend; ++a, ++b) {
+ if ((*a)->first() != (*b)->first() || (*a)->second != (*b)->second)
+ return false;
+ }
+ return true;
+}
+
+template <typename ValueTy>
+inline bool operator!=(const StringMap<ValueTy>& lhs,
+ const StringMap<ValueTy>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename ValueTy>
+bool operator<(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
+ // same instance?
+ if (&lhs == &rhs) return false;
+
+ // copy into vectors and sort by key
+ SmallVector<std::string_view, 16> lhs_keys;
+ lhs_keys.reserve(lhs.size());
+ for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
+ lhs_keys.push_back(i->getKey());
+ std::sort(lhs_keys.begin(), lhs_keys.end());
+
+ SmallVector<std::string_view, 16> rhs_keys;
+ rhs_keys.reserve(rhs.size());
+ for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
+ rhs_keys.push_back(i->getKey());
+ std::sort(rhs_keys.begin(), rhs_keys.end());
+
+ // use std::vector comparison
+ return lhs_keys < rhs_keys;
+}
+
+template <typename ValueTy>
+inline bool operator<=(const StringMap<ValueTy>& lhs,
+ const StringMap<ValueTy>& rhs) {
+ return !(rhs < lhs);
+}
+
+template <typename ValueTy>
+inline bool operator>(const StringMap<ValueTy>& lhs,
+ const StringMap<ValueTy>& rhs) {
+ return !(lhs <= rhs);
+}
+
+template <typename ValueTy>
+inline bool operator>=(const StringMap<ValueTy>& lhs,
+ const StringMap<ValueTy>& rhs) {
+ return !(lhs < rhs);
+}
+
} // end namespace llvm
#endif // LLVM_ADT_STRINGMAP_H
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 57d3091fd5cc5b416e814f9f33811f0fa7afe9cf..5bec803ccc76ce287b7ff3ea037d5e490a7af20c 100644
--- a/llvm/lib/Support/raw_ostream.cpp

View File

@@ -4,48 +4,15 @@ Date: Sun, 8 May 2022 16:38:11 -0400
Subject: [PATCH 16/38] Fixup includes
---
llvm/include/llvm/ADT/StringMap.h | 4 ++++
llvm/include/llvm/ADT/StringMapEntry.h | 4 ++++
llvm/include/llvm/Support/PointerLikeTypeTraits.h | 1 +
llvm/lib/Support/ConvertUTFWrapper.cpp | 1 +
llvm/lib/Support/ErrorHandling.cpp | 7 +++----
llvm/lib/Support/raw_ostream.cpp | 11 ++++++-----
llvm/unittests/ADT/SmallPtrSetTest.cpp | 2 ++
llvm/unittests/ADT/SmallVectorTest.cpp | 1 +
llvm/unittests/ADT/StringMapTest.cpp | 1 +
llvm/unittests/Support/ConvertUTFTest.cpp | 2 ++
10 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h
index c133e84f9b2e3a225cdac782c011fadbf07adab2..2173a4159111e7fea70325de82dbfce628ae3ea8 100644
--- a/llvm/include/llvm/ADT/StringMap.h
+++ b/llvm/include/llvm/ADT/StringMap.h
@@ -17,6 +17,10 @@
#include "llvm/ADT/StringMapEntry.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Support/AllocatorBase.h"
+#include "llvm/Support/MemAlloc.h"
+#include "llvm/Support/SmallVector.h"
+#include "llvm/Support/iterator.h"
+#include "llvm/Support/iterator_range.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
#include <initializer_list>
#include <iterator>
diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
index 388e81c361642113937f7d5680de73a50635b07d..011806f5fd89ff738ed805a82b3ddbc6fc9b08ce 100644
--- a/llvm/include/llvm/ADT/StringMapEntry.h
+++ b/llvm/include/llvm/ADT/StringMapEntry.h
@@ -16,6 +16,10 @@
#ifndef LLVM_ADT_STRINGMAPENTRY_H
#define LLVM_ADT_STRINGMAPENTRY_H
+#include "wpi/MemAlloc.h"
+
+#include <cassert>
+#include <cstring>
#include <optional>
#include <string_view>
diff --git a/llvm/include/llvm/Support/PointerLikeTypeTraits.h b/llvm/include/llvm/Support/PointerLikeTypeTraits.h
index 1b15f930bd87d97d51824af5e62ea5f222a6b4c9..acadd5e89a1651cfbad67a5b1b0933d1f288d094 100644
--- a/llvm/include/llvm/Support/PointerLikeTypeTraits.h
@@ -153,18 +120,6 @@ index f8c37820ef9fdfe0af067f5aa8d2297ed15e73bc..5e91f71bc9ac0e499a64dd1591e581d0
#include <list>
#include <span>
#include <stdarg.h>
diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
index acd8b566f9c7a6efc2c9204624c01104dd34daf6..6b6cf564909f329c220eb225f3b7af6c35301029 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -9,6 +9,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/DataTypes.h"
#include "gtest/gtest.h"
+#include <algorithm>
#include <limits>
#include <tuple>
using namespace llvm;
diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp
index 3b07d344f15a555f11ad5f8177a0a65b8a4fa472..77e70a46d3621ecfaed923d87256184addfda721 100644
--- a/llvm/unittests/Support/ConvertUTFTest.cpp

View File

@@ -1,34 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Wed, 10 Aug 2022 22:35:00 -0700
Subject: [PATCH 27/38] Remove StringMap test for llvm::sort()
---
llvm/unittests/ADT/StringMapTest.cpp | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
index 6b6cf564909f329c220eb225f3b7af6c35301029..0d83669a580408e925ec6308410ebe7c01b48b12 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -322,20 +322,6 @@ TEST_F(StringMapTest, InsertOrAssignTest) {
EXPECT_EQ(0, try1.first->second.copy);
}
-TEST_F(StringMapTest, IterMapKeysVector) {
- StringMap<int> Map;
- Map["A"] = 1;
- Map["B"] = 2;
- Map["C"] = 3;
- Map["D"] = 3;
-
- std::vector<std::string_view> Keys{Map.keys().begin(), Map.keys().end()};
- llvm::sort(Keys);
-
- std::vector<std::string_view> Expected{{"A", "B", "C", "D"}};
- EXPECT_EQ(Expected, Keys);
-}
-
TEST_F(StringMapTest, IterMapKeysSmallVector) {
StringMap<int> Map;
Map["A"] = 1;

View File

@@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ryan Blue <ryanzblue@gmail.com>
Date: Tue, 24 Sep 2024 23:36:16 -0400
Subject: [PATCH 38/38] StringMap: fix structured bindings with move-only types
---
llvm/include/llvm/ADT/StringMapEntry.h | 12 +++++++++++-
llvm/unittests/ADT/StringMapTest.cpp | 10 ++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h
index 011806f5fd89ff738ed805a82b3ddbc6fc9b08ce..5b8eeb167c53c7ab15d05b0c71b75cd7f5b496c5 100644
--- a/llvm/include/llvm/ADT/StringMapEntry.h
+++ b/llvm/include/llvm/ADT/StringMapEntry.h
@@ -159,7 +159,17 @@ decltype(auto) get(const StringMapEntry<ValueTy> &E) {
if constexpr (Index == 0)
return E.first();
else
- return E.second;
+ return (E.second);
+}
+
+// Allow structured bindings on StringMapEntry.
+template <std::size_t Index, typename ValueTy>
+decltype(auto) get(StringMapEntry<ValueTy> &E) {
+ static_assert(Index < 2);
+ if constexpr (Index == 0)
+ return E.first();
+ else
+ return (E.second);
}
} // end namespace llvm
diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp
index 0d83669a580408e925ec6308410ebe7c01b48b12..f1834bec65afec6be4365e52c4e21aa61cbb1d12 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -526,6 +526,16 @@ TEST_F(StringMapTest, StructuredBindings) {
}
}
+TEST_F(StringMapTest, StructuredBindingsMoveOnly) {
+ StringMap<MoveOnly> A;
+ A.insert(std::make_pair("a", MoveOnly(42)));
+
+ for (auto &&[Key, Value] : A) {
+ EXPECT_EQ("a", Key);
+ EXPECT_EQ(42, Value.i);
+ }
+}
+
namespace {
// Simple class that counts how many moves and copy happens when growing a map
struct CountCtorCopyAndMove {

View File

@@ -49,8 +49,8 @@ void Tracer::PrintEpochs(wpi::raw_ostream& os) {
m_lastEpochsPrintTime = now;
for (const auto& epoch : m_epochs) {
os << fmt::format(
"\t{}: {:.6f}s\n", epoch.getKey(),
duration_cast<microseconds>(epoch.getValue()).count() / 1.0e6);
"\t{}: {:.6f}s\n", epoch.first,
duration_cast<microseconds>(epoch.second).count() / 1.0e6);
}
}
}

View File

@@ -46,7 +46,7 @@ void ShuffleboardComponentBase::BuildMetadata(
if (GetProperties().size() > 0) {
auto propTable = metaTable->GetSubTable("Properties");
for (auto& entry : GetProperties()) {
propTable->GetEntry(entry.first()).SetValue(entry.second);
propTable->GetEntry(entry.first).SetValue(entry.second);
}
}
m_metadataDirty = false;

View File

@@ -51,7 +51,7 @@ void Mechanism2d::InitSendable(nt::NTSendableBuilder& builder) {
m_colorPub = m_table->GetStringTopic(kBackgroundColor).Publish();
m_colorPub.Set(m_color);
for (const auto& entry : m_roots) {
const auto& root = entry.getValue().get();
root->Update(m_table->GetSubTable(entry.getKey()));
const auto& root = entry.second.get();
root->Update(m_table->GetSubTable(entry.first));
}
}

View File

@@ -18,8 +18,7 @@ void MechanismObject2d::Update(std::shared_ptr<nt::NetworkTable> table) {
std::scoped_lock lock(m_mutex);
m_table = table;
UpdateEntries(m_table);
for (const wpi::StringMapEntry<std::unique_ptr<MechanismObject2d>>& entry :
m_objects) {
entry.getValue()->Update(m_table->GetSubTable(entry.getKey()));
for (const auto& entry : m_objects) {
entry.second->Update(m_table->GetSubTable(entry.first));
}
}

View File

@@ -126,7 +126,7 @@ wpi::Sendable* SmartDashboard::GetData(std::string_view key) {
if (it == inst.tablesToData.end()) {
throw FRC_MakeError(err::SmartDashboardMissingKey, "{}", key);
}
return wpi::SendableRegistry::GetSendable(it->getValue());
return wpi::SendableRegistry::GetSendable(it->second);
}
bool SmartDashboard::PutBoolean(std::string_view keyName, bool value) {
@@ -254,6 +254,6 @@ void SmartDashboard::UpdateValues() {
inst.listenerExecutor.RunListenerTasks();
std::scoped_lock lock(inst.tablesToDataMutex);
for (auto& i : inst.tablesToData) {
wpi::SendableRegistry::Update(i.getValue());
wpi::SendableRegistry::Update(i.second);
}
}

View File

@@ -49,7 +49,7 @@ class SendableChooser : public SendableChooserBase {
}
public:
using CopyType = decltype(_unwrap_smart_ptr(m_choices.lookup("")));
using CopyType = decltype(_unwrap_smart_ptr(m_choices.find("")->second));
SendableChooser() = default;
~SendableChooser() override = default;
@@ -105,7 +105,11 @@ class SendableChooser : public SendableChooserBase {
if (selected.empty()) {
return CopyType{};
} else {
return _unwrap_smart_ptr(m_choices.lookup(selected));
auto it = m_choices.find(selected);
if (it == m_choices.end()) {
return CopyType{};
}
return _unwrap_smart_ptr(it->second);
}
}
@@ -128,13 +132,8 @@ class SendableChooser : public SendableChooserBase {
[=, this] {
std::vector<std::string> keys;
for (const auto& choice : m_choices) {
keys.emplace_back(choice.first());
keys.emplace_back(choice.first);
}
// Unlike std::map, wpi::StringMap elements
// are not sorted
std::sort(keys.begin(), keys.end());
return keys;
},
nullptr);

View File

@@ -60,7 +60,7 @@ std::string_view MimeTypeFromPath(std::string_view path) {
if (dot_pos > 0 && dot_pos != std::string_view::npos) {
auto type = mimeTypes.find(wpi::substr(path, dot_pos + 1));
if (type != mimeTypes.end()) {
return type->getValue();
return type->second;
}
}
return defaultType;

View File

@@ -375,8 +375,8 @@ class HttpRequest {
return elem.first;
}
template <typename T>
static std::string_view GetFirst(const StringMapEntry<T>& elem) {
return elem.getKey();
static std::string_view GetFirst(const std::pair<std::string, T>& elem) {
return elem.first;
}
template <typename T>
static std::string_view GetSecond(const T& elem) {

View File

@@ -83,7 +83,7 @@ void DataLog::StartFile() {
// Existing start and schema data records
for (auto&& entryInfo : m_entries) {
AppendStartRecord(entryInfo.second.id, entryInfo.first(),
AppendStartRecord(entryInfo.second.id, entryInfo.first,
entryInfo.second.type,
m_entryIds[entryInfo.second.id].metadata, 0);
if (!entryInfo.second.schemaData.empty()) {

View File

@@ -0,0 +1,777 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
namespace wpi {
/**
* StringMap is a sorted associative container that contains key-value pairs
* with unique string keys. Keys are sorted in the same order as std::string's
* are compared. Search, removal, and insertion operations have logarithmic
* complexity. The underlying implementation is std::map<std::string, T>.
*/
template <typename T,
typename Allocator = std::allocator<std::pair<const std::string, T>>>
class StringMap : public std::map<std::string, T, std::less<>, Allocator> {
public:
using map_type = typename std::map<std::string, T, std::less<>>;
using key_type = typename map_type::key_type;
using mapped_type = typename map_type::mapped_type;
using value_type = typename map_type::value_type;
using size_type = typename map_type::size_type;
using difference_type = typename map_type::difference_type;
using key_compare = typename map_type::key_compare;
using allocator_type = typename map_type::allocator_type;
using reference = typename map_type::reference;
using const_reference = typename map_type::const_reference;
using pointer = typename map_type::pointer;
using const_pointer = typename map_type::const_pointer;
using iterator = typename map_type::iterator;
using const_iterator = typename map_type::const_iterator;
using reverse_iterator = typename map_type::reverse_iterator;
using const_reverse_iterator = typename map_type::const_reverse_iterator;
using node_type = typename map_type::node_type;
using insert_return_type = typename map_type::insert_return_type;
/** Constructs an empty container. */
StringMap() = default;
/**
* Constructs an empty container.
*
* @param alloc allocator to use for all memory allocations of this container
*/
explicit StringMap(const Allocator& alloc) : map_type{alloc} {}
/**
* Constructs the container with the contents of the range [first, last). If
* multiple elements in the range have keys that compare equivalent, it is
* unspecified which element is inserted. If [first, last) is not a valid
* range, the behavior is undefined.
*
* @param first start of the range to copy the elements from
* @param last end of the range to copy the elements from
* @param alloc allocator to use for all memory allocations of this container
*/
template <typename InputIt>
StringMap(InputIt first, InputIt last, const Allocator& alloc = Allocator())
: map_type{first, last, alloc} {}
/** Copy constructor. */
StringMap(const StringMap&) = default;
/**
* Copy constructor.
*
* @param other another container to be used as source to initialize the
* elements of the container with
* @param alloc allocator to use for all memory allocations of this container
*/
StringMap(const StringMap& other, const Allocator& alloc)
: map_type{other, alloc} {}
/** Move constructor. */
StringMap(StringMap&&) = default;
/**
* Move constructor.
*
* @param other another container to be used as source to initialize the
* elements of the container with
* @param alloc allocator to use for all memory allocations of this container
*/
StringMap(StringMap&& other, const Allocator& alloc)
: map_type{other, alloc} {}
/**
* Initializer-list constructor. Construct the container with the contents of
* the initializer list init. If multiple elements in the range have keys
* that compare equal, it is unspecified which element is inserted.
*
* @param init initializer list to initialize the elements of the container
* with
* @param alloc allocator to use for all memory allocations of this container
*/
StringMap(std::initializer_list<value_type> init,
const Allocator& alloc = Allocator())
: map_type{init, alloc} {}
/** Copy assignment operator. */
StringMap& operator=(const StringMap&) = default;
/** Move assignment operator. */
StringMap& operator=(StringMap&&) noexcept(
std::allocator_traits<Allocator>::is_always_equal::value &&
std::is_nothrow_move_assignable<std::less<>>::value) = default;
/**
* Replaces the contents with those identified by initializer list ilist.
*
* @param ilist initializer list to use as data source
*/
StringMap& operator=(std::initializer_list<value_type> ilist) {
map_type::operator=(ilist);
return *this;
}
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
T& at(const std::string& key) { return map_type::at(key); }
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
const T& at(const std::string& key) const { return map_type::at(key); }
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
T& at(const char* key) { return at(std::string_view{key}); }
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
const T& at(const char* key) const { return at(std::string_view{key}); }
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
T& at(std::string_view key) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::at(key);
#else
auto it = find(key);
if (it == this->end()) {
throw std::out_of_range{std::string{key}};
}
return it->second;
#endif
}
/**
* Returns a reference to the mapped value of the element with the specified
* key. If no such element exists, an exception of type std::out_of_range is
* thrown.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the requested element.
*/
const T& at(std::string_view key) const {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::at(key);
#else
auto it = find(key);
if (it == this->end()) {
throw std::out_of_range{std::string{key}};
}
return it->second;
#endif
}
/**
* Returns a reference to the value that is mapped to a key, performing an
* insertion if such a key does not exist.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the new element if no element
* with key key existed. Otherwise, a reference to the mapped value
* of the existing element whose key is equal to key.
*/
T& operator[](const std::string& key) { return map_type::operator[](key); }
/**
* Returns a reference to the value that is mapped to a key, performing an
* insertion if such a key does not exist.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the new element if no element
* with key key existed. Otherwise, a reference to the mapped value
* of the existing element whose key is equal to key.
*/
T& operator[](std::string&& key) {
return map_type::operator[](std::move(key));
}
/**
* Returns a reference to the value that is mapped to a key, performing an
* insertion if such a key does not exist.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the new element if no element
* with key key existed. Otherwise, a reference to the mapped value
* of the existing element whose key is equal to key.
*/
T& operator[](const char* key) { return operator[](std::string_view{key}); }
/**
* Returns a reference to the value that is mapped to a key, performing an
* insertion if such a key does not exist.
*
* @param key the key of the element to find
* @return A reference to the mapped value of the new element if no element
* with key key existed. Otherwise, a reference to the mapped value
* of the existing element whose key is equal to key.
*/
T& operator[](std::string_view key) { return try_emplace(key).first->second; }
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return The bool component is true if the insertion took place and false if
* the assignment took place. The iterator component is pointing at
* the element that was inserted or updated.
*/
template <typename M>
std::pair<iterator, bool> insert_or_assign(std::string&& key, M&& obj) {
return map_type::insert_or_assign(std::move(key), std::forward<M>(obj));
}
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return The bool component is true if the insertion took place and false if
* the assignment took place. The iterator component is pointing at
* the element that was inserted or updated.
*/
template <typename M>
std::pair<iterator, bool> insert_or_assign(const char* key, M&& obj) {
return insert_or_assign(std::string_view{key}, std::forward<M>(obj));
}
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return The bool component is true if the insertion took place and false if
* the assignment took place. The iterator component is pointing at
* the element that was inserted or updated.
*/
template <typename M>
std::pair<iterator, bool> insert_or_assign(std::string_view key, M&& obj) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::insert_or_assign(key, std::forward<M>(obj));
#else
auto it = lower_bound(key);
if (it != this->end() && it->first == key) {
it->second = std::forward<M>(obj);
return {it, false};
} else {
return {this->insert(it, {std::string{key}, std::forward<M>(obj)}), true};
}
#endif
}
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return Iterator pointing at the element that was inserted or updated.
*/
template <typename M>
iterator insert_or_assign(const_iterator hint, std::string&& key, M&& obj) {
return map_type::insert_or_assign(hint, std::move(key),
std::forward<M>(obj));
}
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return Iterator pointing at the element that was inserted or updated.
*/
template <typename M>
iterator insert_or_assign(const_iterator hint, const char* key, M&& obj) {
return insert_or_assign(hint, std::string_view{key}, std::forward<M>(obj));
}
/**
* If a key equal to key already exists in the container, assigns obj to the
* mapped type corresponding to that key. If the key does not exist, inserts
* the value as if by calling insert().
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param obj the value to insert or assign
* @return Iterator pointing at the element that was inserted or updated.
*/
template <typename M>
iterator insert_or_assign(const_iterator hint, std::string_view key,
M&& obj) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::insert_or_assign(hint, key, std::forward<M>(obj));
#else
return map_type::insert_or_assign(hint, std::string{key},
std::forward<M>(obj));
#endif
}
/**
* Inserts a new element into the container constructed in-place with the
* given args, if there is no element with the key in the container.
*
* No iterators or references are invalidated.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> emplace(std::string&& key, Args&&... args) {
return map_type::emplace(std::move(key), std::forward<Args>(args)...);
}
/**
* Inserts a new element into the container constructed in-place with the
* given args, if there is no element with the key in the container.
*
* No iterators or references are invalidated.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> emplace(const char* key, Args&&... args) {
return emplace(std::string_view{key}, std::forward<Args>(args)...);
}
/**
* Inserts a new element into the container constructed in-place with the
* given args, if there is no element with the key in the container.
*
* No iterators or references are invalidated.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> emplace(std::string_view key, Args&&... args) {
return try_emplace(key, std::forward<Args>(args)...);
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> try_emplace(std::string&& key, Args&&... args) {
return map_type::try_emplace(std::move(key), std::forward<Args>(args)...);
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> try_emplace(const char* key, Args&&... args) {
return try_emplace(std::string_view{key}, std::forward<Args>(args)...);
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return A pair consisting of an interator to the inserted element (or to
* the element that prevented the insertion) and a bool value set to
* true if and only if the insertion took place.
*/
template <typename... Args>
std::pair<iterator, bool> try_emplace(std::string_view key, Args&&... args) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::try_emplace(key, std::forward<Args>(args)...);
#else
auto it = lower_bound(key);
if (it != this->end() && it->first == key) {
return {it, false};
} else {
return {try_emplace(it, key, std::forward<Args>(args)...), true};
}
#endif
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return An interator to the inserted element, or to the element that
* prevented the insertion.
*/
template <typename... Args>
iterator try_emplace(const_iterator hint, std::string&& key, Args&&... args) {
return map_type::try_emplace(hint, std::move(key),
std::forward<Args>(args)...);
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return An interator to the inserted element, or to the element that
* prevented the insertion.
*/
template <typename... Args>
iterator try_emplace(const_iterator hint, const char* key, Args&&... args) {
return try_emplace(hint, std::string_view{key},
std::forward<Args>(args)...);
}
/**
* If a key equal to key already exists in the container, does nothing.
* Otherwise, inserts a new element into the container with key key and value
* constructed with args.
*
* @param hint iterator to the position before which the new element will be
* inserted
* @param key the key used both to look up and to insert if not found
* @param args arguments to forward to the constructor of the element
* @return An interator to the inserted element, or to the element that
* prevented the insertion.
*/
template <typename... Args>
iterator try_emplace(const_iterator hint, std::string_view key,
Args&&... args) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::try_emplace(hint, key, std::forward<Args>(args)...);
#else
return map_type::try_emplace(hint, std::string{key},
std::forward<Args>(args)...);
#endif
}
/**
* Removes the element at pos.
*
* @param pos iterator to the element to remove
* @return Iterator following the removed element.
*/
iterator erase(iterator pos) { return map_type::erase(pos); }
/**
* Removes the element at pos.
*
* @param pos iterator to the element to remove
* @return Iterator following the removed element.
*/
iterator erase(const_iterator pos) { return map_type::erase(pos); }
/**
* Removes the elements in the range [first, last), which must be a valid
* range in this.
*
* @param first Start of the range of elements to remove
* @param last End of the range of elements to remove
* @return Iterator following the last removed element.
*/
iterator erase(iterator first, iterator last) {
return map_type::erase(first, last);
}
/**
* Removes the elements in the range [first, last), which must be a valid
* range in this.
*
* @param first Start of the range of elements to remove
* @param last End of the range of elements to remove
* @return Iterator following the last removed element.
*/
iterator erase(const_iterator first, const_iterator last) {
return map_type::erase(first, last);
}
/**
* Removes the element (if one exists) with the key equal to key.
*
* @param key key value of the elements to remove
* @return Number of elements removed (0 or 1).
*/
size_type erase(std::string_view key) {
#ifdef __cpp_lib_associative_heterogeneous_erasure
return map_type::erase(key);
#else
auto it = find(key);
if (it == this->end()) {
return 0;
}
this->erase(it);
return 1;
#endif
}
/**
* Exchanges the contents of the container with those of other. Does not
* invoke any move, copy, or swap operations on individual elements.
*
* All iterators and references remain valid. The end() iterator is
* invalidated.
*
* @param other container to exchange the contents with
*/
void swap(StringMap& other) noexcept(
std::allocator_traits<Allocator>::is_always_equal::value &&
std::is_nothrow_swappable<std::less<>>::value) {
map_type::swap(other);
}
/**
* Unlinks the node that contains the element pointed to by position and
* returns a node handle that owns it.
*
* @param position a valid iterator into this container
* @return A node handle that owns the extracted element
*/
node_type extract(const_iterator position) {
return map_type::extract(position);
}
/**
* If the container has an element with key equal to key, unlinks the node
* that contains that element from the container and returns a node handle
* that owns it. Otherwise, returns an empty node handle.
*
* @param key a key to identify the node to be extracted
* @return A node handle that owns the extracted element, or empty node handle
* in case the element is not found.
*/
node_type extract(std::string_view key) {
#ifdef __cpp_lib_associative_heterogeneous_insertion
return map_type::extract(key);
#else
auto it = find(key);
if (it == this->end()) {
return {};
}
return extract(it);
#endif
}
/**
* Returns the number of elements with key that equals the specified argument.
*
* @param key key value of the elements to count
* @return Number of elements with key that equals key (either 0 or 1).
*/
size_type count(std::string_view key) const { return map_type::count(key); }
/**
* Finds an element with key equal to key.
*
* @param key key value of the element to search for
* @return An iterator to the requested element. If no such element is found,
* past-the-end (see end()) iterator is returned.
*/
iterator find(std::string_view key) { return map_type::find(key); }
/**
* Finds an element with key equal to key.
*
* @param key key value of the element to search for
* @return An iterator to the requested element. If no such element is found,
* past-the-end (see end()) iterator is returned.
*/
const_iterator find(std::string_view key) const {
return map_type::find(key);
}
/**
* Checks if there is an element with key equal to key in the container.
*
* @param key key value of the element to search for
* @return true if there is such an element, otherwise false
*/
bool contains(std::string_view key) const { return map_type::contains(key); }
/**
* Returns a range containing all elements with the given key in the
* container. The range is defined by two iterators, one pointing to the
* first element that is not less than key and another pointing to the first
* element greater than key. Alternatively, the first iterator may be
* obtained with lower_bound(), and the second with upper_bound().
*
* @param key key value to compare the elements to
* @return std::pair containing a pair of iterators defining the wanted range:
* the first pointing to the first element that is not less than key
* and the second pointing to the first element greater than key. If
* there are no elements not less than key, past-the-end (see end())
* iterator is returned as the first element. Similarly if there are
* no elements greater than key, past-the-end iterator is returned as
* the second element.
*/
std::pair<iterator, iterator> equal_range(std::string_view key) {
return map_type::equal_range(key);
}
/**
* Returns a range containing all elements with the given key in the
* container. The range is defined by two iterators, one pointing to the
* first element that is not less than key and another pointing to the first
* element greater than key. Alternatively, the first iterator may be
* obtained with lower_bound(), and the second with upper_bound().
*
* @param key key value to compare the elements to
* @return std::pair containing a pair of iterators defining the wanted range:
* the first pointing to the first element that is not less than key
* and the second pointing to the first element greater than key. If
* there are no elements not less than key, past-the-end (see end())
* iterator is returned as the first element. Similarly if there are
* no elements greater than key, past-the-end iterator is returned as
* the second element.
*/
std::pair<const_iterator, const_iterator> equal_range(
std::string_view key) const {
return map_type::equal_range(key);
}
/**
* Returns an iterator pointing to the first element that is not less than
* (i.e. greater or equal to) key.
*
* @param key key value to compare the elements to
* @return Iterator pointing to the first element that is not less than key.
* If no such element is found, a past-the-end iterator (see end()) is
* returned.
*/
iterator lower_bound(std::string_view key) {
return map_type::lower_bound(key);
}
/**
* Returns an iterator pointing to the first element that is not less than
* (i.e. greater or equal to) key.
*
* @param key key value to compare the elements to
* @return Iterator pointing to the first element that is not less than key.
* If no such element is found, a past-the-end iterator (see end()) is
* returned.
*/
const_iterator lower_bound(std::string_view key) const {
return map_type::lower_bound(key);
}
/**
* Returns an interator pointing to the first element that is greater than
* key.
*
* @param key key value to compare the elements to
* @return Iterator pointing to the first element that is greater than key.
* If no such element is found, past-the-end (see end()) iterator is
* returned.
*/
iterator upper_bound(std::string_view key) {
return map_type::upper_bound(key);
}
/**
* Returns an interator pointing to the first element that is greater than
* key.
*
* @param key key value to compare the elements to
* @return Iterator pointing to the first element that is greater than key.
* If no such element is found, past-the-end (see end()) iterator is
* returned.
*/
const_iterator upper_bound(std::string_view key) const {
return map_type::upper_bound(key);
}
};
} // namespace wpi
namespace std {
template <typename T>
inline void swap(wpi::StringMap<T>& lhs, wpi::StringMap<T>& rhs) {
lhs.swap(rhs);
}
} // namespace std

View File

@@ -1,259 +0,0 @@
//===--- StringMap.cpp - String Hash table map implementation -------------===//
//
// 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 StringMap class.
//
//===----------------------------------------------------------------------===//
#include "wpi/StringMap.h"
#include "wpi/MathExtras.h"
#include "wpi/ReverseIteration.h"
#include "wpi/xxhash.h"
using namespace wpi;
/// Returns the number of buckets to allocate to ensure that the DenseMap can
/// accommodate \p NumEntries without need to grow().
static inline unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
// Ensure that "NumEntries * 4 < NumBuckets * 3"
if (NumEntries == 0)
return 0;
// +1 is required because of the strict equality.
// For example if NumEntries is 48, we need to return 401.
return NextPowerOf2(NumEntries * 4 / 3 + 1);
}
static inline StringMapEntryBase **createTable(unsigned NewNumBuckets) {
auto **Table = static_cast<StringMapEntryBase **>(safe_calloc(
NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned)));
// Allocate one extra bucket, set it to look filled so the iterators stop at
// end.
Table[NewNumBuckets] = (StringMapEntryBase *)2;
return Table;
}
static inline unsigned *getHashTable(StringMapEntryBase **TheTable,
unsigned NumBuckets) {
return reinterpret_cast<unsigned *>(TheTable + NumBuckets + 1);
}
StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
ItemSize = itemSize;
// If a size is specified, initialize the table with that many buckets.
if (InitSize) {
// The table will grow when the number of entries reach 3/4 of the number of
// buckets. To guarantee that "InitSize" number of entries can be inserted
// in the table without growing, we allocate just what is needed here.
init(getMinBucketToReserveForEntries(InitSize));
return;
}
// Otherwise, initialize it with zero buckets to avoid the allocation.
TheTable = nullptr;
NumBuckets = 0;
NumItems = 0;
NumTombstones = 0;
}
void StringMapImpl::init(unsigned InitSize) {
assert((InitSize & (InitSize - 1)) == 0 &&
"Init Size must be a power of 2 or zero!");
unsigned NewNumBuckets = InitSize ? InitSize : 16;
NumItems = 0;
NumTombstones = 0;
TheTable = createTable(NewNumBuckets);
// Set the member only if TheTable was successfully allocated
NumBuckets = NewNumBuckets;
}
/// LookupBucketFor - Look up the bucket that the specified string should end
/// up in. If it already exists as a key in the map, the Item pointer for the
/// specified bucket will be non-null. Otherwise, it will be null. In either
/// case, the FullHashValue field of the bucket will be set to the hash value
/// of the string.
unsigned StringMapImpl::LookupBucketFor(std::string_view Name) {
// Hash table unallocated so far?
if (NumBuckets == 0)
init(16);
unsigned FullHashValue = xxh3_64bits(Name);
if (shouldReverseIterate())
FullHashValue = ~FullHashValue;
unsigned BucketNo = FullHashValue & (NumBuckets - 1);
unsigned *HashTable = getHashTable(TheTable, NumBuckets);
unsigned ProbeAmt = 1;
int FirstTombstone = -1;
while (true) {
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return it.
if (LLVM_LIKELY(!BucketItem)) {
// If we found a tombstone, we want to reuse the tombstone instead of an
// empty bucket. This reduces probing.
if (FirstTombstone != -1) {
HashTable[FirstTombstone] = FullHashValue;
return FirstTombstone;
}
HashTable[BucketNo] = FullHashValue;
return BucketNo;
}
if (BucketItem == getTombstoneVal()) {
// Skip over tombstones. However, remember the first one we see.
if (FirstTombstone == -1)
FirstTombstone = BucketNo;
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
// If the full hash value matches, check deeply for a match. The common
// case here is that we are only looking at the buckets (for item info
// being non-null and for the full hash value) not at the items. This
// is important for cache locality.
// Do the comparison like this because Name isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
}
// Okay, we didn't find the item. Probe to the next bucket.
BucketNo = (BucketNo + ProbeAmt) & (NumBuckets - 1);
// Use quadratic probing, it has fewer clumping artifacts than linear
// probing and has good cache behavior in the common case.
++ProbeAmt;
}
}
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
int StringMapImpl::FindKey(std::string_view Key) const {
if (NumBuckets == 0)
return -1; // Really empty table?
unsigned FullHashValue = xxh3_64bits(Key);
if (shouldReverseIterate())
FullHashValue = ~FullHashValue;
unsigned BucketNo = FullHashValue & (NumBuckets - 1);
unsigned *HashTable = getHashTable(TheTable, NumBuckets);
unsigned ProbeAmt = 1;
while (true) {
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return.
if (LLVM_LIKELY(!BucketItem))
return -1;
if (BucketItem == getTombstoneVal()) {
// Ignore tombstones.
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
// If the full hash value matches, check deeply for a match. The common
// case here is that we are only looking at the buckets (for item info
// being non-null and for the full hash value) not at the items. This
// is important for cache locality.
// Do the comparison like this because NameStart isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
}
// Okay, we didn't find the item. Probe to the next bucket.
BucketNo = (BucketNo + ProbeAmt) & (NumBuckets - 1);
// Use quadratic probing, it has fewer clumping artifacts than linear
// probing and has good cache behavior in the common case.
++ProbeAmt;
}
}
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
/// delete it. This aborts if the value isn't in the table.
void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
const char *VStr = (char *)V + ItemSize;
StringMapEntryBase *V2 = RemoveKey(std::string_view(VStr, V->getKeyLength()));
(void)V2;
assert(V == V2 && "Didn't find key?");
}
/// RemoveKey - Remove the StringMapEntry for the specified key from the
/// table, returning it. If the key is not in the table, this returns null.
StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) {
int Bucket = FindKey(Key);
if (Bucket == -1)
return nullptr;
StringMapEntryBase *Result = TheTable[Bucket];
TheTable[Bucket] = getTombstoneVal();
--NumItems;
++NumTombstones;
assert(NumItems + NumTombstones <= NumBuckets);
return Result;
}
/// RehashTable - Grow the table, redistributing values into the buckets with
/// the appropriate mod-of-hashtable-size.
unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
unsigned NewSize;
// If the hash table is now more than 3/4 full, or if fewer than 1/8 of
// the buckets are empty (meaning that many are filled with tombstones),
// grow/rehash the table.
if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) {
NewSize = NumBuckets * 2;
} else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <=
NumBuckets / 8)) {
NewSize = NumBuckets;
} else {
return BucketNo;
}
unsigned NewBucketNo = BucketNo;
auto **NewTableArray = createTable(NewSize);
unsigned *NewHashArray = getHashTable(NewTableArray, NewSize);
unsigned *HashTable = getHashTable(TheTable, NumBuckets);
// Rehash all the items into their new buckets. Luckily :) we already have
// the hash values available, so we don't have to rehash any strings.
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
// If the bucket is not available, probe for a spot.
unsigned FullHash = HashTable[I];
unsigned NewBucket = FullHash & (NewSize - 1);
if (NewTableArray[NewBucket]) {
unsigned ProbeSize = 1;
do {
NewBucket = (NewBucket + ProbeSize++) & (NewSize - 1);
} while (NewTableArray[NewBucket]);
}
// Finally found a slot. Fill it in.
NewTableArray[NewBucket] = Bucket;
NewHashArray[NewBucket] = FullHash;
if (I == BucketNo)
NewBucketNo = NewBucket;
}
}
free(TheTable);
TheTable = NewTableArray;
NumBuckets = NewSize;
NumTombstones = 0;
return NewBucketNo;
}

View File

@@ -1,605 +0,0 @@
//===- StringMap.h - String Hash table map interface ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the StringMap class.
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_STRINGMAP_H
#define WPIUTIL_WPI_STRINGMAP_H
#include "wpi/StringMapEntry.h"
#include "wpi/iterator.h"
#include "wpi/AllocatorBase.h"
#include "wpi/MemAlloc.h"
#include "wpi/SmallVector.h"
#include "wpi/iterator.h"
#include "wpi/iterator_range.h"
#include "wpi/PointerLikeTypeTraits.h"
#include <initializer_list>
#include <iterator>
namespace wpi {
template <typename ValueTy> class StringMapConstIterator;
template <typename ValueTy> class StringMapIterator;
template <typename ValueTy> class StringMapKeyIterator;
/// StringMapImpl - This is the base class of StringMap that is shared among
/// all of its instantiations.
class StringMapImpl {
protected:
// Array of NumBuckets pointers to entries, null pointers are holes.
// TheTable[NumBuckets] contains a sentinel value for easy iteration. Followed
// by an array of the actual hash values as unsigned integers.
StringMapEntryBase **TheTable = nullptr;
unsigned NumBuckets = 0;
unsigned NumItems = 0;
unsigned NumTombstones = 0;
unsigned ItemSize;
protected:
explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {}
StringMapImpl(StringMapImpl &&RHS) noexcept
: TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets),
NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones),
ItemSize(RHS.ItemSize) {
RHS.TheTable = nullptr;
RHS.NumBuckets = 0;
RHS.NumItems = 0;
RHS.NumTombstones = 0;
}
StringMapImpl(unsigned InitSize, unsigned ItemSize);
unsigned RehashTable(unsigned BucketNo = 0);
/// LookupBucketFor - Look up the bucket that the specified string should end
/// up in. If it already exists as a key in the map, the Item pointer for the
/// specified bucket will be non-null. Otherwise, it will be null. In either
/// case, the FullHashValue field of the bucket will be set to the hash value
/// of the string.
unsigned LookupBucketFor(std::string_view Key);
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
int FindKey(std::string_view Key) const;
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
/// delete it. This aborts if the value isn't in the table.
void RemoveKey(StringMapEntryBase *V);
/// RemoveKey - Remove the StringMapEntry for the specified key from the
/// table, returning it. If the key is not in the table, this returns null.
StringMapEntryBase *RemoveKey(std::string_view Key);
/// Allocate the table with the specified number of buckets and otherwise
/// setup the map as empty.
void init(unsigned Size);
public:
static constexpr uintptr_t TombstoneIntVal =
static_cast<uintptr_t>(-1)
<< PointerLikeTypeTraits<StringMapEntryBase *>::NumLowBitsAvailable;
static StringMapEntryBase *getTombstoneVal() {
return reinterpret_cast<StringMapEntryBase *>(TombstoneIntVal);
}
unsigned getNumBuckets() const { return NumBuckets; }
unsigned getNumItems() const { return NumItems; }
bool empty() const { return NumItems == 0; }
unsigned size() const { return NumItems; }
void swap(StringMapImpl &Other) {
std::swap(TheTable, Other.TheTable);
std::swap(NumBuckets, Other.NumBuckets);
std::swap(NumItems, Other.NumItems);
std::swap(NumTombstones, Other.NumTombstones);
}
};
/// StringMap - This is an unconventional map that is specialized for handling
/// keys that are "strings", which are basically ranges of bytes. This does some
/// funky memory allocation and hashing things to make it extremely efficient,
/// storing the string data *after* the value in the map.
template <typename ValueTy, typename AllocatorTy = MallocAllocator>
class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
: public StringMapImpl,
private detail::AllocatorHolder<AllocatorTy> {
using AllocTy = detail::AllocatorHolder<AllocatorTy>;
public:
using MapEntryTy = StringMapEntry<ValueTy>;
StringMap() : StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))) {}
explicit StringMap(unsigned InitialSize)
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))) {}
explicit StringMap(AllocatorTy A)
: StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))), AllocTy(A) {}
StringMap(unsigned InitialSize, AllocatorTy A)
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
AllocTy(A) {}
StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
: StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
insert(List);
}
StringMap(StringMap &&RHS)
: StringMapImpl(std::move(RHS)), AllocTy(std::move(RHS.getAllocator())) {}
StringMap(const StringMap &RHS)
: StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))),
AllocTy(RHS.getAllocator()) {
if (RHS.empty())
return;
// Allocate TheTable of the same size as RHS's TheTable, and set the
// sentinel appropriately (and NumBuckets).
init(RHS.NumBuckets);
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1),
*RHSHashTable = (unsigned *)(RHS.TheTable + NumBuckets + 1);
NumItems = RHS.NumItems;
NumTombstones = RHS.NumTombstones;
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = RHS.TheTable[I];
if (!Bucket || Bucket == getTombstoneVal()) {
TheTable[I] = Bucket;
continue;
}
TheTable[I] = MapEntryTy::create(
static_cast<MapEntryTy *>(Bucket)->getKey(), getAllocator(),
static_cast<MapEntryTy *>(Bucket)->getValue());
HashTable[I] = RHSHashTable[I];
}
// Note that here we've copied everything from the RHS into this object,
// tombstones included. We could, instead, have re-probed for each key to
// instantiate this new object without any tombstone buckets. The
// assumption here is that items are rarely deleted from most StringMaps,
// and so tombstones are rare, so the cost of re-probing for all inputs is
// not worthwhile.
}
StringMap &operator=(StringMap RHS) {
StringMapImpl::swap(RHS);
std::swap(getAllocator(), RHS.getAllocator());
return *this;
}
~StringMap() {
// Delete all the elements in the map, but don't reset the elements
// to default values. This is a copy of clear(), but avoids unnecessary
// work not required in the destructor.
if (!empty()) {
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
}
}
}
free(TheTable);
}
using AllocTy::getAllocator;
using key_type = const char *;
using mapped_type = ValueTy;
using value_type = StringMapEntry<ValueTy>;
using size_type = size_t;
using const_iterator = StringMapConstIterator<ValueTy>;
using iterator = StringMapIterator<ValueTy>;
iterator begin() { return iterator(TheTable, NumBuckets == 0); }
iterator end() { return iterator(TheTable + NumBuckets, true); }
const_iterator begin() const {
return const_iterator(TheTable, NumBuckets == 0);
}
const_iterator end() const {
return const_iterator(TheTable + NumBuckets, true);
}
iterator_range<StringMapKeyIterator<ValueTy>> keys() const {
return make_range(StringMapKeyIterator<ValueTy>(begin()),
StringMapKeyIterator<ValueTy>(end()));
}
iterator find(std::string_view Key) {
int Bucket = FindKey(Key);
if (Bucket == -1)
return end();
return iterator(TheTable + Bucket, true);
}
const_iterator find(std::string_view Key) const {
int Bucket = FindKey(Key);
if (Bucket == -1)
return end();
return const_iterator(TheTable + Bucket, true);
}
/// lookup - Return the entry for the specified key, or a default
/// constructed value if no such entry exists.
ValueTy lookup(std::string_view Key) const {
const_iterator Iter = find(Key);
if (Iter != end())
return Iter->second;
return ValueTy();
}
/// at - Return the entry for the specified key, or abort if no such
/// entry exists.
const ValueTy &at(std::string_view Val) const {
auto Iter = this->find(std::move(Val));
assert(Iter != this->end() && "StringMap::at failed due to a missing key");
return Iter->second;
}
/// Lookup the ValueTy for the \p Key, or create a default constructed value
/// if the key is not in the map.
ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; }
/// contains - Return true if the element is in the map, false otherwise.
bool contains(std::string_view Key) const { return find(Key) != end(); }
/// count - Return 1 if the element is in the map, 0 otherwise.
size_type count(std::string_view Key) const { return contains(Key) ? 1 : 0; }
template <typename InputTy>
size_type count(const StringMapEntry<InputTy> &MapEntry) const {
return count(MapEntry.getKey());
}
/// equal - check whether both of the containers are equal.
bool operator==(const StringMap &RHS) const {
if (size() != RHS.size())
return false;
for (const auto &KeyValue : *this) {
auto FindInRHS = RHS.find(KeyValue.getKey());
if (FindInRHS == RHS.end())
return false;
if (!(KeyValue.getValue() == FindInRHS->getValue()))
return false;
}
return true;
}
bool operator!=(const StringMap &RHS) const { return !(*this == RHS); }
/// insert - Insert the specified key/value pair into the map. If the key
/// already exists in the map, return false and ignore the request, otherwise
/// insert it and return true.
bool insert(MapEntryTy *KeyValue) {
unsigned BucketNo = LookupBucketFor(KeyValue->getKey());
StringMapEntryBase *&Bucket = TheTable[BucketNo];
if (Bucket && Bucket != getTombstoneVal())
return false; // Already exists in map.
if (Bucket == getTombstoneVal())
--NumTombstones;
Bucket = KeyValue;
++NumItems;
assert(NumItems + NumTombstones <= NumBuckets);
RehashTable();
return true;
}
/// insert - Inserts the specified key/value pair into the map if the key
/// isn't already in the map. The bool component of the returned pair is true
/// if and only if the insertion takes place, and the iterator component of
/// the pair points to the element with key equivalent to the key of the pair.
std::pair<iterator, bool> insert(std::pair<std::string_view, ValueTy> KV) {
return try_emplace(KV.first, std::move(KV.second));
}
/// Inserts elements from range [first, last). If multiple elements in the
/// range have keys that compare equivalent, it is unspecified which element
/// is inserted .
template <typename InputIt> void insert(InputIt First, InputIt Last) {
for (InputIt It = First; It != Last; ++It)
insert(*It);
}
/// Inserts elements from initializer list ilist. If multiple elements in
/// the range have keys that compare equivalent, it is unspecified which
/// element is inserted
void insert(std::initializer_list<std::pair<std::string_view, ValueTy>> List) {
insert(List.begin(), List.end());
}
/// Inserts an element or assigns to the current element if the key already
/// exists. The return type is the same as try_emplace.
template <typename V>
std::pair<iterator, bool> insert_or_assign(std::string_view Key, V &&Val) {
auto Ret = try_emplace(Key, std::forward<V>(Val));
if (!Ret.second)
Ret.first->second = std::forward<V>(Val);
return Ret;
}
/// Emplace a new element for the specified key into the map if the key isn't
/// already in the map. The bool component of the returned pair is true
/// if and only if the insertion takes place, and the iterator component of
/// the pair points to the element with key equivalent to the key of the pair.
template <typename... ArgsTy>
std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&...Args) {
unsigned BucketNo = LookupBucketFor(Key);
StringMapEntryBase *&Bucket = TheTable[BucketNo];
if (Bucket && Bucket != getTombstoneVal())
return std::make_pair(iterator(TheTable + BucketNo, false),
false); // Already exists in map.
if (Bucket == getTombstoneVal())
--NumTombstones;
Bucket =
MapEntryTy::create(Key, getAllocator(), std::forward<ArgsTy>(Args)...);
++NumItems;
assert(NumItems + NumTombstones <= NumBuckets);
BucketNo = RehashTable(BucketNo);
return std::make_pair(iterator(TheTable + BucketNo, false), true);
}
// clear - Empties out the StringMap
void clear() {
if (empty())
return;
// Zap all values, resetting the keys back to non-present (not tombstone),
// which is safe because we're removing all elements.
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *&Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
}
Bucket = nullptr;
}
NumItems = 0;
NumTombstones = 0;
}
/// remove - Remove the specified key/value pair from the map, but do not
/// erase it. This aborts if the key is not in the map.
void remove(MapEntryTy *KeyValue) { RemoveKey(KeyValue); }
void erase(iterator I) {
MapEntryTy &V = *I;
remove(&V);
V.Destroy(getAllocator());
}
bool erase(std::string_view Key) {
iterator I = find(Key);
if (I == end())
return false;
erase(I);
return true;
}
};
template <typename DerivedTy, typename ValueTy>
class StringMapIterBase
: public iterator_facade_base<DerivedTy, std::forward_iterator_tag,
ValueTy> {
protected:
StringMapEntryBase **Ptr = nullptr;
public:
StringMapIterBase() = default;
explicit StringMapIterBase(StringMapEntryBase **Bucket,
bool NoAdvance = false)
: Ptr(Bucket) {
if (!NoAdvance)
AdvancePastEmptyBuckets();
}
DerivedTy &operator=(const DerivedTy &Other) {
Ptr = Other.Ptr;
return static_cast<DerivedTy &>(*this);
}
friend bool operator==(const DerivedTy &LHS, const DerivedTy &RHS) {
return LHS.Ptr == RHS.Ptr;
}
DerivedTy &operator++() { // Preincrement
++Ptr;
AdvancePastEmptyBuckets();
return static_cast<DerivedTy &>(*this);
}
DerivedTy operator++(int) { // Post-increment
DerivedTy Tmp(Ptr);
++*this;
return Tmp;
}
DerivedTy &operator--() { // Predecrement
--Ptr;
ReversePastEmptyBuckets();
return static_cast<DerivedTy &>(*this);
}
DerivedTy operator--(int) { // Post-decrement
DerivedTy Tmp(Ptr);
--*this;
return Tmp;
}
private:
void AdvancePastEmptyBuckets() {
while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
++Ptr;
}
void ReversePastEmptyBuckets() {
while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
--Ptr;
}
};
template <typename ValueTy>
class StringMapConstIterator
: public StringMapIterBase<StringMapConstIterator<ValueTy>,
const StringMapEntry<ValueTy>> {
using base = StringMapIterBase<StringMapConstIterator<ValueTy>,
const StringMapEntry<ValueTy>>;
public:
StringMapConstIterator() = default;
explicit StringMapConstIterator(StringMapEntryBase **Bucket,
bool NoAdvance = false)
: base(Bucket, NoAdvance) {}
const StringMapEntry<ValueTy> &operator*() const {
return *static_cast<const StringMapEntry<ValueTy> *>(*this->Ptr);
}
};
template <typename ValueTy>
class StringMapIterator : public StringMapIterBase<StringMapIterator<ValueTy>,
StringMapEntry<ValueTy>> {
using base =
StringMapIterBase<StringMapIterator<ValueTy>, StringMapEntry<ValueTy>>;
public:
StringMapIterator() = default;
explicit StringMapIterator(StringMapEntryBase **Bucket,
bool NoAdvance = false)
: base(Bucket, NoAdvance) {}
StringMapEntry<ValueTy> &operator*() const {
return *static_cast<StringMapEntry<ValueTy> *>(*this->Ptr);
}
operator StringMapConstIterator<ValueTy>() const {
return StringMapConstIterator<ValueTy>(this->Ptr, true);
}
};
template <typename ValueTy>
class StringMapKeyIterator
: public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
StringMapConstIterator<ValueTy>,
std::forward_iterator_tag, std::string_view> {
using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
StringMapConstIterator<ValueTy>,
std::forward_iterator_tag, std::string_view>;
public:
StringMapKeyIterator() = default;
explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
: base(std::move(Iter)) {}
std::string_view operator*() const { return this->wrapped()->getKey(); }
};
template <typename ValueTy>
bool operator==(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
// same instance?
if (&lhs == &rhs) return true;
// first check that sizes are identical
if (lhs.size() != rhs.size()) return false;
// copy into vectors and sort by key
SmallVector<StringMapConstIterator<ValueTy>, 16> lhs_items;
lhs_items.reserve(lhs.size());
for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
lhs_items.push_back(i);
std::sort(lhs_items.begin(), lhs_items.end(),
[](const StringMapConstIterator<ValueTy>& a,
const StringMapConstIterator<ValueTy>& b) {
return a->getKey() < b->getKey();
});
SmallVector<StringMapConstIterator<ValueTy>, 16> rhs_items;
rhs_items.reserve(rhs.size());
for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
rhs_items.push_back(i);
std::sort(rhs_items.begin(), rhs_items.end(),
[](const StringMapConstIterator<ValueTy>& a,
const StringMapConstIterator<ValueTy>& b) {
return a->getKey() < b->getKey();
});
// compare vector keys and values
for (auto a = lhs_items.begin(), b = rhs_items.begin(),
aend = lhs_items.end(), bend = rhs_items.end();
a != aend && b != bend; ++a, ++b) {
if ((*a)->first() != (*b)->first() || (*a)->second != (*b)->second)
return false;
}
return true;
}
template <typename ValueTy>
inline bool operator!=(const StringMap<ValueTy>& lhs,
const StringMap<ValueTy>& rhs) {
return !(lhs == rhs);
}
template <typename ValueTy>
bool operator<(const StringMap<ValueTy>& lhs, const StringMap<ValueTy>& rhs) {
// same instance?
if (&lhs == &rhs) return false;
// copy into vectors and sort by key
SmallVector<std::string_view, 16> lhs_keys;
lhs_keys.reserve(lhs.size());
for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i)
lhs_keys.push_back(i->getKey());
std::sort(lhs_keys.begin(), lhs_keys.end());
SmallVector<std::string_view, 16> rhs_keys;
rhs_keys.reserve(rhs.size());
for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i)
rhs_keys.push_back(i->getKey());
std::sort(rhs_keys.begin(), rhs_keys.end());
// use std::vector comparison
return lhs_keys < rhs_keys;
}
template <typename ValueTy>
inline bool operator<=(const StringMap<ValueTy>& lhs,
const StringMap<ValueTy>& rhs) {
return !(rhs < lhs);
}
template <typename ValueTy>
inline bool operator>(const StringMap<ValueTy>& lhs,
const StringMap<ValueTy>& rhs) {
return !(lhs <= rhs);
}
template <typename ValueTy>
inline bool operator>=(const StringMap<ValueTy>& lhs,
const StringMap<ValueTy>& rhs) {
return !(lhs < rhs);
}
} // end namespace wpi
#endif // WPIUTIL_WPI_STRINGMAP_H

View File

@@ -1,187 +0,0 @@
//===- StringMapEntry.h - String Hash table map interface -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the StringMapEntry class - it is intended to be a low
/// dependency implementation detail of StringMap that is more suitable for
/// inclusion in public headers than StringMap.h itself is.
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_STRINGMAPENTRY_H
#define WPIUTIL_WPI_STRINGMAPENTRY_H
#include "wpi/MemAlloc.h"
#include <cassert>
#include <cstring>
#include <optional>
#include <string_view>
namespace wpi {
/// StringMapEntryBase - Shared base class of StringMapEntry instances.
class StringMapEntryBase {
size_t keyLength;
public:
explicit StringMapEntryBase(size_t keyLength) : keyLength(keyLength) {}
size_t getKeyLength() const { return keyLength; }
protected:
/// Helper to tail-allocate \p Key. It'd be nice to generalize this so it
/// could be reused elsewhere, maybe even taking an wpi::function_ref to
/// type-erase the allocator and put it in a source file.
template <typename AllocatorTy>
static void *allocateWithKey(size_t EntrySize, size_t EntryAlign,
std::string_view Key, AllocatorTy &Allocator);
};
// Define out-of-line to dissuade inlining.
template <typename AllocatorTy>
void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign,
std::string_view Key,
AllocatorTy &Allocator) {
size_t KeyLength = Key.size();
// Allocate a new item with space for the string at the end and a null
// terminator.
size_t AllocSize = EntrySize + KeyLength + 1;
void *Allocation = Allocator.Allocate(AllocSize, EntryAlign);
assert(Allocation && "Unhandled out-of-memory");
// Copy the string information.
char *Buffer = reinterpret_cast<char *>(Allocation) + EntrySize;
if (KeyLength > 0)
::memcpy(Buffer, Key.data(), KeyLength);
Buffer[KeyLength] = 0; // Null terminate for convenience of clients.
return Allocation;
}
/// StringMapEntryStorage - Holds the value in a StringMapEntry.
///
/// Factored out into a separate base class to make it easier to specialize.
/// This is primarily intended to support StringSet, which doesn't need a value
/// stored at all.
template <typename ValueTy>
class StringMapEntryStorage : public StringMapEntryBase {
public:
ValueTy second;
explicit StringMapEntryStorage(size_t keyLength)
: StringMapEntryBase(keyLength), second() {}
template <typename... InitTy>
StringMapEntryStorage(size_t keyLength, InitTy &&...initVals)
: StringMapEntryBase(keyLength),
second(std::forward<InitTy>(initVals)...) {}
StringMapEntryStorage(StringMapEntryStorage &e) = delete;
const ValueTy &getValue() const { return second; }
ValueTy &getValue() { return second; }
void setValue(const ValueTy &V) { second = V; }
};
template <>
class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
public:
explicit StringMapEntryStorage(size_t keyLength,
std::nullopt_t = std::nullopt)
: StringMapEntryBase(keyLength) {}
StringMapEntryStorage(StringMapEntryStorage &entry) = delete;
std::nullopt_t getValue() const { return std::nullopt; }
};
/// StringMapEntry - This is used to represent one value that is inserted into
/// a StringMap. It contains the Value itself and the key: the string length
/// and data.
template <typename ValueTy>
class StringMapEntry final : public StringMapEntryStorage<ValueTy> {
public:
using StringMapEntryStorage<ValueTy>::StringMapEntryStorage;
using ValueType = ValueTy;
std::string_view getKey() const {
return std::string_view(getKeyData(), this->getKeyLength());
}
/// getKeyData - Return the start of the string data that is the key for this
/// value. The string data is always stored immediately after the
/// StringMapEntry object.
const char *getKeyData() const {
return reinterpret_cast<const char *>(this + 1);
}
std::string_view first() const {
return std::string_view(getKeyData(), this->getKeyLength());
}
/// Create a StringMapEntry for the specified key construct the value using
/// \p InitiVals.
template <typename AllocatorTy, typename... InitTy>
static StringMapEntry *create(std::string_view key, AllocatorTy &allocator,
InitTy &&... initVals) {
return new (StringMapEntryBase::allocateWithKey(
sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
StringMapEntry(key.size(), std::forward<InitTy>(initVals)...);
}
/// GetStringMapEntryFromKeyData - Given key data that is known to be embedded
/// into a StringMapEntry, return the StringMapEntry itself.
static StringMapEntry &GetStringMapEntryFromKeyData(const char *keyData) {
char *ptr = const_cast<char *>(keyData) - sizeof(StringMapEntry<ValueTy>);
return *reinterpret_cast<StringMapEntry *>(ptr);
}
/// Destroy - Destroy this StringMapEntry, releasing memory back to the
/// specified allocator.
template <typename AllocatorTy> void Destroy(AllocatorTy &allocator) {
// Free memory referenced by the item.
size_t AllocSize = sizeof(StringMapEntry) + this->getKeyLength() + 1;
this->~StringMapEntry();
allocator.Deallocate(static_cast<void *>(this), AllocSize,
alignof(StringMapEntry));
}
};
// Allow structured bindings on StringMapEntry.
template <std::size_t Index, typename ValueTy>
decltype(auto) get(const StringMapEntry<ValueTy> &E) {
static_assert(Index < 2);
if constexpr (Index == 0)
return E.first();
else
return (E.second);
}
// Allow structured bindings on StringMapEntry.
template <std::size_t Index, typename ValueTy>
decltype(auto) get(StringMapEntry<ValueTy> &E) {
static_assert(Index < 2);
if constexpr (Index == 0)
return E.first();
else
return (E.second);
}
} // end namespace wpi
namespace std {
template <typename ValueTy>
struct tuple_size<wpi::StringMapEntry<ValueTy>>
: std::integral_constant<std::size_t, 2> {};
template <std::size_t I, typename ValueTy>
struct tuple_element<I, wpi::StringMapEntry<ValueTy>>
: std::conditional<I == 0, std::string_view, ValueTy> {};
} // namespace std
#endif // WPIUTIL_WPI_STRINGMAPENTRY_H

View File

@@ -1,3 +1,7 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
//===- llvm/unittest/ADT/StringMapMap.cpp - StringMap unit tests ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -6,27 +10,26 @@
//
//===----------------------------------------------------------------------===//
#include "wpi/StringMap.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <limits>
#include "wpi/StringMap.h" // NOLINT(build/include_order)
#include <string>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
using namespace wpi;
namespace {
static_assert(sizeof(StringMap<uint32_t>) <
sizeof(StringMap<uint32_t, MallocAllocator &>),
"Ensure empty base optimization happens with default allocator");
// Test fixture
class StringMapTest : public testing::Test {
protected:
protected:
StringMap<uint32_t> testMap;
static const char testKey[];
static const uint32_t testValue;
static const char *testKeyFirst;
static const char* testKeyFirst;
static size_t testKeyLength;
static const std::string testKeyStr;
@@ -57,7 +60,7 @@ protected:
// Iterator tests
StringMap<uint32_t>::iterator it = testMap.begin();
EXPECT_STREQ(testKey, it->first().data());
EXPECT_STREQ(testKey, it->first.data());
EXPECT_EQ(testValue, it->second);
++it;
EXPECT_TRUE(it == testMap.end());
@@ -76,26 +79,28 @@ protected:
const char StringMapTest::testKey[] = "key";
const uint32_t StringMapTest::testValue = 1u;
const char *StringMapTest::testKeyFirst = testKey;
const char* StringMapTest::testKeyFirst = testKey;
size_t StringMapTest::testKeyLength = sizeof(testKey) - 1;
const std::string StringMapTest::testKeyStr(testKey);
struct CountCopyAndMove {
CountCopyAndMove() = default;
CountCopyAndMove(const CountCopyAndMove &) { copy = 1; }
CountCopyAndMove(CountCopyAndMove &&) { move = 1; }
void operator=(const CountCopyAndMove &) { ++copy; }
void operator=(CountCopyAndMove &&) { ++move; }
CountCopyAndMove(const CountCopyAndMove&) { copy = 1; }
CountCopyAndMove(CountCopyAndMove&&) { move = 1; }
void operator=(const CountCopyAndMove&) { ++copy; }
void operator=(CountCopyAndMove&&) { ++move; }
int copy = 0;
int move = 0;
};
// Empty map tests.
TEST_F(StringMapTest, EmptyMapTest) { assertEmptyMap(); }
TEST_F(StringMapTest, EmptyMap) {
assertEmptyMap();
}
// Constant map tests.
TEST_F(StringMapTest, ConstEmptyMapTest) {
const StringMap<uint32_t> &constTestMap = testMap;
TEST_F(StringMapTest, ConstEmptyMap) {
const StringMap<uint32_t>& constTestMap = testMap;
// Size tests
EXPECT_EQ(0u, constTestMap.size());
@@ -106,11 +111,12 @@ TEST_F(StringMapTest, ConstEmptyMapTest) {
// Lookup tests
EXPECT_EQ(0u, constTestMap.count(testKey));
EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
EXPECT_EQ(0u,
constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
EXPECT_EQ(0u, constTestMap.count(testKeyStr));
EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());
EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
constTestMap.end());
EXPECT_TRUE(constTestMap.find(std::string_view(
testKeyFirst, testKeyLength)) == constTestMap.end());
EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());
}
@@ -122,63 +128,41 @@ TEST_F(StringMapTest, InitializerListCtor) {
}
// A map with a single entry.
TEST_F(StringMapTest, SingleEntryMapTest) {
TEST_F(StringMapTest, SingleEntryMap) {
testMap[testKey] = testValue;
assertSingleItemMap();
}
// Test clear() method.
TEST_F(StringMapTest, ClearTest) {
TEST_F(StringMapTest, Clear) {
testMap[testKey] = testValue;
testMap.clear();
assertEmptyMap();
}
// Test erase(iterator) method.
TEST_F(StringMapTest, EraseIteratorTest) {
TEST_F(StringMapTest, EraseIterator) {
testMap[testKey] = testValue;
testMap.erase(testMap.begin());
assertEmptyMap();
}
// Test erase(value) method.
TEST_F(StringMapTest, EraseValueTest) {
TEST_F(StringMapTest, EraseValue) {
testMap[testKey] = testValue;
testMap.erase(testKey);
assertEmptyMap();
}
// Test inserting two values and erasing one.
TEST_F(StringMapTest, InsertAndEraseTest) {
TEST_F(StringMapTest, InsertAndErase) {
testMap[testKey] = testValue;
testMap["otherKey"] = 2;
testMap.erase("otherKey");
assertSingleItemMap();
}
TEST_F(StringMapTest, SmallFullMapTest) {
// StringMap has a tricky corner case when the map is small (<8 buckets) and
// it fills up through a balanced pattern of inserts and erases. This can
// lead to inf-loops in some cases (PR13148) so we test it explicitly here.
wpi::StringMap<int> Map(2);
Map["eins"] = 1;
Map["zwei"] = 2;
Map["drei"] = 3;
Map.erase("drei");
Map.erase("eins");
Map["veir"] = 4;
Map["funf"] = 5;
EXPECT_EQ(3u, Map.size());
EXPECT_EQ(0, Map.lookup("eins"));
EXPECT_EQ(2, Map.lookup("zwei"));
EXPECT_EQ(0, Map.lookup("drei"));
EXPECT_EQ(4, Map.lookup("veir"));
EXPECT_EQ(5, Map.lookup("funf"));
}
TEST_F(StringMapTest, CopyCtorTest) {
TEST_F(StringMapTest, SmallFullMap) {
wpi::StringMap<int> Map;
Map["eins"] = 1;
@@ -190,22 +174,41 @@ TEST_F(StringMapTest, CopyCtorTest) {
Map["funf"] = 5;
EXPECT_EQ(3u, Map.size());
EXPECT_EQ(0, Map.lookup("eins"));
EXPECT_EQ(2, Map.lookup("zwei"));
EXPECT_EQ(0, Map.lookup("drei"));
EXPECT_EQ(4, Map.lookup("veir"));
EXPECT_EQ(5, Map.lookup("funf"));
EXPECT_FALSE(Map.contains("eins"));
EXPECT_EQ(2, Map["zwei"]);
EXPECT_FALSE(Map.contains("drei"));
EXPECT_EQ(4, Map["veir"]);
EXPECT_EQ(5, Map["funf"]);
}
TEST_F(StringMapTest, CopyCtor) {
wpi::StringMap<int> Map;
Map["eins"] = 1;
Map["zwei"] = 2;
Map["drei"] = 3;
Map.erase("drei");
Map.erase("eins");
Map["veir"] = 4;
Map["funf"] = 5;
EXPECT_EQ(3u, Map.size());
EXPECT_FALSE(Map.contains("eins"));
EXPECT_EQ(2, Map["zwei"]);
EXPECT_FALSE(Map.contains("drei"));
EXPECT_EQ(4, Map["veir"]);
EXPECT_EQ(5, Map["funf"]);
wpi::StringMap<int> Map2(Map);
EXPECT_EQ(3u, Map2.size());
EXPECT_EQ(0, Map2.lookup("eins"));
EXPECT_EQ(2, Map2.lookup("zwei"));
EXPECT_EQ(0, Map2.lookup("drei"));
EXPECT_EQ(4, Map2.lookup("veir"));
EXPECT_EQ(5, Map2.lookup("funf"));
EXPECT_FALSE(Map2.contains("eins"));
EXPECT_EQ(2, Map2["zwei"]);
EXPECT_FALSE(Map2.contains("drei"));
EXPECT_EQ(4, Map2["veir"]);
EXPECT_EQ(5, Map2["funf"]);
}
TEST_F(StringMapTest, AtTest) {
TEST_F(StringMapTest, At) {
wpi::StringMap<int> Map;
// keys both found and not found on non-empty map
@@ -218,7 +221,7 @@ TEST_F(StringMapTest, AtTest) {
}
// A more complex iteration test.
TEST_F(StringMapTest, IterationTest) {
TEST_F(StringMapTest, Iteration) {
bool visited[100];
// Insert 100 numbers into the map
@@ -234,7 +237,7 @@ TEST_F(StringMapTest, IterationTest) {
++it) {
std::stringstream ss;
ss << "key_" << it->second;
ASSERT_STREQ(ss.str().c_str(), it->first().data());
ASSERT_STREQ(ss.str().c_str(), it->first.data());
visited[it->second] = true;
}
@@ -244,34 +247,23 @@ TEST_F(StringMapTest, IterationTest) {
}
}
// Test StringMapEntry::Create() method.
TEST_F(StringMapTest, StringMapEntryTest) {
MallocAllocator Allocator;
StringMap<uint32_t>::value_type *entry =
StringMap<uint32_t>::value_type::create(
std::string_view(testKeyFirst, testKeyLength), Allocator, 1u);
EXPECT_STREQ(testKey, entry->first().data());
EXPECT_EQ(1u, entry->second);
entry->Destroy(Allocator);
}
// Test insert() method.
TEST_F(StringMapTest, InsertTest) {
TEST_F(StringMapTest, Insert) {
SCOPED_TRACE("InsertTest");
testMap.insert(StringMap<uint32_t>::value_type::create(
std::string_view(testKeyFirst, testKeyLength), testMap.getAllocator(), 1u));
testMap.insert(
std::make_pair(std::string_view(testKeyFirst, testKeyLength), 1u));
assertSingleItemMap();
}
// Test insert(pair<K, V>) method
TEST_F(StringMapTest, InsertPairTest) {
TEST_F(StringMapTest, InsertPair) {
bool Inserted;
StringMap<uint32_t>::iterator NewIt;
std::tie(NewIt, Inserted) =
testMap.insert(std::make_pair(testKeyFirst, testValue));
EXPECT_EQ(1u, testMap.size());
EXPECT_EQ(testValue, testMap[testKeyFirst]);
EXPECT_EQ(testKeyFirst, NewIt->first());
EXPECT_EQ(testKeyFirst, NewIt->first);
EXPECT_EQ(testValue, NewIt->second);
EXPECT_TRUE(Inserted);
@@ -284,28 +276,12 @@ TEST_F(StringMapTest, InsertPairTest) {
EXPECT_EQ(NewIt, ExistingIt);
}
// Test insert(pair<K, V>) method when rehashing occurs
TEST_F(StringMapTest, InsertRehashingPairTest) {
// Check that the correct iterator is returned when the inserted element is
// moved to a different bucket during internal rehashing. This depends on
// the particular key, and the implementation of StringMap and HashString.
// Changes to those might result in this test not actually checking that.
StringMap<uint32_t> t(0);
EXPECT_EQ(0u, t.getNumBuckets());
StringMap<uint32_t>::iterator It =
t.insert(std::make_pair("abcdef", 42)).first;
EXPECT_EQ(16u, t.getNumBuckets());
EXPECT_EQ("abcdef", It->first());
EXPECT_EQ(42u, It->second);
}
TEST_F(StringMapTest, InsertOrAssignTest) {
TEST_F(StringMapTest, InsertOrAssign) {
struct A : CountCopyAndMove {
A(int v) : v(v) {}
explicit A(int v) : v(v) {}
int v;
};
StringMap<A> t(0);
StringMap<A> t;
auto try1 = t.insert_or_assign("A", A(1));
EXPECT_TRUE(try1.second);
@@ -321,23 +297,9 @@ TEST_F(StringMapTest, InsertOrAssignTest) {
EXPECT_EQ(0, try1.first->second.copy);
}
TEST_F(StringMapTest, IterMapKeysSmallVector) {
StringMap<int> Map;
Map["A"] = 1;
Map["B"] = 2;
Map["C"] = 3;
Map["D"] = 3;
auto Keys = to_vector<4>(Map.keys());
std::sort(Keys.begin(), Keys.end());
SmallVector<std::string_view, 4> Expected = {"A", "B", "C", "D"};
EXPECT_EQ(Expected, Keys);
}
// Create a non-default constructable value
struct StringMapTestStruct {
StringMapTestStruct(int i) : i(i) {}
explicit StringMapTestStruct(int i) : i(i) {}
StringMapTestStruct() = delete;
int i;
};
@@ -352,39 +314,24 @@ TEST_F(StringMapTest, NonDefaultConstructable) {
struct Immovable {
Immovable() {}
Immovable(Immovable &&) = delete; // will disable the other special members
Immovable(Immovable&&) = delete; // will disable the other special members
};
struct MoveOnly {
int i;
MoveOnly(int i) : i(i) {}
MoveOnly(const Immovable &) : i(0) {}
MoveOnly(MoveOnly &&RHS) : i(RHS.i) {}
MoveOnly &operator=(MoveOnly &&RHS) {
explicit MoveOnly(int i) : i(i) {}
explicit MoveOnly(const Immovable&) : i(0) {}
MoveOnly(MoveOnly&& RHS) : i(RHS.i) {}
MoveOnly& operator=(MoveOnly&& RHS) {
i = RHS.i;
return *this;
}
private:
MoveOnly(const MoveOnly &) = delete;
MoveOnly &operator=(const MoveOnly &) = delete;
private:
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
};
TEST_F(StringMapTest, MoveOnly) {
StringMap<MoveOnly> t;
t.insert(std::make_pair("Test", MoveOnly(42)));
std::string_view Key = "Test";
StringMapEntry<MoveOnly>::create(Key, t.getAllocator(), MoveOnly(42))
->Destroy(t.getAllocator());
}
TEST_F(StringMapTest, CtorArg) {
std::string_view Key = "Test";
MallocAllocator Allocator;
StringMapEntry<MoveOnly>::create(Key, Allocator, Immovable())
->Destroy(Allocator);
}
TEST_F(StringMapTest, MoveConstruct) {
StringMap<int> A;
A["x"] = 42;
@@ -412,7 +359,7 @@ TEST_F(StringMapTest, EqualEmpty) {
StringMap<int> B;
ASSERT_TRUE(A == B);
ASSERT_FALSE(A != B);
ASSERT_TRUE(A == A); // self check
ASSERT_TRUE(A == A); // self check
}
TEST_F(StringMapTest, EqualWithValues) {
@@ -432,7 +379,7 @@ TEST_F(StringMapTest, EqualWithValues) {
ASSERT_TRUE(B == A);
ASSERT_FALSE(A != B);
ASSERT_FALSE(B != A);
ASSERT_TRUE(A == A); // self check
ASSERT_TRUE(A == A); // self check
}
TEST_F(StringMapTest, NotEqualMissingKeys) {
@@ -472,21 +419,21 @@ TEST_F(StringMapTest, NotEqualWithDifferentValues) {
}
struct Countable {
int &InstanceCount;
int& InstanceCount;
int Number;
Countable(int Number, int &InstanceCount)
Countable(int Number, int& InstanceCount)
: InstanceCount(InstanceCount), Number(Number) {
++InstanceCount;
}
Countable(Countable &&C) : InstanceCount(C.InstanceCount), Number(C.Number) {
Countable(Countable&& C) : InstanceCount(C.InstanceCount), Number(C.Number) {
++InstanceCount;
C.Number = -1;
}
Countable(const Countable &C)
Countable(const Countable& C)
: InstanceCount(C.InstanceCount), Number(C.Number) {
++InstanceCount;
}
Countable &operator=(Countable C) {
Countable& operator=(Countable C) {
Number = C.Number;
return *this;
}
@@ -519,7 +466,7 @@ TEST_F(StringMapTest, StructuredBindings) {
StringMap<int> A;
A["a"] = 42;
for (auto &[Key, Value] : A) {
for (auto& [Key, Value] : A) {
EXPECT_EQ("a", Key);
EXPECT_EQ(42, Value);
}
@@ -529,7 +476,7 @@ TEST_F(StringMapTest, StructuredBindingsMoveOnly) {
StringMap<MoveOnly> A;
A.insert(std::make_pair("a", MoveOnly(42)));
for (auto &&[Key, Value] : A) {
for (auto&& [Key, Value] : A) {
EXPECT_EQ("a", Key);
EXPECT_EQ(42, Value.i);
}
@@ -542,16 +489,16 @@ struct CountCtorCopyAndMove {
static unsigned Move;
static unsigned Copy;
int Data = 0;
CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; }
explicit CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; }
CountCtorCopyAndMove() { Ctor++; }
CountCtorCopyAndMove(const CountCtorCopyAndMove &) { Copy++; }
CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &) {
CountCtorCopyAndMove(const CountCtorCopyAndMove&) { Copy++; }
CountCtorCopyAndMove& operator=(const CountCtorCopyAndMove&) {
Copy++;
return *this;
}
CountCtorCopyAndMove(CountCtorCopyAndMove &&) { Move++; }
CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &&) {
CountCtorCopyAndMove(CountCtorCopyAndMove&&) { Move++; }
CountCtorCopyAndMove& operator=(const CountCtorCopyAndMove&&) {
Move++;
return *this;
}
@@ -560,30 +507,7 @@ unsigned CountCtorCopyAndMove::Copy = 0;
unsigned CountCtorCopyAndMove::Move = 0;
unsigned CountCtorCopyAndMove::Ctor = 0;
} // anonymous namespace
// Make sure creating the map with an initial size of N actually gives us enough
// buckets to insert N items without increasing allocation size.
TEST(StringMapCustomTest, InitialSizeTest) {
// 1 is an "edge value", 32 is an arbitrary power of two, and 67 is an
// arbitrary prime, picked without any good reason.
for (auto Size : {1, 32, 67}) {
StringMap<CountCtorCopyAndMove> Map(Size);
auto NumBuckets = Map.getNumBuckets();
CountCtorCopyAndMove::Move = 0;
CountCtorCopyAndMove::Copy = 0;
for (int i = 0; i < Size; ++i)
Map.insert(std::pair<std::string, CountCtorCopyAndMove>(
std::piecewise_construct, std::forward_as_tuple(std::to_string(i)),
std::forward_as_tuple(i)));
// After the initial move, the map will move the Elts in the Entry.
EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);
// We copy once the pair from the Elts vector
EXPECT_EQ(0u, CountCtorCopyAndMove::Copy);
// Check that the map didn't grow
EXPECT_EQ(Map.getNumBuckets(), NumBuckets);
}
}
} // namespace
TEST(StringMapCustomTest, BracketOperatorCtor) {
StringMap<CountCtorCopyAndMove> Map;
@@ -600,57 +524,18 @@ namespace {
struct NonMoveableNonCopyableType {
int Data = 0;
NonMoveableNonCopyableType() = default;
NonMoveableNonCopyableType(int Data) : Data(Data) {}
NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete;
NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete;
explicit NonMoveableNonCopyableType(int Data) : Data(Data) {}
NonMoveableNonCopyableType(const NonMoveableNonCopyableType&) = delete;
NonMoveableNonCopyableType(NonMoveableNonCopyableType&&) = delete;
};
} // namespace
} // namespace
// Test that we can "emplace" an element in the map without involving map/move
TEST(StringMapCustomTest, EmplaceTest) {
TEST(StringMapCustomTest, Emplace) {
StringMap<NonMoveableNonCopyableType> Map;
Map.try_emplace("abcd", 42);
EXPECT_EQ(1u, Map.count("abcd"));
EXPECT_EQ(42, Map["abcd"].Data);
}
// Test that StringMapEntryBase can handle size_t wide sizes.
TEST(StringMapCustomTest, StringMapEntryBaseSize) {
size_t LargeValue;
// Test that the entry can represent max-unsigned.
if (sizeof(size_t) <= sizeof(unsigned))
LargeValue = std::numeric_limits<unsigned>::max();
else
LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
StringMapEntryBase LargeBase(LargeValue);
EXPECT_EQ(LargeValue, LargeBase.getKeyLength());
// Test that the entry can hold at least max size_t.
LargeValue = std::numeric_limits<size_t>::max();
StringMapEntryBase LargerBase(LargeValue);
LargeValue = std::numeric_limits<size_t>::max();
EXPECT_EQ(LargeValue, LargerBase.getKeyLength());
}
// Test that StringMapEntry can handle size_t wide sizes.
TEST(StringMapCustomTest, StringMapEntrySize) {
size_t LargeValue;
// Test that the entry can represent max-unsigned.
if (sizeof(size_t) <= sizeof(unsigned))
LargeValue = std::numeric_limits<unsigned>::max();
else
LargeValue = std::numeric_limits<unsigned>::max() + 1ULL;
StringMapEntry<int> LargeEntry(LargeValue);
std::string_view Key = LargeEntry.getKey();
EXPECT_EQ(LargeValue, Key.size());
// Test that the entry can hold at least max size_t.
LargeValue = std::numeric_limits<size_t>::max();
StringMapEntry<int> LargerEntry(LargeValue);
Key = LargerEntry.getKey();
EXPECT_EQ(LargeValue, Key.size());
}
} // end anonymous namespace
} // namespace