<#@ template language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <# var sizes = new int[] { 32, 64, 128, 512, 4096 }; #> using Unity.Collections; using Unity.Burst; using Unity.Mathematics; using Unity.Collections.LowLevel.Unsafe; namespace LitMotion { internal static partial class FixedStringHelper { <# foreach(var size in sizes) { #> [BurstCompile] public static int GetUtf8CharCount(ref FixedString<#=size#>Bytes runes) { int length = 0; var enumerator = runes.GetEnumerator(); while (enumerator.MoveNext()) length++; return length; } static Unicode.Rune GetRuneOf(ref FixedString<#=size#>Bytes text, int charIndex) { int index = 0; var enumerator = text.GetEnumerator(); while (enumerator.MoveNext()) { if (index == charIndex) return enumerator.Current; index++; } return Unicode.BadRune; } [BurstCompile] public static void Interpolate(ref FixedString<#=size#>Bytes start, ref FixedString<#=size#>Bytes end, float t, ScrambleMode scrambleMode, bool richTextEnabled, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString<#=size#>Bytes result) { if (richTextEnabled) { RichTextParser.GetSymbols(ref start, Allocator.Temp, out var startTextSymbols, out var startTextUtf8Length); RichTextParser.GetSymbols(ref end, Allocator.Temp, out var endTextSymbols, out var endTextUtf8Length); FillRichText(ref startTextSymbols, ref endTextSymbols, startTextUtf8Length, endTextUtf8Length, t, scrambleMode, ref randomState, ref customScrambleChars, out result); startTextSymbols.Dispose(); endTextSymbols.Dispose(); } else { FillText(ref start, ref end, t, scrambleMode, ref randomState, ref customScrambleChars, out result); } } unsafe static void FillText( ref FixedString<#=size#>Bytes start, ref FixedString<#=size#>Bytes end, float t, ScrambleMode scrambleMode, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString<#=size#>Bytes result) { var startTextUtf8Length = GetUtf8CharCount(ref start); var endTextUtf8Length = GetUtf8CharCount(ref end); var length = math.max(startTextUtf8Length, endTextUtf8Length); var currentTextLength = (int)math.round(length * t); var enumeratorStart = start.GetEnumerator(); var enumeratorEnd = end.GetEnumerator(); result = new(); for (int i = 0; i < length; i++) { var startMoveNext = enumeratorStart.MoveNext(); var endMoveNext = enumeratorEnd.MoveNext(); if (i < currentTextLength) { if (endMoveNext) { result.Append(enumeratorEnd.Current); } } else { if (startMoveNext) { result.Append(enumeratorStart.Current); } } } FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - currentTextLength); } unsafe static void FillRichText( ref UnsafeListBytes> startSymbols, ref UnsafeListBytes> endSymbols, int startTextUtf8Length, int endTextUtf8Length, float t, ScrambleMode scrambleMode, ref Random randomState, ref FixedString64Bytes customScrambleChars, out FixedString<#=size#>Bytes result) { var length = math.max(startTextUtf8Length, endTextUtf8Length); var currentTextLength = (int)math.round(length * t); var slicedText1 = SliceSymbols(ref endSymbols, 0, currentTextLength, out var length1); var slicedText2 = SliceSymbols(ref startSymbols, currentTextLength + 1, length - 1, out var length2); result = new FixedString<#=size#>Bytes(); result.Append(slicedText1); result.Append(slicedText2); FillScrambleChars(ref result, scrambleMode, ref randomState, ref customScrambleChars, length - (length1 + length2)); } unsafe static void FillScrambleChars( ref FixedString<#=size#>Bytes target, ScrambleMode scrambleMode, ref Random randomState, ref FixedString64Bytes customScrambleChars, int count) { if (scrambleMode == ScrambleMode.None) return; if (randomState.state == 0) randomState.InitState(); if (scrambleMode == ScrambleMode.Custom) { var customScrambleCharsUtf8Length = GetUtf8CharCount(ref customScrambleChars); for (int i = 0; i < count; i++) { target.Append(GetRuneOf(ref customScrambleChars, randomState.NextInt(0, customScrambleCharsUtf8Length))); } } else { for (int i = 0; i < count; i++) { target.Append(GetScrambleChar(scrambleMode, ref randomState)); } } } unsafe static FixedString<#=size#>Bytes SliceSymbols(ref UnsafeListBytes> symbols, int from, int to, out int resultRichTextLength) { var text = new FixedString<#=size#>Bytes(); RichTextSymbol<#=size#>Bytes* symbolsPtr = symbols.Ptr; var offset = 0; var tagIndent = 0; resultRichTextLength = 0; for (int i = 0; i < symbols.Length; i++) { RichTextSymbol<#=size#>Bytes* symbol = symbolsPtr + i; switch (symbol->Type) { case RichTextSymbolType.Text: var enumerator = symbol->Text.GetEnumerator(); while (enumerator.MoveNext()) { var current = enumerator.Current; if (from <= offset && offset < to) { text.Append(current); resultRichTextLength++; } offset++; if (offset >= to && tagIndent == 0) goto LOOP_END; } break; case RichTextSymbolType.TagStart: text.Append(symbol->Text); tagIndent++; break; case RichTextSymbolType.TagEnd: text.Append(symbol->Text); tagIndent--; if (offset >= to && tagIndent == 0) goto LOOP_END; break; } } LOOP_END: return text; } <# } #> } }