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
stringTable()43 static inline HashSet<StringImpl*>& stringTable()
44 {
45 return threadGlobalData().atomicStringTable();
46 }
47
48 struct CStringTranslator {
hashWebCore::CStringTranslator49 static unsigned hash(const char* c)
50 {
51 return StringImpl::computeHash(c);
52 }
53
equalWebCore::CStringTranslator54 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
translateWebCore::CStringTranslator66 static void translate(StringImpl*& location, const char* const& c, unsigned hash)
67 {
68 location = new StringImpl(c, strlen(c), hash);
69 }
70 };
71
operator ==(const AtomicString & a,const char * b)72 bool operator==(const AtomicString& a, const char* b)
73 {
74 StringImpl* impl = a.impl();
75 if ((!impl || !impl->characters()) && !b)
76 return true;
77 if ((!impl || !impl->characters()) || !b)
78 return false;
79 return CStringTranslator::equal(impl, b);
80 }
81
add(const char * c)82 PassRefPtr<StringImpl> AtomicString::add(const char* c)
83 {
84 if (!c)
85 return 0;
86 if (!*c)
87 return StringImpl::empty();
88 pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<const char*, CStringTranslator>(c);
89 if (!addResult.second)
90 return *addResult.first;
91 return adoptRef(*addResult.first);
92 }
93
94 struct UCharBuffer {
95 const UChar* s;
96 unsigned length;
97 };
98
equal(StringImpl * string,const UChar * characters,unsigned length)99 static inline bool equal(StringImpl* string, const UChar* characters, unsigned length)
100 {
101 if (string->length() != length)
102 return false;
103
104 #if PLATFORM(ARM) || PLATFORM(SH4)
105 const UChar* stringCharacters = string->characters();
106 for (unsigned i = 0; i != length; ++i) {
107 if (*stringCharacters++ != *characters++)
108 return false;
109 }
110 return true;
111 #else
112 /* Do it 4-bytes-at-a-time on architectures where it's safe */
113
114 const uint32_t* stringCharacters = reinterpret_cast<const uint32_t*>(string->characters());
115 const uint32_t* bufferCharacters = reinterpret_cast<const uint32_t*>(characters);
116
117 unsigned halfLength = length >> 1;
118 for (unsigned i = 0; i != halfLength; ++i) {
119 if (*stringCharacters++ != *bufferCharacters++)
120 return false;
121 }
122
123 if (length & 1 && *reinterpret_cast<const uint16_t*>(stringCharacters) != *reinterpret_cast<const uint16_t*>(bufferCharacters))
124 return false;
125
126 return true;
127 #endif
128 }
129
130 struct UCharBufferTranslator {
hashWebCore::UCharBufferTranslator131 static unsigned hash(const UCharBuffer& buf)
132 {
133 return StringImpl::computeHash(buf.s, buf.length);
134 }
135
equalWebCore::UCharBufferTranslator136 static bool equal(StringImpl* const& str, const UCharBuffer& buf)
137 {
138 return WebCore::equal(str, buf.s, buf.length);
139 }
140
translateWebCore::UCharBufferTranslator141 static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash)
142 {
143 location = new StringImpl(buf.s, buf.length, hash);
144 }
145 };
146
147 struct HashAndCharacters {
148 unsigned hash;
149 const UChar* characters;
150 unsigned length;
151 };
152
153 struct HashAndCharactersTranslator {
hashWebCore::HashAndCharactersTranslator154 static unsigned hash(const HashAndCharacters& buffer)
155 {
156 ASSERT(buffer.hash == StringImpl::computeHash(buffer.characters, buffer.length));
157 return buffer.hash;
158 }
159
equalWebCore::HashAndCharactersTranslator160 static bool equal(StringImpl* const& string, const HashAndCharacters& buffer)
161 {
162 return WebCore::equal(string, buffer.characters, buffer.length);
163 }
164
translateWebCore::HashAndCharactersTranslator165 static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash)
166 {
167 location = new StringImpl(buffer.characters, buffer.length, hash);
168 }
169 };
170
add(const UChar * s,int length)171 PassRefPtr<StringImpl> AtomicString::add(const UChar* s, int length)
172 {
173 if (!s)
174 return 0;
175
176 if (length == 0)
177 return StringImpl::empty();
178
179 UCharBuffer buf = { s, length };
180 pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
181
182 // If the string is newly-translated, then we need to adopt it.
183 // The boolean in the pair tells us if that is so.
184 return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
185 }
186
add(const UChar * s)187 PassRefPtr<StringImpl> AtomicString::add(const UChar* s)
188 {
189 if (!s)
190 return 0;
191
192 int length = 0;
193 while (s[length] != UChar(0))
194 length++;
195
196 if (length == 0)
197 return StringImpl::empty();
198
199 UCharBuffer buf = {s, length};
200 pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<UCharBuffer, UCharBufferTranslator>(buf);
201
202 // If the string is newly-translated, then we need to adopt it.
203 // The boolean in the pair tells us if that is so.
204 return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
205 }
206
add(StringImpl * r)207 PassRefPtr<StringImpl> AtomicString::add(StringImpl* r)
208 {
209 if (!r || r->inTable())
210 return r;
211
212 if (r->length() == 0)
213 return StringImpl::empty();
214
215 StringImpl* result = *stringTable().add(r).first;
216 if (result == r)
217 r->setInTable();
218 return result;
219 }
220
remove(StringImpl * r)221 void AtomicString::remove(StringImpl* r)
222 {
223 stringTable().remove(r);
224 }
225
226 #if USE(JSC)
add(const JSC::Identifier & identifier)227 PassRefPtr<StringImpl> AtomicString::add(const JSC::Identifier& identifier)
228 {
229 if (identifier.isNull())
230 return 0;
231
232 UString::Rep* string = identifier.ustring().rep();
233 unsigned length = string->size();
234 if (!length)
235 return StringImpl::empty();
236
237 HashAndCharacters buffer = { string->computedHash(), string->data(), length };
238 pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<HashAndCharacters, HashAndCharactersTranslator>(buffer);
239 if (!addResult.second)
240 return *addResult.first;
241 return adoptRef(*addResult.first);
242 }
243
add(const JSC::UString & ustring)244 PassRefPtr<StringImpl> AtomicString::add(const JSC::UString& ustring)
245 {
246 if (ustring.isNull())
247 return 0;
248
249 UString::Rep* string = ustring.rep();
250 unsigned length = string->size();
251 if (!length)
252 return StringImpl::empty();
253
254 HashAndCharacters buffer = { string->hash(), string->data(), length };
255 pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<HashAndCharacters, HashAndCharactersTranslator>(buffer);
256 if (!addResult.second)
257 return *addResult.first;
258 return adoptRef(*addResult.first);
259 }
260
find(const JSC::Identifier & identifier)261 AtomicStringImpl* AtomicString::find(const JSC::Identifier& identifier)
262 {
263 if (identifier.isNull())
264 return 0;
265
266 UString::Rep* string = identifier.ustring().rep();
267 unsigned length = string->size();
268 if (!length)
269 return static_cast<AtomicStringImpl*>(StringImpl::empty());
270
271 HashAndCharacters buffer = { string->computedHash(), string->data(), length };
272 HashSet<StringImpl*>::iterator iterator = stringTable().find<HashAndCharacters, HashAndCharactersTranslator>(buffer);
273 if (iterator == stringTable().end())
274 return 0;
275 return static_cast<AtomicStringImpl*>(*iterator);
276 }
277
operator UString() const278 AtomicString::operator UString() const
279 {
280 return m_string;
281 }
282 #endif
283
DEFINE_GLOBAL(AtomicString,nullAtom)284 DEFINE_GLOBAL(AtomicString, nullAtom)
285 DEFINE_GLOBAL(AtomicString, emptyAtom, "")
286 DEFINE_GLOBAL(AtomicString, textAtom, "#text")
287 DEFINE_GLOBAL(AtomicString, commentAtom, "#comment")
288 DEFINE_GLOBAL(AtomicString, starAtom, "*")
289
290 void AtomicString::init()
291 {
292 static bool initialized;
293 if (!initialized) {
294 // Initialization is not thread safe, so this function must be called from the main thread first.
295 ASSERT(isMainThread());
296
297 // Use placement new to initialize the globals.
298 new ((void*)&nullAtom) AtomicString;
299 new ((void*)&emptyAtom) AtomicString("");
300 new ((void*)&textAtom) AtomicString("#text");
301 new ((void*)&commentAtom) AtomicString("#comment");
302 new ((void*)&starAtom) AtomicString("*");
303
304 initialized = true;
305 }
306 }
307
308 }
309