From: Charles Stoner Date: Thu, 18 Apr 2019 21:23:40 +0000 (-0700) Subject: Port StringType (dotnet/corefx#36951) X-Git-Tag: submit/tizen/20210909.063632~11031^2~1836 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bae85db58816819ded495c2a0a220d9fa4a03bd9;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Port StringType (dotnet/corefx#36951) Commit migrated from https://github.com/dotnet/corefx/commit/d69b4f7864a76a375d4178b1afa8c9e97ee549b8 --- diff --git a/src/libraries/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs b/src/libraries/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs index e46a95a..4657f1b 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs @@ -268,6 +268,13 @@ namespace Microsoft.VisualBasic.CompilerServices public static bool FromString(string Value) { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public sealed partial class CharArrayType + { + internal CharArrayType() { } + public static char[] FromObject(object Value) { throw null; } + public static char[] FromString(string Value) { throw null; } + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class Conversions { internal Conversions() { } @@ -496,6 +503,30 @@ namespace Microsoft.VisualBasic.CompilerServices public StaticLocalInitFlag() { } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public sealed partial class StringType + { + internal StringType() { } + public static string FromBoolean(bool Value) { throw null; } + public static string FromByte(byte Value) { throw null; } + public static string FromChar(char Value) { throw null; } + public static string FromDate(System.DateTime Value) { throw null; } + public static string FromDecimal(decimal Value) { throw null; } + public static string FromDecimal(decimal Value, System.Globalization.NumberFormatInfo NumberFormat) { throw null; } + public static string FromDouble(double Value) { throw null; } + public static string FromDouble(double Value, System.Globalization.NumberFormatInfo NumberFormat) { throw null; } + public static string FromInteger(int Value) { throw null; } + public static string FromLong(long Value) { throw null; } + public static string FromObject(object Value) { throw null; } + public static string FromShort(short Value) { throw null; } + public static string FromSingle(float Value) { throw null; } + public static string FromSingle(float Value, System.Globalization.NumberFormatInfo NumberFormat) { throw null; } + public static void MidStmtStr(ref string sDest, int StartPosition, int MaxInsertLength, string sInsert) { throw null; } + public static int StrCmp(string sLeft, string sRight, bool TextCompare) { throw null; } + public static bool StrLike(string Source, string Pattern, Microsoft.VisualBasic.CompareMethod CompareOption) { throw null; } + public static bool StrLikeBinary(string Source, string Pattern) { throw null; } + public static bool StrLikeText(string Source, string Pattern) { throw null; } + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class Utils { internal Utils() { } diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj index 8c74e86..2ae1e9f 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj +++ b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj @@ -28,16 +28,19 @@ - - + + + + + @@ -51,24 +54,23 @@ + + + + + + - - - - - - - diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/CharArrayType.vb b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/CharArrayType.vb new file mode 100644 index 0000000..8417ea2 --- /dev/null +++ b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/CharArrayType.vb @@ -0,0 +1,57 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + + Public NotInheritable Class CharArrayType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Char() + + If Value Is Nothing Then + Value = "" + End If + + Return Value.ToCharArray() + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Char() + + If Value Is Nothing Then + Return "".ToCharArray() + End If + + Dim CharArray As Char() = TryCast(Value, Char()) + + If CharArray IsNot Nothing AndAlso CharArray.Rank = 1 Then + Return CharArray + + Else + Dim ValueInterface As IConvertible + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + If (ValueInterface.GetTypeCode() = TypeCode.String) Then + Return ValueInterface.ToString(Nothing).ToCharArray() + End If + End If + + End If + + Throw New InvalidCastException(GetResourceString(SR.InvalidCast_FromTo, VBFriendlyName(Value), "Char()")) + + End Function + + End Class + +End Namespace + + diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/StringType.vb b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/StringType.vb new file mode 100644 index 0000000..aba572b --- /dev/null +++ b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/StringType.vb @@ -0,0 +1,836 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System +Imports System.Globalization +Imports System.Text +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Public NotInheritable Class StringType + ' Prevent creation. + Private Sub New() + End Sub + + Private Const GENERAL_FORMAT As String = "G" + + '============================================================================ + ' Coercion to functions. + '============================================================================ + Public Shared Function FromBoolean(ByVal Value As Boolean) As String + If Value Then + Return System.Boolean.TrueString + Else + Return System.Boolean.FalseString + End If + End Function + + Public Shared Function FromByte(ByVal Value As Byte) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromChar(ByVal Value As Char) As String + Return Value.ToString() + End Function + + Public Shared Function FromShort(ByVal Value As Short) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromInteger(ByVal Value As Integer) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromLong(ByVal Value As Long) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromSingle(ByVal Value As Single) As String + Return FromSingle(Value, Nothing) + End Function + + Public Shared Function FromDouble(ByVal Value As Double) As String + Return FromDouble(Value, Nothing) + End Function + + 'Change to this code after the NDP drop includes the formatting changes + Public Shared Function FromSingle(ByVal Value As Single, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString(Nothing, NumberFormat) + End Function + + Public Shared Function FromDouble(ByVal Value As Double, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + + Public Shared Function FromDate(ByVal Value As Date) As String + Dim TimeTicks As Long = Value.TimeOfDay.Ticks + + If (TimeTicks = Value.Ticks) OrElse + (Value.Year = 1899 AndAlso Value.Month = 12 AndAlso Value.Day = 30) Then 'OA Date with no date is 1899-12-30 + 'No date (1/1/1) + 'UNDONE: REVIEW OA DATE HACK + Return Value.ToString("T", Nothing) + ElseIf TimeTicks = 0 Then + 'No time, or is midnight + Return Value.ToString("d", Nothing) + Else + Return Value.ToString(GENERAL_FORMAT, Nothing) + End If + End Function + + Public Shared Function FromDecimal(ByVal Value As Decimal) As String + Return FromDecimal(Value, Nothing) + End Function + + Public Shared Function FromDecimal(ByVal Value As Decimal, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + + Public Shared Function FromObject(ByVal Value As Object) As String + + If Value Is Nothing Then + Return Nothing + + Else + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return StringValue + End If + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Boolean + Return FromBoolean(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + Return FromByte(ValueInterface.ToByte(Nothing)) + + Case TypeCode.Int16 + Return FromShort(ValueInterface.ToInt16(Nothing)) + + Case TypeCode.Int32 + Return FromInteger(ValueInterface.ToInt32(Nothing)) + + Case TypeCode.Int64 + Return FromLong(ValueInterface.ToInt64(Nothing)) + + Case TypeCode.Single + Return FromSingle(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return FromDouble(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Decimal + Return FromDecimal(ValueInterface.ToDecimal(Nothing)) + + Case TypeCode.String + Return ValueInterface.ToString(Nothing) + + Case TypeCode.Char + Return FromChar(ValueInterface.ToChar(Nothing)) + + Case TypeCode.DateTime + Return FromDate(ValueInterface.ToDateTime(Nothing)) + + Case Else + ' Fall through to error + End Select + + Else + Dim CharArray As Char() = TryCast(Value, Char()) + + If CharArray IsNot Nothing AndAlso CharArray.Rank = 1 Then + Return New String(CharArrayType.FromObject(Value)) + End If + End If + + Throw New InvalidCastException(GetResourceString(SR.InvalidCast_FromTo, VBFriendlyName(Value), "String")) + + End Function + + '============================================================================ + ' Compare/concat/len functions. + '============================================================================ + Public Shared Function StrCmp(ByVal sLeft As String, ByVal sRight As String, ByVal TextCompare As Boolean) As Integer + + If sLeft Is sRight Then + Return 0 + End If + + If sLeft Is Nothing Then + If sRight.Length() = 0 Then + Return 0 + End If + + Return -1 + End If + + If sRight Is Nothing Then + If sLeft.Length() = 0 Then + Return 0 + End If + + Return 1 + End If + + If TextCompare Then + Return GetCultureInfo().CompareInfo.Compare(sLeft, sRight, OptionCompareTextFlags) + Else + Return System.String.CompareOrdinal(sLeft, sRight) + End If + + End Function + + Public Shared Function StrLike(ByVal Source As String, ByVal Pattern As String, ByVal CompareOption As CompareMethod) As Boolean + If CompareOption = CompareMethod.Binary Then + Return StrLikeBinary(Source, Pattern) + Else + Return StrLikeText(Source, Pattern) + End If + End Function + + Public Shared Function StrLikeBinary(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + ' Options = CompareOptions.Ordinal + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Binary, m_InvariantCompareInfo) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso (Not SeenNot)) Then + + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If "!"c <> s Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = StrLikeCompareBinary(SeenNot, Match, p, s) + End If + Else + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Public Shared Function StrLikeText(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Dim Options As CompareOptions + Dim ci As CompareInfo + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ci = GetCultureInfo().CompareInfo + Options = CompareOptions.IgnoreCase Or _ + CompareOptions.IgnoreWidth Or _ + CompareOptions.IgnoreNonSpace Or _ + CompareOptions.IgnoreKanaType + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Text, ci) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso Not SeenNot) Then + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If (ci.Compare("!", s) <> 0) Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + If Options = CompareOptions.Ordinal Then + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + Else + Match = (ci.Compare(StartRangeChar, s, Options) < 0) AndAlso (ci.Compare(EndRangeChar, s, Options) >= 0) + End If + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = StrLikeCompare(ci, SeenNot, Match, p, s, Options) + End If + Else + If Options = CompareOptions.Ordinal Then + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + Else + ' Slurp up the diacritical marks, if any (both non-spacing marks and modifier symbols) + ' Note that typically, we'll only have at most one diacritical mark. Therefore, I'm not + ' using StringBuilder here, since the minimal overhead of appending a character doesn't + ' justify invoking a couple of instances of StringBuilder. . + Dim pstr As String = p + Dim sstr As String = s + Do While PatternIndex + 1 < PatternEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1))) + pstr = pstr & Pattern.Chars(PatternIndex + 1) + PatternIndex = PatternIndex + 1 + Loop + Do While SourceIndex + 1 < SourceEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1))) + sstr = sstr & Source.Chars(SourceIndex + 1) + SourceIndex = SourceIndex + 1 + Loop + + If (ci.Compare(pstr, sstr, OptionCompareTextFlags) <> 0) AndAlso Not SeenNot Then + Exit Do + End If + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Private Shared Function StrLikeCompareBinary(ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char) As Boolean + If SeenNot AndAlso Match Then + Return p <> s + ElseIf Not SeenNot AndAlso Not Match Then + Return p = s + Else + Return Match + End If + End Function + + Private Shared Function StrLikeCompare(ByVal ci As CompareInfo, ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char, ByVal Options As CompareOptions) As Boolean + If SeenNot AndAlso Match Then + If Options = CompareOptions.Ordinal Then + Return p <> s + Else + Return Not (ci.Compare(p, s, Options) = 0) + End If + ElseIf Not SeenNot AndAlso Not Match Then + If Options = CompareOptions.Ordinal Then + Return p = s + Else + Return (ci.Compare(p, s, Options) = 0) + End If + Else + Return Match + End If + End Function + + Private Shared Function AsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal SourceEndIndex As Integer, _ + ByVal CompareOption As CompareMethod, ByVal ci As CompareInfo) As Integer + + 'Returns the number of source characters to skip over to handle an asterisk in the pattern. + 'When there's only a single asterisk in the pattern, it computes how many pattern equivalent chars + 'follow the *: [a-z], [abcde], ?, # each count as one char. + 'Pattern contains the substring following the * + 'Source contains the substring not yet matched. + + Dim p As Char + Dim SeenLiteral As Boolean + Dim SeenSpecial As Boolean 'Remembers if we've seen #, ?, [abd-eg], or ! when they have their special meanings + Dim InsideBracket As Boolean + Dim Count As Integer + Dim PatternEndIndex As Integer + Dim PatternIndex As Integer + Dim TruncatedPattern As String + Dim Options As CompareOptions + + PatternEndIndex = Len(Pattern) + + 'Determine how many pattern equivalent chars follow the *, and if there are multiple *s + '[a-z], [abcde] each count as one char. + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + Select Case p + Case "*"c + If Count > 0 Then + 'We found multiple asterisks with an intervening pattern + If SeenSpecial Then + 'Pattern uses special characters which means we can't compute easily how far to skip. + Count = MultipleAsteriskSkip(Pattern, Source, Count, CompareOption) + Return SourceEndIndex - Count + Else + 'Pattern uses only literals, so we can directly search for the pattern in the source + 'TODO: Handle cases where pattern could be replicated in the source. + TruncatedPattern = Pattern.Substring(0, PatternIndex) 'Remove the second * and everything trailing + + If CompareOption = CompareMethod.Binary Then + Options = CompareOptions.Ordinal + Else + Options = CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreNonSpace Or CompareOptions.IgnoreKanaType + End If + + 'Count = Source.LastIndexOf(TruncatedPattern) + Count = ci.LastIndexOf(Source, TruncatedPattern, Options) + Return Count + End If + + Else + 'Do nothing, which colalesces multiple asterisks together + End If + + Case "-"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + End If + + Case "!"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + Else + SeenSpecial = True + End If + + Case "["c + If InsideBracket Then + SeenLiteral = True + Else + InsideBracket = True + End If + + Case "]"c + If SeenLiteral OrElse Not InsideBracket Then + Count += 1 + SeenSpecial = True + End If + SeenLiteral = False + InsideBracket = False + + Case "?"c, "#"c + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + SeenSpecial = True + End If + + Case Else + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + End If + End Select + + PatternIndex += 1 + Loop + + Return SourceEndIndex - Count + End Function + + Private Shared Function MultipleAsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal Count As Integer, ByVal CompareOption As CompareMethod) As Integer + 'Multiple asterisks with intervening chars were found in the pattern, such as "**". + 'Use a recursive approach to determine how many source chars to skip. + 'Start near the end of Source and move backwards one char at a time until a match is found or we reach start of Source. + + Dim SourceEndIndex As Integer + Dim NewSource As String + Dim Result As Boolean + + SourceEndIndex = Len(Source) + + Do While Count < SourceEndIndex + NewSource = Source.Substring(SourceEndIndex - Count) + + Try + Result = StrLike(NewSource, Pattern, CompareOption) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Result = False + End Try + + If Result Then + Exit Do + End If + + Count += 1 + Loop + + Return Count + End Function + + Public Shared Sub MidStmtStr(ByRef sDest As String, ByVal StartPosition As Integer, ByVal MaxInsertLength As Integer, ByVal sInsert As String) + Dim DestLength As Integer + Dim InsertLength As Integer + Dim EndSegmentLength As Integer + + If sDest Is Nothing Then + 'DestLength = 0 + Else + DestLength = sDest.Length + End If + + If sInsert Is Nothing Then + 'InsertLength = 0 + Else + InsertLength = sInsert.Length + End If + + 'Zero base the index + StartPosition -= 1 + + If StartPosition < 0 OrElse StartPosition >= DestLength Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Start")) + End If + + If MaxInsertLength < 0 Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Length")) + End If + + ' first, limit the length of the source string + ' to lenChange + + If (InsertLength > MaxInsertLength) Then + InsertLength = MaxInsertLength + End If + + ' second, limit the length to the available space + ' in the destination string + + If (InsertLength > DestLength - StartPosition) Then + InsertLength = DestLength - StartPosition + End If + + If InsertLength = 0 Then + 'Destination string remains unchanged + Exit Sub + End If + + 'This looks a bit complex for removing and inserting strings + 'but when manipulating long strings, it should provide + 'better performance because of fewer memcpys + + Dim sb As StringBuilder + + sb = New StringBuilder(DestLength) + + If StartPosition > 0 Then + 'Append first part of destination string + sb.Append(sDest, 0, StartPosition) + End If + + 'Append InsertString + sb.Append(sInsert, 0, InsertLength) + EndSegmentLength = DestLength - (StartPosition + InsertLength) + + If EndSegmentLength > 0 Then + 'Append remainder of destination string + sb.Append(sDest, StartPosition + InsertLength, EndSegmentLength) + End If + + sDest = sb.ToString() + End Sub + + End Class + +End Namespace + diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/ConversionsTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/ConversionsTests.cs index a1a21f4..d258aa8 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/ConversionsTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/ConversionsTests.cs @@ -2552,6 +2552,181 @@ namespace Microsoft.VisualBasic.Tests Assert.Throws(() => Conversions.ToChar(value)); } + public static IEnumerable ToString_IConvertible_TestData() + { + // byte. + yield return new object[] { byte.MinValue, "0" }; + yield return new object[] { (byte)1, "1" }; + yield return new object[] { byte.MaxValue, "255" }; + yield return new object[] { (ByteEnum)byte.MinValue, "0" }; + yield return new object[] { (ByteEnum)1, "1" }; + yield return new object[] { (ByteEnum)byte.MaxValue, "255" }; + + // sbyte. + yield return new object[] { sbyte.MinValue, "-128" }; + yield return new object[] { (sbyte)(-1), "-1" }; + yield return new object[] { (sbyte)0, "0" }; + yield return new object[] { (sbyte)1, "1" }; + yield return new object[] { sbyte.MaxValue, "127" }; + yield return new object[] { (SByteEnum)sbyte.MinValue, "-128" }; + yield return new object[] { (SByteEnum)(-1), "-1" }; + yield return new object[] { (SByteEnum)0, "0" }; + yield return new object[] { (SByteEnum)1, "1" }; + yield return new object[] { (SByteEnum)sbyte.MaxValue, "127" }; + + // ushort. + yield return new object[] { ushort.MinValue, "0" }; + yield return new object[] { (ushort)1, "1" }; + yield return new object[] { ushort.MaxValue, "65535" }; + yield return new object[] { (UShortEnum)ushort.MinValue, "0" }; + yield return new object[] { (UShortEnum)1, "1" }; + yield return new object[] { (UShortEnum)ushort.MaxValue, "65535" }; + + // short. + yield return new object[] { short.MinValue, "-32768" }; + yield return new object[] { (short)(-1), "-1" }; + yield return new object[] { (short)0, "0" }; + yield return new object[] { (short)1, "1" }; + yield return new object[] { short.MaxValue, "32767" }; + yield return new object[] { (ShortEnum)short.MinValue, "-32768" }; + yield return new object[] { (ShortEnum)(-1), "-1" }; + yield return new object[] { (ShortEnum)0, "0" }; + yield return new object[] { (ShortEnum)1, "1" }; + yield return new object[] { (ShortEnum)short.MaxValue, "32767" }; + + // uint. + yield return new object[] { uint.MinValue, "0" }; + yield return new object[] { (uint)1, "1" }; + yield return new object[] { uint.MaxValue, "4294967295" }; + yield return new object[] { (UIntEnum)uint.MinValue, "0" }; + yield return new object[] { (UIntEnum)1, "1" }; + yield return new object[] { (UIntEnum)uint.MaxValue, "4294967295" }; + + // int. + yield return new object[] { int.MinValue, "-2147483648" }; + yield return new object[] { -1, "-1" }; + yield return new object[] { 0, "0" }; + yield return new object[] { 1, "1" }; + yield return new object[] { int.MaxValue, "2147483647" }; + yield return new object[] { (IntEnum)int.MinValue, "-2147483648" }; + yield return new object[] { (IntEnum)(-1), "-1" }; + yield return new object[] { (IntEnum)0, "0" }; + yield return new object[] { (IntEnum)1, "1" }; + yield return new object[] { (IntEnum)int.MaxValue, "2147483647" }; + + // ulong. + yield return new object[] { ulong.MinValue, "0" }; + yield return new object[] { (ulong)1, "1" }; + yield return new object[] { ulong.MaxValue, "18446744073709551615" }; + yield return new object[] { (ULongEnum)ulong.MinValue, "0" }; + yield return new object[] { (ULongEnum)1, "1" }; + yield return new object[] { (ULongEnum)ulong.MaxValue, "18446744073709551615" }; + + // long. + yield return new object[] { long.MinValue, "-9223372036854775808" }; + yield return new object[] { (long)(-1), "-1" }; + yield return new object[] { (long)0, "0" }; + yield return new object[] { (long)1, "1" }; + yield return new object[] { long.MaxValue, "9223372036854775807" }; + yield return new object[] { (LongEnum)long.MinValue, "-9223372036854775808" }; + yield return new object[] { (LongEnum)(-1), "-1" }; + yield return new object[] { (LongEnum)0, "0" }; + yield return new object[] { (LongEnum)1, "1" }; + yield return new object[] { (LongEnum)long.MaxValue, "9223372036854775807" }; + + // float. + yield return new object[] { (float)(-1), "-1" }; + yield return new object[] { (float)0, "0" }; + yield return new object[] { (float)1, "1" }; + yield return new object[] { float.PositiveInfinity, float.PositiveInfinity.ToString() }; + yield return new object[] { float.NegativeInfinity, float.NegativeInfinity.ToString() }; + yield return new object[] { float.NaN, "NaN" }; + + // double. + yield return new object[] { (double)(-1), "-1" }; + yield return new object[] { (double)0, "0" }; + yield return new object[] { (double)1, "1" }; + yield return new object[] { double.PositiveInfinity, double.PositiveInfinity.ToString() }; + yield return new object[] { double.NegativeInfinity, double.NegativeInfinity.ToString() }; + yield return new object[] { double.NaN, "NaN" }; + + // decimal. + yield return new object[] { decimal.MinValue, decimal.MinValue.ToString() }; + yield return new object[] { (decimal)(-1), "-1" }; + yield return new object[] { (decimal)0, "0" }; + yield return new object[] { (decimal)1, "1" }; + yield return new object[] { decimal.MaxValue, decimal.MaxValue.ToString() }; + + // bool. + yield return new object[] { true, "True" }; + yield return new object[] { false, "False" }; + if (ReflectionEmitSupported) + { + yield return new object[] { BoolEnum, "False" }; + } + + // string. + yield return new object[] { "", "" }; + yield return new object[] { "abc", "abc" }; + + // null. + yield return new object[] { null, (string)null }; + + // char. + yield return new object[] { char.MinValue, "\0" }; + yield return new object[] { (char)1, "\u0001" }; + yield return new object[] { 'a', "a" }; + yield return new object[] { char.MaxValue, char.MaxValue.ToString() }; + + // DateTime. + yield return new object[] { new DateTime(10), new DateTime(10).ToString("T", null) }; + } + + [Theory] + [MemberData(nameof(ToString_IConvertible_TestData))] + public void ToString_IConvertible_ReturnsExpected(IConvertible value, string expected) + { + AssertEqual(expected, Conversions.ToString(value)); + if (value != null) + { + AssertEqual(expected, Conversions.ToString(new ConvertibleWrapper(value))); + } + } + + public static IEnumerable ToString_Object_TestData() + { + // char[] + yield return new object[] { new char[0], "" }; + yield return new object[] { new char[] { (char)0 }, "\0" }; + yield return new object[] { new char[] { 'A', 'B' }, "AB" }; + } + + [Theory] + [MemberData(nameof(ToString_Object_TestData))] + public void ToString_Object_ReturnsExpected(object value, string expected) + { + AssertEqual(expected, Conversions.ToString(value)); + } + + public static IEnumerable ToString_InvalidObject_TestData() + { + yield return new object[] { new object() }; + } + + [Theory] + [MemberData(nameof(ToString_InvalidObject_TestData))] + public void ToString_InvalidObject_ThrowsInvalidCastException(object value) + { + Assert.Throws(() => Conversions.ToString(value)); + } + + [Theory] + [MemberData(nameof(InvalidBool_TestData))] + public void ToString_InvalidBool_ThrowsInvalidOperationException(object value) + { + Assert.Throws(() => Conversions.ToString(value)); + } + private static object s_floatEnum; public static object FloatEnum @@ -2698,21 +2873,21 @@ namespace Microsoft.VisualBasic.Tests Assert.Equal(expected, actual); } } + } - public enum ByteEnum : byte { Value = 1 } + public enum ByteEnum : byte { Value = 1 } - public enum SByteEnum : sbyte { Value = 1 } + public enum SByteEnum : sbyte { Value = 1 } - public enum UShortEnum : ushort { Value = 1 } + public enum UShortEnum : ushort { Value = 1 } - public enum ShortEnum : short { Value = 1 } + public enum ShortEnum : short { Value = 1 } - public enum UIntEnum : uint { Value = 1 } + public enum UIntEnum : uint { Value = 1 } - public enum IntEnum : int { Value = 1 } + public enum IntEnum : int { Value = 1 } - public enum ULongEnum : ulong { Value = 1 } + public enum ULongEnum : ulong { Value = 1 } - public enum LongEnum : long { Value = 1 } - } + public enum LongEnum : long { Value = 1 } } diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj b/src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj index e5ef8c6..668accc 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj @@ -35,6 +35,7 @@ + diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs new file mode 100644 index 0000000..9c5dae1 --- /dev/null +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs @@ -0,0 +1,413 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VisualBasic.CompilerServices; +using Microsoft.VisualBasic.Tests; +using System; +using System.Collections.Generic; +using System.Globalization; +using Xunit; + +namespace Microsoft.VisualBasic.CompilerServices.Tests +{ + public class StringTypeTests + { + [Theory] + [MemberData(nameof(FromBoolean_TestData))] + public void FromBoolean(bool value, string expected) + { + Assert.Equal(expected, StringType.FromBoolean(value)); + } + + [Theory] + [MemberData(nameof(FromByte_TestData))] + public void FromByte(byte value, string expected) + { + Assert.Equal(expected, StringType.FromByte(value)); + } + + [Theory] + [MemberData(nameof(FromChar_TestData))] + public void FromChar(char value, string expected) + { + Assert.Equal(expected, StringType.FromChar(value)); + } + + [Theory] + [MemberData(nameof(FromDateTime_TestData))] + public void FromDate(DateTime value, string expected) + { + Assert.Equal(expected, StringType.FromDate(value)); + } + + [Theory] + [MemberData(nameof(FromDecimal_TestData))] + public void FromDecimal(decimal value, string expected) + { + Assert.Equal(expected, StringType.FromDecimal(value)); + Assert.Equal(expected, StringType.FromDecimal(value, default(NumberFormatInfo))); + } + + [Theory] + [MemberData(nameof(FromDecimal_Format_TestData))] + public void FromDecimal(decimal value, NumberFormatInfo format, string expected) + { + Assert.Equal(expected, StringType.FromDecimal(value, format)); + } + + [Theory] + [MemberData(nameof(FromDouble_TestData))] + public void FromDouble(double value, string expected) + { + Assert.Equal(expected, StringType.FromDouble(value)); + Assert.Equal(expected, StringType.FromDouble(value, default(NumberFormatInfo))); + } + + [Theory] + [MemberData(nameof(FromDouble_Format_TestData))] + public void FromDouble(double value, NumberFormatInfo format, string expected) + { + Assert.Equal(expected, StringType.FromDouble(value, format)); + } + + [Theory] + [MemberData(nameof(FromInt32_TestData))] + public void FromInteger(int value, string expected) + { + Assert.Equal(expected, StringType.FromInteger(value)); + } + + [Theory] + [MemberData(nameof(FromInt64_TestData))] + public void FromLong(long value, string expected) + { + Assert.Equal(expected, StringType.FromLong(value)); + } + + [Theory] + [MemberData(nameof(FromByte_TestData))] + [MemberData(nameof(FromInt16_TestData))] + [MemberData(nameof(FromInt32_TestData))] + [MemberData(nameof(FromInt64_TestData))] + [MemberData(nameof(FromSingle_TestData))] + [MemberData(nameof(FromDouble_TestData))] + [MemberData(nameof(FromDecimal_TestData))] + [MemberData(nameof(FromBoolean_TestData))] + [MemberData(nameof(FromString_TestData))] + [MemberData(nameof(FromNull_TestData))] + [MemberData(nameof(FromChar_TestData))] + [MemberData(nameof(FromCharArray_TestData))] + [MemberData(nameof(FromDateTime_TestData))] + public void FromObject(object value, string expected) + { + Assert.Equal(expected, StringType.FromObject(value)); + } + + // The following should be supported but are not. + [Theory] + [MemberData(nameof(FromSByte_TestData))] + [MemberData(nameof(FromUInt16_TestData))] + [MemberData(nameof(FromUInt32_TestData))] + [MemberData(nameof(FromUInt64_TestData))] + public void FromObject_Unexpected(object value, string expected) + { + Assert.Throws(() => StringType.FromObject(value)); + } + + [Theory] + [MemberData(nameof(FromObject_TestData))] + public void FromObject_InvalidCastException(object value) + { + Assert.Throws(() => StringType.FromObject(value)); + } + + [Theory] + [MemberData(nameof(FromInt16_TestData))] + public void FromShort(short value, string expected) + { + Assert.Equal(expected, StringType.FromShort(value)); + } + + [Theory] + [MemberData(nameof(FromSingle_TestData))] + public void FromSingle(float value, string expected) + { + Assert.Equal(expected, StringType.FromSingle(value)); + Assert.Equal(expected, StringType.FromSingle(value, default(NumberFormatInfo))); + } + + [Theory] + [MemberData(nameof(FromSingle_Format_TestData))] + public void FromSingle(float value, NumberFormatInfo format, string expected) + { + Assert.Equal(expected, StringType.FromSingle(value, format)); + } + + private static IEnumerable FromByte_TestData() + { + yield return new object[] { byte.MinValue, "0" }; + yield return new object[] { (byte)1, "1" }; + yield return new object[] { byte.MaxValue, "255" }; + yield return new object[] { (ByteEnum)byte.MinValue, "0" }; + yield return new object[] { (ByteEnum)1, "1" }; + yield return new object[] { (ByteEnum)byte.MaxValue, "255" }; + } + + private static IEnumerable FromSByte_TestData() + { + yield return new object[] { sbyte.MinValue, "-128" }; + yield return new object[] { (sbyte)(-1), "-1" }; + yield return new object[] { (sbyte)0, "0" }; + yield return new object[] { (sbyte)1, "1" }; + yield return new object[] { sbyte.MaxValue, "127" }; + yield return new object[] { (SByteEnum)sbyte.MinValue, "-128" }; + yield return new object[] { (SByteEnum)(-1), "-1" }; + yield return new object[] { (SByteEnum)0, "0" }; + yield return new object[] { (SByteEnum)1, "1" }; + yield return new object[] { (SByteEnum)sbyte.MaxValue, "127" }; + } + + private static IEnumerable FromUInt16_TestData() + { + yield return new object[] { ushort.MinValue, "0" }; + yield return new object[] { (ushort)1, "1" }; + yield return new object[] { ushort.MaxValue, "65535" }; + yield return new object[] { (UShortEnum)ushort.MinValue, "0" }; + yield return new object[] { (UShortEnum)1, "1" }; + yield return new object[] { (UShortEnum)ushort.MaxValue, "65535" }; + } + + private static IEnumerable FromInt16_TestData() + { + yield return new object[] { short.MinValue, "-32768" }; + yield return new object[] { (short)(-1), "-1" }; + yield return new object[] { (short)0, "0" }; + yield return new object[] { (short)1, "1" }; + yield return new object[] { short.MaxValue, "32767" }; + yield return new object[] { (ShortEnum)short.MinValue, "-32768" }; + yield return new object[] { (ShortEnum)(-1), "-1" }; + yield return new object[] { (ShortEnum)0, "0" }; + yield return new object[] { (ShortEnum)1, "1" }; + yield return new object[] { (ShortEnum)short.MaxValue, "32767" }; + } + + private static IEnumerable FromUInt32_TestData() + { + yield return new object[] { uint.MinValue, "0" }; + yield return new object[] { (uint)1, "1" }; + yield return new object[] { uint.MaxValue, "4294967295" }; + yield return new object[] { (UIntEnum)uint.MinValue, "0" }; + yield return new object[] { (UIntEnum)1, "1" }; + yield return new object[] { (UIntEnum)uint.MaxValue, "4294967295" }; + } + + private static IEnumerable FromInt32_TestData() + { + yield return new object[] { int.MinValue, "-2147483648" }; + yield return new object[] { -1, "-1" }; + yield return new object[] { 0, "0" }; + yield return new object[] { 1, "1" }; + yield return new object[] { int.MaxValue, "2147483647" }; + yield return new object[] { (IntEnum)int.MinValue, "-2147483648" }; + yield return new object[] { (IntEnum)(-1), "-1" }; + yield return new object[] { (IntEnum)0, "0" }; + yield return new object[] { (IntEnum)1, "1" }; + yield return new object[] { (IntEnum)int.MaxValue, "2147483647" }; + } + + private static IEnumerable FromUInt64_TestData() + { + yield return new object[] { ulong.MinValue, "0" }; + yield return new object[] { (ulong)1, "1" }; + yield return new object[] { ulong.MaxValue, "18446744073709551615" }; + yield return new object[] { (ULongEnum)ulong.MinValue, "0" }; + yield return new object[] { (ULongEnum)1, "1" }; + yield return new object[] { (ULongEnum)ulong.MaxValue, "18446744073709551615" }; + } + + private static IEnumerable FromInt64_TestData() + { + yield return new object[] { long.MinValue, "-9223372036854775808" }; + yield return new object[] { (long)(-1), "-1" }; + yield return new object[] { (long)0, "0" }; + yield return new object[] { (long)1, "1" }; + yield return new object[] { long.MaxValue, "9223372036854775807" }; + yield return new object[] { (LongEnum)long.MinValue, "-9223372036854775808" }; + yield return new object[] { (LongEnum)(-1), "-1" }; + yield return new object[] { (LongEnum)0, "0" }; + yield return new object[] { (LongEnum)1, "1" }; + yield return new object[] { (LongEnum)long.MaxValue, "9223372036854775807" }; + } + + private static IEnumerable FromSingle_TestData() + { + yield return new object[] { (float)(-1), "-1" }; + yield return new object[] { (float)0, "0" }; + yield return new object[] { (float)1, "1" }; + yield return new object[] { float.PositiveInfinity, float.PositiveInfinity.ToString() }; + yield return new object[] { float.NegativeInfinity, float.NegativeInfinity.ToString() }; + yield return new object[] { float.NaN, "NaN" }; + } + + private static IEnumerable FromDouble_TestData() + { + yield return new object[] { (double)(-1), "-1" }; + yield return new object[] { (double)0, "0" }; + yield return new object[] { (double)1, "1" }; + yield return new object[] { double.PositiveInfinity, double.PositiveInfinity.ToString() }; + yield return new object[] { double.NegativeInfinity, double.NegativeInfinity.ToString() }; + yield return new object[] { double.NaN, "NaN" }; + } + + private static IEnumerable FromDecimal_TestData() + { + yield return new object[] { decimal.MinValue, decimal.MinValue.ToString() }; + yield return new object[] { (decimal)(-1), "-1" }; + yield return new object[] { (decimal)0, "0" }; + yield return new object[] { (decimal)1, "1" }; + yield return new object[] { decimal.MaxValue, decimal.MaxValue.ToString() }; + } + + private static IEnumerable FromBoolean_TestData() + { + yield return new object[] { true, "True" }; + yield return new object[] { false, "False" }; + } + + private static IEnumerable FromString_TestData() + { + yield return new object[] { "", "" }; + yield return new object[] { "abc", "abc" }; + } + + private static IEnumerable FromNull_TestData() + { + yield return new object[] { null, (string)null }; + } + + private static IEnumerable FromChar_TestData() + { + yield return new object[] { char.MinValue, "\0" }; + yield return new object[] { (char)1, "\u0001" }; + yield return new object[] { 'a', "a" }; + yield return new object[] { char.MaxValue, char.MaxValue.ToString() }; + } + + private static IEnumerable FromCharArray_TestData() + { + yield return new object[] { new char[0], "" }; + yield return new object[] { new char[] { (char)0 }, "\0" }; + yield return new object[] { new char[] { 'A', 'B' }, "AB" }; + } + + private static IEnumerable FromDateTime_TestData() + { + yield return new object[] { new DateTime(10), new DateTime(10).ToString("T", null) }; + } + + private static IEnumerable FromObject_TestData() + { + yield return new object[] { new object() }; + } + + private static IEnumerable FromSingle_Format_TestData() + { + yield return new object[] { (float)(-1), default(NumberFormatInfo), "-1" }; + yield return new object[] { (float)(-1), new NumberFormatInfo() { NegativeSign = "#" }, "#1" }; + } + + private static IEnumerable FromDouble_Format_TestData() + { + yield return new object[] { (double)(-1), default(NumberFormatInfo), "-1" }; + yield return new object[] { (double)(-1), new NumberFormatInfo() { NegativeSign = "#" }, "#1" }; + } + + private static IEnumerable FromDecimal_Format_TestData() + { + yield return new object[] { (decimal)(-1), default(NumberFormatInfo), "-1" }; + yield return new object[] { (decimal)(-1), new NumberFormatInfo() { NegativeSign = "#" }, "#1" }; + } + + [Theory] + [InlineData("a", 1, 0, null, "a")] + [InlineData("a", 1, 0, "", "a")] + [InlineData("a", 1, 1, "", "a")] + [InlineData("a", 1, 0, "b", "a")] + [InlineData("a", 1, 1, "b", "b")] + [InlineData("a", 1, 2, "b", "b")] + [InlineData("abc", 2, 0, "def", "abc")] + [InlineData("abc", 2, 1, "def", "adc")] + [InlineData("abc", 2, 2, "def", "ade")] + [InlineData("abc", 2, 3, "def", "ade")] + public void MidStmtStr(string str, int start, int length, string insert, string expected) + { + StringType.MidStmtStr(ref str, start, length, insert); + Assert.Equal(expected, str); + } + + [Theory] + [InlineData(null, 1, 0, null)] + [InlineData(null, 1, 0, "")] + [InlineData("", 1, 0, null)] + [InlineData("", -1, 0, "")] + [InlineData("", 0, 0, "")] + [InlineData("", 1, 0, "")] + [InlineData("", 2, 0, "")] + [InlineData("", 1, -1, "")] + [InlineData("abc", -1, 0, "")] + [InlineData("abc", 0, 0, "")] + [InlineData("abc", 4, 0, "")] + [InlineData("abc", 1, -3, "")] + public void MidStmtStr_ArgumentException(string str, int start, int length, string insert) + { + Assert.Throws(() => StringType.MidStmtStr(ref str, start, length, insert)); + } + + [Theory] + [InlineData(null, null, 0, 0)] + [InlineData(null, "", 0, 0)] + [InlineData("", null, 0, 0)] + [InlineData(null, "a", -1, -1)] + [InlineData("a", null, 1, 1)] + [InlineData("", "a", -97, -1)] + [InlineData("a", "", 97, 1)] + [InlineData("a", "a", 0, 0)] + [InlineData("a", "b", -1, -1)] + [InlineData("b", "a", 1, 1)] + [InlineData("a", "ABC", 32, -1)] + [InlineData("ABC", "a", -32, 1)] + [InlineData("abc", "ABC", 32, 0)] + public void StrCmp(string left, string right, int expectedBinaryCompare, int expectedTextCompare) + { + Assert.Equal(expectedBinaryCompare, StringType.StrCmp(left, right, TextCompare: false)); + Assert.Equal(expectedTextCompare, StringType.StrCmp(left, right, TextCompare: true)); + } + + [Theory] + [InlineData(null, null, true, true)] + [InlineData("", null, true, true)] + [InlineData("", "*", true, true)] + [InlineData("", "?", false, false)] + [InlineData("a", "?", true, true)] + [InlineData("a3", "[A-Z]#", false, true)] + [InlineData("A3", "[a-z]#", false, true)] + public void StrLike(string source, string pattern, bool expectedBinaryCompare, bool expectedTextCompare) + { + Assert.Equal(expectedBinaryCompare, StringType.StrLike(source, pattern, CompareMethod.Binary)); + Assert.Equal(expectedTextCompare, StringType.StrLike(source, pattern, CompareMethod.Text)); + Assert.Equal(expectedBinaryCompare, StringType.StrLikeBinary(source, pattern)); + Assert.Equal(expectedTextCompare, StringType.StrLikeText(source, pattern)); + } + + [Theory] + [InlineData(null, "*")] + public void StrLike_NullReferenceException(string source, string pattern) + { + Assert.Throws(() => StringType.StrLike(source, pattern, CompareMethod.Binary)); + Assert.Throws(() => StringType.StrLike(source, pattern, CompareMethod.Text)); + Assert.Throws(() => StringType.StrLikeBinary(source, pattern)); + Assert.Throws(() => StringType.StrLikeText(source, pattern)); + } + } +}