private static Version ParseVersion(ReadOnlySpan <char> input, bool throwOnFailure) { // Find the separator between major and minor. It must exist. int majorEnd = input.IndexOf('.'); if (majorEnd < 0) { if (throwOnFailure) { throw new ArgumentException(SR.Arg_VersionString, nameof(input)); } return(null); } // Find the ends of the optional minor and build portions. // We musn't have any separators after build. int buildEnd = -1; int minorEnd = input.Slice(majorEnd + 1).IndexOf('.'); if (minorEnd != -1) { minorEnd += (majorEnd + 1); buildEnd = input.Slice(minorEnd + 1).IndexOf('.'); if (buildEnd != -1) { buildEnd += (minorEnd + 1); if (input.Slice(buildEnd + 1).IndexOf('.') != -1) { if (throwOnFailure) { throw new ArgumentException(SR.Arg_VersionString, nameof(input)); } return(null); } } } int minor, build, revision; // Parse the major version if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out int major)) { return(null); } if (minorEnd != -1) { // If there's more than a major and minor, parse the minor, too. if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor)) { return(null); } if (buildEnd != -1) { // major.minor.build.revision return (TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) && TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ? new Version(major, minor, build, revision) : null); } else { // major.minor.build return(TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ? new Version(major, minor, build) : null); } } else { // major.minor return(TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ? new Version(major, minor) : null); } }
public static unsafe IDictionary GetEnvironmentVariables() { // Format for GetEnvironmentStrings is: // [=HiddenVar=value\0]* [Variable=value\0]* \0 // See the description of Environment Blocks in MSDN's CreateProcess // page (null-terminated array of null-terminated strings). Note // the =HiddenVar's aren't always at the beginning. // Copy strings out, parsing into pairs and inserting into the table. // The first few environment variable entries start with an '='. // The current working directory of every drive (except for those drives // you haven't cd'ed into in your DOS window) are stored in the // environment block (as =C:=pwd) and the program's exit code is // as well (=ExitCode=00000000). char *stringPtr = Interop.Kernel32.GetEnvironmentStringsW(); if (stringPtr == null) { throw new OutOfMemoryException(); } try { var results = new Hashtable(); char *currentPtr = stringPtr; while (true) { ReadOnlySpan <char> variable = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(currentPtr); if (variable.IsEmpty) { break; } // Find the = separating the key and value. We skip entries that begin with =. We also skip entries that don't // have =, which can happen on some older OSes when the environment block gets corrupted. int i = variable.IndexOf('='); if (i > 0) { // Add the key and value. string key = new string(variable.Slice(0, i)); string value = new string(variable.Slice(i + 1)); try { // Add may throw if the environment block was corrupted leading to duplicate entries. // We allow such throws and eat them (rather than proactively checking for duplication) // to provide a non-fatal notification about the corruption. results.Add(key, value); } catch (ArgumentException) { } } // Move to the end of this variable, after its terminator. currentPtr += variable.Length + 1; } return(results); } finally { Interop.BOOL success = Interop.Kernel32.FreeEnvironmentStringsW(stringPtr); Debug.Assert(success != Interop.BOOL.FALSE); } }
/// <summary> /// Gets the null-terminated string length of the given span. /// </summary> internal static int GetFixedBufferStringLength(this ReadOnlySpan <char> span) { int length = span.IndexOf('\0'); return(length < 0 ? span.Length : length); }