blob: 082ece5438ce052c1fcfd7d1daafef9dd8520827 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/wtf/text/AtomicStringTable.h"
#include "platform/wtf/text/StringHash.h"
#include "platform/wtf/text/UTF8.h"
namespace WTF {
using namespace Unicode;
AtomicStringTable::AtomicStringTable() {
for (StringImpl* string : StringImpl::AllStaticStrings().Values())
Add(string);
}
AtomicStringTable::~AtomicStringTable() {
for (StringImpl* string : table_) {
if (!string->IsStatic()) {
DCHECK(string->IsAtomic());
string->SetIsAtomic(false);
}
}
}
void AtomicStringTable::ReserveCapacity(unsigned size) {
table_.ReserveCapacityForSize(size);
}
template <typename T, typename HashTranslator>
scoped_refptr<StringImpl> AtomicStringTable::AddToStringTable(const T& value) {
HashSet<StringImpl*>::AddResult add_result =
table_.AddWithTranslator<HashTranslator>(value);
// If the string is newly-translated, then we need to adopt it.
// The boolean in the pair tells us if that is so.
return add_result.is_new_entry ? base::AdoptRef(*add_result.stored_value)
: *add_result.stored_value;
}
template <typename CharacterType>
struct HashTranslatorCharBuffer {
const CharacterType* s;
unsigned length;
};
typedef HashTranslatorCharBuffer<UChar> UCharBuffer;
struct UCharBufferTranslator {
static unsigned GetHash(const UCharBuffer& buf) {
return StringHasher::ComputeHashAndMaskTop8Bits(buf.s, buf.length);
}
static bool Equal(StringImpl* const& str, const UCharBuffer& buf) {
return WTF::Equal(str, buf.s, buf.length);
}
static void Translate(StringImpl*& location,
const UCharBuffer& buf,
unsigned hash) {
auto string = StringImpl::Create8BitIfPossible(buf.s, buf.length);
if (string)
string->AddRef();
location = string.get();
location->SetHash(hash);
location->SetIsAtomic(true);
}
};
struct HashAndUTF8Characters {
unsigned hash;
const char* characters;
unsigned length;
unsigned utf16_length;
};
struct HashAndUTF8CharactersTranslator {
static unsigned GetHash(const HashAndUTF8Characters& buffer) {
return buffer.hash;
}
static bool Equal(StringImpl* const& string,
const HashAndUTF8Characters& buffer) {
if (buffer.utf16_length != string->length())
return false;
// If buffer contains only ASCII characters UTF-8 and UTF16 length are the
// same.
if (buffer.utf16_length != buffer.length) {
if (string->Is8Bit()) {
const LChar* characters8 = string->Characters8();
return EqualLatin1WithUTF8(characters8, characters8 + string->length(),
buffer.characters,
buffer.characters + buffer.length);
}
const UChar* characters16 = string->Characters16();
return EqualUTF16WithUTF8(characters16, characters16 + string->length(),
buffer.characters,
buffer.characters + buffer.length);
}
if (string->Is8Bit()) {
const LChar* string_characters = string->Characters8();
for (unsigned i = 0; i < buffer.length; ++i) {
DCHECK(IsASCII(buffer.characters[i]));
if (string_characters[i] != buffer.characters[i])
return false;
}
return true;
}
const UChar* string_characters = string->Characters16();
for (unsigned i = 0; i < buffer.length; ++i) {
DCHECK(IsASCII(buffer.characters[i]));
if (string_characters[i] != buffer.characters[i])
return false;
}
return true;
}
static void Translate(StringImpl*& location,
const HashAndUTF8Characters& buffer,
unsigned hash) {
UChar* target;
scoped_refptr<StringImpl> new_string =
StringImpl::CreateUninitialized(buffer.utf16_length, target);
bool is_all_ascii;
const char* source = buffer.characters;
if (ConvertUTF8ToUTF16(&source, source + buffer.length, &target,
target + buffer.utf16_length,
&is_all_ascii) != kConversionOK)
NOTREACHED();
if (is_all_ascii)
new_string = StringImpl::Create(buffer.characters, buffer.length);
new_string->AddRef();
location = new_string.get();
location->SetHash(hash);
location->SetIsAtomic(true);
}
};
scoped_refptr<StringImpl> AtomicStringTable::Add(const UChar* s,
unsigned length) {
if (!s)
return nullptr;
if (!length)
return StringImpl::empty_;
UCharBuffer buffer = {s, length};
return AddToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
}
typedef HashTranslatorCharBuffer<LChar> LCharBuffer;
struct LCharBufferTranslator {
static unsigned GetHash(const LCharBuffer& buf) {
return StringHasher::ComputeHashAndMaskTop8Bits(buf.s, buf.length);
}
static bool Equal(StringImpl* const& str, const LCharBuffer& buf) {
return WTF::Equal(str, buf.s, buf.length);
}
static void Translate(StringImpl*& location,
const LCharBuffer& buf,
unsigned hash) {
auto string = StringImpl::Create(buf.s, buf.length);
string->AddRef();
location = string.get();
location->SetHash(hash);
location->SetIsAtomic(true);
}
};
scoped_refptr<StringImpl> AtomicStringTable::Add(const LChar* s,
unsigned length) {
if (!s)
return nullptr;
if (!length)
return StringImpl::empty_;
LCharBuffer buffer = {s, length};
return AddToStringTable<LCharBuffer, LCharBufferTranslator>(buffer);
}
StringImpl* AtomicStringTable::Add(StringImpl* string) {
if (!string->length())
return StringImpl::empty_;
StringImpl* result = *table_.insert(string).stored_value;
if (!result->IsAtomic())
result->SetIsAtomic(true);
DCHECK(!string->IsStatic() || result->IsStatic());
return result;
}
scoped_refptr<StringImpl> AtomicStringTable::AddUTF8(
const char* characters_start,
const char* characters_end) {
HashAndUTF8Characters buffer;
buffer.characters = characters_start;
buffer.hash = CalculateStringHashAndLengthFromUTF8MaskingTop8Bits(
characters_start, characters_end, buffer.length, buffer.utf16_length);
if (!buffer.hash)
return nullptr;
return AddToStringTable<HashAndUTF8Characters,
HashAndUTF8CharactersTranslator>(buffer);
}
void AtomicStringTable::Remove(StringImpl* string) {
DCHECK(string->IsAtomic());
auto iterator = table_.find(string);
CHECK_NE(iterator, table_.end());
table_.erase(iterator);
}
} // namespace WTF