| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- // JScriptDiag does not link with Runtime.lib and does not include .cpp files, so this file will be included as a header
- #include "RuntimeLibraryPch.h"
- namespace Js
- {
- #pragma region CompoundString::Block
- #ifndef IsJsDiag
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- const uint CompoundString::Block::MaxChainedBlockSize = HeapConstants::MaxSmallObjectSize; // TODO: LargeAlloc seems to be significantly slower, hence this threshold
- const uint CompoundString::Block::ChainSizeThreshold = MaxChainedBlockSize / 2;
- // TODO: Once the above LargeAlloc issue is fixed, experiment with forcing resizing as long as the string has only direct chars
- CompoundString::Block::Block(const CharCount charCapacity, const Block *const previous)
- : bufferOwner(this), charLength(0), charCapacity(charCapacity), previous(previous)
- {
- Assert(HeapInfo::IsAlignedSize(ChainSizeThreshold));
- Assert(ChainSizeThreshold <= MaxChainedBlockSize);
- Assert(HeapInfo::IsAlignedSize(MaxChainedBlockSize));
- Assert((MaxChainedBlockSize << 1) > MaxChainedBlockSize);
- Assert(charCapacity != 0);
- Assert(GrowSize(SizeFromCharCapacity(charCapacity)) != 0);
- }
- CompoundString::Block::Block(
- const void *const buffer,
- const CharCount charLength,
- const CharCount charCapacity)
- : bufferOwner(this), charLength(charLength), charCapacity(charCapacity), previous(nullptr)
- {
- Assert(buffer);
- Assert(charLength <= charCapacity);
- js_wmemcpy_s(Chars(), charLength, Chars(buffer), charLength);
- }
- CompoundString::Block::Block(const Block &other, const CharCount usedCharLength)
- : bufferOwner(other.bufferOwner),
- charLength(usedCharLength),
- charCapacity(other.charCapacity),
- previous(other.previous)
- {
- // This only does a shallow copy. The metadata is copied, and a reference to the other block is included in this copy
- // for access to the other block's buffer.
- Assert(usedCharLength <= other.charCapacity);
- }
- CompoundString::Block *CompoundString::Block::New(
- const uint size,
- const Block *const previous,
- Recycler *const recycler)
- {
- Assert(HeapInfo::IsAlignedSize(size));
- Assert(recycler);
- return RecyclerNewPlus(recycler, size - sizeof(Block), Block, CharCapacityFromSize(size), previous);
- }
- CompoundString::Block *CompoundString::Block::New(
- const void *const buffer,
- const CharCount usedCharLength,
- const bool reserveMoreSpace,
- Recycler *const recycler)
- {
- Assert(buffer);
- Assert(recycler);
- uint size = SizeFromUsedCharLength(usedCharLength);
- if(reserveMoreSpace)
- size = GrowSize(size);
- return RecyclerNewPlus(recycler, size - sizeof(Block), Block, buffer, usedCharLength, CharCapacityFromSize(size));
- }
- CompoundString::Block *CompoundString::Block::Clone(
- const CharCount usedCharLength,
- Recycler *const recycler) const
- {
- Assert(recycler);
- return RecyclerNew(recycler, Block, *this, usedCharLength);
- }
- CharCount CompoundString::Block::CharCapacityFromSize(const uint size)
- {
- Assert(size >= sizeof(Block));
- return (size - sizeof(Block)) / sizeof(char16);
- }
- uint CompoundString::Block::SizeFromCharCapacity(const CharCount charCapacity)
- {
- Assert(IsValidCharCount(charCapacity));
- return UInt32Math::Add(sizeof(Block), charCapacity * sizeof(char16));
- }
- #endif
- inline CharCount CompoundString::Block::PointerAlign(const CharCount charLength)
- {
- const CharCount alignedCharLength = ::Math::Align(charLength, static_cast<CharCount>(sizeof(void *) / sizeof(char16)));
- Assert(alignedCharLength >= charLength);
- return alignedCharLength;
- }
- inline const char16 *CompoundString::Block::Chars(const void *const buffer)
- {
- return static_cast<const char16 *>(buffer);
- }
- #ifndef IsJsDiag
- char16 *CompoundString::Block::Chars(void *const buffer)
- {
- return static_cast<char16 *>(buffer);
- }
- #endif
- inline void *const *CompoundString::Block::Pointers(const void *const buffer)
- {
- return static_cast<void *const *>(buffer);
- }
- #ifndef IsJsDiag
- void **CompoundString::Block::Pointers(void *const buffer)
- {
- return static_cast<void **>(buffer);
- }
- CharCount CompoundString::Block::PointerCapacityFromCharCapacity(const CharCount charCapacity)
- {
- return charCapacity / (sizeof(void *) / sizeof(char16));
- }
- CharCount CompoundString::Block::CharCapacityFromPointerCapacity(const CharCount pointerCapacity)
- {
- return pointerCapacity * (sizeof(void *) / sizeof(char16));
- }
- #endif
- // ChakraDiag includes CompoundString.cpp as a header file so this method needs to be marked as inline
- // to handle that case
- JS_DIAG_INLINE CharCount CompoundString::Block::PointerLengthFromCharLength(const CharCount charLength)
- {
- return PointerAlign(charLength) / (sizeof(void *) / sizeof(char16));
- }
- #ifndef IsJsDiag
- CharCount CompoundString::Block::CharLengthFromPointerLength(const CharCount pointerLength)
- {
- return pointerLength * (sizeof(void *) / sizeof(char16));
- }
- uint CompoundString::Block::SizeFromUsedCharLength(const CharCount usedCharLength)
- {
- const size_t usedSize = SizeFromCharCapacity(usedCharLength);
- const size_t alignedUsedSize = HeapInfo::GetAlignedSizeNoCheck(usedSize);
- if (alignedUsedSize != (uint)alignedUsedSize)
- {
- Js::Throw::OutOfMemory();
- }
- return (uint)alignedUsedSize;
- }
- bool CompoundString::Block::ShouldAppendChars(
- const CharCount appendCharLength,
- const uint additionalSizeForPointerAppend)
- {
- // Append characters instead of pointers when it would save space. Add some buffer as well, as flattening becomes more
- // expensive after the switch to pointer mode.
- //
- // 'additionalSizeForPointerAppend' should be provided when appending a pointer also involves creating a string object
- // or some other additional space (such as LiteralString, in which case this parameter should be sizeof(LiteralString)),
- // as that additional size also needs to be taken into account.
- return appendCharLength <= (sizeof(void *) * 2 + additionalSizeForPointerAppend) / sizeof(char16);
- }
- const void *CompoundString::Block::Buffer() const
- {
- return bufferOwner + 1;
- }
- void *CompoundString::Block::Buffer()
- {
- return bufferOwner + 1;
- }
- const CompoundString::Block *CompoundString::Block::Previous() const
- {
- return previous;
- }
- const char16 *CompoundString::Block::Chars() const
- {
- return Chars(Buffer());
- }
- char16 *CompoundString::Block::Chars()
- {
- return Chars(Buffer());
- }
- CharCount CompoundString::Block::CharLength() const
- {
- return charLength;
- }
- void CompoundString::Block::SetCharLength(const CharCount charLength)
- {
- Assert(charLength <= CharCapacity());
- this->charLength = charLength;
- }
- CharCount CompoundString::Block::CharCapacity() const
- {
- return charCapacity;
- }
- void *const *CompoundString::Block::Pointers() const
- {
- return Pointers(Buffer());
- }
- void **CompoundString::Block::Pointers()
- {
- return Pointers(Buffer());
- }
- CharCount CompoundString::Block::PointerLength() const
- {
- return PointerLengthFromCharLength(CharLength());
- }
- CharCount CompoundString::Block::PointerCapacity() const
- {
- return PointerCapacityFromCharCapacity(CharCapacity());
- }
- uint CompoundString::Block::GrowSize(const uint size)
- {
- Assert(size >= sizeof(Block));
- Assert(HeapInfo::IsAlignedSize(size));
- const uint newSize = size << 1;
- Assert(newSize > size);
- return newSize;
- }
- uint CompoundString::Block::GrowSizeForChaining(const uint size)
- {
- const uint newSize = GrowSize(size);
- return min(MaxChainedBlockSize, newSize);
- }
- CompoundString::Block *CompoundString::Block::Chain(Recycler *const recycler)
- {
- return New(GrowSizeForChaining(SizeFromUsedCharLength(CharLength())), this, recycler);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #endif
- #pragma endregion
- #pragma region CompoundString::BlockInfo
- #ifndef IsJsDiag
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CompoundString::BlockInfo::BlockInfo() : buffer(nullptr), charLength(0), charCapacity(0)
- {
- }
- CompoundString::BlockInfo::BlockInfo(Block *const block)
- {
- CopyFrom(block);
- }
- char16 *CompoundString::BlockInfo::Chars() const
- {
- return Block::Chars(buffer);
- }
- CharCount CompoundString::BlockInfo::CharLength() const
- {
- return charLength;
- }
- void CompoundString::BlockInfo::SetCharLength(const CharCount charLength)
- {
- Assert(charLength <= CharCapacity());
- this->charLength = charLength;
- }
- CharCount CompoundString::BlockInfo::CharCapacity() const
- {
- return charCapacity;
- }
- void **CompoundString::BlockInfo::Pointers() const
- {
- return Block::Pointers(buffer);
- }
- CharCount CompoundString::BlockInfo::PointerLength() const
- {
- return Block::PointerLengthFromCharLength(CharLength());
- }
- void CompoundString::BlockInfo::SetPointerLength(const CharCount pointerLength)
- {
- Assert(pointerLength <= PointerCapacity());
- charLength = Block::CharLengthFromPointerLength(pointerLength);
- }
- CharCount CompoundString::BlockInfo::PointerCapacity() const
- {
- return Block::PointerCapacityFromCharCapacity(CharCapacity());
- }
- CharCount CompoundString::BlockInfo::AlignCharCapacityForAllocation(const CharCount charCapacity)
- {
- const CharCount alignedCharCapacity =
- ::Math::AlignOverflowCheck(
- charCapacity == 0 ? static_cast<CharCount>(1) : charCapacity,
- static_cast<CharCount>(HeapConstants::ObjectGranularity / sizeof(char16)));
- Assert(alignedCharCapacity != 0);
- return alignedCharCapacity;
- }
- CharCount CompoundString::BlockInfo::GrowCharCapacity(const CharCount charCapacity)
- {
- Assert(charCapacity != 0);
- Assert(AlignCharCapacityForAllocation(charCapacity) == charCapacity);
- const CharCount newCharCapacity = UInt32Math::Mul<2>(charCapacity);
- Assert(newCharCapacity > charCapacity);
- return newCharCapacity;
- }
- bool CompoundString::BlockInfo::ShouldAllocateBuffer(const CharCount charCapacity)
- {
- Assert(charCapacity != 0);
- Assert(AlignCharCapacityForAllocation(charCapacity) == charCapacity);
- return charCapacity < Block::ChainSizeThreshold / sizeof(char16);
- }
- void CompoundString::BlockInfo::AllocateBuffer(const CharCount charCapacity, Recycler *const recycler)
- {
- Assert(!buffer);
- Assert(CharLength() == 0);
- Assert(CharCapacity() == 0);
- Assert(ShouldAllocateBuffer(charCapacity));
- Assert(recycler);
- buffer = RecyclerNewArray(recycler, char16, charCapacity);
- this->charCapacity = charCapacity;
- }
- CompoundString::Block *CompoundString::BlockInfo::CopyBuffer(
- const void *const buffer,
- const CharCount usedCharLength,
- const bool reserveMoreSpace,
- Recycler *const recycler)
- {
- Assert(buffer);
- Assert(recycler);
- CharCount charCapacity = AlignCharCapacityForAllocation(usedCharLength);
- if(reserveMoreSpace)
- charCapacity = GrowCharCapacity(charCapacity);
- if(ShouldAllocateBuffer(charCapacity))
- {
- AllocateBuffer(charCapacity, recycler);
- charLength = usedCharLength;
- js_wmemcpy_s((char16*)(this->buffer), charCapacity, (char16*)(buffer), usedCharLength);
- return nullptr;
- }
- Block *const block = Block::New(buffer, usedCharLength, reserveMoreSpace, recycler);
- CopyFrom(block);
- return block;
- }
- CompoundString::Block *CompoundString::BlockInfo::Resize(Recycler *const recycler)
- {
- Assert(recycler);
- const CharCount newCharCapacity = GrowCharCapacity(AlignCharCapacityForAllocation(CharLength()));
- if(ShouldAllocateBuffer(newCharCapacity))
- {
- void *const newBuffer = RecyclerNewArray(recycler, char16, newCharCapacity);
- charCapacity = newCharCapacity;
- const CharCount charLength = CharLength();
- js_wmemcpy_s((char16*)newBuffer, charCapacity, (char16*)buffer, charLength);
- buffer = newBuffer;
- return nullptr;
- }
- Block *const block = Block::New(buffer, CharLength(), true, recycler);
- CopyFrom(block);
- return block;
- }
- void CompoundString::BlockInfo::CopyFrom(Block *const block)
- {
- buffer = block->Buffer();
- charLength = block->CharLength();
- charCapacity = block->CharCapacity();
- }
- void CompoundString::BlockInfo::CopyTo(Block *const block)
- {
- Assert(block->Buffer() == buffer);
- Assert(block->CharLength() <= charLength);
- Assert(block->CharCapacity() == charCapacity);
- block->SetCharLength(charLength);
- }
- void CompoundString::BlockInfo::Unreference()
- {
- buffer = nullptr;
- charLength = 0;
- charCapacity = 0;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #endif
- #pragma endregion
- #pragma region CompoundString
- #ifndef IsJsDiag
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CompoundString::CompoundString(const CharCount initialCharCapacity, JavascriptLibrary *const library)
- : LiteralString(library->GetStringTypeStatic()),
- directCharLength(static_cast<CharCount>(-1)),
- ownsLastBlock(true),
- lastBlock(nullptr)
- {
- Assert(library);
- lastBlockInfo.AllocateBuffer(initialCharCapacity, library->GetRecycler());
- }
- CompoundString::CompoundString(
- const CharCount initialBlockSize,
- const bool allocateBlock,
- JavascriptLibrary *const library)
- : LiteralString(library->GetStringTypeStatic()),
- directCharLength(static_cast<CharCount>(-1)),
- ownsLastBlock(true)
- {
- Assert(allocateBlock);
- Assert(library);
- Block *const block = Block::New(initialBlockSize, nullptr, library->GetRecycler());
- lastBlockInfo.CopyFrom(block);
- lastBlock = block;
- }
- CompoundString::CompoundString(
- const CharCount stringLength,
- const CharCount directCharLength,
- const void *const buffer,
- const CharCount usedCharLength,
- const bool reserveMoreSpace,
- JavascriptLibrary *const library)
- : LiteralString(library->GetStringTypeStatic()),
- directCharLength(directCharLength),
- ownsLastBlock(true)
- {
- Assert(directCharLength == static_cast<CharCount>(-1) || directCharLength <= stringLength);
- Assert(buffer);
- Assert(library);
- SetLength(stringLength);
- lastBlock = lastBlockInfo.CopyBuffer(buffer, usedCharLength, reserveMoreSpace, library->GetRecycler());
- }
- CompoundString::CompoundString(CompoundString &other, const bool forAppending)
- : LiteralString(other.GetLibrary()->GetStringTypeStatic()),
- lastBlockInfo(other.lastBlockInfo),
- directCharLength(other.directCharLength),
- lastBlock(other.lastBlock)
- {
- Assert(!other.IsFinalized());
- SetLength(other.GetLength());
- if(forAppending)
- {
- // This compound string will be used for appending, so take ownership of the last block. Appends are fast for a
- // compound string that owns the last block.
- const bool ownsLastBlock = other.ownsLastBlock;
- other.ownsLastBlock = false;
- this->ownsLastBlock = ownsLastBlock;
- if(ownsLastBlock)
- return;
- TakeOwnershipOfLastBlock();
- return;
- }
- ownsLastBlock = false;
- }
- CompoundString *CompoundString::NewWithCharCapacity(
- const CharCount initialCharCapacity,
- JavascriptLibrary *const library)
- {
- const CharCount alignedInitialCharCapacity = BlockInfo::AlignCharCapacityForAllocation(initialCharCapacity);
- if(BlockInfo::ShouldAllocateBuffer(alignedInitialCharCapacity))
- return NewWithBufferCharCapacity(alignedInitialCharCapacity, library);
- return NewWithBlockSize(Block::SizeFromUsedCharLength(initialCharCapacity), library);
- }
- CompoundString *CompoundString::NewWithPointerCapacity(
- const CharCount initialPointerCapacity,
- JavascriptLibrary *const library)
- {
- return NewWithCharCapacity(Block::CharCapacityFromPointerCapacity(initialPointerCapacity), library);
- }
- CompoundString *CompoundString::NewWithBufferCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library)
- {
- Assert(library);
- return RecyclerNew(library->GetRecycler(), CompoundString, initialCharCapacity, library);
- }
- CompoundString *CompoundString::NewWithBlockSize(const CharCount initialBlockSize, JavascriptLibrary *const library)
- {
- Assert(library);
- return RecyclerNew(library->GetRecycler(), CompoundString, initialBlockSize, true, library);
- }
- CompoundString *CompoundString::New(
- const CharCount stringLength,
- const CharCount directCharLength,
- const void *const buffer,
- const CharCount usedCharLength,
- const bool reserveMoreSpace,
- JavascriptLibrary *const library)
- {
- Assert(library);
- return
- RecyclerNew(
- library->GetRecycler(),
- CompoundString,
- stringLength,
- directCharLength,
- buffer,
- usedCharLength,
- reserveMoreSpace,
- library);
- }
- CompoundString *CompoundString::Clone(const bool forAppending)
- {
- return RecyclerNew(GetLibrary()->GetRecycler(), CompoundString, *this, forAppending);
- }
- CompoundString * CompoundString::JitClone(CompoundString * cs)
- {
- Assert(Is(cs));
- return cs->Clone(false);
- }
- CompoundString * CompoundString::JitCloneForAppending(CompoundString * cs)
- {
- Assert(Is(cs));
- return cs->Clone(true);
- }
- bool CompoundString::Is(RecyclableObject *const object)
- {
- return VirtualTableInfo<CompoundString>::HasVirtualTable(object);
- }
- bool CompoundString::Is(const Var var)
- {
- return RecyclableObject::Is(var) && Is(RecyclableObject::FromVar(var));
- }
- CompoundString *CompoundString::FromVar(RecyclableObject *const object)
- {
- Assert(Is(object));
- CompoundString *const cs = static_cast<CompoundString *>(object);
- Assert(!cs->IsFinalized());
- return cs;
- }
- CompoundString *CompoundString::FromVar(const Var var)
- {
- return FromVar(RecyclableObject::FromVar(var));
- }
- JavascriptString *CompoundString::GetImmutableOrScriptUnreferencedString(JavascriptString *const s)
- {
- Assert(s);
- // The provided string may be referenced by script code. A script-unreferenced version of the string is being requested,
- // likely because the provided string will be referenced directly in a concatenation operation (by ConcatString or
- // another CompoundString, for instance). If the provided string is a CompoundString, it must not be mutated by script
- // code after the concatenation operation. In that case, clone the string to ensure that it is not referenced by script
- // code. If the clone is never handed back to script code, it effectively behaves as an immutable string.
- return Is(s) ? FromVar(s)->Clone(false) : s;
- }
- bool CompoundString::ShouldAppendChars(const CharCount appendCharLength)
- {
- return Block::ShouldAppendChars(appendCharLength);
- }
- bool CompoundString::HasOnlyDirectChars() const
- {
- return directCharLength == static_cast<CharCount>(-1);
- }
- void CompoundString::SwitchToPointerMode()
- {
- Assert(HasOnlyDirectChars());
- directCharLength = GetLength();
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(_u("CompoundString::SwitchToPointerMode()\n"));
- Output::Flush();
- }
- }
- bool CompoundString::OwnsLastBlock() const
- {
- return ownsLastBlock;
- }
- const char16 *CompoundString::GetAppendStringBuffer(JavascriptString *const s) const
- {
- Assert(s);
- // A compound string cannot flatten itself while appending itself to itself since flattening would make the append
- // illegal. Clone the string being appended if necessary, before flattening.
- return s == this ? FromVar(s)->Clone(false)->GetSz() : s->GetString();
- }
- char16 *CompoundString::LastBlockChars() const
- {
- return lastBlockInfo.Chars();
- }
- CharCount CompoundString::LastBlockCharLength() const
- {
- return lastBlockInfo.CharLength();
- }
- void CompoundString::SetLastBlockCharLength(const CharCount charLength)
- {
- lastBlockInfo.SetCharLength(charLength);
- }
- CharCount CompoundString::LastBlockCharCapacity() const
- {
- return lastBlockInfo.CharCapacity();
- }
- void **CompoundString::LastBlockPointers() const
- {
- return lastBlockInfo.Pointers();
- }
- CharCount CompoundString::LastBlockPointerLength() const
- {
- return lastBlockInfo.PointerLength();
- }
- void CompoundString::SetLastBlockPointerLength(const CharCount pointerLength)
- {
- lastBlockInfo.SetPointerLength(pointerLength);
- }
- CharCount CompoundString::LastBlockPointerCapacity() const
- {
- return lastBlockInfo.PointerCapacity();
- }
- void CompoundString::PackSubstringInfo(
- const CharCount startIndex,
- const CharCount length,
- void * *const packedSubstringInfoRef,
- void * *const packedSubstringInfo2Ref)
- {
- Assert(static_cast<int32>(startIndex) >= 0);
- Assert(static_cast<int32>(length) >= 0);
- Assert(packedSubstringInfoRef);
- Assert(packedSubstringInfo2Ref);
- #if defined(_M_X64_OR_ARM64)
- // On 64-bit architectures, two nonnegative 32-bit ints fit completely in a tagged pointer
- *packedSubstringInfoRef =
- reinterpret_cast<void *>(
- (static_cast<uintptr_t>(startIndex) << 32) +
- (static_cast<uintptr_t>(length) << 1) +
- 1);
- *packedSubstringInfo2Ref = nullptr;
- #else
- CompileAssert(sizeof(void *) == sizeof(int32));
- // On 32-bit architectures, it will be attempted to fit both pieces of into one pointer by using 16 bits for the
- // start index, 15 for the length, and 1 for the tag. If it does not fit, an additional pointer will be used.
- if(startIndex <= static_cast<CharCount>(0xffff) && length <= static_cast<CharCount>(0x7fff))
- {
- *packedSubstringInfoRef =
- reinterpret_cast<void *>(
- (static_cast<uintptr_t>(startIndex) << 16) +
- (static_cast<uintptr_t>(length) << 1) +
- 1);
- *packedSubstringInfo2Ref = nullptr;
- }
- else
- {
- *packedSubstringInfoRef = reinterpret_cast<void *>((static_cast<uintptr_t>(startIndex) << 1) + 1);
- *packedSubstringInfo2Ref = reinterpret_cast<void *>((static_cast<uintptr_t>(length) << 1) + 1);
- }
- #endif
- #if DBG
- CharCount unpackedStartIndex, unpackedLength;
- UnpackSubstringInfo(*packedSubstringInfoRef, *packedSubstringInfo2Ref, &unpackedStartIndex, &unpackedLength);
- Assert(unpackedStartIndex == startIndex);
- Assert(unpackedLength == length);
- #endif
- }
- #endif
- inline bool CompoundString::IsPackedInfo(void *const pointer)
- {
- Assert(pointer);
- return reinterpret_cast<uintptr_t>(pointer) & 1;
- }
- inline void CompoundString::UnpackSubstringInfo(
- void *const pointer,
- void *const pointer2,
- CharCount *const startIndexRef,
- CharCount *const lengthRef)
- {
- Assert(pointer);
- Assert(startIndexRef);
- Assert(lengthRef);
- const uintptr_t packedSubstringInfo = reinterpret_cast<uintptr_t>(pointer);
- Assert(packedSubstringInfo & 1);
- #if defined(_M_X64_OR_ARM64)
- // On 64-bit architectures, two nonnegative 32-bit ints fit completely in a tagged pointer
- Assert(!pointer2);
- *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 32);
- *lengthRef = static_cast<CharCount>(static_cast<uint32>(packedSubstringInfo) >> 1);
- #else
- CompileAssert(sizeof(void *) == sizeof(int32));
- // On 32-bit architectures, it will be attempted to fit both pieces of into one pointer by using 16 bits for the
- // start index, 15 for the length, and 1 for the tag. If it does not fit, an additional pointer will be used.
- if(!pointer2)
- {
- *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 16);
- *lengthRef = static_cast<CharCount>(static_cast<uint16>(packedSubstringInfo) >> 1);
- }
- else
- {
- *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 1);
- const uintptr_t packedSubstringInfo2 = reinterpret_cast<uintptr_t>(pointer2);
- Assert(packedSubstringInfo2 & 1);
- *lengthRef = static_cast<CharCount>(packedSubstringInfo2 >> 1);
- }
- #endif
- }
- #ifndef IsJsDiag
- void CompoundString::AppendSlow(const char16 c)
- {
- Grow();
- const bool appended =
- HasOnlyDirectChars()
- ? TryAppendGeneric(c, this)
- : TryAppendGeneric(GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, this);
- Assert(appended);
- }
- void CompoundString::AppendSlow(JavascriptString *const s)
- {
- Grow();
- const bool appended = TryAppendGeneric(s, s->GetLength(), this);
- Assert(appended);
- }
- void CompoundString::AppendSlow(
- __in_xcount(appendCharLength) const char16 *const s,
- const CharCount appendCharLength)
- {
- Assert(!IsFinalized());
- Assert(OwnsLastBlock());
- Assert(HasOnlyDirectChars());
- Assert(s);
- // In case of exception, save enough state to revert back to the current state
- const BlockInfo savedLastBlockInfo(lastBlockInfo);
- Block *const savedLastBlock = lastBlock;
- const CharCount savedStringLength = GetLength();
- SetLength(savedStringLength + appendCharLength);
- CharCount copiedCharLength = 0;
- while(true)
- {
- const CharCount blockCharLength = LastBlockCharLength();
- const CharCount copyCharLength =
- min(LastBlockCharCapacity() - blockCharLength, appendCharLength - copiedCharLength);
- CopyHelper(&LastBlockChars()[blockCharLength], &s[copiedCharLength], copyCharLength);
- SetLastBlockCharLength(blockCharLength + copyCharLength);
- copiedCharLength += copyCharLength;
- if(copiedCharLength >= appendCharLength)
- break;
- try
- {
- Grow();
- }
- catch(...)
- {
- lastBlockInfo = savedLastBlockInfo;
- if(savedLastBlock)
- savedLastBlock->SetCharLength(savedLastBlockInfo.CharLength());
- lastBlock = savedLastBlock;
- SetLength(savedStringLength);
- throw;
- }
- }
- Assert(copiedCharLength == appendCharLength);
- }
- void CompoundString::AppendSlow(
- JavascriptString *const s,
- void *const packedSubstringInfo,
- void *const packedSubstringInfo2,
- const CharCount appendCharLength)
- {
- Grow();
- const bool appended = TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, this);
- Assert(appended);
- }
- void CompoundString::PrepareForAppend()
- {
- Assert(!IsFinalized());
- if(OwnsLastBlock())
- return;
- TakeOwnershipOfLastBlock();
- }
- void CompoundString::Append(const char16 c)
- {
- AppendGeneric(c, this, false);
- }
- void CompoundString::AppendChars(const char16 c)
- {
- AppendGeneric(c, this, true);
- }
- void CompoundString::Append(JavascriptString *const s)
- {
- AppendGeneric(s, this, false);
- }
- void CompoundString::AppendChars(JavascriptString *const s)
- {
- AppendGeneric(s, this, true);
- }
- void CompoundString::Append(
- JavascriptString *const s,
- const CharCount startIndex,
- const CharCount appendCharLength)
- {
- AppendGeneric(s, startIndex, appendCharLength, this, false);
- }
- void CompoundString::AppendChars(
- JavascriptString *const s,
- const CharCount startIndex,
- const CharCount appendCharLength)
- {
- AppendGeneric(s, startIndex, appendCharLength, this, true);
- }
- void CompoundString::Append(
- __in_xcount(appendCharLength) const char16 *const s,
- const CharCount appendCharLength)
- {
- AppendGeneric(s, appendCharLength, this, false);
- }
- void CompoundString::AppendChars(
- __in_xcount(appendCharLength) const char16 *const s,
- const CharCount appendCharLength)
- {
- AppendGeneric(s, appendCharLength, this, true);
- }
- void CompoundString::AppendCharsSz(__in_z const char16 *const s)
- {
- size_t len = wcslen(s);
- // We limit the length of the string to MaxCharCount,
- // so just OOM if we are appending a string that exceed this limit already
- if (!IsValidCharCount(len))
- {
- JavascriptExceptionOperators::ThrowOutOfMemory(this->GetScriptContext());
- }
- AppendChars(s, (CharCount)len);
- }
- void CompoundString::Grow()
- {
- Assert(!IsFinalized());
- Assert(OwnsLastBlock());
- Block *const lastBlock = this->lastBlock;
- if(!lastBlock)
- {
- // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. In that case it is always
- // within the threshold to resize. Resize the buffer or resize it into a new block depending on its size.
- this->lastBlock = lastBlockInfo.Resize(GetLibrary()->GetRecycler());
- return;
- }
- lastBlockInfo.CopyTo(lastBlock);
- Block *const newLastBlock = lastBlock->Chain(GetLibrary()->GetRecycler());
- lastBlockInfo.CopyFrom(newLastBlock);
- this->lastBlock = newLastBlock;
- }
- void CompoundString::TakeOwnershipOfLastBlock()
- {
- Assert(!IsFinalized());
- Assert(!OwnsLastBlock());
- // Another string object owns the last block's buffer. The buffer must be copied, or another block must be chained.
- Block *const lastBlock = this->lastBlock;
- if(!lastBlock)
- {
- // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. In that case it is always
- // within the threshold to resize. Resize the buffer or resize it into a new block depending on its size.
- this->lastBlock = lastBlockInfo.Resize(GetLibrary()->GetRecycler());
- ownsLastBlock = true;
- return;
- }
- // The last block is already in a chain, or is over the threshold to resize. Shallow-clone the last block (clone
- // just its metadata, while still pointing to the original buffer), and chain it to a new last block.
- Recycler *const recycler = GetLibrary()->GetRecycler();
- Block *const newLastBlock = lastBlock->Clone(LastBlockCharLength(), recycler)->Chain(recycler);
- lastBlockInfo.CopyFrom(newLastBlock);
- ownsLastBlock = true;
- this->lastBlock = newLastBlock;
- }
- void CompoundString::Unreference()
- {
- lastBlockInfo.Unreference();
- directCharLength = 0;
- ownsLastBlock = false;
- lastBlock = nullptr;
- }
- const char16 *CompoundString::GetSz()
- {
- Assert(!IsFinalized());
- const CharCount totalCharLength = GetLength();
- switch(totalCharLength)
- {
- case 0:
- {
- Unreference();
- const char16 *const buffer = _u("");
- SetBuffer(buffer);
- VirtualTableInfo<LiteralString>::SetVirtualTable(this);
- return buffer;
- }
- case 1:
- {
- Assert(HasOnlyDirectChars());
- Assert(LastBlockCharLength() == 1);
- const char16 *const buffer = GetLibrary()->GetCharStringCache().GetStringForChar(LastBlockChars()[0])->UnsafeGetBuffer();
- Unreference();
- SetBuffer(buffer);
- VirtualTableInfo<LiteralString>::SetVirtualTable(this);
- return buffer;
- }
- }
- if(OwnsLastBlock() && HasOnlyDirectChars() && !lastBlock && TryAppendGeneric(_u('\0'), this)) // GetSz() requires null termination
- {
- // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. Since this string owns the
- // last block, has only direct chars, and the buffer was allocated directly (buffer pointer is not an internal
- // pointer), there is no need to copy the buffer.
- SetLength(totalCharLength); // terminating null should not count towards the string length
- const char16 *const buffer = LastBlockChars();
- Unreference();
- SetBuffer(buffer);
- VirtualTableInfo<LiteralString>::SetVirtualTable(this);
- return buffer;
- }
- char16 *const buffer = RecyclerNewArrayLeaf(GetScriptContext()->GetRecycler(), char16, SafeSzSize(totalCharLength));
- buffer[totalCharLength] = _u('\0'); // GetSz() requires null termination
- Copy<CompoundString>(buffer, totalCharLength);
- Assert(buffer[totalCharLength] == _u('\0'));
- Unreference();
- SetBuffer(buffer);
- VirtualTableInfo<LiteralString>::SetVirtualTable(this);
- return buffer;
- }
- void CompoundString::CopyVirtual(
- _Out_writes_(m_charLength) char16 *const buffer,
- StringCopyInfoStack &nestedStringTreeCopyInfos,
- const byte recursionDepth)
- {
- Assert(!IsFinalized());
- Assert(buffer);
- const CharCount totalCharLength = GetLength();
- switch(totalCharLength)
- {
- case 0:
- return;
- case 1:
- Assert(HasOnlyDirectChars());
- Assert(LastBlockCharLength() == 1);
- buffer[0] = LastBlockChars()[0];
- return;
- }
- // Copy buffers from string pointers
- const bool hasOnlyDirectChars = HasOnlyDirectChars();
- const CharCount directCharLength = hasOnlyDirectChars ? totalCharLength : this->directCharLength;
- CharCount remainingCharLengthToCopy = totalCharLength;
- const Block *const lastBlock = this->lastBlock;
- const Block *block = lastBlock;
- void *const *blockPointers = LastBlockPointers();
- CharCount pointerIndex = LastBlockPointerLength();
- while(remainingCharLengthToCopy > directCharLength)
- {
- while(pointerIndex == 0)
- {
- Assert(block);
- block = block->Previous();
- Assert(block);
- blockPointers = block->Pointers();
- pointerIndex = block->PointerLength();
- }
- void *const pointer = blockPointers[--pointerIndex];
- if(IsPackedInfo(pointer))
- {
- Assert(pointerIndex != 0);
- void *pointer2 = blockPointers[--pointerIndex];
- JavascriptString *s;
- #if defined(_M_X64_OR_ARM64)
- Assert(!IsPackedInfo(pointer2));
- #else
- if(IsPackedInfo(pointer2))
- {
- Assert(pointerIndex != 0);
- s = JavascriptString::FromVar(blockPointers[--pointerIndex]);
- }
- else
- #endif
- {
- s = JavascriptString::FromVar(pointer2);
- pointer2 = nullptr;
- }
- CharCount startIndex, copyCharLength;
- UnpackSubstringInfo(pointer, pointer2, &startIndex, ©CharLength);
- Assert(startIndex <= s->GetLength());
- Assert(copyCharLength <= s->GetLength() - startIndex);
- Assert(remainingCharLengthToCopy >= copyCharLength);
- remainingCharLengthToCopy -= copyCharLength;
- CopyHelper(&buffer[remainingCharLengthToCopy], &s->GetString()[startIndex], copyCharLength);
- }
- else
- {
- JavascriptString *const s = JavascriptString::FromVar(pointer);
- const CharCount copyCharLength = s->GetLength();
- Assert(remainingCharLengthToCopy >= copyCharLength);
- remainingCharLengthToCopy -= copyCharLength;
- if(recursionDepth == MaxCopyRecursionDepth && s->IsTree())
- {
- // Don't copy nested string trees yet, as that involves a recursive call, and the recursion can become
- // excessive. Just collect the nested string trees and the buffer location where they should be copied, and
- // the caller can deal with those after returning.
- nestedStringTreeCopyInfos.Push(StringCopyInfo(s, &buffer[remainingCharLengthToCopy]));
- }
- else
- {
- Assert(recursionDepth <= MaxCopyRecursionDepth);
- s->Copy(&buffer[remainingCharLengthToCopy], nestedStringTreeCopyInfos, recursionDepth + 1);
- }
- }
- }
- Assert(remainingCharLengthToCopy == directCharLength);
- if(remainingCharLengthToCopy != 0)
- {
- // Determine the number of direct chars in the current block
- CharCount blockCharLength;
- if(pointerIndex == 0)
- {
- // The string switched to pointer mode at the beginning of the current block, or the string never switched to
- // pointer mode and the last block is empty. In either case, direct chars span to the end of the previous block.
- Assert(block);
- block = block->Previous();
- Assert(block);
- blockCharLength = block->CharLength();
- }
- else if(hasOnlyDirectChars)
- {
- // The string never switched to pointer mode, so the current block's char length is where direct chars end
- blockCharLength = block == lastBlock ? LastBlockCharLength() : block->CharLength();
- }
- else
- {
- // The string switched to pointer mode somewhere in the middle of the current block. To determine where direct
- // chars end in this block, all previous blocks are scanned and their char lengths discounted.
- blockCharLength = remainingCharLengthToCopy;
- if(block)
- {
- for(const Block *previousBlock = block->Previous();
- previousBlock;
- previousBlock = previousBlock->Previous())
- {
- Assert(blockCharLength >= previousBlock->CharLength());
- blockCharLength -= previousBlock->CharLength();
- }
- }
- Assert(Block::PointerLengthFromCharLength(blockCharLength) == pointerIndex);
- }
- // Copy direct chars
- const char16 *blockChars = block == lastBlock ? LastBlockChars() : block->Chars();
- while(true)
- {
- if(blockCharLength != 0)
- {
- Assert(remainingCharLengthToCopy >= blockCharLength);
- remainingCharLengthToCopy -= blockCharLength;
- js_wmemcpy_s(&buffer[remainingCharLengthToCopy], blockCharLength, blockChars, blockCharLength);
- if(remainingCharLengthToCopy == 0)
- break;
- }
- Assert(block);
- block = block->Previous();
- Assert(block);
- blockChars = block->Chars();
- blockCharLength = block->CharLength();
- }
- }
- #if DBG
- // Verify that all nonempty blocks have been visited
- if(block)
- {
- while(true)
- {
- block = block->Previous();
- if(!block)
- break;
- Assert(block->CharLength() == 0);
- }
- }
- #endif
- Assert(remainingCharLengthToCopy == 0);
- }
- bool CompoundString::IsTree() const
- {
- Assert(!IsFinalized());
- return !HasOnlyDirectChars();
- }
- DEFINE_RECYCLER_TRACKER_PERF_COUNTER(CompoundString);
- CompileAssert(static_cast<CharCount>(-1) > static_cast<CharCount>(0)); // CharCount is assumed to be unsigned
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #endif
- #pragma endregion
- }
|