ConcatString.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. // Base class for concat strings.
  9. // Concat string is a virtual string, or a non-leaf node in concat string tree.
  10. // It does not hold characters by itself but has one or more child nodes.
  11. // Only leaf nodes (which are not concat strings) hold the actual characters.
  12. // The flattening happens on demand (call GetString() or GetSz()),
  13. // until then we don't create actual big wchar_t* buffer, just keep concat tree as a tree.
  14. // The result of flattening the concat string tree is concat of all leaf nodes from left to right.
  15. // Usage pattern:
  16. // // Create concat tree using one of non-abstract derived classes.
  17. // JavascriptString* result = concatTree->GetString(); // At this time we flatten the tree into 1 actual wchat_t* string.
  18. class ConcatStringBase abstract : public LiteralString // vtable will be switched to LiteralString's vtable after flattening
  19. {
  20. friend JavascriptString;
  21. protected:
  22. ConcatStringBase(StaticType* stringTypeStatic);
  23. DEFINE_VTABLE_CTOR_ABSTRACT(ConcatStringBase, LiteralString);
  24. virtual void CopyVirtual(_Out_writes_(m_charLength) wchar_t *const buffer,
  25. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) = 0;
  26. void CopyImpl(_Out_writes_(m_charLength) wchar_t *const buffer,
  27. int itemCount, _In_reads_(itemCount) JavascriptString * const * items,
  28. StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth);
  29. // Subclass can call this to implement GetSz and use the actual type to avoid virtual call to Copy.
  30. template <typename ConcatStringType> const wchar_t * GetSzImpl();
  31. public:
  32. virtual const wchar_t* GetSz() = 0; // Force subclass to call GetSzImpl with the real type to avoid virtual calls
  33. using JavascriptString::Copy;
  34. virtual bool IsTree() const override sealed;
  35. };
  36. // Concat string with N (or less) child nodes.
  37. // Use it when you know the number of children at compile time.
  38. // There could be less nodes than N. When we find 1st NULL node going incrementing the index,
  39. // we see this as indication that the rest on the right are empty and stop iterating.
  40. // Usage pattern:
  41. // ConcatStringN<3>* tree = ConcatStringN<3>::New(scriptContext);
  42. // tree->SetItem(0, javascriptString1);
  43. // tree->SetItem(1, javascriptString2);
  44. // tree->SetItem(2, javascriptString3);
  45. template <int N>
  46. class ConcatStringN : public ConcatStringBase
  47. {
  48. friend JavascriptString;
  49. protected:
  50. ConcatStringN(StaticType* stringTypeStatic, bool doZeroSlotsAndLength = true);
  51. DEFINE_VTABLE_CTOR(ConcatStringN<N>, ConcatStringBase);
  52. DECLARE_CONCRETE_STRING_CLASS;
  53. virtual void CopyVirtual(_Out_writes_(m_charLength) wchar_t *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override
  54. {
  55. __super::CopyImpl(buffer, N, m_slots, nestedStringTreeCopyInfos, recursionDepth);
  56. }
  57. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  58. {
  59. items = m_slots;
  60. return N;
  61. }
  62. public:
  63. static ConcatStringN<N>* New(ScriptContext* scriptContext);
  64. const wchar_t * GetSz() override sealed;
  65. void SetItem(_In_range_(0, N - 1) int index, JavascriptString* value);
  66. protected:
  67. JavascriptString* m_slots[N]; // These contain the child nodes. 1 slot is per 1 item (JavascriptString*).
  68. };
  69. // Concat string that uses binary tree, each node has 2 children.
  70. // Usage pattern:
  71. // ConcatString* str = ConcatString::New(javascriptString1, javascriptString2);
  72. // Note: it's preferred you would use the following for concats, that would figure out whether concat string is optimal or create a new string is better.
  73. // JavascriptString::Concat(javascriptString1, javascriptString2);
  74. class ConcatString sealed : public ConcatStringN<2>
  75. {
  76. ConcatString(JavascriptString* a, JavascriptString* b);
  77. protected:
  78. DEFINE_VTABLE_CTOR(ConcatString, ConcatStringN<2>);
  79. DECLARE_CONCRETE_STRING_CLASS;
  80. public:
  81. static ConcatString* New(JavascriptString* a, JavascriptString* b);
  82. static const int MaxDepth = 1000;
  83. JavascriptString *LeftString() const { Assert(!IsFinalized()); return m_slots[0]; }
  84. JavascriptString *RightString() const { Assert(!IsFinalized()); return m_slots[1]; }
  85. };
  86. // Concat string with any number of child nodes, can grow dynamically.
  87. // Usage pattern:
  88. // ConcatStringBuilder* tree = ConcatStringBuider::New(scriptContext, 5);
  89. // tree->Append(javascriptString1);
  90. // tree->Append(javascriptString2);
  91. // ...
  92. // Implementation details:
  93. // - uses chunks, max chunk size is specified by c_maxChunkSlotCount, until we fit into that, we realloc, otherwise create new chunk.
  94. // - We use chunks in order to avoid big allocations, we don't expect lots of reallocs, that why chunk size is relatively big.
  95. // - the chunks are linked using m_prevChunk field. flattening happens from left to right, i.e. first we need to get
  96. // head chunk -- the one that has m_prevChunk == NULL.
  97. class ConcatStringBuilder sealed : public ConcatStringBase
  98. {
  99. friend JavascriptString;
  100. ConcatStringBuilder(ScriptContext* scriptContext, int initialSlotCount);
  101. ConcatStringBuilder(const ConcatStringBuilder& other);
  102. void AllocateSlots(int requestedSlotCount);
  103. ConcatStringBuilder* GetHead() const;
  104. protected:
  105. DEFINE_VTABLE_CTOR(ConcatStringBuilder, ConcatStringBase);
  106. DECLARE_CONCRETE_STRING_CLASS;
  107. virtual void CopyVirtual(_Out_writes_(m_charLength) wchar_t *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed;
  108. public:
  109. static ConcatStringBuilder* New(ScriptContext* scriptContext, int initialSlotCount);
  110. const wchar_t * GetSz() override sealed;
  111. void Append(JavascriptString* str);
  112. private:
  113. // MAX number of slots in one chunk. Until we fit into this, we realloc, otherwise create new chunk.
  114. static const int c_maxChunkSlotCount = 1024;
  115. int GetItemCount() const;
  116. JavascriptString** m_slots; // Array of child nodes.
  117. int m_slotCount; // Number of allocated slots (1 slot holds 1 item) in this chunk.
  118. int m_count; // Actual number of items in this chunk.
  119. ConcatStringBuilder* m_prevChunk;
  120. };
  121. // Concat string that wraps another string.
  122. // Use it when you need to wrap something with e.g. { and }.
  123. // Usage pattern:
  124. // result = ConcatStringWrapping<L'{', L'}'>::New(result);
  125. template <wchar_t L, wchar_t R>
  126. class ConcatStringWrapping sealed : public ConcatStringBase
  127. {
  128. friend JavascriptString;
  129. ConcatStringWrapping(JavascriptString* inner);
  130. JavascriptString* GetFirstItem() const;
  131. JavascriptString* GetLastItem() const;
  132. protected:
  133. DEFINE_VTABLE_CTOR(ConcatStringWrapping, ConcatStringBase);
  134. DECLARE_CONCRETE_STRING_CLASS;
  135. virtual void CopyVirtual(_Out_writes_(m_charLength) wchar_t *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override sealed
  136. {
  137. const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots();
  138. __super::CopyImpl(buffer, _countof(m_slots), m_slots, nestedStringTreeCopyInfos, recursionDepth);
  139. }
  140. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const override sealed
  141. {
  142. const_cast<ConcatStringWrapping *>(this)->EnsureAllSlots();
  143. items = m_slots;
  144. return _countof(m_slots);
  145. }
  146. public:
  147. static ConcatStringWrapping<L, R>* New(JavascriptString* inner);
  148. const wchar_t * GetSz() override sealed;
  149. private:
  150. void EnsureAllSlots()
  151. {
  152. m_slots[0] = this->GetFirstItem();
  153. m_slots[1] = m_inner;
  154. m_slots[2] = this->GetLastItem();
  155. }
  156. JavascriptString * m_inner;
  157. // Use the padding space for the concat
  158. JavascriptString * m_slots[3];
  159. };
  160. // Make sure the padding doesn't add tot he size of ConcatStringWrapping
  161. #if defined(_M_X64_OR_ARM64)
  162. CompileAssert(sizeof(ConcatStringWrapping<L'"', L'"'>) == 64);
  163. #else
  164. CompileAssert(sizeof(ConcatStringWrapping<L'"', L'"'>) == 32);
  165. #endif
  166. // Concat string with N child nodes. Use it when you don't know the number of children at compile time.
  167. // Usage pattern:
  168. // ConcatStringMulti* tree = ConcatStringMulti::New(3, scriptContext);
  169. // tree->SetItem(0, javascriptString1);
  170. // tree->SetItem(1, javascriptString2);
  171. // tree->SetItem(2, javascriptString3);
  172. class ConcatStringMulti sealed : public ConcatStringBase
  173. {
  174. friend JavascriptString;
  175. protected:
  176. ConcatStringMulti(uint slotCount, JavascriptString * a1, JavascriptString * a2, StaticType* stringTypeStatic);
  177. DEFINE_VTABLE_CTOR(ConcatStringMulti, ConcatStringBase);
  178. DECLARE_CONCRETE_STRING_CLASS;
  179. virtual void CopyVirtual(_Out_writes_(m_charLength) wchar_t *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos, const byte recursionDepth) override
  180. {
  181. Assert(IsFilled());
  182. __super::CopyImpl(buffer, slotCount, m_slots, nestedStringTreeCopyInfos, recursionDepth);
  183. }
  184. virtual int GetRandomAccessItemsFromConcatString(Js::JavascriptString * const *& items) const
  185. {
  186. Assert(IsFilled());
  187. items = m_slots;
  188. return slotCount;
  189. }
  190. public:
  191. static ConcatStringMulti * New(uint slotCount, JavascriptString * a1, JavascriptString * a2, ScriptContext* scriptContext);
  192. const wchar_t * GetSz() override sealed;
  193. static bool Is(Var var);
  194. static ConcatStringMulti * FromVar(Var value);
  195. static size_t GetAllocSize(uint slotCount);
  196. void SetItem(_In_range_(0, slotCount - 1) uint index, JavascriptString* value);
  197. static uint32 GetOffsetOfSlotCount() { return offsetof(ConcatStringMulti, slotCount); }
  198. static uint32 GetOffsetOfSlots() { return offsetof(ConcatStringMulti, m_slots); }
  199. protected:
  200. uint slotCount;
  201. JavascriptString* m_slots[]; // These contain the child nodes.
  202. #if DBG
  203. bool IsFilled() const;
  204. #endif
  205. };
  206. }