+++ /dev/null
-/*
- * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#include "config.h"
-
-#include "AtomicString.h"
-
-#include "StringHash.h"
-#include <wtf/HashSet.h>
-#include <wtf/Threading.h>
-#include <wtf/WTFThreadData.h>
-#include <wtf/unicode/UTF8.h>
-
-#include <libkern/OSAtomic.h>
-
-namespace WTF {
-
-using namespace Unicode;
-
-COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);
-
-class AtomicStringTableLocker {
- static OSSpinLock s_stringTableLock;
-public:
- AtomicStringTableLocker() { OSSpinLockLock(&s_stringTableLock); }
- ~AtomicStringTableLocker() { OSSpinLockUnlock(&s_stringTableLock); }
-};
-OSSpinLock AtomicStringTableLocker::s_stringTableLock = OS_SPINLOCK_INIT;
-
-class AtomicStringTable {
-public:
- static AtomicStringTable* create()
- {
- static AtomicStringTable* sharedStringTable = new AtomicStringTable;
-
- WTFThreadData& data = wtfThreadData();
- if (pthread_main_np() || WTF::isWebThread())
- data.m_atomicStringTable = sharedStringTable;
- else
- data.m_atomicStringTable = new AtomicStringTable;
-
- // The AtomicStringTable is shared between the main UI thread and the
- // WebThread. We do the following so that its destruction happens only
- // once - on the main UI thread.
- if (!WTF::isWebThread())
- data.m_atomicStringTableDestructor = AtomicStringTable::destroy;
- return data.m_atomicStringTable;
- }
-
- HashSet<StringImpl*>& table()
- {
- return m_table;
- }
-
-private:
- static void destroy(AtomicStringTable* table)
- {
- HashSet<StringImpl*>::iterator end = table->m_table.end();
- for (HashSet<StringImpl*>::iterator iter = table->m_table.begin(); iter != end; ++iter)
- (*iter)->setIsAtomic(false);
- delete table;
- }
-
- HashSet<StringImpl*> m_table;
-};
-
-static inline HashSet<StringImpl*>& stringTable()
-{
- // Once possible we should make this non-lazy (constructed in WTFThreadData's constructor).
- AtomicStringTable* table = wtfThreadData().atomicStringTable();
- if (UNLIKELY(!table))
- table = AtomicStringTable::create();
- return table->table();
-}
-
-template<typename T, typename HashTranslator>
-static inline PassRefPtr<StringImpl> addToStringTable(const T& value)
-{
- pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<T, 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.second ? adoptRef(*addResult.first) : *addResult.first;
-}
-
-struct CStringTranslator {
- static unsigned hash(const char* c)
- {
- return StringHasher::computeHash(c);
- }
-
- static bool equal(StringImpl* r, const char* s)
- {
- int length = r->length();
- const UChar* d = r->characters();
- for (int i = 0; i != length; ++i) {
- unsigned char c = s[i];
- if (d[i] != c)
- return false;
- }
- return !s[length];
- }
-
- static void translate(StringImpl*& location, const char* const& c, unsigned hash)
- {
- location = StringImpl::create(c).leakRef();
- location->setHash(hash);
- location->setIsAtomic(true);
- }
-};
-
-bool operator==(const AtomicString& a, const char* b)
-{
- StringImpl* impl = a.impl();
- if ((!impl || !impl->characters()) && !b)
- return true;
- if ((!impl || !impl->characters()) || !b)
- return false;
- return CStringTranslator::equal(impl, b);
-}
-
-PassRefPtr<StringImpl> AtomicString::add(const char* c)
-{
- if (!c)
- return 0;
- if (!*c)
- return StringImpl::empty();
-
- AtomicStringTableLocker locker;
- return addToStringTable<const char*, CStringTranslator>(c);
-}
-
-struct UCharBuffer {
- const UChar* s;
- unsigned length;
-};
-
-static inline bool equal(StringImpl* string, const UChar* characters, unsigned length)
-{
- if (string->length() != length)
- return false;
-
- // FIXME: perhaps we should have a more abstract macro that indicates when
- // going 4 bytes at a time is unsafe
-#if CPU(ARM) || CPU(SH4) || CPU(MIPS) || CPU(SPARC)
- const UChar* stringCharacters = string->characters();
- for (unsigned i = 0; i != length; ++i) {
- if (*stringCharacters++ != *characters++)
- return false;
- }
- return true;
-#else
- /* Do it 4-bytes-at-a-time on architectures where it's safe */
-
- const uint32_t* stringCharacters = reinterpret_cast<const uint32_t*>(string->characters());
- const uint32_t* bufferCharacters = reinterpret_cast<const uint32_t*>(characters);
-
- unsigned halfLength = length >> 1;
- for (unsigned i = 0; i != halfLength; ++i) {
- if (*stringCharacters++ != *bufferCharacters++)
- return false;
- }
-
- if (length & 1 && *reinterpret_cast<const uint16_t*>(stringCharacters) != *reinterpret_cast<const uint16_t*>(bufferCharacters))
- return false;
-
- return true;
-#endif
-}
-
-bool operator==(const AtomicString& string, const Vector<UChar>& vector)
-{
- return string.impl() && equal(string.impl(), vector.data(), vector.size());
-}
-
-struct UCharBufferTranslator {
- static unsigned hash(const UCharBuffer& buf)
- {
- return StringHasher::computeHash(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::create(buf.s, buf.length).leakRef();
- location->setHash(hash);
- location->setIsAtomic(true);
- }
-};
-
-struct HashAndCharacters {
- unsigned hash;
- const UChar* characters;
- unsigned length;
-};
-
-struct HashAndCharactersTranslator {
- static unsigned hash(const HashAndCharacters& buffer)
- {
- ASSERT(buffer.hash == StringHasher::computeHash(buffer.characters, buffer.length));
- return buffer.hash;
- }
-
- static bool equal(StringImpl* const& string, const HashAndCharacters& buffer)
- {
- return WTF::equal(string, buffer.characters, buffer.length);
- }
-
- static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash)
- {
- location = StringImpl::create(buffer.characters, buffer.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;
-
- const UChar* stringCharacters = string->characters();
-
- // If buffer contains only ASCII characters UTF-8 and UTF16 length are the same.
- if (buffer.utf16Length != buffer.length)
- return equalUTF16WithUTF8(stringCharacters, stringCharacters + string->length(), buffer.characters, buffer.characters + buffer.length);
-
- for (unsigned i = 0; i < buffer.length; ++i) {
- ASSERT(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;
- location = StringImpl::createUninitialized(buffer.utf16Length, target).releaseRef();
-
- const char* source = buffer.characters;
- if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target + buffer.utf16Length) != conversionOK)
- ASSERT_NOT_REACHED();
-
- location->setHash(hash);
- location->setIsAtomic(true);
- }
-};
-
-PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length)
-{
- if (!s)
- return 0;
-
- if (!length)
- return StringImpl::empty();
-
- UCharBuffer buffer = { s, length };
- AtomicStringTableLocker locker;
- return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
-}
-
-PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length, unsigned existingHash)
-{
- ASSERT(s);
- ASSERT(existingHash);
-
- if (!length)
- return StringImpl::empty();
-
- HashAndCharacters buffer = { existingHash, s, length };
- AtomicStringTableLocker locker;
- return addToStringTable<HashAndCharacters, HashAndCharactersTranslator>(buffer);
-}
-
-PassRefPtr<StringImpl> AtomicString::add(const UChar* s)
-{
- if (!s)
- return 0;
-
- int length = 0;
- while (s[length] != UChar(0))
- length++;
-
- if (!length)
- return StringImpl::empty();
-
- UCharBuffer buffer = { s, length };
- AtomicStringTableLocker locker;
- return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
-}
-
-PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* r)
-{
- if (!r || r->isAtomic())
- return r;
-
- if (!r->length())
- return StringImpl::empty();
-
- AtomicStringTableLocker locker;
- StringImpl* result = *stringTable().add(r).first;
- if (result == r)
- r->setIsAtomic(true);
- return result;
-}
-
-AtomicStringImpl* AtomicString::find(const UChar* s, unsigned length, unsigned existingHash)
-{
- ASSERT(s);
- ASSERT(existingHash);
-
- if (!length)
- return static_cast<AtomicStringImpl*>(StringImpl::empty());
-
- HashAndCharacters buffer = { existingHash, s, length };
- AtomicStringTableLocker locker;
- HashSet<StringImpl*>::iterator iterator = stringTable().find<HashAndCharacters, HashAndCharactersTranslator>(buffer);
- if (iterator == stringTable().end())
- return 0;
- return static_cast<AtomicStringImpl*>(*iterator);
-}
-
-void AtomicString::remove(StringImpl* r)
-{
- AtomicStringTableLocker locker;
- stringTable().remove(r);
-}
-
-AtomicString AtomicString::lower() const
-{
- // Note: This is a hot function in the Dromaeo benchmark.
- StringImpl* impl = this->impl();
- if (UNLIKELY(!impl))
- return *this;
- RefPtr<StringImpl> newImpl = impl->lower();
- if (LIKELY(newImpl == impl))
- return *this;
- return AtomicString(newImpl);
-}
-
-AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, const char* charactersEnd)
-{
- HashAndUTF8Characters buffer;
- buffer.characters = charactersStart;
- buffer.hash = calculateStringHashAndLengthFromUTF8(charactersStart, charactersEnd, buffer.length, buffer.utf16Length);
-
- if (!buffer.hash)
- return nullAtom;
-
- AtomicString atomicString;
- AtomicStringTableLocker locker;
- atomicString.m_string = addToStringTable<HashAndUTF8Characters, HashAndUTF8CharactersTranslator>(buffer);
- return atomicString;
-}
-
-} // namespace WTF