internal static char[]? EscapeString( ReadOnlySpan <char> stringToEscape, char[]?dest, ref int destPos, bool checkExistingEscaped, char forceEscape1 = '\0', char forceEscape2 = '\0') { // Get the table of characters that do not need to be escaped. ReadOnlySpan <bool> noEscape = stackalloc bool[0]; if ((forceEscape1 | forceEscape2) == 0) { noEscape = UnreservedReservedTable; } else { Span <bool> tmp = stackalloc bool[0x80]; UnreservedReservedTable.CopyTo(tmp); tmp[forceEscape1] = false; tmp[forceEscape2] = false; noEscape = tmp; } // If the whole string is made up of ASCII unreserved chars, take a fast pasth. Per the contract, if // dest is null, just return it. If it's not null, copy everything to it and update destPos accordingly; // if that requires resizing it, do so. Debug.Assert(!noEscape['%'], "Need to treat % specially in case checkExistingEscaped is true"); int i = 0; char c; for (; i < stringToEscape.Length && (c = stringToEscape[i]) <= 0x7F && noEscape[c]; i++) { ; } if (i == stringToEscape.Length) { if (dest != null) { EnsureCapacity(dest, destPos, stringToEscape.Length); stringToEscape.CopyTo(dest.AsSpan(destPos)); destPos += stringToEscape.Length; } return(dest); } // Otherwise, create a ValueStringBuilder to store the escaped data into, // append to it all of the noEscape chars we already iterated through, and // escape the rest into the ValueStringBuilder. var vsb = new ValueStringBuilder(stackalloc char[256]); vsb.Append(stringToEscape.Slice(0, i)); EscapeStringToBuilder(stringToEscape.Slice(i), ref vsb, noEscape, checkExistingEscaped); // Finally update dest with the result. EnsureCapacity(dest, destPos, vsb.Length); vsb.TryCopyTo(dest.AsSpan(destPos), out int charsWritten); destPos += charsWritten; return(dest);
internal static unsafe void EscapeString(ReadOnlySpan <char> stringToEscape, ref ValueStringBuilder dest, bool checkExistingEscaped, char forceEscape1 = '\0', char forceEscape2 = '\0') { // Get the table of characters that do not need to be escaped. ReadOnlySpan <bool> noEscape = stackalloc bool[0]; if ((forceEscape1 | forceEscape2) == 0) { noEscape = UnreservedReservedTable; } else { Span <bool> tmp = stackalloc bool[0x80]; UnreservedReservedTable.CopyTo(tmp); tmp[forceEscape1] = false; tmp[forceEscape2] = false; noEscape = tmp; } // If the whole string is made up of ASCII unreserved chars, take a fast pasth. Per the contract, if // dest is null, just return it. If it's not null, copy everything to it and update destPos accordingly; // if that requires resizing it, do so. Debug.Assert(!noEscape['%'], "Need to treat % specially in case checkExistingEscaped is true"); int i = 0; char c; for (; i < stringToEscape.Length && (c = stringToEscape[i]) <= 0x7F && noEscape[c]; i++) { ; } if (i == stringToEscape.Length) { dest.Append(stringToEscape); } else { dest.Append(stringToEscape.Slice(0, i)); // CS8350 & CS8352: We can't pass `noEscape` and `dest` as arguments together as that could leak the scope of the above stackalloc // As a workaround, re-create the Span in a way that avoids analysis ReadOnlySpan <bool> noEscapeCopy = MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(noEscape), noEscape.Length); EscapeStringToBuilder(stringToEscape.Slice(i), ref dest, noEscapeCopy, checkExistingEscaped); } }