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> 37using JSC::Identifier; 38using JSC::UString; 39#endif 40 41namespace WebCore { 42 43static inline HashSet<StringImpl*>& stringTable() 44{ 45 return threadGlobalData().atomicStringTable(); 46} 47 48struct 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 74bool 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 84PassRefPtr<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 96struct UCharBuffer { 97 const UChar* s; 98 unsigned length; 99}; 100 101static 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 134struct 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 153struct HashAndCharacters { 154 unsigned hash; 155 const UChar* characters; 156 unsigned length; 157}; 158 159struct 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 179PassRefPtr<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 195PassRefPtr<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 215PassRefPtr<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 229void AtomicString::remove(StringImpl* r) 230{ 231 stringTable().remove(r); 232} 233 234AtomicString 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) 245PassRefPtr<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 262PassRefPtr<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 279AtomicStringImpl* 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 296AtomicString::operator UString() const 297{ 298 return m_string; 299} 300#endif 301 302DEFINE_GLOBAL(AtomicString, nullAtom) 303DEFINE_GLOBAL(AtomicString, emptyAtom, "") 304DEFINE_GLOBAL(AtomicString, textAtom, "#text") 305DEFINE_GLOBAL(AtomicString, commentAtom, "#comment") 306DEFINE_GLOBAL(AtomicString, starAtom, "*") 307DEFINE_GLOBAL(AtomicString, xmlAtom, "xml") 308DEFINE_GLOBAL(AtomicString, xmlnsAtom, "xmlns") 309 310void 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