using System; using Microsoft.SPOT; using System.Globalization; using IndianaJones.NETMF.String; namespace IndianaJones.NETMF.Integer { public enum NumberStyle { Decimal = 1, Hexadecimal } #region Private Static Helper Methods internal static class Helper { public const int MaxDoubleDigits = 16; public static void CheckSign(string str, ref int start, int end, ref bool hasSign, ref bool isNeg) { int counter; int current; string sign = CultureInfo.CurrentUICulture.NumberFormat.NegativeSign; char signChar = sign[0]; int signLength = sign.Length; char curChar = str[start]; // check for negative sign at the beginning if (curChar == signChar) { counter = 1; current = start + 1; ; if (signLength > 1) { while (counter < signLength && current <= end) { if (str[current] != sign[counter]) { break; } current++; counter++; } } if (counter >= signLength) { hasSign = true; isNeg = true; start = current; return; } } // check for positive sign at the beginning sign = CultureInfo.CurrentUICulture.NumberFormat.PositiveSign; signChar = sign[0]; signLength = sign.Length; if (curChar == signChar) { counter = 1; current = start + 1; if (signLength > 1) { while (counter < signLength && current <= end) { if (str[current] != sign[counter]) { break; } current++; counter++; } } if (counter >= signLength) { hasSign = true; start = current; } } } public static bool CheckSeparator(string str, string sep, ref int start, int end) { int strLength = sep.Length; if (strLength > 0) { char curChar = str[start]; char strChar = sep[0]; // check for first Character at the beginning if (curChar == strChar) { int counter = 1; int current = start + 1; while (counter < strLength && current <= end) { if (str[current] != sep[counter]) { break; } current++; counter++; } if (counter >= strLength) // string found { // so update to new start position start = current; return true; } } } return false; } public static void SkipWhiteSpace(string str, ref int start, ref int end) { while (start <= end && IsWhiteSpace(str[start])) { start++; } // remove trailing whitespaces if (start <= end) { while (start <= end && IsWhiteSpace(str[end])) { end--; } } } public static bool IsWhiteSpace(char ch) { return ch == ' '; } // parse the number beginning at str[start] up to maximal str[end]. // passed parameters: // str character array containing the data to parse // style and nfi are the formatspecs // start and end are the indexes of the first and last character to parse // maxDigits must not extend 18 else an overflow may occure // numdigits must be 0 // on exit start points to the first character after the last parsed digit. // end is unchanged. // numDigits is updated to the number of significant digits parsed. // if numDigits > maxDigits the value has to be calculated // returnvalue * Math.Pof(10, (numDigits - maxDigits)). public static ulong ParseNumberCore(string str, ref int start, int end, int maxDigits, ref int numDigits, bool allowGroupSep) { char curChar; ulong ulwork = 0; // now parse the real number string sep = CultureInfo.CurrentUICulture.NumberFormat.NumberGroupSeparator; while (start <= end) { curChar = str[start]; if (curChar >= '0' && curChar <= '9') { if (numDigits < maxDigits) { ulwork = ulwork * 10 + unchecked((uint)(curChar - '0')); } start++; numDigits++; } else { // check for groupseparator if allowed if (!allowGroupSep || !CheckSeparator(str, sep, ref start, end)) { break; } } } return ulwork; } // Parse integer values using localized number format information. public static bool TryParseUInt64Core(string str, bool parseHex, out ulong result, out bool sign) { if (str == null) { throw new ArgumentNullException("str"); } // If number contains the Hex '0x' prefix, then make sure we're // managing a Hex number, and skip over the '0x' if (str.Substring(0, 2).ToLower() == "0x") { str = str.Substring(2); parseHex = true; } char ch; bool noOverflow = true; result = 0; // Skip leading white space. int len = str.Length; int posn = 0; while (posn < len && IsWhiteSpace(str[posn])) { posn++; } // Check for leading sign information. NumberFormatInfo nfi = CultureInfo.CurrentUICulture.NumberFormat; string posSign = nfi.PositiveSign; string negSign = nfi.NegativeSign; sign = false; while (posn < len) { ch = str[posn]; if (!parseHex && ch == negSign[0]) { sign = true; ++posn; } else if (!parseHex && ch == posSign[0]) { sign = false; ++posn; } /* else if (ch == thousandsSep[0]) { ++posn; }*/ else if ((parseHex && ((ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))) || (ch >= '0' && ch <= '9')) { break; } else { return false; } } // Bail out if the string is empty. if (posn >= len) { return false; } // Parse the main part of the number. uint low = 0; uint high = 0; uint digit; ulong tempa, tempb; if (parseHex) { #region Parse a hexadecimal value. do { // Get the next digit from the string. ch = str[posn]; if (ch >= '0' && ch <= '9') { digit = (uint)(ch - '0'); } else if (ch >= 'A' && ch <= 'F') { digit = (uint)(ch - 'A' + 10); } else if (ch >= 'a' && ch <= 'f') { digit = (uint)(ch - 'a' + 10); } else { break; } // Combine the digit with the result, and check for overflow. if (noOverflow) { tempa = ((ulong)low) * ((ulong)16); tempb = ((ulong)high) * ((ulong)16); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { tempa = (tempa & 0xFFFFFFFF) + ((ulong)digit); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { low = unchecked((uint)tempa); high = unchecked((uint)tempb); } } } ++posn; // Advance to the next character. } while (posn < len); #endregion } else { #region Parse a decimal value. do { // Get the next digit from the string. ch = str[posn]; if (ch >= '0' && ch <= '9') { digit = (uint)(ch - '0'); } /* else if (ch == thousandsSep[0]) { // Ignore thousands separators in the string. ++posn; continue; }*/ else { break; } // Combine the digit with the result, and check for overflow. if (noOverflow) { tempa = ((ulong)low) * ((ulong)10); tempb = ((ulong)high) * ((ulong)10); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { tempa = (tempa & 0xFFFFFFFF) + ((ulong)digit); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { low = unchecked((uint)tempa); high = unchecked((uint)tempb); } } } ++posn;// Advance to the next character. } while (posn < len); #endregion } // Process trailing white space. if (posn < len) { do { ch = str[posn]; if (IsWhiteSpace(ch)) ++posn; else break; } while (posn < len); if (posn < len) { return false; } } // Return the results to the caller. result = (((ulong)high) << 32) | ((ulong)low); return noOverflow; } } #endregion public static class DoubleExtensions { public static double Parse(string str) { double result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static bool TryParse(string str, out double result) { ulong decValue = 0; bool hasExpSign = false; bool isExpNeg = false; int expDigits = 0; ulong expValue = 0; result = 0; if (str == null) { throw new ArgumentNullException("str"); } int end = str.Length - 1; int start = 0; // skip whitespaces Helper.SkipWhiteSpace(str, ref start, ref end); // check for leading sign bool hasSign = false; bool isNeg = false; if (start <= end) { Helper.CheckSign(str, ref start, end, ref hasSign, ref isNeg); } // now parse the real number int intDigits = 0; ulong intValue = Helper.ParseNumberCore(str, ref start, end, Helper.MaxDoubleDigits, ref intDigits, true); int decDigits = 0; if (start <= end) { // now check for the decimal point and the decimalplaces if (Helper.CheckSeparator(str, CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ref start, end)) { if (start <= end) { decValue = Helper.ParseNumberCore(str, ref start, end, Helper.MaxDoubleDigits - intDigits, ref decDigits, false); } } } // now check for the exponent if (start <= end) { char curChar = str[start]; if (curChar == 'E' || curChar == 'e') { start++; if (start <= end) { // check for sign Helper.CheckSign(str, ref start, end, ref hasExpSign, ref isExpNeg); // get exponent if (start <= end) { expValue = Helper.ParseNumberCore(str, ref start, end, 5, ref expDigits, false); } if (expDigits <= 0) { return false; } } } } if (start <= end) // characters left { return false; } // now calculate the value result = (double)intValue; if (intDigits > Helper.MaxDoubleDigits) { result *= System.Math.Pow(10, (double)(intDigits - Helper.MaxDoubleDigits)); } if (decDigits > 0) { result += (decValue * System.Math.Pow(10d, (double)(-decDigits))); } if (isNeg) { result *= -1; } //now the exponent if (expDigits > 0) { if (isExpNeg) { result *= System.Math.Pow(10d, (double)expValue * -1); } else { result *= System.Math.Pow(10d, (double)expValue); } } return true; } /// /// Converts a Double to a string using a variant of the Double.ToString() method. /// The problem with the built-in ToString() method is that it wont automatically /// size the precision to fit the number. So a number like 33.3 gets truncated to /// just 33 unless you specify exactly the right amount of precision. This method /// attempts to determine the right amount of precision. /// CAUTION!!! I've seen many times when the built-in ToString() method returns /// a rounding error when you specify any meaningful precision. /// /// /// public static string ToString(double value) { // First, convert to string with impossibly-long precision string fullValue = value.ToString("F64"); // Find the last non-zero number in the mantissa int mantissaLen = 0; int decimalLen = 0; int i = fullValue.Length - 1; for(; i>= 0; i--) { if(fullValue[i] == '.') { // we made it all the way to the dot decimalLen = i; break; } // Stop counting the mantissa once you reach the last non-zero value in the number if(fullValue[i] != '0' && (mantissaLen == 0)) { mantissaLen = i; } } // If the entire mantissa was zero, then add 2 for the dot and one mantissa digit mantissaLen = (mantissaLen > 0) ? mantissaLen : 2; // Truncate the trailing zeros in the mantissa fullValue = fullValue.Substring(0, mantissaLen + 1); return fullValue; } public static string ToHexString(double value) { byte[] bytes = Microsoft.SPOT.Reflection.Serialize(value, typeof(double)); return new string(ToHexStringArray(bytes)); } //convert a hex number to a hex string public static char[] ToHexStringArray(byte[] bytes) { char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] chars = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { int b = bytes[i]; chars[i * 2] = hexDigits[b >> 4]; chars[i * 2 + 1] = hexDigits[b & 0xF]; } return chars; } } public static class Int16Extensions { public static Int16 Parse(string str) { Int16 result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static Int16 Parse(string str, NumberStyle style) { if (style == NumberStyle.Hexadecimal) { return ParseHex(str); } return Parse(str); } public static bool TryParse(string str, out Int16 result) { result = 0; ulong r; bool sign; if (Helper.TryParseUInt64Core(str, false, out r, out sign)) { if (!sign) { if (r <= 9223372036854775807) { result = unchecked((Int16)r); return true; } } else { if (r <= 9223372036854775808) { result = (Int16)unchecked(-((Int16)r)); return true; } } } return false; } private static Int16 ParseHex(string str) { ulong result; if (TryParseHex(str, out result)) { return (Int16)result; } throw new Exception(); } private static bool TryParseHex(string str, out ulong result) { bool sign; return Helper.TryParseUInt64Core(str, true, out result, out sign); } public static string ToHexString(Int16 value) { byte[] bytes = Microsoft.SPOT.Reflection.Serialize(value, typeof(Int16)); return new string(ToHexStringArray(bytes)); } //convert a hex number to a hex string public static char[] ToHexStringArray(byte[] bytes) { char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] chars = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { int b = bytes[i]; chars[i * 2] = hexDigits[b >> 4]; chars[i * 2 + 1] = hexDigits[b & 0xF]; } return chars; } } public static class Int32Extensions { public static Int32 Parse(string str) { Int32 result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static Int32 Parse(string str, NumberStyle style) { if (style == NumberStyle.Hexadecimal) { return ParseHex(str); } return Parse(str); } public static bool TryParse(string str, out Int32 result) { result = 0; ulong r; bool sign; if (Helper.TryParseUInt64Core(str, false, out r, out sign)) { if (!sign) { if (r <= 9223372036854775807) { result = unchecked((Int32)r); return true; } } else { if (r <= 9223372036854775808) { result = unchecked(-((Int32)r)); return true; } } } return false; } private static Int32 ParseHex(string str) { ulong result; if (TryParseHex(str, out result)) { return (Int32)result; } throw new Exception(); } private static bool TryParseHex(string str, out ulong result) { bool sign; return Helper.TryParseUInt64Core(str, true, out result, out sign); } public static string ToHexString(Int32 value) { byte[] bytes = Microsoft.SPOT.Reflection.Serialize(value, typeof(Int32)); return new string(ToHexStringArray(bytes)); } //convert a hex number to a hex string public static char[] ToHexStringArray(byte[] bytes) { char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; char[] chars = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { int b = bytes[i]; chars[i * 2] = hexDigits[b >> 4]; chars[i * 2 + 1] = hexDigits[b & 0xF]; } return chars; } } public static class UInt32Extensions { public static UInt32 Parse(string str) { UInt32 result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static ulong Parse(string str, NumberStyle style) { if (style == NumberStyle.Hexadecimal) { return ParseHex(str); } return Parse(str); } public static bool TryParse(string str, out UInt32 result) { bool sign; ulong tmp; bool bresult = Helper.TryParseUInt64Core(str, false, out tmp, out sign); result = (UInt32)tmp; return bresult && !sign; } public static bool TryParse(string str, NumberStyle style, out UInt32 result) { bool sign; ulong tmp; bool bresult = Helper.TryParseUInt64Core(str, style == NumberStyle.Hexadecimal ? true : false, out tmp, out sign); result = (UInt32)tmp; return bresult && !sign; } private static UInt32 ParseHex(string str) { UInt32 result; if (TryParseHex(str, out result)) { return result; } throw new Exception(); } private static bool TryParseHex(string str, out UInt32 result) { bool sign; ulong tmp; bool bresult = Helper.TryParseUInt64Core(str, true, out tmp, out sign); result = (UInt32)tmp; return bresult && !sign; } } public static class Int64Extensions { public static long Parse(string str) { long result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static long Parse(string str, NumberStyle style) { if (style == NumberStyle.Hexadecimal) { return ParseHex(str); } return Parse(str); } public static bool TryParse(string str, out long result) { result = 0; ulong r; bool sign; if (Helper.TryParseUInt64Core(str, false, out r, out sign)) { if (!sign) { if (r <= 9223372036854775807) { result = unchecked((long)r); return true; } } else { if (r <= 9223372036854775808) { result = unchecked(-((long)r)); return true; } } } return false; } private static long ParseHex(string str) { ulong result; if (TryParseHex(str, out result)) { return (long)result; } throw new Exception(); } private static bool TryParseHex(string str, out ulong result) { bool sign; return Helper.TryParseUInt64Core(str, true, out result, out sign); } } public static class UInt64Extensions { public static ulong Parse(string str) { ulong result; if (TryParse(str, out result)) { return result; } throw new Exception(); } public static ulong Parse(string str, NumberStyle style) { if (style == NumberStyle.Hexadecimal) { return ParseHex(str); } return Parse(str); } public static bool TryParse(string str, out ulong result) { bool sign; return Helper.TryParseUInt64Core(str, false, out result, out sign) && !sign; } private static ulong ParseHex(string str) { ulong result; if (TryParseHex(str, out result)) { return result; } throw new Exception(); } private static bool TryParseHex(string str, out ulong result) { bool sign; return Helper.TryParseUInt64Core(str, true, out result, out sign); } } public static class CharExtensions { /// /// Converts char to integer /// /// Value in string /// Result as integer /// True if conversion succeeded public static bool TryParse(char character, out int result) { result = 0; switch (character) { case '1': result = 1; break; case '2': result = 2; break; case '3': result = 3; break; case '4': result = 4; break; case '5': result = 5; break; case '6': result = 6; break; case '7': result = 7; break; case '8': result = 8; break; case '9': result = 9; break; case '0': result = 0; break; default: return false; } return true; } /// /// Converts a Unicode character to a string of its ASCII equivalent. /// Very simple, it works only on ordinary characters. /// /// /// public static string ConvertFromUtf32(int p) { char c = (char)p; return c.ToString(); } } /// /// Supplement to the functions missing in .NET MF Guid object. /// public static class GuidExtensions { /// /// Creates a Guid from a string input. /// /// /// public static Guid FromString(string s) { byte[] b = new byte[16]; b[0] = ConvertTwoCharsToByte(s[6], s[7]); b[1] = ConvertTwoCharsToByte(s[4], s[5]); b[2] = ConvertTwoCharsToByte(s[2], s[3]); b[3] = ConvertTwoCharsToByte(s[0], s[1]); // - b[4] = ConvertTwoCharsToByte(s[11], s[12]); b[5] = ConvertTwoCharsToByte(s[9], s[10]); // - b[6] = ConvertTwoCharsToByte(s[16], s[17]); b[7] = ConvertTwoCharsToByte(s[14], s[15]); // - b[8] = ConvertTwoCharsToByte(s[19], s[20]); b[9] = ConvertTwoCharsToByte(s[21], s[22]); // - b[10] = ConvertTwoCharsToByte(s[24], s[25]); b[11] = ConvertTwoCharsToByte(s[26], s[27]); b[12] = ConvertTwoCharsToByte(s[28], s[29]); b[13] = ConvertTwoCharsToByte(s[30], s[31]); b[14] = ConvertTwoCharsToByte(s[32], s[33]); b[15] = ConvertTwoCharsToByte(s[34], s[35]); return new Guid(b); } /// /// Helper function that combines two characters into a single byte value. /// /// /// /// private static byte ConvertTwoCharsToByte(char c1, char c2) { byte b1; byte b2; byte result; if (((int)c1 - 0x57) < 0) { b1 = (byte)((int)c1 - 0x30); } else { b1 = (byte)((int)c1 - 0x57); } if (((int)c2 - 0x57) < 0) { b2 = (byte)((int)c2 - 0x30); } else { b2 = (byte)((int)c2 - 0x57); } result = (byte)(((int)b1 << 4) | (int)b2); return result; } } }