Skip to content

Commit

Permalink
[ADT] Add CachedHashString.
Browse files Browse the repository at this point in the history
Summary:
This is like CachedHashStringRef, but owns its data.

This lets us use strings inside of DenseMaps.

Reviewers: timshen

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D25645

llvm-svn: 284871
  • Loading branch information
Justin Lebar committed Oct 21, 2016
1 parent 245c3e7 commit d87ea9a
Showing 1 changed file with 112 additions and 5 deletions.
117 changes: 112 additions & 5 deletions llvm/include/llvm/ADT/CachedHashString.h
Expand Up @@ -7,23 +7,22 @@
//
//===----------------------------------------------------------------------===//
//
// 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
#define LLVM_ADT_CACHED_HASH_STRING_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

namespace llvm {

Expand Down Expand Up @@ -66,6 +65,114 @@ template <> struct DenseMapInfo<CachedHashStringRef> {
}
};

/// 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<CachedHashString>;

char *P;
uint32_t Size;
uint32_t Hash;

static char *getEmptyKeyPtr() { return DenseMapInfo<char *>::getEmptyKey(); }
static char *getTombstoneKeyPtr() {
return DenseMapInfo<char *>::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<StringRef>::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<CachedHashString> {
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

0 comments on commit d87ea9a

Please sign in to comment.