blob: a77d37f15013250874f173c7293ccfaf4a649359 [file] [log] [blame]
// Copyright (c) 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ---
// Authors: jcrim@google.com (Jay Crim)
// csilvers@google.com (Craig Silverstein)
#include <config.h>
#include "base/mutex.h" // This has to come first to get _XOPEN_SOURCE
#include <ctemplate/find_ptr.h>
#include <ctemplate/template_string.h>
#include HASH_SET_H
#include "base/arena.h"
#include "base/thread_annotations.h"
#include <assert.h>
#include "base/macros.h" // for uint32, uint64, UNALIGNED_LOAD32
#include "base/util.h"
#ifdef HAVE_UNORDERED_MAP
using HASH_NAMESPACE::unordered_set;
// This is totally cheap, but minimizes the need for #ifdef's below...
#define hash_set unordered_set
#else
using HASH_NAMESPACE::hash_set;
#endif
namespace ctemplate {
// Based on public domain MurmurHashUnaligned2, by Austin Appleby.
// http://murmurhash.googlepages.com/
// This variation:
// - interleaves odd/even 32-bit words to improve performance and
// to generate more random bits,
// - has a more complex final mix to combine the 32-bit hashes into
// 64-bits,
// - uses a fixed seed.
// This is not static because template_string_test accesses it directly.
uint64 MurmurHash64(const char* ptr, size_t len) {
const uint32 kMultiplyVal = 0x5bd1e995;
const int kShiftVal = 24;
const uint32 kHashSeed1 = 0xc86b14f7;
const uint32 kHashSeed2 = 0x650f5c4d;
uint32 h1 = kHashSeed1 ^ len, h2 = kHashSeed2;
while (len >= 8) {
uint32 k1 = UNALIGNED_LOAD32(ptr);
k1 *= kMultiplyVal;
k1 ^= k1 >> kShiftVal;
k1 *= kMultiplyVal;
h1 *= kMultiplyVal;
h1 ^= k1;
ptr += 4;
uint32 k2 = UNALIGNED_LOAD32(ptr);
k2 *= kMultiplyVal;
k2 ^= k2 >> kShiftVal;
k2 *= kMultiplyVal;
h2 *= kMultiplyVal;
h2 ^= k2;
ptr += 4;
len -= 8;
}
if (len >= 4) {
uint32 k1 = UNALIGNED_LOAD32(ptr);
k1 *= kMultiplyVal;
k1 ^= k1 >> kShiftVal;
k1 *= kMultiplyVal;
h1 *= kShiftVal;
h1 ^= k1;
ptr += 4;
len -= 4;
}
switch(len) {
case 3:
h2 ^= ptr[2] << 16; // fall through.
case 2:
h2 ^= ptr[1] << 8; // fall through.
case 1:
h2 ^= ptr[0]; // fall through.
default:
h2 *= kMultiplyVal;
}
h1 ^= h2 >> 18;
h1 *= kMultiplyVal;
h2 ^= h1 >> 22;
h2 *= kMultiplyVal;
h1 ^= h2 >> 17;
h1 *= kMultiplyVal;
uint64 h = h1;
h = (h << 32) | h2;
return h;
}
// Unlike StaticTemplateString, it is not a good idea to have a
// default TemplateString::Hasher because TemplateString does not
// provide any lifetime guarantees. The global template_string_set is
// an obvious exception.
struct TemplateStringHasher {
size_t operator()(const TemplateString& ts) const {
TemplateId id = ts.GetGlobalId();
DCHECK(IsTemplateIdInitialized(id));
return hasher(id);
}
// Less operator for MSVC's hash containers.
bool operator()(const TemplateString& a, const TemplateString& b) const {
const TemplateId id_a = a.GetGlobalId();
const TemplateId id_b = b.GetGlobalId();
assert(IsTemplateIdInitialized(id_a));
assert(IsTemplateIdInitialized(id_b));
return hasher(id_a, id_b);
}
TemplateIdHasher hasher;
// These two public members are required by msvc. 4 and 8 are defaults.
static const size_t bucket_size = 4;
static const size_t min_buckets = 8;
};
namespace {
Mutex mutex(base::LINKER_INITIALIZED);
typedef hash_set<TemplateString, TemplateStringHasher> TemplateStringSet;
TemplateStringSet* template_string_set
GUARDED_BY(mutex) PT_GUARDED_BY(mutex) = NULL;
UnsafeArena* arena
GUARDED_BY(mutex) PT_GUARDED_BY(mutex) = NULL;
} // unnamed namespace
size_t StringHash::Hash(const char* s, size_t slen) const {
return static_cast<size_t>(MurmurHash64(s, slen));
}
void TemplateString::AddToGlobalIdToNameMap() LOCKS_EXCLUDED(mutex) {
// shouldn't be calling this if we don't have an id.
CHECK(IsTemplateIdInitialized(id_));
{
// Check to see if it's already here.
ReaderMutexLock reader_lock(&mutex);
if (template_string_set) {
const TemplateString* iter =
find_ptr0(*template_string_set, *this);
if (iter) {
DCHECK_EQ(TemplateString(ptr_, length_),
TemplateString(iter->ptr_, iter->length_))
<< "TemplateId collision!";
return;
}
}
}
WriterMutexLock writer_lock(&mutex);
// First initialize our data structures if we need to.
if (!template_string_set) {
template_string_set = new TemplateStringSet;
}
if (!arena) {
arena = new UnsafeArena(1024); // 1024 was picked out of a hat.
}
if (template_string_set->count(*this)) {
return;
}
// If we are immutable, we can store ourselves directly in the map.
// Otherwise, we need to make an immutable copy.
if (is_immutable()) {
template_string_set->insert(*this);
} else {
const char* immutable_copy = arena->Memdup(ptr_, length_);
template_string_set->insert(
TemplateString(immutable_copy, length_, true, id_));
}
}
TemplateId TemplateString::GetGlobalId() const {
if (IsTemplateIdInitialized(id_)) {
return id_;
}
// Initialize the id and sets the "initialized" flag.
return static_cast<TemplateId>(MurmurHash64(ptr_, length_) |
kTemplateStringInitializedFlag);
}
// static
TemplateString TemplateString::IdToString(TemplateId id) LOCKS_EXCLUDED(mutex) {
ReaderMutexLock reader_lock(&mutex);
if (!template_string_set)
return TemplateString(kStsEmpty);
// To search the set by TemplateId, we must first construct a dummy
// TemplateString. This may seem weird, but it lets us use a
// hash_set instead of a hash_map.
TemplateString id_as_template_string(NULL, 0, false, id);
const TemplateString* iter = find_ptr0(*template_string_set, id_as_template_string);
return iter ? *iter : TemplateString(kStsEmpty);
}
StaticTemplateStringInitializer::StaticTemplateStringInitializer(
const StaticTemplateString* sts) {
// Compute the sts's id if it wasn't specified at static-init
// time. If it was specified at static-init time, verify it's
// correct. This is necessary because static-init id's are, by
// nature, pre-computed, and the id-generating algorithm may have
// changed between the time they were computed and now.
if (sts->do_not_use_directly_.id_ == 0) {
sts->do_not_use_directly_.id_ = TemplateString(*sts).GetGlobalId();
} else {
// Don't use the TemplateString(const StaticTemplateString& sts)
// constructor below, since if we do, GetGlobalId will just return
// sts->do_not_use_directly_.id_ and the check will be pointless.
DCHECK_EQ(TemplateString(sts->do_not_use_directly_.ptr_,
sts->do_not_use_directly_.length_).GetGlobalId(),
sts->do_not_use_directly_.id_);
}
// Now add this id/name pair to the backwards map from id to name.
TemplateString ts_copy_of_sts(*sts);
ts_copy_of_sts.AddToGlobalIdToNameMap();
}
}