Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  *
     19  */
     20 
     21 #include "config.h"
     22 
     23 #ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC
     24 #define ATOMICSTRING_HIDE_GLOBALS 1
     25 #endif
     26 
     27 #include "AtomicString.h"
     28 
     29 #include "StaticConstructors.h"
     30 #include "StringHash.h"
     31 #include "ThreadGlobalData.h"
     32 #include <wtf/Threading.h>
     33 #include <wtf/HashSet.h>
     34 
     35 #if USE(JSC)
     36 #include <runtime/Identifier.h>
     37 using JSC::Identifier;
     38 using JSC::UString;
     39 #endif
     40 
     41 namespace WebCore {
     42 
     43 static inline HashSet<StringImpl*>& stringTable()
     44 {
     45     return threadGlobalData().atomicStringTable();
     46 }
     47 
     48 struct CStringTranslator {
     49     static unsigned hash(const char* c)
     50     {
     51         return StringImpl::computeHash(c);
     52     }
     53 
     54     static bool equal(StringImpl* r, const char* s)
     55     {
     56         int length = r->length();
     57         const UChar* d = r->characters();
     58         for (int i = 0; i != length; ++i) {
     59             unsigned char c = s[i];
     60             if (d[i] != c)
     61                 return false;
     62         }
     63         return s[length] == 0;
     64     }
     65 
     66     static void translate(StringImpl*& location, const char* const& c, unsigned hash)
     67     {
     68         location = StringImpl::create(c).releaseRef();
     69         location->setHash(hash);
     70         location->setInTable();
     71     }
     72 };
     73 
     74 bool operator==(const AtomicString& a, const char* b)
     75 {
     76     StringImpl* impl = a.impl();
     77     if ((!impl || !impl->characters()) && !b)
     78         return true;
     79     if ((!impl || !impl->characters()) || !b)
     80         return false;
     81     return CStringTranslator::equal(impl, b);
     82 }
     83 
     84 PassRefPtr<StringImpl> AtomicString::add(const char* c)
     85 {
     86     if (!c)
     87         return 0;
     88     if (!*c)
     89         return StringImpl::empty();
     90     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<const char*, CStringTranslator>(c);
     91     if (!addResult.second)
     92         return *addResult.first;
     93     return adoptRef(*addResult.first);
     94 }
     95 
     96 struct UCharBuffer {
     97     const UChar* s;
     98     unsigned length;
     99 };
    100 
    101 static inline bool equal(StringImpl* string, const UChar* characters, unsigned length)
    102 {
    103     if (string->length() != length)
    104         return false;
    105 
    106     // FIXME: perhaps we should have a more abstract macro that indicates when
    107     // going 4 bytes at a time is unsafe
    108 #if CPU(ARM) || CPU(SH4)
    109     const UChar* stringCharacters = string->characters();
    110     for (unsigned i = 0; i != length; ++i) {
    111         if (*stringCharacters++ != *characters++)
    112             return false;
    113     }
    114     return true;
    115 #else
    116     /* Do it 4-bytes-at-a-time on architectures where it's safe */
    117 
    118     const uint32_t* stringCharacters = reinterpret_cast<const uint32_t*>(string->characters());
    119     const uint32_t* bufferCharacters = reinterpret_cast<const uint32_t*>(characters);
    120 
    121     unsigned halfLength = length >> 1;
    122     for (unsigned i = 0; i != halfLength; ++i) {
    123         if (*stringCharacters++ != *bufferCharacters++)
    124             return false;
    125     }
    126 
    127     if (length & 1 &&  *reinterpret_cast<const uint16_t*>(stringCharacters) != *reinterpret_cast<const uint16_t*>(bufferCharacters))
    128         return false;
    129 
    130     return true;
    131 #endif
    132 }
    133 
    134 struct UCharBufferTranslator {
    135     static unsigned hash(const UCharBuffer& buf)
    136     {
    137         return StringImpl::computeHash(buf.s, buf.length);
    138     }
    139 
    140     static bool equal(StringImpl* const& str, const UCharBuffer& buf)
    141     {
    142         return WebCore::equal(str, buf.s, buf.length);
    143     }
    144 
    145     static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash)
    146     {
    147         location = StringImpl::create(buf.s, buf.length).releaseRef();
    148         location->setHash(hash);
    149         location->setInTable();
    150     }
    151 };
    152 
    153 struct HashAndCharacters {
    154     unsigned hash;
    155     const UChar* characters;
    156     unsigned length;
    157 };
    158 
    159 struct HashAndCharactersTranslator {
    160     static unsigned hash(const HashAndCharacters& buffer)
    161     {
    162         ASSERT(buffer.hash == StringImpl::computeHash(buffer.characters, buffer.length));
    163         return buffer.hash;
    164     }
    165 
    166     static bool equal(StringImpl* const& string, const HashAndCharacters& buffer)
    167     {
    168         return WebCore::equal(string, buffer.characters, buffer.length);
    169     }
    170 
    171     static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash)
    172     {
    173         location = StringImpl::create(buffer.characters, buffer.length).releaseRef();
    174         location->setHash(hash);
    175         location->setInTable();
    176     }
    177 };
    178 
    179 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, int length)
    180 {
    181     if (!s)
    182         return 0;
    183 
    184     if (length == 0)
    185         return StringImpl::empty();
    186 
    187     UCharBuffer buf = { s, length };
    188     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
    189 
    190     // If the string is newly-translated, then we need to adopt it.
    191     // The boolean in the pair tells us if that is so.
    192     return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
    193 }
    194 
    195 PassRefPtr<StringImpl> AtomicString::add(const UChar* s)
    196 {
    197     if (!s)
    198         return 0;
    199 
    200     int length = 0;
    201     while (s[length] != UChar(0))
    202         length++;
    203 
    204     if (length == 0)
    205         return StringImpl::empty();
    206 
    207     UCharBuffer buf = {s, length};
    208     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
    209 
    210     // If the string is newly-translated, then we need to adopt it.
    211     // The boolean in the pair tells us if that is so.
    212     return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
    213 }
    214 
    215 PassRefPtr<StringImpl> AtomicString::add(StringImpl* r)
    216 {
    217     if (!r || r->inTable())
    218         return r;
    219 
    220     if (r->length() == 0)
    221         return StringImpl::empty();
    222 
    223     StringImpl* result = *stringTable().add(r).first;
    224     if (result == r)
    225         r->setInTable();
    226     return result;
    227 }
    228 
    229 void AtomicString::remove(StringImpl* r)
    230 {
    231     stringTable().remove(r);
    232 }
    233 
    234 AtomicString AtomicString::lower() const
    235 {
    236     // Note: This is a hot function in the Dromaeo benchmark.
    237     StringImpl* impl = this->impl();
    238     RefPtr<StringImpl> newImpl = impl->lower();
    239     if (LIKELY(newImpl == impl))
    240         return *this;
    241     return AtomicString(newImpl);
    242 }
    243 
    244 #if USE(JSC)
    245 PassRefPtr<StringImpl> AtomicString::add(const JSC::Identifier& identifier)
    246 {
    247     if (identifier.isNull())
    248         return 0;
    249 
    250     UString::Rep* string = identifier.ustring().rep();
    251     unsigned length = string->size();
    252     if (!length)
    253         return StringImpl::empty();
    254 
    255     HashAndCharacters buffer = { string->existingHash(), string->data(), length };
    256     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<HashAndCharacters, HashAndCharactersTranslator>(buffer);
    257     if (!addResult.second)
    258         return *addResult.first;
    259     return adoptRef(*addResult.first);
    260 }
    261 
    262 PassRefPtr<StringImpl> AtomicString::add(const JSC::UString& ustring)
    263 {
    264     if (ustring.isNull())
    265         return 0;
    266 
    267     UString::Rep* string = ustring.rep();
    268     unsigned length = string->size();
    269     if (!length)
    270         return StringImpl::empty();
    271 
    272     HashAndCharacters buffer = { string->hash(), string->data(), length };
    273     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<HashAndCharacters, HashAndCharactersTranslator>(buffer);
    274     if (!addResult.second)
    275         return *addResult.first;
    276     return adoptRef(*addResult.first);
    277 }
    278 
    279 AtomicStringImpl* AtomicString::find(const JSC::Identifier& identifier)
    280 {
    281     if (identifier.isNull())
    282         return 0;
    283 
    284     UString::Rep* string = identifier.ustring().rep();
    285     unsigned length = string->size();
    286     if (!length)
    287         return static_cast<AtomicStringImpl*>(StringImpl::empty());
    288 
    289     HashAndCharacters buffer = { string->existingHash(), string->data(), length };
    290     HashSet<StringImpl*>::iterator iterator = stringTable().find<HashAndCharacters, HashAndCharactersTranslator>(buffer);
    291     if (iterator == stringTable().end())
    292         return 0;
    293     return static_cast<AtomicStringImpl*>(*iterator);
    294 }
    295 
    296 AtomicString::operator UString() const
    297 {
    298     return m_string;
    299 }
    300 #endif
    301 
    302 DEFINE_GLOBAL(AtomicString, nullAtom)
    303 DEFINE_GLOBAL(AtomicString, emptyAtom, "")
    304 DEFINE_GLOBAL(AtomicString, textAtom, "#text")
    305 DEFINE_GLOBAL(AtomicString, commentAtom, "#comment")
    306 DEFINE_GLOBAL(AtomicString, starAtom, "*")
    307 DEFINE_GLOBAL(AtomicString, xmlAtom, "xml")
    308 DEFINE_GLOBAL(AtomicString, xmlnsAtom, "xmlns")
    309 
    310 void AtomicString::init()
    311 {
    312     static bool initialized;
    313     if (!initialized) {
    314         // Initialization is not thread safe, so this function must be called from the main thread first.
    315         ASSERT(isMainThread());
    316 
    317         // Use placement new to initialize the globals.
    318         new ((void*)&nullAtom) AtomicString;
    319         new ((void*)&emptyAtom) AtomicString("");
    320         new ((void*)&textAtom) AtomicString("#text");
    321         new ((void*)&commentAtom) AtomicString("#comment");
    322         new ((void*)&starAtom) AtomicString("*");
    323         new ((void*)&xmlAtom) AtomicString("xml");
    324         new ((void*)&xmlnsAtom) AtomicString("xmlns");
    325 
    326         initialized = true;
    327     }
    328 }
    329 
    330 }
    331