CompoundString.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  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. // JScriptDiag does not link with Runtime.lib and does not include .cpp files, so this file will be included as a header
  6. #include "RuntimeLibraryPch.h"
  7. namespace Js
  8. {
  9. #pragma region CompoundString::Block
  10. #ifndef IsJsDiag
  11. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  12. const uint CompoundString::Block::MaxChainedBlockSize = HeapConstants::MaxSmallObjectSize; // TODO: LargeAlloc seems to be significantly slower, hence this threshold
  13. const uint CompoundString::Block::ChainSizeThreshold = MaxChainedBlockSize / 2;
  14. // TODO: Once the above LargeAlloc issue is fixed, experiment with forcing resizing as long as the string has only direct chars
  15. CompoundString::Block::Block(const CharCount charCapacity, const Block *const previous)
  16. : bufferOwner(this), charLength(0), charCapacity(charCapacity), previous(previous)
  17. {
  18. Assert(HeapInfo::IsAlignedSize(ChainSizeThreshold));
  19. Assert(ChainSizeThreshold <= MaxChainedBlockSize);
  20. Assert(HeapInfo::IsAlignedSize(MaxChainedBlockSize));
  21. Assert((MaxChainedBlockSize << 1) > MaxChainedBlockSize);
  22. Assert(charCapacity != 0);
  23. Assert(GrowSize(SizeFromCharCapacity(charCapacity)) != 0);
  24. }
  25. CompoundString::Block::Block(
  26. const void *const buffer,
  27. const CharCount charLength,
  28. const CharCount charCapacity)
  29. : bufferOwner(this), charLength(charLength), charCapacity(charCapacity), previous(nullptr)
  30. {
  31. Assert(buffer);
  32. Assert(charLength <= charCapacity);
  33. js_wmemcpy_s(Chars(), charLength, Chars(buffer), charLength);
  34. }
  35. CompoundString::Block::Block(const Block &other, const CharCount usedCharLength)
  36. : bufferOwner(other.bufferOwner),
  37. charLength(usedCharLength),
  38. charCapacity(other.charCapacity),
  39. previous(other.previous)
  40. {
  41. // This only does a shallow copy. The metadata is copied, and a reference to the other block is included in this copy
  42. // for access to the other block's buffer.
  43. Assert(usedCharLength <= other.charCapacity);
  44. }
  45. CompoundString::Block *CompoundString::Block::New(
  46. const uint size,
  47. const Block *const previous,
  48. Recycler *const recycler)
  49. {
  50. Assert(HeapInfo::IsAlignedSize(size));
  51. Assert(recycler);
  52. return RecyclerNewPlus(recycler, size - sizeof(Block), Block, CharCapacityFromSize(size), previous);
  53. }
  54. CompoundString::Block *CompoundString::Block::New(
  55. const void *const buffer,
  56. const CharCount usedCharLength,
  57. const bool reserveMoreSpace,
  58. Recycler *const recycler)
  59. {
  60. Assert(buffer);
  61. Assert(recycler);
  62. uint size = SizeFromUsedCharLength(usedCharLength);
  63. if(reserveMoreSpace)
  64. size = GrowSize(size);
  65. return RecyclerNewPlus(recycler, size - sizeof(Block), Block, buffer, usedCharLength, CharCapacityFromSize(size));
  66. }
  67. CompoundString::Block *CompoundString::Block::Clone(
  68. const CharCount usedCharLength,
  69. Recycler *const recycler) const
  70. {
  71. Assert(recycler);
  72. return RecyclerNew(recycler, Block, *this, usedCharLength);
  73. }
  74. CharCount CompoundString::Block::CharCapacityFromSize(const uint size)
  75. {
  76. Assert(size >= sizeof(Block));
  77. return (size - sizeof(Block)) / sizeof(char16);
  78. }
  79. uint CompoundString::Block::SizeFromCharCapacity(const CharCount charCapacity)
  80. {
  81. Assert(IsValidCharCount(charCapacity));
  82. return UInt32Math::Add(sizeof(Block), charCapacity * sizeof(char16));
  83. }
  84. #endif
  85. inline CharCount CompoundString::Block::PointerAlign(const CharCount charLength)
  86. {
  87. const CharCount alignedCharLength = ::Math::Align(charLength, static_cast<CharCount>(sizeof(void *) / sizeof(char16)));
  88. Assert(alignedCharLength >= charLength);
  89. return alignedCharLength;
  90. }
  91. inline const char16 *CompoundString::Block::Chars(const void *const buffer)
  92. {
  93. return static_cast<const char16 *>(buffer);
  94. }
  95. #ifndef IsJsDiag
  96. char16 *CompoundString::Block::Chars(void *const buffer)
  97. {
  98. return static_cast<char16 *>(buffer);
  99. }
  100. #endif
  101. inline void *const *CompoundString::Block::Pointers(const void *const buffer)
  102. {
  103. return static_cast<void *const *>(buffer);
  104. }
  105. #ifndef IsJsDiag
  106. void **CompoundString::Block::Pointers(void *const buffer)
  107. {
  108. return static_cast<void **>(buffer);
  109. }
  110. CharCount CompoundString::Block::PointerCapacityFromCharCapacity(const CharCount charCapacity)
  111. {
  112. return charCapacity / (sizeof(void *) / sizeof(char16));
  113. }
  114. CharCount CompoundString::Block::CharCapacityFromPointerCapacity(const CharCount pointerCapacity)
  115. {
  116. return pointerCapacity * (sizeof(void *) / sizeof(char16));
  117. }
  118. #endif
  119. inline CharCount CompoundString::Block::PointerLengthFromCharLength(const CharCount charLength)
  120. {
  121. return PointerAlign(charLength) / (sizeof(void *) / sizeof(char16));
  122. }
  123. #ifndef IsJsDiag
  124. CharCount CompoundString::Block::CharLengthFromPointerLength(const CharCount pointerLength)
  125. {
  126. return pointerLength * (sizeof(void *) / sizeof(char16));
  127. }
  128. uint CompoundString::Block::SizeFromUsedCharLength(const CharCount usedCharLength)
  129. {
  130. const size_t usedSize = SizeFromCharCapacity(usedCharLength);
  131. const size_t alignedUsedSize = HeapInfo::GetAlignedSizeNoCheck(usedSize);
  132. if (alignedUsedSize != (uint)alignedUsedSize)
  133. {
  134. Js::Throw::OutOfMemory();
  135. }
  136. return (uint)alignedUsedSize;
  137. }
  138. bool CompoundString::Block::ShouldAppendChars(
  139. const CharCount appendCharLength,
  140. const uint additionalSizeForPointerAppend)
  141. {
  142. // Append characters instead of pointers when it would save space. Add some buffer as well, as flattening becomes more
  143. // expensive after the switch to pointer mode.
  144. //
  145. // 'additionalSizeForPointerAppend' should be provided when appending a pointer also involves creating a string object
  146. // or some other additional space (such as LiteralString, in which case this parameter should be sizeof(LiteralString)),
  147. // as that additional size also needs to be taken into account.
  148. return appendCharLength <= (sizeof(void *) * 2 + additionalSizeForPointerAppend) / sizeof(char16);
  149. }
  150. const void *CompoundString::Block::Buffer() const
  151. {
  152. return bufferOwner + 1;
  153. }
  154. void *CompoundString::Block::Buffer()
  155. {
  156. return bufferOwner + 1;
  157. }
  158. const CompoundString::Block *CompoundString::Block::Previous() const
  159. {
  160. return previous;
  161. }
  162. const char16 *CompoundString::Block::Chars() const
  163. {
  164. return Chars(Buffer());
  165. }
  166. char16 *CompoundString::Block::Chars()
  167. {
  168. return Chars(Buffer());
  169. }
  170. CharCount CompoundString::Block::CharLength() const
  171. {
  172. return charLength;
  173. }
  174. void CompoundString::Block::SetCharLength(const CharCount charLength)
  175. {
  176. Assert(charLength <= CharCapacity());
  177. this->charLength = charLength;
  178. }
  179. CharCount CompoundString::Block::CharCapacity() const
  180. {
  181. return charCapacity;
  182. }
  183. void *const *CompoundString::Block::Pointers() const
  184. {
  185. return Pointers(Buffer());
  186. }
  187. void **CompoundString::Block::Pointers()
  188. {
  189. return Pointers(Buffer());
  190. }
  191. CharCount CompoundString::Block::PointerLength() const
  192. {
  193. return PointerLengthFromCharLength(CharLength());
  194. }
  195. CharCount CompoundString::Block::PointerCapacity() const
  196. {
  197. return PointerCapacityFromCharCapacity(CharCapacity());
  198. }
  199. uint CompoundString::Block::GrowSize(const uint size)
  200. {
  201. Assert(size >= sizeof(Block));
  202. Assert(HeapInfo::IsAlignedSize(size));
  203. const uint newSize = size << 1;
  204. Assert(newSize > size);
  205. return newSize;
  206. }
  207. uint CompoundString::Block::GrowSizeForChaining(const uint size)
  208. {
  209. const uint newSize = GrowSize(size);
  210. return min(MaxChainedBlockSize, newSize);
  211. }
  212. CompoundString::Block *CompoundString::Block::Chain(Recycler *const recycler)
  213. {
  214. return New(GrowSizeForChaining(SizeFromUsedCharLength(CharLength())), this, recycler);
  215. }
  216. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  217. #endif
  218. #pragma endregion
  219. #pragma region CompoundString::BlockInfo
  220. #ifndef IsJsDiag
  221. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  222. CompoundString::BlockInfo::BlockInfo() : buffer(nullptr), charLength(0), charCapacity(0)
  223. {
  224. }
  225. CompoundString::BlockInfo::BlockInfo(Block *const block)
  226. {
  227. CopyFrom(block);
  228. }
  229. char16 *CompoundString::BlockInfo::Chars() const
  230. {
  231. return Block::Chars(buffer);
  232. }
  233. CharCount CompoundString::BlockInfo::CharLength() const
  234. {
  235. return charLength;
  236. }
  237. void CompoundString::BlockInfo::SetCharLength(const CharCount charLength)
  238. {
  239. Assert(charLength <= CharCapacity());
  240. this->charLength = charLength;
  241. }
  242. CharCount CompoundString::BlockInfo::CharCapacity() const
  243. {
  244. return charCapacity;
  245. }
  246. void **CompoundString::BlockInfo::Pointers() const
  247. {
  248. return Block::Pointers(buffer);
  249. }
  250. CharCount CompoundString::BlockInfo::PointerLength() const
  251. {
  252. return Block::PointerLengthFromCharLength(CharLength());
  253. }
  254. void CompoundString::BlockInfo::SetPointerLength(const CharCount pointerLength)
  255. {
  256. Assert(pointerLength <= PointerCapacity());
  257. charLength = Block::CharLengthFromPointerLength(pointerLength);
  258. }
  259. CharCount CompoundString::BlockInfo::PointerCapacity() const
  260. {
  261. return Block::PointerCapacityFromCharCapacity(CharCapacity());
  262. }
  263. CharCount CompoundString::BlockInfo::AlignCharCapacityForAllocation(const CharCount charCapacity)
  264. {
  265. const CharCount alignedCharCapacity =
  266. ::Math::AlignOverflowCheck(
  267. charCapacity == 0 ? static_cast<CharCount>(1) : charCapacity,
  268. static_cast<CharCount>(HeapConstants::ObjectGranularity / sizeof(char16)));
  269. Assert(alignedCharCapacity != 0);
  270. return alignedCharCapacity;
  271. }
  272. CharCount CompoundString::BlockInfo::GrowCharCapacity(const CharCount charCapacity)
  273. {
  274. Assert(charCapacity != 0);
  275. Assert(AlignCharCapacityForAllocation(charCapacity) == charCapacity);
  276. const CharCount newCharCapacity = UInt32Math::Mul<2>(charCapacity);
  277. Assert(newCharCapacity > charCapacity);
  278. return newCharCapacity;
  279. }
  280. bool CompoundString::BlockInfo::ShouldAllocateBuffer(const CharCount charCapacity)
  281. {
  282. Assert(charCapacity != 0);
  283. Assert(AlignCharCapacityForAllocation(charCapacity) == charCapacity);
  284. return charCapacity < Block::ChainSizeThreshold / sizeof(char16);
  285. }
  286. void CompoundString::BlockInfo::AllocateBuffer(const CharCount charCapacity, Recycler *const recycler)
  287. {
  288. Assert(!buffer);
  289. Assert(CharLength() == 0);
  290. Assert(CharCapacity() == 0);
  291. Assert(ShouldAllocateBuffer(charCapacity));
  292. Assert(recycler);
  293. buffer = RecyclerNewArray(recycler, char16, charCapacity);
  294. this->charCapacity = charCapacity;
  295. }
  296. CompoundString::Block *CompoundString::BlockInfo::CopyBuffer(
  297. const void *const buffer,
  298. const CharCount usedCharLength,
  299. const bool reserveMoreSpace,
  300. Recycler *const recycler)
  301. {
  302. Assert(buffer);
  303. Assert(recycler);
  304. CharCount charCapacity = AlignCharCapacityForAllocation(usedCharLength);
  305. if(reserveMoreSpace)
  306. charCapacity = GrowCharCapacity(charCapacity);
  307. if(ShouldAllocateBuffer(charCapacity))
  308. {
  309. AllocateBuffer(charCapacity, recycler);
  310. charLength = usedCharLength;
  311. js_wmemcpy_s((char16*)(this->buffer), charCapacity, (char16*)(buffer), usedCharLength);
  312. return nullptr;
  313. }
  314. Block *const block = Block::New(buffer, usedCharLength, reserveMoreSpace, recycler);
  315. CopyFrom(block);
  316. return block;
  317. }
  318. CompoundString::Block *CompoundString::BlockInfo::Resize(Recycler *const recycler)
  319. {
  320. Assert(recycler);
  321. const CharCount newCharCapacity = GrowCharCapacity(AlignCharCapacityForAllocation(CharLength()));
  322. if(ShouldAllocateBuffer(newCharCapacity))
  323. {
  324. void *const newBuffer = RecyclerNewArray(recycler, char16, newCharCapacity);
  325. charCapacity = newCharCapacity;
  326. const CharCount charLength = CharLength();
  327. js_wmemcpy_s((char16*)newBuffer, charCapacity, (char16*)buffer, charLength);
  328. buffer = newBuffer;
  329. return nullptr;
  330. }
  331. Block *const block = Block::New(buffer, CharLength(), true, recycler);
  332. CopyFrom(block);
  333. return block;
  334. }
  335. void CompoundString::BlockInfo::CopyFrom(Block *const block)
  336. {
  337. buffer = block->Buffer();
  338. charLength = block->CharLength();
  339. charCapacity = block->CharCapacity();
  340. }
  341. void CompoundString::BlockInfo::CopyTo(Block *const block)
  342. {
  343. Assert(block->Buffer() == buffer);
  344. Assert(block->CharLength() <= charLength);
  345. Assert(block->CharCapacity() == charCapacity);
  346. block->SetCharLength(charLength);
  347. }
  348. void CompoundString::BlockInfo::Unreference()
  349. {
  350. buffer = nullptr;
  351. charLength = 0;
  352. charCapacity = 0;
  353. }
  354. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  355. #endif
  356. #pragma endregion
  357. #pragma region CompoundString
  358. #ifndef IsJsDiag
  359. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  360. CompoundString::CompoundString(const CharCount initialCharCapacity, JavascriptLibrary *const library)
  361. : LiteralString(library->GetStringTypeStatic()),
  362. directCharLength(static_cast<CharCount>(-1)),
  363. ownsLastBlock(true),
  364. lastBlock(nullptr)
  365. {
  366. Assert(library);
  367. lastBlockInfo.AllocateBuffer(initialCharCapacity, library->GetRecycler());
  368. }
  369. CompoundString::CompoundString(
  370. const CharCount initialBlockSize,
  371. const bool allocateBlock,
  372. JavascriptLibrary *const library)
  373. : LiteralString(library->GetStringTypeStatic()),
  374. directCharLength(static_cast<CharCount>(-1)),
  375. ownsLastBlock(true)
  376. {
  377. Assert(allocateBlock);
  378. Assert(library);
  379. Block *const block = Block::New(initialBlockSize, nullptr, library->GetRecycler());
  380. lastBlockInfo.CopyFrom(block);
  381. lastBlock = block;
  382. }
  383. CompoundString::CompoundString(
  384. const CharCount stringLength,
  385. const CharCount directCharLength,
  386. const void *const buffer,
  387. const CharCount usedCharLength,
  388. const bool reserveMoreSpace,
  389. JavascriptLibrary *const library)
  390. : LiteralString(library->GetStringTypeStatic()),
  391. directCharLength(directCharLength),
  392. ownsLastBlock(true)
  393. {
  394. Assert(directCharLength == static_cast<CharCount>(-1) || directCharLength <= stringLength);
  395. Assert(buffer);
  396. Assert(library);
  397. SetLength(stringLength);
  398. lastBlock = lastBlockInfo.CopyBuffer(buffer, usedCharLength, reserveMoreSpace, library->GetRecycler());
  399. }
  400. CompoundString::CompoundString(CompoundString &other, const bool forAppending)
  401. : LiteralString(other.GetLibrary()->GetStringTypeStatic()),
  402. lastBlockInfo(other.lastBlockInfo),
  403. directCharLength(other.directCharLength),
  404. lastBlock(other.lastBlock)
  405. {
  406. Assert(!other.IsFinalized());
  407. SetLength(other.GetLength());
  408. if(forAppending)
  409. {
  410. // This compound string will be used for appending, so take ownership of the last block. Appends are fast for a
  411. // compound string that owns the last block.
  412. const bool ownsLastBlock = other.ownsLastBlock;
  413. other.ownsLastBlock = false;
  414. this->ownsLastBlock = ownsLastBlock;
  415. if(ownsLastBlock)
  416. return;
  417. TakeOwnershipOfLastBlock();
  418. return;
  419. }
  420. ownsLastBlock = false;
  421. }
  422. CompoundString *CompoundString::NewWithCharCapacity(
  423. const CharCount initialCharCapacity,
  424. JavascriptLibrary *const library)
  425. {
  426. const CharCount alignedInitialCharCapacity = BlockInfo::AlignCharCapacityForAllocation(initialCharCapacity);
  427. if(BlockInfo::ShouldAllocateBuffer(alignedInitialCharCapacity))
  428. return NewWithBufferCharCapacity(alignedInitialCharCapacity, library);
  429. return NewWithBlockSize(Block::SizeFromUsedCharLength(initialCharCapacity), library);
  430. }
  431. CompoundString *CompoundString::NewWithPointerCapacity(
  432. const CharCount initialPointerCapacity,
  433. JavascriptLibrary *const library)
  434. {
  435. return NewWithCharCapacity(Block::CharCapacityFromPointerCapacity(initialPointerCapacity), library);
  436. }
  437. CompoundString *CompoundString::NewWithBufferCharCapacity(const CharCount initialCharCapacity, JavascriptLibrary *const library)
  438. {
  439. Assert(library);
  440. return RecyclerNew(library->GetRecycler(), CompoundString, initialCharCapacity, library);
  441. }
  442. CompoundString *CompoundString::NewWithBlockSize(const CharCount initialBlockSize, JavascriptLibrary *const library)
  443. {
  444. Assert(library);
  445. return RecyclerNew(library->GetRecycler(), CompoundString, initialBlockSize, true, library);
  446. }
  447. CompoundString *CompoundString::New(
  448. const CharCount stringLength,
  449. const CharCount directCharLength,
  450. const void *const buffer,
  451. const CharCount usedCharLength,
  452. const bool reserveMoreSpace,
  453. JavascriptLibrary *const library)
  454. {
  455. Assert(library);
  456. return
  457. RecyclerNew(
  458. library->GetRecycler(),
  459. CompoundString,
  460. stringLength,
  461. directCharLength,
  462. buffer,
  463. usedCharLength,
  464. reserveMoreSpace,
  465. library);
  466. }
  467. CompoundString *CompoundString::Clone(const bool forAppending)
  468. {
  469. return RecyclerNew(GetLibrary()->GetRecycler(), CompoundString, *this, forAppending);
  470. }
  471. CompoundString * CompoundString::JitClone(CompoundString * cs)
  472. {
  473. Assert(Is(cs));
  474. return cs->Clone(false);
  475. }
  476. CompoundString * CompoundString::JitCloneForAppending(CompoundString * cs)
  477. {
  478. Assert(Is(cs));
  479. return cs->Clone(true);
  480. }
  481. bool CompoundString::Is(RecyclableObject *const object)
  482. {
  483. return VirtualTableInfo<CompoundString>::HasVirtualTable(object);
  484. }
  485. bool CompoundString::Is(const Var var)
  486. {
  487. return RecyclableObject::Is(var) && Is(RecyclableObject::FromVar(var));
  488. }
  489. CompoundString *CompoundString::FromVar(RecyclableObject *const object)
  490. {
  491. Assert(Is(object));
  492. CompoundString *const cs = static_cast<CompoundString *>(object);
  493. Assert(!cs->IsFinalized());
  494. return cs;
  495. }
  496. CompoundString *CompoundString::FromVar(const Var var)
  497. {
  498. return FromVar(RecyclableObject::FromVar(var));
  499. }
  500. JavascriptString *CompoundString::GetImmutableOrScriptUnreferencedString(JavascriptString *const s)
  501. {
  502. Assert(s);
  503. // The provided string may be referenced by script code. A script-unreferenced version of the string is being requested,
  504. // likely because the provided string will be referenced directly in a concatenation operation (by ConcatString or
  505. // another CompoundString, for instance). If the provided string is a CompoundString, it must not be mutated by script
  506. // code after the concatenation operation. In that case, clone the string to ensure that it is not referenced by script
  507. // code. If the clone is never handed back to script code, it effectively behaves as an immutable string.
  508. return Is(s) ? FromVar(s)->Clone(false) : s;
  509. }
  510. bool CompoundString::ShouldAppendChars(const CharCount appendCharLength)
  511. {
  512. return Block::ShouldAppendChars(appendCharLength);
  513. }
  514. bool CompoundString::HasOnlyDirectChars() const
  515. {
  516. return directCharLength == static_cast<CharCount>(-1);
  517. }
  518. void CompoundString::SwitchToPointerMode()
  519. {
  520. Assert(HasOnlyDirectChars());
  521. directCharLength = GetLength();
  522. if(PHASE_TRACE_StringConcat)
  523. {
  524. Output::Print(_u("CompoundString::SwitchToPointerMode()\n"));
  525. Output::Flush();
  526. }
  527. }
  528. bool CompoundString::OwnsLastBlock() const
  529. {
  530. return ownsLastBlock;
  531. }
  532. const char16 *CompoundString::GetAppendStringBuffer(JavascriptString *const s) const
  533. {
  534. Assert(s);
  535. // A compound string cannot flatten itself while appending itself to itself since flattening would make the append
  536. // illegal. Clone the string being appended if necessary, before flattening.
  537. return s == this ? FromVar(s)->Clone(false)->GetSz() : s->GetString();
  538. }
  539. char16 *CompoundString::LastBlockChars() const
  540. {
  541. return lastBlockInfo.Chars();
  542. }
  543. CharCount CompoundString::LastBlockCharLength() const
  544. {
  545. return lastBlockInfo.CharLength();
  546. }
  547. void CompoundString::SetLastBlockCharLength(const CharCount charLength)
  548. {
  549. lastBlockInfo.SetCharLength(charLength);
  550. }
  551. CharCount CompoundString::LastBlockCharCapacity() const
  552. {
  553. return lastBlockInfo.CharCapacity();
  554. }
  555. void **CompoundString::LastBlockPointers() const
  556. {
  557. return lastBlockInfo.Pointers();
  558. }
  559. CharCount CompoundString::LastBlockPointerLength() const
  560. {
  561. return lastBlockInfo.PointerLength();
  562. }
  563. void CompoundString::SetLastBlockPointerLength(const CharCount pointerLength)
  564. {
  565. lastBlockInfo.SetPointerLength(pointerLength);
  566. }
  567. CharCount CompoundString::LastBlockPointerCapacity() const
  568. {
  569. return lastBlockInfo.PointerCapacity();
  570. }
  571. void CompoundString::PackSubstringInfo(
  572. const CharCount startIndex,
  573. const CharCount length,
  574. void * *const packedSubstringInfoRef,
  575. void * *const packedSubstringInfo2Ref)
  576. {
  577. Assert(static_cast<int32>(startIndex) >= 0);
  578. Assert(static_cast<int32>(length) >= 0);
  579. Assert(packedSubstringInfoRef);
  580. Assert(packedSubstringInfo2Ref);
  581. #if defined(_M_X64_OR_ARM64)
  582. // On 64-bit architectures, two nonnegative 32-bit ints fit completely in a tagged pointer
  583. *packedSubstringInfoRef =
  584. reinterpret_cast<void *>(
  585. (static_cast<uintptr_t>(startIndex) << 32) +
  586. (static_cast<uintptr_t>(length) << 1) +
  587. 1);
  588. *packedSubstringInfo2Ref = nullptr;
  589. #else
  590. CompileAssert(sizeof(void *) == sizeof(int32));
  591. // On 32-bit architectures, it will be attempted to fit both pieces of into one pointer by using 16 bits for the
  592. // start index, 15 for the length, and 1 for the tag. If it does not fit, an additional pointer will be used.
  593. if(startIndex <= static_cast<CharCount>(0xffff) && length <= static_cast<CharCount>(0x7fff))
  594. {
  595. *packedSubstringInfoRef =
  596. reinterpret_cast<void *>(
  597. (static_cast<uintptr_t>(startIndex) << 16) +
  598. (static_cast<uintptr_t>(length) << 1) +
  599. 1);
  600. *packedSubstringInfo2Ref = nullptr;
  601. }
  602. else
  603. {
  604. *packedSubstringInfoRef = reinterpret_cast<void *>((static_cast<uintptr_t>(startIndex) << 1) + 1);
  605. *packedSubstringInfo2Ref = reinterpret_cast<void *>((static_cast<uintptr_t>(length) << 1) + 1);
  606. }
  607. #endif
  608. #if DBG
  609. CharCount unpackedStartIndex, unpackedLength;
  610. UnpackSubstringInfo(*packedSubstringInfoRef, *packedSubstringInfo2Ref, &unpackedStartIndex, &unpackedLength);
  611. Assert(unpackedStartIndex == startIndex);
  612. Assert(unpackedLength == length);
  613. #endif
  614. }
  615. #endif
  616. inline bool CompoundString::IsPackedInfo(void *const pointer)
  617. {
  618. Assert(pointer);
  619. return reinterpret_cast<uintptr_t>(pointer) & 1;
  620. }
  621. inline void CompoundString::UnpackSubstringInfo(
  622. void *const pointer,
  623. void *const pointer2,
  624. CharCount *const startIndexRef,
  625. CharCount *const lengthRef)
  626. {
  627. Assert(pointer);
  628. Assert(startIndexRef);
  629. Assert(lengthRef);
  630. const uintptr_t packedSubstringInfo = reinterpret_cast<uintptr_t>(pointer);
  631. Assert(packedSubstringInfo & 1);
  632. #if defined(_M_X64_OR_ARM64)
  633. // On 64-bit architectures, two nonnegative 32-bit ints fit completely in a tagged pointer
  634. Assert(!pointer2);
  635. *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 32);
  636. *lengthRef = static_cast<CharCount>(static_cast<uint32>(packedSubstringInfo) >> 1);
  637. #else
  638. CompileAssert(sizeof(void *) == sizeof(int32));
  639. // On 32-bit architectures, it will be attempted to fit both pieces of into one pointer by using 16 bits for the
  640. // start index, 15 for the length, and 1 for the tag. If it does not fit, an additional pointer will be used.
  641. if(!pointer2)
  642. {
  643. *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 16);
  644. *lengthRef = static_cast<CharCount>(static_cast<uint16>(packedSubstringInfo) >> 1);
  645. }
  646. else
  647. {
  648. *startIndexRef = static_cast<CharCount>(packedSubstringInfo >> 1);
  649. const uintptr_t packedSubstringInfo2 = reinterpret_cast<uintptr_t>(pointer2);
  650. Assert(packedSubstringInfo2 & 1);
  651. *lengthRef = static_cast<CharCount>(packedSubstringInfo2 >> 1);
  652. }
  653. #endif
  654. }
  655. #ifndef IsJsDiag
  656. void CompoundString::AppendSlow(const char16 c)
  657. {
  658. Grow();
  659. const bool appended =
  660. HasOnlyDirectChars()
  661. ? TryAppendGeneric(c, this)
  662. : TryAppendGeneric(GetLibrary()->GetCharStringCache().GetStringForChar(c), 1, this);
  663. Assert(appended);
  664. }
  665. void CompoundString::AppendSlow(JavascriptString *const s)
  666. {
  667. Grow();
  668. const bool appended = TryAppendGeneric(s, s->GetLength(), this);
  669. Assert(appended);
  670. }
  671. void CompoundString::AppendSlow(
  672. __in_xcount(appendCharLength) const char16 *const s,
  673. const CharCount appendCharLength)
  674. {
  675. Assert(!IsFinalized());
  676. Assert(OwnsLastBlock());
  677. Assert(HasOnlyDirectChars());
  678. Assert(s);
  679. // In case of exception, save enough state to revert back to the current state
  680. const BlockInfo savedLastBlockInfo(lastBlockInfo);
  681. Block *const savedLastBlock = lastBlock;
  682. const CharCount savedStringLength = GetLength();
  683. SetLength(savedStringLength + appendCharLength);
  684. CharCount copiedCharLength = 0;
  685. while(true)
  686. {
  687. const CharCount blockCharLength = LastBlockCharLength();
  688. const CharCount copyCharLength =
  689. min(LastBlockCharCapacity() - blockCharLength, appendCharLength - copiedCharLength);
  690. CopyHelper(&LastBlockChars()[blockCharLength], &s[copiedCharLength], copyCharLength);
  691. SetLastBlockCharLength(blockCharLength + copyCharLength);
  692. copiedCharLength += copyCharLength;
  693. if(copiedCharLength >= appendCharLength)
  694. break;
  695. try
  696. {
  697. Grow();
  698. }
  699. catch(...)
  700. {
  701. lastBlockInfo = savedLastBlockInfo;
  702. if(savedLastBlock)
  703. savedLastBlock->SetCharLength(savedLastBlockInfo.CharLength());
  704. lastBlock = savedLastBlock;
  705. SetLength(savedStringLength);
  706. throw;
  707. }
  708. }
  709. Assert(copiedCharLength == appendCharLength);
  710. }
  711. void CompoundString::AppendSlow(
  712. JavascriptString *const s,
  713. void *const packedSubstringInfo,
  714. void *const packedSubstringInfo2,
  715. const CharCount appendCharLength)
  716. {
  717. Grow();
  718. const bool appended = TryAppendGeneric(s, packedSubstringInfo, packedSubstringInfo2, appendCharLength, this);
  719. Assert(appended);
  720. }
  721. void CompoundString::PrepareForAppend()
  722. {
  723. Assert(!IsFinalized());
  724. if(OwnsLastBlock())
  725. return;
  726. TakeOwnershipOfLastBlock();
  727. }
  728. void CompoundString::Append(const char16 c)
  729. {
  730. AppendGeneric(c, this, false);
  731. }
  732. void CompoundString::AppendChars(const char16 c)
  733. {
  734. AppendGeneric(c, this, true);
  735. }
  736. void CompoundString::Append(JavascriptString *const s)
  737. {
  738. AppendGeneric(s, this, false);
  739. }
  740. void CompoundString::AppendChars(JavascriptString *const s)
  741. {
  742. AppendGeneric(s, this, true);
  743. }
  744. void CompoundString::Append(
  745. JavascriptString *const s,
  746. const CharCount startIndex,
  747. const CharCount appendCharLength)
  748. {
  749. AppendGeneric(s, startIndex, appendCharLength, this, false);
  750. }
  751. void CompoundString::AppendChars(
  752. JavascriptString *const s,
  753. const CharCount startIndex,
  754. const CharCount appendCharLength)
  755. {
  756. AppendGeneric(s, startIndex, appendCharLength, this, true);
  757. }
  758. void CompoundString::Append(
  759. __in_xcount(appendCharLength) const char16 *const s,
  760. const CharCount appendCharLength)
  761. {
  762. AppendGeneric(s, appendCharLength, this, false);
  763. }
  764. void CompoundString::AppendChars(
  765. __in_xcount(appendCharLength) const char16 *const s,
  766. const CharCount appendCharLength)
  767. {
  768. AppendGeneric(s, appendCharLength, this, true);
  769. }
  770. void CompoundString::AppendCharsSz(__in_z const char16 *const s)
  771. {
  772. size_t len = wcslen(s);
  773. // We limit the length of the string to MaxCharCount,
  774. // so just OOM if we are appending a string that exceed this limit already
  775. if (!IsValidCharCount(len))
  776. {
  777. JavascriptExceptionOperators::ThrowOutOfMemory(this->GetScriptContext());
  778. }
  779. AppendChars(s, (CharCount)len);
  780. }
  781. void CompoundString::Grow()
  782. {
  783. Assert(!IsFinalized());
  784. Assert(OwnsLastBlock());
  785. Block *const lastBlock = this->lastBlock;
  786. if(!lastBlock)
  787. {
  788. // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. In that case it is always
  789. // within the threshold to resize. Resize the buffer or resize it into a new block depending on its size.
  790. this->lastBlock = lastBlockInfo.Resize(GetLibrary()->GetRecycler());
  791. return;
  792. }
  793. lastBlockInfo.CopyTo(lastBlock);
  794. Block *const newLastBlock = lastBlock->Chain(GetLibrary()->GetRecycler());
  795. lastBlockInfo.CopyFrom(newLastBlock);
  796. this->lastBlock = newLastBlock;
  797. }
  798. void CompoundString::TakeOwnershipOfLastBlock()
  799. {
  800. Assert(!IsFinalized());
  801. Assert(!OwnsLastBlock());
  802. // Another string object owns the last block's buffer. The buffer must be copied, or another block must be chained.
  803. Block *const lastBlock = this->lastBlock;
  804. if(!lastBlock)
  805. {
  806. // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. In that case it is always
  807. // within the threshold to resize. Resize the buffer or resize it into a new block depending on its size.
  808. this->lastBlock = lastBlockInfo.Resize(GetLibrary()->GetRecycler());
  809. ownsLastBlock = true;
  810. return;
  811. }
  812. // The last block is already in a chain, or is over the threshold to resize. Shallow-clone the last block (clone
  813. // just its metadata, while still pointing to the original buffer), and chain it to a new last block.
  814. Recycler *const recycler = GetLibrary()->GetRecycler();
  815. Block *const newLastBlock = lastBlock->Clone(LastBlockCharLength(), recycler)->Chain(recycler);
  816. lastBlockInfo.CopyFrom(newLastBlock);
  817. ownsLastBlock = true;
  818. this->lastBlock = newLastBlock;
  819. }
  820. void CompoundString::Unreference()
  821. {
  822. lastBlockInfo.Unreference();
  823. directCharLength = 0;
  824. ownsLastBlock = false;
  825. lastBlock = nullptr;
  826. }
  827. const char16 *CompoundString::GetSz()
  828. {
  829. Assert(!IsFinalized());
  830. const CharCount totalCharLength = GetLength();
  831. switch(totalCharLength)
  832. {
  833. case 0:
  834. {
  835. Unreference();
  836. const char16 *const buffer = _u("");
  837. SetBuffer(buffer);
  838. VirtualTableInfo<LiteralString>::SetVirtualTable(this);
  839. return buffer;
  840. }
  841. case 1:
  842. {
  843. Assert(HasOnlyDirectChars());
  844. Assert(LastBlockCharLength() == 1);
  845. const char16 *const buffer = GetLibrary()->GetCharStringCache().GetStringForChar(LastBlockChars()[0])->UnsafeGetBuffer();
  846. Unreference();
  847. SetBuffer(buffer);
  848. VirtualTableInfo<LiteralString>::SetVirtualTable(this);
  849. return buffer;
  850. }
  851. }
  852. if(OwnsLastBlock() && HasOnlyDirectChars() && !lastBlock && TryAppendGeneric(_u('\0'), this)) // GetSz() requires null termination
  853. {
  854. // There is no last block. Only the buffer was allocated, and is held in 'lastBlockInfo'. Since this string owns the
  855. // last block, has only direct chars, and the buffer was allocated directly (buffer pointer is not an internal
  856. // pointer), there is no need to copy the buffer.
  857. SetLength(totalCharLength); // terminating null should not count towards the string length
  858. const char16 *const buffer = LastBlockChars();
  859. Unreference();
  860. SetBuffer(buffer);
  861. VirtualTableInfo<LiteralString>::SetVirtualTable(this);
  862. return buffer;
  863. }
  864. char16 *const buffer = RecyclerNewArrayLeaf(GetScriptContext()->GetRecycler(), char16, SafeSzSize(totalCharLength));
  865. buffer[totalCharLength] = _u('\0'); // GetSz() requires null termination
  866. Copy<CompoundString>(buffer, totalCharLength);
  867. Assert(buffer[totalCharLength] == _u('\0'));
  868. Unreference();
  869. SetBuffer(buffer);
  870. VirtualTableInfo<LiteralString>::SetVirtualTable(this);
  871. return buffer;
  872. }
  873. void CompoundString::CopyVirtual(
  874. _Out_writes_(m_charLength) char16 *const buffer,
  875. StringCopyInfoStack &nestedStringTreeCopyInfos,
  876. const byte recursionDepth)
  877. {
  878. Assert(!IsFinalized());
  879. Assert(buffer);
  880. const CharCount totalCharLength = GetLength();
  881. switch(totalCharLength)
  882. {
  883. case 0:
  884. return;
  885. case 1:
  886. Assert(HasOnlyDirectChars());
  887. Assert(LastBlockCharLength() == 1);
  888. buffer[0] = LastBlockChars()[0];
  889. return;
  890. }
  891. // Copy buffers from string pointers
  892. const bool hasOnlyDirectChars = HasOnlyDirectChars();
  893. const CharCount directCharLength = hasOnlyDirectChars ? totalCharLength : this->directCharLength;
  894. CharCount remainingCharLengthToCopy = totalCharLength;
  895. const Block *const lastBlock = this->lastBlock;
  896. const Block *block = lastBlock;
  897. void *const *blockPointers = LastBlockPointers();
  898. CharCount pointerIndex = LastBlockPointerLength();
  899. while(remainingCharLengthToCopy > directCharLength)
  900. {
  901. while(pointerIndex == 0)
  902. {
  903. Assert(block);
  904. block = block->Previous();
  905. Assert(block);
  906. blockPointers = block->Pointers();
  907. pointerIndex = block->PointerLength();
  908. }
  909. void *const pointer = blockPointers[--pointerIndex];
  910. if(IsPackedInfo(pointer))
  911. {
  912. Assert(pointerIndex != 0);
  913. void *pointer2 = blockPointers[--pointerIndex];
  914. JavascriptString *s;
  915. #if defined(_M_X64_OR_ARM64)
  916. Assert(!IsPackedInfo(pointer2));
  917. #else
  918. if(IsPackedInfo(pointer2))
  919. {
  920. Assert(pointerIndex != 0);
  921. s = JavascriptString::FromVar(blockPointers[--pointerIndex]);
  922. }
  923. else
  924. #endif
  925. {
  926. s = JavascriptString::FromVar(pointer2);
  927. pointer2 = nullptr;
  928. }
  929. CharCount startIndex, copyCharLength;
  930. UnpackSubstringInfo(pointer, pointer2, &startIndex, &copyCharLength);
  931. Assert(startIndex <= s->GetLength());
  932. Assert(copyCharLength <= s->GetLength() - startIndex);
  933. Assert(remainingCharLengthToCopy >= copyCharLength);
  934. remainingCharLengthToCopy -= copyCharLength;
  935. CopyHelper(&buffer[remainingCharLengthToCopy], &s->GetString()[startIndex], copyCharLength);
  936. }
  937. else
  938. {
  939. JavascriptString *const s = JavascriptString::FromVar(pointer);
  940. const CharCount copyCharLength = s->GetLength();
  941. Assert(remainingCharLengthToCopy >= copyCharLength);
  942. remainingCharLengthToCopy -= copyCharLength;
  943. if(recursionDepth == MaxCopyRecursionDepth && s->IsTree())
  944. {
  945. // Don't copy nested string trees yet, as that involves a recursive call, and the recursion can become
  946. // excessive. Just collect the nested string trees and the buffer location where they should be copied, and
  947. // the caller can deal with those after returning.
  948. nestedStringTreeCopyInfos.Push(StringCopyInfo(s, &buffer[remainingCharLengthToCopy]));
  949. }
  950. else
  951. {
  952. Assert(recursionDepth <= MaxCopyRecursionDepth);
  953. s->Copy(&buffer[remainingCharLengthToCopy], nestedStringTreeCopyInfos, recursionDepth + 1);
  954. }
  955. }
  956. }
  957. Assert(remainingCharLengthToCopy == directCharLength);
  958. if(remainingCharLengthToCopy != 0)
  959. {
  960. // Determine the number of direct chars in the current block
  961. CharCount blockCharLength;
  962. if(pointerIndex == 0)
  963. {
  964. // The string switched to pointer mode at the beginning of the current block, or the string never switched to
  965. // pointer mode and the last block is empty. In either case, direct chars span to the end of the previous block.
  966. Assert(block);
  967. block = block->Previous();
  968. Assert(block);
  969. blockCharLength = block->CharLength();
  970. }
  971. else if(hasOnlyDirectChars)
  972. {
  973. // The string never switched to pointer mode, so the current block's char length is where direct chars end
  974. blockCharLength = block == lastBlock ? LastBlockCharLength() : block->CharLength();
  975. }
  976. else
  977. {
  978. // The string switched to pointer mode somewhere in the middle of the current block. To determine where direct
  979. // chars end in this block, all previous blocks are scanned and their char lengths discounted.
  980. blockCharLength = remainingCharLengthToCopy;
  981. if(block)
  982. {
  983. for(const Block *previousBlock = block->Previous();
  984. previousBlock;
  985. previousBlock = previousBlock->Previous())
  986. {
  987. Assert(blockCharLength >= previousBlock->CharLength());
  988. blockCharLength -= previousBlock->CharLength();
  989. }
  990. }
  991. Assert(Block::PointerLengthFromCharLength(blockCharLength) == pointerIndex);
  992. }
  993. // Copy direct chars
  994. const char16 *blockChars = block == lastBlock ? LastBlockChars() : block->Chars();
  995. while(true)
  996. {
  997. if(blockCharLength != 0)
  998. {
  999. Assert(remainingCharLengthToCopy >= blockCharLength);
  1000. remainingCharLengthToCopy -= blockCharLength;
  1001. js_wmemcpy_s(&buffer[remainingCharLengthToCopy], blockCharLength, blockChars, blockCharLength);
  1002. if(remainingCharLengthToCopy == 0)
  1003. break;
  1004. }
  1005. Assert(block);
  1006. block = block->Previous();
  1007. Assert(block);
  1008. blockChars = block->Chars();
  1009. blockCharLength = block->CharLength();
  1010. }
  1011. }
  1012. #if DBG
  1013. // Verify that all nonempty blocks have been visited
  1014. if(block)
  1015. {
  1016. while(true)
  1017. {
  1018. block = block->Previous();
  1019. if(!block)
  1020. break;
  1021. Assert(block->CharLength() == 0);
  1022. }
  1023. }
  1024. #endif
  1025. Assert(remainingCharLengthToCopy == 0);
  1026. }
  1027. bool CompoundString::IsTree() const
  1028. {
  1029. Assert(!IsFinalized());
  1030. return !HasOnlyDirectChars();
  1031. }
  1032. DEFINE_RECYCLER_TRACKER_PERF_COUNTER(CompoundString);
  1033. CompileAssert(static_cast<CharCount>(-1) > static_cast<CharCount>(0)); // CharCount is assumed to be unsigned
  1034. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1035. #endif
  1036. #pragma endregion
  1037. }