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); }
public void SetOverIndexThrowsArgumentOutOfRange() { using (var buffer = new StringBuffer()) { Assert.Throws<ArgumentOutOfRangeException>(() => { buffer[0] = 'Q'; }); } }
static void CustomFormat(StringBuffer buffer, Blah blah, StringView format) { if (format == "yes") buffer.Append("World!"); else buffer.Append("(Goodbye)"); }
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] == '\\'; }
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]) ); }
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()); } }
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])); }
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; } }
private static bool IsUnc(StringBuffer buffer) { return buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\'; }
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; }
private static bool IsDosUnc(StringBuffer buffer) { return !PathInternal.IsDevice(buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\'; }
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(); } }
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; } }
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()); } }
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(); } }
public void StartsWithNullThrows() { using (var buffer = new StringBuffer()) { Assert.Throws<ArgumentNullException>(() => buffer.StartsWith(null)); } }
/// <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(); } }
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); }); } }
public void CopyFromStringThrowsIndexingBeyondBufferLength() { using (var buffer = new StringBuffer()) { Assert.Throws<ArgumentOutOfRangeException>(() => { buffer.CopyFrom(1, ""); }); } }
public void CopyToBufferThrowsOnNull() { using (var buffer = new StringBuffer()) { Assert.Throws<ArgumentNullException>(() => { buffer.CopyTo(0, null, 0, 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; } }
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(); } }
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; }
public void CopyFromStringThrowsIndexingBeyondStringLength(string value, int index, int count) { using (var buffer = new StringBuffer()) { Assert.Throws<ArgumentOutOfRangeException>(() => { buffer.CopyFrom(0, value, index, count); }); } }
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(); } }
public void StartsWith(string source, string value, bool expected) { using (var buffer = new StringBuffer(source)) { Assert.Equal(expected, buffer.StartsWith(value)); } }