mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
[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:
@@ -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()) {
|
||||
|
||||
777
wpiutil/src/main/native/include/wpi/StringMap.h
Normal file
777
wpiutil/src/main/native/include/wpi/StringMap.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user