CompoundString.h 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace Js
  7. {
  8. // -------------------------------------------------------------------------------------------------------------------------
  9. // Storage
  10. // -------------------------------------------------------------------------------------------------------------------------
  11. //
  12. // CompoundString uses available buffer space to directly store characters or pointers, or to pack information such as a
  13. // substring's start index and length. It is optimized for concatenation. A compound string begins in direct character mode,
  14. // where it appends characters directly to the buffers. When a somewhat larger string is concatenated, the compound string
  15. // switches to pointer mode and records the direct character length. From that point onwards, only pointers or packed
  16. // information is stored in the buffers. Each piece of packed information is stored as a pointer with the lowest bit tagged.
  17. //
  18. // A compound string may have several chained Block objects, each with a buffer allocated inline with the block. The
  19. // compound string references only the last block in the chain (to save space), and each block references its previous block
  20. // in the chain. As a consequence, during flattening, blocks are iterated backwards and flattening is also done backwards.
  21. //
  22. // -------------------------------------------------------------------------------------------------------------------------
  23. // Using as a character-only string builder
  24. // -------------------------------------------------------------------------------------------------------------------------
  25. //
  26. // Using the AppendChars set of functions requires that the compound string is in direct character mode, and forces it to
  27. // remain in direct character mode by appending all characters directly to the buffer. Those functions can be used to build
  28. // a string like a typical character-only string builder. Flattening is much faster when in direct character mode, and the
  29. // AppendChars set of functions also get to omit the check to see if the compound string is in direct character mode, but it
  30. // is at the cost of having to append all characters even in the case of appending large strings, instead of just appending
  31. // a pointer.
  32. //
  33. // -------------------------------------------------------------------------------------------------------------------------
  34. // Appending
  35. // -------------------------------------------------------------------------------------------------------------------------
  36. //
  37. // The compound string and builder have simple Append and AppendChars functions that delegate to a set of AppendGeneric
  38. // functions that do the actual work. AppendGeneric functions are templatized and their implementation is shared between the
  39. // compound string and builder.
  40. //
  41. // After determining how to append, the AppendGeneric functions call a TryAppendGeneric function that will perform the
  42. // append if there is enough space in the last block's buffer. If there is no space, the AppendGeneric functions call a
  43. // AppendSlow function. In a compound string, the AppendSlow function grows the buffer or creates a new chained block, and
  44. // performs the append. In a builder, the AppendSlow function creates the compound string and delegates to it from that
  45. // point onwards.
  46. //
  47. // -------------------------------------------------------------------------------------------------------------------------
  48. // Buffer sharing and ownership
  49. // -------------------------------------------------------------------------------------------------------------------------
  50. //
  51. // Multiple compound string objects may reference and use the same buffers. Cloning a compound string creates a new object
  52. // that references the same buffer. However, only one compound string may own the last block at any given time, since the
  53. // last block's buffer is mutable through concatenation. Compound string objects that don't own the last block keep track of
  54. // the character length of the buffer in their last block info (BlockInfo object), since the block's length may be changed
  55. // by the block's owner.
  56. //
  57. // When a concatenation operation is performed on a compound string that does not own the last block, it will first need to
  58. // take ownership of that block. So, it is necessary to call PrepareForAppend() before the first append operation for a
  59. // compound string whose buffers may be shared. Taking ownership of a block is done by either resizing the block (and hence
  60. // copying the buffer up to the point to which it is used), or shallow-cloning the last block and chaining a new block to
  61. // it. Shallow cloning copies the block's metadata but does not copy the buffer. The character length of the clone is set to
  62. // the length of the portion of the buffer that is used by the compound string. The cloned block references the original
  63. // block that owns the buffer, for access to the buffer. Once a new block is chained to it, it then becomes the last block,
  64. // effectively making the clone immutable by the compound string.
  65. //
  66. // -------------------------------------------------------------------------------------------------------------------------
  67. // Last block info (BlockInfo object)
  68. // -------------------------------------------------------------------------------------------------------------------------
  69. //
  70. // Blocks are only created once chaining begins. Until then, the buffer is allocated directly and stored in the last block
  71. // info. The buffer is resized until it reaches a threshold, and upon the final resize before chaining begins, a block is
  72. // created. From that point onwards, blocks are no longer resized and only chained, although a new chained block may be
  73. // larger than the previous block.
  74. //
  75. // The last block info is also used to serve as a cache for information in the last block. Since the last block is where
  76. // concatenation occurs, it is significantly beneficial to prevent having to dereference the last block to get to its
  77. // information. So, the BlockInfo object representing the last block info caches the last block's information and only it is
  78. // used during append operations. Only when space runs out, is the actual last block updated with information from the last
  79. // block info. As a consequence of this and the fact that multiple compound strings may share blocks, the last block's
  80. // character length may not be up-to-date, or may not be relevant to the compound string querying it, so it should never be
  81. // queried except in specific cases where it is guaranteed to be correct.
  82. //
  83. // -------------------------------------------------------------------------------------------------------------------------
  84. // Builder
  85. // -------------------------------------------------------------------------------------------------------------------------
  86. //
  87. // The builder uses stack-allocated space for the initial buffer. It may perform better in some scenarios, but the tradeoff
  88. // is that it is at the cost of an additional check per append.
  89. //
  90. // It typically performs better in cases where the number of concatenations is highly unpredictable and may range from just
  91. // a few to a large number:
  92. // - For few concatenations, the final compound string's buffer will be the minimum size necessary, so it helps by
  93. // saving space, and as a result, performing a faster allocation
  94. // - For many concatenations, the use of stack space reduces the number of allocations that would otherwise be necessary
  95. // to grow the buffer
  96. class CompoundString sealed : public LiteralString // vtable will be switched to LiteralString's vtable after flattening
  97. {
  98. #pragma region CompoundString::Block
  99. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  100. private:
  101. class Block
  102. {
  103. public:
  104. static const uint ChainSizeThreshold;
  105. private:
  106. static const uint MaxChainedBlockSize;
  107. private:
  108. Field(Block *const) bufferOwner;
  109. Field(CharCount) charLength;
  110. Field(CharCount) charCapacity;
  111. Field(const Block *const) previous;
  112. private:
  113. Block(const CharCount charCapacity, const Block *const previous);
  114. Block(const void *const buffer, const CharCount charLength, const CharCount charCapacity);
  115. Block(const Block &other, const CharCount usedCharLength);
  116. public:
  117. static Block *New(const uint size, const Block *const previous, Recycler *const recycler);
  118. static Block *New(const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, Recycler *const recycler);
  119. Block *Clone(const CharCount usedCharLength, Recycler *const recycler) const;
  120. private:
  121. static CharCount CharCapacityFromSize(const uint size);
  122. static uint SizeFromCharCapacity(const CharCount charCapacity);
  123. private:
  124. static CharCount PointerAlign(const CharCount charLength);
  125. public:
  126. static const char16 *Chars(const void *const buffer);
  127. static char16 *Chars(void *const buffer);
  128. static const Field(void*) *Pointers(const void *const buffer);
  129. static Field(void*) *Pointers(void *const buffer);
  130. static CharCount PointerCapacityFromCharCapacity(const CharCount charCapacity);
  131. static CharCount CharCapacityFromPointerCapacity(const CharCount pointerCapacity);
  132. static CharCount PointerLengthFromCharLength(const CharCount charLength);
  133. static CharCount CharLengthFromPointerLength(const CharCount pointerLength);
  134. static uint SizeFromUsedCharLength(const CharCount usedCharLength);
  135. public:
  136. static bool ShouldAppendChars(const CharCount appendCharLength, const uint additionalSizeForPointerAppend = 0);
  137. public:
  138. const void *Buffer() const;
  139. void *Buffer();
  140. const Block *Previous() const;
  141. public:
  142. const char16 *Chars() const;
  143. char16 *Chars();
  144. CharCount CharLength() const;
  145. void SetCharLength(const CharCount charLength);
  146. CharCount CharCapacity() const;
  147. public:
  148. const Field(void*) *Pointers() const;
  149. Field(void*) *Pointers();
  150. CharCount PointerLength() const;
  151. CharCount PointerCapacity() const;
  152. private:
  153. static uint GrowSize(const uint size);
  154. static uint GrowSizeForChaining(const uint size);
  155. public:
  156. Block *Chain(Recycler *const recycler);
  157. private:
  158. PREVENT_COPY(Block);
  159. };
  160. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. #pragma endregion
  162. #pragma region CompoundString::BlockInfo
  163. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  164. private:
  165. class BlockInfo
  166. {
  167. private:
  168. Field(void *) buffer;
  169. Field(CharCount) charLength;
  170. Field(CharCount) charCapacity;
  171. public:
  172. BlockInfo();
  173. BlockInfo(Block *const block);
  174. public:
  175. char16 *Chars() const;
  176. CharCount CharLength() const;
  177. void SetCharLength(const CharCount charLength);
  178. CharCount CharCapacity() const;
  179. public:
  180. Field(void*) *Pointers() const;
  181. CharCount PointerLength() const;
  182. void SetPointerLength(const CharCount pointerLength);
  183. CharCount PointerCapacity() const;
  184. public:
  185. static CharCount AlignCharCapacityForAllocation(const CharCount charCapacity);
  186. static CharCount GrowCharCapacity(const CharCount charCapacity);
  187. static bool ShouldAllocateBuffer(const CharCount charCapacity);
  188. void AllocateBuffer(const CharCount charCapacity, Recycler *const recycler);
  189. Block *CopyBuffer(const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, Recycler *const recycler);
  190. Block *Resize(Recycler *const recycler);
  191. static size_t GetOffsetOfCharLength() { return offsetof(BlockInfo, charLength); }
  192. static size_t GetOffsetOfCharCapacity() { return offsetof(BlockInfo, charCapacity); }
  193. static size_t GetOffsetOfBuffer() { return offsetof(BlockInfo, buffer); }
  194. public:
  195. void CopyFrom(Block *const block);
  196. void CopyTo(Block *const block);
  197. void Unreference();
  198. };
  199. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  200. #pragma endregion
  201. #pragma region CompoundString::Builder
  202. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. public:
  204. template<CharCount MinimumCharCapacity>
  205. class Builder
  206. {
  207. private:
  208. // Array size needs to be a constant expression. This expression is equivalent to
  209. // Block::PointerLengthFromCharLength(MinimumCharCapacity), and generates a pointer capacity that equates to a char
  210. // capacity that is >= MinimumCharCapacity.
  211. void *buffer[
  212. (
  213. (MinimumCharCapacity + sizeof(void *) / sizeof(char16) - 1) &
  214. ~(sizeof(void *) / sizeof(char16) - 1)
  215. ) / (sizeof(void *) / sizeof(char16))];
  216. CharCount stringLength;
  217. CharCount charLength;
  218. CharCount directCharLength;
  219. CompoundString *compoundString;
  220. ScriptContext *const scriptContext;
  221. #if DBG
  222. bool isFinalized;
  223. #endif
  224. public:
  225. Builder(ScriptContext *const scriptContext);
  226. private:
  227. bool IsFinalized() const;
  228. bool HasOnlyDirectChars() const;
  229. void SwitchToPointerMode();
  230. bool OwnsLastBlock() const;
  231. const char16 *GetAppendStringBuffer(JavascriptString *const s) const;
  232. ScriptContext *GetScriptContext() const;
  233. JavascriptLibrary *GetLibrary() const;
  234. private:
  235. char16 *LastBlockChars();
  236. CharCount LastBlockCharLength() const;
  237. void SetLastBlockCharLength(const CharCount charLength);
  238. CharCount LastBlockCharCapacity() const;
  239. private:
  240. Field(void*) *LastBlockPointers();
  241. CharCount LastBlockPointerLength() const;
  242. void SetLastBlockPointerLength(const CharCount pointerLength);
  243. CharCount LastBlockPointerCapacity() const;
  244. private:
  245. CharCount GetLength() const;
  246. void SetLength(const CharCount stringLength);
  247. private:
  248. void AppendSlow(const char16 c);
  249. void AppendSlow(JavascriptString *const s);
  250. void AppendSlow(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  251. void AppendSlow(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength);
  252. public:
  253. void Append(const char16 c);
  254. void AppendChars(const char16 c);
  255. void Append(JavascriptString *const s);
  256. void AppendChars(JavascriptString *const s);
  257. void Append(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  258. void AppendChars(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  259. template<CharCount AppendCharLengthPlusOne> void Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  260. template<CharCount AppendCharLengthPlusOne> void AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  261. void Append(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  262. void AppendChars(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  263. template<class TValue, class FConvertToString> void Append(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  264. template<class TValue, class FConvertToString> void AppendChars(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  265. private:
  266. CompoundString *CreateCompoundString(const bool reserveMoreSpace) const;
  267. public:
  268. JavascriptString *ToString();
  269. friend CompoundString;
  270. PREVENT_COPY(Builder);
  271. };
  272. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  273. #pragma endregion
  274. #pragma region CompoundString
  275. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  276. private:
  277. Field(BlockInfo) lastBlockInfo;
  278. Field(CharCount) directCharLength;
  279. Field(bool) ownsLastBlock;
  280. Field(Block *) lastBlock;
  281. private:
  282. CompoundString(const CharCount initialCharCapacity, JavascriptLibrary *const library);
  283. CompoundString(const CharCount initialBlockSize, const bool allocateBlock, JavascriptLibrary *const library);
  284. CompoundString(const CharCount stringLength, const CharCount directCharLength, const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, JavascriptLibrary *const library);
  285. CompoundString(CompoundString &other, const bool forAppending);
  286. public:
  287. static CompoundString *NewWithCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library);
  288. static CompoundString *NewWithPointerCapacity(const CharCount initialPointerCapacity, JavascriptLibrary *const library);
  289. private:
  290. static CompoundString *NewWithBufferCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library);
  291. static CompoundString *NewWithBlockSize(const CharCount initialBlockSize, JavascriptLibrary *const library);
  292. static CompoundString *New(const CharCount stringLength, const CharCount directCharLength, const void *const buffer, const CharCount usedCharLength, const bool reserveMoreSpace, JavascriptLibrary *const library);
  293. public:
  294. CompoundString *Clone(const bool forAppending);
  295. static CompoundString * JitClone(CompoundString * cs);
  296. static CompoundString * JitCloneForAppending(CompoundString * cs);
  297. public:
  298. static size_t GetOffsetOfOwnsLastBlock() { return offsetof(CompoundString, ownsLastBlock); }
  299. static size_t GetOffsetOfDirectCharLength() { return offsetof(CompoundString, directCharLength); }
  300. static size_t GetOffsetOfLastBlockInfo() { return offsetof(CompoundString, lastBlockInfo); }
  301. static size_t GetOffsetOfLastBlockInfoCharLength() { return CompoundString::BlockInfo::GetOffsetOfCharLength(); }
  302. static size_t GetOffsetOfLastBlockInfoCharCapacity() { return CompoundString::BlockInfo::GetOffsetOfCharCapacity(); }
  303. static size_t GetOffsetOfLastBlockInfoBuffer() { return CompoundString::BlockInfo::GetOffsetOfBuffer(); }
  304. public:
  305. static JavascriptString *GetImmutableOrScriptUnreferencedString(JavascriptString *const s);
  306. static bool ShouldAppendChars(const CharCount appendCharLength);
  307. private:
  308. bool HasOnlyDirectChars() const;
  309. void SwitchToPointerMode();
  310. bool OwnsLastBlock() const;
  311. const char16 *GetAppendStringBuffer(JavascriptString *const s) const;
  312. private:
  313. char16 *LastBlockChars() const;
  314. CharCount LastBlockCharLength() const;
  315. void SetLastBlockCharLength(const CharCount charLength);
  316. CharCount LastBlockCharCapacity() const;
  317. private:
  318. Field(void*) *LastBlockPointers() const;
  319. CharCount LastBlockPointerLength() const;
  320. void SetLastBlockPointerLength(const CharCount pointerLength);
  321. CharCount LastBlockPointerCapacity() const;
  322. private:
  323. static void PackSubstringInfo(const CharCount startIndex, const CharCount length, void * *const packedSubstringInfoRef, void * *const packedSubstringInfo2Ref);
  324. public:
  325. static bool IsPackedInfo(void *const pointer);
  326. static void UnpackSubstringInfo(void *const pointer, void *const pointer2, CharCount *const startIndexRef, CharCount *const lengthRef);
  327. private:
  328. template<class String> static bool TryAppendGeneric(const char16 c, String *const toString);
  329. template<class String> static bool TryAppendGeneric(JavascriptString *const s, const CharCount appendCharLength, String *const toString);
  330. template<class String> static bool TryAppendFewCharsGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString);
  331. template<class String> static bool TryAppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString);
  332. template<class String> static bool TryAppendGeneric(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength, String *const toString);
  333. private:
  334. template<class String> static void AppendGeneric(const char16 c, String *const toString, const bool appendChars);
  335. template<class String> static void AppendGeneric(JavascriptString *const s, String *const toString, const bool appendChars);
  336. template<class String> static void AppendGeneric(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength, String *const toString, const bool appendChars);
  337. template<CharCount AppendCharLengthPlusOne, class String> static void AppendGeneric(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral, String *const toString, const bool appendChars);
  338. template<class String> static void AppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString, const bool appendChars);
  339. template<class TValue, class FConvertToString, class String> static void AppendGeneric(const TValue &value, CharCount maximumAppendCharLength, const FConvertToString ConvertToString, String *const toString, const bool appendChars);
  340. private:
  341. void AppendSlow(const char16 c);
  342. void AppendSlow(JavascriptString *const s);
  343. void AppendSlow(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  344. void AppendSlow(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength);
  345. public:
  346. void PrepareForAppend();
  347. void Append(const char16 c);
  348. void AppendChars(const char16 c);
  349. void Append(JavascriptString *const s);
  350. void AppendChars(JavascriptString *const s);
  351. void Append(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  352. void AppendChars(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  353. template<CharCount AppendCharLengthPlusOne> void Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  354. template<CharCount AppendCharLengthPlusOne> void AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  355. void Append(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  356. void AppendChars(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  357. void AppendCharsSz(__in_z const char16 *const s);
  358. template<class TValue, class FConvertToString> void Append(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  359. template<class TValue, class FConvertToString> void AppendChars(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  360. private:
  361. void Grow();
  362. void TakeOwnershipOfLastBlock();
  363. private:
  364. void Unreference();
  365. public:
  366. virtual const char16 *GetSz() override sealed;
  367. using JavascriptString::Copy;
  368. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed;
  369. virtual bool IsTree() const override sealed;
  370. protected:
  371. DEFINE_VTABLE_CTOR(CompoundString, LiteralString);
  372. private:
  373. PREVENT_COPY(CompoundString);
  374. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  375. #pragma endregion
  376. public:
  377. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  378. {
  379. return VTableValue::VtableCompoundString;
  380. }
  381. };
  382. template <> bool VarIsImpl<CompoundString>(RecyclableObject * object);
  383. #pragma region CompoundString::Builder definition
  384. #ifndef CompoundStringJsDiag
  385. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  386. template<CharCount MinimumCharCapacity>
  387. CompoundString::Builder<MinimumCharCapacity>::Builder(ScriptContext *const scriptContext)
  388. : stringLength(0),
  389. charLength(0),
  390. directCharLength(static_cast<CharCount>(-1)),
  391. compoundString(nullptr),
  392. scriptContext(scriptContext)
  393. #if DBG
  394. , isFinalized(false)
  395. #endif
  396. {
  397. CompileAssert(MinimumCharCapacity != 0);
  398. Assert(LastBlockCharCapacity() >= MinimumCharCapacity);
  399. }
  400. template<CharCount MinimumCharCapacity>
  401. bool CompoundString::Builder<MinimumCharCapacity>::IsFinalized() const
  402. {
  403. #if DBG
  404. return isFinalized;
  405. #else
  406. return false;
  407. #endif
  408. }
  409. template<CharCount MinimumCharCapacity>
  410. bool CompoundString::Builder<MinimumCharCapacity>::HasOnlyDirectChars() const
  411. {
  412. return directCharLength == static_cast<CharCount>(-1);
  413. }
  414. template<CharCount MinimumCharCapacity>
  415. void CompoundString::Builder<MinimumCharCapacity>::SwitchToPointerMode()
  416. {
  417. Assert(HasOnlyDirectChars());
  418. directCharLength = charLength;
  419. if(PHASE_TRACE_StringConcat)
  420. {
  421. Output::Print(_u("CompoundString::SwitchToPointerMode() - directCharLength = %u\n"), directCharLength);
  422. Output::Flush();
  423. }
  424. }
  425. template<CharCount MinimumCharCapacity>
  426. bool CompoundString::Builder<MinimumCharCapacity>::OwnsLastBlock() const
  427. {
  428. return true;
  429. }
  430. template<CharCount MinimumCharCapacity>
  431. inline const char16 *CompoundString::Builder<MinimumCharCapacity>::GetAppendStringBuffer(
  432. JavascriptString *const s) const
  433. {
  434. Assert(s);
  435. return s->GetString();
  436. }
  437. template<CharCount MinimumCharCapacity>
  438. ScriptContext *CompoundString::Builder<MinimumCharCapacity>::GetScriptContext() const
  439. {
  440. return scriptContext;
  441. }
  442. template<CharCount MinimumCharCapacity>
  443. JavascriptLibrary *CompoundString::Builder<MinimumCharCapacity>::GetLibrary() const
  444. {
  445. return scriptContext->GetLibrary();
  446. }
  447. template<CharCount MinimumCharCapacity>
  448. char16 *CompoundString::Builder<MinimumCharCapacity>::LastBlockChars()
  449. {
  450. return Block::Chars(buffer);
  451. }
  452. template<CharCount MinimumCharCapacity>
  453. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharLength() const
  454. {
  455. return charLength;
  456. }
  457. template<CharCount MinimumCharCapacity>
  458. void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockCharLength(const CharCount charLength)
  459. {
  460. this->charLength = charLength;
  461. }
  462. template<CharCount MinimumCharCapacity>
  463. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharCapacity() const
  464. {
  465. return Block::CharCapacityFromPointerCapacity(LastBlockPointerCapacity());
  466. }
  467. template<CharCount MinimumCharCapacity>
  468. Field(void*) *CompoundString::Builder<MinimumCharCapacity>::LastBlockPointers()
  469. {
  470. return Block::Pointers(buffer);
  471. }
  472. template<CharCount MinimumCharCapacity>
  473. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerLength() const
  474. {
  475. return Block::PointerLengthFromCharLength(charLength);
  476. }
  477. template<CharCount MinimumCharCapacity>
  478. void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockPointerLength(const CharCount pointerLength)
  479. {
  480. charLength = Block::CharLengthFromPointerLength(pointerLength);
  481. }
  482. template<CharCount MinimumCharCapacity>
  483. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerCapacity() const
  484. {
  485. return _countof(buffer);
  486. }
  487. template<CharCount MinimumCharCapacity>
  488. CharCount CompoundString::Builder<MinimumCharCapacity>::GetLength() const
  489. {
  490. return stringLength;
  491. }
  492. template<CharCount MinimumCharCapacity>
  493. void CompoundString::Builder<MinimumCharCapacity>::SetLength(const CharCount stringLength)
  494. {
  495. if(!IsValidCharCount(stringLength))
  496. Throw::OutOfMemory();
  497. this->stringLength = stringLength;
  498. }
  499. template<CharCount MinimumCharCapacity>
  500. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(const char16 c)
  501. {
  502. Assert(!this->compoundString);
  503. CompoundString *const compoundString = CreateCompoundString(true);
  504. this->compoundString = compoundString;
  505. const bool appended =
  506. HasOnlyDirectChars()
  507. ? TryAppendGeneric(c, compoundString)
  508. : TryAppendGeneric(GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, compoundString);
  509. Assert(appended);
  510. }
  511. template<CharCount MinimumCharCapacity>
  512. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(JavascriptString *const s)
  513. {
  514. Assert(!this->compoundString);
  515. CompoundString *const compoundString = CreateCompoundString(true);
  516. this->compoundString = compoundString;
  517. const bool appended = TryAppendGeneric(s, s->GetLength(), compoundString);
  518. Assert(appended);
  519. }
  520. template<CharCount MinimumCharCapacity>
  521. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(
  522. __in_xcount(appendCharLength) const char16 *const s,
  523. const CharCount appendCharLength)
  524. {
  525. // Even though CreateCompoundString() will create a compound string with some additional space reserved for appending,
  526. // the amount of space available may still not be enough, so need to check and fall back to the slow path as well
  527. Assert(!this->compoundString);
  528. CompoundString *const compoundString = CreateCompoundString(true);
  529. this->compoundString = compoundString;
  530. if(TryAppendGeneric(s, appendCharLength, compoundString))
  531. return;
  532. compoundString->AppendSlow(s, appendCharLength);
  533. }
  534. template<CharCount MinimumCharCapacity>
  535. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(
  536. JavascriptString *const s,
  537. void *const packedSubstringInfo,
  538. void *const packedSubstringInfo2,
  539. const CharCount appendCharLength)
  540. {
  541. Assert(!this->compoundString);
  542. CompoundString *const compoundString = CreateCompoundString(true);
  543. this->compoundString = compoundString;
  544. const bool appended = TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, compoundString);
  545. Assert(appended);
  546. }
  547. template<CharCount MinimumCharCapacity>
  548. inline void CompoundString::Builder<MinimumCharCapacity>::Append(const char16 c)
  549. {
  550. if(!compoundString)
  551. {
  552. AppendGeneric(c, this, false);
  553. return;
  554. }
  555. compoundString->Append(c);
  556. }
  557. template<CharCount MinimumCharCapacity>
  558. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(const char16 c)
  559. {
  560. if(!compoundString)
  561. {
  562. AppendGeneric(c, this, true);
  563. return;
  564. }
  565. compoundString->AppendChars(c);
  566. }
  567. template<CharCount MinimumCharCapacity>
  568. inline void CompoundString::Builder<MinimumCharCapacity>::Append(JavascriptString *const s)
  569. {
  570. if(!compoundString)
  571. {
  572. AppendGeneric(s, this, false);
  573. return;
  574. }
  575. compoundString->Append(s);
  576. }
  577. template<CharCount MinimumCharCapacity>
  578. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(JavascriptString *const s)
  579. {
  580. if(!compoundString)
  581. {
  582. AppendGeneric(s, this, true);
  583. return;
  584. }
  585. compoundString->AppendChars(s);
  586. }
  587. template<CharCount MinimumCharCapacity>
  588. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  589. JavascriptString *const s,
  590. const CharCount startIndex,
  591. const CharCount appendCharLength)
  592. {
  593. if(!compoundString)
  594. {
  595. AppendGeneric(s, startIndex, appendCharLength, this, false);
  596. return;
  597. }
  598. compoundString->Append(s, startIndex, appendCharLength);
  599. }
  600. template<CharCount MinimumCharCapacity>
  601. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  602. JavascriptString *const s,
  603. const CharCount startIndex,
  604. const CharCount appendCharLength)
  605. {
  606. if(!compoundString)
  607. {
  608. AppendGeneric(s, startIndex, appendCharLength, this, true);
  609. return;
  610. }
  611. compoundString->AppendChars(s, startIndex, appendCharLength);
  612. }
  613. template<CharCount MinimumCharCapacity>
  614. template<CharCount AppendCharLengthPlusOne>
  615. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  616. const char16 (&s)[AppendCharLengthPlusOne],
  617. const bool isCppLiteral)
  618. {
  619. if(!compoundString)
  620. {
  621. AppendGeneric(s, isCppLiteral, this, false);
  622. return;
  623. }
  624. compoundString->Append(s, isCppLiteral);
  625. }
  626. template<CharCount MinimumCharCapacity>
  627. template<CharCount AppendCharLengthPlusOne>
  628. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  629. const char16 (&s)[AppendCharLengthPlusOne],
  630. const bool isCppLiteral)
  631. {
  632. if(!compoundString)
  633. {
  634. AppendGeneric(s, isCppLiteral, this, true);
  635. return;
  636. }
  637. compoundString->AppendChars(s, isCppLiteral);
  638. }
  639. template<CharCount MinimumCharCapacity>
  640. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  641. __in_xcount(appendCharLength) const char16 *const s,
  642. const CharCount appendCharLength)
  643. {
  644. if(!compoundString)
  645. {
  646. AppendGeneric(s, appendCharLength, this, false);
  647. return;
  648. }
  649. compoundString->Append(s, appendCharLength);
  650. }
  651. template<CharCount MinimumCharCapacity>
  652. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  653. __in_xcount(appendCharLength) const char16 *const s,
  654. const CharCount appendCharLength)
  655. {
  656. if(!compoundString)
  657. {
  658. AppendGeneric(s, appendCharLength, this, true);
  659. return;
  660. }
  661. compoundString->AppendChars(s, appendCharLength);
  662. }
  663. template<CharCount MinimumCharCapacity>
  664. template<class TValue, class FConvertToString>
  665. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  666. const TValue &value,
  667. const CharCount maximumAppendCharLength,
  668. const FConvertToString ConvertToString)
  669. {
  670. if(!compoundString)
  671. {
  672. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false);
  673. return;
  674. }
  675. compoundString->Append(value, maximumAppendCharLength, ConvertToString);
  676. }
  677. template<CharCount MinimumCharCapacity>
  678. template<class TValue, class FConvertToString>
  679. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  680. const TValue &value,
  681. const CharCount maximumAppendCharLength,
  682. const FConvertToString ConvertToString)
  683. {
  684. if(!compoundString)
  685. {
  686. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true);
  687. return;
  688. }
  689. compoundString->AppendChars(value, maximumAppendCharLength, ConvertToString);
  690. }
  691. template<CharCount MinimumCharCapacity>
  692. CompoundString *CompoundString::Builder<MinimumCharCapacity>::CreateCompoundString(const bool reserveMoreSpace) const
  693. {
  694. return
  695. CompoundString::New(
  696. stringLength,
  697. directCharLength,
  698. buffer,
  699. charLength,
  700. reserveMoreSpace,
  701. this->GetLibrary());
  702. }
  703. template<CharCount MinimumCharCapacity>
  704. inline JavascriptString *CompoundString::Builder<MinimumCharCapacity>::ToString()
  705. {
  706. #if DBG
  707. // Should not append to the builder after this function is called
  708. isFinalized = true;
  709. #endif
  710. CompoundString *const compoundString = this->compoundString;
  711. if(compoundString)
  712. return compoundString;
  713. switch(stringLength)
  714. {
  715. default:
  716. return CreateCompoundString(false);
  717. case 0:
  718. return this->GetLibrary()->GetEmptyString();
  719. case 1:
  720. Assert(HasOnlyDirectChars());
  721. Assert(LastBlockCharLength() == 1);
  722. return this->GetLibrary()->GetCharStringCache().GetStringForChar(LastBlockChars()[0]);
  723. }
  724. }
  725. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  726. #endif
  727. #pragma endregion
  728. #pragma region CompoundString template member definitions
  729. #ifndef CompoundStringJsDiag
  730. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  731. template<class String>
  732. inline bool CompoundString::TryAppendGeneric(const char16 c, String *const toString)
  733. {
  734. Assert(toString);
  735. Assert(!toString->IsFinalized());
  736. Assert(toString->OwnsLastBlock());
  737. Assert(toString->HasOnlyDirectChars());
  738. const CharCount blockCharLength = toString->LastBlockCharLength();
  739. if(blockCharLength < toString->LastBlockCharCapacity())
  740. {
  741. toString->LastBlockChars()[blockCharLength] = c;
  742. toString->SetLength(toString->GetLength() + 1);
  743. toString->SetLastBlockCharLength(blockCharLength + 1);
  744. return true;
  745. }
  746. return false;
  747. }
  748. template<class String>
  749. inline bool CompoundString::TryAppendGeneric(
  750. JavascriptString *const s,
  751. const CharCount appendCharLength,
  752. String *const toString)
  753. {
  754. Assert(s);
  755. Assert(appendCharLength == s->GetLength());
  756. Assert(toString);
  757. Assert(!toString->IsFinalized());
  758. Assert(toString->OwnsLastBlock());
  759. Assert(!toString->HasOnlyDirectChars());
  760. const CharCount blockPointerLength = toString->LastBlockPointerLength();
  761. if(blockPointerLength < toString->LastBlockPointerCapacity())
  762. {
  763. toString->LastBlockPointers()[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s);
  764. toString->SetLength(toString->GetLength() + appendCharLength);
  765. toString->SetLastBlockPointerLength(blockPointerLength + 1);
  766. return true;
  767. }
  768. return false;
  769. }
  770. template<class String>
  771. inline bool CompoundString::TryAppendFewCharsGeneric(
  772. __in_xcount(appendCharLength) const char16 *const s,
  773. const CharCount appendCharLength,
  774. String *const toString)
  775. {
  776. Assert(s);
  777. Assert(toString);
  778. Assert(!toString->IsFinalized());
  779. Assert(toString->OwnsLastBlock());
  780. Assert(toString->HasOnlyDirectChars());
  781. const CharCount blockCharLength = toString->LastBlockCharLength();
  782. if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength)
  783. {
  784. const char16 *appendCharBuffer = s;
  785. char16 *charBuffer = &toString->LastBlockChars()[blockCharLength];
  786. const char16 *const charBufferEnd = charBuffer + appendCharLength;
  787. for(; charBuffer != charBufferEnd; ++appendCharBuffer, ++charBuffer)
  788. *charBuffer = *appendCharBuffer;
  789. toString->SetLength(toString->GetLength() + appendCharLength);
  790. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  791. return true;
  792. }
  793. return false;
  794. }
  795. template<class String>
  796. inline bool CompoundString::TryAppendGeneric(
  797. __in_xcount(appendCharLength) const char16 *const s,
  798. const CharCount appendCharLength,
  799. String *const toString)
  800. {
  801. Assert(s);
  802. Assert(toString);
  803. Assert(!toString->IsFinalized());
  804. Assert(toString->OwnsLastBlock());
  805. Assert(toString->HasOnlyDirectChars());
  806. const CharCount blockCharLength = toString->LastBlockCharLength();
  807. if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength)
  808. {
  809. CopyHelper(&toString->LastBlockChars()[blockCharLength], s, appendCharLength);
  810. toString->SetLength(toString->GetLength() + appendCharLength);
  811. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  812. return true;
  813. }
  814. return false;
  815. }
  816. template<class String>
  817. inline bool CompoundString::TryAppendGeneric(
  818. JavascriptString *const s,
  819. void *const packedSubstringInfo,
  820. void *const packedSubstringInfo2,
  821. const CharCount appendCharLength,
  822. String *const toString)
  823. {
  824. Assert(s);
  825. Assert(packedSubstringInfo);
  826. Assert(appendCharLength <= s->GetLength());
  827. Assert(toString);
  828. Assert(!toString->IsFinalized());
  829. Assert(toString->OwnsLastBlock());
  830. Assert(!toString->HasOnlyDirectChars());
  831. const CharCount blockPointerLength = toString->LastBlockPointerLength();
  832. const CharCount appendPointerLength = 2 + !!packedSubstringInfo2;
  833. if(blockPointerLength < toString->LastBlockPointerCapacity() - (appendPointerLength - 1))
  834. {
  835. Field(void*)* pointers = toString->LastBlockPointers();
  836. pointers[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s);
  837. if(packedSubstringInfo2)
  838. pointers[blockPointerLength + 1] = packedSubstringInfo2;
  839. pointers[blockPointerLength + (appendPointerLength - 1)] = packedSubstringInfo;
  840. toString->SetLength(toString->GetLength() + appendCharLength);
  841. toString->SetLastBlockPointerLength(blockPointerLength + appendPointerLength);
  842. return true;
  843. }
  844. return false;
  845. }
  846. template<class String>
  847. inline void CompoundString::AppendGeneric(const char16 c, String *const toString, const bool appendChars)
  848. {
  849. Assert(toString);
  850. Assert(!toString->IsFinalized());
  851. Assert(toString->OwnsLastBlock());
  852. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  853. if(PHASE_TRACE_StringConcat)
  854. {
  855. Output::Print(_u("CompoundString::AppendGeneric('%c', appendChars = %s)\n"), c, appendChars ? _u("true") : _u("false"));
  856. Output::Flush();
  857. }
  858. if(appendChars || toString->HasOnlyDirectChars()
  859. ? TryAppendGeneric(c, toString)
  860. : TryAppendGeneric(toString->GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, toString))
  861. {
  862. return;
  863. }
  864. toString->AppendSlow(c);
  865. }
  866. template<class String>
  867. inline void CompoundString::AppendGeneric(
  868. JavascriptString *const s,
  869. String *const toString,
  870. const bool appendChars)
  871. {
  872. Assert(s);
  873. Assert(toString);
  874. Assert(!toString->IsFinalized());
  875. Assert(toString->OwnsLastBlock());
  876. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  877. const CharCount appendCharLength = s->GetLength();
  878. if(appendCharLength == 0)
  879. return;
  880. if(PHASE_TRACE_StringConcat)
  881. {
  882. Output::Print(
  883. _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  884. s->IsFinalized() ? s->GetString() : _u(""),
  885. !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""),
  886. appendCharLength,
  887. appendChars ? _u("true") : _u("false"));
  888. Output::Flush();
  889. }
  890. if(appendChars || toString->HasOnlyDirectChars())
  891. {
  892. if(appendCharLength == 1)
  893. {
  894. const char16 c = toString->GetAppendStringBuffer(s)[0];
  895. if(TryAppendGeneric(c, toString))
  896. return;
  897. toString->AppendSlow(c);
  898. return;
  899. }
  900. if(appendChars || Block::ShouldAppendChars(appendCharLength))
  901. {
  902. const char16 *const appendBuffer = toString->GetAppendStringBuffer(s);
  903. if(appendChars
  904. ? TryAppendGeneric(appendBuffer, appendCharLength, toString)
  905. : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString))
  906. {
  907. return;
  908. }
  909. toString->AppendSlow(appendBuffer, appendCharLength);
  910. return;
  911. }
  912. toString->SwitchToPointerMode();
  913. }
  914. if(TryAppendGeneric(s, appendCharLength, toString))
  915. return;
  916. toString->AppendSlow(s);
  917. }
  918. template<class String>
  919. inline void CompoundString::AppendGeneric(
  920. JavascriptString *const s,
  921. const CharCount startIndex,
  922. const CharCount appendCharLength,
  923. String *const toString,
  924. const bool appendChars)
  925. {
  926. Assert(s);
  927. Assert(startIndex <= s->GetLength());
  928. Assert(appendCharLength <= s->GetLength() - startIndex);
  929. Assert(toString);
  930. Assert(!toString->IsFinalized());
  931. Assert(toString->OwnsLastBlock());
  932. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  933. if(appendCharLength == 0)
  934. return;
  935. if(PHASE_TRACE_StringConcat)
  936. {
  937. Output::Print(
  938. _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.*s%s\", startIndex = %u, appendCharLength = %u, appendChars = %s)\n"),
  939. min(static_cast<CharCount>(8), appendCharLength),
  940. s->IsFinalized() ? &s->GetString()[startIndex] : _u(""),
  941. !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""),
  942. startIndex,
  943. appendCharLength,
  944. appendChars ? _u("true") : _u("false"));
  945. Output::Flush();
  946. }
  947. if(appendChars || toString->HasOnlyDirectChars())
  948. {
  949. if(appendCharLength == 1)
  950. {
  951. const char16 c = toString->GetAppendStringBuffer(s)[startIndex];
  952. if(TryAppendGeneric(c, toString))
  953. return;
  954. toString->AppendSlow(c);
  955. return;
  956. }
  957. if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(void *)))
  958. {
  959. const char16 *const appendBuffer = &toString->GetAppendStringBuffer(s)[startIndex];
  960. if(appendChars
  961. ? TryAppendGeneric(appendBuffer, appendCharLength, toString)
  962. : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString))
  963. {
  964. return;
  965. }
  966. toString->AppendSlow(appendBuffer, appendCharLength);
  967. return;
  968. }
  969. toString->SwitchToPointerMode();
  970. }
  971. if(appendCharLength == 1)
  972. {
  973. JavascriptString *const js =
  974. toString->GetLibrary()->GetCharStringCache().GetStringForChar(toString->GetAppendStringBuffer(s)[startIndex]);
  975. if(TryAppendGeneric(js, 1, toString))
  976. return;
  977. toString->AppendSlow(js);
  978. return;
  979. }
  980. void *packedSubstringInfo, *packedSubstringInfo2;
  981. PackSubstringInfo(startIndex, appendCharLength, &packedSubstringInfo, &packedSubstringInfo2);
  982. if(TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, toString))
  983. return;
  984. toString->AppendSlow(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength);
  985. }
  986. template<CharCount AppendCharLengthPlusOne, class String>
  987. inline void CompoundString::AppendGeneric(
  988. const char16 (&s)[AppendCharLengthPlusOne],
  989. const bool isCppLiteral,
  990. String *const toString,
  991. const bool appendChars)
  992. {
  993. CompileAssert(AppendCharLengthPlusOne != 0);
  994. Assert(s);
  995. Assert(s[AppendCharLengthPlusOne - 1] == _u('\0'));
  996. Assert(toString);
  997. Assert(!toString->IsFinalized());
  998. Assert(toString->OwnsLastBlock());
  999. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1000. if(AppendCharLengthPlusOne == 1)
  1001. return;
  1002. if(AppendCharLengthPlusOne == 2)
  1003. {
  1004. AppendGeneric(s[0], toString, appendChars);
  1005. return;
  1006. }
  1007. const CharCount appendCharLength = AppendCharLengthPlusOne - 1;
  1008. if(!isCppLiteral)
  1009. {
  1010. AppendGeneric(s, appendCharLength, toString, appendChars);
  1011. return;
  1012. }
  1013. if(PHASE_TRACE_StringConcat)
  1014. {
  1015. Output::Print(
  1016. _u("CompoundString::AppendGeneric(C++ literal \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  1017. s,
  1018. appendCharLength > 8 ? _u("...") : _u(""),
  1019. appendCharLength,
  1020. appendChars ? _u("true") : _u("false"));
  1021. Output::Flush();
  1022. }
  1023. if(appendChars || toString->HasOnlyDirectChars())
  1024. {
  1025. if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(LiteralString)))
  1026. {
  1027. if(appendChars
  1028. ? TryAppendGeneric(s, appendCharLength, toString)
  1029. : TryAppendFewCharsGeneric(s, appendCharLength, toString))
  1030. {
  1031. return;
  1032. }
  1033. toString->AppendSlow(s, appendCharLength);
  1034. return;
  1035. }
  1036. toString->SwitchToPointerMode();
  1037. }
  1038. JavascriptString *const js = toString->GetLibrary()->CreateStringFromCppLiteral(s);
  1039. if(TryAppendGeneric(js, appendCharLength, toString))
  1040. return;
  1041. toString->AppendSlow(js);
  1042. }
  1043. template<class String>
  1044. inline void CompoundString::AppendGeneric(
  1045. __in_xcount(appendCharLength) const char16 *const s,
  1046. const CharCount appendCharLength,
  1047. String *const toString,
  1048. const bool appendChars)
  1049. {
  1050. Assert(s);
  1051. Assert(toString);
  1052. Assert(!toString->IsFinalized());
  1053. Assert(toString->OwnsLastBlock());
  1054. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1055. if(appendCharLength == 0)
  1056. return;
  1057. if(PHASE_TRACE_StringConcat)
  1058. {
  1059. Output::Print(
  1060. _u("CompoundString::AppendGeneric(char16 *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  1061. s,
  1062. appendCharLength > 8 ? _u("...") : _u(""),
  1063. appendCharLength,
  1064. appendChars ? _u("true") : _u("false"));
  1065. Output::Flush();
  1066. }
  1067. if(appendChars || toString->HasOnlyDirectChars())
  1068. {
  1069. if(appendCharLength == 1)
  1070. {
  1071. const char16 c = s[0];
  1072. if(TryAppendGeneric(c, toString))
  1073. return;
  1074. toString->AppendSlow(c);
  1075. return;
  1076. }
  1077. // Skip the check for Block::ShouldAppendChars because the string buffer has to be copied anyway
  1078. if(TryAppendGeneric(s, appendCharLength, toString))
  1079. return;
  1080. toString->AppendSlow(s, appendCharLength);
  1081. return;
  1082. }
  1083. JavascriptString *const js = JavascriptString::NewCopyBuffer(s, appendCharLength, toString->GetScriptContext());
  1084. if(TryAppendGeneric(js, appendCharLength, toString))
  1085. return;
  1086. toString->AppendSlow(js);
  1087. }
  1088. template<class TValue, class FConvertToString, class String>
  1089. inline void CompoundString::AppendGeneric(
  1090. const TValue &value,
  1091. CharCount maximumAppendCharLength,
  1092. const FConvertToString ConvertToString,
  1093. String *const toString,
  1094. const bool appendChars)
  1095. {
  1096. const CharCount AbsoluteMaximumAppendCharLength = 20; // maximum length of uint64 converted to base-10 string
  1097. Assert(maximumAppendCharLength != 0);
  1098. Assert(maximumAppendCharLength <= AbsoluteMaximumAppendCharLength);
  1099. Assert(toString);
  1100. Assert(!toString->IsFinalized());
  1101. Assert(toString->OwnsLastBlock());
  1102. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1103. ++maximumAppendCharLength; // + 1 for null terminator
  1104. const CharCount blockCharLength = toString->LastBlockCharLength();
  1105. const bool convertInPlace =
  1106. (appendChars || toString->HasOnlyDirectChars()) &&
  1107. maximumAppendCharLength <= toString->LastBlockCharCapacity() - blockCharLength;
  1108. char16 localConvertBuffer[AbsoluteMaximumAppendCharLength + 1]; // + 1 for null terminator
  1109. char16 *const convertBuffer = convertInPlace ? &toString->LastBlockChars()[blockCharLength] : localConvertBuffer;
  1110. ConvertToString(value, convertBuffer, maximumAppendCharLength);
  1111. const CharCount appendCharLength = static_cast<CharCount>(wcslen(convertBuffer));
  1112. if(PHASE_TRACE_StringConcat)
  1113. {
  1114. Output::Print(
  1115. _u("CompoundString::AppendGeneric(TValue &, appendChars = %s) - converted = \"%.8s%s\", appendCharLength = %u\n"),
  1116. appendChars ? _u("true") : _u("false"),
  1117. convertBuffer,
  1118. appendCharLength > 8 ? _u("...") : _u(""),
  1119. appendCharLength);
  1120. Output::Flush();
  1121. }
  1122. if(convertInPlace)
  1123. {
  1124. toString->SetLength(toString->GetLength() + appendCharLength);
  1125. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  1126. return;
  1127. }
  1128. AnalysisAssert(convertBuffer == localConvertBuffer);
  1129. AppendGeneric(static_cast<const char16* const>(localConvertBuffer), appendCharLength, toString, appendChars);
  1130. }
  1131. template<CharCount AppendCharLengthPlusOne>
  1132. inline void CompoundString::Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral)
  1133. {
  1134. AppendGeneric(s, isCppLiteral, this, false);
  1135. }
  1136. template<CharCount AppendCharLengthPlusOne>
  1137. inline void CompoundString::AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral)
  1138. {
  1139. AppendGeneric(s, isCppLiteral, this, true);
  1140. }
  1141. template<class TValue, class FConvertToString>
  1142. inline void CompoundString::Append(
  1143. const TValue &value,
  1144. const CharCount maximumAppendCharLength,
  1145. const FConvertToString ConvertToString)
  1146. {
  1147. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false);
  1148. }
  1149. template<class TValue, class FConvertToString>
  1150. inline void CompoundString::AppendChars(
  1151. const TValue &value,
  1152. const CharCount maximumAppendCharLength,
  1153. const FConvertToString ConvertToString)
  1154. {
  1155. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true);
  1156. }
  1157. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1158. #endif
  1159. #pragma endregion
  1160. }