From 5e4d6cab00cb29cd088ab7b62ab13aff103b64cb Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 19 May 2019 21:24:15 +1000 Subject: Dump OpenSim 0.9.0.1 into it's own branch. --- OpenSim/Framework/Util.cs | 735 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 604 insertions(+), 131 deletions(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index b5c3e75..7093010 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -59,17 +59,41 @@ namespace OpenSim.Framework { [Flags] public enum PermissionMask : uint - { + { None = 0, - Transfer = 1 << 13, - Modify = 1 << 14, - Copy = 1 << 15, - Export = 1 << 16, - Move = 1 << 19, - Damage = 1 << 20, + + // folded perms + FoldedTransfer = 1, + FoldedModify = 1 << 1, + FoldedCopy = 1 << 2, + FoldedExport = 1 << 3, + + // DO NOT USE THIS FOR NEW WORK. IT IS DEPRECATED AND + // EXISTS ONLY TO REACT TO EXISTING OBJECTS HAVING IT. + // NEW CODE SHOULD NEVER SET THIS BIT! + // Use InventoryItemFlags.ObjectSlamPerm in the Flags field of + // this legacy slam bit. It comes from prior incomplete + // understanding of the code and the prohibition on + // reading viewer code that used to be in place. + Slam = (1 << 4), + + FoldedMask = 0x0f, + + FoldingShift = 13 , // number of bit shifts from normal perm to folded or back (same as Transfer shift below) + // when doing as a block + + Transfer = 1 << 13, // 0x02000 + Modify = 1 << 14, // 0x04000 + Copy = 1 << 15, // 0x08000 + Export = 1 << 16, // 0x10000 + Move = 1 << 19, // 0x80000 + Damage = 1 << 20, // 0x100000 does not seem to be in use // All does not contain Export, which is special and must be // explicitly given - All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19) + All = 0x8e000, + AllAndExport = 0x9e000, + AllEffective = 0x9e000, + UnfoldedMask = 0x1e000 } /// @@ -103,7 +127,7 @@ namespace OpenSim.Framework public STPStartInfo STPStartInfo { get; set; } public WIGStartInfo WIGStartInfo { get; set; } public bool IsIdle { get; set; } - public bool IsShuttingDown { get; set; } + public bool IsShuttingDown { get; set; } public int MaxThreads { get; set; } public int MinThreads { get; set; } public int InUseThreads { get; set; } @@ -131,10 +155,16 @@ namespace OpenSim.Framework public static readonly int MAX_THREADPOOL_LEVEL = 3; + public static double TimeStampClockPeriodMS; + public static double TimeStampClockPeriod; + static Util() { LogThreadPool = 0; LogOverloads = true; + TimeStampClockPeriod = 1.0D/ (double)Stopwatch.Frequency; + TimeStampClockPeriodMS = 1e3 * TimeStampClockPeriod; + m_log.InfoFormat("[UTIL] TimeStamp clock with period of {0}ms", Math.Round(TimeStampClockPeriodMS,6,MidpointRounding.AwayFromZero)); } private static uint nextXferID = 5000; @@ -217,7 +247,7 @@ namespace OpenSim.Framework public static Encoding UTF8NoBomEncoding = new UTF8Encoding(false); /// - /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards) + /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards) /// public static UUID BLANK_TEXTURE_UUID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f"); @@ -267,14 +297,12 @@ namespace OpenSim.Framework /// /// A 3d vector /// A new vector which is normalized form of the vector - /// The vector paramater cannot be <0,0,0> + public static Vector3 GetNormalizedVector(Vector3 a) { - if (IsZeroVector(a)) - throw new ArgumentException("Vector paramater cannot be a zero vector."); - - float Mag = (float) GetMagnitude(a); - return new Vector3(a.X / Mag, a.Y / Mag, a.Z / Mag); + Vector3 v = new Vector3(a.X, a.Y, a.Z); + v.Normalize(); + return v; } /// @@ -357,47 +385,185 @@ namespace OpenSim.Framework return Utils.UIntsToLong(X, Y); } - // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong. - // Several places rely on the ability to extract a region's location from its handle. - // Note the location is in 'world coordinates' (see below). - // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0. + // Regions are identified with a 'handle' made up of its world coordinates packed into a ulong. + // Region handles are based on the coordinate of the region corner with lower X and Y + // var regions need more work than this to get that right corner from a generic world position + // this corner must be on a grid point public static ulong RegionWorldLocToHandle(uint X, uint Y) { - return Utils.UIntsToLong(X, Y); + ulong handle = X & 0xffffff00; // make sure it matchs grid coord points. + handle <<= 32; // to higher half + handle |= (Y & 0xffffff00); + return handle; } - public static ulong RegionLocToHandle(uint X, uint Y) + public static ulong RegionGridLocToHandle(uint X, uint Y) { - return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y)); + ulong handle = X; + handle <<= 40; // shift to higher half and mult by 256) + handle |= (Y << 8); // mult by 256) + return handle; } public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y) { X = (uint)(handle >> 32); - Y = (uint)(handle & (ulong)uint.MaxValue); + Y = (uint)(handle & 0xfffffffful); } public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y) { - uint worldX, worldY; - RegionHandleToWorldLoc(handle, out worldX, out worldY); - X = WorldToRegionLoc(worldX); - Y = WorldToRegionLoc(worldY); + X = (uint)(handle >> 40) & 0x00ffffffu; // bring from higher half, divide by 256 and clean + Y = (uint)(handle >> 8) & 0x00ffffffu; // divide by 256 and clean + // if you trust the uint cast then the clean can be removed. } - // A region location can be 'world coordinates' (meters from zero) or 'region coordinates' - // (number of regions from zero). This measurement of regions relies on the legacy 256 region size. - // These routines exist to make what is being converted explicit so the next person knows what was meant. - // Convert a region's 'world coordinate' to its 'region coordinate'. + // A region location can be 'world coordinates' (meters) or 'region grid coordinates' + // grid coordinates have a fixed step of 256m as defined by viewers public static uint WorldToRegionLoc(uint worldCoord) { - return worldCoord / Constants.RegionSize; + return worldCoord >> 8; } - // Convert a region's 'region coordinate' to its 'world coordinate'. + // Convert a region's 'region grid coordinate' to its 'world coordinate'. public static uint RegionToWorldLoc(uint regionCoord) { - return regionCoord * Constants.RegionSize; + return regionCoord << 8; + } + + + public static bool checkServiceURI(string uristr, out string serviceURI) + { + serviceURI = string.Empty; + try + { + Uri uri = new Uri(uristr); + serviceURI = uri.AbsoluteUri; + if(uri.Port == 80) + serviceURI = serviceURI.Trim(new char[] { '/', ' ' }) +":80/"; + else if(uri.Port == 443) + serviceURI = serviceURI.Trim(new char[] { '/', ' ' }) +":443/"; + return true; + } + catch + { + serviceURI = string.Empty; + } + return false; + } + + public static bool buildHGRegionURI(string inputName, out string serverURI, out string regionName) + { + serverURI = string.Empty; + regionName = string.Empty; + + inputName = inputName.Trim(); + + if (!inputName.StartsWith("http") && !inputName.StartsWith("https")) + { + // Formats: grid.example.com:8002:region name + // grid.example.com:region name + // grid.example.com:8002 + // grid.example.com + + string host; + uint port = 80; + + string[] parts = inputName.Split(new char[] { ':' }); + int indx; + if(parts.Length == 0) + return false; + if (parts.Length == 1) + { + indx = inputName.IndexOf('/'); + if (indx < 0) + serverURI = "http://"+ inputName + "/"; + else + { + serverURI = "http://"+ inputName.Substring(0,indx + 1); + if(indx + 2 < inputName.Length) + regionName = inputName.Substring(indx + 1); + } + } + else + { + host = parts[0]; + + if (parts.Length >= 2) + { + indx = parts[1].IndexOf('/'); + if(indx < 0) + { + // If it's a number then assume it's a port. Otherwise, it's a region name. + if (!UInt32.TryParse(parts[1], out port)) + { + port = 80; + regionName = parts[1]; + } + } + else + { + string portstr = parts[1].Substring(0, indx); + if(indx + 2 < parts[1].Length) + regionName = parts[1].Substring(indx + 1); + if (!UInt32.TryParse(portstr, out port)) + port = 80; + } + } + // always take the last one + if (parts.Length >= 3) + { + regionName = parts[2]; + } + + serverURI = "http://"+ host +":"+ port.ToString() + "/"; + } + } + else + { + // Formats: http://grid.example.com region name + // http://grid.example.com "region name" + // http://grid.example.com + + string[] parts = inputName.Split(new char[] { ' ' }); + + if (parts.Length == 0) + return false; + + serverURI = parts[0]; + + int indx = serverURI.LastIndexOf('/'); + if(indx > 10) + { + if(indx + 2 < inputName.Length) + regionName = inputName.Substring(indx + 1); + serverURI = inputName.Substring(0, indx + 1); + } + else if (parts.Length >= 2) + { + regionName = inputName.Substring(serverURI.Length); + } + } + + // use better code for sanity check + Uri uri; + try + { + uri = new Uri(serverURI); + } + catch + { + return false; + } + + if(!string.IsNullOrEmpty(regionName)) + regionName = regionName.Trim(new char[] { '"', ' ' }); + serverURI = uri.AbsoluteUri; + if(uri.Port == 80) + serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":80/"; + else if(uri.Port == 443) + serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":443/"; + return true; } public static T Clamp(T x, T min, T max) @@ -505,6 +671,7 @@ namespace OpenSim.Framework public static string GetFormattedXml(string rawXml) { XmlDocument xd = new XmlDocument(); + xd.XmlResolver=null; xd.LoadXml(rawXml); StringBuilder sb = new StringBuilder(); @@ -641,19 +808,25 @@ namespace OpenSim.Framework /// /// /// + public static string Md5Hash(string data) { - byte[] dataMd5 = ComputeMD5Hash(data); + return Md5Hash(data, Encoding.Default); + } + + public static string Md5Hash(string data, Encoding encoding) + { + byte[] dataMd5 = ComputeMD5Hash(data, encoding); StringBuilder sb = new StringBuilder(); for (int i = 0; i < dataMd5.Length; i++) sb.AppendFormat("{0:x2}", dataMd5[i]); return sb.ToString(); } - private static byte[] ComputeMD5Hash(string data) + private static byte[] ComputeMD5Hash(string data, Encoding encoding) { MD5 md5 = MD5.Create(); - return md5.ComputeHash(Encoding.Default.GetBytes(data)); + return md5.ComputeHash(encoding.GetBytes(data)); } /// @@ -661,6 +834,12 @@ namespace OpenSim.Framework /// /// /// + + public static string SHA1Hash(string data, Encoding enc) + { + return SHA1Hash(enc.GetBytes(data)); + } + public static string SHA1Hash(string data) { return SHA1Hash(Encoding.Default.GetBytes(data)); @@ -679,8 +858,10 @@ namespace OpenSim.Framework private static byte[] ComputeSHA1Hash(byte[] src) { - SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider(); - return SHA1.ComputeHash(src); + byte[] ret; + using(SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider()) + ret = SHA1.ComputeHash(src); + return ret; } public static int fast_distance2d(int x, int y) @@ -713,18 +894,27 @@ namespace OpenSim.Framework /// New region x-coord /// Old region y-coord /// New region y-coord - /// - public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy) - { - int dd = (int)((drawdist + Constants.RegionSize - 1) / Constants.RegionSize); - - int startX = (int)oldx - dd; - int startY = (int)oldy - dd; + /// + public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy, + int oldsizex, int oldsizey, int newsizex, int newsizey) + { + // we still need to make sure we see new region 1stNeighbors + drawdist--; + oldx *= Constants.RegionSize; + newx *= Constants.RegionSize; + if (oldx + oldsizex + drawdist < newx) + return true; + if (newx + newsizex + drawdist < oldx) + return true; - int endX = (int)oldx + dd; - int endY = (int)oldy + dd; + oldy *= Constants.RegionSize; + newy *= Constants.RegionSize; + if (oldy + oldsizey + drawdist < newy) + return true; + if (newy + newsizey + drawdist < oldy) + return true; - return (newx < startX || endX < newx || newy < startY || endY < newy); + return false; } public static string FieldToString(byte[] bytes) @@ -804,6 +994,18 @@ namespace OpenSim.Framework return output.ToString(); } + private static ExpiringCache dnscache = new ExpiringCache(); + + /// + /// Converts a URL to a IPAddress + /// + /// URL Standard Format + /// A resolved IP Address + public static IPAddress GetHostFromURL(string url) + { + return GetHostFromDNS(url.Split(new char[] {'/', ':'})[3]); + } + /// /// Returns a IP address from a specified DNS, favouring IPv4 addresses. /// @@ -811,38 +1013,128 @@ namespace OpenSim.Framework /// An IP address, or null public static IPAddress GetHostFromDNS(string dnsAddress) { - // Is it already a valid IP? No need to look it up. - IPAddress ipa; - if (IPAddress.TryParse(dnsAddress, out ipa)) - return ipa; + if(String.IsNullOrWhiteSpace(dnsAddress)) + return null; - IPAddress[] hosts = null; + IPAddress ia = null; + if(dnscache.TryGetValue(dnsAddress, out ia) && ia != null) + { + dnscache.AddOrUpdate(dnsAddress, ia, 300); + return ia; + } - // Not an IP, lookup required + ia = null; + // If it is already an IP, don't let GetHostEntry see it + if (IPAddress.TryParse(dnsAddress, out ia) && ia != null) + { + if (ia.Equals(IPAddress.Any) || ia.Equals(IPAddress.IPv6Any)) + return null; + dnscache.AddOrUpdate(dnsAddress, ia, 300); + return ia; + } + + IPHostEntry IPH; try { - hosts = Dns.GetHostEntry(dnsAddress).AddressList; + IPH = Dns.GetHostEntry(dnsAddress); } - catch (Exception e) + catch // (SocketException e) + { + return null; + } + + if(IPH == null || IPH.AddressList.Length == 0) + return null; + + ia = null; + foreach (IPAddress Adr in IPH.AddressList) { - m_log.WarnFormat("[UTIL]: An error occurred while resolving host name {0}, {1}", dnsAddress, e); + if (ia == null) + ia = Adr; - // Still going to throw the exception on for now, since this was what was happening in the first place - throw e; + if (Adr.AddressFamily == AddressFamily.InterNetwork) + { + ia = Adr; + break; + } } + if(ia != null) + dnscache.AddOrUpdate(dnsAddress, ia, 300); + return ia; + } - foreach (IPAddress host in hosts) + public static IPEndPoint getEndPoint(IPAddress ia, int port) + { + if(ia == null) + return null; + + IPEndPoint newEP = null; + try + { + newEP = new IPEndPoint(ia, port); + } + catch + { + newEP = null; + } + return newEP; + } + + public static IPEndPoint getEndPoint(string hostname, int port) + { + if(String.IsNullOrWhiteSpace(hostname)) + return null; + + IPAddress ia = null; + if(dnscache.TryGetValue(hostname, out ia) && ia != null) + { + dnscache.AddOrUpdate(hostname, ia, 300); + return getEndPoint(ia, port); + } + + ia = null; + + // If it is already an IP, don't let GetHostEntry see it + if (IPAddress.TryParse(hostname, out ia) && ia != null) + { + if (ia.Equals(IPAddress.Any) || ia.Equals(IPAddress.IPv6Any)) + return null; + + dnscache.AddOrUpdate(hostname, ia, 300); + return getEndPoint(ia, port); + } + + + IPHostEntry IPH; + try + { + IPH = Dns.GetHostEntry(hostname); + } + catch // (SocketException e) + { + return null; + } + + if(IPH == null || IPH.AddressList.Length == 0) + return null; + + ia = null; + foreach (IPAddress Adr in IPH.AddressList) { - if (host.AddressFamily == AddressFamily.InterNetwork) + if (ia == null) + ia = Adr; + + if (Adr.AddressFamily == AddressFamily.InterNetwork) { - return host; + ia = Adr; + break; } } - if (hosts.Length > 0) - return hosts[0]; + if(ia != null) + dnscache.AddOrUpdate(hostname, ia, 300); - return null; + return getEndPoint(ia,port); } public static Uri GetURI(string protocol, string hostname, int port, string path) @@ -987,7 +1279,7 @@ namespace OpenSim.Framework public static string configDir() { - return "../config"; + return "."; } public static string dataDir() @@ -999,13 +1291,26 @@ namespace OpenSim.Framework { foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) { - if (appender is FileAppender) + if (appender is FileAppender && appender.Name == "LogFileAppender") { return ((FileAppender)appender).File; } } - return "../logs/OpenSim.log"; + return "./OpenSim.log"; + } + + public static string statsLogFile() + { + foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) + { + if (appender is FileAppender && appender.Name == "StatsLogFileAppender") + { + return ((FileAppender)appender).File; + } + } + + return "./OpenSimStats.log"; } public static string logDir() @@ -1065,9 +1370,28 @@ namespace OpenSim.Framework } } + public static string GetConfigVarWithDefaultSection(IConfigSource config, string varname, string section) + { + // First, check the Startup section, the default section + IConfig cnf = config.Configs["Startup"]; + if (cnf == null) + return string.Empty; + string val = cnf.GetString(varname, string.Empty); + + // Then check for an overwrite of the default in the given section + if (!string.IsNullOrEmpty(section)) + { + cnf = config.Configs[section]; + if (cnf != null) + val = cnf.GetString(varname, val); + } + + return val; + } + /// /// Gets the value of a configuration variable by looking into - /// multiple sections in order. The latter sections overwrite + /// multiple sections in order. The latter sections overwrite /// any values previously found. /// /// Type of the variable @@ -1082,7 +1406,7 @@ namespace OpenSim.Framework /// /// Gets the value of a configuration variable by looking into - /// multiple sections in order. The latter sections overwrite + /// multiple sections in order. The latter sections overwrite /// any values previously found. /// /// @@ -1138,7 +1462,7 @@ namespace OpenSim.Framework ConfigSource.ExpandKeyValues(); } } - + public static T ReadSettingsFromIniFile(IConfig config, T settingsClass) { Type settingsType = settingsClass.GetType(); @@ -1249,7 +1573,7 @@ namespace OpenSim.Framework if (File.Exists(configFile)) { - // Merge + // Merge config.Merge(new IniConfigSource(configFile)); config.ExpandKeyValues(); configFilePath = configFile; @@ -1388,6 +1712,46 @@ namespace OpenSim.Framework return ret; } + public static string Compress(string text) + { + byte[] buffer = Util.UTF8.GetBytes(text); + MemoryStream memory = new MemoryStream(); + using (GZipStream compressor = new GZipStream(memory, CompressionMode.Compress, true)) + { + compressor.Write(buffer, 0, buffer.Length); + } + + memory.Position = 0; + + byte[] compressed = new byte[memory.Length]; + memory.Read(compressed, 0, compressed.Length); + + byte[] compressedBuffer = new byte[compressed.Length + 4]; + Buffer.BlockCopy(compressed, 0, compressedBuffer, 4, compressed.Length); + Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, compressedBuffer, 0, 4); + return Convert.ToBase64String(compressedBuffer); + } + + public static string Decompress(string compressedText) + { + byte[] compressedBuffer = Convert.FromBase64String(compressedText); + using (MemoryStream memory = new MemoryStream()) + { + int msgLength = BitConverter.ToInt32(compressedBuffer, 0); + memory.Write(compressedBuffer, 4, compressedBuffer.Length - 4); + + byte[] buffer = new byte[msgLength]; + + memory.Position = 0; + using (GZipStream decompressor = new GZipStream(memory, CompressionMode.Decompress)) + { + decompressor.Read(buffer, 0, buffer.Length); + } + + return Util.UTF8.GetString(buffer); + } + } + /// /// Copy data from one stream to another, leaving the read position of both streams at the beginning. /// @@ -1405,7 +1769,7 @@ namespace OpenSim.Framework const int readSize = 256; byte[] buffer = new byte[readSize]; MemoryStream ms = new MemoryStream(); - + int count = inputStream.Read(buffer, 0, readSize); while (count > 0) @@ -1485,12 +1849,16 @@ namespace OpenSim.Framework return new UUID(bytes, 0); } - public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) + public static bool ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) { byte[] bytes = parcelID.GetBytes(); regionHandle = Utils.BytesToUInt64(bytes); x = Utils.BytesToUInt(bytes, 8) & 0xffff; y = Utils.BytesToUInt(bytes, 12) & 0xffff; + // validation may fail, just reducing the odds of using a real UUID as encoded parcel + return ( bytes[0] == 0 && bytes[4] == 0 && // handler x,y multiples of 256 + bytes[9] < 64 && bytes[13] < 64 && // positions < 16km + bytes[14] == 0 && bytes[15] == 0); } public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z) @@ -1513,7 +1881,7 @@ namespace OpenSim.Framework x += rx; y += ry; } - + /// /// Get operating system information if available. Returns only the first 45 characters of information /// @@ -1524,20 +1892,20 @@ namespace OpenSim.Framework { string os = String.Empty; - if (Environment.OSVersion.Platform != PlatformID.Unix) - { - os = Environment.OSVersion.ToString(); - } - else - { - os = ReadEtcIssue(); - } - - if (os.Length > 45) - { - os = os.Substring(0, 45); - } - +// if (Environment.OSVersion.Platform != PlatformID.Unix) +// { +// os = Environment.OSVersion.ToString(); +// } +// else +// { +// os = ReadEtcIssue(); +// } +// +// if (os.Length > 45) +// { +// os = os.Substring(0, 45); +// } + return os; } @@ -1579,6 +1947,8 @@ namespace OpenSim.Framework // hide the password in the connection string passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase); + if (passPosition == -1) + return connectionString; passPosition = connectionString.IndexOf("=", passPosition); if (passPosition < connectionString.Length) passPosition += 1; @@ -1645,7 +2015,7 @@ namespace OpenSim.Framework public static Guid GetHashGuid(string data, string salt) { - byte[] hash = ComputeMD5Hash(data + salt); + byte[] hash = ComputeMD5Hash(data + salt, Encoding.Default); //string s = BitConverter.ToString(hash); @@ -1747,7 +2117,7 @@ namespace OpenSim.Framework vol = vcomps[0]; } } - + string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); // Glob @@ -1792,6 +2162,32 @@ namespace OpenSim.Framework return found.ToArray(); } + public static string ServerURI(string uri) + { + if (uri == string.Empty) + return string.Empty; + + // Get rid of eventual slashes at the end + uri = uri.TrimEnd('/'); + + IPAddress ipaddr1 = null; + string port1 = ""; + try + { + ipaddr1 = Util.GetHostFromURL(uri); + } + catch { } + + try + { + port1 = uri.Split(new char[] { ':' })[2]; + } + catch { } + + // We tried our best to convert the domain names to IP addresses + return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; + } + /// /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. /// @@ -1823,14 +2219,14 @@ namespace OpenSim.Framework if (!str.EndsWith("\0")) str += "\0"; - + // Because this is UTF-8 encoding and not ASCII, it's possible we // might have gotten an oversized array even after the string trim byte[] data = UTF8.GetBytes(str); - if (data.Length > 256) + if (data.Length > 255) //play safe { - int cut = 255; + int cut = 254; if((data[cut] & 0x80 ) != 0 ) { while(cut > 0 && (data[cut] & 0xc0) != 0xc0) @@ -1895,6 +2291,56 @@ namespace OpenSim.Framework } /// + /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to MaxLength bytes if necessary. + /// + /// + /// If null or empty, then an bytes[0] is returned. + /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] + /// + /// + /// Arguments to substitute into the string via the {} mechanism. + /// + /// + public static byte[] StringToBytes(string str, int MaxLength, params object[] args) + { + return StringToBytes1024(string.Format(str, args), MaxLength); + } + + /// + /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to MaxLength bytes if necessary. + /// + /// + /// If null or empty, then an bytes[0] is returned. + /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] + /// + /// + public static byte[] StringToBytes(string str, int MaxLength) + { + if (String.IsNullOrEmpty(str)) + return Utils.EmptyBytes; + + if (!str.EndsWith("\0")) + str += "\0"; + + // Because this is UTF-8 encoding and not ASCII, it's possible we + // might have gotten an oversized array even after the string trim + byte[] data = UTF8.GetBytes(str); + + if (data.Length > MaxLength) + { + int cut = MaxLength - 1 ; + if((data[cut] & 0x80 ) != 0 ) + { + while(cut > 0 && (data[cut] & 0xc0) != 0xc0) + cut--; + } + Array.Resize(ref data, cut + 1); + data[cut] = 0; + } + + return data; + } + /// /// Pretty format the hashtable contents to a single line. /// /// @@ -2004,7 +2450,7 @@ namespace OpenSim.Framework STPStartInfo startInfo = new STPStartInfo(); startInfo.ThreadPoolName = "Util"; - startInfo.IdleTimeout = 2000; + startInfo.IdleTimeout = 20000; startInfo.MaxWorkerThreads = maxThreads; startInfo.MinWorkerThreads = minThreads; @@ -2032,7 +2478,7 @@ namespace OpenSim.Framework throw new NotImplementedException(); } } - + /// /// Additional information about threads in the main thread pool. Used to time how long the /// thread has been running, and abort it if it has timed-out. @@ -2043,14 +2489,15 @@ namespace OpenSim.Framework public string StackTrace { get; set; } private string context; public bool LogThread { get; set; } - + public IWorkItemResult WorkItem { get; set; } public Thread Thread { get; set; } public bool Running { get; set; } public bool Aborted { get; set; } private int started; + public bool DoTimeout; - public ThreadInfo(long threadFuncNum, string context) + public ThreadInfo(long threadFuncNum, string context, bool dotimeout = true) { ThreadFuncNum = threadFuncNum; this.context = context; @@ -2058,6 +2505,7 @@ namespace OpenSim.Framework Thread = null; Running = false; Aborted = false; + DoTimeout = dotimeout; } public void Started() @@ -2128,7 +2576,7 @@ namespace OpenSim.Framework foreach (KeyValuePair entry in activeThreads) { ThreadInfo t = entry.Value; - if (t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT)) + if (t.DoTimeout && t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT)) { m_log.WarnFormat("Timeout in threadfunc {0} ({1}) {2}", t.ThreadFuncNum, t.Thread.Name, t.GetStackTrace()); t.Abort(); @@ -2138,7 +2586,7 @@ namespace OpenSim.Framework // It's possible that the thread won't abort. To make sure the thread pool isn't // depleted, increase the pool size. - m_ThreadPool.MaxThreads++; +// m_ThreadPool.MaxThreads++; } } } @@ -2148,7 +2596,7 @@ namespace OpenSim.Framework public static Dictionary GetFireAndForgetCallsMade() { return new Dictionary(m_fireAndForgetCallsMade); - } + } private static Dictionary m_fireAndForgetCallsMade = new Dictionary(); @@ -2168,11 +2616,11 @@ namespace OpenSim.Framework { FireAndForget(callback, obj, null); } - - public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context) + + public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context, bool dotimeout = true) { Interlocked.Increment(ref numTotalThreadFuncsCalled); - +/* if (context != null) { if (!m_fireAndForgetCallsMade.ContainsKey(context)) @@ -2185,25 +2633,25 @@ namespace OpenSim.Framework else m_fireAndForgetCallsInProgress[context]++; } - +*/ WaitCallback realCallback; bool loggingEnabled = LogThreadPool > 0; - + long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum); - ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context); + ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context, dotimeout); if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) { // If we're running regression tests, then we want any exceptions to rise up to the test code. - realCallback = - o => - { - Culture.SetCurrentCulture(); - callback(o); - - if (context != null) - m_fireAndForgetCallsInProgress[context]--; + realCallback = + o => + { + Culture.SetCurrentCulture(); + callback(o); + +// if (context != null) +// m_fireAndForgetCallsInProgress[context]--; }; } else @@ -2229,7 +2677,6 @@ namespace OpenSim.Framework } catch (ThreadAbortException e) { - m_log.Error(string.Format("Aborted threadfunc {0} ", threadFuncNum), e); } catch (Exception e) { @@ -2244,8 +2691,8 @@ namespace OpenSim.Framework if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed())); - if (context != null) - m_fireAndForgetCallsInProgress[context]--; +// if (context != null) +// m_fireAndForgetCallsInProgress[context]--; } }; } @@ -2253,6 +2700,7 @@ namespace OpenSim.Framework long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs); try { +/* long numRunning = numRunningThreadFuncs; if (m_ThreadPool != null && LogOverloads) @@ -2285,6 +2733,7 @@ namespace OpenSim.Framework } } else +*/ { // Since we didn't log "Queue threadfunc", don't log "Run threadfunc" or "End threadfunc" either. // Those log lines aren't useful when we don't know which function is running in the thread. @@ -2342,7 +2791,7 @@ namespace OpenSim.Framework if (stackTrace.Contains("BeginFireQueueEmpty")) return false; } - + return true; } @@ -2355,7 +2804,7 @@ namespace OpenSim.Framework { string src = Environment.StackTrace; string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); - + StringBuilder dest = new StringBuilder(src.Length); bool started = false; @@ -2400,11 +2849,11 @@ namespace OpenSim.Framework /// trace. And pausing another thread can cause a deadlock. This method attempts to /// avoid deadlock by using a short timeout (200ms), after which it gives up and /// returns 'null' instead of the stack trace. - /// + /// /// Take from: http://stackoverflow.com/a/14935378 - /// + /// /// WARNING: this doesn't work in Mono. See https://bugzilla.novell.com/show_bug.cgi?id=571691 - /// + /// /// /// The stack trace, or null if failed to get it private static StackTrace GetStackTrace(Thread targetThread) @@ -2500,12 +2949,22 @@ namespace OpenSim.Framework return stpi; } + public static void StopThreadPool() + { + if (m_ThreadPool == null) + return; + SmartThreadPool pool = m_ThreadPool; + m_ThreadPool = null; + + try { pool.Shutdown(); } catch {} + } + #endregion FireAndForget Threading Pattern /// /// Environment.TickCount is an int but it counts all 32 bits so it goes positive /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap - /// for the callers. + /// for the callers. /// This trims it to a 12 day interval so don't let your frame time get too long. /// /// @@ -2513,6 +2972,7 @@ namespace OpenSim.Framework { return Environment.TickCount & EnvironmentTickCountMask; } + const Int32 EnvironmentTickCountMask = 0x3fffffff; /// @@ -2557,6 +3017,18 @@ namespace OpenSim.Framework return tcA - tcB; } + // returns a timestamp in ms as double + // using the time resolution avaiable to StopWatch + public static double GetTimeStamp() + { + return (double)Stopwatch.GetTimestamp() * TimeStampClockPeriod; + } + + public static double GetTimeStampMS() + { + return (double)Stopwatch.GetTimestamp() * TimeStampClockPeriodMS; + } + /// /// Formats a duration (given in milliseconds). /// @@ -2855,7 +3327,7 @@ namespace OpenSim.Framework if (parts.Length == 2) return CalcUniversalIdentifier(id, agentsURI, parts[0] + " " + parts[1]); } - + return CalcUniversalIdentifier(id, agentsURI, firstName + " " + lastName); } @@ -2940,6 +3412,7 @@ namespace OpenSim.Framework } +/* don't like this code public class DoubleQueue where T:class { private Queue m_lowQueue = new Queue(); @@ -2954,10 +3427,10 @@ namespace OpenSim.Framework public virtual int Count { - get - { + get + { lock (m_syncRoot) - return m_highQueue.Count + m_lowQueue.Count; + return m_highQueue.Count + m_lowQueue.Count; } } @@ -3051,7 +3524,7 @@ namespace OpenSim.Framework } } } - +*/ public class BetterRandom { private const int BufferSize = 1024; // must be a multiple of 4 -- cgit v1.1