| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserv
ed. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserv
ed. |
| 3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> | 3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> |
| 4 * Copyright (C) 2012 Google Inc. All rights reserved. | 4 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 5 * | 5 * |
| 6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
| 8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
| 9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
| 10 * | 10 * |
| 11 * This library is distributed in the hope that it will be useful, | 11 * This library is distributed in the hope that it will be useful, |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 * Library General Public License for more details. | 14 * Library General Public License for more details. |
| 15 * | 15 * |
| 16 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 19 * Boston, MA 02110-1301, USA. | 19 * Boston, MA 02110-1301, USA. |
| 20 * | 20 * |
| 21 */ | 21 */ |
| 22 | 22 |
| 23 #include "wtf/text/AtomicString.h" | 23 #include "wtf/text/AtomicString.h" |
| 24 | 24 |
| 25 #include "wtf/HashSet.h" | |
| 26 #include "wtf/WTFThreadData.h" | |
| 27 #include "wtf/dtoa.h" | 25 #include "wtf/dtoa.h" |
| 26 #include "wtf/text/AtomicStringTable.h" |
| 28 #include "wtf/text/IntegerToStringConversion.h" | 27 #include "wtf/text/IntegerToStringConversion.h" |
| 29 #include "wtf/text/StringHash.h" | |
| 30 #include "wtf/text/UTF8.h" | |
| 31 | 28 |
| 32 namespace WTF { | 29 namespace WTF { |
| 33 | 30 |
| 34 using namespace Unicode; | |
| 35 | |
| 36 static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String m
ust be same size"); | 31 static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String m
ust be same size"); |
| 37 | 32 |
| 38 static inline HashSet<StringImpl*>& atomicStrings() | 33 AtomicString::AtomicString(const LChar* chars, unsigned length) |
| 34 : m_string(AtomicStringTable::instance().add(chars, length)) {} |
| 35 |
| 36 AtomicString::AtomicString(const UChar* chars, unsigned length) |
| 37 : m_string(AtomicStringTable::instance().add(chars, length)) {} |
| 38 |
| 39 AtomicString::AtomicString(const UChar* chars, unsigned length, unsigned existin
gHash) |
| 40 : m_string(AtomicStringTable::instance().add(chars, length, existingHash)) {
} |
| 41 |
| 42 AtomicString::AtomicString(const UChar* chars) |
| 43 : m_string(AtomicStringTable::instance().add(chars)) {} |
| 44 |
| 45 AtomicString::AtomicString(StringImpl* string, unsigned offset, unsigned length) |
| 46 : m_string(AtomicStringTable::instance().add(string, offset, length)) {} |
| 47 |
| 48 PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* chars) |
| 39 { | 49 { |
| 40 return wtfThreadData().getAtomicStringTable().table(); | 50 DCHECK(!chars->isAtomic()); |
| 51 return AtomicStringTable::instance().add(chars); |
| 41 } | 52 } |
| 42 | 53 |
| 43 void AtomicString::reserveTableCapacity(size_t size) | 54 AtomicString AtomicString::fromUTF8(const char* chars, size_t length) |
| 44 { | 55 { |
| 45 wtfThreadData().getAtomicStringTable().table().reserveCapacityForSize(size); | 56 if (!chars) |
| 57 return nullAtom; |
| 58 if (!length) |
| 59 return emptyAtom; |
| 60 return AtomicString(AtomicStringTable::instance().addUTF8(chars, chars + len
gth)); |
| 46 } | 61 } |
| 47 | 62 |
| 48 template<typename T, typename HashTranslator> | 63 AtomicString AtomicString::fromUTF8(const char* chars) |
| 49 static inline PassRefPtr<StringImpl> addToStringTable(const T& value) | |
| 50 { | 64 { |
| 51 HashSet<StringImpl*>::AddResult addResult = atomicStrings().addWithTranslato
r<HashTranslator>(value); | 65 if (!chars) |
| 52 | 66 return nullAtom; |
| 53 // If the string is newly-translated, then we need to adopt it. | 67 if (!*chars) |
| 54 // The boolean in the pair tells us if that is so. | 68 return emptyAtom; |
| 55 return addResult.isNewEntry ? adoptRef(*addResult.storedValue) : *addResult.
storedValue; | 69 return AtomicString(AtomicStringTable::instance().addUTF8(chars, nullptr)); |
| 56 } | |
| 57 | |
| 58 template<typename CharacterType> | |
| 59 struct HashTranslatorCharBuffer { | |
| 60 const CharacterType* s; | |
| 61 unsigned length; | |
| 62 }; | |
| 63 | |
| 64 typedef HashTranslatorCharBuffer<UChar> UCharBuffer; | |
| 65 struct UCharBufferTranslator { | |
| 66 static unsigned hash(const UCharBuffer& buf) | |
| 67 { | |
| 68 return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); | |
| 69 } | |
| 70 | |
| 71 static bool equal(StringImpl* const& str, const UCharBuffer& buf) | |
| 72 { | |
| 73 return WTF::equal(str, buf.s, buf.length); | |
| 74 } | |
| 75 | |
| 76 static void translate(StringImpl*& location, const UCharBuffer& buf, unsigne
d hash) | |
| 77 { | |
| 78 location = StringImpl::create8BitIfPossible(buf.s, buf.length).leakRef()
; | |
| 79 location->setHash(hash); | |
| 80 location->setIsAtomic(true); | |
| 81 } | |
| 82 }; | |
| 83 | |
| 84 template<typename CharacterType> | |
| 85 struct HashAndCharacters { | |
| 86 unsigned hash; | |
| 87 const CharacterType* characters; | |
| 88 unsigned length; | |
| 89 }; | |
| 90 | |
| 91 template<typename CharacterType> | |
| 92 struct HashAndCharactersTranslator { | |
| 93 static unsigned hash(const HashAndCharacters<CharacterType>& buffer) | |
| 94 { | |
| 95 ASSERT(buffer.hash == StringHasher::computeHashAndMaskTop8Bits(buffer.ch
aracters, buffer.length)); | |
| 96 return buffer.hash; | |
| 97 } | |
| 98 | |
| 99 static bool equal(StringImpl* const& string, const HashAndCharacters<Charact
erType>& buffer) | |
| 100 { | |
| 101 return WTF::equal(string, buffer.characters, buffer.length); | |
| 102 } | |
| 103 | |
| 104 static void translate(StringImpl*& location, const HashAndCharacters<Charact
erType>& buffer, unsigned hash) | |
| 105 { | |
| 106 location = StringImpl::create(buffer.characters, buffer.length).leakRef(
); | |
| 107 location->setHash(hash); | |
| 108 location->setIsAtomic(true); | |
| 109 } | |
| 110 }; | |
| 111 | |
| 112 struct HashAndUTF8Characters { | |
| 113 unsigned hash; | |
| 114 const char* characters; | |
| 115 unsigned length; | |
| 116 unsigned utf16Length; | |
| 117 }; | |
| 118 | |
| 119 struct HashAndUTF8CharactersTranslator { | |
| 120 static unsigned hash(const HashAndUTF8Characters& buffer) | |
| 121 { | |
| 122 return buffer.hash; | |
| 123 } | |
| 124 | |
| 125 static bool equal(StringImpl* const& string, const HashAndUTF8Characters& bu
ffer) | |
| 126 { | |
| 127 if (buffer.utf16Length != string->length()) | |
| 128 return false; | |
| 129 | |
| 130 // If buffer contains only ASCII characters UTF-8 and UTF16 length are t
he same. | |
| 131 if (buffer.utf16Length != buffer.length) { | |
| 132 if (string->is8Bit()) { | |
| 133 const LChar* characters8 = string->characters8(); | |
| 134 return equalLatin1WithUTF8(characters8, characters8 + string->le
ngth(), buffer.characters, buffer.characters + buffer.length); | |
| 135 } | |
| 136 const UChar* characters16 = string->characters16(); | |
| 137 return equalUTF16WithUTF8(characters16, characters16 + string->lengt
h(), buffer.characters, buffer.characters + buffer.length); | |
| 138 } | |
| 139 | |
| 140 if (string->is8Bit()) { | |
| 141 const LChar* stringCharacters = string->characters8(); | |
| 142 | |
| 143 for (unsigned i = 0; i < buffer.length; ++i) { | |
| 144 ASSERT(isASCII(buffer.characters[i])); | |
| 145 if (stringCharacters[i] != buffer.characters[i]) | |
| 146 return false; | |
| 147 } | |
| 148 | |
| 149 return true; | |
| 150 } | |
| 151 | |
| 152 const UChar* stringCharacters = string->characters16(); | |
| 153 | |
| 154 for (unsigned i = 0; i < buffer.length; ++i) { | |
| 155 ASSERT(isASCII(buffer.characters[i])); | |
| 156 if (stringCharacters[i] != buffer.characters[i]) | |
| 157 return false; | |
| 158 } | |
| 159 | |
| 160 return true; | |
| 161 } | |
| 162 | |
| 163 static void translate(StringImpl*& location, const HashAndUTF8Characters& bu
ffer, unsigned hash) | |
| 164 { | |
| 165 UChar* target; | |
| 166 RefPtr<StringImpl> newString = StringImpl::createUninitialized(buffer.ut
f16Length, target); | |
| 167 | |
| 168 bool isAllASCII; | |
| 169 const char* source = buffer.characters; | |
| 170 if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target
+ buffer.utf16Length, &isAllASCII) != conversionOK) | |
| 171 ASSERT_NOT_REACHED(); | |
| 172 | |
| 173 if (isAllASCII) | |
| 174 newString = StringImpl::create(buffer.characters, buffer.length); | |
| 175 | |
| 176 location = newString.release().leakRef(); | |
| 177 location->setHash(hash); | |
| 178 location->setIsAtomic(true); | |
| 179 } | |
| 180 }; | |
| 181 | |
| 182 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length) | |
| 183 { | |
| 184 if (!s) | |
| 185 return nullptr; | |
| 186 | |
| 187 if (!length) | |
| 188 return StringImpl::empty(); | |
| 189 | |
| 190 UCharBuffer buffer = { s, length }; | |
| 191 return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer); | |
| 192 } | |
| 193 | |
| 194 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length, unsign
ed existingHash) | |
| 195 { | |
| 196 ASSERT(s); | |
| 197 ASSERT(existingHash); | |
| 198 | |
| 199 if (!length) | |
| 200 return StringImpl::empty(); | |
| 201 | |
| 202 HashAndCharacters<UChar> buffer = { existingHash, s, length }; | |
| 203 return addToStringTable<HashAndCharacters<UChar>, HashAndCharactersTranslato
r<UChar>>(buffer); | |
| 204 } | |
| 205 | |
| 206 PassRefPtr<StringImpl> AtomicString::add(const UChar* s) | |
| 207 { | |
| 208 if (!s) | |
| 209 return nullptr; | |
| 210 | |
| 211 unsigned length = 0; | |
| 212 while (s[length] != UChar(0)) | |
| 213 ++length; | |
| 214 | |
| 215 if (!length) | |
| 216 return StringImpl::empty(); | |
| 217 | |
| 218 UCharBuffer buffer = { s, length }; | |
| 219 return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer); | |
| 220 } | |
| 221 | |
| 222 struct SubstringLocation { | |
| 223 StringImpl* baseString; | |
| 224 unsigned start; | |
| 225 unsigned length; | |
| 226 }; | |
| 227 | |
| 228 struct SubstringTranslator { | |
| 229 static unsigned hash(const SubstringLocation& buffer) | |
| 230 { | |
| 231 if (buffer.baseString->is8Bit()) | |
| 232 return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->c
haracters8() + buffer.start, buffer.length); | |
| 233 return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->chara
cters16() + buffer.start, buffer.length); | |
| 234 } | |
| 235 | |
| 236 static bool equal(StringImpl* const& string, const SubstringLocation& buffer
) | |
| 237 { | |
| 238 if (buffer.baseString->is8Bit()) | |
| 239 return WTF::equal(string, buffer.baseString->characters8() + buffer.
start, buffer.length); | |
| 240 return WTF::equal(string, buffer.baseString->characters16() + buffer.sta
rt, buffer.length); | |
| 241 } | |
| 242 | |
| 243 static void translate(StringImpl*& location, const SubstringLocation& buffer
, unsigned hash) | |
| 244 { | |
| 245 location = buffer.baseString->substring(buffer.start, buffer.length).lea
kRef(); | |
| 246 location->setHash(hash); | |
| 247 location->setIsAtomic(true); | |
| 248 } | |
| 249 }; | |
| 250 | |
| 251 PassRefPtr<StringImpl> AtomicString::add(StringImpl* baseString, unsigned start,
unsigned length) | |
| 252 { | |
| 253 if (!baseString) | |
| 254 return nullptr; | |
| 255 | |
| 256 if (!length || start >= baseString->length()) | |
| 257 return StringImpl::empty(); | |
| 258 | |
| 259 unsigned maxLength = baseString->length() - start; | |
| 260 if (length >= maxLength) { | |
| 261 if (!start) | |
| 262 return add(baseString); | |
| 263 length = maxLength; | |
| 264 } | |
| 265 | |
| 266 SubstringLocation buffer = { baseString, start, length }; | |
| 267 return addToStringTable<SubstringLocation, SubstringTranslator>(buffer); | |
| 268 } | |
| 269 | |
| 270 typedef HashTranslatorCharBuffer<LChar> LCharBuffer; | |
| 271 struct LCharBufferTranslator { | |
| 272 static unsigned hash(const LCharBuffer& buf) | |
| 273 { | |
| 274 return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); | |
| 275 } | |
| 276 | |
| 277 static bool equal(StringImpl* const& str, const LCharBuffer& buf) | |
| 278 { | |
| 279 return WTF::equal(str, buf.s, buf.length); | |
| 280 } | |
| 281 | |
| 282 static void translate(StringImpl*& location, const LCharBuffer& buf, unsigne
d hash) | |
| 283 { | |
| 284 location = StringImpl::create(buf.s, buf.length).leakRef(); | |
| 285 location->setHash(hash); | |
| 286 location->setIsAtomic(true); | |
| 287 } | |
| 288 }; | |
| 289 | |
| 290 PassRefPtr<StringImpl> AtomicString::add(const LChar* s, unsigned length) | |
| 291 { | |
| 292 if (!s) | |
| 293 return nullptr; | |
| 294 | |
| 295 if (!length) | |
| 296 return StringImpl::empty(); | |
| 297 | |
| 298 LCharBuffer buffer = { s, length }; | |
| 299 return addToStringTable<LCharBuffer, LCharBufferTranslator>(buffer); | |
| 300 } | |
| 301 | |
| 302 PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* string) | |
| 303 { | |
| 304 return wtfThreadData().getAtomicStringTable().addStringImpl(string); | |
| 305 } | |
| 306 | |
| 307 template<typename CharacterType> | |
| 308 static inline HashSet<StringImpl*>::iterator findString(const StringImpl* string
Impl) | |
| 309 { | |
| 310 HashAndCharacters<CharacterType> buffer = { stringImpl->existingHash(), stri
ngImpl->getCharacters<CharacterType>(), stringImpl->length() }; | |
| 311 return atomicStrings().find<HashAndCharactersTranslator<CharacterType>>(buff
er); | |
| 312 } | |
| 313 | |
| 314 StringImpl* AtomicString::find(const StringImpl* stringImpl) | |
| 315 { | |
| 316 ASSERT(stringImpl); | |
| 317 ASSERT(stringImpl->existingHash()); | |
| 318 | |
| 319 if (!stringImpl->length()) | |
| 320 return StringImpl::empty(); | |
| 321 | |
| 322 HashSet<StringImpl*>::iterator iterator; | |
| 323 if (stringImpl->is8Bit()) | |
| 324 iterator = findString<LChar>(stringImpl); | |
| 325 else | |
| 326 iterator = findString<UChar>(stringImpl); | |
| 327 if (iterator == atomicStrings().end()) | |
| 328 return 0; | |
| 329 return *iterator; | |
| 330 } | |
| 331 | |
| 332 void AtomicString::remove(StringImpl* r) | |
| 333 { | |
| 334 HashSet<StringImpl*>::iterator iterator; | |
| 335 if (r->is8Bit()) | |
| 336 iterator = findString<LChar>(r); | |
| 337 else | |
| 338 iterator = findString<UChar>(r); | |
| 339 RELEASE_ASSERT(iterator != atomicStrings().end()); | |
| 340 atomicStrings().remove(iterator); | |
| 341 } | 70 } |
| 342 | 71 |
| 343 AtomicString AtomicString::lower() const | 72 AtomicString AtomicString::lower() const |
| 344 { | 73 { |
| 345 // Note: This is a hot function in the Dromaeo benchmark. | 74 // Note: This is a hot function in the Dromaeo benchmark. |
| 346 StringImpl* impl = this->impl(); | 75 StringImpl* impl = this->impl(); |
| 347 if (UNLIKELY(!impl)) | 76 if (UNLIKELY(!impl)) |
| 348 return *this; | 77 return *this; |
| 349 RefPtr<StringImpl> newImpl = impl->lower(); | 78 RefPtr<StringImpl> newImpl = impl->lower(); |
| 350 if (LIKELY(newImpl == impl)) | 79 if (LIKELY(newImpl == impl)) |
| 351 return *this; | 80 return *this; |
| 352 return AtomicString(newImpl.release()); | 81 return AtomicString(newImpl.release()); |
| 353 } | 82 } |
| 354 | 83 |
| 355 AtomicString AtomicString::lowerASCII() const | 84 AtomicString AtomicString::lowerASCII() const |
| 356 { | 85 { |
| 357 StringImpl* impl = this->impl(); | 86 StringImpl* impl = this->impl(); |
| 358 if (UNLIKELY(!impl)) | 87 if (UNLIKELY(!impl)) |
| 359 return *this; | 88 return *this; |
| 360 RefPtr<StringImpl> newImpl = impl->lowerASCII(); | 89 RefPtr<StringImpl> newImpl = impl->lowerASCII(); |
| 361 if (LIKELY(newImpl == impl)) | 90 if (LIKELY(newImpl == impl)) |
| 362 return *this; | 91 return *this; |
| 363 return AtomicString(newImpl.release()); | 92 return AtomicString(newImpl.release()); |
| 364 } | 93 } |
| 365 | 94 |
| 366 | |
| 367 AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, const c
har* charactersEnd) | |
| 368 { | |
| 369 HashAndUTF8Characters buffer; | |
| 370 buffer.characters = charactersStart; | |
| 371 buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits(characters
Start, charactersEnd, buffer.length, buffer.utf16Length); | |
| 372 | |
| 373 if (!buffer.hash) | |
| 374 return nullAtom; | |
| 375 | |
| 376 AtomicString atomicString; | |
| 377 atomicString.m_string = addToStringTable<HashAndUTF8Characters, HashAndUTF8C
haractersTranslator>(buffer); | |
| 378 return atomicString; | |
| 379 } | |
| 380 | |
| 381 template<typename IntegerType> | 95 template<typename IntegerType> |
| 382 static AtomicString integerToAtomicString(IntegerType input) | 96 static AtomicString integerToAtomicString(IntegerType input) |
| 383 { | 97 { |
| 384 IntegerToStringConverter<IntegerType> converter(input); | 98 IntegerToStringConverter<IntegerType> converter(input); |
| 385 return AtomicString(converter.characters8(), converter.length()); | 99 return AtomicString(converter.characters8(), converter.length()); |
| 386 } | 100 } |
| 387 | 101 |
| 388 AtomicString AtomicString::number(int number) | 102 AtomicString AtomicString::number(int number) |
| 389 { | 103 { |
| 390 return integerToAtomicString(number); | 104 return integerToAtomicString(number); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 } | 141 } |
| 428 | 142 |
| 429 #ifndef NDEBUG | 143 #ifndef NDEBUG |
| 430 void AtomicString::show() const | 144 void AtomicString::show() const |
| 431 { | 145 { |
| 432 m_string.show(); | 146 m_string.show(); |
| 433 } | 147 } |
| 434 #endif | 148 #endif |
| 435 | 149 |
| 436 } // namespace WTF | 150 } // namespace WTF |
| OLD | NEW |