/* * Copyright (c) 2008, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Text; namespace OpenMetaverse { public static class Utils { /// /// Operating system /// public enum Platform { /// Unknown Unknown, /// Microsoft Windows Windows, /// Microsoft Windows CE WindowsCE, /// Linux Linux, /// Apple OSX OSX } /// /// Runtime platform /// public enum Runtime { /// .NET runtime Windows, /// Mono runtime: http://www.mono-project.com/ Mono } public const float E = (float)Math.E; public const float LOG10E = 0.4342945f; public const float LOG2E = 1.442695f; public const float PI = (float)Math.PI; public const float TWO_PI = (float)(Math.PI * 2.0d); public const float PI_OVER_TWO = (float)(Math.PI / 2.0d); public const float PI_OVER_FOUR = (float)(Math.PI / 4.0d); /// Used for converting degrees to radians public const float DEG_TO_RAD = (float)(Math.PI / 180.0d); /// Used for converting radians to degrees public const float RAD_TO_DEG = (float)(180.0d / Math.PI); /// Provide a single instance of the CultureInfo class to /// help parsing in situations where the grid assumes an en-us /// culture public static readonly System.Globalization.CultureInfo EnUsCulture = new System.Globalization.CultureInfo("en-us"); /// UNIX epoch in DateTime format public static readonly DateTime Epoch = new DateTime(1970, 1, 1); /// Provide a single instance of the MD5 class to avoid making /// duplicate copies private static readonly System.Security.Cryptography.MD5 MD5Builder = new System.Security.Cryptography.MD5CryptoServiceProvider(); #region Math /// /// Clamp a given value between a range /// /// Value to clamp /// Minimum allowable value /// Maximum allowable value /// A value inclusively between lower and upper public static float Clamp(float value, float min, float max) { // First we check to see if we're greater than the max value = (value > max) ? max : value; // Then we check to see if we're less than the min. value = (value < min) ? min : value; // There's no check to see if min > max. return value; } /// /// Clamp a given value between a range /// /// Value to clamp /// Minimum allowable value /// Maximum allowable value /// A value inclusively between lower and upper public static double Clamp(double value, double min, double max) { // First we check to see if we're greater than the max value = (value > max) ? max : value; // Then we check to see if we're less than the min. value = (value < min) ? min : value; // There's no check to see if min > max. return value; } /// /// Round a floating-point value to the nearest integer /// /// Floating point number to round /// Integer public static int Round(float val) { return (int)Math.Floor(val + 0.5f); } /// /// Test if a single precision float is a finite number /// public static bool IsFinite(float value) { return !(Single.IsNaN(value) || Single.IsInfinity(value)); } /// /// Test if a double precision float is a finite number /// public static bool IsFinite(double value) { return !(Double.IsNaN(value) || Double.IsInfinity(value)); } /// /// Get the distance between two floating-point values /// /// First value /// Second value /// The distance between the two values public static float Distance(float value1, float value2) { return Math.Abs(value1 - value2); } public static float Hermite(float value1, float tangent1, float value2, float tangent2, float amount) { // All transformed to double not to lose precission // Otherwise, for high numbers of param:amount the result is NaN instead of Infinity double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result; double sCubed = s * s * s; double sSquared = s * s; if (amount == 0f) result = value1; else if (amount == 1f) result = value2; else result = (2d * v1 - 2d * v2 + t2 + t1) * sCubed + (3d * v2 - 3d * v1 - 2d * t1 - t2) * sSquared + t1 * s + v1; return (float)result; } public static double Hermite(double value1, double tangent1, double value2, double tangent2, double amount) { // All transformed to double not to lose precission // Otherwise, for high numbers of param:amount the result is NaN instead of Infinity double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result; double sCubed = s * s * s; double sSquared = s * s; if (amount == 0d) result = value1; else if (amount == 1f) result = value2; else result = (2d * v1 - 2d * v2 + t2 + t1) * sCubed + (3d * v2 - 3d * v1 - 2d * t1 - t2) * sSquared + t1 * s + v1; return result; } public static float Lerp(float value1, float value2, float amount) { return value1 + (value2 - value1) * amount; } public static double Lerp(double value1, double value2, double amount) { return value1 + (value2 - value1) * amount; } public static float SmoothStep(float value1, float value2, float amount) { // It is expected that 0 < amount < 1 // If amount < 0, return value1 // If amount > 1, return value2 float result = Utils.Clamp(amount, 0f, 1f); return Utils.Hermite(value1, 0f, value2, 0f, result); } public static double SmoothStep(double value1, double value2, double amount) { // It is expected that 0 < amount < 1 // If amount < 0, return value1 // If amount > 1, return value2 double result = Utils.Clamp(amount, 0f, 1f); return Utils.Hermite(value1, 0f, value2, 0f, result); } public static float ToDegrees(float radians) { // This method uses double precission internally, // though it returns single float // Factor = 180 / pi return (float)(radians * 57.295779513082320876798154814105); } public static float ToRadians(float degrees) { // This method uses double precission internally, // though it returns single float // Factor = pi / 180 return (float)(degrees * 0.017453292519943295769236907684886); } /// /// Compute the MD5 hash for a byte array /// /// Byte array to compute the hash for /// MD5 hash of the input data public static byte[] MD5(byte[] data) { lock (MD5Builder) return MD5Builder.ComputeHash(data); } /// /// Calculate the MD5 hash of a given string /// /// The password to hash /// An MD5 hash in string format, with $1$ prepended public static string MD5(string password) { StringBuilder digest = new StringBuilder(); byte[] hash = MD5(ASCIIEncoding.Default.GetBytes(password)); // Convert the hash to a hex string foreach (byte b in hash) { digest.AppendFormat(Utils.EnUsCulture, "{0:x2}", b); } return "$1$" + digest.ToString(); } #endregion Math #region Platform /// /// Get the current running platform /// /// Enumeration of the current platform we are running on public static Platform GetRunningPlatform() { const string OSX_CHECK_FILE = "/Library/Extensions.kextcache"; if (Environment.OSVersion.Platform == PlatformID.WinCE) { return Platform.WindowsCE; } else { int plat = (int)Environment.OSVersion.Platform; if ((plat != 4) && (plat != 128)) { return Platform.Windows; } else { if (System.IO.File.Exists(OSX_CHECK_FILE)) return Platform.OSX; else return Platform.Linux; } } } /// /// Get the current running runtime /// /// Enumeration of the current runtime we are running on public static Runtime GetRunningRuntime() { Type t = Type.GetType("Mono.Runtime"); if (t != null) return Runtime.Mono; else return Runtime.Windows; } #endregion Platform #region Conversion /// /// Convert four bytes in little endian ordering to a floating point /// value /// /// Byte array containing a little ending floating /// point value /// Starting position of the floating point value in /// the byte array /// Single precision value public static float BytesToFloat(byte[] bytes, int pos) { if (!BitConverter.IsLittleEndian) Array.Reverse(bytes, pos, 4); return BitConverter.ToSingle(bytes, pos); } /// /// Convert a float value to a byte given a minimum and maximum range /// /// Value to convert to a byte /// Minimum value range /// Maximum value range /// A single byte representing the original float value public static byte FloatToByte(float val, float lower, float upper) { val = Clamp(val, lower, upper); // Normalize the value val -= lower; val /= (upper - lower); return (byte)Math.Floor(val * (float)byte.MaxValue); } /// /// Convert a floating point value to four bytes in little endian /// ordering /// /// A floating point value /// A four byte array containing the value in little endian /// ordering public static byte[] FloatToBytes(float value) { byte[] bytes = BitConverter.GetBytes(value); if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); return bytes; } /// /// Convert an IP address object to an unsigned 32-bit integer /// /// IP address to convert /// 32-bit unsigned integer holding the IP address bits public static uint IPToUInt(System.Net.IPAddress address) { byte[] bytes = address.GetAddressBytes(); return (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]); } /// /// Convert a variable length UTF8 byte array to a string /// /// The UTF8 encoded byte array to convert /// The decoded string public static string BytesToString(byte[] bytes) { if (bytes.Length > 0 && bytes[bytes.Length - 1] == 0x00) return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1); else return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length); } /// /// Converts a byte array to a string containing hexadecimal characters /// /// The byte array to convert to a string /// The name of the field to prepend to each /// line of the string /// A string containing hexadecimal characters on multiple /// lines. Each line is prepended with the field name public static string BytesToHexString(byte[] bytes, string fieldName) { return BytesToHexString(bytes, bytes.Length, fieldName); } /// /// Converts a byte array to a string containing hexadecimal characters /// /// The byte array to convert to a string /// Number of bytes in the array to parse /// A string to prepend to each line of the hex /// dump /// A string containing hexadecimal characters on multiple /// lines. Each line is prepended with the field name public static string BytesToHexString(byte[] bytes, int length, string fieldName) { StringBuilder output = new StringBuilder(); for (int i = 0; i < length; i += 16) { if (i != 0) output.Append('\n'); if (!String.IsNullOrEmpty(fieldName)) { output.Append(fieldName); output.Append(": "); } for (int j = 0; j < 16; j++) { if ((i + j) < length) output.Append(String.Format("{0:X2} ", bytes[i + j])); else output.Append(" "); } } return output.ToString(); } /// /// Convert a string to a UTF8 encoded byte array /// /// The string to convert /// A null-terminated UTF8 byte array public static byte[] StringToBytes(string str) { if (String.IsNullOrEmpty(str)) { return new byte[0]; } if (!str.EndsWith("\0")) { str += "\0"; } return System.Text.UTF8Encoding.UTF8.GetBytes(str); } ///// ///// Converts a string containing hexadecimal characters to a byte array ///// ///// String containing hexadecimal characters ///// The converted byte array public static byte[] HexStringToBytes(string hexString) { if (String.IsNullOrEmpty(hexString)) return new byte[0]; StringBuilder stripped = new StringBuilder(hexString.Length); char c; // remove all non A-F, 0-9, characters for (int i = 0; i < hexString.Length; i++) { c = hexString[i]; if (IsHexDigit(c)) stripped.Append(c); } string newString = stripped.ToString(); // if odd number of characters, discard last character if (newString.Length % 2 != 0) { newString = newString.Substring(0, newString.Length - 1); } int byteLength = newString.Length / 2; byte[] bytes = new byte[byteLength]; string hex; int j = 0; for (int i = 0; i < bytes.Length; i++) { hex = new String(new Char[] { newString[j], newString[j + 1] }); bytes[i] = HexToByte(hex); j = j + 2; } return bytes; } /// /// Returns true is c is a hexadecimal digit (A-F, a-f, 0-9) /// /// Character to test /// true if hex digit, false if not private static bool IsHexDigit(Char c) { const int numA = 65; const int num0 = 48; int numChar; c = Char.ToUpper(c); numChar = Convert.ToInt32(c); if (numChar >= numA && numChar < (numA + 6)) return true; else if (numChar >= num0 && numChar < (num0 + 10)) return true; else return false; } /// /// Converts 1 or 2 character string into equivalant byte value /// /// 1 or 2 character string /// byte private static byte HexToByte(string hex) { if (hex.Length > 2 || hex.Length <= 0) throw new ArgumentException("hex must be 1 or 2 characters in length"); byte newByte = Byte.Parse(hex, System.Globalization.NumberStyles.HexNumber); return newByte; } /// /// Gets a unix timestamp for the current time /// /// An unsigned integer representing a unix timestamp for now public static uint GetUnixTime() { return (uint)(DateTime.UtcNow - Epoch).TotalSeconds; } /// /// Convert a UNIX timestamp to a native DateTime object /// /// An unsigned integer representing a UNIX /// timestamp /// A DateTime object containing the same time specified in /// the given timestamp public static DateTime UnixTimeToDateTime(uint timestamp) { System.DateTime dateTime = Epoch; // Add the number of seconds in our UNIX timestamp dateTime = dateTime.AddSeconds(timestamp); return dateTime; } /// /// Convert a UNIX timestamp to a native DateTime object /// /// A signed integer representing a UNIX /// timestamp /// A DateTime object containing the same time specified in /// the given timestamp public static DateTime UnixTimeToDateTime(int timestamp) { return DateTime.FromBinary(timestamp); } /// /// Convert a native DateTime object to a UNIX timestamp /// /// A DateTime object you want to convert to a /// timestamp /// An unsigned integer representing a UNIX timestamp public static uint DateTimeToUnixTime(DateTime time) { TimeSpan ts = (time - new DateTime(1970, 1, 1, 0, 0, 0)); return (uint)ts.TotalSeconds; } /// /// Swap two values /// /// Type of the values to swap /// First value /// Second value public static void Swap(ref T lhs, ref T rhs) { T temp = lhs; lhs = rhs; rhs = temp; } /// /// Attempts to parse a floating point value from a string, using an /// EN-US number format /// /// String to parse /// Resulting floating point number /// True if the parse was successful, otherwise false public static bool TryParseSingle(string s, out float result) { return Single.TryParse(s, System.Globalization.NumberStyles.Float, EnUsCulture.NumberFormat, out result); } /// /// Attempts to parse a floating point value from a string, using an /// EN-US number format /// /// String to parse /// Resulting floating point number /// True if the parse was successful, otherwise false public static bool TryParseDouble(string s, out double result) { return Double.TryParse(s, System.Globalization.NumberStyles.Float, EnUsCulture.NumberFormat, out result); } /// /// Tries to parse an unsigned 32-bit integer from a hexadecimal string /// /// String to parse /// Resulting integer /// True if the parse was successful, otherwise false public static bool TryParseHex(string s, out uint result) { return UInt32.TryParse(s, System.Globalization.NumberStyles.HexNumber, EnUsCulture.NumberFormat, out result); } #endregion Conversion } }