Beispiel #1
0
        static void PerfTest()
        {
            var formatter = new StringBuffer();
            var builder = new StringBuilder();

            GC.Collect(2, GCCollectionMode.Forced, true);
            var gcCount = GC.CollectionCount(0);
            var timer = Stopwatch.StartNew();

            for (int k = 0; k < mul; k++) {
                for (int i = 0; i < count; i++)
                    formatter.AppendFormat(formatTest, v1, v2);
                formatter.Clear();
            }
            timer.Stop();
            Console.WriteLine("Mine : {0} us/format", timer.ElapsedMilliseconds * 1000.0 / (count * mul));
            Console.WriteLine("GCs  : {0}", GC.CollectionCount(0) - gcCount);
            Console.WriteLine();

            GC.Collect(2, GCCollectionMode.Forced, true);
            gcCount = GC.CollectionCount(0);
            timer = Stopwatch.StartNew();

            for (int k = 0; k < mul; k++) {
                for (int i = 0; i < count; i++)
                    builder.AppendFormat(formatTest, v1, v2);
                builder.Clear();
            }
            timer.Stop();
            Console.WriteLine("BCL  : {0} us/format", timer.ElapsedMilliseconds * 1000.0 / (count * mul));
            Console.WriteLine("GCs  : {0}", GC.CollectionCount(0) - gcCount);
        }
Beispiel #2
0
 public void SetOverIndexThrowsArgumentOutOfRange()
 {
     using (var buffer = new StringBuffer())
     {
         Assert.Throws<ArgumentOutOfRangeException>(() => { buffer[0] = 'Q'; });
     }
 }
Beispiel #3
0
 static void CustomFormat(StringBuffer buffer, Blah blah, StringView format)
 {
     if (format == "yes")
         buffer.Append("World!");
     else
         buffer.Append("(Goodbye)");
 }
Beispiel #4
0
 public void CanIndexChar()
 {
     using (var buffer = new StringBuffer())
     {
         buffer.Length = 1;
         buffer[0] = 'Q';
         Assert.Equal(buffer[0], 'Q');
     }
 }
 /// <summary>
 /// Returns true if the path uses the extended syntax (\\?\)
 /// </summary>
 internal static bool IsExtended(StringBuffer path)
 {
     // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
     // Skipping of normalization will *only* occur if back slashes ('\') are used.
     return path.Length >= DevicePrefixLength
         && path[0] == '\\'
         && (path[1] == '\\' || path[1] == '?')
         && path[2] == '?'
         && path[3] == '\\';
 }
Beispiel #6
0
 public void ReduceLength()
 {
     using (var buffer = new StringBuffer("Food"))
     {
         Assert.Equal((ulong)5, buffer.CharCapacity);
         buffer.Length = 3;
         Assert.Equal("Foo", buffer.ToString());
         // Shouldn't reduce capacity when dropping length
         Assert.Equal((ulong)5, buffer.CharCapacity);
     }
 }
 /// <summary>
 /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
 /// </summary>
 internal static bool IsDevice(StringBuffer path)
 {
     // If the path begins with any two separators is will be recognized and normalized and prepped with
     // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
     return IsExtended(path)
         ||
         (
             path.Length >= DevicePrefixLength
             && IsDirectorySeparator(path[0])
             && IsDirectorySeparator(path[1])
             && (path[2] == '.' || path[2] == '?')
             && IsDirectorySeparator(path[3])
         );
 }
Beispiel #8
0
        public unsafe void CreateFromString()
        {
            string testString = "Test";
            using (var buffer = new StringBuffer(testString))
            {
                Assert.Equal((ulong)testString.Length, buffer.Length);
                Assert.Equal((ulong)testString.Length + 1, buffer.CharCapacity);

                for (int i = 0; i < testString.Length; i++)
                {
                    Assert.Equal(testString[i], buffer[(ulong)i]);
                }

                // Check the null termination
                Assert.Equal('\0', buffer.CharPointer[testString.Length]);

                Assert.Equal(testString, buffer.ToString());
            }
        }
Beispiel #9
0
        static void Main(string[] args)
        {
            var f = new StringBuffer();
            f.AppendFormat(formatTest, v1, v2);
            Console.WriteLine(f.ToString());
            Console.WriteLine(formatTest, v1, v2);

            // test custom formatters
            StringBuffer.SetCustomFormatter<Blah>(CustomFormat);
            f.Clear();
            f.AppendFormat("Hello {0:yes}{0:no}", new Blah { Thing = 42 });
            Console.WriteLine(f.ToString());

            // test static convenience method
            Console.WriteLine(StringBuffer.Format(formatTest, v1, v2));

            PerfTest();
            #if DEBUG
            Console.ReadLine();
            #endif
        }
        /// <summary>
        /// Returns true if the path specified is relative to the current drive or working directory.
        /// Returns false if the path is fixed to a specific drive or UNC path.  This method does no
        /// validation of the path (URIs will be returned as relative as a result).
        /// </summary>
        /// <remarks>
        /// Handles paths that use the alternate directory separator.  It is a frequent mistake to
        /// assume that rooted paths (Path.IsPathRooted) are not relative.  This isn't the case.
        /// "C:a" is drive relative- meaning that it will be resolved against the current directory
        /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
        /// will not be used to modify the path).
        /// </remarks>
        internal static bool IsRelative(StringBuffer path)
        {
            if (path.Length < 2)
            {
                // It isn't fixed, it must be relative.  There is no way to specify a fixed
                // path with one character (or less).
                return true;
            }

            if (IsDirectorySeparator(path[0]))
            {
                // There is no valid way to specify a relative path with two initial slashes
                return !IsDirectorySeparator(path[1]);
            }

            // The only way to specify a fixed path that doesn't begin with two slashes
            // is the drive, colon, slash format- i.e. C:\
            return !((path.Length >= 3)
                && (path[1] == Path.VolumeSeparatorChar)
                && IsDirectorySeparator(path[2]));
        }
        /// <summary>
        /// Returns true if the path specified is relative to the current drive or working directory.
        /// Returns false if the path is fixed to a specific drive or UNC path.  This method does no
        /// validation of the path (URIs will be returned as relative as a result).
        /// </summary>
        /// <remarks>
        /// Handles paths that use the alternate directory separator.  It is a frequent mistake to
        /// assume that rooted paths (Path.IsPathRooted) are not relative.  This isn't the case.
        /// "C:a" is drive relative- meaning that it will be resolved against the current directory
        /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
        /// will not be used to modify the path).
        /// </remarks>
        internal static bool IsPartiallyQualified(StringBuffer path)
        {
            if (path.Length < 2)
            {
                // It isn't fixed, it must be relative.  There is no way to specify a fixed
                // path with one character (or less).
                return true;
            }

            if (IsDirectorySeparator(path[0]))
            {
                // There is no valid way to specify a relative path with two initial slashes or
                // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
                return !(path[1] == '?' || IsDirectorySeparator(path[1]));
            }

            // The only way to specify a fixed path that doesn't begin with two slashes
            // is the drive, colon, slash format- i.e. C:\
            return !((path.Length >= 3)
                && (path[1] == Path.VolumeSeparatorChar)
                && IsDirectorySeparator(path[2]));
        }
Beispiel #12
0
        private static void GetFullPathName(string path, StringBuffer fullPath)
        {
            // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
            // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
            Debug.Assert(PathInternal.IsRelative(path) || !PathInternal.IsExtended(path));

            // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
            int startIndex = PathInternal.PathStartSkip(path);

            fixed (char* pathStart = path)
            {
                uint result = 0;
                while ((result = Interop.mincore.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.CharCapacity, fullPath.GetHandle(), IntPtr.Zero)) > fullPath.CharCapacity)
                {
                    // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity.
                    fullPath.EnsureCharCapacity(result);
                }

                if (result == 0)
                {
                    // Failure, get the error and throw
                    int errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == 0)
                        errorCode = Interop.mincore.Errors.ERROR_BAD_PATHNAME;
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
                }

                fullPath.Length = result;
            }
        }
Beispiel #13
0
 private static bool IsUnc(StringBuffer buffer)
 {
     return buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
 }
Beispiel #14
0
        private static string TryExpandShortFileName(StringBuffer outputBuffer, 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 like crazy we'll create only one input array and modify the contents with embedded nulls.

            Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuffer), "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, 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).

            uint rootLength = PathInternal.GetRootLength(outputBuffer);
            bool isDevice = PathInternal.IsDevice(outputBuffer);

            StringBuffer inputBuffer = null;
            bool isDosUnc = false;
            uint 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 \\.\)
                inputBuffer = new StringBuffer();
                inputBuffer.Append(outputBuffer);

                if (outputBuffer[2] == '.')
                {
                    wasDotDevice = true;
                    inputBuffer[2] = '?';
                }
            }
            else
            {
                isDosUnc = IsDosUnc(outputBuffer);
                rootDifference = GetInputBuffer(outputBuffer, isDosUnc, out inputBuffer);
            }

            rootLength += rootDifference;
            uint inputLength = inputBuffer.Length;

            bool success = false;
            uint foundIndex = inputBuffer.Length - 1;

            while (!success)
            {
                uint result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity);

                // Replace any temporary null we added
                if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';

                if (result == 0)
                {
                    // Look to see if we couldn't find the file
                    int error = Marshal.GetLastWin32Error();
                    if (error != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && error != Interop.mincore.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 && inputBuffer[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
                        inputBuffer[foundIndex] = '\0';
                    }
                }
                else if (result > outputBuffer.CharCapacity)
                {
                    // Not enough space. The result count for this API does not include the null terminator.
                    outputBuffer.EnsureCharCapacity(result);
                    result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity);
                }
                else
                {
                    // Found the path
                    success = true;
                    outputBuffer.Length = result;
                    if (foundIndex < inputLength - 1)
                    {
                        // It was a partial find, put the non-existent part of the path back
                        outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
                    }
                }
            }

            // Strip out the prefix and return the string
            StringBuffer bufferToUse = success ? outputBuffer : inputBuffer;
            if (wasDotDevice)
                bufferToUse[2] = '.';

            string returnValue = null;

            int newLength = (int)(bufferToUse.Length - rootDifference);
            if (isDosUnc)
            {
                // Need to go from \\?\UNC\ to \\?\UN\\
                bufferToUse[(uint)PathInternal.UncExtendedPathPrefix.Length - 1] = '\\';
            }

            // We now need to strip out any added characters at the front of the string
            if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
            {
                // Use the original path to avoid allocating
                returnValue = originalPath;
            }
            else
            {
                returnValue = bufferToUse.Substring(rootDifference, newLength);
            }

            inputBuffer.Dispose();
            return returnValue;
        }
Beispiel #15
0
 private static bool IsDosUnc(StringBuffer buffer)
 {
     return !PathInternal.IsDevice(buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
 }
Beispiel #16
0
        unsafe internal static string Normalize(string path, uint maxPathLength, bool checkInvalidCharacters, bool expandShortPaths)
        {
            // Get the full path
            StringBuffer fullPath = t_fullPathBuffer ?? (t_fullPathBuffer = new StringBuffer(PathInternal.MaxShortPath));
            try
            {
                GetFullPathName(path, fullPath);

                // Trim whitespace off the end of the string. Win32 normalization trims only U+0020.
                fullPath.TrimEnd(Path.TrimEndChars);

                if (fullPath.Length >= maxPathLength)
                {
                    // Fullpath is genuinely too long
                    throw new PathTooLongException();
                }

                // Checking path validity used to happen before getting the full path name. To avoid additional input allocation
                // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that
                // used to get kicked back (notably segments with invalid characters might get removed via "..").
                //
                // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to
                // expand short file names.

                // Scan the path for:
                //
                //  - Illegal path characters.
                //  - Invalid UNC paths like \\, \\server, \\server\.
                //  - Segments that are too long (over MaxComponentLength)

                // As the path could be > 60K, we'll combine the validity scan. None of these checks are performed by the Win32
                // GetFullPathName() API.

                bool possibleShortPath = false;
                bool foundTilde = false;

                // We can get UNCs as device paths through this code (e.g. \\.\UNC\), we won't validate them as there isn't
                // an easy way to normalize without extensive cost (we'd have to hunt down the canonical name for any device
                // path that contains UNC or  to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\,
                // \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc.
                bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\';
                bool isDevice = PathInternal.IsDevice(fullPath);
                bool possibleBadUnc = specialPath && !isDevice;
                uint index = specialPath ? 2u : 0;
                uint lastSeparator = specialPath ? 1u : 0;
                uint segmentLength;
                char* start = fullPath.CharPointer;
                char current;

                while (index < fullPath.Length)
                {
                    current = start[index];

                    // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~'
                    if (current < '?' || current == '\\' || current == '|' || current == '~')
                    {
                        switch (current)
                        {
                            case '|':
                            case '>':
                            case '<':
                            case '\"':
                                if (checkInvalidCharacters) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));
                                // No point in expanding a bad path
                                foundTilde = false;
                                break;
                            case '~':
                                foundTilde = true;
                                break;
                            case '\\':
                                segmentLength = index - lastSeparator - 1;
                                if (segmentLength > (uint)PathInternal.MaxComponentLength)
                                    throw new PathTooLongException();
                                lastSeparator = index;

                                if (foundTilde)
                                {
                                    if (segmentLength <= MaxShortName)
                                    {
                                        // Possibly a short path.
                                        possibleShortPath = true;
                                    }

                                    foundTilde = false;
                                }

                                if (possibleBadUnc)
                                {
                                    // If we're at the end of the path and this is the first separator, we're missing the share.
                                    // Otherwise we're good, so ignore UNC tracking from here.
                                    if (index == fullPath.Length - 1)
                                        throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC"));
                                    else
                                        possibleBadUnc = false;
                                }

                                break;

                            default:
                                if (checkInvalidCharacters && current < ' ') throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));
                                break;
                        }
                    }

                    index++;
                }

                if (possibleBadUnc)
                    throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC"));

                segmentLength = fullPath.Length - lastSeparator - 1;
                if (segmentLength > (uint)PathInternal.MaxComponentLength)
                    throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

                if (foundTilde && segmentLength <= MaxShortName)
                    possibleShortPath = true;

                // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but
                // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide.
                if (expandShortPaths && possibleShortPath)
                {
                    return TryExpandShortFileName(fullPath, originalPath: path);
                }
                else
                {
                    if (fullPath.Length == (uint)path.Length && fullPath.StartsWith(path))
                    {
                        // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder.
                        return path;
                    }
                    else
                    {
                        return fullPath.ToString();
                    }
                }
            }
            finally
            {
                // Clear the buffer
                fullPath.Free();
            }
        }
Beispiel #17
0
        private static uint GetInputBuffer(StringBuffer content, bool isDosUnc, out StringBuffer buffer)
        {
            uint length = content.Length;

            length += isDosUnc
                ? (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
                : PathInternal.DevicePrefixLength;

            buffer = new StringBuffer(length);

            if (isDosUnc)
            {
                // Put the extended UNC prefix (\\?\UNC\) in front of the path
                buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix);

                // Copy the source buffer over after the existing UNC prefix
                content.CopyTo(
                    bufferIndex: PathInternal.UncPrefixLength,
                    destination: buffer,
                    destinationIndex: PathInternal.UncExtendedPrefixLength,
                    count: content.Length - PathInternal.UncPrefixLength);

                // Return the prefix difference
                return (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
            }
            else
            {
                uint prefixSize = (uint)PathInternal.ExtendedPathPrefix.Length;
                buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix);
                content.CopyTo(bufferIndex: 0, destination: buffer, destinationIndex: prefixSize, count: content.Length);
                return prefixSize;
            }
        }
Beispiel #18
0
 public void CopyToBufferString(string destination, string content, ulong destinationIndex, ulong bufferIndex, ulong count, string expected)
 {
     using (var buffer = new StringBuffer(content))
     using (var destinationBuffer = new StringBuffer(destination))
     {
         buffer.CopyTo(bufferIndex, destinationBuffer, destinationIndex, count);
         Assert.Equal(expected, destinationBuffer.ToString());
     }
 }
Beispiel #19
0
        unsafe internal static string GetLongPathName(string path)
        {
            using (StringBuffer outputBuffer = new StringBuffer((uint)path.Length))
            {
                uint result = 0;
                while ((result = Win32Native.GetLongPathNameW(path, outputBuffer.GetHandle(), outputBuffer.CharCapacity)) > outputBuffer.CharCapacity)
                {
                    // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity.
                    outputBuffer.EnsureCharCapacity(result);
                }

                if (result == 0)
                {
                    // Failure, get the error and throw
                    GetErrorAndThrow(path);
                }

                outputBuffer.Length = result;
                return outputBuffer.ToString();
            }
        }
Beispiel #20
0
 public void StartsWithNullThrows()
 {
     using (var buffer = new StringBuffer())
     {
         Assert.Throws<ArgumentNullException>(() => buffer.StartsWith(null));
     }
 }
Beispiel #21
0
        /// <summary>
        /// Normalize the given path.
        /// </summary>
        /// <remarks>
        /// Normalizes via Win32 GetFullPathName(). It will also trim all "typical" whitespace at the end of the path (see s_trimEndChars). Will also trim initial
        /// spaces if the path is determined to be rooted.
        /// 
        /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt)
        /// </remarks>
        /// <param name="path">Path to normalize</param>
        /// <param name="checkInvalidCharacters">True to check for invalid characters</param>
        /// <param name="expandShortPaths">Attempt to expand short paths if true</param>
        /// <exception cref="ArgumentException">Thrown if the path is an illegal UNC (does not contain a full server/share) or contains illegal characters.</exception>
        /// <exception cref="PathTooLongException">Thrown if the path or a path segment exceeds the filesystem limits.</exception>
        /// <exception cref="FileNotFoundException">Thrown if Windows returns ERROR_FILE_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
        /// <exception cref="DirectoryNotFoundException">Thrown if Windows returns ERROR_PATH_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
        /// <exception cref="UnauthorizedAccessException">Thrown if Windows returns ERROR_ACCESS_DENIED. (See Win32Marshal.GetExceptionForWin32Error)</exception>
        /// <exception cref="IOException">Thrown if Windows returns an error that doesn't map to the above. (See Win32Marshal.GetExceptionForWin32Error)</exception>
        /// <returns>Normalized path</returns>
        internal static string Normalize(string path, bool checkInvalidCharacters, bool expandShortPaths)
        {
            // Get the full path
            StringBuffer fullPath = t_fullPathBuffer ?? (t_fullPathBuffer = new StringBuffer(PathInternal.MaxShortPath));
            try
            {
                GetFullPathName(path, fullPath);

                // Trim whitespace off the end of the string. Win32 normalization trims only U+0020.
                fullPath.TrimEnd(s_trimEndChars);

                if (fullPath.Length >= PathInternal.MaxLongPath)
                {
                    // Fullpath is genuinely too long
                    throw new PathTooLongException(SR.IO_PathTooLong);
                }

                // Checking path validity used to happen before getting the full path name. To avoid additional input allocation
                // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that
                // used to get kicked back (notably segments with invalid characters might get removed via "..").
                //
                // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to
                // expand short file names.

                // Scan the path for:
                //
                //  - Illegal path characters.
                //  - Invalid UNC paths like \\, \\server, \\server\.
                //  - Segments that are too long (over MaxComponentLength)

                // As the path could be > 30K, we'll combine the validity scan. None of these checks are performed by the Win32
                // GetFullPathName() API.

                bool possibleShortPath = false;
                bool foundTilde = false;
                bool possibleBadUnc = IsUnc(fullPath);
                ulong index = possibleBadUnc ? (ulong)2 : 0;
                ulong lastSeparator = possibleBadUnc ? (ulong)1 : 0;
                ulong segmentLength;
                char* start = fullPath.CharPointer;
                char current;

                while (index < fullPath.Length)
                {
                    current = start[index];

                    // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~'
                    if (current < '?' || current == '\\' || current == '|' || current == '~')
                    {
                        switch (current)
                        {
                            case '|':
                            case '>':
                            case '<':
                            case '\"':
                                if (checkInvalidCharacters) throw new ArgumentException(SR.Argument_InvalidPathChars, "path");
                                foundTilde = false;
                                break;
                            case '~':
                                foundTilde = true;
                                break;
                            case '\\':
                                segmentLength = index - lastSeparator - 1;
                                if (segmentLength > (ulong)PathInternal.MaxComponentLength)
                                    throw new PathTooLongException(SR.IO_PathTooLong);
                                lastSeparator = index;

                                if (foundTilde)
                                {
                                    if (segmentLength <= MaxShortName)
                                    {
                                        // Possibly a short path.
                                        possibleShortPath = true;
                                    }

                                    foundTilde = false;
                                }

                                if (possibleBadUnc)
                                {
                                    // If we're at the end of the path and this is the first separator, we're missing the share.
                                    // Otherwise we're good, so ignore UNC tracking from here.
                                    if (index == fullPath.Length - 1)
                                        throw new ArgumentException(SR.Arg_PathIllegalUNC);
                                    else
                                        possibleBadUnc = false;
                                }

                                break;

                            default:
                                if (checkInvalidCharacters && current < ' ') throw new ArgumentException(SR.Argument_InvalidPathChars, "path");
                                break;
                        }
                    }

                    index++;
                }

                if (possibleBadUnc)
                    throw new ArgumentException(SR.Arg_PathIllegalUNC);

                segmentLength = fullPath.Length - lastSeparator - 1;
                if (segmentLength > (ulong)PathInternal.MaxComponentLength)
                    throw new PathTooLongException(SR.IO_PathTooLong);

                if (foundTilde && segmentLength <= MaxShortName)
                    possibleShortPath = true;

                // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but
                // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide.
                if (expandShortPaths && possibleShortPath)
                {
                    return TryExpandShortFileName(fullPath, originalPath: path);
                }
                else
                {
                    if (fullPath.Length == (ulong)path.Length && fullPath.StartsWith(path))
                    {
                        // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder.
                        return path;
                    }
                    else
                    {
                        return fullPath.ToString();
                    }
                }
            }
            finally
            {
                // Clear the buffer
                fullPath.Free();
            }
        }
Beispiel #22
0
 public void CopyToBufferThrowsIndexingBeyondSourceBufferLength(string source, ulong index, ulong count)
 {
     using (var buffer = new StringBuffer(source))
     using (var targetBuffer = new StringBuffer())
     {
         Assert.Throws<ArgumentOutOfRangeException>(() => { buffer.CopyTo(index, targetBuffer, 0, count); });
     }
 }
Beispiel #23
0
 public void CopyFromStringThrowsIndexingBeyondBufferLength()
 {
     using (var buffer = new StringBuffer())
     {
         Assert.Throws<ArgumentOutOfRangeException>(() => { buffer.CopyFrom(1, ""); });
     }
 }
Beispiel #24
0
 public void CopyToBufferThrowsOnNull()
 {
     using (var buffer = new StringBuffer())
     {
         Assert.Throws<ArgumentNullException>(() => { buffer.CopyTo(0, null, 0, 0); });
     }
 }
Beispiel #25
0
        private static ulong GetInputBuffer(StringBuffer content, bool isUnc, out StringBuffer buffer)
        {
            ulong length = content.Length;
            length += isUnc ? (ulong)PathInternal.UncExtendedPrefixToInsert.Length : (ulong)PathInternal.ExtendedPathPrefix.Length;
            buffer = new StringBuffer(length);

            if (isUnc)
            {
                buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix);
                ulong prefixDifference = (ulong)(PathInternal.UncExtendedPathPrefix.Length - PathInternal.UncPathPrefix.Length);
                content.CopyTo(bufferIndex: prefixDifference, destination: buffer, destinationIndex: (ulong)PathInternal.ExtendedPathPrefix.Length, count: content.Length - prefixDifference);
                return prefixDifference;
            }
            else
            {
                ulong prefixSize = (ulong)PathInternal.ExtendedPathPrefix.Length;
                buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix);
                content.CopyTo(bufferIndex: 0, destination: buffer, destinationIndex: prefixSize, count: content.Length);
                return prefixSize;
            }
        }
Beispiel #26
0
        private unsafe static string TryExpandShortFileName(StringBuffer outputBuffer, 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 like crazy we'll create only one input array and modify the contents with embedded nulls.

            Contract.Assert(!PathInternal.IsPartiallyQualified(outputBuffer), "should have resolved by now");

            using (StringBuffer inputBuffer = new StringBuffer(outputBuffer))
            {
                bool success = false;
                uint lastIndex = outputBuffer.Length - 1;
                uint foundIndex = lastIndex;
                uint rootLength = PathInternal.GetRootLength(outputBuffer);

                while (!success)
                {
                    uint result = Win32Native.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity);

                    // Replace any temporary null we added
                    if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';

                    if (result == 0)
                    {
                        // Look to see if we couldn't find the file
                        int error = Marshal.GetLastWin32Error();
                        if (error != Win32Native.ERROR_FILE_NOT_FOUND && error != Win32Native.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 && inputBuffer[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
                            inputBuffer[foundIndex] = '\0';
                        }
                    }
                    else if (result > outputBuffer.CharCapacity)
                    {
                        // Not enough space. The result count for this API does not include the null terminator.
                        outputBuffer.EnsureCharCapacity(result);
                    }
                    else
                    {
                        // Found the path
                        success = true;
                        outputBuffer.Length = result;
                        if (foundIndex < lastIndex)
                        {
                            // It was a partial find, put the non-existant part of the path back
                            outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
                        }
                    }
                }

                StringBuffer bufferToUse = success ? outputBuffer : inputBuffer;

                if (bufferToUse.SubstringEquals(originalPath))
                {
                    // Use the original path to avoid allocating
                    return originalPath;
                }

                return bufferToUse.ToString();
            }
        }
Beispiel #27
0
        private static string TryExpandShortFileName(StringBuffer outputBuffer, 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 like crazy we'll create only one input array and modify the contents with embedded nulls.

            Debug.Assert(!PathInternal.IsRelative(outputBuffer), "should have resolved by now");
            Debug.Assert(!PathInternal.IsExtended(outputBuffer), "expanding short names expects normal paths");

            // Add the extended prefix before expanding to allow growth over MAX_PATH
            StringBuffer inputBuffer = null;
            ulong rootLength = PathInternal.GetRootLength(outputBuffer);
            bool isUnc = IsUnc(outputBuffer);

            ulong rootDifference = GetInputBuffer(outputBuffer, isUnc, out inputBuffer);
            rootLength += rootDifference;
            ulong inputLength = inputBuffer.Length;

            bool success = false;
            ulong foundIndex = inputBuffer.Length - 1;

            while (!success)
            {
                uint result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), (uint)outputBuffer.CharCapacity);

                // Replace any temporary null we added
                if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';

                if (result == 0)
                {
                    // Look to see if we couldn't find the file
                    int error = Marshal.GetLastWin32Error();
                    if (error != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && error != Interop.mincore.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 && inputBuffer[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
                        inputBuffer[foundIndex] = '\0';
                    }
                }
                else if (result > outputBuffer.CharCapacity)
                {
                    // Not enough space. The result count for this API does not include the null terminator.
                    outputBuffer.EnsureCharCapacity(result);
                    result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), (uint)outputBuffer.CharCapacity);
                }
                else
                {
                    // Found the path
                    success = true;
                    outputBuffer.Length = result;
                    if (foundIndex < inputLength - 1)
                    {
                        // It was a partial find, put the non-existant part of the path back
                        outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
                    }
                }
            }

            // Strip out the prefix and return the string
            StringBuffer bufferToUse = success ? outputBuffer : inputBuffer;
            string returnValue = null;

            int newLength = (int)(bufferToUse.Length - rootDifference);
            if (isUnc)
            {
                // Need to go from \\?\UNC\ to \\?\UN\\
                bufferToUse[(ulong)PathInternal.UncExtendedPathPrefix.Length - 1] = '\\';
            }

            if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
            {
                // Use the original path to avoid allocating
                returnValue = originalPath;
            }
            else
            {
                returnValue = bufferToUse.Substring(rootDifference, newLength);
            }

            inputBuffer.Dispose();
            return returnValue;
        }
Beispiel #28
0
 public void CopyFromStringThrowsIndexingBeyondStringLength(string value, int index, int count)
 {
     using (var buffer = new StringBuffer())
     {
         Assert.Throws<ArgumentOutOfRangeException>(() => { buffer.CopyFrom(0, value, index, count); });
     }
 }
Beispiel #29
0
        private static string NewGetCurrentDirectory()
        {
            using (StringBuffer buffer = new StringBuffer(PathInternal.MaxShortPath))
            {
                uint result = 0;
                while ((result = Win32Native.GetCurrentDirectoryW(buffer.CharCapacity, buffer.GetHandle())) > buffer.CharCapacity)
                {
                    // Reported size is greater than the buffer size. Increase the capacity.
                    // The size returned includes the null only if more space is needed (this case).
                    buffer.EnsureCharCapacity(result);
                }

                if (result == 0)
                    __Error.WinIOError();

                buffer.Length = result;

#if !PLATFORM_UNIX
                if (buffer.Contains('~'))
                    return LongPathHelper.GetLongPathName(buffer);
#endif

                return buffer.ToString();
            }
        }
Beispiel #30
0
 public void StartsWith(string source, string value, bool expected)
 {
     using (var buffer = new StringBuffer(source))
     {
         Assert.Equal(expected, buffer.StartsWith(value));
     }
 }