| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #pragma once |
| |
| namespace Js |
| { |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Storage |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // CompoundString uses available buffer space to directly store characters or pointers, or to pack information such as a |
| // substring's start index and length. It is optimized for concatenation. A compound string begins in direct character mode, |
| // where it appends characters directly to the buffers. When a somewhat larger string is concatenated, the compound string |
| // switches to pointer mode and records the direct character length. From that point onwards, only pointers or packed |
| // information is stored in the buffers. Each piece of packed information is stored as a pointer with the lowest bit tagged. |
| // |
| // A compound string may have several chained Block objects, each with a buffer allocated inline with the block. The |
| // compound string references only the last block in the chain (to save space), and each block references its previous block |
| // in the chain. As a consequence, during flattening, blocks are iterated backwards and flattening is also done backwards. |
| // |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Using as a character-only string builder |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // Using the AppendChars set of functions requires that the compound string is in direct character mode, and forces it to |
| // remain in direct character mode by appending all characters directly to the buffer. Those functions can be used to build |
| // a string like a typical character-only string builder. Flattening is much faster when in direct character mode, and the |
| // AppendChars set of functions also get to omit the check to see if the compound string is in direct character mode, but it |
| // is at the cost of having to append all characters even in the case of appending large strings, instead of just appending |
| // a pointer. |
| // |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Appending |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // The compound string and builder have simple Append and AppendChars functions that delegate to a set of AppendGeneric |
| // functions that do the actual work. AppendGeneric functions are templatized and their implementation is shared between the |
| // compound string and builder. |
| // |
| // After determining how to append, the AppendGeneric functions call a TryAppendGeneric function that will perform the |
| // append if there is enough space in the last block's buffer. If there is no space, the AppendGeneric functions call a |
| // AppendSlow function. In a compound string, the AppendSlow function grows the buffer or creates a new chained block, and |
| // performs the append. In a builder, the AppendSlow function creates the compound string and delegates to it from that |
| // point onwards. |
| // |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Buffer sharing and ownership |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // Multiple compound string objects may reference and use the same buffers. Cloning a compound string creates a new object |
| // that references the same buffer. However, only one compound string may own the last block at any given time, since the |
| // last block's buffer is mutable through concatenation. Compound string objects that don't own the last block keep track of |
| // the character length of the buffer in their last block info (BlockInfo object), since the block's length may be changed |
| // by the block's owner. |
| // |
| // When a concatenation operation is performed on a compound string that does not own the last block, it will first need to |
| // take ownership of that block. So, it is necessary to call PrepareForAppend() before the first append operation for a |
| // compound string whose buffers may be shared. Taking ownership of a block is done by either resizing the block (and hence |
| // copying the buffer up to the point to which it is used), or shallow-cloning the last block and chaining a new block to |
| // it. Shallow cloning copies the block's metadata but does not copy the buffer. The character length of the clone is set to |
| // the length of the portion of the buffer that is used by the compound string. The cloned block references the original |
| // block that owns the buffer, for access to the buffer. Once a new block is chained to it, it then becomes the last block, |
| // effectively making the clone immutable by the compound string. |
| // |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Last block info (BlockInfo object) |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // Blocks are only created once chaining begins. Until then, the buffer is allocated directly and stored in the last block |
| // info. The buffer is resized until it reaches a threshold, and upon the final resize before chaining begins, a block is |
| // created. From that point onwards, blocks are no longer resized and only chained, although a new chained block may be |
| // larger than the previous block. |
| // |
| // The last block info is also used to serve as a cache for information in the last block. Since the last block is where |
| // concatenation occurs, it is significantly beneficial to prevent having to dereference the last block to get to its |
| // information. So, the BlockInfo object representing the last block info caches the last block's information and only it is |
| // used during append operations. Only when space runs out, is the actual last block updated with information from the last |
| // block info. As a consequence of this and the fact that multiple compound strings may share blocks, the last block's |
| // character length may not be up-to-date, or may not be relevant to the compound string querying it, so it should never be |
| // queried except in specific cases where it is guaranteed to be correct. |
| // |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // Builder |
| // ------------------------------------------------------------------------------------------------------------------------- |
| // |
| // The builder uses stack-allocated space for the initial buffer. It may perform better in some scenarios, but the tradeoff |
| // is that it is at the cost of an additional check per append. |
| // |
| // It typically performs better in cases where the number of concatenations is highly unpredictable and may range from just |
| // a few to a large number: |
| // - For few concatenations, the final compound string's buffer will be the minimum size necessary, so it helps by |
| // saving space, and as a result, performing a faster allocation |
| // - For many concatenations, the use of stack space reduces the number of allocations that would otherwise be necessary |
| // to grow the buffer |
| |
| class CompoundString sealed : public LiteralString // vtable will be switched to LiteralString's vtable after flattening |
| { |
| #pragma region CompoundString::Block |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| private: |
| class Block |
| { |
| public: |
| static const uint ChainSizeThreshold; |
| private: |
| static const uint MaxChainedBlockSize; |
| |
| private: |
| Field(Block *const) bufferOwner; |
| Field(CharCount) charLength; |
| Field(CharCount) charCapacity; |
| Field(const Block *const) previous; |
| |
| private: |
| Block(const CharCount charCapacity, const Block *const previous); |
| Block(const void *const buffer, const CharCount charLength, const CharCount charCapacity); |
| Block(const Block &other, const CharCount usedCharLength); |
| |
| public: |
| static Block *New(const uint size, const Block *const previous, Recycler *const recycler); |
| static Block *New(const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, Recycler *const recycler); |
| Block *Clone(const CharCount usedCharLength, Recycler *const recycler) const; |
| |
| private: |
| static CharCount CharCapacityFromSize(const uint size); |
| static uint SizeFromCharCapacity(const CharCount charCapacity); |
| |
| private: |
| static CharCount PointerAlign(const CharCount charLength); |
| public: |
| static const char16 *Chars(const void *const buffer); |
| static char16 *Chars(void *const buffer); |
| static const Field(void*) *Pointers(const void *const buffer); |
| static Field(void*) *Pointers(void *const buffer); |
| static CharCount PointerCapacityFromCharCapacity(const CharCount charCapacity); |
| static CharCount CharCapacityFromPointerCapacity(const CharCount pointerCapacity); |
| static CharCount PointerLengthFromCharLength(const CharCount charLength); |
| static CharCount CharLengthFromPointerLength(const CharCount pointerLength); |
| static uint SizeFromUsedCharLength(const CharCount usedCharLength); |
| |
| public: |
| static bool ShouldAppendChars(const CharCount appendCharLength, const uint additionalSizeForPointerAppend = 0); |
| |
| public: |
| const void *Buffer() const; |
| void *Buffer(); |
| const Block *Previous() const; |
| |
| public: |
| const char16 *Chars() const; |
| char16 *Chars(); |
| CharCount CharLength() const; |
| void SetCharLength(const CharCount charLength); |
| CharCount CharCapacity() const; |
| |
| public: |
| const Field(void*) *Pointers() const; |
| Field(void*) *Pointers(); |
| CharCount PointerLength() const; |
| CharCount PointerCapacity() const; |
| |
| private: |
| static uint GrowSize(const uint size); |
| static uint GrowSizeForChaining(const uint size); |
| public: |
| Block *Chain(Recycler *const recycler); |
| |
| private: |
| PREVENT_COPY(Block); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #pragma endregion |
| |
| #pragma region CompoundString::BlockInfo |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| private: |
| class BlockInfo |
| { |
| private: |
| Field(void *) buffer; |
| Field(CharCount) charLength; |
| Field(CharCount) charCapacity; |
| |
| public: |
| BlockInfo(); |
| BlockInfo(Block *const block); |
| |
| public: |
| char16 *Chars() const; |
| CharCount CharLength() const; |
| void SetCharLength(const CharCount charLength); |
| CharCount CharCapacity() const; |
| |
| public: |
| Field(void*) *Pointers() const; |
| CharCount PointerLength() const; |
| void SetPointerLength(const CharCount pointerLength); |
| CharCount PointerCapacity() const; |
| |
| public: |
| static CharCount AlignCharCapacityForAllocation(const CharCount charCapacity); |
| static CharCount GrowCharCapacity(const CharCount charCapacity); |
| static bool ShouldAllocateBuffer(const CharCount charCapacity); |
| void AllocateBuffer(const CharCount charCapacity, Recycler *const recycler); |
| Block *CopyBuffer(const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, Recycler *const recycler); |
| Block *Resize(Recycler *const recycler); |
| static size_t GetOffsetOfCharLength() { return offsetof(BlockInfo, charLength); } |
| static size_t GetOffsetOfCharCapacity() { return offsetof(BlockInfo, charCapacity); } |
| static size_t GetOffsetOfBuffer() { return offsetof(BlockInfo, buffer); } |
| |
| public: |
| void CopyFrom(Block *const block); |
| void CopyTo(Block *const block); |
| void Unreference(); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #pragma endregion |
| |
| #pragma region CompoundString::Builder |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| public: |
| template<CharCount MinimumCharCapacity> |
| class Builder |
| { |
| private: |
| // Array size needs to be a constant expression. This expression is equivalent to |
| // Block::PointerLengthFromCharLength(MinimumCharCapacity), and generates a pointer capacity that equates to a char |
| // capacity that is >= MinimumCharCapacity. |
| void *buffer[ |
| ( |
| (MinimumCharCapacity + sizeof(void *) / sizeof(char16) - 1) & |
| ~(sizeof(void *) / sizeof(char16) - 1) |
| ) / (sizeof(void *) / sizeof(char16))]; |
| |
| CharCount stringLength; |
| CharCount charLength; |
| CharCount directCharLength; |
| CompoundString *compoundString; |
| ScriptContext *const scriptContext; |
| |
| #if DBG |
| bool isFinalized; |
| #endif |
| |
| public: |
| Builder(ScriptContext *const scriptContext); |
| |
| private: |
| bool IsFinalized() const; |
| bool HasOnlyDirectChars() const; |
| void SwitchToPointerMode(); |
| bool OwnsLastBlock() const; |
| const char16 *GetAppendStringBuffer(JavascriptString *const s) const; |
| ScriptContext *GetScriptContext() const; |
| JavascriptLibrary *GetLibrary() const; |
| |
| private: |
| char16 *LastBlockChars(); |
| CharCount LastBlockCharLength() const; |
| void SetLastBlockCharLength(const CharCount charLength); |
| CharCount LastBlockCharCapacity() const; |
| |
| private: |
| Field(void*) *LastBlockPointers(); |
| CharCount LastBlockPointerLength() const; |
| void SetLastBlockPointerLength(const CharCount pointerLength); |
| CharCount LastBlockPointerCapacity() const; |
| |
| private: |
| CharCount GetLength() const; |
| void SetLength(const CharCount stringLength); |
| |
| private: |
| void AppendSlow(const char16 c); |
| void AppendSlow(JavascriptString *const s); |
| void AppendSlow(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| void AppendSlow(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength); |
| |
| public: |
| void Append(const char16 c); |
| void AppendChars(const char16 c); |
| void Append(JavascriptString *const s); |
| void AppendChars(JavascriptString *const s); |
| void Append(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength); |
| void AppendChars(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength); |
| template<CharCount AppendCharLengthPlusOne> void Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true); |
| template<CharCount AppendCharLengthPlusOne> void AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true); |
| void Append(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| void AppendChars(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| template<class TValue, class FConvertToString> void Append(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString); |
| template<class TValue, class FConvertToString> void AppendChars(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString); |
| |
| private: |
| CompoundString *CreateCompoundString(const bool reserveMoreSpace) const; |
| |
| public: |
| JavascriptString *ToString(); |
| |
| friend CompoundString; |
| |
| PREVENT_COPY(Builder); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #pragma endregion |
| |
| #pragma region CompoundString |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| private: |
| Field(BlockInfo) lastBlockInfo; |
| Field(CharCount) directCharLength; |
| Field(bool) ownsLastBlock; |
| Field(Block *) lastBlock; |
| |
| private: |
| CompoundString(const CharCount initialCharCapacity, JavascriptLibrary *const library); |
| CompoundString(const CharCount initialBlockSize, const bool allocateBlock, JavascriptLibrary *const library); |
| CompoundString(const CharCount stringLength, const CharCount directCharLength, const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, JavascriptLibrary *const library); |
| CompoundString(CompoundString &other, const bool forAppending); |
| |
| public: |
| static CompoundString *NewWithCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library); |
| static CompoundString *NewWithPointerCapacity(const CharCount initialPointerCapacity, JavascriptLibrary *const library); |
| private: |
| static CompoundString *NewWithBufferCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library); |
| static CompoundString *NewWithBlockSize(const CharCount initialBlockSize, JavascriptLibrary *const library); |
| static CompoundString *New(const CharCount stringLength, const CharCount directCharLength, const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, JavascriptLibrary *const library); |
| public: |
| CompoundString *Clone(const bool forAppending); |
| static CompoundString * JitClone(CompoundString * cs); |
| static CompoundString * JitCloneForAppending(CompoundString * cs); |
| public: |
| static bool Is(RecyclableObject *const object); |
| static bool Is(const Var var); |
| static CompoundString *FromVar(RecyclableObject *const object); |
| static CompoundString *FromVar(const Var var); |
| static size_t GetOffsetOfOwnsLastBlock() { return offsetof(CompoundString, ownsLastBlock); } |
| static size_t GetOffsetOfDirectCharLength() { return offsetof(CompoundString, directCharLength); } |
| static size_t GetOffsetOfLastBlockInfo() { return offsetof(CompoundString, lastBlockInfo); } |
| static size_t GetOffsetOfLastBlockInfoCharLength() { return CompoundString::BlockInfo::GetOffsetOfCharLength(); } |
| static size_t GetOffsetOfLastBlockInfoCharCapacity() { return CompoundString::BlockInfo::GetOffsetOfCharCapacity(); } |
| static size_t GetOffsetOfLastBlockInfoBuffer() { return CompoundString::BlockInfo::GetOffsetOfBuffer(); } |
| |
| public: |
| static JavascriptString *GetImmutableOrScriptUnreferencedString(JavascriptString *const s); |
| static bool ShouldAppendChars(const CharCount appendCharLength); |
| |
| private: |
| bool HasOnlyDirectChars() const; |
| void SwitchToPointerMode(); |
| bool OwnsLastBlock() const; |
| const char16 *GetAppendStringBuffer(JavascriptString *const s) const; |
| |
| private: |
| char16 *LastBlockChars() const; |
| CharCount LastBlockCharLength() const; |
| void SetLastBlockCharLength(const CharCount charLength); |
| CharCount LastBlockCharCapacity() const; |
| |
| private: |
| Field(void*) *LastBlockPointers() const; |
| CharCount LastBlockPointerLength() const; |
| void SetLastBlockPointerLength(const CharCount pointerLength); |
| CharCount LastBlockPointerCapacity() const; |
| |
| private: |
| static void PackSubstringInfo(const CharCount startIndex, const CharCount length, void * *const packedSubstringInfoRef, void * *const packedSubstringInfo2Ref); |
| public: |
| static bool IsPackedInfo(void *const pointer); |
| static void UnpackSubstringInfo(void *const pointer, void *const pointer2, CharCount *const startIndexRef, CharCount *const lengthRef); |
| |
| private: |
| template<class String> static bool TryAppendGeneric(const char16 c, String *const toString); |
| template<class String> static bool TryAppendGeneric(JavascriptString *const s, const CharCount appendCharLength, String *const toString); |
| template<class String> static bool TryAppendFewCharsGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString); |
| template<class String> static bool TryAppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString); |
| template<class String> static bool TryAppendGeneric(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength, String *const toString); |
| |
| private: |
| template<class String> static void AppendGeneric(const char16 c, String *const toString, const bool appendChars); |
| template<class String> static void AppendGeneric(JavascriptString *const s, String *const toString, const bool appendChars); |
| template<class String> static void AppendGeneric(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength, String *const toString, const bool appendChars); |
| template<CharCount AppendCharLengthPlusOne, class String> static void AppendGeneric(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral, String *const toString, const bool appendChars); |
| template<class String> static void AppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString, const bool appendChars); |
| template<class TValue, class FConvertToString, class String> static void AppendGeneric(const TValue &value, CharCount maximumAppendCharLength, const FConvertToString ConvertToString, String *const toString, const bool appendChars); |
| |
| private: |
| void AppendSlow(const char16 c); |
| void AppendSlow(JavascriptString *const s); |
| void AppendSlow(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| void AppendSlow(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength); |
| |
| public: |
| void PrepareForAppend(); |
| void Append(const char16 c); |
| void AppendChars(const char16 c); |
| void Append(JavascriptString *const s); |
| void AppendChars(JavascriptString *const s); |
| void Append(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength); |
| void AppendChars(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength); |
| template<CharCount AppendCharLengthPlusOne> void Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true); |
| template<CharCount AppendCharLengthPlusOne> void AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true); |
| void Append(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| void AppendChars(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength); |
| void AppendCharsSz(__in_z const char16 *const s); |
| template<class TValue, class FConvertToString> void Append(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString); |
| template<class TValue, class FConvertToString> void AppendChars(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString); |
| |
| private: |
| void Grow(); |
| void TakeOwnershipOfLastBlock(); |
| |
| private: |
| void Unreference(); |
| public: |
| virtual const char16 *GetSz() override sealed; |
| using JavascriptString::Copy; |
| virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed; |
| virtual bool IsTree() const override sealed; |
| |
| protected: |
| DEFINE_VTABLE_CTOR(CompoundString, LiteralString); |
| DECLARE_CONCRETE_STRING_CLASS; |
| |
| private: |
| PREVENT_COPY(CompoundString); |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #pragma endregion |
| |
| public: |
| virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() |
| { |
| return VTableValue::VtableCompoundString; |
| } |
| }; |
| |
| #pragma region CompoundString::Builder definition |
| #ifndef CompoundStringJsDiag |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| template<CharCount MinimumCharCapacity> |
| CompoundString::Builder<MinimumCharCapacity>::Builder(ScriptContext *const scriptContext) |
| : stringLength(0), |
| charLength(0), |
| directCharLength(static_cast<CharCount>(-1)), |
| compoundString(nullptr), |
| scriptContext(scriptContext) |
| #if DBG |
| , isFinalized(false) |
| #endif |
| { |
| CompileAssert(MinimumCharCapacity != 0); |
| Assert(LastBlockCharCapacity() >= MinimumCharCapacity); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| bool CompoundString::Builder<MinimumCharCapacity>::IsFinalized() const |
| { |
| #if DBG |
| return isFinalized; |
| #else |
| return false; |
| #endif |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| bool CompoundString::Builder<MinimumCharCapacity>::HasOnlyDirectChars() const |
| { |
| return directCharLength == static_cast<CharCount>(-1); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::SwitchToPointerMode() |
| { |
| Assert(HasOnlyDirectChars()); |
| |
| directCharLength = charLength; |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print(_u("CompoundString::SwitchToPointerMode() - directCharLength = %u\n"), directCharLength); |
| Output::Flush(); |
| } |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| bool CompoundString::Builder<MinimumCharCapacity>::OwnsLastBlock() const |
| { |
| return true; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline const char16 *CompoundString::Builder<MinimumCharCapacity>::GetAppendStringBuffer( |
| JavascriptString *const s) const |
| { |
| Assert(s); |
| |
| return s->GetString(); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| ScriptContext *CompoundString::Builder<MinimumCharCapacity>::GetScriptContext() const |
| { |
| return scriptContext; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| JavascriptLibrary *CompoundString::Builder<MinimumCharCapacity>::GetLibrary() const |
| { |
| return scriptContext->GetLibrary(); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| char16 *CompoundString::Builder<MinimumCharCapacity>::LastBlockChars() |
| { |
| return Block::Chars(buffer); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharLength() const |
| { |
| return charLength; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockCharLength(const CharCount charLength) |
| { |
| this->charLength = charLength; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharCapacity() const |
| { |
| return Block::CharCapacityFromPointerCapacity(LastBlockPointerCapacity()); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| Field(void*) *CompoundString::Builder<MinimumCharCapacity>::LastBlockPointers() |
| { |
| return Block::Pointers(buffer); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerLength() const |
| { |
| return Block::PointerLengthFromCharLength(charLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockPointerLength(const CharCount pointerLength) |
| { |
| charLength = Block::CharLengthFromPointerLength(pointerLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerCapacity() const |
| { |
| return _countof(buffer); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CharCount CompoundString::Builder<MinimumCharCapacity>::GetLength() const |
| { |
| return stringLength; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::SetLength(const CharCount stringLength) |
| { |
| if(!IsValidCharCount(stringLength)) |
| Throw::OutOfMemory(); |
| this->stringLength = stringLength; |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(const char16 c) |
| { |
| Assert(!this->compoundString); |
| CompoundString *const compoundString = CreateCompoundString(true); |
| this->compoundString = compoundString; |
| const bool appended = |
| HasOnlyDirectChars() |
| ? TryAppendGeneric(c, compoundString) |
| : TryAppendGeneric(GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, compoundString); |
| Assert(appended); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(JavascriptString *const s) |
| { |
| Assert(!this->compoundString); |
| CompoundString *const compoundString = CreateCompoundString(true); |
| this->compoundString = compoundString; |
| const bool appended = TryAppendGeneric(s, s->GetLength(), compoundString); |
| Assert(appended); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::AppendSlow( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength) |
| { |
| // Even though CreateCompoundString() will create a compound string with some additional space reserved for appending, |
| // the amount of space available may still not be enough, so need to check and fall back to the slow path as well |
| Assert(!this->compoundString); |
| CompoundString *const compoundString = CreateCompoundString(true); |
| this->compoundString = compoundString; |
| if(TryAppendGeneric(s, appendCharLength, compoundString)) |
| return; |
| compoundString->AppendSlow(s, appendCharLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| void CompoundString::Builder<MinimumCharCapacity>::AppendSlow( |
| JavascriptString *const s, |
| void *const packedSubstringInfo, |
| void *const packedSubstringInfo2, |
| const CharCount appendCharLength) |
| { |
| Assert(!this->compoundString); |
| CompoundString *const compoundString = CreateCompoundString(true); |
| this->compoundString = compoundString; |
| const bool appended = TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, compoundString); |
| Assert(appended); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append(const char16 c) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(c, this, false); |
| return; |
| } |
| |
| compoundString->Append(c); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(const char16 c) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(c, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(c); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append(JavascriptString *const s) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, this, false); |
| return; |
| } |
| |
| compoundString->Append(s); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(JavascriptString *const s) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(s); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append( |
| JavascriptString *const s, |
| const CharCount startIndex, |
| const CharCount appendCharLength) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, startIndex, appendCharLength, this, false); |
| return; |
| } |
| |
| compoundString->Append(s, startIndex, appendCharLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars( |
| JavascriptString *const s, |
| const CharCount startIndex, |
| const CharCount appendCharLength) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, startIndex, appendCharLength, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(s, startIndex, appendCharLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| template<CharCount AppendCharLengthPlusOne> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append( |
| const char16 (&s)[AppendCharLengthPlusOne], |
| const bool isCppLiteral) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, isCppLiteral, this, false); |
| return; |
| } |
| |
| compoundString->Append(s, isCppLiteral); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| template<CharCount AppendCharLengthPlusOne> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars( |
| const char16 (&s)[AppendCharLengthPlusOne], |
| const bool isCppLiteral) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, isCppLiteral, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(s, isCppLiteral); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, appendCharLength, this, false); |
| return; |
| } |
| |
| compoundString->Append(s, appendCharLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(s, appendCharLength, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(s, appendCharLength); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| template<class TValue, class FConvertToString> |
| inline void CompoundString::Builder<MinimumCharCapacity>::Append( |
| const TValue &value, |
| const CharCount maximumAppendCharLength, |
| const FConvertToString ConvertToString) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false); |
| return; |
| } |
| |
| compoundString->Append(value, maximumAppendCharLength, ConvertToString); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| template<class TValue, class FConvertToString> |
| inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars( |
| const TValue &value, |
| const CharCount maximumAppendCharLength, |
| const FConvertToString ConvertToString) |
| { |
| if(!compoundString) |
| { |
| AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true); |
| return; |
| } |
| |
| compoundString->AppendChars(value, maximumAppendCharLength, ConvertToString); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| CompoundString *CompoundString::Builder<MinimumCharCapacity>::CreateCompoundString(const bool reserveMoreSpace) const |
| { |
| return |
| CompoundString::New( |
| stringLength, |
| directCharLength, |
| buffer, |
| charLength, |
| reserveMoreSpace, |
| this->GetLibrary()); |
| } |
| |
| template<CharCount MinimumCharCapacity> |
| inline JavascriptString *CompoundString::Builder<MinimumCharCapacity>::ToString() |
| { |
| #if DBG |
| // Should not append to the builder after this function is called |
| isFinalized = true; |
| #endif |
| |
| CompoundString *const compoundString = this->compoundString; |
| if(compoundString) |
| return compoundString; |
| |
| switch(stringLength) |
| { |
| default: |
| return CreateCompoundString(false); |
| |
| case 0: |
| return this->GetLibrary()->GetEmptyString(); |
| |
| case 1: |
| Assert(HasOnlyDirectChars()); |
| Assert(LastBlockCharLength() == 1); |
| |
| return this->GetLibrary()->GetCharStringCache().GetStringForChar(LastBlockChars()[0]); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #endif |
| #pragma endregion |
| |
| #pragma region CompoundString template member definitions |
| #ifndef CompoundStringJsDiag |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| template<class String> |
| inline bool CompoundString::TryAppendGeneric(const char16 c, String *const toString) |
| { |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(toString->HasOnlyDirectChars()); |
| |
| const CharCount blockCharLength = toString->LastBlockCharLength(); |
| if(blockCharLength < toString->LastBlockCharCapacity()) |
| { |
| toString->LastBlockChars()[blockCharLength] = c; |
| toString->SetLength(toString->GetLength() + 1); |
| toString->SetLastBlockCharLength(blockCharLength + 1); |
| return true; |
| } |
| return false; |
| } |
| |
| template<class String> |
| inline bool CompoundString::TryAppendGeneric( |
| JavascriptString *const s, |
| const CharCount appendCharLength, |
| String *const toString) |
| { |
| Assert(s); |
| Assert(appendCharLength == s->GetLength()); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!toString->HasOnlyDirectChars()); |
| |
| const CharCount blockPointerLength = toString->LastBlockPointerLength(); |
| if(blockPointerLength < toString->LastBlockPointerCapacity()) |
| { |
| toString->LastBlockPointers()[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s); |
| toString->SetLength(toString->GetLength() + appendCharLength); |
| toString->SetLastBlockPointerLength(blockPointerLength + 1); |
| return true; |
| } |
| return false; |
| } |
| |
| template<class String> |
| inline bool CompoundString::TryAppendFewCharsGeneric( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength, |
| String *const toString) |
| { |
| Assert(s); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(toString->HasOnlyDirectChars()); |
| |
| const CharCount blockCharLength = toString->LastBlockCharLength(); |
| if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength) |
| { |
| const char16 *appendCharBuffer = s; |
| char16 *charBuffer = &toString->LastBlockChars()[blockCharLength]; |
| const char16 *const charBufferEnd = charBuffer + appendCharLength; |
| for(; charBuffer != charBufferEnd; ++appendCharBuffer, ++charBuffer) |
| *charBuffer = *appendCharBuffer; |
| toString->SetLength(toString->GetLength() + appendCharLength); |
| toString->SetLastBlockCharLength(blockCharLength + appendCharLength); |
| return true; |
| } |
| return false; |
| } |
| |
| template<class String> |
| inline bool CompoundString::TryAppendGeneric( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength, |
| String *const toString) |
| { |
| Assert(s); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(toString->HasOnlyDirectChars()); |
| |
| const CharCount blockCharLength = toString->LastBlockCharLength(); |
| if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength) |
| { |
| CopyHelper(&toString->LastBlockChars()[blockCharLength], s, appendCharLength); |
| toString->SetLength(toString->GetLength() + appendCharLength); |
| toString->SetLastBlockCharLength(blockCharLength + appendCharLength); |
| return true; |
| } |
| return false; |
| } |
| |
| template<class String> |
| inline bool CompoundString::TryAppendGeneric( |
| JavascriptString *const s, |
| void *const packedSubstringInfo, |
| void *const packedSubstringInfo2, |
| const CharCount appendCharLength, |
| String *const toString) |
| { |
| Assert(s); |
| Assert(packedSubstringInfo); |
| Assert(appendCharLength <= s->GetLength()); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!toString->HasOnlyDirectChars()); |
| |
| const CharCount blockPointerLength = toString->LastBlockPointerLength(); |
| const CharCount appendPointerLength = 2 + !!packedSubstringInfo2; |
| if(blockPointerLength < toString->LastBlockPointerCapacity() - (appendPointerLength - 1)) |
| { |
| Field(void*)* pointers = toString->LastBlockPointers(); |
| pointers[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s); |
| if(packedSubstringInfo2) |
| pointers[blockPointerLength + 1] = packedSubstringInfo2; |
| pointers[blockPointerLength + (appendPointerLength - 1)] = packedSubstringInfo; |
| toString->SetLength(toString->GetLength() + appendCharLength); |
| toString->SetLastBlockPointerLength(blockPointerLength + appendPointerLength); |
| return true; |
| } |
| return false; |
| } |
| |
| template<class String> |
| inline void CompoundString::AppendGeneric(const char16 c, String *const toString, const bool appendChars) |
| { |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print(_u("CompoundString::AppendGeneric('%c', appendChars = %s)\n"), c, appendChars ? _u("true") : _u("false")); |
| Output::Flush(); |
| } |
| |
| if(appendChars || toString->HasOnlyDirectChars() |
| ? TryAppendGeneric(c, toString) |
| : TryAppendGeneric(toString->GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, toString)) |
| { |
| return; |
| } |
| toString->AppendSlow(c); |
| } |
| |
| template<class String> |
| inline void CompoundString::AppendGeneric( |
| JavascriptString *const s, |
| String *const toString, |
| const bool appendChars) |
| { |
| Assert(s); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| const CharCount appendCharLength = s->GetLength(); |
| if(appendCharLength == 0) |
| return; |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print( |
| _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"), |
| s->IsFinalized() ? s->GetString() : _u(""), |
| !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""), |
| appendCharLength, |
| appendChars ? _u("true") : _u("false")); |
| Output::Flush(); |
| } |
| |
| if(appendChars || toString->HasOnlyDirectChars()) |
| { |
| if(appendCharLength == 1) |
| { |
| const char16 c = toString->GetAppendStringBuffer(s)[0]; |
| if(TryAppendGeneric(c, toString)) |
| return; |
| toString->AppendSlow(c); |
| return; |
| } |
| |
| if(appendChars || Block::ShouldAppendChars(appendCharLength)) |
| { |
| const char16 *const appendBuffer = toString->GetAppendStringBuffer(s); |
| if(appendChars |
| ? TryAppendGeneric(appendBuffer, appendCharLength, toString) |
| : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString)) |
| { |
| return; |
| } |
| toString->AppendSlow(appendBuffer, appendCharLength); |
| return; |
| } |
| |
| toString->SwitchToPointerMode(); |
| } |
| |
| if(TryAppendGeneric(s, appendCharLength, toString)) |
| return; |
| toString->AppendSlow(s); |
| } |
| |
| template<class String> |
| inline void CompoundString::AppendGeneric( |
| JavascriptString *const s, |
| const CharCount startIndex, |
| const CharCount appendCharLength, |
| String *const toString, |
| const bool appendChars) |
| { |
| Assert(s); |
| Assert(startIndex <= s->GetLength()); |
| Assert(appendCharLength <= s->GetLength() - startIndex); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| if(appendCharLength == 0) |
| return; |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print( |
| _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.*s%s\", startIndex = %u, appendCharLength = %u, appendChars = %s)\n"), |
| min(static_cast<CharCount>(8), appendCharLength), |
| s->IsFinalized() ? &s->GetString()[startIndex] : _u(""), |
| !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""), |
| startIndex, |
| appendCharLength, |
| appendChars ? _u("true") : _u("false")); |
| Output::Flush(); |
| } |
| |
| if(appendChars || toString->HasOnlyDirectChars()) |
| { |
| if(appendCharLength == 1) |
| { |
| const char16 c = toString->GetAppendStringBuffer(s)[startIndex]; |
| if(TryAppendGeneric(c, toString)) |
| return; |
| toString->AppendSlow(c); |
| return; |
| } |
| |
| if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(void *))) |
| { |
| const char16 *const appendBuffer = &toString->GetAppendStringBuffer(s)[startIndex]; |
| if(appendChars |
| ? TryAppendGeneric(appendBuffer, appendCharLength, toString) |
| : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString)) |
| { |
| return; |
| } |
| toString->AppendSlow(appendBuffer, appendCharLength); |
| return; |
| } |
| |
| toString->SwitchToPointerMode(); |
| } |
| |
| if(appendCharLength == 1) |
| { |
| JavascriptString *const js = |
| toString->GetLibrary()->GetCharStringCache().GetStringForChar(toString->GetAppendStringBuffer(s)[startIndex]); |
| if(TryAppendGeneric(js, 1, toString)) |
| return; |
| toString->AppendSlow(js); |
| return; |
| } |
| |
| void *packedSubstringInfo, *packedSubstringInfo2; |
| PackSubstringInfo(startIndex, appendCharLength, &packedSubstringInfo, &packedSubstringInfo2); |
| if(TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, toString)) |
| return; |
| toString->AppendSlow(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength); |
| } |
| |
| template<CharCount AppendCharLengthPlusOne, class String> |
| inline void CompoundString::AppendGeneric( |
| const char16 (&s)[AppendCharLengthPlusOne], |
| const bool isCppLiteral, |
| String *const toString, |
| const bool appendChars) |
| { |
| CompileAssert(AppendCharLengthPlusOne != 0); |
| Assert(s); |
| Assert(s[AppendCharLengthPlusOne - 1] == _u('\0')); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| if(AppendCharLengthPlusOne == 1) |
| return; |
| if(AppendCharLengthPlusOne == 2) |
| { |
| AppendGeneric(s[0], toString, appendChars); |
| return; |
| } |
| |
| const CharCount appendCharLength = AppendCharLengthPlusOne - 1; |
| if(!isCppLiteral) |
| { |
| AppendGeneric(s, appendCharLength, toString, appendChars); |
| return; |
| } |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print( |
| _u("CompoundString::AppendGeneric(C++ literal \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"), |
| s, |
| appendCharLength > 8 ? _u("...") : _u(""), |
| appendCharLength, |
| appendChars ? _u("true") : _u("false")); |
| Output::Flush(); |
| } |
| |
| if(appendChars || toString->HasOnlyDirectChars()) |
| { |
| if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(LiteralString))) |
| { |
| if(appendChars |
| ? TryAppendGeneric(s, appendCharLength, toString) |
| : TryAppendFewCharsGeneric(s, appendCharLength, toString)) |
| { |
| return; |
| } |
| toString->AppendSlow(s, appendCharLength); |
| return; |
| } |
| |
| toString->SwitchToPointerMode(); |
| } |
| |
| JavascriptString *const js = toString->GetLibrary()->CreateStringFromCppLiteral(s); |
| if(TryAppendGeneric(js, appendCharLength, toString)) |
| return; |
| toString->AppendSlow(js); |
| } |
| |
| template<class String> |
| inline void CompoundString::AppendGeneric( |
| __in_xcount(appendCharLength) const char16 *const s, |
| const CharCount appendCharLength, |
| String *const toString, |
| const bool appendChars) |
| { |
| Assert(s); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| if(appendCharLength == 0) |
| return; |
| |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print( |
| _u("CompoundString::AppendGeneric(char16 *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"), |
| s, |
| appendCharLength > 8 ? _u("...") : _u(""), |
| appendCharLength, |
| appendChars ? _u("true") : _u("false")); |
| Output::Flush(); |
| } |
| |
| if(appendChars || toString->HasOnlyDirectChars()) |
| { |
| if(appendCharLength == 1) |
| { |
| const char16 c = s[0]; |
| if(TryAppendGeneric(c, toString)) |
| return; |
| toString->AppendSlow(c); |
| return; |
| } |
| |
| // Skip the check for Block::ShouldAppendChars because the string buffer has to be copied anyway |
| if(TryAppendGeneric(s, appendCharLength, toString)) |
| return; |
| toString->AppendSlow(s, appendCharLength); |
| return; |
| } |
| |
| JavascriptString *const js = LiteralString::NewCopyBuffer(s, appendCharLength, toString->GetScriptContext()); |
| if(TryAppendGeneric(js, appendCharLength, toString)) |
| return; |
| toString->AppendSlow(js); |
| } |
| |
| template<class TValue, class FConvertToString, class String> |
| inline void CompoundString::AppendGeneric( |
| const TValue &value, |
| CharCount maximumAppendCharLength, |
| const FConvertToString ConvertToString, |
| String *const toString, |
| const bool appendChars) |
| { |
| const CharCount AbsoluteMaximumAppendCharLength = 20; // maximum length of uint64 converted to base-10 string |
| |
| Assert(maximumAppendCharLength != 0); |
| Assert(maximumAppendCharLength <= AbsoluteMaximumAppendCharLength); |
| Assert(toString); |
| Assert(!toString->IsFinalized()); |
| Assert(toString->OwnsLastBlock()); |
| Assert(!(appendChars && !toString->HasOnlyDirectChars())); |
| |
| ++maximumAppendCharLength; // + 1 for null terminator |
| const CharCount blockCharLength = toString->LastBlockCharLength(); |
| const bool convertInPlace = |
| (appendChars || toString->HasOnlyDirectChars()) && |
| maximumAppendCharLength <= toString->LastBlockCharCapacity() - blockCharLength; |
| char16 localConvertBuffer[AbsoluteMaximumAppendCharLength + 1]; // + 1 for null terminator |
| char16 *const convertBuffer = convertInPlace ? &toString->LastBlockChars()[blockCharLength] : localConvertBuffer; |
| ConvertToString(value, convertBuffer, maximumAppendCharLength); |
| |
| const CharCount appendCharLength = static_cast<CharCount>(wcslen(convertBuffer)); |
| if(PHASE_TRACE_StringConcat) |
| { |
| Output::Print( |
| _u("CompoundString::AppendGeneric(TValue &, appendChars = %s) - converted = \"%.8s%s\", appendCharLength = %u\n"), |
| appendChars ? _u("true") : _u("false"), |
| convertBuffer, |
| appendCharLength > 8 ? _u("...") : _u(""), |
| appendCharLength); |
| Output::Flush(); |
| } |
| |
| if(convertInPlace) |
| { |
| toString->SetLength(toString->GetLength() + appendCharLength); |
| toString->SetLastBlockCharLength(blockCharLength + appendCharLength); |
| return; |
| } |
| AnalysisAssert(convertBuffer == localConvertBuffer); |
| AppendGeneric(static_cast<const char16* const>(localConvertBuffer), appendCharLength, toString, appendChars); |
| } |
| |
| template<CharCount AppendCharLengthPlusOne> |
| inline void CompoundString::Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral) |
| { |
| AppendGeneric(s, isCppLiteral, this, false); |
| } |
| |
| template<CharCount AppendCharLengthPlusOne> |
| inline void CompoundString::AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral) |
| { |
| AppendGeneric(s, isCppLiteral, this, true); |
| } |
| |
| template<class TValue, class FConvertToString> |
| inline void CompoundString::Append( |
| const TValue &value, |
| const CharCount maximumAppendCharLength, |
| const FConvertToString ConvertToString) |
| { |
| AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false); |
| } |
| |
| template<class TValue, class FConvertToString> |
| inline void CompoundString::AppendChars( |
| const TValue &value, |
| const CharCount maximumAppendCharLength, |
| const FConvertToString ConvertToString) |
| { |
| AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| #endif |
| #pragma endregion |
| } |