Example #1
0
        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);
            }
        }
Example #2
0
        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();
        }
Example #3
0
        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);
        }
Example #4
0
        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);
        }
Example #5
0
 // 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);
 }
Example #6
0
        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);
        }
Example #7
0
        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);
                }
            }
        }
Example #8
0
        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);
        }
Example #9
0
        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();
        }
Example #10
0
        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);