2 * Copyright (C) 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <wtf/CrossThreadRefCounted.h>
31 #include <wtf/OwnFastMallocPtr.h>
32 #include <wtf/PossiblyNull.h>
33 #include <wtf/StringHashFunctions.h>
34 #include <wtf/Vector.h>
35 #include <wtf/unicode/Unicode.h>
39 class IdentifierTable
;
41 typedef CrossThreadRefCounted
<OwnFastMallocPtr
<UChar
> > SharedUChar
;
43 class UntypedPtrAndBitfield
{
45 UntypedPtrAndBitfield() {}
47 UntypedPtrAndBitfield(void* ptrValue
, uintptr_t bitValue
)
48 : m_value(reinterpret_cast<uintptr_t>(ptrValue
) | bitValue
)
50 , m_leaksPtr(ptrValue
)
53 ASSERT(ptrValue
== asPtr
<void*>());
54 ASSERT((*this & ~s_alignmentMask
) == bitValue
);
58 T
asPtr() const { return reinterpret_cast<T
>(m_value
& s_alignmentMask
); }
60 UntypedPtrAndBitfield
& operator&=(uintptr_t bits
)
62 m_value
&= bits
| s_alignmentMask
;
66 UntypedPtrAndBitfield
& operator|=(uintptr_t bits
)
68 m_value
|= bits
& ~s_alignmentMask
;
72 uintptr_t operator&(uintptr_t mask
) const
74 return m_value
& mask
& ~s_alignmentMask
;
78 static const uintptr_t s_alignmentMask
= ~static_cast<uintptr_t>(0x7);
81 void* m_leaksPtr
; // Only used to allow tools like leaks on OSX to detect that the memory is referenced.
85 class UStringImpl
: Noncopyable
{
87 template<size_t inlineCapacity
>
88 static PassRefPtr
<UStringImpl
> adopt(Vector
<UChar
, inlineCapacity
>& vector
)
90 if (unsigned length
= vector
.size())
91 return adoptRef(new UStringImpl(vector
.releaseBuffer(), length
, BufferOwned
));
95 static PassRefPtr
<UStringImpl
> create(const UChar
* buffer
, int length
)
98 if (PassRefPtr
<UStringImpl
> impl
= tryCreateUninitialized(length
, newBuffer
)) {
99 copyChars(newBuffer
, buffer
, length
);
105 static PassRefPtr
<UStringImpl
> create(PassRefPtr
<UStringImpl
> rep
, int offset
, int length
)
108 rep
->checkConsistency();
109 return adoptRef(new UStringImpl(rep
->m_data
+ offset
, length
, rep
->bufferOwnerString()));
112 static PassRefPtr
<UStringImpl
> create(PassRefPtr
<SharedUChar
> sharedBuffer
, UChar
* buffer
, int length
)
114 return adoptRef(new UStringImpl(buffer
, length
, sharedBuffer
));
117 static PassRefPtr
<UStringImpl
> createUninitialized(unsigned length
, UChar
*& output
)
124 if (length
> ((std::numeric_limits
<size_t>::max() - sizeof(UStringImpl
)) / sizeof(UChar
)))
126 UStringImpl
* resultImpl
= static_cast<UStringImpl
*>(fastMalloc(sizeof(UChar
) * length
+ sizeof(UStringImpl
)));
127 output
= reinterpret_cast<UChar
*>(resultImpl
+ 1);
128 return adoptRef(new(resultImpl
) UStringImpl(output
, length
, BufferInternal
));
131 static PassRefPtr
<UStringImpl
> tryCreateUninitialized(unsigned length
, UChar
*& output
)
138 if (length
> ((std::numeric_limits
<size_t>::max() - sizeof(UStringImpl
)) / sizeof(UChar
)))
140 UStringImpl
* resultImpl
;
141 if (!tryFastMalloc(sizeof(UChar
) * length
+ sizeof(UStringImpl
)).getValue(resultImpl
))
143 output
= reinterpret_cast<UChar
*>(resultImpl
+ 1);
144 return adoptRef(new(resultImpl
) UStringImpl(output
, length
, BufferInternal
));
147 SharedUChar
* sharedBuffer();
148 UChar
* data() const { return m_data
; }
149 int size() const { return m_length
; }
152 // For substrings, return the cost of the base string.
153 if (bufferOwnership() == BufferSubstring
)
154 return m_dataBuffer
.asPtr
<UStringImpl
*>()->cost();
156 if (m_dataBuffer
& s_reportedCostBit
)
158 m_dataBuffer
|= s_reportedCostBit
;
161 unsigned hash() const { if (!m_hash
) m_hash
= computeHash(data(), m_length
); return m_hash
; }
162 unsigned existingHash() const { ASSERT(m_hash
); return m_hash
; } // fast path for Identifiers
163 void setHash(unsigned hash
) { ASSERT(hash
== computeHash(data(), m_length
)); m_hash
= hash
; } // fast path for Identifiers
164 bool isIdentifier() const { return m_isIdentifier
; }
165 void setIsIdentifier(bool isIdentifier
) { m_isIdentifier
= isIdentifier
; }
167 UStringImpl
* ref() { m_refCount
+= s_refCountIncrement
; return this; }
168 ALWAYS_INLINE
void deref() { if (!(m_refCount
-= s_refCountIncrement
)) delete this; }
170 static void copyChars(UChar
* destination
, const UChar
* source
, unsigned numCharacters
)
172 if (numCharacters
<= s_copyCharsInlineCutOff
) {
173 for (unsigned i
= 0; i
< numCharacters
; ++i
)
174 destination
[i
] = source
[i
];
176 memcpy(destination
, source
, numCharacters
* sizeof(UChar
));
179 static unsigned computeHash(const UChar
* s
, int length
) { ASSERT(length
>= 0); return WTF::stringHash(s
, length
); }
180 static unsigned computeHash(const char* s
, int length
) { ASSERT(length
>= 0); return WTF::stringHash(s
, length
); }
181 static unsigned computeHash(const char* s
) { return WTF::stringHash(s
); }
183 static UStringImpl
& null() { return *s_null
; }
184 static UStringImpl
& empty() { return *s_empty
; }
186 ALWAYS_INLINE
void checkConsistency() const
188 // There is no recursion of substrings.
189 ASSERT(bufferOwnerString()->bufferOwnership() != BufferSubstring
);
190 // Static strings cannot be put in identifier tables, because they are globally shared.
191 ASSERT(!isStatic() || !isIdentifier());
195 enum BufferOwnership
{
202 // For SmallStringStorage, which allocates an array and uses an in-place new.
205 // Used to construct normal strings with an internal or external buffer.
206 UStringImpl(UChar
* data
, int length
, BufferOwnership ownership
)
209 , m_refCount(s_refCountIncrement
)
211 , m_isIdentifier(false)
212 , m_dataBuffer(0, ownership
)
214 ASSERT((ownership
== BufferInternal
) || (ownership
== BufferOwned
));
218 // Used to construct static strings, which have an special refCount that can never hit zero.
219 // This means that the static string will never be destroyed, which is important because
220 // static strings will be shared across threads & ref-counted in a non-threadsafe manner.
221 enum StaticStringConstructType
{ ConstructStaticString
};
222 UStringImpl(UChar
* data
, int length
, StaticStringConstructType
)
225 , m_refCount(s_staticRefCountInitialValue
)
227 , m_isIdentifier(false)
228 , m_dataBuffer(0, BufferOwned
)
233 // Used to create new strings that are a substring of an existing string.
234 UStringImpl(UChar
* data
, int length
, PassRefPtr
<UStringImpl
> base
)
237 , m_refCount(s_refCountIncrement
)
239 , m_isIdentifier(false)
240 , m_dataBuffer(base
.releaseRef(), BufferSubstring
)
242 // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes
243 // that all pointers will be at least 8-byte aligned, we cannot guarantee that of
244 // UStringImpls that are not heap allocated.
245 ASSERT(m_dataBuffer
.asPtr
<UStringImpl
*>()->size());
246 ASSERT(!m_dataBuffer
.asPtr
<UStringImpl
*>()->isStatic());
250 // Used to construct new strings sharing an existing shared buffer.
251 UStringImpl(UChar
* data
, int length
, PassRefPtr
<SharedUChar
> sharedBuffer
)
254 , m_refCount(s_refCountIncrement
)
256 , m_isIdentifier(false)
257 , m_dataBuffer(sharedBuffer
.releaseRef(), BufferShared
)
262 using Noncopyable::operator new;
263 void* operator new(size_t, void* inPlace
) { return inPlace
; }
267 // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings.
268 static const int s_minLengthToShare
= 10;
269 static const unsigned s_copyCharsInlineCutOff
= 20;
270 static const uintptr_t s_bufferOwnershipMask
= 3;
271 static const uintptr_t s_reportedCostBit
= 4;
272 // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2.
273 // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero.
274 static const int s_refCountIncrement
= 2;
275 static const int s_staticRefCountInitialValue
= 1;
277 UStringImpl
* bufferOwnerString() { return (bufferOwnership() == BufferSubstring
) ? m_dataBuffer
.asPtr
<UStringImpl
*>() : this; }
278 const UStringImpl
* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring
) ? m_dataBuffer
.asPtr
<UStringImpl
*>() : this; }
279 SharedUChar
* baseSharedBuffer();
280 unsigned bufferOwnership() const { return m_dataBuffer
& s_bufferOwnershipMask
; }
281 bool isStatic() const { return m_refCount
& 1; }
287 mutable unsigned m_hash
: 31;
288 mutable unsigned m_isIdentifier
: 1;
289 UntypedPtrAndBitfield m_dataBuffer
;
291 JS_EXPORTDATA
static UStringImpl
* s_null
;
292 JS_EXPORTDATA
static UStringImpl
* s_empty
;
295 friend class SmallStringsStorage
;
296 friend void initializeUString();
299 bool equal(const UStringImpl
*, const UStringImpl
*);