CompoundString.h 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  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 bool Is(RecyclableObject *const object);
  299. static bool Is(const Var var);
  300. static CompoundString *FromVar(RecyclableObject *const object);
  301. static CompoundString *FromVar(const Var var);
  302. static size_t GetOffsetOfOwnsLastBlock() { return offsetof(CompoundString, ownsLastBlock); }
  303. static size_t GetOffsetOfDirectCharLength() { return offsetof(CompoundString, directCharLength); }
  304. static size_t GetOffsetOfLastBlockInfo() { return offsetof(CompoundString, lastBlockInfo); }
  305. static size_t GetOffsetOfLastBlockInfoCharLength() { return CompoundString::BlockInfo::GetOffsetOfCharLength(); }
  306. static size_t GetOffsetOfLastBlockInfoCharCapacity() { return CompoundString::BlockInfo::GetOffsetOfCharCapacity(); }
  307. static size_t GetOffsetOfLastBlockInfoBuffer() { return CompoundString::BlockInfo::GetOffsetOfBuffer(); }
  308. public:
  309. static JavascriptString *GetImmutableOrScriptUnreferencedString(JavascriptString *const s);
  310. static bool ShouldAppendChars(const CharCount appendCharLength);
  311. private:
  312. bool HasOnlyDirectChars() const;
  313. void SwitchToPointerMode();
  314. bool OwnsLastBlock() const;
  315. const char16 *GetAppendStringBuffer(JavascriptString *const s) const;
  316. private:
  317. char16 *LastBlockChars() const;
  318. CharCount LastBlockCharLength() const;
  319. void SetLastBlockCharLength(const CharCount charLength);
  320. CharCount LastBlockCharCapacity() const;
  321. private:
  322. Field(void*) *LastBlockPointers() const;
  323. CharCount LastBlockPointerLength() const;
  324. void SetLastBlockPointerLength(const CharCount pointerLength);
  325. CharCount LastBlockPointerCapacity() const;
  326. private:
  327. static void PackSubstringInfo(const CharCount startIndex, const CharCount length, void * *const packedSubstringInfoRef, void * *const packedSubstringInfo2Ref);
  328. public:
  329. static bool IsPackedInfo(void *const pointer);
  330. static void UnpackSubstringInfo(void *const pointer, void *const pointer2, CharCount *const startIndexRef, CharCount *const lengthRef);
  331. private:
  332. template<class String> static bool TryAppendGeneric(const char16 c, String *const toString);
  333. template<class String> static bool TryAppendGeneric(JavascriptString *const s, const CharCount appendCharLength, String *const toString);
  334. template<class String> static bool TryAppendFewCharsGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString);
  335. template<class String> static bool TryAppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString);
  336. template<class String> static bool TryAppendGeneric(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength, String *const toString);
  337. private:
  338. template<class String> static void AppendGeneric(const char16 c, String *const toString, const bool appendChars);
  339. template<class String> static void AppendGeneric(JavascriptString *const s, String *const toString, const bool appendChars);
  340. template<class String> static void AppendGeneric(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength, String *const toString, const bool appendChars);
  341. template<CharCount AppendCharLengthPlusOne, class String> static void AppendGeneric(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral, String *const toString, const bool appendChars);
  342. template<class String> static void AppendGeneric(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength, String *const toString, const bool appendChars);
  343. template<class TValue, class FConvertToString, class String> static void AppendGeneric(const TValue &value, CharCount maximumAppendCharLength, const FConvertToString ConvertToString, String *const toString, const bool appendChars);
  344. private:
  345. void AppendSlow(const char16 c);
  346. void AppendSlow(JavascriptString *const s);
  347. void AppendSlow(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  348. void AppendSlow(JavascriptString *const s, void *const packedSubstringInfo, void *const packedSubstringInfo2, const CharCount appendCharLength);
  349. public:
  350. void PrepareForAppend();
  351. void Append(const char16 c);
  352. void AppendChars(const char16 c);
  353. void Append(JavascriptString *const s);
  354. void AppendChars(JavascriptString *const s);
  355. void Append(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  356. void AppendChars(JavascriptString *const s, const CharCount startIndex, const CharCount appendCharLength);
  357. template<CharCount AppendCharLengthPlusOne> void Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  358. template<CharCount AppendCharLengthPlusOne> void AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral = true);
  359. void Append(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  360. void AppendChars(__in_xcount(appendCharLength) const char16 *const s, const CharCount appendCharLength);
  361. void AppendCharsSz(__in_z const char16 *const s);
  362. template<class TValue, class FConvertToString> void Append(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  363. template<class TValue, class FConvertToString> void AppendChars(const TValue &value, const CharCount maximumAppendCharLength, const FConvertToString ConvertToString);
  364. private:
  365. void Grow();
  366. void TakeOwnershipOfLastBlock();
  367. private:
  368. void Unreference();
  369. public:
  370. virtual const char16 *GetSz() override sealed;
  371. using JavascriptString::Copy;
  372. virtual void CopyVirtual(_Out_writes_(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed;
  373. virtual bool IsTree() const override sealed;
  374. protected:
  375. DEFINE_VTABLE_CTOR(CompoundString, LiteralString);
  376. DECLARE_CONCRETE_STRING_CLASS;
  377. private:
  378. PREVENT_COPY(CompoundString);
  379. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  380. #pragma endregion
  381. public:
  382. virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
  383. {
  384. return VTableValue::VtableCompoundString;
  385. }
  386. };
  387. #pragma region CompoundString::Builder definition
  388. #ifndef CompoundStringJsDiag
  389. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  390. template<CharCount MinimumCharCapacity>
  391. CompoundString::Builder<MinimumCharCapacity>::Builder(ScriptContext *const scriptContext)
  392. : stringLength(0),
  393. charLength(0),
  394. directCharLength(static_cast<CharCount>(-1)),
  395. compoundString(nullptr),
  396. scriptContext(scriptContext)
  397. #if DBG
  398. , isFinalized(false)
  399. #endif
  400. {
  401. CompileAssert(MinimumCharCapacity != 0);
  402. Assert(LastBlockCharCapacity() >= MinimumCharCapacity);
  403. }
  404. template<CharCount MinimumCharCapacity>
  405. bool CompoundString::Builder<MinimumCharCapacity>::IsFinalized() const
  406. {
  407. #if DBG
  408. return isFinalized;
  409. #else
  410. return false;
  411. #endif
  412. }
  413. template<CharCount MinimumCharCapacity>
  414. bool CompoundString::Builder<MinimumCharCapacity>::HasOnlyDirectChars() const
  415. {
  416. return directCharLength == static_cast<CharCount>(-1);
  417. }
  418. template<CharCount MinimumCharCapacity>
  419. void CompoundString::Builder<MinimumCharCapacity>::SwitchToPointerMode()
  420. {
  421. Assert(HasOnlyDirectChars());
  422. directCharLength = charLength;
  423. if(PHASE_TRACE_StringConcat)
  424. {
  425. Output::Print(_u("CompoundString::SwitchToPointerMode() - directCharLength = %u\n"), directCharLength);
  426. Output::Flush();
  427. }
  428. }
  429. template<CharCount MinimumCharCapacity>
  430. bool CompoundString::Builder<MinimumCharCapacity>::OwnsLastBlock() const
  431. {
  432. return true;
  433. }
  434. template<CharCount MinimumCharCapacity>
  435. inline const char16 *CompoundString::Builder<MinimumCharCapacity>::GetAppendStringBuffer(
  436. JavascriptString *const s) const
  437. {
  438. Assert(s);
  439. return s->GetString();
  440. }
  441. template<CharCount MinimumCharCapacity>
  442. ScriptContext *CompoundString::Builder<MinimumCharCapacity>::GetScriptContext() const
  443. {
  444. return scriptContext;
  445. }
  446. template<CharCount MinimumCharCapacity>
  447. JavascriptLibrary *CompoundString::Builder<MinimumCharCapacity>::GetLibrary() const
  448. {
  449. return scriptContext->GetLibrary();
  450. }
  451. template<CharCount MinimumCharCapacity>
  452. char16 *CompoundString::Builder<MinimumCharCapacity>::LastBlockChars()
  453. {
  454. return Block::Chars(buffer);
  455. }
  456. template<CharCount MinimumCharCapacity>
  457. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharLength() const
  458. {
  459. return charLength;
  460. }
  461. template<CharCount MinimumCharCapacity>
  462. void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockCharLength(const CharCount charLength)
  463. {
  464. this->charLength = charLength;
  465. }
  466. template<CharCount MinimumCharCapacity>
  467. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockCharCapacity() const
  468. {
  469. return Block::CharCapacityFromPointerCapacity(LastBlockPointerCapacity());
  470. }
  471. template<CharCount MinimumCharCapacity>
  472. Field(void*) *CompoundString::Builder<MinimumCharCapacity>::LastBlockPointers()
  473. {
  474. return Block::Pointers(buffer);
  475. }
  476. template<CharCount MinimumCharCapacity>
  477. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerLength() const
  478. {
  479. return Block::PointerLengthFromCharLength(charLength);
  480. }
  481. template<CharCount MinimumCharCapacity>
  482. void CompoundString::Builder<MinimumCharCapacity>::SetLastBlockPointerLength(const CharCount pointerLength)
  483. {
  484. charLength = Block::CharLengthFromPointerLength(pointerLength);
  485. }
  486. template<CharCount MinimumCharCapacity>
  487. CharCount CompoundString::Builder<MinimumCharCapacity>::LastBlockPointerCapacity() const
  488. {
  489. return _countof(buffer);
  490. }
  491. template<CharCount MinimumCharCapacity>
  492. CharCount CompoundString::Builder<MinimumCharCapacity>::GetLength() const
  493. {
  494. return stringLength;
  495. }
  496. template<CharCount MinimumCharCapacity>
  497. void CompoundString::Builder<MinimumCharCapacity>::SetLength(const CharCount stringLength)
  498. {
  499. if(!IsValidCharCount(stringLength))
  500. Throw::OutOfMemory();
  501. this->stringLength = stringLength;
  502. }
  503. template<CharCount MinimumCharCapacity>
  504. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(const char16 c)
  505. {
  506. Assert(!this->compoundString);
  507. CompoundString *const compoundString = CreateCompoundString(true);
  508. this->compoundString = compoundString;
  509. const bool appended =
  510. HasOnlyDirectChars()
  511. ? TryAppendGeneric(c, compoundString)
  512. : TryAppendGeneric(GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, compoundString);
  513. Assert(appended);
  514. }
  515. template<CharCount MinimumCharCapacity>
  516. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(JavascriptString *const s)
  517. {
  518. Assert(!this->compoundString);
  519. CompoundString *const compoundString = CreateCompoundString(true);
  520. this->compoundString = compoundString;
  521. const bool appended = TryAppendGeneric(s, s->GetLength(), compoundString);
  522. Assert(appended);
  523. }
  524. template<CharCount MinimumCharCapacity>
  525. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(
  526. __in_xcount(appendCharLength) const char16 *const s,
  527. const CharCount appendCharLength)
  528. {
  529. // Even though CreateCompoundString() will create a compound string with some additional space reserved for appending,
  530. // the amount of space available may still not be enough, so need to check and fall back to the slow path as well
  531. Assert(!this->compoundString);
  532. CompoundString *const compoundString = CreateCompoundString(true);
  533. this->compoundString = compoundString;
  534. if(TryAppendGeneric(s, appendCharLength, compoundString))
  535. return;
  536. compoundString->AppendSlow(s, appendCharLength);
  537. }
  538. template<CharCount MinimumCharCapacity>
  539. void CompoundString::Builder<MinimumCharCapacity>::AppendSlow(
  540. JavascriptString *const s,
  541. void *const packedSubstringInfo,
  542. void *const packedSubstringInfo2,
  543. const CharCount appendCharLength)
  544. {
  545. Assert(!this->compoundString);
  546. CompoundString *const compoundString = CreateCompoundString(true);
  547. this->compoundString = compoundString;
  548. const bool appended = TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, compoundString);
  549. Assert(appended);
  550. }
  551. template<CharCount MinimumCharCapacity>
  552. inline void CompoundString::Builder<MinimumCharCapacity>::Append(const char16 c)
  553. {
  554. if(!compoundString)
  555. {
  556. AppendGeneric(c, this, false);
  557. return;
  558. }
  559. compoundString->Append(c);
  560. }
  561. template<CharCount MinimumCharCapacity>
  562. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(const char16 c)
  563. {
  564. if(!compoundString)
  565. {
  566. AppendGeneric(c, this, true);
  567. return;
  568. }
  569. compoundString->AppendChars(c);
  570. }
  571. template<CharCount MinimumCharCapacity>
  572. inline void CompoundString::Builder<MinimumCharCapacity>::Append(JavascriptString *const s)
  573. {
  574. if(!compoundString)
  575. {
  576. AppendGeneric(s, this, false);
  577. return;
  578. }
  579. compoundString->Append(s);
  580. }
  581. template<CharCount MinimumCharCapacity>
  582. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(JavascriptString *const s)
  583. {
  584. if(!compoundString)
  585. {
  586. AppendGeneric(s, this, true);
  587. return;
  588. }
  589. compoundString->AppendChars(s);
  590. }
  591. template<CharCount MinimumCharCapacity>
  592. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  593. JavascriptString *const s,
  594. const CharCount startIndex,
  595. const CharCount appendCharLength)
  596. {
  597. if(!compoundString)
  598. {
  599. AppendGeneric(s, startIndex, appendCharLength, this, false);
  600. return;
  601. }
  602. compoundString->Append(s, startIndex, appendCharLength);
  603. }
  604. template<CharCount MinimumCharCapacity>
  605. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  606. JavascriptString *const s,
  607. const CharCount startIndex,
  608. const CharCount appendCharLength)
  609. {
  610. if(!compoundString)
  611. {
  612. AppendGeneric(s, startIndex, appendCharLength, this, true);
  613. return;
  614. }
  615. compoundString->AppendChars(s, startIndex, appendCharLength);
  616. }
  617. template<CharCount MinimumCharCapacity>
  618. template<CharCount AppendCharLengthPlusOne>
  619. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  620. const char16 (&s)[AppendCharLengthPlusOne],
  621. const bool isCppLiteral)
  622. {
  623. if(!compoundString)
  624. {
  625. AppendGeneric(s, isCppLiteral, this, false);
  626. return;
  627. }
  628. compoundString->Append(s, isCppLiteral);
  629. }
  630. template<CharCount MinimumCharCapacity>
  631. template<CharCount AppendCharLengthPlusOne>
  632. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  633. const char16 (&s)[AppendCharLengthPlusOne],
  634. const bool isCppLiteral)
  635. {
  636. if(!compoundString)
  637. {
  638. AppendGeneric(s, isCppLiteral, this, true);
  639. return;
  640. }
  641. compoundString->AppendChars(s, isCppLiteral);
  642. }
  643. template<CharCount MinimumCharCapacity>
  644. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  645. __in_xcount(appendCharLength) const char16 *const s,
  646. const CharCount appendCharLength)
  647. {
  648. if(!compoundString)
  649. {
  650. AppendGeneric(s, appendCharLength, this, false);
  651. return;
  652. }
  653. compoundString->Append(s, appendCharLength);
  654. }
  655. template<CharCount MinimumCharCapacity>
  656. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  657. __in_xcount(appendCharLength) const char16 *const s,
  658. const CharCount appendCharLength)
  659. {
  660. if(!compoundString)
  661. {
  662. AppendGeneric(s, appendCharLength, this, true);
  663. return;
  664. }
  665. compoundString->AppendChars(s, appendCharLength);
  666. }
  667. template<CharCount MinimumCharCapacity>
  668. template<class TValue, class FConvertToString>
  669. inline void CompoundString::Builder<MinimumCharCapacity>::Append(
  670. const TValue &value,
  671. const CharCount maximumAppendCharLength,
  672. const FConvertToString ConvertToString)
  673. {
  674. if(!compoundString)
  675. {
  676. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false);
  677. return;
  678. }
  679. compoundString->Append(value, maximumAppendCharLength, ConvertToString);
  680. }
  681. template<CharCount MinimumCharCapacity>
  682. template<class TValue, class FConvertToString>
  683. inline void CompoundString::Builder<MinimumCharCapacity>::AppendChars(
  684. const TValue &value,
  685. const CharCount maximumAppendCharLength,
  686. const FConvertToString ConvertToString)
  687. {
  688. if(!compoundString)
  689. {
  690. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true);
  691. return;
  692. }
  693. compoundString->AppendChars(value, maximumAppendCharLength, ConvertToString);
  694. }
  695. template<CharCount MinimumCharCapacity>
  696. CompoundString *CompoundString::Builder<MinimumCharCapacity>::CreateCompoundString(const bool reserveMoreSpace) const
  697. {
  698. return
  699. CompoundString::New(
  700. stringLength,
  701. directCharLength,
  702. buffer,
  703. charLength,
  704. reserveMoreSpace,
  705. this->GetLibrary());
  706. }
  707. template<CharCount MinimumCharCapacity>
  708. inline JavascriptString *CompoundString::Builder<MinimumCharCapacity>::ToString()
  709. {
  710. #if DBG
  711. // Should not append to the builder after this function is called
  712. isFinalized = true;
  713. #endif
  714. CompoundString *const compoundString = this->compoundString;
  715. if(compoundString)
  716. return compoundString;
  717. switch(stringLength)
  718. {
  719. default:
  720. return CreateCompoundString(false);
  721. case 0:
  722. return this->GetLibrary()->GetEmptyString();
  723. case 1:
  724. Assert(HasOnlyDirectChars());
  725. Assert(LastBlockCharLength() == 1);
  726. return this->GetLibrary()->GetCharStringCache().GetStringForChar(LastBlockChars()[0]);
  727. }
  728. }
  729. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  730. #endif
  731. #pragma endregion
  732. #pragma region CompoundString template member definitions
  733. #ifndef CompoundStringJsDiag
  734. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  735. template<class String>
  736. inline bool CompoundString::TryAppendGeneric(const char16 c, String *const toString)
  737. {
  738. Assert(toString);
  739. Assert(!toString->IsFinalized());
  740. Assert(toString->OwnsLastBlock());
  741. Assert(toString->HasOnlyDirectChars());
  742. const CharCount blockCharLength = toString->LastBlockCharLength();
  743. if(blockCharLength < toString->LastBlockCharCapacity())
  744. {
  745. toString->LastBlockChars()[blockCharLength] = c;
  746. toString->SetLength(toString->GetLength() + 1);
  747. toString->SetLastBlockCharLength(blockCharLength + 1);
  748. return true;
  749. }
  750. return false;
  751. }
  752. template<class String>
  753. inline bool CompoundString::TryAppendGeneric(
  754. JavascriptString *const s,
  755. const CharCount appendCharLength,
  756. String *const toString)
  757. {
  758. Assert(s);
  759. Assert(appendCharLength == s->GetLength());
  760. Assert(toString);
  761. Assert(!toString->IsFinalized());
  762. Assert(toString->OwnsLastBlock());
  763. Assert(!toString->HasOnlyDirectChars());
  764. const CharCount blockPointerLength = toString->LastBlockPointerLength();
  765. if(blockPointerLength < toString->LastBlockPointerCapacity())
  766. {
  767. toString->LastBlockPointers()[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s);
  768. toString->SetLength(toString->GetLength() + appendCharLength);
  769. toString->SetLastBlockPointerLength(blockPointerLength + 1);
  770. return true;
  771. }
  772. return false;
  773. }
  774. template<class String>
  775. inline bool CompoundString::TryAppendFewCharsGeneric(
  776. __in_xcount(appendCharLength) const char16 *const s,
  777. const CharCount appendCharLength,
  778. String *const toString)
  779. {
  780. Assert(s);
  781. Assert(toString);
  782. Assert(!toString->IsFinalized());
  783. Assert(toString->OwnsLastBlock());
  784. Assert(toString->HasOnlyDirectChars());
  785. const CharCount blockCharLength = toString->LastBlockCharLength();
  786. if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength)
  787. {
  788. const char16 *appendCharBuffer = s;
  789. char16 *charBuffer = &toString->LastBlockChars()[blockCharLength];
  790. const char16 *const charBufferEnd = charBuffer + appendCharLength;
  791. for(; charBuffer != charBufferEnd; ++appendCharBuffer, ++charBuffer)
  792. *charBuffer = *appendCharBuffer;
  793. toString->SetLength(toString->GetLength() + appendCharLength);
  794. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  795. return true;
  796. }
  797. return false;
  798. }
  799. template<class String>
  800. inline bool CompoundString::TryAppendGeneric(
  801. __in_xcount(appendCharLength) const char16 *const s,
  802. const CharCount appendCharLength,
  803. String *const toString)
  804. {
  805. Assert(s);
  806. Assert(toString);
  807. Assert(!toString->IsFinalized());
  808. Assert(toString->OwnsLastBlock());
  809. Assert(toString->HasOnlyDirectChars());
  810. const CharCount blockCharLength = toString->LastBlockCharLength();
  811. if(appendCharLength <= toString->LastBlockCharCapacity() - blockCharLength)
  812. {
  813. CopyHelper(&toString->LastBlockChars()[blockCharLength], s, appendCharLength);
  814. toString->SetLength(toString->GetLength() + appendCharLength);
  815. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  816. return true;
  817. }
  818. return false;
  819. }
  820. template<class String>
  821. inline bool CompoundString::TryAppendGeneric(
  822. JavascriptString *const s,
  823. void *const packedSubstringInfo,
  824. void *const packedSubstringInfo2,
  825. const CharCount appendCharLength,
  826. String *const toString)
  827. {
  828. Assert(s);
  829. Assert(packedSubstringInfo);
  830. Assert(appendCharLength <= s->GetLength());
  831. Assert(toString);
  832. Assert(!toString->IsFinalized());
  833. Assert(toString->OwnsLastBlock());
  834. Assert(!toString->HasOnlyDirectChars());
  835. const CharCount blockPointerLength = toString->LastBlockPointerLength();
  836. const CharCount appendPointerLength = 2 + !!packedSubstringInfo2;
  837. if(blockPointerLength < toString->LastBlockPointerCapacity() - (appendPointerLength - 1))
  838. {
  839. Field(void*)* pointers = toString->LastBlockPointers();
  840. pointers[blockPointerLength] = GetImmutableOrScriptUnreferencedString(s);
  841. if(packedSubstringInfo2)
  842. pointers[blockPointerLength + 1] = packedSubstringInfo2;
  843. pointers[blockPointerLength + (appendPointerLength - 1)] = packedSubstringInfo;
  844. toString->SetLength(toString->GetLength() + appendCharLength);
  845. toString->SetLastBlockPointerLength(blockPointerLength + appendPointerLength);
  846. return true;
  847. }
  848. return false;
  849. }
  850. template<class String>
  851. inline void CompoundString::AppendGeneric(const char16 c, String *const toString, const bool appendChars)
  852. {
  853. Assert(toString);
  854. Assert(!toString->IsFinalized());
  855. Assert(toString->OwnsLastBlock());
  856. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  857. if(PHASE_TRACE_StringConcat)
  858. {
  859. Output::Print(_u("CompoundString::AppendGeneric('%c', appendChars = %s)\n"), c, appendChars ? _u("true") : _u("false"));
  860. Output::Flush();
  861. }
  862. if(appendChars || toString->HasOnlyDirectChars()
  863. ? TryAppendGeneric(c, toString)
  864. : TryAppendGeneric(toString->GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, toString))
  865. {
  866. return;
  867. }
  868. toString->AppendSlow(c);
  869. }
  870. template<class String>
  871. inline void CompoundString::AppendGeneric(
  872. JavascriptString *const s,
  873. String *const toString,
  874. const bool appendChars)
  875. {
  876. Assert(s);
  877. Assert(toString);
  878. Assert(!toString->IsFinalized());
  879. Assert(toString->OwnsLastBlock());
  880. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  881. const CharCount appendCharLength = s->GetLength();
  882. if(appendCharLength == 0)
  883. return;
  884. if(PHASE_TRACE_StringConcat)
  885. {
  886. Output::Print(
  887. _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  888. s->IsFinalized() ? s->GetString() : _u(""),
  889. !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""),
  890. appendCharLength,
  891. appendChars ? _u("true") : _u("false"));
  892. Output::Flush();
  893. }
  894. if(appendChars || toString->HasOnlyDirectChars())
  895. {
  896. if(appendCharLength == 1)
  897. {
  898. const char16 c = toString->GetAppendStringBuffer(s)[0];
  899. if(TryAppendGeneric(c, toString))
  900. return;
  901. toString->AppendSlow(c);
  902. return;
  903. }
  904. if(appendChars || Block::ShouldAppendChars(appendCharLength))
  905. {
  906. const char16 *const appendBuffer = toString->GetAppendStringBuffer(s);
  907. if(appendChars
  908. ? TryAppendGeneric(appendBuffer, appendCharLength, toString)
  909. : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString))
  910. {
  911. return;
  912. }
  913. toString->AppendSlow(appendBuffer, appendCharLength);
  914. return;
  915. }
  916. toString->SwitchToPointerMode();
  917. }
  918. if(TryAppendGeneric(s, appendCharLength, toString))
  919. return;
  920. toString->AppendSlow(s);
  921. }
  922. template<class String>
  923. inline void CompoundString::AppendGeneric(
  924. JavascriptString *const s,
  925. const CharCount startIndex,
  926. const CharCount appendCharLength,
  927. String *const toString,
  928. const bool appendChars)
  929. {
  930. Assert(s);
  931. Assert(startIndex <= s->GetLength());
  932. Assert(appendCharLength <= s->GetLength() - startIndex);
  933. Assert(toString);
  934. Assert(!toString->IsFinalized());
  935. Assert(toString->OwnsLastBlock());
  936. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  937. if(appendCharLength == 0)
  938. return;
  939. if(PHASE_TRACE_StringConcat)
  940. {
  941. Output::Print(
  942. _u("CompoundString::AppendGeneric(JavascriptString *s = \"%.*s%s\", startIndex = %u, appendCharLength = %u, appendChars = %s)\n"),
  943. min(static_cast<CharCount>(8), appendCharLength),
  944. s->IsFinalized() ? &s->GetString()[startIndex] : _u(""),
  945. !s->IsFinalized() || appendCharLength > 8 ? _u("...") : _u(""),
  946. startIndex,
  947. appendCharLength,
  948. appendChars ? _u("true") : _u("false"));
  949. Output::Flush();
  950. }
  951. if(appendChars || toString->HasOnlyDirectChars())
  952. {
  953. if(appendCharLength == 1)
  954. {
  955. const char16 c = toString->GetAppendStringBuffer(s)[startIndex];
  956. if(TryAppendGeneric(c, toString))
  957. return;
  958. toString->AppendSlow(c);
  959. return;
  960. }
  961. if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(void *)))
  962. {
  963. const char16 *const appendBuffer = &toString->GetAppendStringBuffer(s)[startIndex];
  964. if(appendChars
  965. ? TryAppendGeneric(appendBuffer, appendCharLength, toString)
  966. : TryAppendFewCharsGeneric(appendBuffer, appendCharLength, toString))
  967. {
  968. return;
  969. }
  970. toString->AppendSlow(appendBuffer, appendCharLength);
  971. return;
  972. }
  973. toString->SwitchToPointerMode();
  974. }
  975. if(appendCharLength == 1)
  976. {
  977. JavascriptString *const js =
  978. toString->GetLibrary()->GetCharStringCache().GetStringForChar(toString->GetAppendStringBuffer(s)[startIndex]);
  979. if(TryAppendGeneric(js, 1, toString))
  980. return;
  981. toString->AppendSlow(js);
  982. return;
  983. }
  984. void *packedSubstringInfo, *packedSubstringInfo2;
  985. PackSubstringInfo(startIndex, appendCharLength, &packedSubstringInfo, &packedSubstringInfo2);
  986. if(TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, toString))
  987. return;
  988. toString->AppendSlow(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength);
  989. }
  990. template<CharCount AppendCharLengthPlusOne, class String>
  991. inline void CompoundString::AppendGeneric(
  992. const char16 (&s)[AppendCharLengthPlusOne],
  993. const bool isCppLiteral,
  994. String *const toString,
  995. const bool appendChars)
  996. {
  997. CompileAssert(AppendCharLengthPlusOne != 0);
  998. Assert(s);
  999. Assert(s[AppendCharLengthPlusOne - 1] == _u('\0'));
  1000. Assert(toString);
  1001. Assert(!toString->IsFinalized());
  1002. Assert(toString->OwnsLastBlock());
  1003. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1004. if(AppendCharLengthPlusOne == 1)
  1005. return;
  1006. if(AppendCharLengthPlusOne == 2)
  1007. {
  1008. AppendGeneric(s[0], toString, appendChars);
  1009. return;
  1010. }
  1011. const CharCount appendCharLength = AppendCharLengthPlusOne - 1;
  1012. if(!isCppLiteral)
  1013. {
  1014. AppendGeneric(s, appendCharLength, toString, appendChars);
  1015. return;
  1016. }
  1017. if(PHASE_TRACE_StringConcat)
  1018. {
  1019. Output::Print(
  1020. _u("CompoundString::AppendGeneric(C++ literal \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  1021. s,
  1022. appendCharLength > 8 ? _u("...") : _u(""),
  1023. appendCharLength,
  1024. appendChars ? _u("true") : _u("false"));
  1025. Output::Flush();
  1026. }
  1027. if(appendChars || toString->HasOnlyDirectChars())
  1028. {
  1029. if(appendChars || Block::ShouldAppendChars(appendCharLength, sizeof(LiteralString)))
  1030. {
  1031. if(appendChars
  1032. ? TryAppendGeneric(s, appendCharLength, toString)
  1033. : TryAppendFewCharsGeneric(s, appendCharLength, toString))
  1034. {
  1035. return;
  1036. }
  1037. toString->AppendSlow(s, appendCharLength);
  1038. return;
  1039. }
  1040. toString->SwitchToPointerMode();
  1041. }
  1042. JavascriptString *const js = toString->GetLibrary()->CreateStringFromCppLiteral(s);
  1043. if(TryAppendGeneric(js, appendCharLength, toString))
  1044. return;
  1045. toString->AppendSlow(js);
  1046. }
  1047. template<class String>
  1048. inline void CompoundString::AppendGeneric(
  1049. __in_xcount(appendCharLength) const char16 *const s,
  1050. const CharCount appendCharLength,
  1051. String *const toString,
  1052. const bool appendChars)
  1053. {
  1054. Assert(s);
  1055. Assert(toString);
  1056. Assert(!toString->IsFinalized());
  1057. Assert(toString->OwnsLastBlock());
  1058. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1059. if(appendCharLength == 0)
  1060. return;
  1061. if(PHASE_TRACE_StringConcat)
  1062. {
  1063. Output::Print(
  1064. _u("CompoundString::AppendGeneric(char16 *s = \"%.8s%s\", appendCharLength = %u, appendChars = %s)\n"),
  1065. s,
  1066. appendCharLength > 8 ? _u("...") : _u(""),
  1067. appendCharLength,
  1068. appendChars ? _u("true") : _u("false"));
  1069. Output::Flush();
  1070. }
  1071. if(appendChars || toString->HasOnlyDirectChars())
  1072. {
  1073. if(appendCharLength == 1)
  1074. {
  1075. const char16 c = s[0];
  1076. if(TryAppendGeneric(c, toString))
  1077. return;
  1078. toString->AppendSlow(c);
  1079. return;
  1080. }
  1081. // Skip the check for Block::ShouldAppendChars because the string buffer has to be copied anyway
  1082. if(TryAppendGeneric(s, appendCharLength, toString))
  1083. return;
  1084. toString->AppendSlow(s, appendCharLength);
  1085. return;
  1086. }
  1087. JavascriptString *const js = LiteralString::NewCopyBuffer(s, appendCharLength, toString->GetScriptContext());
  1088. if(TryAppendGeneric(js, appendCharLength, toString))
  1089. return;
  1090. toString->AppendSlow(js);
  1091. }
  1092. template<class TValue, class FConvertToString, class String>
  1093. inline void CompoundString::AppendGeneric(
  1094. const TValue &value,
  1095. CharCount maximumAppendCharLength,
  1096. const FConvertToString ConvertToString,
  1097. String *const toString,
  1098. const bool appendChars)
  1099. {
  1100. const CharCount AbsoluteMaximumAppendCharLength = 20; // maximum length of uint64 converted to base-10 string
  1101. Assert(maximumAppendCharLength != 0);
  1102. Assert(maximumAppendCharLength <= AbsoluteMaximumAppendCharLength);
  1103. Assert(toString);
  1104. Assert(!toString->IsFinalized());
  1105. Assert(toString->OwnsLastBlock());
  1106. Assert(!(appendChars && !toString->HasOnlyDirectChars()));
  1107. ++maximumAppendCharLength; // + 1 for null terminator
  1108. const CharCount blockCharLength = toString->LastBlockCharLength();
  1109. const bool convertInPlace =
  1110. (appendChars || toString->HasOnlyDirectChars()) &&
  1111. maximumAppendCharLength <= toString->LastBlockCharCapacity() - blockCharLength;
  1112. char16 localConvertBuffer[AbsoluteMaximumAppendCharLength + 1]; // + 1 for null terminator
  1113. char16 *const convertBuffer = convertInPlace ? &toString->LastBlockChars()[blockCharLength] : localConvertBuffer;
  1114. ConvertToString(value, convertBuffer, maximumAppendCharLength);
  1115. const CharCount appendCharLength = static_cast<CharCount>(wcslen(convertBuffer));
  1116. if(PHASE_TRACE_StringConcat)
  1117. {
  1118. Output::Print(
  1119. _u("CompoundString::AppendGeneric(TValue &, appendChars = %s) - converted = \"%.8s%s\", appendCharLength = %u\n"),
  1120. appendChars ? _u("true") : _u("false"),
  1121. convertBuffer,
  1122. appendCharLength > 8 ? _u("...") : _u(""),
  1123. appendCharLength);
  1124. Output::Flush();
  1125. }
  1126. if(convertInPlace)
  1127. {
  1128. toString->SetLength(toString->GetLength() + appendCharLength);
  1129. toString->SetLastBlockCharLength(blockCharLength + appendCharLength);
  1130. return;
  1131. }
  1132. AnalysisAssert(convertBuffer == localConvertBuffer);
  1133. AppendGeneric(static_cast<const char16* const>(localConvertBuffer), appendCharLength, toString, appendChars);
  1134. }
  1135. template<CharCount AppendCharLengthPlusOne>
  1136. inline void CompoundString::Append(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral)
  1137. {
  1138. AppendGeneric(s, isCppLiteral, this, false);
  1139. }
  1140. template<CharCount AppendCharLengthPlusOne>
  1141. inline void CompoundString::AppendChars(const char16 (&s)[AppendCharLengthPlusOne], const bool isCppLiteral)
  1142. {
  1143. AppendGeneric(s, isCppLiteral, this, true);
  1144. }
  1145. template<class TValue, class FConvertToString>
  1146. inline void CompoundString::Append(
  1147. const TValue &value,
  1148. const CharCount maximumAppendCharLength,
  1149. const FConvertToString ConvertToString)
  1150. {
  1151. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, false);
  1152. }
  1153. template<class TValue, class FConvertToString>
  1154. inline void CompoundString::AppendChars(
  1155. const TValue &value,
  1156. const CharCount maximumAppendCharLength,
  1157. const FConvertToString ConvertToString)
  1158. {
  1159. AppendGeneric(value, maximumAppendCharLength, ConvertToString, this, true);
  1160. }
  1161. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1162. #endif
  1163. #pragma endregion
  1164. }