mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-29 02:21: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
|
||||
Reference in New Issue
Block a user