' Classes/System/String.ab #require #require #require #ifdef __STRING_IS_NOT_ALWAYS_UNICODE #ifndef UNICODE TypeDef StrChar = Char #define __STRING_IS_NOT_UNICODE #endif #endif #ifndef __STRING_IS_NOT_UNICODE TypeDef StrChar = WCHAR #ifdef UNICODE #define __STRING_IS_UNICODE #else #define __STRING_UNICODE_WINDOWS_ANSI #endif #endif Namespace System Class String ' Inherits IComparable, ICloneable, IConvertible, IEnumerable m_Length As Long Chars As *StrChar Sub validPointerCheck(p As VoidPtr, size = 1 As Long) If p As ULONG_PTR < &h10000 Then 'Throw ArgumentException Debug ElseIf IsBadReadPtr(p, size As ULONG_PTR) Then 'Throw ArgumentException Debug End If End Sub Public Static Const Empty = New String Sub String() ' Chars = 0 ' m_Length = 0 End Sub Sub String(initStr As PCWSTR) validPointerCheck(initStr) Assign(initStr, lstrlenW(initStr)) End Sub Sub String(initStr As PCWSTR, length As Long) validPointerCheck(initStr, length) Assign(initStr, length) End Sub Sub String(initStr As PCWSTR, start As Long, length As Long) If start < 0 Or length Or start + length < 0 Then Throw New ArgumentOutOfRangeException("String constractor: One or more arguments are out of range value.", "start or length or both") End If validPointerCheck(initStr + start, length) Assign(initStr + start, length) End Sub Sub String(initStr As PCSTR) validPointerCheck(initStr) Assign(initStr, lstrlenA(initStr)) End Sub Sub String(initStr As PCSTR, length As Long) validPointerCheck(initStr, length) Assign(initStr, length) End Sub Sub String(initStr As PCSTR, start As Long, length As Long) If start < 0 Or length < 0 Then Throw New ArgumentOutOfRangeException("String constructor: One or more arguments are out of range value.", "start or length or both") End If validPointerCheck(initStr + start, length) Assign(initStr + start, length) End Sub Sub String(initStr As String) If Not String.IsNullOrEmpty(initStr) Then Assign(initStr.Chars, initStr.m_Length) End If End Sub Sub String(initChar As StrChar, length As Long) AllocStringBuffer(length) ActiveBasic.Strings.ChrFill(Chars, length, initChar) Chars[length] = 0 End Sub Sub String(sb As Text.StringBuilder) Chars = StrPtr(sb) m_Length = sb.Length sb.__Stringized() End Sub Const Function Length() As Long Return m_Length End Function Function Operator() As *StrChar Return Chars End Function Const Function Operator [] (n As Long) As StrChar rangeCheck(n) Return Chars[n] End Function Const Function Operator + (y As PCSTR) As String If y = 0 Then Return This Else Return Concat(y, lstrlenA(y)) End If End Function Const Function Operator + (y As PCWSTR) As String If y = 0 Then Return This Else Return Concat(y, lstrlenW(y)) End If End Function Const Function Operator + (y As String) As String If ActiveBasic.IsNothing(y) Then Return This Else Return Concat(y.Chars, y.m_Length) End If End Function Const Function Operator & (y As PCSTR) As String Return This + y End Function Const Function Operator & (y As PCWSTR) As String Return This + y End Function Const Function Operator & (y As String) As String Return This + y End Function Const Function Operator == (y As String) As Boolean Return String.Compare(This, y) = 0 End Function Const Function Operator == (y As *StrChar) As Boolean Return String.Compare(This, y) = 0 End Function Const Function Operator <> (y As String) As Boolean Return String.Compare(This, y) <> 0 End Function Const Function Operator <> (y As *StrChar) As Boolean Return String.Compare(This, y) <> 0 End Function Const Function Operator < (y As String) As Boolean Return String.Compare(This, y) < 0 End Function Const Function Operator < (y As *StrChar) As Boolean Return String.Compare(This, y) < 0 End Function Const Function Operator > (y As String) As Boolean Return String.Compare(This, y) > 0 End Function Const Function Operator > (y As *StrChar) As Boolean Return String.Compare(This, y) > 0 End Function Const Function Operator <= (y As String) As Boolean Return String.Compare(This, y) <= 0 End Function Const Function Operator <= (y As *StrChar) As Boolean Return String.Compare(This, y) <= 0 End Function Const Function Operator >= (y As String) As Boolean Return String.Compare(This, y) >= 0 End Function Const Function Operator >= (y As *StrChar) As Boolean Return String.Compare(This, y) >= 0 End Function Static Function Compare(x As String, y As String) As Long Return CompareOrdinal(x, y) End Function Public Static Function Compare(x As String, indexX As Long, y As String, indexY As Long, length As Long) As Long Return String.CompareOrdinal(x, indexX, y, indexY, length) End Function Static Function CompareOrdinal(x As String, y As String) As Long Return String.CompareOrdinal(x.Chars, y.Chars) End Function Static Function CompareOrdinal(x As String, indexX As Long, y As String, indexY As Long, length As Long) As Long Return String.CompareOrdinal(x.Chars, indexX, y.Chars, indexY, length) End Function Private Static Function Compare(x As String, y As *StrChar) As Long Return String.CompareOrdinal(x, y) End Function Static Function CompareOrdinal(x As String, y As *StrChar) As Long Return String.CompareOrdinal(x.Chars, y) End Function Static Function CompareOrdinal(x As *StrChar, y As *StrChar) As Long If x = 0 Then If y = 0 Then Return 0 Else Return -1 End If ElseIf y = 0 Then Return 1 End If Return ActiveBasic.Strings.StrCmp(x, y) End Function Static Function CompareOrdinal(x As *StrChar, indexX As Long, y As *StrChar, indexY As Long, length As Long) As Long If x = 0 Then If y = 0 Then Return 0 Else Return -1 End If ElseIf y = 0 Then Return 1 End If Return ActiveBasic.Strings.ChrCmp(VarPtr(x[indexX]), VarPtr(y[indexY]), length As SIZE_T) End Function Public Function CompareTo(y As String) As Long Return String.Compare(This, y) End Function Function CompareTo(y As Object) As Long If Not Object.Equals(This.GetType(), y.GetType()) Then Throw New ArgumentException("String.CompareTo: An argument is out of range value.", "y") End If Return CompareTo(y As String) End Function Function Equals(s As String) As Boolean Return This = s End Function Override Function Equals(s As Object) As Boolean If Object.Equals( This.GetType(), s.GetType() ) Then Return This.Equals(s As String) End If Return False End Function Const Function StrPtr() As *StrChar Return Chars End Function Private Sub Assign(text As PCSTR, textLengthA As Long) #ifdef __STRING_IS_NOT_UNICODE AssignFromStrChar(text, textLengthA) #else Dim textLengthW = MultiByteToWideChar(CP_THREAD_ACP, 0, text, textLengthA, 0, 0) If AllocStringBuffer(textLengthW) <> 0 Then MultiByteToWideChar(CP_THREAD_ACP, 0, text, textLengthA, Chars, textLengthW) Chars[textLengthW] = 0 End If #endif End Sub Sub Assign(text As PCWSTR, textLengthW As Long) #ifdef __STRING_IS_NOT_UNICODE Dim textLengthA = WideCharToMultiByte(CP_THREAD_ACP, 0, text, textLengthW, 0, 0, 0, 0) If AllocStringBuffer(textLengthA) <> 0 Then WideCharToMultiByte(CP_THREAD_ACP, 0, text, textLengthW, Chars, textLengthA, 0, 0) Chars[textLengthA] = 0 End If #else AssignFromStrChar(text, textLengthW) #endif End Sub Private Static Function ConcatStrChar(text1 As *StrChar, text1Length As Long, text2 As *StrChar, text2Length As Long) As String ConcatStrChar = New String() With ConcatStrChar .AllocStringBuffer(text1Length + text2Length) ActiveBasic.Strings.ChrCopy(.Chars, text1, text1Length As SIZE_T) ActiveBasic.Strings.ChrCopy(VarPtr(.Chars[text1Length]), text2, text2Length As SIZE_T) .Chars[text1Length + text2Length] = 0 End With End Function Public Const Function Concat(text As PCSTR, len As Long) As String #ifdef __STRING_IS_NOT_UNICODE Return ConcatStrChar(This.Chars, m_Length, text, len) #else With Concat Dim lenW = MultiByteToWideChar(CP_THREAD_ACP, 0, text, len, 0, 0) Concat = New String .AllocStringBuffer(m_Length + lenW) ActiveBasic.Strings.ChrCopy(.Chars, This.Chars, m_Length As SIZE_T) MultiByteToWideChar(CP_THREAD_ACP, 0, text, len, VarPtr(.Chars[m_Length]), lenW) .Chars[m_Length + lenW] = 0 End With #endif End Function Const Function Concat(text As PCWSTR, len As Long) As String #ifdef __STRING_IS_NOT_UNICODE With Concat Concat = New String Dim lenA = WideCharToMultiByte(CP_THREAD_ACP, 0, text, len, 0, 0, 0, 0) .AllocStringBuffer(m_Length + lenA) ActiveBasic.Strings.ChrCopy(.Chars, This.Chars, m_Length As SIZE_T) WideCharToMultiByte(CP_THREAD_ACP, 0, text, len, VarPtr(.Chars[m_Length]), lenA, 0, 0) .Chars[m_Length + lenA] = 0 End With #else Return ConcatStrChar(This.Chars, m_Length, text, len) #endif End Function Static Function Concat(x As String, y As String) As String If String.IsNullOrEmpty(x) Then Return y Else Return x.Concat(y.Chars, y.m_Length) End If End Function Static Function Concat(x As Object, y As Object) As String Return String.Concat(x.ToString, y.ToString) End Function Const Function Contains(c As StrChar) As Boolean Return IndexOf(c) >= 0 End Function Const Function Contains(s As String) As Boolean If Object.ReferenceEquals(s, Nothing) Then Throw New ArgumentNullException("String.Contains: An argument is null value.", "s") ElseIf s = "" Then Return True Else Return IndexOf(s, 0, m_Length) >= 0 End If End Function Const Function IndexOf(c As StrChar) As Long Return indexOfCore(c, 0, m_Length) End Function Const Function IndexOf(c As StrChar, start As Long) As Long rangeCheck(start) Return indexOfCore(c, start, m_Length - start) End Function Const Function IndexOf(c As StrChar, start As Long, count As Long) As Long rangeCheck(start, count) Return indexOfCore(c, start, count) End Function Private Const Function indexOfCore(c As StrChar, start As Long, count As Long) As Long indexOfCore = ActiveBasic.Strings.ChrFind(VarPtr(Chars[start]), count, c) As Long If indexOfCore <> -1 Then indexOfCore += start End If End Function Public Const Function IndexOf(s As String) As Long Return IndexOf(s, 0, m_Length) End Function Const Function IndexOf(s As String, startIndex As Long) As Long Return IndexOf(s, startIndex, m_Length - startIndex) End Function Const Function IndexOf(s As String, startIndex As Long, count As Long) As Long rangeCheck(startIndex, count) If Object.ReferenceEquals(s, Nothing) Then Throw New ArgumentNullException("String.IndexOf: An argument is out of range value.", "s") End If Dim length = s.Length If length = 0 Then Return startIndex Dim i As Long, j As Long For i = startIndex To startIndex + count - 1 For j = 0 To length - 1 If Chars[i + j] = s[j] Then If j = length - 1 Then Return i Else Exit For End If Next Next Return -1 End Function Const Function LastIndexOf(c As StrChar) As Long Return lastIndexOf(c, m_Length - 1, m_Length) End Function Const Function LastIndexOf(c As StrChar, start As Long) As Long rangeCheck(start) Return lastIndexOf(c, start, start + 1) End Function Const Function LastIndexOf(c As StrChar, start As Long, count As Long) As Long rangeCheck(start) Dim lastFindPos = start - (count - 1) If Not (m_Length > lastFindPos And lastFindPos >= 0) Then Throw New ArgumentOutOfRangeException("String.LastIndexOf: An argument is out of range value.", "count") End If Return lastIndexOf(c, start, count) End Function Private Const Function lastIndexOf(c As StrChar, start As Long, count As Long) As Long Dim lastFindPos = start - (count - 1) Dim i As Long For i = start To lastFindPos Step -1 If Chars[i] = c Then Return i End If Next Return -1 End Function Public Const Function LastIndexOf(s As String) As Long Return LastIndexOf(s, m_Length - 1, m_Length) End Function Const Function LastIndexOf(s As String, startIndex As Long) As Long Return LastIndexOf(s, startIndex, startIndex + 1) End Function Const Function LastIndexOf(s As String, start As Long, count As Long) As Long If Object.ReferenceEquals(s, Nothing) Then Throw New ArgumentNullException("String.LastIndexOf: An argument is out of range value.", "s") End If If start < 0 Or start > m_Length - 1 Or _ count < 0 Or count > start + 2 Then Throw New ArgumentOutOfRangeException("String.LastIndexOf: One or more arguments are out of range value.", "start or count or both") End If Dim length = s.m_Length If length > m_Length Then Return -1 If length = 0 Then Return start Dim i As Long, j As Long For i = start To start - count + 1 Step -1 For j = length - 1 To 0 Step -1 If Chars[i + j] = s[j] Then If j = 0 Then Return i Else Exit For End If Next Next Return -1 End Function Const Function StartsWith(c As StrChar) As Boolean Return IndexOf(c) = 0 End Function Const Function StartsWith(s As String) As Boolean Return IndexOf(s) = 0 End Function Const Function EndsWith(c As StrChar) As Boolean Return LastIndexOf(c) = m_Length - 1 End Function Const Function EndsWith(s As String) As Boolean Return LastIndexOf(s) = m_Length - s.Length End Function Const Function Insert(startIndex As Long, text As String) As String Dim sb = New Text.StringBuilder(This) sb.Insert(startIndex, text) Return sb.ToString End Function Const Function Substring(startIndex As Long) As String rangeCheck(startIndex) Return Substring(startIndex, m_Length - startIndex) End Function Const Function Substring(startIndex As Long, length As Long) As String rangeCheck(startIndex, length) Return New String(Chars, startIndex, length) End Function Const Function Remove(startIndex As Long) As String rangeCheck(startIndex) Remove = Substring(0, startIndex) End Function Const Function Remove(startIndex As Long, count As Long) As String Dim sb = New Text.StringBuilder(This) sb.Remove(startIndex, count) Remove = sb.ToString End Function Static Function IsNullOrEmpty(s As String) As Boolean If Not Object.ReferenceEquals(s, Nothing) Then If s.m_Length > 0 Then Return False End If End If Return True End Function Const Function Replace(oldChar As StrChar, newChar As StrChar) As String Dim sb = New Text.StringBuilder(This) sb.Replace(oldChar, newChar) Replace = sb.ToString End Function Const Function Replace(oldStr As String, newStr As String) As String Dim sb = New Text.StringBuilder(This) sb.Replace(oldStr, newStr) Return sb.ToString End Function Const Function ToLower() As String Dim sb = New Text.StringBuilder(m_Length) sb.Length = m_Length Dim i As Long For i = 0 To ELM(m_Length) sb[i] = ActiveBasic.CType.ToLower(Chars[i]) Next Return sb.ToString End Function Const Function ToUpper() As String Dim sb = New Text.StringBuilder(m_Length) sb.Length = m_Length Dim i As Long For i = 0 To ELM(m_Length) sb[i] = ActiveBasic.CType.ToUpper(Chars[i]) Next Return sb.ToString End Function Override Function ToString() As String ToString = This End Function Const Function Clone() As String Clone = This End Function Static Function Copy(s As String) As String Copy = New String(s.Chars, s.m_Length) End Function Sub CopyTo(sourceIndex As Long, destination As *StrChar, destinationIndex As Long, count As Long) ActiveBasic.Strings.ChrCopy(VarPtr(destination[destinationIndex]), VarPtr(Chars[sourceIndex]), count As SIZE_T) End Sub Override Function GetHashCode() As Long #ifdef __STRING_IS_NOT_UNICODE Dim size = (m_Length + 1) >> 1 #else Dim size = m_Length #endif Return _System_GetHashFromWordArray(Chars As *Word, size) Xor m_Length End Function Function PadLeft(total As Long) As String PadLeft(total, &h30 As StrChar) End Function Function PadLeft(total As Long, c As StrChar) As String If total < 0 Then Throw New ArgumentOutOfRangeException("String.PadLeft: An arguments is out of range value.", "total") End If If total >= m_Length Then Return This End If Dim sb = New Text.StringBuilder(total) sb.Append(c, total - m_Length) sb.Append(This) Return sb.ToString End Function Function PadRight(total As Long) As String PadRight(total, &h30 As StrChar) End Function Function PadRight(total As Long, c As StrChar) As String If total < 0 Then Throw New ArgumentOutOfRangeException("String.PadRight: An arguments is out of range value.", "total") End If If total >= m_Length Then Return This End If Dim sb = New Text.StringBuilder(total) sb.Append(This) sb.Append(c, total - m_Length) Return sb.ToString End Function Private Function AllocStringBuffer(textLength As Long) As *StrChar If textLength < 0 Then Return 0 End If AllocStringBuffer = GC_malloc_atomic(SizeOf(StrChar) * (textLength + 1)) If AllocStringBuffer = 0 Then 'Throw New OutOfMemoryException End If m_Length = textLength Chars = AllocStringBuffer End Function Sub AssignFromStrChar(text As *StrChar, textLength As Long) AllocStringBuffer(textLength) ActiveBasic.Strings.ChrCopy(Chars, text, textLength As SIZE_T) Chars[m_Length] = 0 End Sub Const Sub rangeCheck(index As Long) If index < 0 Or index > m_Length Then Throw New ArgumentOutOfRangeException("String: An arguments is out of range value.", "index") End If End Sub Const Sub rangeCheck(start As Long, length As Long) If start < 0 Or start > This.m_Length Or length < 0 Then Throw New ArgumentOutOfRangeException("String: One or more arguments are out of range value.", "start or length or both") End If End Sub End Class End Namespace