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

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

View File

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

View File

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

View File

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

View File

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

View File

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