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).Contains('.')) { 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); } }
/// <summary> /// Determines whether the beginning of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option. /// </summary> /// <param name="span">The source span.</param> /// <param name="value">The sequence to compare to the beginning of the source span.</param> /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param> public static bool StartsWith(this ReadOnlySpan <char> span, ReadOnlySpan <char> value, StringComparison comparisonType) { string.CheckStringComparison(comparisonType); if (value.Length == 0) { return(true); } if (comparisonType >= StringComparison.Ordinal || GlobalizationMode.Invariant) { if (string.GetCaseCompareOfComparisonCulture(comparisonType) == CompareOptions.None) { return(span.StartsWith(value)); } return((span.Length >= value.Length) ? (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(0, value.Length), value) == 0) : false); } if (span.Length == 0) { return(false); } return((comparisonType >= StringComparison.InvariantCulture) ? CompareInfo.Invariant.IsPrefix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType)) : CultureInfo.CurrentCulture.CompareInfo.IsPrefix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType))); }
// // Parse // // Convert this IPv6 address into a sequence of 8 16-bit numbers // // Inputs: // <member> Name // The validated IPv6 address // // Outputs: // <member> numbers // Array filled in with the numbers in the IPv6 groups // // <member> PrefixLength // Set to the number after the prefix separator (/) if found // // Assumes: // <Name> has been validated and contains only hex digits in groups of // 16-bit numbers, the characters ':' and '/', and a possible IPv4 // address // // Throws: // Nothing // internal static void Parse(ReadOnlySpan <char> address, Span <ushort> numbers, int start, ref string?scopeId) { int number = 0; int index = 0; int compressorIndex = -1; bool numberIsValid = true; //This used to be a class instance member but have not been used so far int PrefixLength = 0; if (address[start] == '[') { ++start; } for (int i = start; i < address.Length && address[i] != ']';) { switch (address[i]) { case '%': if (numberIsValid) { numbers[index++] = (ushort)number; numberIsValid = false; } start = i; for (++i; i < address.Length && address[i] != ']' && address[i] != '/'; ++i) { } scopeId = new string(address.Slice(start, i - start)); // ignore prefix if any for (; i < address.Length && address[i] != ']'; ++i) { } break; case ':': numbers[index++] = (ushort)number; number = 0; ++i; if (address[i] == ':') { compressorIndex = index; ++i; } else if ((compressorIndex < 0) && (index < 6)) { // no point checking for IPv4 address if we don't // have a compressor or we haven't seen 6 16-bit // numbers yet break; } // check to see if the upcoming number is really an IPv4 // address. If it is, convert it to 2 ushort numbers for (int j = i; j < address.Length && (address[j] != ']') && (address[j] != ':') && (address[j] != '%') && (address[j] != '/') && (j < i + 4); ++j) { if (address[j] == '.') { // we have an IPv4 address. Find the end of it: // we know that since we have a valid IPv6 // address, the only things that will terminate // the IPv4 address are the prefix delimiter '/' // or the end-of-string (which we conveniently // delimited with ']') while (j < address.Length && (address[j] != ']') && (address[j] != '/') && (address[j] != '%')) { ++j; } number = IPv4AddressHelper.ParseHostNumber(address, i, j); numbers[index++] = (ushort)(number >> 16); numbers[index++] = (ushort)number; i = j; // set this to avoid adding another number to // the array if there's a prefix number = 0; numberIsValid = false; break; } } break; case '/': if (numberIsValid) { numbers[index++] = (ushort)number; numberIsValid = false; } // since we have a valid IPv6 address string, the prefix // length is the last token in the string for (++i; address[i] != ']'; ++i) { PrefixLength = PrefixLength * 10 + (address[i] - '0'); } break; default: number = number * 16 + Uri.FromHex(address[i++]); break; } } // add number to the array if its not the prefix length or part of // an IPv4 address that's already been handled if (numberIsValid) { numbers[index++] = (ushort)number; } // if we had a compressor sequence ("::") then we need to expand the // numbers array if (compressorIndex > 0) { int toIndex = NumberOfLabels - 1; int fromIndex = index - 1; // if fromIndex and toIndex are the same, it means that "zero bits" are already in the correct place // it happens for leading and trailing compression if (fromIndex != toIndex) { for (int i = index - compressorIndex; i > 0; --i) { numbers[toIndex--] = numbers[fromIndex]; numbers[fromIndex--] = 0; } } } }
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> /// Removes all trailing occurrences of a specified element from the span. /// </summary> /// <param name="span">The source span from which the element is removed.</param> /// <param name="trimElement">The specified element to look for and remove.</param> public static ReadOnlySpan <T> TrimEnd <T>(this ReadOnlySpan <T> span, T trimElement) where T : IEquatable <T> => span.Slice(0, ClampEnd(span, 0, trimElement));
/// <summary> /// Removes all leading occurrences of a specified element from the span. /// </summary> /// <param name="span">The source span from which the element is removed.</param> /// <param name="trimElement">The specified element to look for and remove.</param> public static ReadOnlySpan <T> TrimStart <T>(this ReadOnlySpan <T> span, T trimElement) where T : IEquatable <T> => span.Slice(ClampStart(span, trimElement));