Index: llvm/include/llvm/ADT/CachedHashString.h =================================================================== --- llvm/include/llvm/ADT/CachedHashString.h +++ llvm/include/llvm/ADT/CachedHashString.h @@ -7,16 +7,14 @@ // //===----------------------------------------------------------------------===// // -// This file defines CachedHashString and CachedHashStringRef. These are like -// std::string and StringRef, except they store their hash in addition to their -// string data. +// This file defines CachedHashString and CachedHashStringRef. These are owning +// and not-owning string types that store their hash in addition to their string +// data. // // Unlike std::string, CachedHashString can be used in DenseSet/DenseMap // (because, unlike std::string, CachedHashString lets us have empty and // tombstone values). // -// TODO: Add CachedHashString. -// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_CACHED_HASH_STRING_H @@ -24,6 +22,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { @@ -66,6 +65,114 @@ } }; +/// A container which contains a string, which it owns, plus a precomputed hash. +/// +/// We do not null-terminate the string. +class CachedHashString { + friend struct DenseMapInfo; + + char *P; + uint32_t Size; + uint32_t Hash; + + static char *getEmptyKeyPtr() { return DenseMapInfo::getEmptyKey(); } + static char *getTombstoneKeyPtr() { + return DenseMapInfo::getTombstoneKey(); + } + + bool isEmptyOrTombstone() const { + return P == getEmptyKeyPtr() || P == getTombstoneKeyPtr(); + } + + explicit CachedHashString(char *EmptyOrTombstonePtr) + : P(EmptyOrTombstonePtr), Size(0), Hash(0) { + assert(isEmptyOrTombstone()); + } + + // TODO: Use small-string optimization to avoid allocating. + +public: + // Explicit because copying and hashing a string isn't free. + explicit CachedHashString(StringRef S) + : CachedHashString(S, DenseMapInfo::getHashValue(S)) {} + + CachedHashString(StringRef S, uint32_t Hash) + : P(new char[S.size()]), Size(S.size()), Hash(Hash) { + memcpy(P, S.data(), S.size()); + } + + // Ideally this class would not be copyable. But SetVector requires copyable + // keys, and we want this to be usable there. + CachedHashString(const CachedHashString &Other) + : Size(Other.Size), Hash(Other.Hash) { + if (Other.isEmptyOrTombstone()) { + P = Other.P; + } else { + P = new char[Size]; + memcpy(P, Other.P, Size); + } + } + + CachedHashString &operator=(CachedHashString Other) { + swap(*this, Other); + return *this; + } + + CachedHashString(CachedHashString &&Other) LLVM_NOEXCEPT : P(Other.P), + Size(Other.Size), + Hash(Other.Hash) { + Other.P = getEmptyKeyPtr(); + } + + ~CachedHashString() { + if (!isEmptyOrTombstone()) + delete[] P; + } + + StringRef val() const { return StringRef(P, Size); } + uint32_t size() const { return Size; } + uint32_t hash() const { return Hash; } + + operator StringRef() const { return val(); } + operator CachedHashStringRef() const { + return CachedHashStringRef(val(), Hash); + } + + friend void swap(CachedHashString &LHS, CachedHashString &RHS) { + using std::swap; + swap(LHS.P, RHS.P); + swap(LHS.Size, RHS.Size); + swap(LHS.Hash, RHS.Hash); + } +}; + +template <> struct DenseMapInfo { + static CachedHashString getEmptyKey() { + return CachedHashString(CachedHashString::getEmptyKeyPtr()); + } + static CachedHashString getTombstoneKey() { + return CachedHashString(CachedHashString::getTombstoneKeyPtr()); + } + static unsigned getHashValue(const CachedHashString &S) { + assert(!isEqual(S, getEmptyKey()) && "Cannot hash the empty key!"); + assert(!isEqual(S, getTombstoneKey()) && "Cannot hash the tombstone key!"); + return S.hash(); + } + static bool isEqual(const CachedHashString &LHS, + const CachedHashString &RHS) { + if (LHS.hash() != RHS.hash()) + return false; + if (LHS.P == CachedHashString::getEmptyKeyPtr()) + return RHS.P == CachedHashString::getEmptyKeyPtr(); + if (LHS.P == CachedHashString::getTombstoneKeyPtr()) + return RHS.P == CachedHashString::getTombstoneKeyPtr(); + + // This is safe because if RHS.P is the empty or tombstone key, it will have + // length 0, so we'll never dereference its pointer. + return LHS.val() == RHS.val(); + } +}; + } // namespace llvm #endif