1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 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. 12 * 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. 24 */ 25 26 #ifndef UStringImpl_h 27 #define UStringImpl_h 28 29 #include <limits> 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> 36 37 namespace JSC { 38 39 class IdentifierTable; 40 41 typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; 42 43 class UntypedPtrAndBitfield { 44 public: UntypedPtrAndBitfield()45 UntypedPtrAndBitfield() {} 46 UntypedPtrAndBitfield(void * ptrValue,uintptr_t bitValue)47 UntypedPtrAndBitfield(void* ptrValue, uintptr_t bitValue) 48 : m_value(reinterpret_cast<uintptr_t>(ptrValue) | bitValue) 49 #ifndef NDEBUG 50 , m_leaksPtr(ptrValue) 51 #endif 52 { 53 ASSERT(ptrValue == asPtr<void*>()); 54 ASSERT((*this & ~s_alignmentMask) == bitValue); 55 } 56 57 template<typename T> asPtr()58 T asPtr() const { return reinterpret_cast<T>(m_value & s_alignmentMask); } 59 60 UntypedPtrAndBitfield& operator&=(uintptr_t bits) 61 { 62 m_value &= bits | s_alignmentMask; 63 return *this; 64 } 65 66 UntypedPtrAndBitfield& operator|=(uintptr_t bits) 67 { 68 m_value |= bits & ~s_alignmentMask; 69 return *this; 70 } 71 72 uintptr_t operator&(uintptr_t mask) const 73 { 74 return m_value & mask & ~s_alignmentMask; 75 } 76 77 private: 78 static const uintptr_t s_alignmentMask = ~static_cast<uintptr_t>(0x7); 79 uintptr_t m_value; 80 #ifndef NDEBUG 81 void* m_leaksPtr; // Only used to allow tools like leaks on OSX to detect that the memory is referenced. 82 #endif 83 }; 84 85 class UStringImpl : Noncopyable { 86 public: 87 template<size_t inlineCapacity> adopt(Vector<UChar,inlineCapacity> & vector)88 static PassRefPtr<UStringImpl> adopt(Vector<UChar, inlineCapacity>& vector) 89 { 90 if (unsigned length = vector.size()) { 91 ASSERT(vector.data()); 92 return adoptRef(new UStringImpl(vector.releaseBuffer(), length, BufferOwned)); 93 } 94 return &empty(); 95 } 96 97 static PassRefPtr<UStringImpl> create(const char* c); 98 static PassRefPtr<UStringImpl> create(const char* c, int length); 99 static PassRefPtr<UStringImpl> create(const UChar* buffer, int length); 100 create(PassRefPtr<UStringImpl> rep,int offset,int length)101 static PassRefPtr<UStringImpl> create(PassRefPtr<UStringImpl> rep, int offset, int length) 102 { 103 ASSERT(rep); 104 rep->checkConsistency(); 105 return adoptRef(new UStringImpl(rep->m_data + offset, length, rep->bufferOwnerString())); 106 } 107 create(PassRefPtr<SharedUChar> sharedBuffer,UChar * buffer,int length)108 static PassRefPtr<UStringImpl> create(PassRefPtr<SharedUChar> sharedBuffer, UChar* buffer, int length) 109 { 110 return adoptRef(new UStringImpl(buffer, length, sharedBuffer)); 111 } 112 createUninitialized(unsigned length,UChar * & output)113 static PassRefPtr<UStringImpl> createUninitialized(unsigned length, UChar*& output) 114 { 115 if (!length) { 116 output = 0; 117 return &empty(); 118 } 119 120 if (length > ((std::numeric_limits<size_t>::max() - sizeof(UStringImpl)) / sizeof(UChar))) 121 CRASH(); 122 UStringImpl* resultImpl = static_cast<UStringImpl*>(fastMalloc(sizeof(UChar) * length + sizeof(UStringImpl))); 123 output = reinterpret_cast<UChar*>(resultImpl + 1); 124 return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); 125 } 126 tryCreateUninitialized(unsigned length,UChar * & output)127 static PassRefPtr<UStringImpl> tryCreateUninitialized(unsigned length, UChar*& output) 128 { 129 if (!length) { 130 output = 0; 131 return &empty(); 132 } 133 134 if (length > ((std::numeric_limits<size_t>::max() - sizeof(UStringImpl)) / sizeof(UChar))) 135 return 0; 136 UStringImpl* resultImpl; 137 if (!tryFastMalloc(sizeof(UChar) * length + sizeof(UStringImpl)).getValue(resultImpl)) 138 return 0; 139 output = reinterpret_cast<UChar*>(resultImpl + 1); 140 return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); 141 } 142 143 SharedUChar* sharedBuffer(); data()144 UChar* data() const { return m_data; } size()145 int size() const { return m_length; } cost()146 size_t cost() 147 { 148 // For substrings, return the cost of the base string. 149 if (bufferOwnership() == BufferSubstring) 150 return m_dataBuffer.asPtr<UStringImpl*>()->cost(); 151 152 if (m_dataBuffer & s_reportedCostBit) 153 return 0; 154 m_dataBuffer |= s_reportedCostBit; 155 return m_length; 156 } hash()157 unsigned hash() const { if (!m_hash) m_hash = computeHash(data(), m_length); return m_hash; } existingHash()158 unsigned existingHash() const { ASSERT(m_hash); return m_hash; } // fast path for Identifiers setHash(unsigned hash)159 void setHash(unsigned hash) { ASSERT(hash == computeHash(data(), m_length)); m_hash = hash; } // fast path for Identifiers isIdentifier()160 bool isIdentifier() const { return m_isIdentifier; } setIsIdentifier(bool isIdentifier)161 void setIsIdentifier(bool isIdentifier) { m_isIdentifier = isIdentifier; } 162 ref()163 UStringImpl* ref() { m_refCount += s_refCountIncrement; return this; } deref()164 ALWAYS_INLINE void deref() { if (!(m_refCount -= s_refCountIncrement)) delete this; } 165 copyChars(UChar * destination,const UChar * source,unsigned numCharacters)166 static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) 167 { 168 if (numCharacters <= s_copyCharsInlineCutOff) { 169 for (unsigned i = 0; i < numCharacters; ++i) 170 destination[i] = source[i]; 171 } else 172 memcpy(destination, source, numCharacters * sizeof(UChar)); 173 } 174 computeHash(const UChar * s,int length)175 static unsigned computeHash(const UChar* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } computeHash(const char * s,int length)176 static unsigned computeHash(const char* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } computeHash(const char * s)177 static unsigned computeHash(const char* s) { return WTF::stringHash(s); } 178 empty()179 static UStringImpl& empty() { return *s_empty; } 180 checkConsistency()181 ALWAYS_INLINE void checkConsistency() const 182 { 183 // There is no recursion of substrings. 184 ASSERT(bufferOwnerString()->bufferOwnership() != BufferSubstring); 185 // Static strings cannot be put in identifier tables, because they are globally shared. 186 ASSERT(!isStatic() || !isIdentifier()); 187 } 188 189 private: 190 enum BufferOwnership { 191 BufferInternal, 192 BufferOwned, 193 BufferSubstring, 194 BufferShared, 195 }; 196 197 // For SmallStringStorage, which allocates an array and uses an in-place new. UStringImpl()198 UStringImpl() { } 199 200 // Used to construct normal strings with an internal or external buffer. UStringImpl(UChar * data,int length,BufferOwnership ownership)201 UStringImpl(UChar* data, int length, BufferOwnership ownership) 202 : m_data(data) 203 , m_length(length) 204 , m_refCount(s_refCountIncrement) 205 , m_hash(0) 206 , m_isIdentifier(false) 207 , m_dataBuffer(0, ownership) 208 { 209 ASSERT((ownership == BufferInternal) || (ownership == BufferOwned)); 210 checkConsistency(); 211 } 212 213 // Used to construct static strings, which have an special refCount that can never hit zero. 214 // This means that the static string will never be destroyed, which is important because 215 // static strings will be shared across threads & ref-counted in a non-threadsafe manner. 216 enum StaticStringConstructType { ConstructStaticString }; UStringImpl(UChar * data,int length,StaticStringConstructType)217 UStringImpl(UChar* data, int length, StaticStringConstructType) 218 : m_data(data) 219 , m_length(length) 220 , m_refCount(s_staticRefCountInitialValue) 221 , m_hash(0) 222 , m_isIdentifier(false) 223 , m_dataBuffer(0, BufferOwned) 224 { 225 checkConsistency(); 226 } 227 228 // Used to create new strings that are a substring of an existing string. UStringImpl(UChar * data,int length,PassRefPtr<UStringImpl> base)229 UStringImpl(UChar* data, int length, PassRefPtr<UStringImpl> base) 230 : m_data(data) 231 , m_length(length) 232 , m_refCount(s_refCountIncrement) 233 , m_hash(0) 234 , m_isIdentifier(false) 235 , m_dataBuffer(base.releaseRef(), BufferSubstring) 236 { 237 // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes 238 // that all pointers will be at least 8-byte aligned, we cannot guarantee that of 239 // UStringImpls that are not heap allocated. 240 ASSERT(m_dataBuffer.asPtr<UStringImpl*>()->size()); 241 ASSERT(!m_dataBuffer.asPtr<UStringImpl*>()->isStatic()); 242 checkConsistency(); 243 } 244 245 // Used to construct new strings sharing an existing shared buffer. UStringImpl(UChar * data,int length,PassRefPtr<SharedUChar> sharedBuffer)246 UStringImpl(UChar* data, int length, PassRefPtr<SharedUChar> sharedBuffer) 247 : m_data(data) 248 , m_length(length) 249 , m_refCount(s_refCountIncrement) 250 , m_hash(0) 251 , m_isIdentifier(false) 252 , m_dataBuffer(sharedBuffer.releaseRef(), BufferShared) 253 { 254 checkConsistency(); 255 } 256 257 using Noncopyable::operator new; new(size_t,void * inPlace)258 void* operator new(size_t, void* inPlace) { return inPlace; } 259 260 ~UStringImpl(); 261 262 // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. 263 static const int s_minLengthToShare = 10; 264 static const unsigned s_copyCharsInlineCutOff = 20; 265 static const uintptr_t s_bufferOwnershipMask = 3; 266 static const uintptr_t s_reportedCostBit = 4; 267 // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2. 268 // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero. 269 static const int s_refCountIncrement = 2; 270 static const int s_staticRefCountInitialValue = 1; 271 bufferOwnerString()272 UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } bufferOwnerString()273 const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } 274 SharedUChar* baseSharedBuffer(); bufferOwnership()275 unsigned bufferOwnership() const { return m_dataBuffer & s_bufferOwnershipMask; } isStatic()276 bool isStatic() const { return m_refCount & 1; } 277 278 // unshared data 279 UChar* m_data; 280 int m_length; 281 unsigned m_refCount; 282 mutable unsigned m_hash : 31; 283 mutable unsigned m_isIdentifier : 1; 284 UntypedPtrAndBitfield m_dataBuffer; 285 286 JS_EXPORTDATA static UStringImpl* s_empty; 287 288 friend class JIT; 289 friend class SmallStringsStorage; 290 friend void initializeUString(); 291 }; 292 293 bool equal(const UStringImpl*, const UStringImpl*); 294 295 } 296 297 #endif 298