private static void GetFullPathName(ReadOnlySpan <char> path, ref ValueStringBuilder builder) { // 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.IsPartiallyQualified(path) || !PathInternal.IsExtended(path)); uint result; while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } if (result == 0) { // Failure, get the error and throw int errorCode = Marshal.GetLastWin32Error(); if (errorCode == 0) { errorCode = Interop.Errors.ERROR_BAD_PATHNAME; } throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString()); } builder.Length = (int)result; }
private unsafe string? GetServiceDisplayName(SafeServiceHandle? scmHandle, string serviceName) { var builder = new ValueStringBuilder(4096); int bufLen; while (true) { bufLen = builder.Capacity; fixed (char* c = builder) { if (Interop.Advapi32.GetServiceDisplayName(scmHandle, serviceName, c, ref bufLen)) break; } int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST) { return null; } else if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER) { throw new InvalidOperationException(SR.Format(SR.NoService, serviceName, _machineName), new Win32Exception(lastError)); } builder.EnsureCapacity(bufLen + 1); // Does not include null } builder.Length = bufLen; return builder.ToString(); }
// This is only used by RegistryKey on Windows. internal static string ExpandEnvironmentVariables(string name) { Debug.Assert(name != null); if (name.Length == 0) { return(name); } Span <char> initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; while ((length = Win32Native.ExpandEnvironmentStringsW(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } if (length == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // length includes the null terminator builder.Length = (int)length - 1; return(builder.ToString()); }
// Returns a unique temporary file name, and creates a 0-byte file by that // name on disk. public static string GetTempFileName() { Span <char> initialTempPathBuffer = stackalloc char[PathInternal.MaxShortPath]; ValueStringBuilder tempPathBuilder = new ValueStringBuilder(initialTempPathBuffer); GetTempPath(ref tempPathBuilder); Span <char> initialBuffer = stackalloc char[PathInternal.MaxShortPath]; var builder = new ValueStringBuilder(initialBuffer); uint result = 0; while ((result = Interop.Kernel32.GetTempFileNameW( ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference())) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } tempPathBuilder.Dispose(); if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = (int)result; string path = PathHelper.Normalize(ref builder); builder.Dispose(); return(path); }
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 unsafe string GetRaw() { string variable = Variable; if (variable == null) { throw new ArgumentNullException(nameof(variable)); } Span <char> stack = stackalloc char[128]; ValueStringBuilder buffer = new ValueStringBuilder(stack); uint returnValue; while ((returnValue = Raw.GetEnvironmentVariable(variable, buffer.RawChars)) > buffer.Capacity) { buffer.EnsureCapacity((int)returnValue); } if (returnValue == 0) { return(null); } buffer.Length = (int)returnValue; return(buffer.ToString()); }
public void EnsureCapacity_IfBufferTimesTwoWins() { Span <char> initialBuffer = stackalloc char[32]; var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(33); Assert.Equal(64, builder.Capacity); }
public static unsafe string Combine(params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } int capacity = 0; int num = 0; for (int index = 0; index < paths.Length; ++index) { if (paths[index] == null) { throw new ArgumentNullException(nameof(paths)); } if (paths[index].Length != 0) { if (Path.IsPathRooted(paths[index])) { num = index; capacity = paths[index].Length; } else { capacity += paths[index].Length; } if (!PathInternal.IsDirectorySeparator(paths[index][paths[index].Length - 1])) { ++capacity; } } } // ISSUE: untyped stack allocation ValueStringBuilder valueStringBuilder = new ValueStringBuilder(new Span <char>((void *)__untypedstackalloc(new IntPtr(520)), 260)); valueStringBuilder.EnsureCapacity(capacity); for (int index = num; index < paths.Length; ++index) { if (paths[index].Length != 0) { if (valueStringBuilder.Length == 0) { valueStringBuilder.Append(paths[index]); } else { if (!PathInternal.IsDirectorySeparator(valueStringBuilder[valueStringBuilder.Length - 1])) { valueStringBuilder.Append('\\'); } valueStringBuilder.Append(paths[index]); } } } return(valueStringBuilder.ToString()); }
public void EnsureCapacityTest() { Span <char> buffer = stackalloc char[4]; using ValueStringBuilder sb = new ValueStringBuilder(buffer); sb.EnsureCapacity(32); Assert.True(sb.Capacity >= 32); Assert.AreEqual(0, sb.Length); }
public void EnsureCapacity_IfRequestedCapacityWins() { // Note: constants used here may be dependent on minimal buffer size // the ArrayPool is able to return. Span <char> initialBuffer = stackalloc char[32]; var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(65); Assert.Equal(128, builder.Capacity); }
public void EnsureCapacity_NoAllocIfNotNeeded() { // Note: constants used here may be dependent on minimal buffer size // the ArrayPool is able to return. Span <char> initialBuffer = stackalloc char[64]; var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(16); Assert.Equal(64, builder.Capacity); }
public static string Join(params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } if (paths.Length == 0) { return(string.Empty); } int maxSize = 0; foreach (string path in paths) { maxSize += path?.Length ?? 0; } maxSize += paths.Length - 1; Span <char> initialBuffer = stackalloc char[260]; // MaxShortPath on Windows var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(maxSize); for (int i = 0; i < paths.Length; i++) { if ((paths[i]?.Length ?? 0) == 0) { continue; } string path = paths[i]; if (builder.Length == 0) { builder.Append(path); } else { if (!PathInternal.IsDirectorySeparator(builder[builder.Length - 1]) && !PathInternal.IsDirectorySeparator(path[0])) { builder.Append(PathInternal.DirectorySeparatorChar); } builder.Append(path); } } return(builder.ToString()); }
private static void GetTempPath(ref ValueStringBuilder builder) { uint result; while ((result = Interop.Kernel32.GetTempPathW(builder.Capacity, ref builder.GetPinnableReference())) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = (int)result; }
private static void GetUserName(ref ValueStringBuilder builder) { uint size = 0; while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE) { if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA) { builder.EnsureCapacity(checked ((int)size)); } else { builder.Length = 0; return; } } builder.Length = (int)size; }
public static unsafe string Join([Nullable(new byte[] { 1, 2 })] params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } if (paths.Length == 0) { return(string.Empty); } int num = 0; foreach (string path in paths) { num += path != null ? path.Length : 0; } int capacity = num + (paths.Length - 1); // ISSUE: untyped stack allocation ValueStringBuilder valueStringBuilder = new ValueStringBuilder(new Span <char>((void *)__untypedstackalloc(new IntPtr(520)), 260)); valueStringBuilder.EnsureCapacity(capacity); for (int index = 0; index < paths.Length; ++index) { string path = paths[index]; if (path != null && path.Length != 0) { if (valueStringBuilder.Length == 0) { valueStringBuilder.Append(path); } else { if (!PathInternal.IsDirectorySeparator(valueStringBuilder[valueStringBuilder.Length - 1]) && !PathInternal.IsDirectorySeparator(path[0])) { valueStringBuilder.Append('\\'); } valueStringBuilder.Append(path); } } } return(valueStringBuilder.ToString()); }
public unsafe string GetRawInline() { string variable = Variable; if (variable == null) { throw new ArgumentNullException(nameof(variable)); } Span <char> stack = stackalloc char[128]; ValueStringBuilder buffer = new ValueStringBuilder(stack); uint returnValue; fixed(char *v = variable) { while (true) { fixed(char *b = buffer) { if ((returnValue = Raw.GetEnvironmentVariableW(v, b, (uint)buffer.Capacity)) <= buffer.Capacity) { break; } else { buffer.EnsureCapacity((int)returnValue); } } } ; } if (returnValue == 0) { return(null); } buffer.Length = (int)returnValue; return(buffer.ToString()); }
private static string ExpandEnvironmentVariablesCore(string name) { Span <char> initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; while ((length = Interop.Kernel32.ExpandEnvironmentStrings(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } if (length == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // length includes the null terminator builder.Length = (int)length - 1; return(builder.ToString()); }
public static string Combine(params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } int maxSize = 0; int firstComponent = 0; // We have two passes, the first calculates how large a buffer to allocate and does some precondition // checks on the paths passed in. The second actually does the combination. for (int i = 0; i < paths.Length; i++) { if (paths[i] == null) { throw new ArgumentNullException(nameof(paths)); } if (paths[i].Length == 0) { continue; } if (IsPathRooted(paths[i])) { firstComponent = i; maxSize = paths[i].Length; } else { maxSize += paths[i].Length; } char ch = paths[i][paths[i].Length - 1]; if (!PathInternal.IsDirectorySeparator(ch)) { maxSize++; } } Span <char> initialBuffer = stackalloc char[260]; // MaxShortPath on Windows var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(maxSize); for (int i = firstComponent; i < paths.Length; i++) { if (paths[i].Length == 0) { continue; } if (builder.Length == 0) { builder.Append(paths[i]); } else { char ch = builder[builder.Length - 1]; if (!PathInternal.IsDirectorySeparator(ch)) { builder.Append(PathInternal.DirectorySeparatorChar); } builder.Append(paths[i]); } } return(builder.ToString()); }
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);