Port StringType (dotnet/corefx#36951)
authorCharles Stoner <chucks@microsoft.com>
Thu, 18 Apr 2019 21:23:40 +0000 (14:23 -0700)
committerGitHub <noreply@github.com>
Thu, 18 Apr 2019 21:23:40 +0000 (14:23 -0700)
Commit migrated from https://github.com/dotnet/corefx/commit/d69b4f7864a76a375d4178b1afa8c9e97ee549b8

src/libraries/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs
src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj
src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/CharArrayType.vb [new file with mode: 0644]
src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/CompilerServices/StringType.vb [new file with mode: 0644]
src/libraries/Microsoft.VisualBasic.Core/tests/ConversionsTests.cs
src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj
src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs [new file with mode: 0644]

index e46a95a..4657f1b 100644 (file)
@@ -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() { }
index 8c74e86..2ae1e9f 100644 (file)
     <Compile Include="Microsoft\VisualBasic\FileIO\FileSystem.Unix.vb" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Microsoft\VisualBasic\CallType.vb" />
-    <Compile Include="Microsoft\VisualBasic\Collection.vb" />
     <Compile Include="Microsoft\VisualBasic\ApplicationServices\StartupEventArgs.vb" />
     <Compile Include="Microsoft\VisualBasic\ApplicationServices\StartupNextInstanceEventArgs.vb" />
     <Compile Include="Microsoft\VisualBasic\ApplicationServices\UnhandledExceptionEventArgs.vb" />
+    <Compile Include="Microsoft\VisualBasic\CallType.vb" />
+    <Compile Include="Microsoft\VisualBasic\Collection.vb" />
+    <Compile Include="Microsoft\VisualBasic\ComClassAttribute.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\BooleanType.vb" />
+    <Compile Include="Microsoft\VisualBasic\CompilerServices\CharArrayType.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\ConversionResolution.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\Conversions.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\DecimalType.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\DesignerGeneratedAttribute.vb" />
+    <Compile Include="Microsoft\VisualBasic\CompilerServices\DoubleType.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\ExceptionUtils.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\IDOBinder.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\IncompleteInitialization.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\ProjectData.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\StandardModuleAttribute.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\StaticLocalInitFlag.vb" />
+    <Compile Include="Microsoft\VisualBasic\CompilerServices\StringType.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\StructUtils.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\Symbols.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\Utils.LateBinder.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\Utils.vb" />
     <Compile Include="Microsoft\VisualBasic\CompilerServices\Versioned.vb" />
+    <Compile Include="Microsoft\VisualBasic\Constants.vb" />
+    <Compile Include="Microsoft\VisualBasic\ControlChars.vb" />
+    <Compile Include="Microsoft\VisualBasic\DateAndTime.vb" />
+    <Compile Include="Microsoft\VisualBasic\Devices\NetworkAvailableEventArgs.vb" />
+    <Compile Include="Microsoft\VisualBasic\ErrObject.vb" />
     <Compile Include="Microsoft\VisualBasic\FileIO\FileSystem.vb" />
     <Compile Include="Microsoft\VisualBasic\FileIO\MalformedLineException.vb" />
     <Compile Include="Microsoft\VisualBasic\FileIO\SpecialDirectories.vb" />
     <Compile Include="Microsoft\VisualBasic\FileIO\TextFieldParser.vb" />
     <Compile Include="Microsoft\VisualBasic\Globals.vb" />
-    <Compile Include="Microsoft\VisualBasic\Devices\NetworkAvailableEventArgs.vb" />
-    <Compile Include="Microsoft\VisualBasic\ComClassAttribute.vb" />
-    <Compile Include="Microsoft\VisualBasic\Constants.vb" />
-    <Compile Include="Microsoft\VisualBasic\ControlChars.vb" />
     <Compile Include="Microsoft\VisualBasic\Helpers\ForEachEnum.vb" />
-    <Compile Include="Microsoft\VisualBasic\CompilerServices\DoubleType.vb" />
-    <Compile Include="Microsoft\VisualBasic\DateAndTime.vb" />
-    <Compile Include="Microsoft\VisualBasic\ErrObject.vb" />
     <Compile Include="Microsoft\VisualBasic\HideModuleNameAttribute.vb" />
     <Compile Include="Microsoft\VisualBasic\Information.vb" />
     <Compile Include="Microsoft\VisualBasic\Interaction.vb" />
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 (file)
index 0000000..8417ea2
--- /dev/null
@@ -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
+
+    <System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)>
+    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 (file)
index 0000000..aba572b
--- /dev/null
@@ -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
+
+    <System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)> _
+    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 "*<chars>*".
+            '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
+
index a1a21f4..d258aa8 100644 (file)
@@ -2552,6 +2552,181 @@ namespace Microsoft.VisualBasic.Tests
             Assert.Throws<InvalidOperationException>(() => Conversions.ToChar(value));
         }
 
+        public static IEnumerable<object[]> 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<object[]> 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<object[]> ToString_InvalidObject_TestData()
+        {
+            yield return new object[] { new object() };
+        }
+
+        [Theory]
+        [MemberData(nameof(ToString_InvalidObject_TestData))]
+        public void ToString_InvalidObject_ThrowsInvalidCastException(object value)
+        {
+            Assert.Throws<InvalidCastException>(() => Conversions.ToString(value));
+        }
+
+        [Theory]
+        [MemberData(nameof(InvalidBool_TestData))]
+        public void ToString_InvalidBool_ThrowsInvalidOperationException(object value)
+        {
+            Assert.Throws<InvalidOperationException>(() => 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 }
 }
index e5ef8c6..668accc 100644 (file)
@@ -35,6 +35,7 @@
     <Compile Include="UtilsTests.cs" />
     <Compile Include="VBMathTests.cs" />
     <Compile Include="StringsTests.cs" />
+    <Compile Include="StringTypeTests.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/StringTypeTests.cs
new file mode 100644 (file)
index 0000000..9c5dae1
--- /dev/null
@@ -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<InvalidCastException>(() => StringType.FromObject(value));
+        }
+
+        [Theory]
+        [MemberData(nameof(FromObject_TestData))]
+        public void FromObject_InvalidCastException(object value)
+        {
+            Assert.Throws<InvalidCastException>(() => 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> 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<object[]> FromBoolean_TestData()
+        {
+            yield return new object[] { true, "True" };
+            yield return new object[] { false, "False" };
+        }
+
+        private static IEnumerable<object[]> FromString_TestData()
+        {
+            yield return new object[] { "", "" };
+            yield return new object[] { "abc", "abc" };
+        }
+
+        private static IEnumerable<object[]> FromNull_TestData()
+        {
+            yield return new object[] { null, (string)null };
+        }
+
+        private static IEnumerable<object[]> 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<object[]> 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<object[]> FromDateTime_TestData()
+        {
+            yield return new object[] { new DateTime(10), new DateTime(10).ToString("T", null) };
+        }
+
+        private static IEnumerable<object[]> FromObject_TestData()
+        {
+            yield return new object[] { new object() };
+        }
+
+        private static IEnumerable<object[]> 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<object[]> 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<object[]> 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<ArgumentException>(() => 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<NullReferenceException>(() => StringType.StrLike(source, pattern, CompareMethod.Binary));
+            Assert.Throws<NullReferenceException>(() => StringType.StrLike(source, pattern, CompareMethod.Text));
+            Assert.Throws<NullReferenceException>(() => StringType.StrLikeBinary(source, pattern));
+            Assert.Throws<NullReferenceException>(() => StringType.StrLikeText(source, pattern));
+        }
+    }
+}