using System; using System.Globalization; using System.Text; using Cosmos.Common; using Cosmos.IL2CPU.Plugs; using Debugger = Cosmos.Debug.Kernel.Debugger; namespace Cosmos.Core.Plugs.System { [Plug(Target = typeof(string), IsMicrosoftdotNETOnly = true)] public static class StringImpl { internal static Debugger mDebugger = new Debugger("Core", "String Plugs"); public static unsafe void Ctor( string aThis, char* aChars, [FieldAccess(Name = "System.String System.String.Empty")] ref string aStringEmpty, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aStringLength, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { aStringEmpty = ""; while (*aChars != '\0') { aChars++; aStringLength++; } for (int i = 0; i < aStringLength; i++) { aFirstChar[i] = aChars[i]; } } public static unsafe void Ctor( string aThis, char* aChars, int start, int length, [FieldAccess(Name = "System.String System.String.Empty")] ref string aStringEmpty, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aStringLength, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { aStringEmpty = ""; aStringLength = length; for (int i = 0; i < length; i++) { aFirstChar[i] = aChars[start + i]; } } public static unsafe void Ctor( string aThis, char[] aChars, [FieldAccess(Name = "System.String System.String.Empty")] ref string aStringEmpty, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aStringLength, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { aStringEmpty = ""; aStringLength = aChars.Length; for (int i = 0; i < aChars.Length; i++) { aFirstChar[i] = aChars[i]; } } public static unsafe void Ctor( string aThis, char[] aChars, int start, int length, [FieldAccess(Name = "System.String System.String.Empty")] ref string aStringEmpty, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aStringLength, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { aStringEmpty = ""; aStringLength = length; for (int i = 0; i < length; i++) { aFirstChar[i] = aChars[start + i]; } } public static unsafe void Ctor( string aThis, char aChar, int aLength, [FieldAccess(Name = "System.String System.String.Empty")] ref string aStringEmpty, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aStringLength, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { aStringEmpty = ""; aStringLength = aLength; for (int i = 0; i < aLength; i++) { aFirstChar[i] = aChar; } } public static unsafe int get_Length( int* aThis, [FieldAccess(Name = "System.Int32 System.String.m_stringLength")] ref int aLength) { return aLength; } public static unsafe char get_Chars( uint* aThis, int aIndex, [FieldAccess(Name = "System.Char System.String.m_firstChar")] char* aFirstChar) { return *(aFirstChar + aIndex); } public static bool IsAscii(string aThis) { for (int i = 0; i < aThis.Length; i++) { if (aThis[i] >= 0x80) { return false; } } return true; } public static string Format(string aFormat, object aArg0) { if (aArg0 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(null, aFormat, aArg0); } public static string Format(string aFormat, object aArg0, object aArg1) { if (aArg0 == null || aArg1 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(null, aFormat, aArg0, aArg1); } public static string Format(string aFormat, object aArg0, object aArg1, object aArg2) { if (aArg0 == null || aArg1 == null || aArg2 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(null, aFormat, aArg0, aArg1, aArg2); } public static string Format(string aFormat, params object[] aArgs) { if (aArgs == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(null, aFormat, aArgs); } public static string Format(IFormatProvider aProvider, string aFormat, object aArg0) { if (aArg0 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(aProvider, aFormat, aArg0); } public static string Format(IFormatProvider aProvider, string aFormat, object aArg0, object aArg1) { if (aArg0 == null | aArg1 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(aProvider, aFormat, aArg0, aArg1); } public static string Format(IFormatProvider aProvider, string aFormat, object aArg0, object aArg1, object aArg2) { if (aArg0 == null | aArg1 == null || aArg2 == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(aProvider, aFormat, aArg0, aArg1, aArg2); } public static string Format(IFormatProvider aProvider, string aFormat, params object[] aArgs) { if (aArgs == null) { throw new ArgumentNullException(aFormat == null ? "aFormat" : "aArgs"); } return FormatHelper(aProvider, aFormat, aArgs); } internal static string FormatHelper(IFormatProvider aFormatProvider, string aFormat, params object[] aArgs) { char[] xCharArray = aFormat.ToCharArray(); string xFormattedString = string.Empty, xStaticString; bool xFoundPlaceholder = false, xParamNumberDone = true; int xStartParamNumber = -1, xEndParamNumber = -1, xLastPlaceHolder = 0; for (int i = 0; i < xCharArray.Length; i++) { if (xFoundPlaceholder) { if (xCharArray[i] == '{') { throw new FormatException("The format string provided is invalid."); } if (xCharArray[i] == '}') { mDebugger.SendInternal("Found closing placeholder."); if (xEndParamNumber < 0) { xEndParamNumber = i; } string xParamNumber = aFormat.Substring(xStartParamNumber, xEndParamNumber - xStartParamNumber); mDebugger.SendInternal("Calling StringHelper.GetStringToNumber"); mDebugger.SendInternal(xParamNumber); int xParamIndex = StringHelper.GetStringToNumber(xParamNumber); mDebugger.SendInternal("Converted paramindex to a number."); if ((xParamIndex < aArgs.Length) && (aArgs[xParamIndex] != null)) { string xParamValue = aArgs[xParamIndex].ToString(); xFormattedString = string.Concat(xFormattedString, xParamValue); } xFoundPlaceholder = false; xParamNumberDone = true; xStartParamNumber = -1; xEndParamNumber = -1; xLastPlaceHolder = i + 1; } else if (xCharArray[i] == ':') { xParamNumberDone = true; xEndParamNumber = i; // TODO: Need to handle different formats. (X, N, etc) } else if ((char.IsDigit(xCharArray[i])) && (!xParamNumberDone)) { mDebugger.SendInternal("Getting param number."); if (xStartParamNumber < 0) { xStartParamNumber = i; } } } else if (xCharArray[i] == '{') { mDebugger.SendInternal("Found opening placeholder"); xStaticString = aFormat.Substring(xLastPlaceHolder, i - xLastPlaceHolder); xFormattedString = string.Concat(xFormattedString, xStaticString); xFoundPlaceholder = true; xParamNumberDone = false; } } xStaticString = aFormat.Substring(xLastPlaceHolder, aFormat.Length - xLastPlaceHolder); xFormattedString = string.Concat(xFormattedString, xStaticString); return xFormattedString; } public static bool StartsWith(string aThis, string aSubstring, StringComparison aComparison) { Char[] di = aThis.ToCharArray(); Char[] ci = aSubstring.ToCharArray(); if (aSubstring.Length > aThis.Length) { return false; } for (int i = 0; i < ci.Length; i++) { if (di[i] != ci[i]) { return false; } } return true; } public static string PadHelper(string aThis, int totalWidth, char paddingChar, bool isRightPadded) { var cs = new char[totalWidth]; int pos = aThis.Length; if (isRightPadded) { for (int i = 0; i < aThis.Length; i++) { cs[i] = aThis[i]; } for (int i = aThis.Length; i < totalWidth; i++) { cs[i] = paddingChar; } } else { int offset = totalWidth - aThis.Length; for (int i = 0; i < aThis.Length; i++) { cs[i + offset] = aThis[i]; } for (int i = 0; i < offset; i++) { cs[i] = paddingChar; } } return new string(cs); } public static string Replace(string aThis, char oldValue, char newValue) { var cs = new char[aThis.Length]; for (int i = 0; i < aThis.Length; i++) { if (aThis[i] != oldValue) { cs[i] = aThis[i]; } else { cs[i] = newValue; } } return new string(cs); } // HACK: We need to redo this once char support is complete (only returns 0, -1). public static int CompareTo(string aThis, string other) { if (aThis.Length != other.Length) { return -1; } for (int i = 0; i < aThis.Length; i++) { if (aThis[i] != other[i]) { return -1; } } return 0; } public static int IndexOf(string aThis, char value, int startIndex, int count) { int xEndIndex = aThis.Length; if (startIndex + count < xEndIndex) { xEndIndex = startIndex + count; } for (int i = startIndex; i < xEndIndex; i++) { if (aThis[i] == value) { return i; } } return -1; } // HACK: TODO - improve efficiency of this. //How do we access the raw memory to copy it into a char array? public static char[] ToCharArray(string aThis) { var result = new char[aThis.Length]; for (int i = 0; i < aThis.Length; i++) { result[i] = aThis[i]; } return result; } [PlugMethod(Enabled = false)] public static uint GetStorage(string aString) { return 0; } //[PlugMethod(Enabled = false)] //public static char[] GetStorageArray(string aString) { // return null; //} private static int[] BuildBadCharTable(char[] needle) { var badShift = new int[256]; for (int i = 0; i < 256; i++) { badShift[i] = needle.Length; } int last = needle.Length - 1; for (int i = 0; i < last; i++) { badShift[needle[i]] = last - i; } return badShift; } private static int boyerMooreHorsepool(string pattern, string text) { var needle = pattern.ToCharArray(); var haystack = text.ToCharArray(); if (needle.Length > haystack.Length) { return -1; } var badShift = BuildBadCharTable(needle); int offset = 0; int scan = 0; int last = needle.Length - 1; int maxoffset = haystack.Length - needle.Length; while (offset <= maxoffset) { for (scan = last; needle[scan] == haystack[scan + offset]; scan--) { if (scan == 0) { //Match found return offset; } } offset += badShift[haystack[offset + last]]; } return -1; } public static int IndexOf(string aThis, string aSubstring, int aIdx, int aLength, StringComparison aComparison) { return boyerMooreHorsepool(aSubstring, aThis.Substring(aIdx, aLength)); } //private static void WriteNumber(uint aValue, // byte aBitCount) //{ // uint xValue = aValue; // byte xCurrentBits = aBitCount; // Console.Write("0x"); // while (xCurrentBits >= 4) // { // xCurrentBits -= 4; // byte xCurrentDigit = (byte)((xValue >> xCurrentBits) & 0xF); // string xDigitString = null; // switch (xCurrentDigit) // { // case 0: // xDigitString = "0"; // goto default; // case 1: // xDigitString = "1"; // goto default; // case 2: // xDigitString = "2"; // goto default; // case 3: // xDigitString = "3"; // goto default; // case 4: // xDigitString = "4"; // goto default; // case 5: // xDigitString = "5"; // goto default; // case 6: // xDigitString = "6"; // goto default; // case 7: // xDigitString = "7"; // goto default; // case 8: // xDigitString = "8"; // goto default; // case 9: // xDigitString = "9"; // goto default; // case 10: // xDigitString = "A"; // goto default; // case 11: // xDigitString = "B"; // goto default; // case 12: // xDigitString = "C"; // goto default; // case 13: // xDigitString = "D"; // goto default; // case 14: // xDigitString = "E"; // goto default; // case 15: // xDigitString = "F"; // goto default; // default: // Console.Write(xDigitString); // break; // } // } //} public static bool Contains(string aThis, string value) { Char[] di = aThis.ToCharArray(); Char[] ci = value.ToCharArray(); if (value.Length == aThis.Length) { if (value == aThis) { return true; } else { return false; } } else if (!(value.Length > aThis.Length) && (value.Length != aThis.Length)) { for (int i = 0; i < aThis.Length; i++) { if (di[i] == ci[0]) { for (int j = 1; j < value.Length; j++) { if (di[i + j] != ci[j]) { return false; } } return true; } } } return false; } public static bool EndsWith(string aThis, string aSubStr, bool aIgnoreCase, CultureInfo aCulture) { return EndsWith(aThis, aSubStr, StringComparison.CurrentCulture); } public static bool EndsWith(string aThis, string aSubStr, StringComparison aComparison) { Char[] di = aThis.ToCharArray(); Char[] ci = aSubStr.ToCharArray(); if (aThis.Length == aSubStr.Length) { if (aThis == aSubStr) { return true; } return false; } else if (aThis.Length > aSubStr.Length) { return false; } else { for (int i = aThis.Length - aSubStr.Length; i < aThis.Length; i++) { if (di[aThis.Length - aSubStr.Length + i] != ci[i]) { return false; } } return true; } } // System.Int32 System.String.IndexOf(System.String, System.Int32, System.Int32, System.StringComparison) public static bool Equals(string aThis, string aThat, StringComparison aComparison) { #warning TODO: implement if (aComparison == StringComparison.OrdinalIgnoreCase) { string xLowerThis = aThis.ToLower(); string xLowerThat = aThat.ToLower(); return EqualsHelper(xLowerThis, xLowerThat); } return EqualsHelper(aThis, aThat); } public static bool EqualsHelper(string aStrA, string aStrB) { return aStrA.CompareTo(aStrB) == 0; } private static bool CharArrayContainsChar(char[] aArray, char aChar) { for (int i = 0; i < aArray.Length; i++) { if (aArray[i] == aChar) { return true; } } return false; } public static int IndexOf(string aThis, string aValue) { return aThis.IndexOf(aValue, 0, aThis.Length, StringComparison.CurrentCulture); } public static int IndexOfAny(string aThis, char[] aSeparators, int aStartIndex, int aLength) { if (aSeparators == null) { throw new ArgumentNullException("aSeparators"); } int xResult = -1; for (int i = 0; i < aSeparators.Length; i++) { int xValue = IndexOf(aThis, aSeparators[i], aStartIndex, aLength); if (xValue < xResult || xResult == -1) { xResult = xValue; } } return xResult; } public static string Insert(string aThis, int aStartPos, string aValue) { return aThis.Substring(0, aStartPos) + aValue + aThis.Substring(aStartPos); } public static int LastIndexOf(string aThis, char aChar, int aStartIndex, int aCount) { return LastIndexOfAny(aThis, new[] { aChar }, aStartIndex, aCount); } public static int LastIndexOfAny(string aThis, char[] aChars, int aStartIndex, int aCount) { for (int i = 0; i < aCount; i++) { if (CharArrayContainsChar(aChars, aThis[aStartIndex - i])) { return aStartIndex - i; } } return -1; } public static int nativeCompareOrdinalEx(string aStrA, int aIndexA, string aStrB, int aIndexB, int aCount) { //mDebugger.SendInternal($"nativeCompareOrdinalEx : aStrA|aIndexA = {aStrA}|{aIndexA}, aStrB|aIndexB = {aStrB}|{aIndexB}, aCount = {aCount}"); if (aCount < 0) { throw new ArgumentOutOfRangeException("aCount"); } if (aIndexA < 0 || aIndexA > aStrA.Length) { throw new ArgumentOutOfRangeException("aIndexA"); } if (aIndexB < 0 || aIndexB > aStrB.Length) { throw new ArgumentOutOfRangeException("aIndexB"); } if (aStrA == null) { mDebugger.SendInternal("nativeCompareOrdinalEx : aStrA is null"); if (aStrB == null) { mDebugger.SendInternal($"nativeCompareOrdinalEx : aStrB is null"); mDebugger.SendInternal($"nativeCompareOrdinalEx : returning 0"); return 0; } mDebugger.SendInternal($"nativeCompareOrdinalEx : aStrB is not null"); mDebugger.SendInternal($"nativeCompareOrdinalEx : returning -1"); return -1; } if (aStrB == null) { mDebugger.SendInternal("nativeCompareOrdinalEx : aStrA is not null"); mDebugger.SendInternal($"nativeCompareOrdinalEx : aStrB is null"); mDebugger.SendInternal($"nativeCompareOrdinalEx : returning 1"); return 1; } int xLengthA = Math.Min(aStrA.Length, aCount - aIndexA); int xLengthB = Math.Min(aStrB.Length, aCount - aIndexB); //mDebugger.SendInternal($"nativeCompareOrdinalEx : xLengthA = {xLengthA}"); //mDebugger.SendInternal($"nativeCompareOrdinalEx : xLengthB = {xLengthB}"); if (xLengthA == xLengthB && aIndexA == aIndexB && ReferenceEquals(aStrA, aStrB)) { mDebugger.SendInternal("nativeCompareOrdinalEx : xLengthA == xLengthB && aIndexA == aIndexB && aStrA is the same object asaStrB, returning 0"); return 0; } int xResult = 0; if (xLengthA != xLengthB) { xResult = xLengthA - xLengthB; mDebugger.SendInternal("nativeCompareOrdinalEx : xLengthA != xLengthB, returning " + xResult); } for (int i = 0; i < xLengthA; i++) { if (aStrA != aStrB) { xResult = (byte)aStrA[i] - (byte)aStrB[i]; mDebugger.SendInternal("nativeCompareOrdinalEx : aStrA[i] != aStrB[i], returning " + xResult); return xResult; } } return xResult; } public static bool StartsWith(string aThis, string aSubStr, bool aIgnoreCase, CultureInfo aCulture) { Char[] di = aThis.ToCharArray(); Char[] ci = aSubStr.ToCharArray(); if (aSubStr.Length > aThis.Length) { return false; } for (int i = 0; i < ci.Length; i++) { if (di[i] != ci[i]) { return false; } } return true; } public static string Remove(string aThis, int aStart, int aCount) { return aThis.Substring(0, aStart) + aThis.Substring(aStart + aCount, aThis.Length - (aStart + aCount)); } public static string Replace(string aThis, string oldValue, string newValue) { while (aThis.IndexOf(oldValue) != -1) { int xIndex = aThis.IndexOf(oldValue); aThis = aThis.Remove(xIndex, oldValue.Length); aThis = aThis.Insert(xIndex, newValue); } return aThis; } public static string ToLower(string aThis, CultureInfo aCulture) { return ChangeCasing(aThis, 65, 90, 32); } public static string ToUpper(string aThis, CultureInfo aCulture) { return ChangeCasing(aThis, 97, 122, -32); } public static string ToUpper(string aThis) { return ChangeCasing(aThis, 97, 122, -32); } private static string ChangeCasing(string aValue, int lowerAscii, int upperAscii, int offset) { var xChars = new char[aValue.Length]; for (int i = 0; i < aValue.Length; i++) { int xAsciiCode = aValue[i]; if ((xAsciiCode <= upperAscii) && (xAsciiCode >= lowerAscii)) { xChars[i] = (char)(xAsciiCode + offset); } else { xChars[i] = aValue[i]; } } return new string(xChars); } public static string ToString(string aThis) { return aThis; } public static string FastAllocateString(int length) { return new string(new char[length]); } public static string TrimStart(string aThis, string aSubStr) { char[] ci = aThis.ToCharArray(); char[] di = aSubStr.ToCharArray(); if (aThis.StartsWith(aSubStr)) { if (aThis != aSubStr) { char[] oi = new char[ci.Length - di.Length]; for (int i = 0; i < ci.Length - di.Length; i++) { oi[i] = ci[i + di.Length]; } return oi.ToString(); } return string.Empty; } throw new ArgumentNullException(); } public static int GetHashCode(ref String aThis) { throw new NotImplementedException("String.GetHashCode()"); } } }