So that we can reuse the same implementation for the public span-based APIs.
//
public static DateTime Parse(String s)
{
- return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return (DateTimeParse.Parse(s.AsReadOnlySpan(), DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None));
}
// Constructs a DateTime from a string. The string must specify a
//
public static DateTime Parse(String s, IFormatProvider provider)
{
- return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return (DateTimeParse.Parse(s.AsReadOnlySpan(), DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
}
public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles)
{
DateTimeFormatInfo.ValidateStyles(styles, nameof(styles));
- return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return (DateTimeParse.Parse(s.AsReadOnlySpan(), DateTimeFormatInfo.GetInstance(provider), styles));
}
// Constructs a DateTime from a string. The string must specify a
//
public static DateTime ParseExact(String s, String format, IFormatProvider provider)
{
- return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return (DateTimeParse.ParseExact(s.AsReadOnlySpan(), format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
}
// Constructs a DateTime from a string. The string must specify a
public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
- return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return (DateTimeParse.ParseExact(s.AsReadOnlySpan(), format, DateTimeFormatInfo.GetInstance(provider), style));
}
public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
- return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return DateTimeParse.ParseExactMultiple(s.AsReadOnlySpan(), formats, DateTimeFormatInfo.GetInstance(provider), style);
}
public TimeSpan Subtract(DateTime value)
public static Boolean TryParse(String s, out DateTime result)
{
- return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result);
+ if (s == null)
+ {
+ result = default(DateTime);
+ return false;
+ }
+ return DateTimeParse.TryParse(s.AsReadOnlySpan(), DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result);
}
public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(styles, nameof(styles));
- return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result);
+
+ if (s == null)
+ {
+ result = default(DateTime);
+ return false;
+ }
+
+ return DateTimeParse.TryParse(s.AsReadOnlySpan(), DateTimeFormatInfo.GetInstance(provider), styles, out result);
}
public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
- return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result);
+
+ if (s == null)
+ {
+ result = default(DateTime);
+ return false;
+ }
+
+ return DateTimeParse.TryParseExact(s.AsReadOnlySpan(), format, DateTimeFormatInfo.GetInstance(provider), style, out result);
}
public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
- return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
+
+ if (s == null)
+ {
+ result = default(DateTime);
+ return false;
+ }
+
+ return DateTimeParse.TryParseExactMultiple(s.AsReadOnlySpan(), formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
}
public static DateTime operator +(DateTime d, TimeSpan t)
//
public static DateTimeOffset Parse(String input)
{
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
+
TimeSpan offset;
- DateTime dateResult = DateTimeParse.Parse(input,
+ DateTime dateResult = DateTimeParse.Parse(input.AsReadOnlySpan(),
DateTimeFormatInfo.CurrentInfo,
DateTimeStyles.None,
out offset);
//
public static DateTimeOffset Parse(String input, IFormatProvider formatProvider)
{
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
return Parse(input, formatProvider, DateTimeStyles.None);
}
public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
+
TimeSpan offset;
- DateTime dateResult = DateTimeParse.Parse(input,
+ DateTime dateResult = DateTimeParse.Parse(input.AsReadOnlySpan(),
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
out offset);
//
public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider)
{
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
return ParseExact(input, format, formatProvider, DateTimeStyles.None);
}
public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
+
TimeSpan offset;
- DateTime dateResult = DateTimeParse.ParseExact(input,
+ DateTime dateResult = DateTimeParse.ParseExact(input.AsReadOnlySpan(),
format,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.index); // TODO: index => input
+
TimeSpan offset;
- DateTime dateResult = DateTimeParse.ParseExactMultiple(input,
+ DateTime dateResult = DateTimeParse.ParseExactMultiple(input.AsReadOnlySpan(),
formats,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
{
TimeSpan offset;
DateTime dateResult;
- Boolean parsed = DateTimeParse.TryParse(input,
+ Boolean parsed = DateTimeParse.TryParse(input.AsReadOnlySpan(),
DateTimeFormatInfo.CurrentInfo,
DateTimeStyles.None,
out dateResult,
public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null)
+ {
+ result = default(DateTimeOffset);
+ return false;
+ }
+
TimeSpan offset;
DateTime dateResult;
- Boolean parsed = DateTimeParse.TryParse(input,
+ Boolean parsed = DateTimeParse.TryParse(input.AsReadOnlySpan(),
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
out dateResult,
out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null)
+ {
+ result = default(DateTimeOffset);
+ return false;
+ }
+
TimeSpan offset;
DateTime dateResult;
- Boolean parsed = DateTimeParse.TryParseExact(input,
+ Boolean parsed = DateTimeParse.TryParseExact(input.AsReadOnlySpan(),
format,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
+ if (input == null)
+ {
+ result = default(DateTimeOffset);
+ return false;
+ }
+
TimeSpan offset;
DateTime dateResult;
- Boolean parsed = DateTimeParse.TryParseExactMultiple(input,
+ Boolean parsed = DateTimeParse.TryParseExactMultiple(input.AsReadOnlySpan(),
formats,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
int hashcode = ch % TOKEN_HASH_SIZE;
int hashProbe = 1 + ch % SECOND_PRIME;
- int remaining = str.len - str.Index;
+ int remaining = str.Length - str.Index;
int i = 0;
TokenHashValue[] hashTable = _dtfiTokenHash;
// If this token starts with a letter, make sure that we won't allow partial match. So you can't tokenize "MarchWed" separately.
// Also an optimization to avoid string comparison
int nextCharIndex = str.Index + value.tokenString.Length;
- if (nextCharIndex > str.len)
+ if (nextCharIndex > str.Length)
{
compareStrings = false;
}
- else if (nextCharIndex < str.len)
+ else if (nextCharIndex < str.Length)
{
// Check word boundary. The next character should NOT be a letter.
char nextCh = str.Value[nextCharIndex];
compareStrings = !(Char.IsLetter(nextCh));
}
}
- if (compareStrings && CompareStringIgnoreCaseOptimized(str.Value, str.Index, value.tokenString.Length, value.tokenString, 0, value.tokenString.Length))
+ if (compareStrings && CompareStringIgnoreCaseOptimized(str.Value.Slice(str.Index, value.tokenString.Length), value.tokenString.AsReadOnlySpan()))
{
tokenType = value.tokenType & TokenMask;
tokenValue = value.tokenValue;
return (this.Culture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.IgnoreCase) == 0);
}
+ private bool CompareStringIgnoreCaseOptimized(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2)
+ {
+ // Optimize for one character cases which are common due to date and time separators (/ and :)
+ if (string1.Length == 1 && string2.Length == 1 && string1[0] == string2[0])
+ {
+ return true;
+ }
+
+ return (this.Culture.CompareInfo.Compare(string1, string2, CompareOptions.IgnoreCase) == 0);
+ }
+
// class DateTimeFormatInfo
internal class TokenHashValue
using System.Diagnostics;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Text;
namespace System
internal static MatchNumberDelegate m_hebrewNumberParser = new MatchNumberDelegate(DateTimeParse.MatchHebrewDigits);
- internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
+ internal static DateTime ParseExact(ReadOnlySpan<char> s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
result.Init();
}
}
- internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
+ internal static DateTime ParseExact(ReadOnlySpan<char> s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
offset = TimeSpan.Zero;
}
}
- internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
+ internal static bool TryParseExact(ReadOnlySpan<char> s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
{
result = DateTime.MinValue;
DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
return false;
}
- internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
+ internal static bool TryParseExact(ReadOnlySpan<char> s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
{
result = DateTime.MinValue;
offset = TimeSpan.Zero;
return false;
}
- internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
+ internal static bool TryParseExact(ReadOnlySpan<char> s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
{
- if (s == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
- return false;
- }
if (format == null)
{
result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(format));
return DoStrictParse(s, format, style, dtfi, ref result);
}
- internal static DateTime ParseExactMultiple(String s, String[] formats,
+ internal static DateTime ParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
DateTimeFormatInfo dtfi, DateTimeStyles style)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
}
- internal static DateTime ParseExactMultiple(String s, String[] formats,
+ internal static DateTime ParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
}
}
- internal static bool TryParseExactMultiple(String s, String[] formats,
+ internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
{
result = DateTime.MinValue;
}
- internal static bool TryParseExactMultiple(String s, String[] formats,
+ internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
{
result = DateTime.MinValue;
return false;
}
- internal static bool TryParseExactMultiple(String s, String[] formats,
+ internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
{
- if (s == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
- return false;
- }
if (formats == null)
{
result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(formats));
//
private static bool MatchWord(ref __DTString str, String target)
{
- int length = target.Length;
- if (length > (str.Value.Length - str.Index))
+ if (target.Length > (str.Value.Length - str.Index))
{
return false;
}
- if (str.CompareInfo.Compare(str.Value, str.Index, length,
- target, 0, length, CompareOptions.IgnoreCase) != 0)
+ if (str.CompareInfo.Compare(str.Value.Slice(str.Index, target.Length), target.AsReadOnlySpan(), CompareOptions.IgnoreCase) != 0)
{
return (false);
}
}
}
str.Index = nextCharIndex;
- if (str.Index < str.len)
+ if (str.Index < str.Length)
{
str.m_current = str.Value[str.Index];
}
// This is the helper function to handle timezone in string in the format like +/-0800
private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result)
{
- if ((str.Index < str.len - 1))
+ if ((str.Index < str.Length - 1))
{
char nextCh = str.Value[str.Index];
// Skip whitespace, but don't update the index unless we find a time zone marker
int whitespaceCount = 0;
- while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.len - 1)
+ while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.Length - 1)
{
whitespaceCount++;
nextCh = str.Value[str.Index + whitespaceCount];
// "11:22:33.1234" or "11:22:33-08".
if (dps == DS.T_NNt)
{
- if ((str.Index < str.len - 1))
+ if ((str.Index < str.Length - 1))
{
char nextCh = str.Value[str.Index];
if (nextCh == '.')
}
if (dps == DS.T_NNt || dps == DS.T_Nt)
{
- if ((str.Index < str.len - 1))
+ if ((str.Index < str.Length - 1))
{
if (false == HandleTimeZone(ref str, ref result))
{
{
bool foundStart = false;
bool foundEnd = false;
- for (int i = 0; i < str.len; i++)
+ for (int i = 0; i < str.Length; i++)
{
ch = str.Value[i];
if (ch == '#')
}
else if (ch == '\0')
{
- for (int i = str.Index; i < str.len; i++)
+ for (int i = str.Index; i < str.Length; i++)
{
if (str.Value[i] != '\0')
{
}
}
// Move to the end of the string
- str.Index = str.len;
+ str.Index = str.Length;
return true;
}
return false;
return true;
}
- internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
+ internal static DateTime Parse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
result.Init();
}
}
- internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset)
+ internal static DateTime Parse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset)
{
DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
result.Init();
}
- internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result)
+ internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result)
{
result = DateTime.MinValue;
DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
return false;
}
- internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset)
+ internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset)
{
result = DateTime.MinValue;
offset = TimeSpan.Zero;
//
// This is the real method to do the parsing work.
//
- internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result)
+ internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result)
{
- if (s == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
- return false;
- }
if (s.Length == 0)
{
result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
// The pos should point to a quote character. This method will
// get the string enclosed by the quote character.
//
- internal static bool TryParseQuoteString(String format, int pos, StringBuilder result, out int returnValue)
+ internal static bool TryParseQuoteString(ReadOnlySpan<char> format, int pos, StringBuilder result, out int returnValue)
{
//
// NOTE : pos will be the index of the quote character in the 'format' string.
==============================================================================*/
private static bool DoStrictParse(
- String s,
+ ReadOnlySpan<char> s,
String formatParam,
DateTimeStyles styles,
DateTimeFormatInfo dtfi,
// if we have parsed every item twice.
result.Hour = result.Minute = result.Second = -1;
- __DTString format = new __DTString(formatParam, dtfi, false);
+ __DTString format = new __DTString(formatParam.AsReadOnlySpan(), dtfi, false);
__DTString str = new __DTString(s, dtfi, false);
if (parseInfo.fAllowTrailingWhite)
return buffer.ToString();
}
// return a string in the form: "Sun"
- internal static string Hex(string str)
+ internal static string Hex(string str) => Hex(str.AsReadOnlySpan());
+ internal static string Hex(ReadOnlySpan<char> str)
{
StringBuilder buffer = new StringBuilder();
buffer.Append("\"");
// It has a Index property which tracks
// the current parsing pointer of the string.
//
- internal
- struct __DTString
+ [IsByRefLike]
+ internal struct __DTString
{
//
// Value propery: stores the real string to be parsed.
//
- internal String Value;
+ internal ReadOnlySpan<char> Value;
//
// Index property: points to the character that we are currently parsing.
internal int Index;
// The length of Value string.
- internal int len;
+ internal int Length => Value.Length;
// The current chracter to be looked at.
internal char m_current;
// In some cultures, such as mn-MN, it uses "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" in month names.
private bool m_checkDigitToken;
- internal __DTString(String str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi)
+ internal __DTString(ReadOnlySpan<char> str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi)
{
m_checkDigitToken = checkDigitToken;
}
- internal __DTString(String str, DateTimeFormatInfo dtfi)
+ internal __DTString(ReadOnlySpan<char> str, DateTimeFormatInfo dtfi)
{
Index = -1;
Value = str;
- len = Value.Length;
m_current = '\0';
if (dtfi != null)
internal bool GetNext()
{
Index++;
- if (Index < len)
+ if (Index < Length)
{
m_current = Value[Index];
return (true);
internal bool AtEnd()
{
- return Index < len ? false : true;
+ return Index < Length ? false : true;
}
internal bool Advance(int count)
{
- Debug.Assert(Index + count <= len, "__DTString::Advance: Index + count <= len");
+ Debug.Assert(Index + count <= Length, "__DTString::Advance: Index + count <= len");
Index += count;
- if (Index < len)
+ if (Index < Length)
{
m_current = Value[Index];
return (true);
internal void GetRegularToken(out TokenType tokenType, out int tokenValue, DateTimeFormatInfo dtfi)
{
tokenValue = 0;
- if (Index >= len)
+ if (Index >= Length)
{
tokenType = TokenType.EndOfString;
return;
//
// Collect other digits.
//
- while (++Index < len)
+ while (++Index < Length)
{
m_current = Value[Index];
value = m_current - '0';
else if (Char.IsWhiteSpace(m_current))
{
// Just skip to the next character.
- while (++Index < len)
+ while (++Index < Length)
{
m_current = Value[Index];
if (!(Char.IsWhiteSpace(m_current)))
return false;
}
- if (Index + count > len)
+ if (Index + count > Length)
{
return false;
}
- return (m_info.Compare(Value, Index, count, target, 0, count, CompareOptions.IgnoreCase) == 0);
+ return (m_info.Compare(Value.Slice(Index, count), target.AsReadOnlySpan().Slice(0, count), CompareOptions.IgnoreCase) == 0);
}
private static Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' };
int valueRemaining = Value.Length - Index;
matchLength = target.Length;
- if (matchLength > valueRemaining || m_info.Compare(Value, Index, matchLength, target, 0, matchLength, CompareOptions.IgnoreCase) != 0)
+ if (matchLength > valueRemaining || m_info.Compare(Value.Slice(Index, matchLength), target.AsReadOnlySpan(), CompareOptions.IgnoreCase) != 0)
{
// Check word by word
int targetPosition = 0; // Where we are in the target string
{
return false;
}
- if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+ if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsReadOnlySpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
{
return false;
}
{
return false;
}
- if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+ if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsReadOnlySpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0)
{
return false;
}
//
internal bool Match(String str)
{
- if (++Index >= len)
+ if (++Index >= Length)
{
return (false);
}
return false;
}
- if (m_info.Compare(Value, Index, str.Length, str, 0, str.Length, CompareOptions.Ordinal) == 0)
+ if (m_info.Compare(Value.Slice(Index, str.Length), str.AsReadOnlySpan(), CompareOptions.Ordinal) == 0)
{
// Update the Index to the end of the matching string.
// So the following GetNext()/Match() opeartion will get
internal bool Match(char ch)
{
- if (++Index >= len)
+ if (++Index >= Length)
{
return (false);
}
{
char repeatChar = Value[Index];
int pos = Index + 1;
- while ((pos < len) && (Value[pos] == repeatChar))
+ while ((pos < Length) && (Value[pos] == repeatChar))
{
pos++;
}
// Return false when end of string is encountered or a non-digit character is found.
internal bool GetNextDigit()
{
- if (++Index >= len)
+ if (++Index >= Length)
{
return (false);
}
//
internal char GetChar()
{
- Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+ Debug.Assert(Index >= 0 && Index < Length, "Index >= 0 && Index < len");
return (Value[Index]);
}
//
internal int GetDigit()
{
- Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+ Debug.Assert(Index >= 0 && Index < Length, "Index >= 0 && Index < len");
Debug.Assert(DateTimeParse.IsDigit(Value[Index]), "IsDigit(Value[Index])");
return (Value[Index] - '0');
}
{
// Look ahead to see if the next character
// is a whitespace.
- while (Index + 1 < len)
+ while (Index + 1 < Length)
{
char ch = Value[Index + 1];
if (!Char.IsWhiteSpace(ch))
//
internal bool SkipWhiteSpaceCurrent()
{
- if (Index >= len)
+ if (Index >= Length)
{
return (false);
}
return (true);
}
- while (++Index < len)
+ while (++Index < Length)
{
m_current = Value[Index];
if (!Char.IsWhiteSpace(m_current))
internal void TrimTail()
{
- int i = len - 1;
+ int i = Length - 1;
while (i >= 0 && Char.IsWhiteSpace(Value[i]))
{
i--;
}
- Value = Value.Substring(0, i + 1);
- len = Value.Length;
+ Value = Value.Slice(0, i + 1);
}
// Trim the trailing spaces within a quoted string.
// Call this after TrimTail() is done.
internal void RemoveTrailingInQuoteSpaces()
{
- int i = len - 1;
+ int i = Length - 1;
if (i <= 1)
{
return;
i--;
}
Value = Value.Remove(i, Value.Length - 1 - i);
- len = Value.Length;
}
}
}
// Call this after the leading spaces before quoted string are trimmed.
internal void RemoveLeadingInQuoteSpaces()
{
- if (len <= 2)
+ if (Length <= 2)
{
return;
}
// Check if the last character is a quote.
if (ch == '\'' || ch == '\"')
{
- while ((i + 1) < len && Char.IsWhiteSpace(Value[i + 1]))
+ while ((i + 1) < Length && Char.IsWhiteSpace(Value[i + 1]))
{
i++;
}
if (i != 0)
{
Value = Value.Remove(1, i);
- len = Value.Length;
}
}
}
DTSubString sub = new DTSubString();
sub.index = Index;
sub.s = Value;
- while (Index + sub.length < len)
+ while (Index + sub.length < Length)
{
DTSubStringType currentType;
Char ch = Value[Index + sub.length];
internal void ConsumeSubString(DTSubString sub)
{
Debug.Assert(sub.index == Index, "sub.index == Index");
- Debug.Assert(sub.index + sub.length <= len, "sub.index + sub.length <= len");
+ Debug.Assert(sub.index + sub.length <= Length, "sub.index + sub.length <= len");
Index = sub.index + sub.length;
- if (Index < len)
+ if (Index < Length)
{
m_current = Value[Index];
}
Other = 4,
}
+ [IsByRefLike]
internal struct DTSubString
{
- internal String s;
+ internal ReadOnlySpan<char> s;
internal Int32 index;
internal Int32 length;
internal DTSubStringType type;
case '\'':
case '\"':
StringBuilder enquotedString = new StringBuilder();
- if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen))
+ if (!DateTimeParse.TryParseQuoteString(format.AsReadOnlySpan(), i, enquotedString, out tokenLen))
{
return result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch);
}
return -1;
}
+
+ public static ReadOnlySpan<char> Remove(this ReadOnlySpan<char> source, int startIndex, int count)
+ {
+ if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
+ if (count > source.Length - startIndex) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount);
+
+ if (count == 0)
+ {
+ return source;
+ }
+
+ int newLength = source.Length - count;
+ if (newLength == 0)
+ {
+ return ReadOnlySpan<char>.Empty;
+ }
+
+ Span<char> result = new char[newLength];
+ source.Slice(0, startIndex).CopyTo(result);
+ source.Slice(startIndex + count).CopyTo(result.Slice(startIndex));
+ return result;
+ }
}
}
return Interop.GlobalizationInterop.CompareStringOrdinalIgnoreCase(string1, count1, string2, count2);
}
- private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options)
+ private unsafe int CompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
{
Debug.Assert(!_invariantMode);
Debug.Assert(string2 != null);
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
- fixed (char* pString1 = string1)
+ fixed (char* pString1 = &string1.DangerousGetPinnableReference())
+ fixed (char* pString2 = &string2.DangerousGetPinnableReference())
{
- fixed (char* pString2 = string2)
- {
- return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1 + offset1, length1, pString2 + offset2, length2, options);
- }
+ return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options);
}
}
return Interop.Kernel32.CompareStringOrdinal(string1, count1, string2, count2, true) - 2;
}
- private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options)
+ private unsafe int CompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
{
Debug.Assert(!_invariantMode);
-
- Debug.Assert(string1 != null);
- Debug.Assert(string2 != null);
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
string localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
fixed (char* pLocaleName = localeName)
- fixed (char* pString1 = string1)
- fixed (char* pString2 = string2)
+ fixed (char* pString1 = &string1.DangerousGetPinnableReference())
+ fixed (char* pString2 = &string2.DangerousGetPinnableReference())
{
int result = Interop.Kernel32.CompareStringEx(
pLocaleName,
(uint)GetNativeCompareFlags(options),
- pString1 + offset1,
- length1,
- pString2 + offset2,
- length2,
+ pString1,
+ string1.Length,
+ pString2,
+ string2.Length,
null,
null,
_sortHandle);
return String.CompareOrdinal(string1, string2);
}
- return CompareString(string1, 0, string1.Length, string2, 0, string2.Length, options);
+ return CompareString(string1.AsReadOnlySpan(), string2.AsReadOnlySpan(), options);
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly?
+ internal unsafe virtual int Compare(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
+ {
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
+ return CompareOrdinalIgnoreCase(string1, string2);
+ }
+
+ // Verify the options before we do any real comparison.
+ if ((options & CompareOptions.Ordinal) != 0)
+ {
+ if (options != CompareOptions.Ordinal)
+ {
+ throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options));
+ }
+
+ return string.CompareOrdinal(string1, string2);
+ }
+
+ if ((options & ValidCompareMaskOffFlags) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
+ }
+
+ if (_invariantMode)
+ {
+ return (options & CompareOptions.IgnoreCase) != 0 ?
+ CompareOrdinalIgnoreCase(string1, string2) :
+ string.CompareOrdinal(string1, string2);
+ }
+
+ return CompareString(string1, string2, options);
}
return CompareOrdinal(string1, offset1, length1, string2, offset2, length2);
}
- return CompareString(string1, offset1, length1,
- string2, offset2, length2,
- options);
+ return CompareString(
+ string1.AsReadOnlySpan().Slice(offset1, length1),
+ string2.AsReadOnlySpan().Slice(offset2, length2),
+ options);
}
private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2)
{
Debug.Assert(indexA + lengthA <= strA.Length);
Debug.Assert(indexB + lengthB <= strB.Length);
+ return CompareOrdinalIgnoreCase(strA.AsReadOnlySpan().Slice(indexA, lengthA), strB.AsReadOnlySpan().Slice(indexB, lengthB));
+ }
- int length = Math.Min(lengthA, lengthB);
+ internal static unsafe int CompareOrdinalIgnoreCase(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
+ {
+ int length = Math.Min(strA.Length, strB.Length);
int range = length;
- fixed (char* ap = strA) fixed (char* bp = strB)
+ fixed (char* ap = &strA.DangerousGetPinnableReference())
+ fixed (char* bp = &strB.DangerousGetPinnableReference())
{
- char* a = ap + indexA;
- char* b = bp + indexB;
+ char* a = ap;
+ char* b = bp;
// in InvariantMode we support all range and not only the ascii characters.
char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80);
}
// uppercase both chars - notice that we need just one compare per char
- if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
- if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
+ if ((uint)(charA - 'a') <= 'z' - 'a') charA -= 0x20;
+ if ((uint)(charB - 'a') <= 'z' - 'a') charB -= 0x20;
// Return the (case-insensitive) difference between them.
if (charA != charB)
}
if (length == 0)
- return lengthA - lengthB;
+ return strA.Length - strB.Length;
Debug.Assert(!GlobalizationMode.Invariant);
range -= length;
- return CompareStringOrdinalIgnoreCase(a, lengthA - range, b, lengthB - range);
+ return CompareStringOrdinalIgnoreCase(a, strA.Length - range, b, strB.Length - range);
}
}
}
}
- private unsafe static int CompareOrdinalHelper(String strA, String strB)
+ private unsafe static int CompareOrdinalHelper(string strA, string strB)
{
- Debug.Assert(strA != null);
- Debug.Assert(strB != null);
-
- // NOTE: This may be subject to change if eliminating the check
- // in the callers makes them small enough to be inlined by the JIT
- Debug.Assert(strA._firstChar == strB._firstChar,
- "For performance reasons, callers of this method should " +
- "check/short-circuit beforehand if the first char is the same.");
-
int length = Math.Min(strA.Length, strB.Length);
- fixed (char* ap = &strA._firstChar) fixed (char* bp = &strB._firstChar)
+ fixed (char* ap = strA)
+ fixed (char* bp = strB)
{
char* a = ap;
char* b = bp;
return CompareOrdinalHelper(strA, strB);
}
+ // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly?
+ internal static int CompareOrdinal(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
+ {
+ // TODO: This needs to be optimized / unrolled. It can't just use CompareOrdinalHelper(str, str)
+ // (changed to accept spans) because its implementation is based on a string layout,
+ // in a way that doesn't work when there isn't guaranteed to be a null terminator.
+
+ int minLength = Math.Min(strA.Length, strB.Length);
+ for (int i = 0; i < minLength; i++)
+ {
+ if (strA[i] != strB[i])
+ {
+ return strA[i] - strB[i];
+ }
+ }
+
+ return strA.Length - strB.Length;
+ }
// Compares strA and strB using an ordinal (code-point) comparison.
//