internal static int PrependDevicePathChars(ref ValueStringBuilder content, bool isDosUnc, ref ValueStringBuilder buffer) { int length = content.Length; length += isDosUnc ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength : PathInternal.DevicePrefixLength; buffer.EnsureCapacity(length + 1); buffer.Length = 0; if (isDosUnc) { // Is a \\Server\Share, put \\?\UNC\ in the front buffer.Append(PathInternal.UncExtendedPathPrefix); // Copy Server\Share\... over to the buffer buffer.Append(content.AsSpan(PathInternal.UncPrefixLength)); // Return the prefix difference return(PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength); } else { // Not an UNC, put the \\?\ prefix in front, then the original string buffer.Append(PathInternal.ExtendedPathPrefix); buffer.Append(content.AsSpan()); return(PathInternal.DevicePrefixLength); } }
public static void HtmlDecode(string?value, TextWriter output) { if (output == null) { throw new ArgumentNullException(nameof(output)); } if (string.IsNullOrEmpty(value)) { output.Write(value); return; } ReadOnlySpan <char> valueSpan = value.AsSpan(); int index = IndexOfHtmlDecodingChars(valueSpan); if (index == -1) { output.Write(value); return; } // In the worst case the decoded string has the same length. // For small inputs we use stack allocation. ValueStringBuilder sb = value.Length <= 256 ? new ValueStringBuilder(stackalloc char[256]) : new ValueStringBuilder(value.Length); sb.Append(valueSpan.Slice(0, index)); HtmlDecode(valueSpan.Slice(index), ref sb); output.Write(sb.AsSpan()); sb.Dispose(); }
internal static string Normalize(string path) { Span <char> initialBuffer = stackalloc char[PathInternal.MaxShortPath]; var builder = new ValueStringBuilder(initialBuffer); // Get the full path GetFullPathName(path.AsSpan(), ref builder); // If we have the exact same string we were passed in, don't allocate another string. // TryExpandShortName does this input identity check. string result = builder.AsSpan().IndexOf('~') >= 0 ? TryExpandShortFileName(ref builder, originalPath: path) : builder.AsSpan().Equals(path.AsSpan(), StringComparison.Ordinal) ? path : builder.ToString(); // Clear the buffer builder.Dispose(); return(result); }
internal static unsafe char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { ValueStringBuilder vsb = new ValueStringBuilder(dest.Length); vsb.Append(dest.AsSpan(0, destPosition)); UnescapeString(pStr, start, end, ref vsb, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); if (vsb.Length > dest.Length) { dest = vsb.AsSpan().ToArray(); } else { vsb.AsSpan(destPosition).TryCopyTo(dest.AsSpan(destPosition)); } destPosition = vsb.Length; vsb.Dispose(); return(dest); }
// In case of link target being relative: // Preserve the full path of the directory of the previous path // so the final target is returned with a valid full path static void GetLinkTargetFullPath(ref ValueStringBuilder sb, ReadOnlySpan <char> linkTarget) { if (PathInternal.IsPartiallyQualified(linkTarget)) { sb.Length = Path.GetDirectoryNameOffset(sb.AsSpan()); sb.Append(PathInternal.DirectorySeparatorChar); } else { sb.Length = 0; } sb.Append(linkTarget); }
internal static string Normalize(ref ValueStringBuilder path) { var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); // Get the full path GetFullPathName(path.AsSpan(terminate: true), ref builder); string result = builder.AsSpan().IndexOf('~') >= 0 ? TryExpandShortFileName(ref builder, originalPath: null) : builder.ToString(); // Clear the buffer builder.Dispose(); return(result); }
public static string UnescapeDataString(string stringToUnescape) { if ((object)stringToUnescape == null) { throw new ArgumentNullException(nameof(stringToUnescape)); } if (stringToUnescape.Length == 0) { return(string.Empty); } unsafe { fixed(char *pStr = stringToUnescape) { int position; for (position = 0; position < stringToUnescape.Length; ++position) { if (pStr[position] == '%') { break; } } if (position == stringToUnescape.Length) { return(stringToUnescape); } UnescapeMode unescapeMode = UnescapeMode.Unescape | UnescapeMode.UnescapeAll; position = 0; ValueStringBuilder vsb = new ValueStringBuilder(stringToUnescape.Length); UriHelper.UnescapeString(stringToUnescape, 0, stringToUnescape.Length, ref vsb, ref position, c_DummyChar, c_DummyChar, c_DummyChar, unescapeMode, null, false); ReadOnlySpan <char> resultSpan = vsb.AsSpan(0, position); string result = resultSpan.SequenceEqual(stringToUnescape) ? stringToUnescape : resultSpan.ToString(); vsb.Dispose(); return(result); } } }
public void AsSpan_ReturnsCorrectValue_DoesntClearBuilder() { var sb = new StringBuilder(); var vsb = new ValueStringBuilder(); for (int i = 1; i <= 100; i++) { string s = i.ToString(); sb.Append(s); vsb.Append(s); } var resultString = new string(vsb.AsSpan()); Assert.Equal(sb.ToString(), resultString); Assert.NotEqual(0, sb.Length); Assert.Equal(sb.Length, vsb.Length); }
public static void HtmlEncode(string?value, TextWriter output) { if (output == null) { throw new ArgumentNullException(nameof(output)); } if (string.IsNullOrEmpty(value)) { output.Write(value); return; } ReadOnlySpan <char> valueSpan = value.AsSpan(); // Don't create ValueStringBuilder if we don't have anything to encode int index = IndexOfHtmlEncodingChars(valueSpan); if (index == -1) { output.Write(value); return; } // For small inputs we allocate on the stack. In most cases a buffer three // times larger the original string should be sufficient as usually not all // characters need to be encoded. // For larger string we rent the input string's length plus a fixed // conservative amount of chars from the ArrayPool. Span <char> buffer = value.Length < 80 ? stackalloc char[256] : null; ValueStringBuilder sb = buffer != null ? new ValueStringBuilder(buffer) : new ValueStringBuilder(value.Length + 200); sb.Append(valueSpan.Slice(0, index)); HtmlEncode(valueSpan.Slice(index), ref sb); output.Write(sb.AsSpan()); sb.Dispose(); }
internal static string TryExpandShortFileName(ref ValueStringBuilder outputBuilder, string?originalPath) { // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To // avoid allocating a lot we'll create only one input array and modify the contents with embedded nulls. Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuilder.AsSpan()), "should have resolved by now"); // We'll have one of a few cases by now (the normalized path will have already: // // 1. Dos path (C:\) // 2. Dos UNC (\\Server\Share) // 3. Dos device path (\\.\C:\, \\?\C:\) // // We want to put the extended syntax on the front if it doesn't already have it (for long path support and speed), which may mean switching from \\.\. // // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is). int rootLength = PathInternal.GetRootLength(outputBuilder.AsSpan()); bool isDevice = PathInternal.IsDevice(outputBuilder.AsSpan()); // As this is a corner case we're not going to add a stackalloc here to keep the stack pressure down. var inputBuilder = new ValueStringBuilder(); bool isDosUnc = false; int rootDifference = 0; bool wasDotDevice = false; // Add the extended prefix before expanding to allow growth over MAX_PATH if (isDevice) { // We have one of the following (\\?\ or \\.\) inputBuilder.Append(outputBuilder.AsSpan()); if (outputBuilder[2] == '.') { wasDotDevice = true; inputBuilder[2] = '?'; } } else { isDosUnc = !PathInternal.IsDevice(outputBuilder.AsSpan()) && outputBuilder.Length > 1 && outputBuilder[0] == '\\' && outputBuilder[1] == '\\'; rootDifference = PrependDevicePathChars(ref outputBuilder, isDosUnc, ref inputBuilder); } rootLength += rootDifference; int inputLength = inputBuilder.Length; bool success = false; int foundIndex = inputBuilder.Length - 1; while (!success) { uint result = Interop.Kernel32.GetLongPathNameW( ref inputBuilder.GetPinnableReference(terminate: true), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity); // Replace any temporary null we added if (inputBuilder[foundIndex] == '\0') { inputBuilder[foundIndex] = '\\'; } if (result == 0) { // Look to see if we couldn't find the file int error = Marshal.GetLastWin32Error(); if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND) { // Some other failure, give up break; } // We couldn't find the path at the given index, start looking further back in the string. foundIndex--; for (; foundIndex > rootLength && inputBuilder[foundIndex] != '\\'; foundIndex--) { ; } if (foundIndex == rootLength) { // Can't trim the path back any further break; } else { // Temporarily set a null in the string to get Windows to look further up the path inputBuilder[foundIndex] = '\0'; } } else if (result > outputBuilder.Capacity) { // Not enough space. The result count for this API does not include the null terminator. outputBuilder.EnsureCapacity(checked ((int)result)); } else { // Found the path success = true; outputBuilder.Length = checked ((int)result); if (foundIndex < inputLength - 1) { // It was a partial find, put the non-existent part of the path back outputBuilder.Append(inputBuilder.AsSpan(foundIndex, inputBuilder.Length - foundIndex)); } } } // If we were able to expand the path, use it, otherwise use the original full path result ref ValueStringBuilder builderToUse = ref (success ? ref outputBuilder : ref inputBuilder);