This broke windows.
[WebKit-https.git] / WebCore / platform / text / AtomicString.cpp
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 namespace WebCore {
36
37 static inline HashSet<StringImpl*>& stringTable()
38 {
39     return threadGlobalData().atomicStringTable();
40 }
41
42 struct CStringTranslator {
43     static unsigned hash(const char* c)
44     {
45         return StringImpl::computeHash(c);
46     }
47
48     static bool equal(StringImpl* r, const char* s)
49     {
50         int length = r->length();
51         const UChar* d = r->characters();
52         for (int i = 0; i != length; ++i) {
53             unsigned char c = s[i];
54             if (d[i] != c)
55                 return false;
56         }
57         return s[length] == 0;
58     }
59
60     static void translate(StringImpl*& location, const char* const& c, unsigned hash)
61     {
62         location = StringImpl::create(c).releaseRef(); 
63         location->setHash(hash);
64         location->setInTable();
65     }
66 };
67
68 bool operator==(const AtomicString& a, const char* b)
69
70     StringImpl* impl = a.impl();
71     if ((!impl || !impl->characters()) && !b)
72         return true;
73     if ((!impl || !impl->characters()) || !b)
74         return false;
75     return CStringTranslator::equal(impl, b); 
76 }
77
78 PassRefPtr<StringImpl> AtomicString::add(const char* c)
79 {
80     if (!c)
81         return 0;
82     if (!*c)
83         return StringImpl::empty();    
84     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<const char*, CStringTranslator>(c);
85     if (!addResult.second)
86         return *addResult.first;
87     return adoptRef(*addResult.first);
88 }
89
90 struct UCharBuffer {
91     const UChar* s;
92     unsigned length;
93 };
94
95 static inline bool equal(StringImpl* string, const UChar* characters, unsigned length)
96 {
97     if (string->length() != length)
98         return false;
99
100     // FIXME: perhaps we should have a more abstract macro that indicates when
101     // going 4 bytes at a time is unsafe
102 #if CPU(ARM) || CPU(SH4)
103     const UChar* stringCharacters = string->characters();
104     for (unsigned i = 0; i != length; ++i) {
105         if (*stringCharacters++ != *characters++)
106             return false;
107     }
108     return true;
109 #else
110     /* Do it 4-bytes-at-a-time on architectures where it's safe */
111
112     const uint32_t* stringCharacters = reinterpret_cast<const uint32_t*>(string->characters());
113     const uint32_t* bufferCharacters = reinterpret_cast<const uint32_t*>(characters);
114
115     unsigned halfLength = length >> 1;
116     for (unsigned i = 0; i != halfLength; ++i) {
117         if (*stringCharacters++ != *bufferCharacters++)
118             return false;
119     }
120
121     if (length & 1 &&  *reinterpret_cast<const uint16_t*>(stringCharacters) != *reinterpret_cast<const uint16_t*>(bufferCharacters))
122         return false;
123
124     return true;
125 #endif
126 }
127
128 struct UCharBufferTranslator {
129     static unsigned hash(const UCharBuffer& buf)
130     {
131         return StringImpl::computeHash(buf.s, buf.length);
132     }
133
134     static bool equal(StringImpl* const& str, const UCharBuffer& buf)
135     {
136         return WebCore::equal(str, buf.s, buf.length);
137     }
138
139     static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash)
140     {
141         location = StringImpl::create(buf.s, buf.length).releaseRef(); 
142         location->setHash(hash);
143         location->setInTable();
144     }
145 };
146
147 struct HashAndCharacters {
148     unsigned hash;
149     const UChar* characters;
150     unsigned length;
151 };
152
153 struct HashAndCharactersTranslator {
154     static unsigned hash(const HashAndCharacters& buffer)
155     {
156         ASSERT(buffer.hash == StringImpl::computeHash(buffer.characters, buffer.length));
157         return buffer.hash;
158     }
159
160     static bool equal(StringImpl* const& string, const HashAndCharacters& buffer)
161     {
162         return WebCore::equal(string, buffer.characters, buffer.length);
163     }
164
165     static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash)
166     {
167         location = StringImpl::create(buffer.characters, buffer.length).releaseRef();
168         location->setHash(hash);
169         location->setInTable();
170     }
171 };
172
173 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length)
174 {
175     if (!s)
176         return 0;
177
178     if (length == 0)
179         return StringImpl::empty();
180     
181     UCharBuffer buf = { s, length }; 
182     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
183
184     // If the string is newly-translated, then we need to adopt it.
185     // The boolean in the pair tells us if that is so.
186     return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
187 }
188
189 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length, unsigned existingHash)
190 {
191     ASSERT(s);
192     ASSERT(existingHash);
193
194     if (length == 0)
195         return StringImpl::empty();
196     
197     HashAndCharacters buffer = { existingHash, s, length }; 
198     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<HashAndCharacters, HashAndCharactersTranslator>(buffer);
199     if (!addResult.second)
200         return *addResult.first;
201     return adoptRef(*addResult.first);
202 }
203
204 PassRefPtr<StringImpl> AtomicString::add(const UChar* s)
205 {
206     if (!s)
207         return 0;
208
209     int length = 0;
210     while (s[length] != UChar(0))
211         length++;
212
213     if (length == 0)
214         return StringImpl::empty();
215
216     UCharBuffer buf = {s, length}; 
217     pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
218
219     // If the string is newly-translated, then we need to adopt it.
220     // The boolean in the pair tells us if that is so.
221     return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
222 }
223
224 PassRefPtr<StringImpl> AtomicString::add(StringImpl* r)
225 {
226     if (!r || r->inTable())
227         return r;
228
229     if (r->length() == 0)
230         return StringImpl::empty();
231     
232     StringImpl* result = *stringTable().add(r).first;
233     if (result == r)
234         r->setInTable();
235     return result;
236 }
237
238 AtomicStringImpl* AtomicString::find(const UChar* s, unsigned length, unsigned existingHash)
239 {
240     ASSERT(s);
241     ASSERT(existingHash);
242
243     if (length == 0)
244         return static_cast<AtomicStringImpl*>(StringImpl::empty());
245
246     HashAndCharacters buffer = { existingHash, s, length }; 
247     HashSet<StringImpl*>::iterator iterator = stringTable().find<HashAndCharacters, HashAndCharactersTranslator>(buffer);
248     if (iterator == stringTable().end())
249         return 0;
250     return static_cast<AtomicStringImpl*>(*iterator);
251 }
252
253 void AtomicString::remove(StringImpl* r)
254 {
255     stringTable().remove(r);
256 }
257     
258 AtomicString AtomicString::lower() const
259 {
260     // Note: This is a hot function in the Dromaeo benchmark.
261     StringImpl* impl = this->impl();
262     RefPtr<StringImpl> newImpl = impl->lower();
263     if (LIKELY(newImpl == impl))
264         return *this;
265     return AtomicString(newImpl);
266 }
267
268 DEFINE_GLOBAL(AtomicString, nullAtom)
269 DEFINE_GLOBAL(AtomicString, emptyAtom, "")
270 DEFINE_GLOBAL(AtomicString, textAtom, "#text")
271 DEFINE_GLOBAL(AtomicString, commentAtom, "#comment")
272 DEFINE_GLOBAL(AtomicString, starAtom, "*")
273 DEFINE_GLOBAL(AtomicString, xmlAtom, "xml")
274 DEFINE_GLOBAL(AtomicString, xmlnsAtom, "xmlns")
275
276 void AtomicString::init()
277 {
278     static bool initialized;
279     if (!initialized) {
280         // Initialization is not thread safe, so this function must be called from the main thread first.
281         ASSERT(isMainThread());
282
283         // Use placement new to initialize the globals.
284         new ((void*)&nullAtom) AtomicString;
285         new ((void*)&emptyAtom) AtomicString("");
286         new ((void*)&textAtom) AtomicString("#text");
287         new ((void*)&commentAtom) AtomicString("#comment");
288         new ((void*)&starAtom) AtomicString("*");
289         new ((void*)&xmlAtom) AtomicString("xml");
290         new ((void*)&xmlnsAtom) AtomicString("xmlns");
291
292         initialized = true;
293     }
294 }
295
296 }