Remove all uses of PassRefPtr in WTF
[WebKit-https.git] / Source / WTF / wtf / text / AtomicStringImpl.cpp
1 /*
2  * Copyright (C) 2004-2008, 2013-2014 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
4  * Copyright (C) 2012 Google Inc. All rights reserved.
5  * Copyright (C) 2015 Yusuke Suzuki<utatane.tea@gmail.com>. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "AtomicStringImpl.h"
26
27 #include "AtomicStringTable.h"
28 #include "HashSet.h"
29 #include "IntegerToStringConversion.h"
30 #include "StringHash.h"
31 #include "Threading.h"
32 #include "WTFThreadData.h"
33 #include <wtf/unicode/UTF8.h>
34
35 #if USE(WEB_THREAD)
36 #include "Lock.h"
37 #endif
38
39 namespace WTF {
40
41 using namespace Unicode;
42
43 #if USE(WEB_THREAD)
44
45 class AtomicStringTableLocker : public LockHolder {
46     WTF_MAKE_NONCOPYABLE(AtomicStringTableLocker);
47
48     static StaticLock s_stringTableLock;
49 public:
50     AtomicStringTableLocker()
51         : LockHolder(&s_stringTableLock)
52     {
53     }
54 };
55
56 StaticLock AtomicStringTableLocker::s_stringTableLock;
57
58 #else
59
60 class AtomicStringTableLocker {
61     WTF_MAKE_NONCOPYABLE(AtomicStringTableLocker);
62 public:
63     AtomicStringTableLocker() { }
64 };
65
66 #endif // USE(WEB_THREAD)
67
68 static ALWAYS_INLINE HashSet<StringImpl*>& stringTable()
69 {
70     return wtfThreadData().atomicStringTable()->table();
71 }
72
73 template<typename T, typename HashTranslator>
74 static inline Ref<AtomicStringImpl> addToStringTable(const T& value)
75 {
76     AtomicStringTableLocker locker;
77
78     HashSet<StringImpl*>::AddResult addResult = stringTable().add<HashTranslator>(value);
79
80     // If the string is newly-translated, then we need to adopt it.
81     // The boolean in the pair tells us if that is so.
82     if (addResult.isNewEntry)
83         return adoptRef(static_cast<AtomicStringImpl&>(**addResult.iterator));
84     return *static_cast<AtomicStringImpl*>(*addResult.iterator);
85 }
86
87 struct CStringTranslator {
88     static unsigned hash(const LChar* c)
89     {
90         return StringHasher::computeHashAndMaskTop8Bits(c);
91     }
92
93     static inline bool equal(StringImpl* r, const LChar* s)
94     {
95         return WTF::equal(r, s);
96     }
97
98     static void translate(StringImpl*& location, const LChar* const& c, unsigned hash)
99     {
100         location = &StringImpl::create(c).leakRef();
101         location->setHash(hash);
102         location->setIsAtomic(true);
103     }
104 };
105
106 RefPtr<AtomicStringImpl> AtomicStringImpl::add(const LChar* c)
107 {
108     if (!c)
109         return nullptr;
110     if (!*c)
111         return static_cast<AtomicStringImpl*>(StringImpl::empty());
112
113     return addToStringTable<const LChar*, CStringTranslator>(c);
114 }
115
116 template<typename CharacterType>
117 struct HashTranslatorCharBuffer {
118     const CharacterType* s;
119     unsigned length;
120 };
121
122 typedef HashTranslatorCharBuffer<UChar> UCharBuffer;
123 struct UCharBufferTranslator {
124     static unsigned hash(const UCharBuffer& buf)
125     {
126         return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length);
127     }
128
129     static bool equal(StringImpl* const& str, const UCharBuffer& buf)
130     {
131         return WTF::equal(str, buf.s, buf.length);
132     }
133
134     static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash)
135     {
136         location = &StringImpl::create8BitIfPossible(buf.s, buf.length).leakRef();
137         location->setHash(hash);
138         location->setIsAtomic(true);
139     }
140 };
141
142 template<typename CharacterType>
143 struct HashAndCharacters {
144     unsigned hash;
145     const CharacterType* characters;
146     unsigned length;
147 };
148
149 template<typename CharacterType>
150 struct HashAndCharactersTranslator {
151     static unsigned hash(const HashAndCharacters<CharacterType>& buffer)
152     {
153         ASSERT(buffer.hash == StringHasher::computeHashAndMaskTop8Bits(buffer.characters, buffer.length));
154         return buffer.hash;
155     }
156
157     static bool equal(StringImpl* const& string, const HashAndCharacters<CharacterType>& buffer)
158     {
159         return WTF::equal(string, buffer.characters, buffer.length);
160     }
161
162     static void translate(StringImpl*& location, const HashAndCharacters<CharacterType>& buffer, unsigned hash)
163     {
164         location = &StringImpl::create(buffer.characters, buffer.length).leakRef();
165         location->setHash(hash);
166         location->setIsAtomic(true);
167     }
168 };
169
170 struct HashAndUTF8Characters {
171     unsigned hash;
172     const char* characters;
173     unsigned length;
174     unsigned utf16Length;
175 };
176
177 struct HashAndUTF8CharactersTranslator {
178     static unsigned hash(const HashAndUTF8Characters& buffer)
179     {
180         return buffer.hash;
181     }
182
183     static bool equal(StringImpl* const& string, const HashAndUTF8Characters& buffer)
184     {
185         if (buffer.utf16Length != string->length())
186             return false;
187
188         // If buffer contains only ASCII characters UTF-8 and UTF16 length are the same.
189         if (buffer.utf16Length != buffer.length) {
190             if (string->is8Bit())
191                 return equalLatin1WithUTF8(string->characters8(), buffer.characters, buffer.characters + buffer.length);
192
193             return equalUTF16WithUTF8(string->characters16(), buffer.characters, buffer.characters + buffer.length);
194         }
195
196         if (string->is8Bit()) {
197             const LChar* stringCharacters = string->characters8();
198
199             for (unsigned i = 0; i < buffer.length; ++i) {
200                 ASSERT(isASCII(buffer.characters[i]));
201                 if (stringCharacters[i] != buffer.characters[i])
202                     return false;
203             }
204
205             return true;
206         }
207
208         const UChar* stringCharacters = string->characters16();
209
210         for (unsigned i = 0; i < buffer.length; ++i) {
211             ASSERT(isASCII(buffer.characters[i]));
212             if (stringCharacters[i] != buffer.characters[i])
213                 return false;
214         }
215
216         return true;
217     }
218
219     static void translate(StringImpl*& location, const HashAndUTF8Characters& buffer, unsigned hash)
220     {
221         UChar* target;
222         auto newString = StringImpl::createUninitialized(buffer.utf16Length, target);
223
224         bool isAllASCII;
225         const char* source = buffer.characters;
226         if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target + buffer.utf16Length, &isAllASCII) != conversionOK)
227             ASSERT_NOT_REACHED();
228
229         if (isAllASCII)
230             newString = StringImpl::create(buffer.characters, buffer.length);
231
232         location = &newString.leakRef();
233         location->setHash(hash);
234         location->setIsAtomic(true);
235     }
236 };
237
238 RefPtr<AtomicStringImpl> AtomicStringImpl::add(const UChar* s, unsigned length)
239 {
240     if (!s)
241         return nullptr;
242
243     if (!length)
244         return static_cast<AtomicStringImpl*>(StringImpl::empty());
245
246     UCharBuffer buffer = { s, length };
247     return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
248 }
249
250 Ref<AtomicStringImpl> AtomicStringImpl::add(const UChar* s, unsigned length, unsigned existingHash)
251 {
252     ASSERT(s);
253     ASSERT(existingHash);
254
255     if (!length)
256         return *static_cast<AtomicStringImpl*>(StringImpl::empty());
257
258     HashAndCharacters<UChar> buffer = { existingHash, s, length };
259     return addToStringTable<HashAndCharacters<UChar>, HashAndCharactersTranslator<UChar>>(buffer);
260 }
261
262 RefPtr<AtomicStringImpl> AtomicStringImpl::add(const UChar* s)
263 {
264     if (!s)
265         return nullptr;
266
267     unsigned length = 0;
268     while (s[length] != UChar(0))
269         ++length;
270
271     if (!length)
272         return static_cast<AtomicStringImpl*>(StringImpl::empty());
273
274     UCharBuffer buffer = { s, length };
275     return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
276 }
277
278 struct SubstringLocation {
279     StringImpl* baseString;
280     unsigned start;
281     unsigned length;
282 };
283
284 struct SubstringTranslator {
285     static void translate(StringImpl*& location, const SubstringLocation& buffer, unsigned hash)
286     {
287         location = &StringImpl::createSubstringSharingImpl(*buffer.baseString, buffer.start, buffer.length).leakRef();
288         location->setHash(hash);
289         location->setIsAtomic(true);
290     }
291 };
292
293 struct SubstringTranslator8 : SubstringTranslator {
294     static unsigned hash(const SubstringLocation& buffer)
295     {
296         return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->characters8() + buffer.start, buffer.length);
297     }
298
299     static bool equal(StringImpl* const& string, const SubstringLocation& buffer)
300     {
301         return WTF::equal(string, buffer.baseString->characters8() + buffer.start, buffer.length);
302     }
303 };
304
305 struct SubstringTranslator16 : SubstringTranslator {
306     static unsigned hash(const SubstringLocation& buffer)
307     {
308         return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->characters16() + buffer.start, buffer.length);
309     }
310
311     static bool equal(StringImpl* const& string, const SubstringLocation& buffer)
312     {
313         return WTF::equal(string, buffer.baseString->characters16() + buffer.start, buffer.length);
314     }
315 };
316
317 RefPtr<AtomicStringImpl> AtomicStringImpl::add(StringImpl* baseString, unsigned start, unsigned length)
318 {
319     if (!baseString)
320         return nullptr;
321
322     if (!length || start >= baseString->length())
323         return static_cast<AtomicStringImpl*>(StringImpl::empty());
324
325     unsigned maxLength = baseString->length() - start;
326     if (length >= maxLength) {
327         if (!start)
328             return add(baseString);
329         length = maxLength;
330     }
331
332     SubstringLocation buffer = { baseString, start, length };
333     if (baseString->is8Bit())
334         return addToStringTable<SubstringLocation, SubstringTranslator8>(buffer);
335     return addToStringTable<SubstringLocation, SubstringTranslator16>(buffer);
336 }
337     
338 typedef HashTranslatorCharBuffer<LChar> LCharBuffer;
339 struct LCharBufferTranslator {
340     static unsigned hash(const LCharBuffer& buf)
341     {
342         return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length);
343     }
344
345     static bool equal(StringImpl* const& str, const LCharBuffer& buf)
346     {
347         return WTF::equal(str, buf.s, buf.length);
348     }
349
350     static void translate(StringImpl*& location, const LCharBuffer& buf, unsigned hash)
351     {
352         location = &StringImpl::create(buf.s, buf.length).leakRef();
353         location->setHash(hash);
354         location->setIsAtomic(true);
355     }
356 };
357
358 typedef HashTranslatorCharBuffer<char> CharBuffer;
359 struct CharBufferFromLiteralDataTranslator {
360     static unsigned hash(const CharBuffer& buf)
361     {
362         return StringHasher::computeHashAndMaskTop8Bits(reinterpret_cast<const LChar*>(buf.s), buf.length);
363     }
364
365     static bool equal(StringImpl* const& str, const CharBuffer& buf)
366     {
367         return WTF::equal(str, buf.s, buf.length);
368     }
369
370     static void translate(StringImpl*& location, const CharBuffer& buf, unsigned hash)
371     {
372         location = &StringImpl::createFromLiteral(buf.s, buf.length).leakRef();
373         location->setHash(hash);
374         location->setIsAtomic(true);
375     }
376 };
377
378 RefPtr<AtomicStringImpl> AtomicStringImpl::add(const LChar* s, unsigned length)
379 {
380     if (!s)
381         return nullptr;
382
383     if (!length)
384         return static_cast<AtomicStringImpl*>(StringImpl::empty());
385
386     LCharBuffer buffer = { s, length };
387     return addToStringTable<LCharBuffer, LCharBufferTranslator>(buffer);
388 }
389
390 Ref<AtomicStringImpl> AtomicStringImpl::addLiteral(const char* characters, unsigned length)
391 {
392     ASSERT(characters);
393     ASSERT(length);
394
395     CharBuffer buffer = { characters, length };
396     return addToStringTable<CharBuffer, CharBufferFromLiteralDataTranslator>(buffer);
397 }
398
399 Ref<AtomicStringImpl> AtomicStringImpl::addSlowCase(StringImpl& string)
400 {
401     if (!string.length())
402         return *static_cast<AtomicStringImpl*>(StringImpl::empty());
403
404     if (string.isSymbol()) {
405         if (string.is8Bit())
406             return *add(string.characters8(), string.length());
407         return *add(string.characters16(), string.length());
408     }
409
410     ASSERT_WITH_MESSAGE(!string.isAtomic(), "AtomicStringImpl should not hit the slow case if the string is already atomic.");
411
412     AtomicStringTableLocker locker;
413     auto addResult = stringTable().add(&string);
414
415     if (addResult.isNewEntry) {
416         ASSERT(*addResult.iterator == &string);
417         string.setIsAtomic(true);
418     }
419
420     return *static_cast<AtomicStringImpl*>(*addResult.iterator);
421 }
422
423 Ref<AtomicStringImpl> AtomicStringImpl::addSlowCase(AtomicStringTable& stringTable, StringImpl& string)
424 {
425     if (!string.length())
426         return *static_cast<AtomicStringImpl*>(StringImpl::empty());
427
428     if (string.isSymbol()) {
429         if (string.is8Bit())
430             return *add(string.characters8(), string.length());
431         return *add(string.characters16(), string.length());
432     }
433
434     ASSERT_WITH_MESSAGE(!string.isAtomic(), "AtomicStringImpl should not hit the slow case if the string is already atomic.");
435
436     AtomicStringTableLocker locker;
437     auto addResult = stringTable.table().add(&string);
438
439     if (addResult.isNewEntry) {
440         ASSERT(*addResult.iterator == &string);
441         string.setIsAtomic(true);
442     }
443
444     return *static_cast<AtomicStringImpl*>(*addResult.iterator);
445 }
446
447 void AtomicStringImpl::remove(AtomicStringImpl* string)
448 {
449     ASSERT(string->isAtomic());
450     AtomicStringTableLocker locker;
451     HashSet<StringImpl*>& atomicStringTable = stringTable();
452     HashSet<StringImpl*>::iterator iterator = atomicStringTable.find(string);
453     ASSERT_WITH_MESSAGE(iterator != atomicStringTable.end(), "The string being removed is atomic in the string table of an other thread!");
454     atomicStringTable.remove(iterator);
455 }
456
457 RefPtr<AtomicStringImpl> AtomicStringImpl::lookUpSlowCase(StringImpl& string)
458 {
459     ASSERT_WITH_MESSAGE(!string.isAtomic(), "AtomicStringImpls should return from the fast case.");
460
461     if (!string.length())
462         return static_cast<AtomicStringImpl*>(StringImpl::empty());
463
464     if (string.isSymbol()) {
465         if (string.is8Bit())
466             return lookUpInternal(string.characters8(), string.length());
467         return lookUpInternal(string.characters16(), string.length());
468     }
469
470     AtomicStringTableLocker locker;
471     HashSet<StringImpl*>& atomicStringTable = stringTable();
472     auto iterator = atomicStringTable.find(&string);
473     if (iterator != atomicStringTable.end())
474         return static_cast<AtomicStringImpl*>(*iterator);
475     return nullptr;
476 }
477
478 RefPtr<AtomicStringImpl> AtomicStringImpl::addUTF8(const char* charactersStart, const char* charactersEnd)
479 {
480     HashAndUTF8Characters buffer;
481     buffer.characters = charactersStart;
482     buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits(charactersStart, charactersEnd, buffer.length, buffer.utf16Length);
483
484     if (!buffer.hash)
485         return nullptr;
486
487     return addToStringTable<HashAndUTF8Characters, HashAndUTF8CharactersTranslator>(buffer);
488 }
489
490 RefPtr<AtomicStringImpl> AtomicStringImpl::lookUpInternal(const LChar* characters, unsigned length)
491 {
492     AtomicStringTableLocker locker;
493     auto& table = stringTable();
494
495     LCharBuffer buffer = { characters, length };
496     auto iterator = table.find<LCharBufferTranslator>(buffer);
497     if (iterator != table.end())
498         return static_cast<AtomicStringImpl*>(*iterator);
499     return nullptr;
500 }
501
502 RefPtr<AtomicStringImpl> AtomicStringImpl::lookUpInternal(const UChar* characters, unsigned length)
503 {
504     AtomicStringTableLocker locker;
505     auto& table = stringTable();
506
507     UCharBuffer buffer = { characters, length };
508     auto iterator = table.find<UCharBufferTranslator>(buffer);
509     if (iterator != table.end())
510         return static_cast<AtomicStringImpl*>(*iterator);
511     return nullptr;
512 }
513
514 #if !ASSERT_DISABLED
515 bool AtomicStringImpl::isInAtomicStringTable(StringImpl* string)
516 {
517     AtomicStringTableLocker locker;
518     return stringTable().contains(string);
519 }
520 #endif
521
522 } // namespace WTF