private static void EncodeHexString(byte[] sArray, ref ValueStringBuilder stringBuilder) { for (int i = 0; i < sArray.Length; i++) { HexConverter.ToCharsBuffer(sArray[i], stringBuilder.AppendSpan(2), 0, HexConverter.Casing.Upper); } }
public void AppendSpan_DataAppendedCorrectly() { var sb = new StringBuilder(); var vsb = new ValueStringBuilder(); for (int i = 1; i <= 1000; i++) { string s = i.ToString(); sb.Append(s); Span <char> span = vsb.AppendSpan(s.Length); Assert.Equal(sb.Length, vsb.Length); s.AsSpan().CopyTo(span); } Assert.Equal(sb.Length, vsb.Length); Assert.Equal(sb.ToString(), vsb.ToString()); }
private static void HtmlEncode(ReadOnlySpan <char> input, ref ValueStringBuilder output) { for (int i = 0; i < input.Length; i++) { char ch = input[i]; if (ch <= '>') { switch (ch) { case '<': output.Append("<"); break; case '>': output.Append(">"); break; case '"': output.Append("""); break; case '\'': output.Append("'"); break; case '&': output.Append("&"); break; default: output.Append(ch); break; } } else { int valueToEncode = -1; // set to >= 0 if needs to be encoded #if ENTITY_ENCODE_HIGH_ASCII_CHARS if (ch >= 160 && ch < 256) { // The seemingly arbitrary 160 comes from RFC valueToEncode = ch; } else #endif // ENTITY_ENCODE_HIGH_ASCII_CHARS if (char.IsSurrogate(ch)) { int scalarValue = GetNextUnicodeScalarValueFromUtf16Surrogate(input, ref i); if (scalarValue >= UNICODE_PLANE01_START) { valueToEncode = scalarValue; } else { // Don't encode BMP characters (like U+FFFD) since they wouldn't have // been encoded if explicitly present in the string anyway. ch = (char)scalarValue; } } if (valueToEncode >= 0) { // value needs to be encoded output.Append("&#"); // Use the buffer directly and reserve a conservative estimate of 10 chars. Span <char> encodingBuffer = output.AppendSpan(MaxInt32Digits); valueToEncode.TryFormat(encodingBuffer, out int charsWritten); // Invariant output.Length -= (MaxInt32Digits - charsWritten); output.Append(';'); } else { // write out the character directly output.Append(ch); } } } }
private static string FormatBigIntegerToHex(bool targetSpan, BigInteger value, char format, int digits, NumberFormatInfo info, Span <char> destination, out int charsWritten, out bool spanSuccess) { Debug.Assert(format == 'x' || format == 'X'); // Get the bytes that make up the BigInteger. byte[] arrayToReturnToPool = null; Span <byte> bits = stackalloc byte[64]; // arbitrary threshold if (!value.TryWriteOrCountBytes(bits, out int bytesWrittenOrNeeded)) { bits = arrayToReturnToPool = ArrayPool <byte> .Shared.Rent(bytesWrittenOrNeeded); bool success = value.TryWriteBytes(bits, out bytesWrittenOrNeeded); Debug.Assert(success); } bits = bits.Slice(0, bytesWrittenOrNeeded); Span <char> stackSpace = stackalloc char[128]; // each byte is typically two chars var sb = new ValueStringBuilder(stackSpace); int cur = bits.Length - 1; if (cur > -1) { // [FF..F8] drop the high F as the two's complement negative number remains clear // [F7..08] retain the high bits as the two's complement number is wrong without it // [07..00] drop the high 0 as the two's complement positive number remains clear bool clearHighF = false; byte head = bits[cur]; if (head > 0xF7) { head -= 0xF0; clearHighF = true; } if (head < 0x08 || clearHighF) { // {0xF8-0xFF} print as {8-F} // {0x00-0x07} print as {0-7} sb.Append(head < 10 ? (char)(head + '0') : format == 'X' ? (char)((head & 0xF) - 10 + 'A') : (char)((head & 0xF) - 10 + 'a')); cur--; } } if (cur > -1) { Span <char> chars = sb.AppendSpan((cur + 1) * 2); int charsPos = 0; string hexValues = format == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; while (cur > -1) { byte b = bits[cur--]; chars[charsPos++] = hexValues[b >> 4]; chars[charsPos++] = hexValues[b & 0xF]; } } if (digits > sb.Length) { // Insert leading zeros, e.g. user specified "X5" so we create "0ABCD" instead of "ABCD" sb.Insert( 0, value._sign >= 0 ? '0' : (format == 'x') ? 'f' : 'F', digits - sb.Length); } if (arrayToReturnToPool != null) { ArrayPool <byte> .Shared.Return(arrayToReturnToPool); } if (targetSpan) { spanSuccess = sb.TryCopyTo(destination, out charsWritten); return(null); } else { charsWritten = 0; spanSuccess = false; return(sb.ToString()); } }
internal static void EscapeAsciiChar(byte b, ref ValueStringBuilder to) { to.Append('%'); HexConverter.ToCharsBuffer(b, to.AppendSpan(2), 0, HexConverter.Casing.Upper); }
private static void EscapeStringToBuilder( ReadOnlySpan <char> stringToEscape, ref ValueStringBuilder vsb, ReadOnlySpan <bool> noEscape, bool checkExistingEscaped) { // Allocate enough stack space to hold any Rune's UTF8 encoding. Span <byte> utf8Bytes = stackalloc byte[4]; // Then enumerate every rune in the input. SpanRuneEnumerator e = stringToEscape.EnumerateRunes(); while (e.MoveNext()) { Rune r = e.Current; if (!r.IsAscii) { // The rune is non-ASCII, so encode it as UTF8, and escape each UTF8 byte. r.TryEncodeToUtf8(utf8Bytes, out int bytesWritten); foreach (byte b in utf8Bytes.Slice(0, bytesWritten)) { vsb.Append('%'); HexConverter.ToCharsBuffer(b, vsb.AppendSpan(2), 0, HexConverter.Casing.Upper); } continue; } // If the value doesn't need to be escaped, append it and continue. byte value = (byte)r.Value; if (noEscape[value]) { vsb.Append((char)value); continue; } // If we're checking for existing escape sequences, then if this is the beginning of // one, check the next two characters in the sequence. This is a little tricky to do // as we're using an enumerator, but luckily it's a ref struct-based enumerator: we can // make a copy and iterate through the copy without impacting the original, and then only // push the original ahead if we find what we're looking for in the copy. if (checkExistingEscaped && value == '%') { // If the next two characters are valid escaped ASCII, then just output them as-is. SpanRuneEnumerator tmpEnumerator = e; if (tmpEnumerator.MoveNext()) { Rune r1 = tmpEnumerator.Current; if (r1.IsAscii && IsHexDigit((char)r1.Value) && tmpEnumerator.MoveNext()) { Rune r2 = tmpEnumerator.Current; if (r2.IsAscii && IsHexDigit((char)r2.Value)) { vsb.Append('%'); vsb.Append((char)r1.Value); vsb.Append((char)r2.Value); e = tmpEnumerator; continue; } } } } // Otherwise, append the escaped character. vsb.Append('%'); HexConverter.ToCharsBuffer(value, vsb.AppendSpan(2), 0, HexConverter.Casing.Upper); } }
internal static bool TryGetUnicodeEquivalent(string hostname, ref ValueStringBuilder dest) { Debug.Assert(ReferenceEquals(hostname, UriHelper.StripBidiControlCharacters(hostname, hostname))); int curPos = 0; // We run a loop where for every label // a) if label is ascii and no ace then we lowercase it // b) if label is ascii and ace and not valid idn then just lowercase it // c) if label is ascii and ace and is valid idn then get its unicode eqvl // d) if label is unicode then clean it by running it through idnmapping do { if (curPos != 0) { dest.Append('.'); } bool asciiLabel = true; //find the dot or hit the end int newPos; for (newPos = curPos; (uint)newPos < (uint)hostname.Length; newPos++) { char c = hostname[newPos]; if (c == '.') { break; } if (c > '\x7F') { asciiLabel = false; if ((c == '\u3002') || // IDEOGRAPHIC FULL STOP (c == '\uFF0E') || // FULLWIDTH FULL STOP (c == '\uFF61')) // HALFWIDTH IDEOGRAPHIC FULL STOP { break; } } } if (!asciiLabel) { try { string asciiForm = s_idnMapping.GetAscii(hostname, curPos, newPos - curPos); dest.Append(s_idnMapping.GetUnicode(asciiForm)); } catch (ArgumentException) { return(false); } } else { bool aceValid = false; if ((uint)(curPos + 3) < (uint)hostname.Length && hostname[curPos] == 'x' && hostname[curPos + 1] == 'n' && hostname[curPos + 2] == '-' && hostname[curPos + 3] == '-') { // check ace validity try { dest.Append(s_idnMapping.GetUnicode(hostname, curPos, newPos - curPos)); aceValid = true; } catch (ArgumentException) { // not valid ace so treat it as a normal ascii label } } if (!aceValid) { // for invalid aces we just lowercase the label ReadOnlySpan <char> slice = hostname.AsSpan(curPos, newPos - curPos); int charsWritten = slice.ToLowerInvariant(dest.AppendSpan(slice.Length)); Debug.Assert(charsWritten == slice.Length); } } curPos = newPos + 1; } while (curPos < hostname.Length); return(true); }
private static void AppendByteArrayAsHexString(ref ValueStringBuilder builder, byte[] byteArray) { Debug.Assert(byteArray != null); HexConverter.EncodeToUtf16(byteArray, builder.AppendSpan(byteArray.Length * 2)); }
private static string FormatBigIntegerToHex(BigInteger value, char format, int digits, NumberFormatInfo info) { Debug.Assert(format == 'x' || format == 'X'); // Get the bytes that make up the BigInteger. Span <byte> bits = stackalloc byte[64]; // arbitrary limit to switch from stack to heap bits = value.TryWriteBytes(bits, out int bytesWritten) ? bits.Slice(0, bytesWritten) : value.ToByteArray(); Span <char> stackSpace = stackalloc char[128]; // each byte is typically two chars var sb = new ValueStringBuilder(stackSpace); int cur = bits.Length - 1; if (cur > -1) { // [FF..F8] drop the high F as the two's complement negative number remains clear // [F7..08] retain the high bits as the two's complement number is wrong without it // [07..00] drop the high 0 as the two's complement positive number remains clear bool clearHighF = false; byte head = bits[cur]; if (head > 0xF7) { head -= 0xF0; clearHighF = true; } if (head < 0x08 || clearHighF) { // {0xF8-0xFF} print as {8-F} // {0x00-0x07} print as {0-7} sb.Append(head < 10 ? (char)(head + '0') : format == 'X' ? (char)((head & 0xF) - 10 + 'A') : (char)((head & 0xF) - 10 + 'a')); cur--; } } if (cur > -1) { Span <char> chars = sb.AppendSpan((cur + 1) * 2); int charsPos = 0; string hexValues = format == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; while (cur > -1) { byte b = bits[cur--]; chars[charsPos++] = hexValues[b >> 4]; chars[charsPos++] = hexValues[b & 0xF]; } } if (digits > sb.Length) { // Insert leading zeros, e.g. user specified "X5" so we create "0ABCD" instead of "ABCD" sb.Insert( 0, value._sign >= 0 ? '0' : (format == 'x') ? 'f' : 'F', digits - sb.Length); } return(sb.ToString()); }