/* * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UStringImpl_h #define UStringImpl_h #include #include #include #include #include #include #include namespace JSC { class IdentifierTable; typedef OwnFastMallocPtr SharableUChar; typedef CrossThreadRefCounted SharedUChar; class UStringOrRopeImpl : public Noncopyable { public: bool isRope() { return (m_refCountAndFlags & s_refCountIsRope) == s_refCountIsRope; } unsigned length() const { return m_length; } void ref() { m_refCountAndFlags += s_refCountIncrement; } inline void deref(); protected: enum BufferOwnership { BufferInternal, BufferOwned, BufferSubstring, BufferShared, }; using Noncopyable::operator new; // For SmallStringStorage, which allocates an array and uses an in-place new. UStringOrRopeImpl() { } UStringOrRopeImpl(unsigned length, BufferOwnership ownership) : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | ownership) , m_length(length) { ASSERT(!isRope()); } enum StaticStringConstructType { ConstructStaticString }; UStringOrRopeImpl(unsigned length, StaticStringConstructType) : m_refCountAndFlags(s_refCountFlagStatic | s_refCountFlagIsIdentifier | BufferOwned) , m_length(length) { ASSERT(!isRope()); } enum RopeConstructType { ConstructRope }; UStringOrRopeImpl(RopeConstructType) : m_refCountAndFlags(s_refCountIncrement | s_refCountIsRope) , m_length(0) { ASSERT(isRope()); } // The bottom 5 bits hold flags, the top 27 bits hold the ref count. // When dereferencing UStringImpls we check for the ref count AND the // static bit both being zero - static strings are never deleted. static const unsigned s_refCountMask = 0xFFFFFFE0; static const unsigned s_refCountIncrement = 0x20; static const unsigned s_refCountFlagStatic = 0x10; static const unsigned s_refCountFlagShouldReportedCost = 0x8; static const unsigned s_refCountFlagIsIdentifier = 0x4; static const unsigned s_refCountMaskBufferOwnership = 0x3; // Use an otherwise invalid permutation of flags (static & shouldReportedCost - // static strings do not set shouldReportedCost in the constructor, and this bit // is only ever cleared, not set) to identify objects that are ropes. static const unsigned s_refCountIsRope = s_refCountFlagStatic | s_refCountFlagShouldReportedCost; unsigned m_refCountAndFlags; unsigned m_length; }; class UStringImpl : public UStringOrRopeImpl { friend class CStringTranslator; friend class UCharBufferTranslator; friend class JIT; friend class SmallStringsStorage; friend class UStringOrRopeImpl; friend void initializeUString(); private: // For SmallStringStorage, which allocates an array and uses an in-place new. UStringImpl() { } // Used to construct static strings, which have an special refCount that can never hit zero. // This means that the static string will never be destroyed, which is important because // static strings will be shared across threads & ref-counted in a non-threadsafe manner. UStringImpl(const UChar* characters, unsigned length, StaticStringConstructType) : UStringOrRopeImpl(length, ConstructStaticString) , m_data(characters) , m_buffer(0) , m_hash(0) { hash(); } // Create a normal string with internal storage (BufferInternal) UStringImpl(unsigned length) : UStringOrRopeImpl(length, BufferInternal) , m_data(reinterpret_cast(this + 1)) , m_buffer(0) , m_hash(0) { ASSERT(m_data); ASSERT(m_length); } // Create a UStringImpl adopting ownership of the provided buffer (BufferOwned) UStringImpl(const UChar* characters, unsigned length) : UStringOrRopeImpl(length, BufferOwned) , m_data(characters) , m_buffer(0) , m_hash(0) { ASSERT(m_data); ASSERT(m_length); } // Used to create new strings that are a substring of an existing UStringImpl (BufferSubstring) UStringImpl(const UChar* characters, unsigned length, PassRefPtr base) : UStringOrRopeImpl(length, BufferSubstring) , m_data(characters) , m_substringBuffer(base.releaseRef()) , m_hash(0) { ASSERT(m_data); ASSERT(m_length); ASSERT(m_substringBuffer->bufferOwnership() != BufferSubstring); } // Used to construct new strings sharing an existing SharedUChar (BufferShared) UStringImpl(const UChar* characters, unsigned length, PassRefPtr sharedBuffer) : UStringOrRopeImpl(length, BufferShared) , m_data(characters) , m_sharedBuffer(sharedBuffer.releaseRef()) , m_hash(0) { ASSERT(m_data); ASSERT(m_length); } // For use only by Identifier's XXXTranslator helpers. void setHash(unsigned hash) { ASSERT(!isStatic()); ASSERT(!m_hash); ASSERT(hash == computeHash(m_data, m_length)); m_hash = hash; } public: ~UStringImpl(); static PassRefPtr create(const UChar*, unsigned length); static PassRefPtr create(const char*, unsigned length); static PassRefPtr create(const char*); static PassRefPtr create(PassRefPtr, const UChar*, unsigned length); static PassRefPtr create(PassRefPtr rep, unsigned offset, unsigned length) { ASSERT(rep); ASSERT(length <= rep->length()); if (!length) return empty(); UStringImpl* ownerRep = (rep->bufferOwnership() == BufferSubstring) ? rep->m_substringBuffer : rep.get(); return adoptRef(new UStringImpl(rep->m_data + offset, length, ownerRep)); } static PassRefPtr createUninitialized(unsigned length, UChar*& output); static PassRefPtr tryCreateUninitialized(unsigned length, UChar*& output) { if (!length) { output = 0; return empty(); } if (length > ((std::numeric_limits::max() - sizeof(UStringImpl)) / sizeof(UChar))) return 0; UStringImpl* resultImpl; if (!tryFastMalloc(sizeof(UChar) * length + sizeof(UStringImpl)).getValue(resultImpl)) return 0; output = reinterpret_cast(resultImpl + 1); return adoptRef(new(resultImpl) UStringImpl(length)); } template static PassRefPtr adopt(Vector& vector) { if (size_t size = vector.size()) { ASSERT(vector.data()); return adoptRef(new UStringImpl(vector.releaseBuffer(), size)); } return empty(); } SharedUChar* sharedBuffer(); const UChar* characters() const { return m_data; } size_t cost() { // For substrings, return the cost of the base string. if (bufferOwnership() == BufferSubstring) return m_substringBuffer->cost(); if (m_refCountAndFlags & s_refCountFlagShouldReportedCost) { m_refCountAndFlags &= ~s_refCountFlagShouldReportedCost; return m_length; } return 0; } bool isIdentifier() const { return m_refCountAndFlags & s_refCountFlagIsIdentifier; } void setIsIdentifier(bool isIdentifier) { ASSERT(!isStatic()); if (isIdentifier) m_refCountAndFlags |= s_refCountFlagIsIdentifier; else m_refCountAndFlags &= ~s_refCountFlagIsIdentifier; } unsigned hash() const { if (!m_hash) m_hash = computeHash(m_data, m_length); return m_hash; } unsigned existingHash() const { ASSERT(m_hash); return m_hash; } static unsigned computeHash(const UChar* data, unsigned length) { return WTF::stringHash(data, length); } static unsigned computeHash(const char* data, unsigned length) { return WTF::stringHash(data, length); } static unsigned computeHash(const char* data) { return WTF::stringHash(data); } ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & (s_refCountMask | s_refCountFlagStatic))) delete this; } static UStringImpl* empty(); static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) { if (numCharacters <= s_copyCharsInlineCutOff) { for (unsigned i = 0; i < numCharacters; ++i) destination[i] = source[i]; } else memcpy(destination, source, numCharacters * sizeof(UChar)); } private: // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. static const unsigned s_copyCharsInlineCutOff = 20; BufferOwnership bufferOwnership() const { return static_cast(m_refCountAndFlags & s_refCountMaskBufferOwnership); } bool isStatic() const { return m_refCountAndFlags & s_refCountFlagStatic; } const UChar* m_data; union { void* m_buffer; UStringImpl* m_substringBuffer; SharedUChar* m_sharedBuffer; }; mutable unsigned m_hash; }; class URopeImpl : public UStringOrRopeImpl { friend class UStringOrRopeImpl; public: // A URopeImpl is composed from a set of smaller strings called Fibers. // Each Fiber in a rope is either UStringImpl or another URopeImpl. typedef UStringOrRopeImpl* Fiber; // Creates a URopeImpl comprising of 'fiberCount' Fibers. // The URopeImpl is constructed in an uninitialized state - initialize must be called for each Fiber in the URopeImpl. static PassRefPtr tryCreateUninitialized(unsigned fiberCount) { void* allocation; if (tryFastMalloc(sizeof(URopeImpl) + (fiberCount - 1) * sizeof(Fiber)).getValue(allocation)) return adoptRef(new (allocation) URopeImpl(fiberCount)); return 0; } void initializeFiber(unsigned &index, Fiber fiber) { m_fibers[index++] = fiber; fiber->ref(); m_length += fiber->length(); } unsigned fiberCount() { return m_fiberCount; } Fiber& fibers(unsigned index) { return m_fibers[index]; } ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & s_refCountMask)) destructNonRecursive(); } private: URopeImpl(unsigned fiberCount) : UStringOrRopeImpl(ConstructRope), m_fiberCount(fiberCount) {} void destructNonRecursive(); void derefFibersNonRecursive(Vector& workQueue); bool hasOneRef() { return (m_refCountAndFlags & s_refCountMask) == s_refCountIncrement; } unsigned m_fiberCount; Fiber m_fibers[1]; }; inline void UStringOrRopeImpl::deref() { if (isRope()) static_cast(this)->deref(); else static_cast(this)->deref(); } bool equal(const UStringImpl*, const UStringImpl*); } #endif