blob: c5126ab222debbe2415b7c04a54bb976bb7cd403 [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 "wtf/text/AtomicStringTable.h"
#include "wtf/text/StringHash.h"
#include "wtf/text/UTF8.h"
namespace WTF {
using namespace Unicode;
AtomicStringTable::AtomicStringTable() {
for (StringImpl* string : StringImpl::allStaticStrings().values())
add(string);
}
AtomicStringTable::~AtomicStringTable() {
for (StringImpl* string : m_table) {
if (!string->isStatic()) {
DCHECK(string->isAtomic());
string->setIsAtomic(false);
}
}
}
void AtomicStringTable::reserveCapacity(unsigned size) {
m_table.reserveCapacityForSize(size);
}
template <typename T, typename HashTranslator>
PassRefPtr<StringImpl> AtomicStringTable::addToStringTable(const T& value) {
HashSet<StringImpl*>::AddResult addResult =
m_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 addResult.isNewEntry ? adoptRef(*addResult.storedValue)
: *addResult.storedValue;
}
template <typename CharacterType>
struct HashTranslatorCharBuffer {
const CharacterType* s;
unsigned length;
};
typedef HashTranslatorCharBuffer<UChar> UCharBuffer;
struct UCharBufferTranslator {
static unsigned hash(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) {
location = StringImpl::create8BitIfPossible(buf.s, buf.length).leakRef();
location->setHash(hash);
location->setIsAtomic(true);
}
};
struct HashAndUTF8Characters {
unsigned hash;
const char* characters;
unsigned length;
unsigned utf16Length;
};
struct HashAndUTF8CharactersTranslator {
static unsigned hash(const HashAndUTF8Characters& buffer) {
return buffer.hash;
}
static bool equal(StringImpl* const& string,
const HashAndUTF8Characters& buffer) {
if (buffer.utf16Length != string->length())
return false;
// If buffer contains only ASCII characters UTF-8 and UTF16 length are the
// same.
if (buffer.utf16Length != 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* stringCharacters = string->characters8();
for (unsigned i = 0; i < buffer.length; ++i) {
DCHECK(isASCII(buffer.characters[i]));
if (stringCharacters[i] != buffer.characters[i])
return false;
}
return true;
}
const UChar* stringCharacters = string->characters16();
for (unsigned i = 0; i < buffer.length; ++i) {
DCHECK(isASCII(buffer.characters[i]));
if (stringCharacters[i] != buffer.characters[i])
return false;
}
return true;
}
static void translate(StringImpl*& location,
const HashAndUTF8Characters& buffer,
unsigned hash) {
UChar* target;
RefPtr<StringImpl> newString =
StringImpl::createUninitialized(buffer.utf16Length, target);
bool isAllASCII;
const char* source = buffer.characters;
if (convertUTF8ToUTF16(&source, source + buffer.length, &target,
target + buffer.utf16Length,
&isAllASCII) != conversionOK)
NOTREACHED();
if (isAllASCII)
newString = StringImpl::create(buffer.characters, buffer.length);
location = newString.release().leakRef();
location->setHash(hash);
location->setIsAtomic(true);
}
};
PassRefPtr<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 hash(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) {
location = StringImpl::create(buf.s, buf.length).leakRef();
location->setHash(hash);
location->setIsAtomic(true);
}
};
PassRefPtr<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 = *m_table.add(string).storedValue;
if (!result->isAtomic())
result->setIsAtomic(true);
DCHECK(!string->isStatic() || result->isStatic());
return result;
}
PassRefPtr<StringImpl> AtomicStringTable::addUTF8(const char* charactersStart,
const char* charactersEnd) {
HashAndUTF8Characters buffer;
buffer.characters = charactersStart;
buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits(
charactersStart, charactersEnd, buffer.length, buffer.utf16Length);
if (!buffer.hash)
return nullptr;
return addToStringTable<HashAndUTF8Characters,
HashAndUTF8CharactersTranslator>(buffer);
}
void AtomicStringTable::remove(StringImpl* string) {
DCHECK(string->isAtomic());
auto iterator = m_table.find(string);
RELEASE_ASSERT(iterator != m_table.end());
m_table.remove(iterator);
}
} // namespace WTF