2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
4 * Copyright (C) 2012 Google Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
25 #include "AtomicString.h"
27 #include "StringHash.h"
28 #include <wtf/HashSet.h>
29 #include <wtf/Threading.h>
30 #include <wtf/WTFThreadData.h>
31 #include <wtf/unicode/UTF8.h>
35 using namespace Unicode;
37 COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);
39 class AtomicStringTable {
41 static AtomicStringTable* create()
43 AtomicStringTable* table = new AtomicStringTable;
45 WTFThreadData& data = wtfThreadData();
46 data.m_atomicStringTable = table;
47 data.m_atomicStringTableDestructor = AtomicStringTable::destroy;
52 HashSet<StringImpl*>& table()
58 static void destroy(AtomicStringTable* table)
60 HashSet<StringImpl*>::iterator end = table->m_table.end();
61 for (HashSet<StringImpl*>::iterator iter = table->m_table.begin(); iter != end; ++iter)
62 (*iter)->setIsAtomic(false);
66 HashSet<StringImpl*> m_table;
69 static inline HashSet<StringImpl*>& stringTable()
71 // Once possible we should make this non-lazy (constructed in WTFThreadData's constructor).
72 AtomicStringTable* table = wtfThreadData().atomicStringTable();
74 table = AtomicStringTable::create();
75 return table->table();
78 template<typename T, typename HashTranslator>
79 static inline PassRefPtr<StringImpl> addToStringTable(const T& value)
81 HashSet<StringImpl*>::AddResult addResult = stringTable().add<T, HashTranslator>(value);
83 // If the string is newly-translated, then we need to adopt it.
84 // The boolean in the pair tells us if that is so.
85 return addResult.isNewEntry ? adoptRef(*addResult.iterator) : *addResult.iterator;
88 struct CStringTranslator {
89 static unsigned hash(const LChar* c)
91 return StringHasher::computeHashAndMaskTop8Bits(c);
94 static inline bool equal(StringImpl* r, const LChar* s)
96 return WTF::equal(r, s);
99 static void translate(StringImpl*& location, const LChar* const& c, unsigned hash)
101 location = StringImpl::create(c).leakRef();
102 location->setHash(hash);
103 location->setIsAtomic(true);
107 PassRefPtr<StringImpl> AtomicString::add(const LChar* c)
112 return StringImpl::empty();
114 return addToStringTable<const LChar*, CStringTranslator>(c);
117 template<typename CharacterType>
118 struct HashTranslatorCharBuffer {
119 const CharacterType* s;
123 typedef HashTranslatorCharBuffer<UChar> UCharBuffer;
124 struct UCharBufferTranslator {
125 static unsigned hash(const UCharBuffer& buf)
127 return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length);
130 static bool equal(StringImpl* const& str, const UCharBuffer& buf)
132 return WTF::equal(str, buf.s, buf.length);
135 static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash)
137 location = StringImpl::create(buf.s, buf.length).leakRef();
138 location->setHash(hash);
139 location->setIsAtomic(true);
143 template<typename CharacterType>
144 struct HashAndCharacters {
146 const CharacterType* characters;
150 template<typename CharacterType>
151 struct HashAndCharactersTranslator {
152 static unsigned hash(const HashAndCharacters<CharacterType>& buffer)
154 ASSERT(buffer.hash == StringHasher::computeHashAndMaskTop8Bits(buffer.characters, buffer.length));
158 static bool equal(StringImpl* const& string, const HashAndCharacters<CharacterType>& buffer)
160 return WTF::equal(string, buffer.characters, buffer.length);
163 static void translate(StringImpl*& location, const HashAndCharacters<CharacterType>& buffer, unsigned hash)
165 location = StringImpl::create(buffer.characters, buffer.length).leakRef();
166 location->setHash(hash);
167 location->setIsAtomic(true);
171 struct HashAndUTF8Characters {
173 const char* characters;
175 unsigned utf16Length;
178 struct HashAndUTF8CharactersTranslator {
179 static unsigned hash(const HashAndUTF8Characters& buffer)
184 static bool equal(StringImpl* const& string, const HashAndUTF8Characters& buffer)
186 if (buffer.utf16Length != string->length())
189 const UChar* stringCharacters = string->characters();
191 // If buffer contains only ASCII characters UTF-8 and UTF16 length are the same.
192 if (buffer.utf16Length != buffer.length)
193 return equalUTF16WithUTF8(stringCharacters, stringCharacters + string->length(), buffer.characters, buffer.characters + buffer.length);
195 for (unsigned i = 0; i < buffer.length; ++i) {
196 ASSERT(isASCII(buffer.characters[i]));
197 if (stringCharacters[i] != buffer.characters[i])
204 static void translate(StringImpl*& location, const HashAndUTF8Characters& buffer, unsigned hash)
207 location = StringImpl::createUninitialized(buffer.utf16Length, target).leakRef();
209 const char* source = buffer.characters;
210 if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target + buffer.utf16Length) != conversionOK)
211 ASSERT_NOT_REACHED();
213 location->setHash(hash);
214 location->setIsAtomic(true);
218 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length)
224 return StringImpl::empty();
226 UCharBuffer buffer = { s, length };
227 return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
230 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length, unsigned existingHash)
233 ASSERT(existingHash);
236 return StringImpl::empty();
238 HashAndCharacters<UChar> buffer = { existingHash, s, length };
239 return addToStringTable<HashAndCharacters<UChar>, HashAndCharactersTranslator<UChar> >(buffer);
242 PassRefPtr<StringImpl> AtomicString::add(const UChar* s)
248 while (s[length] != UChar(0))
252 return StringImpl::empty();
254 UCharBuffer buffer = { s, length };
255 return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
258 struct SubstringLocation {
259 StringImpl* baseString;
264 struct SubstringTranslator {
265 static unsigned hash(const SubstringLocation& buffer)
267 return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->characters() + buffer.start, buffer.length);
270 static bool equal(StringImpl* const& string, const SubstringLocation& buffer)
272 return WTF::equal(string, buffer.baseString->characters() + buffer.start, buffer.length);
275 static void translate(StringImpl*& location, const SubstringLocation& buffer, unsigned hash)
277 location = StringImpl::create(buffer.baseString, buffer.start, buffer.length).leakRef();
278 location->setHash(hash);
279 location->setIsAtomic(true);
283 PassRefPtr<StringImpl> AtomicString::add(StringImpl* baseString, unsigned start, unsigned length)
288 if (!length || start >= baseString->length())
289 return StringImpl::empty();
291 unsigned maxLength = baseString->length() - start;
292 if (length >= maxLength) {
294 return add(baseString);
298 SubstringLocation buffer = { baseString, start, length };
299 return addToStringTable<SubstringLocation, SubstringTranslator>(buffer);
302 typedef HashTranslatorCharBuffer<LChar> LCharBuffer;
303 struct LCharBufferTranslator {
304 static unsigned hash(const LCharBuffer& buf)
306 return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length);
309 static bool equal(StringImpl* const& str, const LCharBuffer& buf)
311 return WTF::equal(str, buf.s, buf.length);
314 static void translate(StringImpl*& location, const LCharBuffer& buf, unsigned hash)
316 location = StringImpl::create(buf.s, buf.length).leakRef();
317 location->setHash(hash);
318 location->setIsAtomic(true);
322 typedef HashTranslatorCharBuffer<char> CharBuffer;
323 struct CharBufferFromLiteralDataTranslator {
324 static unsigned hash(const CharBuffer& buf)
326 return StringHasher::computeHashAndMaskTop8Bits(reinterpret_cast<const LChar*>(buf.s), buf.length);
329 static bool equal(StringImpl* const& str, const CharBuffer& buf)
331 return WTF::equal(str, buf.s, buf.length);
334 static void translate(StringImpl*& location, const CharBuffer& buf, unsigned hash)
336 location = StringImpl::createFromLiteral(buf.s, buf.length).leakRef();
337 location->setHash(hash);
338 location->setIsAtomic(true);
342 PassRefPtr<StringImpl> AtomicString::add(const LChar* s, unsigned length)
348 return StringImpl::empty();
350 LCharBuffer buffer = { s, length };
351 return addToStringTable<LCharBuffer, LCharBufferTranslator>(buffer);
354 PassRefPtr<StringImpl> AtomicString::addFromLiteralData(const char* characters, unsigned length)
359 CharBuffer buffer = { characters, length };
360 return addToStringTable<CharBuffer, CharBufferFromLiteralDataTranslator>(buffer);
363 PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* r)
366 return StringImpl::empty();
368 StringImpl* result = *stringTable().add(r).iterator;
370 r->setIsAtomic(true);
374 template<typename CharacterType>
375 static inline HashSet<StringImpl*>::iterator findString(const StringImpl* stringImpl)
377 HashAndCharacters<CharacterType> buffer = { stringImpl->existingHash(), stringImpl->getCharacters<CharacterType>(), stringImpl->length() };
378 return stringTable().find<HashAndCharacters<CharacterType>, HashAndCharactersTranslator<CharacterType> >(buffer);
381 AtomicStringImpl* AtomicString::find(const StringImpl* stringImpl)
384 ASSERT(stringImpl->existingHash());
386 if (!stringImpl->length())
387 return static_cast<AtomicStringImpl*>(StringImpl::empty());
389 HashSet<StringImpl*>::iterator iterator;
390 if (stringImpl->is8Bit())
391 iterator = findString<LChar>(stringImpl);
393 iterator = findString<UChar>(stringImpl);
394 if (iterator == stringTable().end())
396 return static_cast<AtomicStringImpl*>(*iterator);
399 void AtomicString::remove(StringImpl* r)
401 stringTable().remove(r);
404 AtomicString AtomicString::lower() const
406 // Note: This is a hot function in the Dromaeo benchmark.
407 StringImpl* impl = this->impl();
410 RefPtr<StringImpl> newImpl = impl->lower();
411 if (LIKELY(newImpl == impl))
413 return AtomicString(newImpl);
416 AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, const char* charactersEnd)
418 HashAndUTF8Characters buffer;
419 buffer.characters = charactersStart;
420 buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits(charactersStart, charactersEnd, buffer.length, buffer.utf16Length);
425 AtomicString atomicString;
426 atomicString.m_string = addToStringTable<HashAndUTF8Characters, HashAndUTF8CharactersTranslator>(buffer);
431 void AtomicString::show() const