FixedStringHelper.Interpolate.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. using Unity.Collections;
  2. using Unity.Burst;
  3. using Unity.Mathematics;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. namespace LitMotion
  6. {
  7. internal static partial class FixedStringHelper
  8. {
  9. [BurstCompile]
  10. public static int GetUtf8CharCount(ref FixedString32Bytes runes)
  11. {
  12. int length = 0;
  13. var enumerator = runes.GetEnumerator();
  14. while (enumerator.MoveNext()) length++;
  15. return length;
  16. }
  17. static Unicode.Rune GetRuneOf(ref FixedString32Bytes text, int charIndex)
  18. {
  19. int index = 0;
  20. var enumerator = text.GetEnumerator();
  21. while (enumerator.MoveNext())
  22. {
  23. if (index == charIndex) return enumerator.Current;
  24. index++;
  25. }
  26. return Unicode.BadRune;
  27. }
  28. [BurstCompile]
  29. public static void Interpolate(ref FixedString32Bytes start, ref FixedString32Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString32Bytes result)
  30. {
  31. if (richTextEnabled)
  32. {
  33. RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length);
  34. RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length);
  35. FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  36. startTextSymbols.Dispose();
  37. endTextSymbols.Dispose();
  38. }
  39. else
  40. {
  41. FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  42. }
  43. }
  44. unsafe static void FillText(
  45. ref FixedString32Bytes start,
  46. ref FixedString32Bytes end,
  47. float t,
  48. ScrambleMode scrambleMode,
  49. ref Random randomState,
  50. ref FixedString64Bytes customScrambleChars,
  51. out FixedString32Bytes result)
  52. {
  53. var startTextUtf8Length = GetUtf8CharCount(ref start);
  54. var endTextUtf8Length = GetUtf8CharCount(ref end);
  55. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  56. var currentTextLength = (int)math.round(length * t);
  57. var enumeratorStart = start.GetEnumerator();
  58. var enumeratorEnd = end.GetEnumerator();
  59. result = new();
  60. for (int i = 0; i < length; i++)
  61. {
  62. var startMoveNext = enumeratorStart.MoveNext();
  63. var endMoveNext = enumeratorEnd.MoveNext();
  64. if (i < currentTextLength)
  65. {
  66. if (endMoveNext)
  67. {
  68. result.Append(enumeratorEnd.Current);
  69. }
  70. }
  71. else
  72. {
  73. if (startMoveNext)
  74. {
  75. result.Append(enumeratorStart.Current);
  76. }
  77. }
  78. }
  79. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength);
  80. }
  81. unsafe static void FillRichText(
  82. ref UnsafeList<RichTextSymbol32Bytes> startSymbols,
  83. ref UnsafeList<RichTextSymbol32Bytes> endSymbols,
  84. int startTextUtf8Length,
  85. int endTextUtf8Length,
  86. float t,
  87. ScrambleMode scrambleMode,
  88. ref Random randomState,
  89. ref FixedString64Bytes customScrambleChars,
  90. out FixedString32Bytes result)
  91. {
  92. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  93. var currentTextLength = (int)math.round(length * t);
  94. var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1);
  95. var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2);
  96. result = new FixedString32Bytes();
  97. result.Append(slicedText1);
  98. result.Append(slicedText2);
  99. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2));
  100. }
  101. unsafe static void FillScrambleChars(
  102. ref FixedString32Bytes target,
  103. ScrambleMode scrambleMode,
  104. ref Random randomState,
  105. ref FixedString64Bytes customScrambleChars,
  106. int count)
  107. {
  108. if (scrambleMode == ScrambleMode.None) return;
  109. if (randomState.state == 0) randomState.InitState();
  110. if (scrambleMode == ScrambleMode.Custom)
  111. {
  112. var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars);
  113. for (int i = 0; i < count; i++)
  114. {
  115. target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length)));
  116. }
  117. }
  118. else
  119. {
  120. for (int i = 0; i < count; i++)
  121. {
  122. target.Append(GetScrambleChar(scrambleMode, ref randomState));
  123. }
  124. }
  125. }
  126. unsafe static FixedString32Bytes SliceSymbols(ref UnsafeList<RichTextSymbol32Bytes> symbols, int from, int to, out int resultRichTextLength)
  127. {
  128. var text = new FixedString32Bytes();
  129. RichTextSymbol32Bytes* symbolsPtr = symbols.Ptr;
  130. var offset = 0;
  131. var tagIndent = 0;
  132. resultRichTextLength = 0;
  133. for (int i = 0; i < symbols.Length; i++)
  134. {
  135. RichTextSymbol32Bytes* symbol = symbolsPtr + i;
  136. switch (symbol->Type)
  137. {
  138. case RichTextSymbolType.Text:
  139. var enumerator = symbol->Text.GetEnumerator();
  140. while (enumerator.MoveNext())
  141. {
  142. var current = enumerator.Current;
  143. if (from <= offset && offset < to)
  144. {
  145. text.Append(current);
  146. resultRichTextLength++;
  147. }
  148. offset++;
  149. if (offset >= to && tagIndent == 0) goto LOOP_END;
  150. }
  151. break;
  152. case RichTextSymbolType.TagStart:
  153. text.Append(symbol->Text);
  154. tagIndent++;
  155. break;
  156. case RichTextSymbolType.TagEnd:
  157. text.Append(symbol->Text);
  158. tagIndent--;
  159. if (offset >= to && tagIndent == 0) goto LOOP_END;
  160. break;
  161. }
  162. }
  163. LOOP_END:
  164. return text;
  165. }
  166. [BurstCompile]
  167. public static int GetUtf8CharCount(ref FixedString64Bytes runes)
  168. {
  169. int length = 0;
  170. var enumerator = runes.GetEnumerator();
  171. while (enumerator.MoveNext()) length++;
  172. return length;
  173. }
  174. static Unicode.Rune GetRuneOf(ref FixedString64Bytes text, int charIndex)
  175. {
  176. int index = 0;
  177. var enumerator = text.GetEnumerator();
  178. while (enumerator.MoveNext())
  179. {
  180. if (index == charIndex) return enumerator.Current;
  181. index++;
  182. }
  183. return Unicode.BadRune;
  184. }
  185. [BurstCompile]
  186. public static void Interpolate(ref FixedString64Bytes start, ref FixedString64Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString64Bytes result)
  187. {
  188. if (richTextEnabled)
  189. {
  190. RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length);
  191. RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length);
  192. FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  193. startTextSymbols.Dispose();
  194. endTextSymbols.Dispose();
  195. }
  196. else
  197. {
  198. FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  199. }
  200. }
  201. unsafe static void FillText(
  202. ref FixedString64Bytes start,
  203. ref FixedString64Bytes end,
  204. float t,
  205. ScrambleMode scrambleMode,
  206. ref Random randomState,
  207. ref FixedString64Bytes customScrambleChars,
  208. out FixedString64Bytes result)
  209. {
  210. var startTextUtf8Length = GetUtf8CharCount(ref start);
  211. var endTextUtf8Length = GetUtf8CharCount(ref end);
  212. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  213. var currentTextLength = (int)math.round(length * t);
  214. var enumeratorStart = start.GetEnumerator();
  215. var enumeratorEnd = end.GetEnumerator();
  216. result = new();
  217. for (int i = 0; i < length; i++)
  218. {
  219. var startMoveNext = enumeratorStart.MoveNext();
  220. var endMoveNext = enumeratorEnd.MoveNext();
  221. if (i < currentTextLength)
  222. {
  223. if (endMoveNext)
  224. {
  225. result.Append(enumeratorEnd.Current);
  226. }
  227. }
  228. else
  229. {
  230. if (startMoveNext)
  231. {
  232. result.Append(enumeratorStart.Current);
  233. }
  234. }
  235. }
  236. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength);
  237. }
  238. unsafe static void FillRichText(
  239. ref UnsafeList<RichTextSymbol64Bytes> startSymbols,
  240. ref UnsafeList<RichTextSymbol64Bytes> endSymbols,
  241. int startTextUtf8Length,
  242. int endTextUtf8Length,
  243. float t,
  244. ScrambleMode scrambleMode,
  245. ref Random randomState,
  246. ref FixedString64Bytes customScrambleChars,
  247. out FixedString64Bytes result)
  248. {
  249. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  250. var currentTextLength = (int)math.round(length * t);
  251. var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1);
  252. var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2);
  253. result = new FixedString64Bytes();
  254. result.Append(slicedText1);
  255. result.Append(slicedText2);
  256. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2));
  257. }
  258. unsafe static void FillScrambleChars(
  259. ref FixedString64Bytes target,
  260. ScrambleMode scrambleMode,
  261. ref Random randomState,
  262. ref FixedString64Bytes customScrambleChars,
  263. int count)
  264. {
  265. if (scrambleMode == ScrambleMode.None) return;
  266. if (randomState.state == 0) randomState.InitState();
  267. if (scrambleMode == ScrambleMode.Custom)
  268. {
  269. var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars);
  270. for (int i = 0; i < count; i++)
  271. {
  272. target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length)));
  273. }
  274. }
  275. else
  276. {
  277. for (int i = 0; i < count; i++)
  278. {
  279. target.Append(GetScrambleChar(scrambleMode, ref randomState));
  280. }
  281. }
  282. }
  283. unsafe static FixedString64Bytes SliceSymbols(ref UnsafeList<RichTextSymbol64Bytes> symbols, int from, int to, out int resultRichTextLength)
  284. {
  285. var text = new FixedString64Bytes();
  286. RichTextSymbol64Bytes* symbolsPtr = symbols.Ptr;
  287. var offset = 0;
  288. var tagIndent = 0;
  289. resultRichTextLength = 0;
  290. for (int i = 0; i < symbols.Length; i++)
  291. {
  292. RichTextSymbol64Bytes* symbol = symbolsPtr + i;
  293. switch (symbol->Type)
  294. {
  295. case RichTextSymbolType.Text:
  296. var enumerator = symbol->Text.GetEnumerator();
  297. while (enumerator.MoveNext())
  298. {
  299. var current = enumerator.Current;
  300. if (from <= offset && offset < to)
  301. {
  302. text.Append(current);
  303. resultRichTextLength++;
  304. }
  305. offset++;
  306. if (offset >= to && tagIndent == 0) goto LOOP_END;
  307. }
  308. break;
  309. case RichTextSymbolType.TagStart:
  310. text.Append(symbol->Text);
  311. tagIndent++;
  312. break;
  313. case RichTextSymbolType.TagEnd:
  314. text.Append(symbol->Text);
  315. tagIndent--;
  316. if (offset >= to && tagIndent == 0) goto LOOP_END;
  317. break;
  318. }
  319. }
  320. LOOP_END:
  321. return text;
  322. }
  323. [BurstCompile]
  324. public static int GetUtf8CharCount(ref FixedString128Bytes runes)
  325. {
  326. int length = 0;
  327. var enumerator = runes.GetEnumerator();
  328. while (enumerator.MoveNext()) length++;
  329. return length;
  330. }
  331. static Unicode.Rune GetRuneOf(ref FixedString128Bytes text, int charIndex)
  332. {
  333. int index = 0;
  334. var enumerator = text.GetEnumerator();
  335. while (enumerator.MoveNext())
  336. {
  337. if (index == charIndex) return enumerator.Current;
  338. index++;
  339. }
  340. return Unicode.BadRune;
  341. }
  342. [BurstCompile]
  343. public static void Interpolate(ref FixedString128Bytes start, ref FixedString128Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString128Bytes result)
  344. {
  345. if (richTextEnabled)
  346. {
  347. RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length);
  348. RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length);
  349. FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  350. startTextSymbols.Dispose();
  351. endTextSymbols.Dispose();
  352. }
  353. else
  354. {
  355. FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  356. }
  357. }
  358. unsafe static void FillText(
  359. ref FixedString128Bytes start,
  360. ref FixedString128Bytes end,
  361. float t,
  362. ScrambleMode scrambleMode,
  363. ref Random randomState,
  364. ref FixedString64Bytes customScrambleChars,
  365. out FixedString128Bytes result)
  366. {
  367. var startTextUtf8Length = GetUtf8CharCount(ref start);
  368. var endTextUtf8Length = GetUtf8CharCount(ref end);
  369. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  370. var currentTextLength = (int)math.round(length * t);
  371. var enumeratorStart = start.GetEnumerator();
  372. var enumeratorEnd = end.GetEnumerator();
  373. result = new();
  374. for (int i = 0; i < length; i++)
  375. {
  376. var startMoveNext = enumeratorStart.MoveNext();
  377. var endMoveNext = enumeratorEnd.MoveNext();
  378. if (i < currentTextLength)
  379. {
  380. if (endMoveNext)
  381. {
  382. result.Append(enumeratorEnd.Current);
  383. }
  384. }
  385. else
  386. {
  387. if (startMoveNext)
  388. {
  389. result.Append(enumeratorStart.Current);
  390. }
  391. }
  392. }
  393. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength);
  394. }
  395. unsafe static void FillRichText(
  396. ref UnsafeList<RichTextSymbol128Bytes> startSymbols,
  397. ref UnsafeList<RichTextSymbol128Bytes> endSymbols,
  398. int startTextUtf8Length,
  399. int endTextUtf8Length,
  400. float t,
  401. ScrambleMode scrambleMode,
  402. ref Random randomState,
  403. ref FixedString64Bytes customScrambleChars,
  404. out FixedString128Bytes result)
  405. {
  406. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  407. var currentTextLength = (int)math.round(length * t);
  408. var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1);
  409. var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2);
  410. result = new FixedString128Bytes();
  411. result.Append(slicedText1);
  412. result.Append(slicedText2);
  413. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2));
  414. }
  415. unsafe static void FillScrambleChars(
  416. ref FixedString128Bytes target,
  417. ScrambleMode scrambleMode,
  418. ref Random randomState,
  419. ref FixedString64Bytes customScrambleChars,
  420. int count)
  421. {
  422. if (scrambleMode == ScrambleMode.None) return;
  423. if (randomState.state == 0) randomState.InitState();
  424. if (scrambleMode == ScrambleMode.Custom)
  425. {
  426. var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars);
  427. for (int i = 0; i < count; i++)
  428. {
  429. target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length)));
  430. }
  431. }
  432. else
  433. {
  434. for (int i = 0; i < count; i++)
  435. {
  436. target.Append(GetScrambleChar(scrambleMode, ref randomState));
  437. }
  438. }
  439. }
  440. unsafe static FixedString128Bytes SliceSymbols(ref UnsafeList<RichTextSymbol128Bytes> symbols, int from, int to, out int resultRichTextLength)
  441. {
  442. var text = new FixedString128Bytes();
  443. RichTextSymbol128Bytes* symbolsPtr = symbols.Ptr;
  444. var offset = 0;
  445. var tagIndent = 0;
  446. resultRichTextLength = 0;
  447. for (int i = 0; i < symbols.Length; i++)
  448. {
  449. RichTextSymbol128Bytes* symbol = symbolsPtr + i;
  450. switch (symbol->Type)
  451. {
  452. case RichTextSymbolType.Text:
  453. var enumerator = symbol->Text.GetEnumerator();
  454. while (enumerator.MoveNext())
  455. {
  456. var current = enumerator.Current;
  457. if (from <= offset && offset < to)
  458. {
  459. text.Append(current);
  460. resultRichTextLength++;
  461. }
  462. offset++;
  463. if (offset >= to && tagIndent == 0) goto LOOP_END;
  464. }
  465. break;
  466. case RichTextSymbolType.TagStart:
  467. text.Append(symbol->Text);
  468. tagIndent++;
  469. break;
  470. case RichTextSymbolType.TagEnd:
  471. text.Append(symbol->Text);
  472. tagIndent--;
  473. if (offset >= to && tagIndent == 0) goto LOOP_END;
  474. break;
  475. }
  476. }
  477. LOOP_END:
  478. return text;
  479. }
  480. [BurstCompile]
  481. public static int GetUtf8CharCount(ref FixedString512Bytes runes)
  482. {
  483. int length = 0;
  484. var enumerator = runes.GetEnumerator();
  485. while (enumerator.MoveNext()) length++;
  486. return length;
  487. }
  488. static Unicode.Rune GetRuneOf(ref FixedString512Bytes text, int charIndex)
  489. {
  490. int index = 0;
  491. var enumerator = text.GetEnumerator();
  492. while (enumerator.MoveNext())
  493. {
  494. if (index == charIndex) return enumerator.Current;
  495. index++;
  496. }
  497. return Unicode.BadRune;
  498. }
  499. [BurstCompile]
  500. public static void Interpolate(ref FixedString512Bytes start, ref FixedString512Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString512Bytes result)
  501. {
  502. if (richTextEnabled)
  503. {
  504. RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length);
  505. RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length);
  506. FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  507. startTextSymbols.Dispose();
  508. endTextSymbols.Dispose();
  509. }
  510. else
  511. {
  512. FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  513. }
  514. }
  515. unsafe static void FillText(
  516. ref FixedString512Bytes start,
  517. ref FixedString512Bytes end,
  518. float t,
  519. ScrambleMode scrambleMode,
  520. ref Random randomState,
  521. ref FixedString64Bytes customScrambleChars,
  522. out FixedString512Bytes result)
  523. {
  524. var startTextUtf8Length = GetUtf8CharCount(ref start);
  525. var endTextUtf8Length = GetUtf8CharCount(ref end);
  526. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  527. var currentTextLength = (int)math.round(length * t);
  528. var enumeratorStart = start.GetEnumerator();
  529. var enumeratorEnd = end.GetEnumerator();
  530. result = new();
  531. for (int i = 0; i < length; i++)
  532. {
  533. var startMoveNext = enumeratorStart.MoveNext();
  534. var endMoveNext = enumeratorEnd.MoveNext();
  535. if (i < currentTextLength)
  536. {
  537. if (endMoveNext)
  538. {
  539. result.Append(enumeratorEnd.Current);
  540. }
  541. }
  542. else
  543. {
  544. if (startMoveNext)
  545. {
  546. result.Append(enumeratorStart.Current);
  547. }
  548. }
  549. }
  550. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength);
  551. }
  552. unsafe static void FillRichText(
  553. ref UnsafeList<RichTextSymbol512Bytes> startSymbols,
  554. ref UnsafeList<RichTextSymbol512Bytes> endSymbols,
  555. int startTextUtf8Length,
  556. int endTextUtf8Length,
  557. float t,
  558. ScrambleMode scrambleMode,
  559. ref Random randomState,
  560. ref FixedString64Bytes customScrambleChars,
  561. out FixedString512Bytes result)
  562. {
  563. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  564. var currentTextLength = (int)math.round(length * t);
  565. var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1);
  566. var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2);
  567. result = new FixedString512Bytes();
  568. result.Append(slicedText1);
  569. result.Append(slicedText2);
  570. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2));
  571. }
  572. unsafe static void FillScrambleChars(
  573. ref FixedString512Bytes target,
  574. ScrambleMode scrambleMode,
  575. ref Random randomState,
  576. ref FixedString64Bytes customScrambleChars,
  577. int count)
  578. {
  579. if (scrambleMode == ScrambleMode.None) return;
  580. if (randomState.state == 0) randomState.InitState();
  581. if (scrambleMode == ScrambleMode.Custom)
  582. {
  583. var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars);
  584. for (int i = 0; i < count; i++)
  585. {
  586. target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length)));
  587. }
  588. }
  589. else
  590. {
  591. for (int i = 0; i < count; i++)
  592. {
  593. target.Append(GetScrambleChar(scrambleMode, ref randomState));
  594. }
  595. }
  596. }
  597. unsafe static FixedString512Bytes SliceSymbols(ref UnsafeList<RichTextSymbol512Bytes> symbols, int from, int to, out int resultRichTextLength)
  598. {
  599. var text = new FixedString512Bytes();
  600. RichTextSymbol512Bytes* symbolsPtr = symbols.Ptr;
  601. var offset = 0;
  602. var tagIndent = 0;
  603. resultRichTextLength = 0;
  604. for (int i = 0; i < symbols.Length; i++)
  605. {
  606. RichTextSymbol512Bytes* symbol = symbolsPtr + i;
  607. switch (symbol->Type)
  608. {
  609. case RichTextSymbolType.Text:
  610. var enumerator = symbol->Text.GetEnumerator();
  611. while (enumerator.MoveNext())
  612. {
  613. var current = enumerator.Current;
  614. if (from <= offset && offset < to)
  615. {
  616. text.Append(current);
  617. resultRichTextLength++;
  618. }
  619. offset++;
  620. if (offset >= to && tagIndent == 0) goto LOOP_END;
  621. }
  622. break;
  623. case RichTextSymbolType.TagStart:
  624. text.Append(symbol->Text);
  625. tagIndent++;
  626. break;
  627. case RichTextSymbolType.TagEnd:
  628. text.Append(symbol->Text);
  629. tagIndent--;
  630. if (offset >= to && tagIndent == 0) goto LOOP_END;
  631. break;
  632. }
  633. }
  634. LOOP_END:
  635. return text;
  636. }
  637. [BurstCompile]
  638. public static int GetUtf8CharCount(ref FixedString4096Bytes runes)
  639. {
  640. int length = 0;
  641. var enumerator = runes.GetEnumerator();
  642. while (enumerator.MoveNext()) length++;
  643. return length;
  644. }
  645. static Unicode.Rune GetRuneOf(ref FixedString4096Bytes text, int charIndex)
  646. {
  647. int index = 0;
  648. var enumerator = text.GetEnumerator();
  649. while (enumerator.MoveNext())
  650. {
  651. if (index == charIndex) return enumerator.Current;
  652. index++;
  653. }
  654. return Unicode.BadRune;
  655. }
  656. [BurstCompile]
  657. public static void Interpolate(ref FixedString4096Bytes start, ref FixedString4096Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString4096Bytes result)
  658. {
  659. if (richTextEnabled)
  660. {
  661. RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length);
  662. RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length);
  663. FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  664. startTextSymbols.Dispose();
  665. endTextSymbols.Dispose();
  666. }
  667. else
  668. {
  669. FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result);
  670. }
  671. }
  672. unsafe static void FillText(
  673. ref FixedString4096Bytes start,
  674. ref FixedString4096Bytes end,
  675. float t,
  676. ScrambleMode scrambleMode,
  677. ref Random randomState,
  678. ref FixedString64Bytes customScrambleChars,
  679. out FixedString4096Bytes result)
  680. {
  681. var startTextUtf8Length = GetUtf8CharCount(ref start);
  682. var endTextUtf8Length = GetUtf8CharCount(ref end);
  683. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  684. var currentTextLength = (int)math.round(length * t);
  685. var enumeratorStart = start.GetEnumerator();
  686. var enumeratorEnd = end.GetEnumerator();
  687. result = new();
  688. for (int i = 0; i < length; i++)
  689. {
  690. var startMoveNext = enumeratorStart.MoveNext();
  691. var endMoveNext = enumeratorEnd.MoveNext();
  692. if (i < currentTextLength)
  693. {
  694. if (endMoveNext)
  695. {
  696. result.Append(enumeratorEnd.Current);
  697. }
  698. }
  699. else
  700. {
  701. if (startMoveNext)
  702. {
  703. result.Append(enumeratorStart.Current);
  704. }
  705. }
  706. }
  707. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength);
  708. }
  709. unsafe static void FillRichText(
  710. ref UnsafeList<RichTextSymbol4096Bytes> startSymbols,
  711. ref UnsafeList<RichTextSymbol4096Bytes> endSymbols,
  712. int startTextUtf8Length,
  713. int endTextUtf8Length,
  714. float t,
  715. ScrambleMode scrambleMode,
  716. ref Random randomState,
  717. ref FixedString64Bytes customScrambleChars,
  718. out FixedString4096Bytes result)
  719. {
  720. var length = math.max(startTextUtf8Length, endTextUtf8Length);
  721. var currentTextLength = (int)math.round(length * t);
  722. var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1);
  723. var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2);
  724. result = new FixedString4096Bytes();
  725. result.Append(slicedText1);
  726. result.Append(slicedText2);
  727. FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2));
  728. }
  729. unsafe static void FillScrambleChars(
  730. ref FixedString4096Bytes target,
  731. ScrambleMode scrambleMode,
  732. ref Random randomState,
  733. ref FixedString64Bytes customScrambleChars,
  734. int count)
  735. {
  736. if (scrambleMode == ScrambleMode.None) return;
  737. if (randomState.state == 0) randomState.InitState();
  738. if (scrambleMode == ScrambleMode.Custom)
  739. {
  740. var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars);
  741. for (int i = 0; i < count; i++)
  742. {
  743. target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length)));
  744. }
  745. }
  746. else
  747. {
  748. for (int i = 0; i < count; i++)
  749. {
  750. target.Append(GetScrambleChar(scrambleMode, ref randomState));
  751. }
  752. }
  753. }
  754. unsafe static FixedString4096Bytes SliceSymbols(ref UnsafeList<RichTextSymbol4096Bytes> symbols, int from, int to, out int resultRichTextLength)
  755. {
  756. var text = new FixedString4096Bytes();
  757. RichTextSymbol4096Bytes* symbolsPtr = symbols.Ptr;
  758. var offset = 0;
  759. var tagIndent = 0;
  760. resultRichTextLength = 0;
  761. for (int i = 0; i < symbols.Length; i++)
  762. {
  763. RichTextSymbol4096Bytes* symbol = symbolsPtr + i;
  764. switch (symbol->Type)
  765. {
  766. case RichTextSymbolType.Text:
  767. var enumerator = symbol->Text.GetEnumerator();
  768. while (enumerator.MoveNext())
  769. {
  770. var current = enumerator.Current;
  771. if (from <= offset && offset < to)
  772. {
  773. text.Append(current);
  774. resultRichTextLength++;
  775. }
  776. offset++;
  777. if (offset >= to && tagIndent == 0) goto LOOP_END;
  778. }
  779. break;
  780. case RichTextSymbolType.TagStart:
  781. text.Append(symbol->Text);
  782. tagIndent++;
  783. break;
  784. case RichTextSymbolType.TagEnd:
  785. text.Append(symbol->Text);
  786. tagIndent--;
  787. if (offset >= to && tagIndent == 0) goto LOOP_END;
  788. break;
  789. }
  790. }
  791. LOOP_END:
  792. return text;
  793. }
  794. }
  795. }