diff options
author | UbitUmarov | 2015-09-01 11:43:07 +0100 |
---|---|---|
committer | UbitUmarov | 2015-09-01 11:43:07 +0100 |
commit | fb78b182520fc9bb0f971afd0322029c70278ea6 (patch) | |
tree | b4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Framework/Util.cs | |
parent | lixo (diff) | |
parent | Mantis #7713: fixed bug introduced by 1st MOSES patch. (diff) | |
download | opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2 opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz |
Merge remote-tracking branch 'os/master'
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/Util.cs | 3087 |
1 files changed, 3087 insertions, 0 deletions
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs new file mode 100644 index 0000000..a5f798d --- /dev/null +++ b/OpenSim/Framework/Util.cs | |||
@@ -0,0 +1,3087 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Data; | ||
32 | using System.Diagnostics; | ||
33 | using System.Globalization; | ||
34 | using System.IO; | ||
35 | using System.IO.Compression; | ||
36 | using System.Net; | ||
37 | using System.Net.Sockets; | ||
38 | using System.Reflection; | ||
39 | using System.Runtime.InteropServices; | ||
40 | using System.Runtime.Serialization; | ||
41 | using System.Runtime.Serialization.Formatters.Binary; | ||
42 | using System.Security.Cryptography; | ||
43 | using System.Text; | ||
44 | using System.Text.RegularExpressions; | ||
45 | using System.Xml; | ||
46 | using System.Threading; | ||
47 | using log4net; | ||
48 | using log4net.Appender; | ||
49 | using Nini.Config; | ||
50 | using Nwc.XmlRpc; | ||
51 | using OpenMetaverse; | ||
52 | using OpenMetaverse.StructuredData; | ||
53 | using Amib.Threading; | ||
54 | using System.Collections.Concurrent; | ||
55 | using System.Collections.Specialized; | ||
56 | using System.Web; | ||
57 | |||
58 | namespace OpenSim.Framework | ||
59 | { | ||
60 | [Flags] | ||
61 | public enum PermissionMask : uint | ||
62 | { | ||
63 | None = 0, | ||
64 | Transfer = 1 << 13, | ||
65 | Modify = 1 << 14, | ||
66 | Copy = 1 << 15, | ||
67 | Export = 1 << 16, | ||
68 | Move = 1 << 19, | ||
69 | Damage = 1 << 20, | ||
70 | // All does not contain Export, which is special and must be | ||
71 | // explicitly given | ||
72 | All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19) | ||
73 | } | ||
74 | |||
75 | /// <summary> | ||
76 | /// The method used by Util.FireAndForget for asynchronously firing events | ||
77 | /// </summary> | ||
78 | /// <remarks> | ||
79 | /// None is used to execute the method in the same thread that made the call. It should only be used by regression | ||
80 | /// test code that relies on predictable event ordering. | ||
81 | /// RegressionTest is used by regression tests. It fires the call synchronously and does not catch any exceptions. | ||
82 | /// </remarks> | ||
83 | public enum FireAndForgetMethod | ||
84 | { | ||
85 | None, | ||
86 | RegressionTest, | ||
87 | UnsafeQueueUserWorkItem, | ||
88 | QueueUserWorkItem, | ||
89 | BeginInvoke, | ||
90 | SmartThreadPool, | ||
91 | Thread, | ||
92 | } | ||
93 | |||
94 | /// <summary> | ||
95 | /// Class for delivering SmartThreadPool statistical information | ||
96 | /// </summary> | ||
97 | /// <remarks> | ||
98 | /// We do it this way so that we do not directly expose STP. | ||
99 | /// </remarks> | ||
100 | public class STPInfo | ||
101 | { | ||
102 | public string Name { get; set; } | ||
103 | public STPStartInfo STPStartInfo { get; set; } | ||
104 | public WIGStartInfo WIGStartInfo { get; set; } | ||
105 | public bool IsIdle { get; set; } | ||
106 | public bool IsShuttingDown { get; set; } | ||
107 | public int MaxThreads { get; set; } | ||
108 | public int MinThreads { get; set; } | ||
109 | public int InUseThreads { get; set; } | ||
110 | public int ActiveThreads { get; set; } | ||
111 | public int WaitingCallbacks { get; set; } | ||
112 | public int MaxConcurrentWorkItems { get; set; } | ||
113 | } | ||
114 | |||
115 | /// <summary> | ||
116 | /// Miscellaneous utility functions | ||
117 | /// </summary> | ||
118 | public static class Util | ||
119 | { | ||
120 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
121 | |||
122 | /// <summary> | ||
123 | /// Log-level for the thread pool: | ||
124 | /// 0 = no logging | ||
125 | /// 1 = only first line of stack trace; don't log common threads | ||
126 | /// 2 = full stack trace; don't log common threads | ||
127 | /// 3 = full stack trace, including common threads | ||
128 | /// </summary> | ||
129 | public static int LogThreadPool { get; set; } | ||
130 | public static bool LogOverloads { get; set; } | ||
131 | |||
132 | public static readonly int MAX_THREADPOOL_LEVEL = 3; | ||
133 | |||
134 | static Util() | ||
135 | { | ||
136 | LogThreadPool = 0; | ||
137 | LogOverloads = true; | ||
138 | } | ||
139 | |||
140 | private static uint nextXferID = 5000; | ||
141 | private static Random randomClass = new ThreadSafeRandom(); | ||
142 | |||
143 | // Get a list of invalid file characters (OS dependent) | ||
144 | private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; | ||
145 | private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]"; | ||
146 | private static object XferLock = new object(); | ||
147 | |||
148 | /// <summary> | ||
149 | /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used | ||
150 | /// </summary> | ||
151 | private static SmartThreadPool m_ThreadPool; | ||
152 | |||
153 | // Watchdog timer that aborts threads that have timed-out | ||
154 | private static Timer m_threadPoolWatchdog; | ||
155 | |||
156 | // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. | ||
157 | public static readonly DateTime UnixEpoch = | ||
158 | DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); | ||
159 | |||
160 | private static readonly string rawUUIDPattern | ||
161 | = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"; | ||
162 | public static readonly Regex PermissiveUUIDPattern = new Regex(rawUUIDPattern); | ||
163 | public static readonly Regex UUIDPattern = new Regex(string.Format("^{0}$", rawUUIDPattern)); | ||
164 | |||
165 | public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool; | ||
166 | public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod; | ||
167 | |||
168 | public static bool IsPlatformMono | ||
169 | { | ||
170 | get { return Type.GetType("Mono.Runtime") != null; } | ||
171 | } | ||
172 | |||
173 | /// <summary> | ||
174 | /// Gets the name of the directory where the current running executable | ||
175 | /// is located | ||
176 | /// </summary> | ||
177 | /// <returns>Filesystem path to the directory containing the current | ||
178 | /// executable</returns> | ||
179 | public static string ExecutingDirectory() | ||
180 | { | ||
181 | return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); | ||
182 | } | ||
183 | |||
184 | /// <summary> | ||
185 | /// Linear interpolates B<->C using percent A | ||
186 | /// </summary> | ||
187 | /// <param name="a"></param> | ||
188 | /// <param name="b"></param> | ||
189 | /// <param name="c"></param> | ||
190 | /// <returns></returns> | ||
191 | public static double lerp(double a, double b, double c) | ||
192 | { | ||
193 | return (b*a) + (c*(1 - a)); | ||
194 | } | ||
195 | |||
196 | /// <summary> | ||
197 | /// Bilinear Interpolate, see Lerp but for 2D using 'percents' X & Y. | ||
198 | /// Layout: | ||
199 | /// A B | ||
200 | /// C D | ||
201 | /// A<->C = Y | ||
202 | /// C<->D = X | ||
203 | /// </summary> | ||
204 | /// <param name="x"></param> | ||
205 | /// <param name="y"></param> | ||
206 | /// <param name="a"></param> | ||
207 | /// <param name="b"></param> | ||
208 | /// <param name="c"></param> | ||
209 | /// <param name="d"></param> | ||
210 | /// <returns></returns> | ||
211 | public static double lerp2D(double x, double y, double a, double b, double c, double d) | ||
212 | { | ||
213 | return lerp(y, lerp(x, a, b), lerp(x, c, d)); | ||
214 | } | ||
215 | |||
216 | public static Encoding UTF8 = Encoding.UTF8; | ||
217 | public static Encoding UTF8NoBomEncoding = new UTF8Encoding(false); | ||
218 | |||
219 | /// <value> | ||
220 | /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards) | ||
221 | /// </value> | ||
222 | public static UUID BLANK_TEXTURE_UUID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f"); | ||
223 | |||
224 | #region Vector Equations | ||
225 | |||
226 | /// <summary> | ||
227 | /// Get the distance between two 3d vectors | ||
228 | /// </summary> | ||
229 | /// <param name="a">A 3d vector</param> | ||
230 | /// <param name="b">A 3d vector</param> | ||
231 | /// <returns>The distance between the two vectors</returns> | ||
232 | public static double GetDistanceTo(Vector3 a, Vector3 b) | ||
233 | { | ||
234 | float dx = a.X - b.X; | ||
235 | float dy = a.Y - b.Y; | ||
236 | float dz = a.Z - b.Z; | ||
237 | return Math.Sqrt(dx * dx + dy * dy + dz * dz); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Returns true if the distance beween A and B is less than amount. Significantly faster than GetDistanceTo since it eliminates the Sqrt. | ||
242 | /// </summary> | ||
243 | /// <param name="a"></param> | ||
244 | /// <param name="b"></param> | ||
245 | /// <param name="amount"></param> | ||
246 | /// <returns></returns> | ||
247 | public static bool DistanceLessThan(Vector3 a, Vector3 b, double amount) | ||
248 | { | ||
249 | float dx = a.X - b.X; | ||
250 | float dy = a.Y - b.Y; | ||
251 | float dz = a.Z - b.Z; | ||
252 | return (dx*dx + dy*dy + dz*dz) < (amount*amount); | ||
253 | } | ||
254 | |||
255 | /// <summary> | ||
256 | /// Get the magnitude of a 3d vector | ||
257 | /// </summary> | ||
258 | /// <param name="a">A 3d vector</param> | ||
259 | /// <returns>The magnitude of the vector</returns> | ||
260 | public static double GetMagnitude(Vector3 a) | ||
261 | { | ||
262 | return Math.Sqrt((a.X * a.X) + (a.Y * a.Y) + (a.Z * a.Z)); | ||
263 | } | ||
264 | |||
265 | /// <summary> | ||
266 | /// Get a normalized form of a 3d vector | ||
267 | /// </summary> | ||
268 | /// <param name="a">A 3d vector</param> | ||
269 | /// <returns>A new vector which is normalized form of the vector</returns> | ||
270 | /// <remarks>The vector paramater cannot be <0,0,0></remarks> | ||
271 | public static Vector3 GetNormalizedVector(Vector3 a) | ||
272 | { | ||
273 | if (IsZeroVector(a)) | ||
274 | throw new ArgumentException("Vector paramater cannot be a zero vector."); | ||
275 | |||
276 | float Mag = (float) GetMagnitude(a); | ||
277 | return new Vector3(a.X / Mag, a.Y / Mag, a.Z / Mag); | ||
278 | } | ||
279 | |||
280 | /// <summary> | ||
281 | /// Returns if a vector is a zero vector (has all zero components) | ||
282 | /// </summary> | ||
283 | /// <returns></returns> | ||
284 | public static bool IsZeroVector(Vector3 v) | ||
285 | { | ||
286 | if (v.X == 0 && v.Y == 0 && v.Z == 0) | ||
287 | { | ||
288 | return true; | ||
289 | } | ||
290 | |||
291 | return false; | ||
292 | } | ||
293 | |||
294 | # endregion | ||
295 | |||
296 | public static Quaternion Axes2Rot(Vector3 fwd, Vector3 left, Vector3 up) | ||
297 | { | ||
298 | float s; | ||
299 | float tr = (float) (fwd.X + left.Y + up.Z + 1.0); | ||
300 | |||
301 | if (tr >= 1.0) | ||
302 | { | ||
303 | s = (float) (0.5 / Math.Sqrt(tr)); | ||
304 | return new Quaternion( | ||
305 | (left.Z - up.Y) * s, | ||
306 | (up.X - fwd.Z) * s, | ||
307 | (fwd.Y - left.X) * s, | ||
308 | (float) 0.25 / s); | ||
309 | } | ||
310 | else | ||
311 | { | ||
312 | float max = (left.Y > up.Z) ? left.Y : up.Z; | ||
313 | |||
314 | if (max < fwd.X) | ||
315 | { | ||
316 | s = (float) (Math.Sqrt(fwd.X - (left.Y + up.Z) + 1.0)); | ||
317 | float x = (float) (s * 0.5); | ||
318 | s = (float) (0.5 / s); | ||
319 | return new Quaternion( | ||
320 | x, | ||
321 | (fwd.Y + left.X) * s, | ||
322 | (up.X + fwd.Z) * s, | ||
323 | (left.Z - up.Y) * s); | ||
324 | } | ||
325 | else if (max == left.Y) | ||
326 | { | ||
327 | s = (float) (Math.Sqrt(left.Y - (up.Z + fwd.X) + 1.0)); | ||
328 | float y = (float) (s * 0.5); | ||
329 | s = (float) (0.5 / s); | ||
330 | return new Quaternion( | ||
331 | (fwd.Y + left.X) * s, | ||
332 | y, | ||
333 | (left.Z + up.Y) * s, | ||
334 | (up.X - fwd.Z) * s); | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | s = (float) (Math.Sqrt(up.Z - (fwd.X + left.Y) + 1.0)); | ||
339 | float z = (float) (s * 0.5); | ||
340 | s = (float) (0.5 / s); | ||
341 | return new Quaternion( | ||
342 | (up.X + fwd.Z) * s, | ||
343 | (left.Z + up.Y) * s, | ||
344 | z, | ||
345 | (fwd.Y - left.X) * s); | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | |||
350 | public static Random RandomClass | ||
351 | { | ||
352 | get { return randomClass; } | ||
353 | } | ||
354 | |||
355 | public static ulong UIntsToLong(uint X, uint Y) | ||
356 | { | ||
357 | return Utils.UIntsToLong(X, Y); | ||
358 | } | ||
359 | |||
360 | // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong. | ||
361 | // Several places rely on the ability to extract a region's location from its handle. | ||
362 | // Note the location is in 'world coordinates' (see below). | ||
363 | // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0. | ||
364 | public static ulong RegionWorldLocToHandle(uint X, uint Y) | ||
365 | { | ||
366 | return Utils.UIntsToLong(X, Y); | ||
367 | } | ||
368 | |||
369 | public static ulong RegionLocToHandle(uint X, uint Y) | ||
370 | { | ||
371 | return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y)); | ||
372 | } | ||
373 | |||
374 | public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y) | ||
375 | { | ||
376 | X = (uint)(handle >> 32); | ||
377 | Y = (uint)(handle & (ulong)uint.MaxValue); | ||
378 | } | ||
379 | |||
380 | public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y) | ||
381 | { | ||
382 | uint worldX, worldY; | ||
383 | RegionHandleToWorldLoc(handle, out worldX, out worldY); | ||
384 | X = WorldToRegionLoc(worldX); | ||
385 | Y = WorldToRegionLoc(worldY); | ||
386 | } | ||
387 | |||
388 | // A region location can be 'world coordinates' (meters from zero) or 'region coordinates' | ||
389 | // (number of regions from zero). This measurement of regions relies on the legacy 256 region size. | ||
390 | // These routines exist to make what is being converted explicit so the next person knows what was meant. | ||
391 | // Convert a region's 'world coordinate' to its 'region coordinate'. | ||
392 | public static uint WorldToRegionLoc(uint worldCoord) | ||
393 | { | ||
394 | return worldCoord / Constants.RegionSize; | ||
395 | } | ||
396 | |||
397 | // Convert a region's 'region coordinate' to its 'world coordinate'. | ||
398 | public static uint RegionToWorldLoc(uint regionCoord) | ||
399 | { | ||
400 | return regionCoord * Constants.RegionSize; | ||
401 | } | ||
402 | |||
403 | public static T Clamp<T>(T x, T min, T max) | ||
404 | where T : IComparable<T> | ||
405 | { | ||
406 | return x.CompareTo(max) > 0 ? max : | ||
407 | x.CompareTo(min) < 0 ? min : | ||
408 | x; | ||
409 | } | ||
410 | |||
411 | // Clamp the maximum magnitude of a vector | ||
412 | public static Vector3 ClampV(Vector3 x, float max) | ||
413 | { | ||
414 | float lenSq = x.LengthSquared(); | ||
415 | if (lenSq > (max * max)) | ||
416 | { | ||
417 | x = x / x.Length() * max; | ||
418 | } | ||
419 | |||
420 | return x; | ||
421 | } | ||
422 | |||
423 | /// <summary> | ||
424 | /// Check if any of the values in a Vector3 are NaN or Infinity | ||
425 | /// </summary> | ||
426 | /// <param name="v">Vector3 to check</param> | ||
427 | /// <returns></returns> | ||
428 | public static bool IsNanOrInfinity(Vector3 v) | ||
429 | { | ||
430 | if (float.IsNaN(v.X) || float.IsNaN(v.Y) || float.IsNaN(v.Z)) | ||
431 | return true; | ||
432 | |||
433 | if (float.IsInfinity(v.X) || float.IsInfinity(v.Y) || float.IsNaN(v.Z)) | ||
434 | return true; | ||
435 | |||
436 | return false; | ||
437 | } | ||
438 | |||
439 | // Inclusive, within range test (true if equal to the endpoints) | ||
440 | public static bool InRange<T>(T x, T min, T max) | ||
441 | where T : IComparable<T> | ||
442 | { | ||
443 | return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0; | ||
444 | } | ||
445 | |||
446 | public static uint GetNextXferID() | ||
447 | { | ||
448 | uint id = 0; | ||
449 | lock (XferLock) | ||
450 | { | ||
451 | id = nextXferID; | ||
452 | nextXferID++; | ||
453 | } | ||
454 | return id; | ||
455 | } | ||
456 | |||
457 | public static string GetFileName(string file) | ||
458 | { | ||
459 | // Return just the filename on UNIX platforms | ||
460 | // TODO: this should be customisable with a prefix, but that's something to do later. | ||
461 | if (Environment.OSVersion.Platform == PlatformID.Unix) | ||
462 | { | ||
463 | return file; | ||
464 | } | ||
465 | |||
466 | // Return %APPDATA%/OpenSim/file for 2K/XP/NT/2K3/VISTA | ||
467 | // TODO: Switch this to System.Enviroment.SpecialFolders.ApplicationData | ||
468 | if (Environment.OSVersion.Platform == PlatformID.Win32NT) | ||
469 | { | ||
470 | if (!Directory.Exists("%APPDATA%\\OpenSim\\")) | ||
471 | { | ||
472 | Directory.CreateDirectory("%APPDATA%\\OpenSim"); | ||
473 | } | ||
474 | |||
475 | return "%APPDATA%\\OpenSim\\" + file; | ||
476 | } | ||
477 | |||
478 | // Catch all - covers older windows versions | ||
479 | // (but those probably wont work anyway) | ||
480 | return file; | ||
481 | } | ||
482 | |||
483 | /// <summary> | ||
484 | /// Debug utility function to convert OSD into formatted XML for debugging purposes. | ||
485 | /// </summary> | ||
486 | /// <param name="osd"> | ||
487 | /// A <see cref="OSD"/> | ||
488 | /// </param> | ||
489 | /// <returns> | ||
490 | /// A <see cref="System.String"/> | ||
491 | /// </returns> | ||
492 | public static string GetFormattedXml(OSD osd) | ||
493 | { | ||
494 | return GetFormattedXml(OSDParser.SerializeLLSDXmlString(osd)); | ||
495 | } | ||
496 | |||
497 | /// <summary> | ||
498 | /// Debug utility function to convert unbroken strings of XML into something human readable for occasional debugging purposes. | ||
499 | /// </summary> | ||
500 | /// <remarks> | ||
501 | /// Please don't delete me even if I appear currently unused! | ||
502 | /// </remarks> | ||
503 | /// <param name="rawXml"></param> | ||
504 | /// <returns></returns> | ||
505 | public static string GetFormattedXml(string rawXml) | ||
506 | { | ||
507 | XmlDocument xd = new XmlDocument(); | ||
508 | xd.LoadXml(rawXml); | ||
509 | |||
510 | StringBuilder sb = new StringBuilder(); | ||
511 | StringWriter sw = new StringWriter(sb); | ||
512 | |||
513 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
514 | xtw.Formatting = Formatting.Indented; | ||
515 | |||
516 | try | ||
517 | { | ||
518 | xd.WriteTo(xtw); | ||
519 | } | ||
520 | finally | ||
521 | { | ||
522 | xtw.Close(); | ||
523 | } | ||
524 | |||
525 | return sb.ToString(); | ||
526 | } | ||
527 | |||
528 | public static byte[] DocToBytes(XmlDocument doc) | ||
529 | { | ||
530 | using (MemoryStream ms = new MemoryStream()) | ||
531 | using (XmlTextWriter xw = new XmlTextWriter(ms, null)) | ||
532 | { | ||
533 | xw.Formatting = Formatting.Indented; | ||
534 | doc.WriteTo(xw); | ||
535 | xw.Flush(); | ||
536 | |||
537 | return ms.ToArray(); | ||
538 | } | ||
539 | } | ||
540 | |||
541 | /// <summary> | ||
542 | /// Is the platform Windows? | ||
543 | /// </summary> | ||
544 | /// <returns>true if so, false otherwise</returns> | ||
545 | public static bool IsWindows() | ||
546 | { | ||
547 | PlatformID platformId = Environment.OSVersion.Platform; | ||
548 | |||
549 | return (platformId == PlatformID.Win32NT | ||
550 | || platformId == PlatformID.Win32S | ||
551 | || platformId == PlatformID.Win32Windows | ||
552 | || platformId == PlatformID.WinCE); | ||
553 | } | ||
554 | |||
555 | public static bool LoadArchSpecificWindowsDll(string libraryName) | ||
556 | { | ||
557 | return LoadArchSpecificWindowsDll(libraryName, string.Empty); | ||
558 | } | ||
559 | |||
560 | public static bool LoadArchSpecificWindowsDll(string libraryName, string path) | ||
561 | { | ||
562 | // We do this so that OpenSimulator on Windows loads the correct native library depending on whether | ||
563 | // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports | ||
564 | // will find it already loaded later on. | ||
565 | // | ||
566 | // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be | ||
567 | // controlled in config files. | ||
568 | string nativeLibraryPath; | ||
569 | |||
570 | if (Util.Is64BitProcess()) | ||
571 | nativeLibraryPath = Path.Combine(Path.Combine(path, "lib64"), libraryName); | ||
572 | else | ||
573 | nativeLibraryPath = Path.Combine(Path.Combine(path, "lib32"), libraryName); | ||
574 | |||
575 | m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath); | ||
576 | |||
577 | if (Util.LoadLibrary(nativeLibraryPath) == IntPtr.Zero) | ||
578 | { | ||
579 | m_log.ErrorFormat( | ||
580 | "[UTIL]: Couldn't find native Windows library at {0}", nativeLibraryPath); | ||
581 | |||
582 | return false; | ||
583 | } | ||
584 | else | ||
585 | { | ||
586 | return true; | ||
587 | } | ||
588 | } | ||
589 | |||
590 | public static bool IsEnvironmentSupported(ref string reason) | ||
591 | { | ||
592 | // Must have .NET 2.0 (Generics / libsl) | ||
593 | if (Environment.Version.Major < 2) | ||
594 | { | ||
595 | reason = ".NET 1.0/1.1 lacks components that is used by OpenSim"; | ||
596 | return false; | ||
597 | } | ||
598 | |||
599 | // Windows 95/98/ME are unsupported | ||
600 | if (Environment.OSVersion.Platform == PlatformID.Win32Windows && | ||
601 | Environment.OSVersion.Platform != PlatformID.Win32NT) | ||
602 | { | ||
603 | reason = "Windows 95/98/ME will not run OpenSim"; | ||
604 | return false; | ||
605 | } | ||
606 | |||
607 | // Windows 2000 / Pre-SP2 XP | ||
608 | if (Environment.OSVersion.Version.Major == 5 && | ||
609 | Environment.OSVersion.Version.Minor == 0) | ||
610 | { | ||
611 | reason = "Please update to Windows XP Service Pack 2 or Server2003"; | ||
612 | return false; | ||
613 | } | ||
614 | |||
615 | return true; | ||
616 | } | ||
617 | |||
618 | public static int UnixTimeSinceEpoch() | ||
619 | { | ||
620 | return ToUnixTime(DateTime.UtcNow); | ||
621 | } | ||
622 | |||
623 | public static int ToUnixTime(DateTime stamp) | ||
624 | { | ||
625 | TimeSpan t = stamp.ToUniversalTime() - UnixEpoch; | ||
626 | return (int)t.TotalSeconds; | ||
627 | } | ||
628 | |||
629 | public static DateTime ToDateTime(ulong seconds) | ||
630 | { | ||
631 | return UnixEpoch.AddSeconds(seconds); | ||
632 | } | ||
633 | |||
634 | public static DateTime ToDateTime(int seconds) | ||
635 | { | ||
636 | return UnixEpoch.AddSeconds(seconds); | ||
637 | } | ||
638 | |||
639 | /// <summary> | ||
640 | /// Return an md5 hash of the given string | ||
641 | /// </summary> | ||
642 | /// <param name="data"></param> | ||
643 | /// <returns></returns> | ||
644 | public static string Md5Hash(string data) | ||
645 | { | ||
646 | byte[] dataMd5 = ComputeMD5Hash(data); | ||
647 | StringBuilder sb = new StringBuilder(); | ||
648 | for (int i = 0; i < dataMd5.Length; i++) | ||
649 | sb.AppendFormat("{0:x2}", dataMd5[i]); | ||
650 | return sb.ToString(); | ||
651 | } | ||
652 | |||
653 | private static byte[] ComputeMD5Hash(string data) | ||
654 | { | ||
655 | MD5 md5 = MD5.Create(); | ||
656 | return md5.ComputeHash(Encoding.Default.GetBytes(data)); | ||
657 | } | ||
658 | |||
659 | /// <summary> | ||
660 | /// Return an SHA1 hash | ||
661 | /// </summary> | ||
662 | /// <param name="data"></param> | ||
663 | /// <returns></returns> | ||
664 | public static string SHA1Hash(string data) | ||
665 | { | ||
666 | return SHA1Hash(Encoding.Default.GetBytes(data)); | ||
667 | } | ||
668 | |||
669 | /// <summary> | ||
670 | /// Return an SHA1 hash | ||
671 | /// </summary> | ||
672 | /// <param name="data"></param> | ||
673 | /// <returns></returns> | ||
674 | public static string SHA1Hash(byte[] data) | ||
675 | { | ||
676 | byte[] hash = ComputeSHA1Hash(data); | ||
677 | return BitConverter.ToString(hash).Replace("-", String.Empty); | ||
678 | } | ||
679 | |||
680 | private static byte[] ComputeSHA1Hash(byte[] src) | ||
681 | { | ||
682 | SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider(); | ||
683 | return SHA1.ComputeHash(src); | ||
684 | } | ||
685 | |||
686 | public static int fast_distance2d(int x, int y) | ||
687 | { | ||
688 | x = Math.Abs(x); | ||
689 | y = Math.Abs(y); | ||
690 | |||
691 | int min = Math.Min(x, y); | ||
692 | |||
693 | return (x + y - (min >> 1) - (min >> 2) + (min >> 4)); | ||
694 | } | ||
695 | |||
696 | /// <summary> | ||
697 | /// Determines whether a point is inside a bounding box. | ||
698 | /// </summary> | ||
699 | /// <param name='v'></param> | ||
700 | /// <param name='min'></param> | ||
701 | /// <param name='max'></param> | ||
702 | /// <returns></returns> | ||
703 | public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max) | ||
704 | { | ||
705 | return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z | ||
706 | && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z; | ||
707 | } | ||
708 | |||
709 | /// <summary> | ||
710 | /// Are the co-ordinates of the new region visible from the old region? | ||
711 | /// </summary> | ||
712 | /// <param name="oldx">Old region x-coord</param> | ||
713 | /// <param name="newx">New region x-coord</param> | ||
714 | /// <param name="oldy">Old region y-coord</param> | ||
715 | /// <param name="newy">New region y-coord</param> | ||
716 | /// <returns></returns> | ||
717 | public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy) | ||
718 | { | ||
719 | int dd = (int)((drawdist + Constants.RegionSize - 1) / Constants.RegionSize); | ||
720 | |||
721 | int startX = (int)oldx - dd; | ||
722 | int startY = (int)oldy - dd; | ||
723 | |||
724 | int endX = (int)oldx + dd; | ||
725 | int endY = (int)oldy + dd; | ||
726 | |||
727 | return (newx < startX || endX < newx || newy < startY || endY < newy); | ||
728 | } | ||
729 | |||
730 | public static string FieldToString(byte[] bytes) | ||
731 | { | ||
732 | return FieldToString(bytes, String.Empty); | ||
733 | } | ||
734 | |||
735 | /// <summary> | ||
736 | /// Convert a variable length field (byte array) to a string, with a | ||
737 | /// field name prepended to each line of the output | ||
738 | /// </summary> | ||
739 | /// <remarks>If the byte array has unprintable characters in it, a | ||
740 | /// hex dump will be put in the string instead</remarks> | ||
741 | /// <param name="bytes">The byte array to convert to a string</param> | ||
742 | /// <param name="fieldName">A field name to prepend to each line of output</param> | ||
743 | /// <returns>An ASCII string or a string containing a hex dump, minus | ||
744 | /// the null terminator</returns> | ||
745 | public static string FieldToString(byte[] bytes, string fieldName) | ||
746 | { | ||
747 | // Check for a common case | ||
748 | if (bytes.Length == 0) return String.Empty; | ||
749 | |||
750 | StringBuilder output = new StringBuilder(); | ||
751 | bool printable = true; | ||
752 | |||
753 | for (int i = 0; i < bytes.Length; ++i) | ||
754 | { | ||
755 | // Check if there are any unprintable characters in the array | ||
756 | if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09 | ||
757 | && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00) | ||
758 | { | ||
759 | printable = false; | ||
760 | break; | ||
761 | } | ||
762 | } | ||
763 | |||
764 | if (printable) | ||
765 | { | ||
766 | if (fieldName.Length > 0) | ||
767 | { | ||
768 | output.Append(fieldName); | ||
769 | output.Append(": "); | ||
770 | } | ||
771 | |||
772 | output.Append(CleanString(Util.UTF8.GetString(bytes, 0, bytes.Length - 1))); | ||
773 | } | ||
774 | else | ||
775 | { | ||
776 | for (int i = 0; i < bytes.Length; i += 16) | ||
777 | { | ||
778 | if (i != 0) | ||
779 | output.Append(Environment.NewLine); | ||
780 | if (fieldName.Length > 0) | ||
781 | { | ||
782 | output.Append(fieldName); | ||
783 | output.Append(": "); | ||
784 | } | ||
785 | |||
786 | for (int j = 0; j < 16; j++) | ||
787 | { | ||
788 | if ((i + j) < bytes.Length) | ||
789 | output.Append(String.Format("{0:X2} ", bytes[i + j])); | ||
790 | else | ||
791 | output.Append(" "); | ||
792 | } | ||
793 | |||
794 | for (int j = 0; j < 16 && (i + j) < bytes.Length; j++) | ||
795 | { | ||
796 | if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E) | ||
797 | output.Append((char) bytes[i + j]); | ||
798 | else | ||
799 | output.Append("."); | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | |||
804 | return output.ToString(); | ||
805 | } | ||
806 | |||
807 | /// <summary> | ||
808 | /// Returns a IP address from a specified DNS, favouring IPv4 addresses. | ||
809 | /// </summary> | ||
810 | /// <param name="dnsAddress">DNS Hostname</param> | ||
811 | /// <returns>An IP address, or null</returns> | ||
812 | public static IPAddress GetHostFromDNS(string dnsAddress) | ||
813 | { | ||
814 | // Is it already a valid IP? No need to look it up. | ||
815 | IPAddress ipa; | ||
816 | if (IPAddress.TryParse(dnsAddress, out ipa)) | ||
817 | return ipa; | ||
818 | |||
819 | IPAddress[] hosts = null; | ||
820 | |||
821 | // Not an IP, lookup required | ||
822 | try | ||
823 | { | ||
824 | hosts = Dns.GetHostEntry(dnsAddress).AddressList; | ||
825 | } | ||
826 | catch (Exception e) | ||
827 | { | ||
828 | m_log.WarnFormat("[UTIL]: An error occurred while resolving host name {0}, {1}", dnsAddress, e); | ||
829 | |||
830 | // Still going to throw the exception on for now, since this was what was happening in the first place | ||
831 | throw e; | ||
832 | } | ||
833 | |||
834 | foreach (IPAddress host in hosts) | ||
835 | { | ||
836 | if (host.AddressFamily == AddressFamily.InterNetwork) | ||
837 | { | ||
838 | return host; | ||
839 | } | ||
840 | } | ||
841 | |||
842 | if (hosts.Length > 0) | ||
843 | return hosts[0]; | ||
844 | |||
845 | return null; | ||
846 | } | ||
847 | |||
848 | public static Uri GetURI(string protocol, string hostname, int port, string path) | ||
849 | { | ||
850 | return new UriBuilder(protocol, hostname, port, path).Uri; | ||
851 | } | ||
852 | |||
853 | /// <summary> | ||
854 | /// Gets a list of all local system IP addresses | ||
855 | /// </summary> | ||
856 | /// <returns></returns> | ||
857 | public static IPAddress[] GetLocalHosts() | ||
858 | { | ||
859 | return Dns.GetHostAddresses(Dns.GetHostName()); | ||
860 | } | ||
861 | |||
862 | public static IPAddress GetLocalHost() | ||
863 | { | ||
864 | IPAddress[] iplist = GetLocalHosts(); | ||
865 | |||
866 | if (iplist.Length == 0) // No accessible external interfaces | ||
867 | { | ||
868 | IPAddress[] loopback = Dns.GetHostAddresses("localhost"); | ||
869 | IPAddress localhost = loopback[0]; | ||
870 | |||
871 | return localhost; | ||
872 | } | ||
873 | |||
874 | foreach (IPAddress host in iplist) | ||
875 | { | ||
876 | if (!IPAddress.IsLoopback(host) && host.AddressFamily == AddressFamily.InterNetwork) | ||
877 | { | ||
878 | return host; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | if (iplist.Length > 0) | ||
883 | { | ||
884 | foreach (IPAddress host in iplist) | ||
885 | { | ||
886 | if (host.AddressFamily == AddressFamily.InterNetwork) | ||
887 | return host; | ||
888 | } | ||
889 | // Well all else failed... | ||
890 | return iplist[0]; | ||
891 | } | ||
892 | |||
893 | return null; | ||
894 | } | ||
895 | |||
896 | /// <summary> | ||
897 | /// Parses a foreign asset ID. | ||
898 | /// </summary> | ||
899 | /// <param name="id">A possibly-foreign asset ID: http://grid.example.com:8002/00000000-0000-0000-0000-000000000000 </param> | ||
900 | /// <param name="url">The URL: http://grid.example.com:8002</param> | ||
901 | /// <param name="assetID">The asset ID: 00000000-0000-0000-0000-000000000000. Returned even if 'id' isn't foreign.</param> | ||
902 | /// <returns>True: this is a foreign asset ID; False: it isn't</returns> | ||
903 | public static bool ParseForeignAssetID(string id, out string url, out string assetID) | ||
904 | { | ||
905 | url = String.Empty; | ||
906 | assetID = String.Empty; | ||
907 | |||
908 | UUID uuid; | ||
909 | if (UUID.TryParse(id, out uuid)) | ||
910 | { | ||
911 | assetID = uuid.ToString(); | ||
912 | return false; | ||
913 | } | ||
914 | |||
915 | if ((id.Length == 0) || (id[0] != 'h' && id[0] != 'H')) | ||
916 | return false; | ||
917 | |||
918 | Uri assetUri; | ||
919 | if (!Uri.TryCreate(id, UriKind.Absolute, out assetUri) || assetUri.Scheme != Uri.UriSchemeHttp) | ||
920 | return false; | ||
921 | |||
922 | // Simian | ||
923 | if (assetUri.Query != string.Empty) | ||
924 | { | ||
925 | NameValueCollection qscoll = HttpUtility.ParseQueryString(assetUri.Query); | ||
926 | assetID = qscoll["id"]; | ||
927 | if (assetID != null) | ||
928 | url = id.Replace(assetID, ""); // Malformed again, as simian expects | ||
929 | else | ||
930 | url = id; // !!! best effort | ||
931 | } | ||
932 | else // robust | ||
933 | { | ||
934 | url = "http://" + assetUri.Authority; | ||
935 | assetID = assetUri.LocalPath.Trim(new char[] { '/' }); | ||
936 | } | ||
937 | |||
938 | if (!UUID.TryParse(assetID, out uuid)) | ||
939 | return false; | ||
940 | |||
941 | return true; | ||
942 | } | ||
943 | |||
944 | /// <summary> | ||
945 | /// Removes all invalid path chars (OS dependent) | ||
946 | /// </summary> | ||
947 | /// <param name="path">path</param> | ||
948 | /// <returns>safe path</returns> | ||
949 | public static string safePath(string path) | ||
950 | { | ||
951 | return Regex.Replace(path, regexInvalidPathChars, String.Empty); | ||
952 | } | ||
953 | |||
954 | /// <summary> | ||
955 | /// Removes all invalid filename chars (OS dependent) | ||
956 | /// </summary> | ||
957 | /// <param name="path">filename</param> | ||
958 | /// <returns>safe filename</returns> | ||
959 | public static string safeFileName(string filename) | ||
960 | { | ||
961 | return Regex.Replace(filename, regexInvalidFileChars, String.Empty); | ||
962 | ; | ||
963 | } | ||
964 | |||
965 | // | ||
966 | // directory locations | ||
967 | // | ||
968 | |||
969 | public static string homeDir() | ||
970 | { | ||
971 | string temp; | ||
972 | // string personal=(Environment.GetFolderPath(Environment.SpecialFolder.Personal)); | ||
973 | // temp = Path.Combine(personal,".OpenSim"); | ||
974 | temp = "."; | ||
975 | return temp; | ||
976 | } | ||
977 | |||
978 | public static string assetsDir() | ||
979 | { | ||
980 | return Path.Combine(configDir(), "assets"); | ||
981 | } | ||
982 | |||
983 | public static string inventoryDir() | ||
984 | { | ||
985 | return Path.Combine(configDir(), "inventory"); | ||
986 | } | ||
987 | |||
988 | public static string configDir() | ||
989 | { | ||
990 | return "."; | ||
991 | } | ||
992 | |||
993 | public static string dataDir() | ||
994 | { | ||
995 | return "."; | ||
996 | } | ||
997 | |||
998 | public static string logFile() | ||
999 | { | ||
1000 | foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) | ||
1001 | { | ||
1002 | if (appender is FileAppender) | ||
1003 | { | ||
1004 | return ((FileAppender)appender).File; | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | return "./OpenSim.log"; | ||
1009 | } | ||
1010 | |||
1011 | public static string logDir() | ||
1012 | { | ||
1013 | return Path.GetDirectoryName(logFile()); | ||
1014 | } | ||
1015 | |||
1016 | // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html | ||
1017 | public static string GetUniqueFilename(string FileName) | ||
1018 | { | ||
1019 | int count = 0; | ||
1020 | string Name; | ||
1021 | |||
1022 | if (File.Exists(FileName)) | ||
1023 | { | ||
1024 | FileInfo f = new FileInfo(FileName); | ||
1025 | |||
1026 | if (!String.IsNullOrEmpty(f.Extension)) | ||
1027 | { | ||
1028 | Name = f.FullName.Substring(0, f.FullName.LastIndexOf('.')); | ||
1029 | } | ||
1030 | else | ||
1031 | { | ||
1032 | Name = f.FullName; | ||
1033 | } | ||
1034 | |||
1035 | while (File.Exists(FileName)) | ||
1036 | { | ||
1037 | count++; | ||
1038 | FileName = Name + count + f.Extension; | ||
1039 | } | ||
1040 | } | ||
1041 | return FileName; | ||
1042 | } | ||
1043 | |||
1044 | #region Nini (config) related Methods | ||
1045 | |||
1046 | public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) | ||
1047 | { | ||
1048 | if (!File.Exists(fileName)) | ||
1049 | { | ||
1050 | // create new file | ||
1051 | } | ||
1052 | XmlConfigSource config = new XmlConfigSource(fileName); | ||
1053 | AddDataRowToConfig(config, row); | ||
1054 | config.Save(); | ||
1055 | |||
1056 | return config; | ||
1057 | } | ||
1058 | |||
1059 | public static void AddDataRowToConfig(IConfigSource config, DataRow row) | ||
1060 | { | ||
1061 | config.Configs.Add((string) row[0]); | ||
1062 | for (int i = 0; i < row.Table.Columns.Count; i++) | ||
1063 | { | ||
1064 | config.Configs[(string) row[0]].Set(row.Table.Columns[i].ColumnName, row[i]); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | /// <summary> | ||
1069 | /// Gets the value of a configuration variable by looking into | ||
1070 | /// multiple sections in order. The latter sections overwrite | ||
1071 | /// any values previously found. | ||
1072 | /// </summary> | ||
1073 | /// <typeparam name="T">Type of the variable</typeparam> | ||
1074 | /// <param name="config">The configuration object</param> | ||
1075 | /// <param name="varname">The configuration variable</param> | ||
1076 | /// <param name="sections">Ordered sequence of sections to look at</param> | ||
1077 | /// <returns></returns> | ||
1078 | public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections) | ||
1079 | { | ||
1080 | return GetConfigVarFromSections<T>(config, varname, sections, default(T)); | ||
1081 | } | ||
1082 | |||
1083 | /// <summary> | ||
1084 | /// Gets the value of a configuration variable by looking into | ||
1085 | /// multiple sections in order. The latter sections overwrite | ||
1086 | /// any values previously found. | ||
1087 | /// </summary> | ||
1088 | /// <remarks> | ||
1089 | /// If no value is found then the given default value is returned | ||
1090 | /// </remarks> | ||
1091 | /// <typeparam name="T">Type of the variable</typeparam> | ||
1092 | /// <param name="config">The configuration object</param> | ||
1093 | /// <param name="varname">The configuration variable</param> | ||
1094 | /// <param name="sections">Ordered sequence of sections to look at</param> | ||
1095 | /// <param name="val">Default value</param> | ||
1096 | /// <returns></returns> | ||
1097 | public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val) | ||
1098 | { | ||
1099 | foreach (string section in sections) | ||
1100 | { | ||
1101 | IConfig cnf = config.Configs[section]; | ||
1102 | if (cnf == null) | ||
1103 | continue; | ||
1104 | |||
1105 | if (typeof(T) == typeof(String)) | ||
1106 | val = cnf.GetString(varname, (string)val); | ||
1107 | else if (typeof(T) == typeof(Boolean)) | ||
1108 | val = cnf.GetBoolean(varname, (bool)val); | ||
1109 | else if (typeof(T) == typeof(Int32)) | ||
1110 | val = cnf.GetInt(varname, (int)val); | ||
1111 | else if (typeof(T) == typeof(float)) | ||
1112 | val = cnf.GetFloat(varname, (float)val); | ||
1113 | else | ||
1114 | m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T)); | ||
1115 | } | ||
1116 | |||
1117 | return (T)val; | ||
1118 | } | ||
1119 | |||
1120 | public static void MergeEnvironmentToConfig(IConfigSource ConfigSource) | ||
1121 | { | ||
1122 | IConfig enVars = ConfigSource.Configs["Environment"]; | ||
1123 | // if section does not exist then user isn't expecting them, so don't bother. | ||
1124 | if( enVars != null ) | ||
1125 | { | ||
1126 | // load the values from the environment | ||
1127 | EnvConfigSource envConfigSource = new EnvConfigSource(); | ||
1128 | // add the requested keys | ||
1129 | string[] env_keys = enVars.GetKeys(); | ||
1130 | foreach ( string key in env_keys ) | ||
1131 | { | ||
1132 | envConfigSource.AddEnv(key, string.Empty); | ||
1133 | } | ||
1134 | // load the values from environment | ||
1135 | envConfigSource.LoadEnv(); | ||
1136 | // add them in to the master | ||
1137 | ConfigSource.Merge(envConfigSource); | ||
1138 | ConfigSource.ExpandKeyValues(); | ||
1139 | } | ||
1140 | } | ||
1141 | |||
1142 | public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass) | ||
1143 | { | ||
1144 | Type settingsType = settingsClass.GetType(); | ||
1145 | |||
1146 | FieldInfo[] fieldInfos = settingsType.GetFields(); | ||
1147 | foreach (FieldInfo fieldInfo in fieldInfos) | ||
1148 | { | ||
1149 | if (!fieldInfo.IsStatic) | ||
1150 | { | ||
1151 | if (fieldInfo.FieldType == typeof(System.String)) | ||
1152 | { | ||
1153 | fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass))); | ||
1154 | } | ||
1155 | else if (fieldInfo.FieldType == typeof(System.Boolean)) | ||
1156 | { | ||
1157 | fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass))); | ||
1158 | } | ||
1159 | else if (fieldInfo.FieldType == typeof(System.Int32)) | ||
1160 | { | ||
1161 | fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass))); | ||
1162 | } | ||
1163 | else if (fieldInfo.FieldType == typeof(System.Single)) | ||
1164 | { | ||
1165 | fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass))); | ||
1166 | } | ||
1167 | else if (fieldInfo.FieldType == typeof(System.UInt32)) | ||
1168 | { | ||
1169 | fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString()))); | ||
1170 | } | ||
1171 | } | ||
1172 | } | ||
1173 | |||
1174 | PropertyInfo[] propertyInfos = settingsType.GetProperties(); | ||
1175 | foreach (PropertyInfo propInfo in propertyInfos) | ||
1176 | { | ||
1177 | if ((propInfo.CanRead) && (propInfo.CanWrite)) | ||
1178 | { | ||
1179 | if (propInfo.PropertyType == typeof(System.String)) | ||
1180 | { | ||
1181 | propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null); | ||
1182 | } | ||
1183 | else if (propInfo.PropertyType == typeof(System.Boolean)) | ||
1184 | { | ||
1185 | propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null); | ||
1186 | } | ||
1187 | else if (propInfo.PropertyType == typeof(System.Int32)) | ||
1188 | { | ||
1189 | propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null); | ||
1190 | } | ||
1191 | else if (propInfo.PropertyType == typeof(System.Single)) | ||
1192 | { | ||
1193 | propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null); | ||
1194 | } | ||
1195 | if (propInfo.PropertyType == typeof(System.UInt32)) | ||
1196 | { | ||
1197 | propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null); | ||
1198 | } | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1202 | return settingsClass; | ||
1203 | } | ||
1204 | |||
1205 | /// <summary> | ||
1206 | /// Reads a configuration file, configFile, merging it with the main configuration, config. | ||
1207 | /// If the file doesn't exist, it copies a given exampleConfigFile onto configFile, and then | ||
1208 | /// merges it. | ||
1209 | /// </summary> | ||
1210 | /// <param name="config">The main configuration data</param> | ||
1211 | /// <param name="configFileName">The name of a configuration file in ConfigDirectory variable, no path</param> | ||
1212 | /// <param name="exampleConfigFile">Full path to an example configuration file</param> | ||
1213 | /// <param name="configFilePath">Full path ConfigDirectory/configFileName</param> | ||
1214 | /// <param name="created">True if the file was created in ConfigDirectory, false if it existed</param> | ||
1215 | /// <returns>True if success</returns> | ||
1216 | public static bool MergeConfigurationFile(IConfigSource config, string configFileName, string exampleConfigFile, out string configFilePath, out bool created) | ||
1217 | { | ||
1218 | created = false; | ||
1219 | configFilePath = string.Empty; | ||
1220 | |||
1221 | IConfig cnf = config.Configs["Startup"]; | ||
1222 | if (cnf == null) | ||
1223 | { | ||
1224 | m_log.WarnFormat("[UTILS]: Startup section doesn't exist"); | ||
1225 | return false; | ||
1226 | } | ||
1227 | |||
1228 | string configDirectory = cnf.GetString("ConfigDirectory", "."); | ||
1229 | string configFile = Path.Combine(configDirectory, configFileName); | ||
1230 | |||
1231 | if (!File.Exists(configFile) && !string.IsNullOrEmpty(exampleConfigFile)) | ||
1232 | { | ||
1233 | // We need to copy the example onto it | ||
1234 | |||
1235 | if (!Directory.Exists(configDirectory)) | ||
1236 | Directory.CreateDirectory(configDirectory); | ||
1237 | |||
1238 | try | ||
1239 | { | ||
1240 | File.Copy(exampleConfigFile, configFile); | ||
1241 | created = true; | ||
1242 | } | ||
1243 | catch (Exception e) | ||
1244 | { | ||
1245 | m_log.WarnFormat("[UTILS]: Exception copying configuration file {0} to {1}: {2}", configFile, exampleConfigFile, e.Message); | ||
1246 | return false; | ||
1247 | } | ||
1248 | } | ||
1249 | |||
1250 | if (File.Exists(configFile)) | ||
1251 | { | ||
1252 | // Merge | ||
1253 | config.Merge(new IniConfigSource(configFile)); | ||
1254 | config.ExpandKeyValues(); | ||
1255 | configFilePath = configFile; | ||
1256 | return true; | ||
1257 | } | ||
1258 | else | ||
1259 | return false; | ||
1260 | } | ||
1261 | |||
1262 | #endregion | ||
1263 | |||
1264 | public static float Clip(float x, float min, float max) | ||
1265 | { | ||
1266 | return Math.Min(Math.Max(x, min), max); | ||
1267 | } | ||
1268 | |||
1269 | public static int Clip(int x, int min, int max) | ||
1270 | { | ||
1271 | return Math.Min(Math.Max(x, min), max); | ||
1272 | } | ||
1273 | |||
1274 | public static Vector3 Clip(Vector3 vec, float min, float max) | ||
1275 | { | ||
1276 | return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max), | ||
1277 | Clip(vec.Z, min, max)); | ||
1278 | } | ||
1279 | |||
1280 | /// <summary> | ||
1281 | /// Convert an UUID to a raw uuid string. Right now this is a string without hyphens. | ||
1282 | /// </summary> | ||
1283 | /// <param name="UUID"></param> | ||
1284 | /// <returns></returns> | ||
1285 | public static String ToRawUuidString(UUID UUID) | ||
1286 | { | ||
1287 | return UUID.Guid.ToString("n"); | ||
1288 | } | ||
1289 | |||
1290 | public static string CleanString(string input) | ||
1291 | { | ||
1292 | if (input.Length == 0) | ||
1293 | return input; | ||
1294 | |||
1295 | int clip = input.Length; | ||
1296 | |||
1297 | // Test for ++ string terminator | ||
1298 | int pos = input.IndexOf("\0"); | ||
1299 | if (pos != -1 && pos < clip) | ||
1300 | clip = pos; | ||
1301 | |||
1302 | // Test for CR | ||
1303 | pos = input.IndexOf("\r"); | ||
1304 | if (pos != -1 && pos < clip) | ||
1305 | clip = pos; | ||
1306 | |||
1307 | // Test for LF | ||
1308 | pos = input.IndexOf("\n"); | ||
1309 | if (pos != -1 && pos < clip) | ||
1310 | clip = pos; | ||
1311 | |||
1312 | // Truncate string before first end-of-line character found | ||
1313 | return input.Substring(0, clip); | ||
1314 | } | ||
1315 | |||
1316 | /// <summary> | ||
1317 | /// returns the contents of /etc/issue on Unix Systems | ||
1318 | /// Use this for where it's absolutely necessary to implement platform specific stuff | ||
1319 | /// </summary> | ||
1320 | /// <returns></returns> | ||
1321 | public static string ReadEtcIssue() | ||
1322 | { | ||
1323 | try | ||
1324 | { | ||
1325 | StreamReader sr = new StreamReader("/etc/issue.net"); | ||
1326 | string issue = sr.ReadToEnd(); | ||
1327 | sr.Close(); | ||
1328 | return issue; | ||
1329 | } | ||
1330 | catch (Exception) | ||
1331 | { | ||
1332 | return ""; | ||
1333 | } | ||
1334 | } | ||
1335 | |||
1336 | public static void SerializeToFile(string filename, Object obj) | ||
1337 | { | ||
1338 | IFormatter formatter = new BinaryFormatter(); | ||
1339 | Stream stream = null; | ||
1340 | |||
1341 | try | ||
1342 | { | ||
1343 | stream = new FileStream( | ||
1344 | filename, FileMode.Create, | ||
1345 | FileAccess.Write, FileShare.None); | ||
1346 | |||
1347 | formatter.Serialize(stream, obj); | ||
1348 | } | ||
1349 | catch (Exception e) | ||
1350 | { | ||
1351 | m_log.Error(e.ToString()); | ||
1352 | } | ||
1353 | finally | ||
1354 | { | ||
1355 | if (stream != null) | ||
1356 | { | ||
1357 | stream.Close(); | ||
1358 | } | ||
1359 | } | ||
1360 | } | ||
1361 | |||
1362 | public static Object DeserializeFromFile(string filename) | ||
1363 | { | ||
1364 | IFormatter formatter = new BinaryFormatter(); | ||
1365 | Stream stream = null; | ||
1366 | Object ret = null; | ||
1367 | |||
1368 | try | ||
1369 | { | ||
1370 | stream = new FileStream( | ||
1371 | filename, FileMode.Open, | ||
1372 | FileAccess.Read, FileShare.None); | ||
1373 | |||
1374 | ret = formatter.Deserialize(stream); | ||
1375 | } | ||
1376 | catch (Exception e) | ||
1377 | { | ||
1378 | m_log.Error(e.ToString()); | ||
1379 | } | ||
1380 | finally | ||
1381 | { | ||
1382 | if (stream != null) | ||
1383 | { | ||
1384 | stream.Close(); | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | return ret; | ||
1389 | } | ||
1390 | |||
1391 | /// <summary> | ||
1392 | /// Copy data from one stream to another, leaving the read position of both streams at the beginning. | ||
1393 | /// </summary> | ||
1394 | /// <param name='inputStream'> | ||
1395 | /// Input stream. Must be seekable. | ||
1396 | /// </param> | ||
1397 | /// <exception cref='ArgumentException'> | ||
1398 | /// Thrown if the input stream is not seekable. | ||
1399 | /// </exception> | ||
1400 | public static Stream Copy(Stream inputStream) | ||
1401 | { | ||
1402 | if (!inputStream.CanSeek) | ||
1403 | throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek"); | ||
1404 | |||
1405 | const int readSize = 256; | ||
1406 | byte[] buffer = new byte[readSize]; | ||
1407 | MemoryStream ms = new MemoryStream(); | ||
1408 | |||
1409 | int count = inputStream.Read(buffer, 0, readSize); | ||
1410 | |||
1411 | while (count > 0) | ||
1412 | { | ||
1413 | ms.Write(buffer, 0, count); | ||
1414 | count = inputStream.Read(buffer, 0, readSize); | ||
1415 | } | ||
1416 | |||
1417 | ms.Position = 0; | ||
1418 | inputStream.Position = 0; | ||
1419 | |||
1420 | return ms; | ||
1421 | } | ||
1422 | |||
1423 | public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args) | ||
1424 | { | ||
1425 | return SendXmlRpcCommand(url, methodName, args); | ||
1426 | } | ||
1427 | |||
1428 | public static XmlRpcResponse SendXmlRpcCommand(string url, string methodName, object[] args) | ||
1429 | { | ||
1430 | XmlRpcRequest client = new XmlRpcRequest(methodName, args); | ||
1431 | return client.Send(url, 6000); | ||
1432 | } | ||
1433 | |||
1434 | /// <summary> | ||
1435 | /// Returns an error message that the user could not be found in the database | ||
1436 | /// </summary> | ||
1437 | /// <returns>XML string consisting of a error element containing individual error(s)</returns> | ||
1438 | public static XmlRpcResponse CreateUnknownUserErrorResponse() | ||
1439 | { | ||
1440 | XmlRpcResponse response = new XmlRpcResponse(); | ||
1441 | Hashtable responseData = new Hashtable(); | ||
1442 | responseData["error_type"] = "unknown_user"; | ||
1443 | responseData["error_desc"] = "The user requested is not in the database"; | ||
1444 | |||
1445 | response.Value = responseData; | ||
1446 | return response; | ||
1447 | } | ||
1448 | |||
1449 | /// <summary> | ||
1450 | /// Converts a byte array in big endian order into an ulong. | ||
1451 | /// </summary> | ||
1452 | /// <param name="bytes"> | ||
1453 | /// The array of bytes | ||
1454 | /// </param> | ||
1455 | /// <returns> | ||
1456 | /// The extracted ulong | ||
1457 | /// </returns> | ||
1458 | public static ulong BytesToUInt64Big(byte[] bytes) | ||
1459 | { | ||
1460 | if (bytes.Length < 8) return 0; | ||
1461 | return ((ulong)bytes[0] << 56) | ((ulong)bytes[1] << 48) | ((ulong)bytes[2] << 40) | ((ulong)bytes[3] << 32) | | ||
1462 | ((ulong)bytes[4] << 24) | ((ulong)bytes[5] << 16) | ((ulong)bytes[6] << 8) | (ulong)bytes[7]; | ||
1463 | } | ||
1464 | |||
1465 | // used for RemoteParcelRequest (for "About Landmark") | ||
1466 | public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y) | ||
1467 | { | ||
1468 | byte[] bytes = | ||
1469 | { | ||
1470 | (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), | ||
1471 | (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), | ||
1472 | (byte)x, (byte)(x >> 8), 0, 0, | ||
1473 | (byte)y, (byte)(y >> 8), 0, 0 }; | ||
1474 | return new UUID(bytes, 0); | ||
1475 | } | ||
1476 | |||
1477 | public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y, uint z) | ||
1478 | { | ||
1479 | byte[] bytes = | ||
1480 | { | ||
1481 | (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), | ||
1482 | (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), | ||
1483 | (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), | ||
1484 | (byte)y, (byte)(y >> 8), 0, 0 }; | ||
1485 | return new UUID(bytes, 0); | ||
1486 | } | ||
1487 | |||
1488 | public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) | ||
1489 | { | ||
1490 | byte[] bytes = parcelID.GetBytes(); | ||
1491 | regionHandle = Utils.BytesToUInt64(bytes); | ||
1492 | x = Utils.BytesToUInt(bytes, 8) & 0xffff; | ||
1493 | y = Utils.BytesToUInt(bytes, 12) & 0xffff; | ||
1494 | } | ||
1495 | |||
1496 | public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z) | ||
1497 | { | ||
1498 | byte[] bytes = parcelID.GetBytes(); | ||
1499 | regionHandle = Utils.BytesToUInt64(bytes); | ||
1500 | x = Utils.BytesToUInt(bytes, 8) & 0xffff; | ||
1501 | z = (Utils.BytesToUInt(bytes, 8) & 0xffff0000) >> 16; | ||
1502 | y = Utils.BytesToUInt(bytes, 12) & 0xffff; | ||
1503 | } | ||
1504 | |||
1505 | public static void FakeParcelIDToGlobalPosition(UUID parcelID, out uint x, out uint y) | ||
1506 | { | ||
1507 | ulong regionHandle; | ||
1508 | uint rx, ry; | ||
1509 | |||
1510 | ParseFakeParcelID(parcelID, out regionHandle, out x, out y); | ||
1511 | Utils.LongToUInts(regionHandle, out rx, out ry); | ||
1512 | |||
1513 | x += rx; | ||
1514 | y += ry; | ||
1515 | } | ||
1516 | |||
1517 | /// <summary> | ||
1518 | /// Get operating system information if available. Returns only the first 45 characters of information | ||
1519 | /// </summary> | ||
1520 | /// <returns> | ||
1521 | /// Operating system information. Returns an empty string if none was available. | ||
1522 | /// </returns> | ||
1523 | public static string GetOperatingSystemInformation() | ||
1524 | { | ||
1525 | string os = String.Empty; | ||
1526 | |||
1527 | if (Environment.OSVersion.Platform != PlatformID.Unix) | ||
1528 | { | ||
1529 | os = Environment.OSVersion.ToString(); | ||
1530 | } | ||
1531 | else | ||
1532 | { | ||
1533 | os = ReadEtcIssue(); | ||
1534 | } | ||
1535 | |||
1536 | if (os.Length > 45) | ||
1537 | { | ||
1538 | os = os.Substring(0, 45); | ||
1539 | } | ||
1540 | |||
1541 | return os; | ||
1542 | } | ||
1543 | |||
1544 | public static string GetRuntimeInformation() | ||
1545 | { | ||
1546 | string ru = String.Empty; | ||
1547 | |||
1548 | if (Environment.OSVersion.Platform == PlatformID.Unix) | ||
1549 | ru = "Unix/Mono"; | ||
1550 | else | ||
1551 | if (Environment.OSVersion.Platform == PlatformID.MacOSX) | ||
1552 | ru = "OSX/Mono"; | ||
1553 | else | ||
1554 | { | ||
1555 | if (IsPlatformMono) | ||
1556 | ru = "Win/Mono"; | ||
1557 | else | ||
1558 | ru = "Win/.NET"; | ||
1559 | } | ||
1560 | |||
1561 | return ru; | ||
1562 | } | ||
1563 | |||
1564 | /// <summary> | ||
1565 | /// Is the given string a UUID? | ||
1566 | /// </summary> | ||
1567 | /// <param name="s"></param> | ||
1568 | /// <returns></returns> | ||
1569 | public static bool isUUID(string s) | ||
1570 | { | ||
1571 | return UUIDPattern.IsMatch(s); | ||
1572 | } | ||
1573 | |||
1574 | public static string GetDisplayConnectionString(string connectionString) | ||
1575 | { | ||
1576 | int passPosition = 0; | ||
1577 | int passEndPosition = 0; | ||
1578 | string displayConnectionString = null; | ||
1579 | |||
1580 | // hide the password in the connection string | ||
1581 | passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase); | ||
1582 | passPosition = connectionString.IndexOf("=", passPosition); | ||
1583 | if (passPosition < connectionString.Length) | ||
1584 | passPosition += 1; | ||
1585 | passEndPosition = connectionString.IndexOf(";", passPosition); | ||
1586 | |||
1587 | displayConnectionString = connectionString.Substring(0, passPosition); | ||
1588 | displayConnectionString += "***"; | ||
1589 | displayConnectionString += connectionString.Substring(passEndPosition, connectionString.Length - passEndPosition); | ||
1590 | |||
1591 | return displayConnectionString; | ||
1592 | } | ||
1593 | |||
1594 | public static string Base64ToString(string str) | ||
1595 | { | ||
1596 | Decoder utf8Decode = Encoding.UTF8.GetDecoder(); | ||
1597 | |||
1598 | byte[] todecode_byte = Convert.FromBase64String(str); | ||
1599 | int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length); | ||
1600 | char[] decoded_char = new char[charCount]; | ||
1601 | utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0); | ||
1602 | string result = new String(decoded_char); | ||
1603 | return result; | ||
1604 | } | ||
1605 | |||
1606 | public static void BinaryToASCII(char[] chars) | ||
1607 | { | ||
1608 | for (int i = 0; i < chars.Length; i++) | ||
1609 | { | ||
1610 | char ch = chars[i]; | ||
1611 | if (ch < 32 || ch > 127) | ||
1612 | chars[i] = '.'; | ||
1613 | } | ||
1614 | } | ||
1615 | |||
1616 | public static string BinaryToASCII(string src) | ||
1617 | { | ||
1618 | char[] chars = src.ToCharArray(); | ||
1619 | BinaryToASCII(chars); | ||
1620 | return new String(chars); | ||
1621 | } | ||
1622 | |||
1623 | /// <summary> | ||
1624 | /// Reads a known number of bytes from a stream. | ||
1625 | /// Throws EndOfStreamException if the stream doesn't contain enough data. | ||
1626 | /// </summary> | ||
1627 | /// <param name="stream">The stream to read data from</param> | ||
1628 | /// <param name="data">The array to write bytes into. The array | ||
1629 | /// will be completely filled from the stream, so an appropriate | ||
1630 | /// size must be given.</param> | ||
1631 | public static void ReadStream(Stream stream, byte[] data) | ||
1632 | { | ||
1633 | int offset = 0; | ||
1634 | int remaining = data.Length; | ||
1635 | |||
1636 | while (remaining > 0) | ||
1637 | { | ||
1638 | int read = stream.Read(data, offset, remaining); | ||
1639 | if (read <= 0) | ||
1640 | throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining)); | ||
1641 | remaining -= read; | ||
1642 | offset += read; | ||
1643 | } | ||
1644 | } | ||
1645 | |||
1646 | public static Guid GetHashGuid(string data, string salt) | ||
1647 | { | ||
1648 | byte[] hash = ComputeMD5Hash(data + salt); | ||
1649 | |||
1650 | //string s = BitConverter.ToString(hash); | ||
1651 | |||
1652 | Guid guid = new Guid(hash); | ||
1653 | |||
1654 | return guid; | ||
1655 | } | ||
1656 | |||
1657 | public static byte ConvertMaturityToAccessLevel(uint maturity) | ||
1658 | { | ||
1659 | byte retVal = 0; | ||
1660 | switch (maturity) | ||
1661 | { | ||
1662 | case 0: //PG | ||
1663 | retVal = 13; | ||
1664 | break; | ||
1665 | case 1: //Mature | ||
1666 | retVal = 21; | ||
1667 | break; | ||
1668 | case 2: // Adult | ||
1669 | retVal = 42; | ||
1670 | break; | ||
1671 | } | ||
1672 | |||
1673 | return retVal; | ||
1674 | |||
1675 | } | ||
1676 | |||
1677 | public static uint ConvertAccessLevelToMaturity(byte maturity) | ||
1678 | { | ||
1679 | if (maturity <= 13) | ||
1680 | return 0; | ||
1681 | else if (maturity <= 21) | ||
1682 | return 1; | ||
1683 | else | ||
1684 | return 2; | ||
1685 | } | ||
1686 | |||
1687 | /// <summary> | ||
1688 | /// Produces an OSDMap from its string representation on a stream | ||
1689 | /// </summary> | ||
1690 | /// <param name="data">The stream</param> | ||
1691 | /// <param name="length">The size of the data on the stream</param> | ||
1692 | /// <returns>The OSDMap or an exception</returns> | ||
1693 | public static OSDMap GetOSDMap(Stream stream, int length) | ||
1694 | { | ||
1695 | byte[] data = new byte[length]; | ||
1696 | stream.Read(data, 0, length); | ||
1697 | string strdata = Util.UTF8.GetString(data); | ||
1698 | OSDMap args = null; | ||
1699 | OSD buffer; | ||
1700 | buffer = OSDParser.DeserializeJson(strdata); | ||
1701 | if (buffer.Type == OSDType.Map) | ||
1702 | { | ||
1703 | args = (OSDMap)buffer; | ||
1704 | return args; | ||
1705 | } | ||
1706 | return null; | ||
1707 | } | ||
1708 | |||
1709 | public static OSDMap GetOSDMap(string data) | ||
1710 | { | ||
1711 | OSDMap args = null; | ||
1712 | try | ||
1713 | { | ||
1714 | OSD buffer; | ||
1715 | // We should pay attention to the content-type, but let's assume we know it's Json | ||
1716 | buffer = OSDParser.DeserializeJson(data); | ||
1717 | if (buffer.Type == OSDType.Map) | ||
1718 | { | ||
1719 | args = (OSDMap)buffer; | ||
1720 | return args; | ||
1721 | } | ||
1722 | else | ||
1723 | { | ||
1724 | // uh? | ||
1725 | m_log.Debug(("[UTILS]: Got OSD of unexpected type " + buffer.Type.ToString())); | ||
1726 | return null; | ||
1727 | } | ||
1728 | } | ||
1729 | catch (Exception ex) | ||
1730 | { | ||
1731 | m_log.Debug("[UTILS]: exception on GetOSDMap " + ex.Message); | ||
1732 | return null; | ||
1733 | } | ||
1734 | } | ||
1735 | |||
1736 | public static string[] Glob(string path) | ||
1737 | { | ||
1738 | string vol=String.Empty; | ||
1739 | |||
1740 | if (Path.VolumeSeparatorChar != Path.DirectorySeparatorChar) | ||
1741 | { | ||
1742 | string[] vcomps = path.Split(new char[] {Path.VolumeSeparatorChar}, 2, StringSplitOptions.RemoveEmptyEntries); | ||
1743 | |||
1744 | if (vcomps.Length > 1) | ||
1745 | { | ||
1746 | path = vcomps[1]; | ||
1747 | vol = vcomps[0]; | ||
1748 | } | ||
1749 | } | ||
1750 | |||
1751 | string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); | ||
1752 | |||
1753 | // Glob | ||
1754 | |||
1755 | path = vol; | ||
1756 | if (vol != String.Empty) | ||
1757 | path += new String(new char[] {Path.VolumeSeparatorChar, Path.DirectorySeparatorChar}); | ||
1758 | else | ||
1759 | path = new String(new char[] {Path.DirectorySeparatorChar}); | ||
1760 | |||
1761 | List<string> paths = new List<string>(); | ||
1762 | List<string> found = new List<string>(); | ||
1763 | paths.Add(path); | ||
1764 | |||
1765 | int compIndex = -1; | ||
1766 | foreach (string c in comps) | ||
1767 | { | ||
1768 | compIndex++; | ||
1769 | |||
1770 | List<string> addpaths = new List<string>(); | ||
1771 | foreach (string p in paths) | ||
1772 | { | ||
1773 | string[] dirs = Directory.GetDirectories(p, c); | ||
1774 | |||
1775 | if (dirs.Length != 0) | ||
1776 | { | ||
1777 | foreach (string dir in dirs) | ||
1778 | addpaths.Add(Path.Combine(path, dir)); | ||
1779 | } | ||
1780 | |||
1781 | // Only add files if that is the last path component | ||
1782 | if (compIndex == comps.Length - 1) | ||
1783 | { | ||
1784 | string[] files = Directory.GetFiles(p, c); | ||
1785 | foreach (string f in files) | ||
1786 | found.Add(f); | ||
1787 | } | ||
1788 | } | ||
1789 | paths = addpaths; | ||
1790 | } | ||
1791 | |||
1792 | return found.ToArray(); | ||
1793 | } | ||
1794 | |||
1795 | /// <summary> | ||
1796 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. | ||
1797 | /// </summary> | ||
1798 | /// <param name="str"> | ||
1799 | /// If null or empty, then an bytes[0] is returned. | ||
1800 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] | ||
1801 | /// </param> | ||
1802 | /// <param name="args"> | ||
1803 | /// Arguments to substitute into the string via the {} mechanism. | ||
1804 | /// </param> | ||
1805 | /// <returns></returns> | ||
1806 | public static byte[] StringToBytes256(string str, params object[] args) | ||
1807 | { | ||
1808 | return StringToBytes256(string.Format(str, args)); | ||
1809 | } | ||
1810 | |||
1811 | /// <summary> | ||
1812 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. | ||
1813 | /// </summary> | ||
1814 | /// <param name="str"> | ||
1815 | /// If null or empty, then an bytes[0] is returned. | ||
1816 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] | ||
1817 | /// </param> | ||
1818 | /// <returns></returns> | ||
1819 | public static byte[] StringToBytes256(string str) | ||
1820 | { | ||
1821 | if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } | ||
1822 | if (str.Length > 254) str = str.Remove(254); | ||
1823 | if (!str.EndsWith("\0")) { str += "\0"; } | ||
1824 | |||
1825 | // Because this is UTF-8 encoding and not ASCII, it's possible we | ||
1826 | // might have gotten an oversized array even after the string trim | ||
1827 | byte[] data = UTF8.GetBytes(str); | ||
1828 | if (data.Length > 256) | ||
1829 | { | ||
1830 | Array.Resize<byte>(ref data, 256); | ||
1831 | data[255] = 0; | ||
1832 | } | ||
1833 | |||
1834 | return data; | ||
1835 | } | ||
1836 | |||
1837 | /// <summary> | ||
1838 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary. | ||
1839 | /// </summary> | ||
1840 | /// <param name="str"> | ||
1841 | /// If null or empty, then an bytes[0] is returned. | ||
1842 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] | ||
1843 | /// </param> | ||
1844 | /// <param name="args"> | ||
1845 | /// Arguments to substitute into the string via the {} mechanism. | ||
1846 | /// </param> | ||
1847 | /// <returns></returns> | ||
1848 | public static byte[] StringToBytes1024(string str, params object[] args) | ||
1849 | { | ||
1850 | return StringToBytes1024(string.Format(str, args)); | ||
1851 | } | ||
1852 | |||
1853 | /// <summary> | ||
1854 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary. | ||
1855 | /// </summary> | ||
1856 | /// <param name="str"> | ||
1857 | /// If null or empty, then an bytes[0] is returned. | ||
1858 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] | ||
1859 | /// </param> | ||
1860 | /// <returns></returns> | ||
1861 | public static byte[] StringToBytes1024(string str) | ||
1862 | { | ||
1863 | if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } | ||
1864 | if (str.Length > 1023) str = str.Remove(1023); | ||
1865 | if (!str.EndsWith("\0")) { str += "\0"; } | ||
1866 | |||
1867 | // Because this is UTF-8 encoding and not ASCII, it's possible we | ||
1868 | // might have gotten an oversized array even after the string trim | ||
1869 | byte[] data = UTF8.GetBytes(str); | ||
1870 | if (data.Length > 1024) | ||
1871 | { | ||
1872 | Array.Resize<byte>(ref data, 1024); | ||
1873 | data[1023] = 0; | ||
1874 | } | ||
1875 | |||
1876 | return data; | ||
1877 | } | ||
1878 | |||
1879 | /// <summary> | ||
1880 | /// Pretty format the hashtable contents to a single line. | ||
1881 | /// </summary> | ||
1882 | /// <remarks> | ||
1883 | /// Used for debugging output. | ||
1884 | /// </remarks> | ||
1885 | /// <param name='ht'></param> | ||
1886 | public static string PrettyFormatToSingleLine(Hashtable ht) | ||
1887 | { | ||
1888 | StringBuilder sb = new StringBuilder(); | ||
1889 | |||
1890 | int i = 0; | ||
1891 | |||
1892 | foreach (string key in ht.Keys) | ||
1893 | { | ||
1894 | sb.AppendFormat("{0}:{1}", key, ht[key]); | ||
1895 | |||
1896 | if (++i < ht.Count) | ||
1897 | sb.AppendFormat(", "); | ||
1898 | } | ||
1899 | |||
1900 | return sb.ToString(); | ||
1901 | } | ||
1902 | |||
1903 | /// <summary> | ||
1904 | /// Used to trigger an early library load on Windows systems. | ||
1905 | /// </summary> | ||
1906 | /// <remarks> | ||
1907 | /// Required to get 32-bit and 64-bit processes to automatically use the | ||
1908 | /// appropriate native library. | ||
1909 | /// </remarks> | ||
1910 | /// <param name="dllToLoad"></param> | ||
1911 | /// <returns></returns> | ||
1912 | [DllImport("kernel32.dll")] | ||
1913 | public static extern IntPtr LoadLibrary(string dllToLoad); | ||
1914 | |||
1915 | /// <summary> | ||
1916 | /// Determine whether the current process is 64 bit | ||
1917 | /// </summary> | ||
1918 | /// <returns>true if so, false if not</returns> | ||
1919 | public static bool Is64BitProcess() | ||
1920 | { | ||
1921 | return IntPtr.Size == 8; | ||
1922 | } | ||
1923 | |||
1924 | #region FireAndForget Threading Pattern | ||
1925 | |||
1926 | /// <summary> | ||
1927 | /// Created to work around a limitation in Mono with nested delegates | ||
1928 | /// </summary> | ||
1929 | private sealed class FireAndForgetWrapper | ||
1930 | { | ||
1931 | private static volatile FireAndForgetWrapper instance; | ||
1932 | private static object syncRoot = new Object(); | ||
1933 | |||
1934 | public static FireAndForgetWrapper Instance { | ||
1935 | get { | ||
1936 | |||
1937 | if (instance == null) | ||
1938 | { | ||
1939 | lock (syncRoot) | ||
1940 | { | ||
1941 | if (instance == null) | ||
1942 | { | ||
1943 | instance = new FireAndForgetWrapper(); | ||
1944 | } | ||
1945 | } | ||
1946 | } | ||
1947 | |||
1948 | return instance; | ||
1949 | } | ||
1950 | } | ||
1951 | |||
1952 | public void FireAndForget(System.Threading.WaitCallback callback) | ||
1953 | { | ||
1954 | callback.BeginInvoke(null, EndFireAndForget, callback); | ||
1955 | } | ||
1956 | |||
1957 | public void FireAndForget(System.Threading.WaitCallback callback, object obj) | ||
1958 | { | ||
1959 | callback.BeginInvoke(obj, EndFireAndForget, callback); | ||
1960 | } | ||
1961 | |||
1962 | private static void EndFireAndForget(IAsyncResult ar) | ||
1963 | { | ||
1964 | System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; | ||
1965 | |||
1966 | try { callback.EndInvoke(ar); } | ||
1967 | catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } | ||
1968 | |||
1969 | ar.AsyncWaitHandle.Close(); | ||
1970 | } | ||
1971 | } | ||
1972 | |||
1973 | public static void InitThreadPool(int minThreads, int maxThreads) | ||
1974 | { | ||
1975 | if (maxThreads < 2) | ||
1976 | throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); | ||
1977 | |||
1978 | if (minThreads > maxThreads || minThreads < 2) | ||
1979 | throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); | ||
1980 | |||
1981 | if (m_ThreadPool != null) | ||
1982 | { | ||
1983 | m_log.Warn("SmartThreadPool is already initialized. Ignoring request."); | ||
1984 | return; | ||
1985 | } | ||
1986 | |||
1987 | STPStartInfo startInfo = new STPStartInfo(); | ||
1988 | startInfo.ThreadPoolName = "Util"; | ||
1989 | startInfo.IdleTimeout = 2000; | ||
1990 | startInfo.MaxWorkerThreads = maxThreads; | ||
1991 | startInfo.MinWorkerThreads = minThreads; | ||
1992 | |||
1993 | m_ThreadPool = new SmartThreadPool(startInfo); | ||
1994 | m_threadPoolWatchdog = new Timer(ThreadPoolWatchdog, null, 0, 1000); | ||
1995 | } | ||
1996 | |||
1997 | public static int FireAndForgetCount() | ||
1998 | { | ||
1999 | const int MAX_SYSTEM_THREADS = 200; | ||
2000 | |||
2001 | switch (FireAndForgetMethod) | ||
2002 | { | ||
2003 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: | ||
2004 | case FireAndForgetMethod.QueueUserWorkItem: | ||
2005 | case FireAndForgetMethod.BeginInvoke: | ||
2006 | int workerThreads, iocpThreads; | ||
2007 | ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); | ||
2008 | return workerThreads; | ||
2009 | case FireAndForgetMethod.SmartThreadPool: | ||
2010 | return m_ThreadPool.MaxThreads - m_ThreadPool.InUseThreads; | ||
2011 | case FireAndForgetMethod.Thread: | ||
2012 | return MAX_SYSTEM_THREADS - System.Diagnostics.Process.GetCurrentProcess().Threads.Count; | ||
2013 | default: | ||
2014 | throw new NotImplementedException(); | ||
2015 | } | ||
2016 | } | ||
2017 | |||
2018 | /// <summary> | ||
2019 | /// Additional information about threads in the main thread pool. Used to time how long the | ||
2020 | /// thread has been running, and abort it if it has timed-out. | ||
2021 | /// </summary> | ||
2022 | private class ThreadInfo | ||
2023 | { | ||
2024 | public long ThreadFuncNum { get; set; } | ||
2025 | public string StackTrace { get; set; } | ||
2026 | private string context; | ||
2027 | public bool LogThread { get; set; } | ||
2028 | |||
2029 | public IWorkItemResult WorkItem { get; set; } | ||
2030 | public Thread Thread { get; set; } | ||
2031 | public bool Running { get; set; } | ||
2032 | public bool Aborted { get; set; } | ||
2033 | private int started; | ||
2034 | |||
2035 | public ThreadInfo(long threadFuncNum, string context) | ||
2036 | { | ||
2037 | ThreadFuncNum = threadFuncNum; | ||
2038 | this.context = context; | ||
2039 | LogThread = false; | ||
2040 | Thread = null; | ||
2041 | Running = false; | ||
2042 | Aborted = false; | ||
2043 | } | ||
2044 | |||
2045 | public void Started() | ||
2046 | { | ||
2047 | Thread = Thread.CurrentThread; | ||
2048 | started = EnvironmentTickCount(); | ||
2049 | Running = true; | ||
2050 | } | ||
2051 | |||
2052 | public void Ended() | ||
2053 | { | ||
2054 | Running = false; | ||
2055 | } | ||
2056 | |||
2057 | public int Elapsed() | ||
2058 | { | ||
2059 | return EnvironmentTickCountSubtract(started); | ||
2060 | } | ||
2061 | |||
2062 | public void Abort() | ||
2063 | { | ||
2064 | Aborted = true; | ||
2065 | WorkItem.Cancel(true); | ||
2066 | } | ||
2067 | |||
2068 | /// <summary> | ||
2069 | /// Returns the thread's stack trace. | ||
2070 | /// </summary> | ||
2071 | /// <remarks> | ||
2072 | /// May return one of two stack traces. First, tries to get the thread's active stack | ||
2073 | /// trace. But this can fail, so as a fallback this method will return the stack | ||
2074 | /// trace that was active when the task was queued. | ||
2075 | /// </remarks> | ||
2076 | public string GetStackTrace() | ||
2077 | { | ||
2078 | string ret = (context == null) ? "" : ("(" + context + ") "); | ||
2079 | |||
2080 | StackTrace activeStackTrace = Util.GetStackTrace(Thread); | ||
2081 | if (activeStackTrace != null) | ||
2082 | ret += activeStackTrace.ToString(); | ||
2083 | else if (StackTrace != null) | ||
2084 | ret += "(Stack trace when queued) " + StackTrace; | ||
2085 | // else, no stack trace available | ||
2086 | |||
2087 | return ret; | ||
2088 | } | ||
2089 | } | ||
2090 | |||
2091 | private static long nextThreadFuncNum = 0; | ||
2092 | private static long numQueuedThreadFuncs = 0; | ||
2093 | private static long numRunningThreadFuncs = 0; | ||
2094 | private static long numTotalThreadFuncsCalled = 0; | ||
2095 | private static Int32 threadFuncOverloadMode = 0; | ||
2096 | |||
2097 | public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } } | ||
2098 | public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } } | ||
2099 | |||
2100 | // Maps (ThreadFunc number -> Thread) | ||
2101 | private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>(); | ||
2102 | |||
2103 | private static readonly int THREAD_TIMEOUT = 10 * 60 * 1000; // 10 minutes | ||
2104 | |||
2105 | /// <summary> | ||
2106 | /// Finds threads in the main thread pool that have timed-out, and aborts them. | ||
2107 | /// </summary> | ||
2108 | private static void ThreadPoolWatchdog(object state) | ||
2109 | { | ||
2110 | foreach (KeyValuePair<long, ThreadInfo> entry in activeThreads) | ||
2111 | { | ||
2112 | ThreadInfo t = entry.Value; | ||
2113 | if (t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT)) | ||
2114 | { | ||
2115 | m_log.WarnFormat("Timeout in threadfunc {0} ({1}) {2}", t.ThreadFuncNum, t.Thread.Name, t.GetStackTrace()); | ||
2116 | t.Abort(); | ||
2117 | |||
2118 | ThreadInfo dummy; | ||
2119 | activeThreads.TryRemove(entry.Key, out dummy); | ||
2120 | |||
2121 | // It's possible that the thread won't abort. To make sure the thread pool isn't | ||
2122 | // depleted, increase the pool size. | ||
2123 | m_ThreadPool.MaxThreads++; | ||
2124 | } | ||
2125 | } | ||
2126 | } | ||
2127 | |||
2128 | public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } } | ||
2129 | |||
2130 | public static Dictionary<string, int> GetFireAndForgetCallsMade() | ||
2131 | { | ||
2132 | return new Dictionary<string, int>(m_fireAndForgetCallsMade); | ||
2133 | } | ||
2134 | |||
2135 | private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>(); | ||
2136 | |||
2137 | public static Dictionary<string, int> GetFireAndForgetCallsInProgress() | ||
2138 | { | ||
2139 | return new Dictionary<string, int>(m_fireAndForgetCallsInProgress); | ||
2140 | } | ||
2141 | |||
2142 | private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>(); | ||
2143 | |||
2144 | public static void FireAndForget(System.Threading.WaitCallback callback) | ||
2145 | { | ||
2146 | FireAndForget(callback, null, null); | ||
2147 | } | ||
2148 | |||
2149 | public static void FireAndForget(System.Threading.WaitCallback callback, object obj) | ||
2150 | { | ||
2151 | FireAndForget(callback, obj, null); | ||
2152 | } | ||
2153 | |||
2154 | public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context) | ||
2155 | { | ||
2156 | Interlocked.Increment(ref numTotalThreadFuncsCalled); | ||
2157 | |||
2158 | if (context != null) | ||
2159 | { | ||
2160 | if (!m_fireAndForgetCallsMade.ContainsKey(context)) | ||
2161 | m_fireAndForgetCallsMade[context] = 1; | ||
2162 | else | ||
2163 | m_fireAndForgetCallsMade[context]++; | ||
2164 | |||
2165 | if (!m_fireAndForgetCallsInProgress.ContainsKey(context)) | ||
2166 | m_fireAndForgetCallsInProgress[context] = 1; | ||
2167 | else | ||
2168 | m_fireAndForgetCallsInProgress[context]++; | ||
2169 | } | ||
2170 | |||
2171 | WaitCallback realCallback; | ||
2172 | |||
2173 | bool loggingEnabled = LogThreadPool > 0; | ||
2174 | |||
2175 | long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum); | ||
2176 | ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context); | ||
2177 | |||
2178 | if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) | ||
2179 | { | ||
2180 | // If we're running regression tests, then we want any exceptions to rise up to the test code. | ||
2181 | realCallback = | ||
2182 | o => | ||
2183 | { | ||
2184 | Culture.SetCurrentCulture(); | ||
2185 | callback(o); | ||
2186 | |||
2187 | if (context != null) | ||
2188 | m_fireAndForgetCallsInProgress[context]--; | ||
2189 | }; | ||
2190 | } | ||
2191 | else | ||
2192 | { | ||
2193 | // When OpenSim interacts with a database or sends data over the wire, it must send this in en_US culture | ||
2194 | // so that we don't encounter problems where, for instance, data is saved with a culture that uses commas | ||
2195 | // for decimals places but is read by a culture that treats commas as number seperators. | ||
2196 | realCallback = o => | ||
2197 | { | ||
2198 | long numQueued1 = Interlocked.Decrement(ref numQueuedThreadFuncs); | ||
2199 | long numRunning1 = Interlocked.Increment(ref numRunningThreadFuncs); | ||
2200 | threadInfo.Started(); | ||
2201 | activeThreads[threadFuncNum] = threadInfo; | ||
2202 | |||
2203 | try | ||
2204 | { | ||
2205 | if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) | ||
2206 | m_log.DebugFormat("Run threadfunc {0} (Queued {1}, Running {2})", threadFuncNum, numQueued1, numRunning1); | ||
2207 | |||
2208 | Culture.SetCurrentCulture(); | ||
2209 | |||
2210 | callback(o); | ||
2211 | } | ||
2212 | catch (ThreadAbortException e) | ||
2213 | { | ||
2214 | m_log.Error(string.Format("Aborted threadfunc {0} ", threadFuncNum), e); | ||
2215 | } | ||
2216 | catch (Exception e) | ||
2217 | { | ||
2218 | m_log.Error(string.Format("[UTIL]: Util STP threadfunc {0} terminated with error ", threadFuncNum), e); | ||
2219 | } | ||
2220 | finally | ||
2221 | { | ||
2222 | Interlocked.Decrement(ref numRunningThreadFuncs); | ||
2223 | threadInfo.Ended(); | ||
2224 | ThreadInfo dummy; | ||
2225 | activeThreads.TryRemove(threadFuncNum, out dummy); | ||
2226 | if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) | ||
2227 | m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed())); | ||
2228 | |||
2229 | if (context != null) | ||
2230 | m_fireAndForgetCallsInProgress[context]--; | ||
2231 | } | ||
2232 | }; | ||
2233 | } | ||
2234 | |||
2235 | long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs); | ||
2236 | try | ||
2237 | { | ||
2238 | long numRunning = numRunningThreadFuncs; | ||
2239 | |||
2240 | if (m_ThreadPool != null && LogOverloads) | ||
2241 | { | ||
2242 | if ((threadFuncOverloadMode == 0) && (numRunning >= m_ThreadPool.MaxThreads)) | ||
2243 | { | ||
2244 | if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 1, 0) == 0) | ||
2245 | m_log.DebugFormat("Threadfunc: enable overload mode (Queued {0}, Running {1})", numQueued, numRunning); | ||
2246 | } | ||
2247 | else if ((threadFuncOverloadMode == 1) && (numRunning <= (m_ThreadPool.MaxThreads * 2) / 3)) | ||
2248 | { | ||
2249 | if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 0, 1) == 1) | ||
2250 | m_log.DebugFormat("Threadfunc: disable overload mode (Queued {0}, Running {1})", numQueued, numRunning); | ||
2251 | } | ||
2252 | } | ||
2253 | |||
2254 | if (loggingEnabled || (threadFuncOverloadMode == 1)) | ||
2255 | { | ||
2256 | string full, partial; | ||
2257 | GetFireAndForgetStackTrace(out full, out partial); | ||
2258 | threadInfo.StackTrace = full; | ||
2259 | threadInfo.LogThread = ShouldLogThread(partial); | ||
2260 | |||
2261 | if (threadInfo.LogThread) | ||
2262 | { | ||
2263 | m_log.DebugFormat("Queue threadfunc {0} (Queued {1}, Running {2}) {3}{4}", | ||
2264 | threadFuncNum, numQueued, numRunningThreadFuncs, | ||
2265 | (context == null) ? "" : ("(" + context + ") "), | ||
2266 | (LogThreadPool >= 2) ? full : partial); | ||
2267 | } | ||
2268 | } | ||
2269 | else | ||
2270 | { | ||
2271 | // Since we didn't log "Queue threadfunc", don't log "Run threadfunc" or "End threadfunc" either. | ||
2272 | // Those log lines aren't useful when we don't know which function is running in the thread. | ||
2273 | threadInfo.LogThread = false; | ||
2274 | } | ||
2275 | |||
2276 | switch (FireAndForgetMethod) | ||
2277 | { | ||
2278 | case FireAndForgetMethod.RegressionTest: | ||
2279 | case FireAndForgetMethod.None: | ||
2280 | realCallback.Invoke(obj); | ||
2281 | break; | ||
2282 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: | ||
2283 | ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); | ||
2284 | break; | ||
2285 | case FireAndForgetMethod.QueueUserWorkItem: | ||
2286 | ThreadPool.QueueUserWorkItem(realCallback, obj); | ||
2287 | break; | ||
2288 | case FireAndForgetMethod.BeginInvoke: | ||
2289 | FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; | ||
2290 | wrapper.FireAndForget(realCallback, obj); | ||
2291 | break; | ||
2292 | case FireAndForgetMethod.SmartThreadPool: | ||
2293 | if (m_ThreadPool == null) | ||
2294 | InitThreadPool(2, 15); | ||
2295 | threadInfo.WorkItem = m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); | ||
2296 | break; | ||
2297 | case FireAndForgetMethod.Thread: | ||
2298 | Thread thread = new Thread(delegate(object o) { realCallback(o); }); | ||
2299 | thread.Start(obj); | ||
2300 | break; | ||
2301 | default: | ||
2302 | throw new NotImplementedException(); | ||
2303 | } | ||
2304 | } | ||
2305 | catch (Exception) | ||
2306 | { | ||
2307 | Interlocked.Decrement(ref numQueuedThreadFuncs); | ||
2308 | ThreadInfo dummy; | ||
2309 | activeThreads.TryRemove(threadFuncNum, out dummy); | ||
2310 | throw; | ||
2311 | } | ||
2312 | } | ||
2313 | |||
2314 | /// <summary> | ||
2315 | /// Returns whether the thread should be logged. Some very common threads aren't logged, | ||
2316 | /// to avoid filling up the log. | ||
2317 | /// </summary> | ||
2318 | /// <param name="stackTrace">A partial stack trace of where the thread was queued</param> | ||
2319 | /// <returns>Whether to log this thread</returns> | ||
2320 | private static bool ShouldLogThread(string stackTrace) | ||
2321 | { | ||
2322 | if (LogThreadPool < 3) | ||
2323 | { | ||
2324 | if (stackTrace.Contains("BeginFireQueueEmpty")) | ||
2325 | return false; | ||
2326 | } | ||
2327 | |||
2328 | return true; | ||
2329 | } | ||
2330 | |||
2331 | /// <summary> | ||
2332 | /// Returns a stack trace for a thread added using FireAndForget(). | ||
2333 | /// </summary> | ||
2334 | /// <param name="full">Will contain the full stack trace</param> | ||
2335 | /// <param name="partial">Will contain only the first frame of the stack trace</param> | ||
2336 | private static void GetFireAndForgetStackTrace(out string full, out string partial) | ||
2337 | { | ||
2338 | string src = Environment.StackTrace; | ||
2339 | string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); | ||
2340 | |||
2341 | StringBuilder dest = new StringBuilder(src.Length); | ||
2342 | |||
2343 | bool started = false; | ||
2344 | bool first = true; | ||
2345 | partial = ""; | ||
2346 | |||
2347 | for (int i = 0; i < lines.Length; i++) | ||
2348 | { | ||
2349 | string line = lines[i]; | ||
2350 | |||
2351 | if (!started) | ||
2352 | { | ||
2353 | // Skip the initial stack frames, because they're of no interest for debugging | ||
2354 | if (line.Contains("StackTrace") || line.Contains("FireAndForget")) | ||
2355 | continue; | ||
2356 | started = true; | ||
2357 | } | ||
2358 | |||
2359 | if (first) | ||
2360 | { | ||
2361 | line = line.TrimStart(); | ||
2362 | first = false; | ||
2363 | partial = line; | ||
2364 | } | ||
2365 | |||
2366 | bool last = (i == lines.Length - 1); | ||
2367 | if (last) | ||
2368 | dest.Append(line); | ||
2369 | else | ||
2370 | dest.AppendLine(line); | ||
2371 | } | ||
2372 | |||
2373 | full = dest.ToString(); | ||
2374 | } | ||
2375 | |||
2376 | #pragma warning disable 0618 | ||
2377 | /// <summary> | ||
2378 | /// Return the stack trace of a different thread. | ||
2379 | /// </summary> | ||
2380 | /// <remarks> | ||
2381 | /// This is complicated because the thread needs to be paused in order to get its stack | ||
2382 | /// trace. And pausing another thread can cause a deadlock. This method attempts to | ||
2383 | /// avoid deadlock by using a short timeout (200ms), after which it gives up and | ||
2384 | /// returns 'null' instead of the stack trace. | ||
2385 | /// | ||
2386 | /// Take from: http://stackoverflow.com/a/14935378 | ||
2387 | /// | ||
2388 | /// WARNING: this doesn't work in Mono. See https://bugzilla.novell.com/show_bug.cgi?id=571691 | ||
2389 | /// | ||
2390 | /// </remarks> | ||
2391 | /// <returns>The stack trace, or null if failed to get it</returns> | ||
2392 | private static StackTrace GetStackTrace(Thread targetThread) | ||
2393 | { | ||
2394 | if (IsPlatformMono) | ||
2395 | { | ||
2396 | // This doesn't work in Mono | ||
2397 | return null; | ||
2398 | } | ||
2399 | |||
2400 | ManualResetEventSlim fallbackThreadReady = new ManualResetEventSlim(); | ||
2401 | ManualResetEventSlim exitedSafely = new ManualResetEventSlim(); | ||
2402 | |||
2403 | try | ||
2404 | { | ||
2405 | new Thread(delegate() | ||
2406 | { | ||
2407 | fallbackThreadReady.Set(); | ||
2408 | while (!exitedSafely.Wait(200)) | ||
2409 | { | ||
2410 | try | ||
2411 | { | ||
2412 | targetThread.Resume(); | ||
2413 | } | ||
2414 | catch (Exception) | ||
2415 | { | ||
2416 | // Whatever happens, do never stop to resume the main-thread regularly until the main-thread has exited safely. | ||
2417 | } | ||
2418 | } | ||
2419 | }).Start(); | ||
2420 | |||
2421 | fallbackThreadReady.Wait(); | ||
2422 | // From here, you have about 200ms to get the stack-trace | ||
2423 | |||
2424 | targetThread.Suspend(); | ||
2425 | |||
2426 | StackTrace trace = null; | ||
2427 | try | ||
2428 | { | ||
2429 | trace = new StackTrace(targetThread, true); | ||
2430 | } | ||
2431 | catch (ThreadStateException) | ||
2432 | { | ||
2433 | //failed to get stack trace, since the fallback-thread resumed the thread | ||
2434 | //possible reasons: | ||
2435 | //1.) This thread was just too slow | ||
2436 | //2.) A deadlock ocurred | ||
2437 | //Automatic retry seems too risky here, so just return null. | ||
2438 | } | ||
2439 | |||
2440 | try | ||
2441 | { | ||
2442 | targetThread.Resume(); | ||
2443 | } | ||
2444 | catch (ThreadStateException) | ||
2445 | { | ||
2446 | // Thread is running again already | ||
2447 | } | ||
2448 | |||
2449 | return trace; | ||
2450 | } | ||
2451 | finally | ||
2452 | { | ||
2453 | // Signal the fallack-thread to stop | ||
2454 | exitedSafely.Set(); | ||
2455 | } | ||
2456 | } | ||
2457 | #pragma warning restore 0618 | ||
2458 | |||
2459 | /// <summary> | ||
2460 | /// Get information about the current state of the smart thread pool. | ||
2461 | /// </summary> | ||
2462 | /// <returns> | ||
2463 | /// null if this isn't the pool being used for non-scriptengine threads. | ||
2464 | /// </returns> | ||
2465 | public static STPInfo GetSmartThreadPoolInfo() | ||
2466 | { | ||
2467 | if (m_ThreadPool == null) | ||
2468 | return null; | ||
2469 | |||
2470 | STPInfo stpi = new STPInfo(); | ||
2471 | stpi.Name = m_ThreadPool.Name; | ||
2472 | stpi.STPStartInfo = m_ThreadPool.STPStartInfo; | ||
2473 | stpi.IsIdle = m_ThreadPool.IsIdle; | ||
2474 | stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown; | ||
2475 | stpi.MaxThreads = m_ThreadPool.MaxThreads; | ||
2476 | stpi.MinThreads = m_ThreadPool.MinThreads; | ||
2477 | stpi.InUseThreads = m_ThreadPool.InUseThreads; | ||
2478 | stpi.ActiveThreads = m_ThreadPool.ActiveThreads; | ||
2479 | stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks; | ||
2480 | stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency; | ||
2481 | |||
2482 | return stpi; | ||
2483 | } | ||
2484 | |||
2485 | #endregion FireAndForget Threading Pattern | ||
2486 | |||
2487 | /// <summary> | ||
2488 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
2489 | /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap | ||
2490 | /// for the callers. | ||
2491 | /// This trims it to a 12 day interval so don't let your frame time get too long. | ||
2492 | /// </summary> | ||
2493 | /// <returns></returns> | ||
2494 | public static Int32 EnvironmentTickCount() | ||
2495 | { | ||
2496 | return Environment.TickCount & EnvironmentTickCountMask; | ||
2497 | } | ||
2498 | const Int32 EnvironmentTickCountMask = 0x3fffffff; | ||
2499 | |||
2500 | /// <summary> | ||
2501 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
2502 | /// and negative every 24.9 days. Subtracts the passed value (previously fetched by | ||
2503 | /// 'EnvironmentTickCount()') and accounts for any wrapping. | ||
2504 | /// </summary> | ||
2505 | /// <param name="newValue"></param> | ||
2506 | /// <param name="prevValue"></param> | ||
2507 | /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns> | ||
2508 | public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue) | ||
2509 | { | ||
2510 | Int32 diff = newValue - prevValue; | ||
2511 | return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); | ||
2512 | } | ||
2513 | |||
2514 | /// <summary> | ||
2515 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
2516 | /// and negative every 24.9 days. Subtracts the passed value (previously fetched by | ||
2517 | /// 'EnvironmentTickCount()') and accounts for any wrapping. | ||
2518 | /// </summary> | ||
2519 | /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns> | ||
2520 | public static Int32 EnvironmentTickCountSubtract(Int32 prevValue) | ||
2521 | { | ||
2522 | return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue); | ||
2523 | } | ||
2524 | |||
2525 | // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount | ||
2526 | // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount(). | ||
2527 | // A positive return value indicates A occured later than B | ||
2528 | public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB) | ||
2529 | { | ||
2530 | // A, B and TC are all between 0 and 0x3fffffff | ||
2531 | int tc = EnvironmentTickCount(); | ||
2532 | |||
2533 | if (tc - tcA >= 0) | ||
2534 | tcA += EnvironmentTickCountMask + 1; | ||
2535 | |||
2536 | if (tc - tcB >= 0) | ||
2537 | tcB += EnvironmentTickCountMask + 1; | ||
2538 | |||
2539 | return tcA - tcB; | ||
2540 | } | ||
2541 | |||
2542 | /// <summary> | ||
2543 | /// Formats a duration (given in milliseconds). | ||
2544 | /// </summary> | ||
2545 | public static string FormatDuration(int ms) | ||
2546 | { | ||
2547 | TimeSpan span = new TimeSpan(ms * TimeSpan.TicksPerMillisecond); | ||
2548 | |||
2549 | string str = ""; | ||
2550 | string suffix = null; | ||
2551 | |||
2552 | int hours = (int)span.TotalHours; | ||
2553 | if (hours > 0) | ||
2554 | { | ||
2555 | str += hours.ToString(str.Length == 0 ? "0" : "00"); | ||
2556 | suffix = "hours"; | ||
2557 | } | ||
2558 | |||
2559 | if ((hours > 0) || (span.Minutes > 0)) | ||
2560 | { | ||
2561 | if (str.Length > 0) | ||
2562 | str += ":"; | ||
2563 | str += span.Minutes.ToString(str.Length == 0 ? "0" : "00"); | ||
2564 | if (suffix == null) | ||
2565 | suffix = "min"; | ||
2566 | } | ||
2567 | |||
2568 | if ((hours > 0) || (span.Minutes > 0) || (span.Seconds > 0)) | ||
2569 | { | ||
2570 | if (str.Length > 0) | ||
2571 | str += ":"; | ||
2572 | str += span.Seconds.ToString(str.Length == 0 ? "0" : "00"); | ||
2573 | if (suffix == null) | ||
2574 | suffix = "sec"; | ||
2575 | } | ||
2576 | |||
2577 | if (suffix == null) | ||
2578 | suffix = "ms"; | ||
2579 | |||
2580 | if (span.TotalMinutes < 1) | ||
2581 | { | ||
2582 | int ms1 = span.Milliseconds; | ||
2583 | if (str.Length > 0) | ||
2584 | { | ||
2585 | ms1 /= 100; | ||
2586 | str += "."; | ||
2587 | } | ||
2588 | str += ms1.ToString("0"); | ||
2589 | } | ||
2590 | |||
2591 | str += " " + suffix; | ||
2592 | |||
2593 | return str; | ||
2594 | } | ||
2595 | |||
2596 | /// <summary> | ||
2597 | /// Prints the call stack at any given point. Useful for debugging. | ||
2598 | /// </summary> | ||
2599 | public static void PrintCallStack() | ||
2600 | { | ||
2601 | PrintCallStack(m_log.DebugFormat); | ||
2602 | } | ||
2603 | |||
2604 | public delegate void DebugPrinter(string msg, params Object[] parm); | ||
2605 | public static void PrintCallStack(DebugPrinter printer) | ||
2606 | { | ||
2607 | StackTrace stackTrace = new StackTrace(true); // get call stack | ||
2608 | StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) | ||
2609 | |||
2610 | // write call stack method names | ||
2611 | foreach (StackFrame stackFrame in stackFrames) | ||
2612 | { | ||
2613 | MethodBase mb = stackFrame.GetMethod(); | ||
2614 | printer("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name | ||
2615 | } | ||
2616 | } | ||
2617 | |||
2618 | /// <summary> | ||
2619 | /// Gets the client IP address | ||
2620 | /// </summary> | ||
2621 | /// <param name="xff"></param> | ||
2622 | /// <returns></returns> | ||
2623 | public static IPEndPoint GetClientIPFromXFF(string xff) | ||
2624 | { | ||
2625 | if (xff == string.Empty) | ||
2626 | return null; | ||
2627 | |||
2628 | string[] parts = xff.Split(new char[] { ',' }); | ||
2629 | if (parts.Length > 0) | ||
2630 | { | ||
2631 | try | ||
2632 | { | ||
2633 | return new IPEndPoint(IPAddress.Parse(parts[0]), 0); | ||
2634 | } | ||
2635 | catch (Exception e) | ||
2636 | { | ||
2637 | m_log.WarnFormat("[UTIL]: Exception parsing XFF header {0}: {1}", xff, e.Message); | ||
2638 | } | ||
2639 | } | ||
2640 | |||
2641 | return null; | ||
2642 | } | ||
2643 | |||
2644 | public static string GetCallerIP(Hashtable req) | ||
2645 | { | ||
2646 | if (req.ContainsKey("headers")) | ||
2647 | { | ||
2648 | try | ||
2649 | { | ||
2650 | Hashtable headers = (Hashtable)req["headers"]; | ||
2651 | if (headers.ContainsKey("remote_addr") && headers["remote_addr"] != null) | ||
2652 | return headers["remote_addr"].ToString(); | ||
2653 | } | ||
2654 | catch (Exception e) | ||
2655 | { | ||
2656 | m_log.WarnFormat("[UTIL]: exception in GetCallerIP: {0}", e.Message); | ||
2657 | } | ||
2658 | } | ||
2659 | return string.Empty; | ||
2660 | } | ||
2661 | |||
2662 | #region Xml Serialization Utilities | ||
2663 | public static bool ReadBoolean(XmlReader reader) | ||
2664 | { | ||
2665 | // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this. | ||
2666 | reader.ReadStartElement(); | ||
2667 | string val = reader.ReadContentAsString().ToLower(); | ||
2668 | bool result = val.Equals("true") || val.Equals("1"); | ||
2669 | reader.ReadEndElement(); | ||
2670 | |||
2671 | return result; | ||
2672 | } | ||
2673 | |||
2674 | public static UUID ReadUUID(XmlReader reader, string name) | ||
2675 | { | ||
2676 | UUID id; | ||
2677 | string idStr; | ||
2678 | |||
2679 | reader.ReadStartElement(name); | ||
2680 | |||
2681 | if (reader.Name == "Guid") | ||
2682 | idStr = reader.ReadElementString("Guid"); | ||
2683 | else if (reader.Name == "UUID") | ||
2684 | idStr = reader.ReadElementString("UUID"); | ||
2685 | else // no leading tag | ||
2686 | idStr = reader.ReadContentAsString(); | ||
2687 | UUID.TryParse(idStr, out id); | ||
2688 | reader.ReadEndElement(); | ||
2689 | |||
2690 | return id; | ||
2691 | } | ||
2692 | |||
2693 | public static Vector3 ReadVector(XmlReader reader, string name) | ||
2694 | { | ||
2695 | Vector3 vec; | ||
2696 | |||
2697 | reader.ReadStartElement(name); | ||
2698 | vec.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // X or x | ||
2699 | vec.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Y or y | ||
2700 | vec.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Z or z | ||
2701 | reader.ReadEndElement(); | ||
2702 | |||
2703 | return vec; | ||
2704 | } | ||
2705 | |||
2706 | public static Quaternion ReadQuaternion(XmlReader reader, string name) | ||
2707 | { | ||
2708 | Quaternion quat = new Quaternion(); | ||
2709 | |||
2710 | reader.ReadStartElement(name); | ||
2711 | while (reader.NodeType != XmlNodeType.EndElement) | ||
2712 | { | ||
2713 | switch (reader.Name.ToLower()) | ||
2714 | { | ||
2715 | case "x": | ||
2716 | quat.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); | ||
2717 | break; | ||
2718 | case "y": | ||
2719 | quat.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); | ||
2720 | break; | ||
2721 | case "z": | ||
2722 | quat.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); | ||
2723 | break; | ||
2724 | case "w": | ||
2725 | quat.W = reader.ReadElementContentAsFloat(reader.Name, String.Empty); | ||
2726 | break; | ||
2727 | } | ||
2728 | } | ||
2729 | |||
2730 | reader.ReadEndElement(); | ||
2731 | |||
2732 | return quat; | ||
2733 | } | ||
2734 | |||
2735 | public static T ReadEnum<T>(XmlReader reader, string name) | ||
2736 | { | ||
2737 | string value = reader.ReadElementContentAsString(name, String.Empty); | ||
2738 | // !!!!! to deal with flags without commas | ||
2739 | if (value.Contains(" ") && !value.Contains(",")) | ||
2740 | value = value.Replace(" ", ", "); | ||
2741 | |||
2742 | return (T)Enum.Parse(typeof(T), value); ; | ||
2743 | } | ||
2744 | #endregion | ||
2745 | |||
2746 | #region Universal User Identifiers | ||
2747 | |||
2748 | /// <summary> | ||
2749 | /// Attempts to parse a UUI into its components: UUID, name and URL. | ||
2750 | /// </summary> | ||
2751 | /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param> | ||
2752 | /// <param name="uuid">the uuid part</param> | ||
2753 | /// <param name="url">the endpoint part (e.g. http://foo.com)</param> | ||
2754 | /// <param name="firstname">the first name part (e.g. Test)</param> | ||
2755 | /// <param name="lastname">the last name part (e.g User)</param> | ||
2756 | /// <param name="secret">the secret part</param> | ||
2757 | public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) | ||
2758 | { | ||
2759 | uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty; | ||
2760 | |||
2761 | string[] parts = value.Split(';'); | ||
2762 | if (parts.Length >= 1) | ||
2763 | if (!UUID.TryParse(parts[0], out uuid)) | ||
2764 | return false; | ||
2765 | |||
2766 | if (parts.Length >= 2) | ||
2767 | url = parts[1]; | ||
2768 | |||
2769 | if (parts.Length >= 3) | ||
2770 | { | ||
2771 | string[] name = parts[2].Split(); | ||
2772 | if (name.Length == 2) | ||
2773 | { | ||
2774 | firstname = name[0]; | ||
2775 | lastname = name[1]; | ||
2776 | } | ||
2777 | } | ||
2778 | if (parts.Length >= 4) | ||
2779 | secret = parts[3]; | ||
2780 | |||
2781 | return true; | ||
2782 | } | ||
2783 | |||
2784 | /// <summary> | ||
2785 | /// For foreign avatars, extracts their original name and Server URL from their First Name and Last Name. | ||
2786 | /// </summary> | ||
2787 | public static bool ParseForeignAvatarName(string firstname, string lastname, | ||
2788 | out string realFirstName, out string realLastName, out string serverURI) | ||
2789 | { | ||
2790 | realFirstName = realLastName = serverURI = string.Empty; | ||
2791 | |||
2792 | if (!lastname.Contains("@")) | ||
2793 | return false; | ||
2794 | |||
2795 | if (!firstname.Contains(".")) | ||
2796 | return false; | ||
2797 | |||
2798 | realFirstName = firstname.Split('.')[0]; | ||
2799 | realLastName = firstname.Split('.')[1]; | ||
2800 | serverURI = new Uri("http://" + lastname.Replace("@", "")).ToString(); | ||
2801 | |||
2802 | return true; | ||
2803 | } | ||
2804 | |||
2805 | /// <summary> | ||
2806 | /// Produces a universal (HG) system-facing identifier given the information | ||
2807 | /// </summary> | ||
2808 | /// <param name="acircuit"></param> | ||
2809 | /// <returns>uuid[;homeURI[;first last]]</returns> | ||
2810 | public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit) | ||
2811 | { | ||
2812 | if (acircuit.ServiceURLs.ContainsKey("HomeURI")) | ||
2813 | return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString()); | ||
2814 | else | ||
2815 | return acircuit.AgentID.ToString(); | ||
2816 | } | ||
2817 | |||
2818 | /// <summary> | ||
2819 | /// Produces a universal (HG) system-facing identifier given the information | ||
2820 | /// </summary> | ||
2821 | /// <param name="id">UUID of the user</param> | ||
2822 | /// <param name="firstName">first name (e.g Test)</param> | ||
2823 | /// <param name="lastName">last name (e.g. User)</param> | ||
2824 | /// <param name="homeURI">homeURI (e.g. http://foo.com)</param> | ||
2825 | /// <returns>a string of the form uuid[;homeURI[;first last]]</returns> | ||
2826 | public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI) | ||
2827 | { | ||
2828 | string agentsURI = homeURI; | ||
2829 | if (!agentsURI.EndsWith("/")) | ||
2830 | agentsURI += "/"; | ||
2831 | |||
2832 | // This is ugly, but there's no other way, given that the name is changed | ||
2833 | // in the agent circuit data for foreigners | ||
2834 | if (lastName.Contains("@")) | ||
2835 | { | ||
2836 | string[] parts = firstName.Split(new char[] { '.' }); | ||
2837 | if (parts.Length == 2) | ||
2838 | return CalcUniversalIdentifier(id, agentsURI, parts[0] + " " + parts[1]); | ||
2839 | } | ||
2840 | |||
2841 | return CalcUniversalIdentifier(id, agentsURI, firstName + " " + lastName); | ||
2842 | } | ||
2843 | |||
2844 | private static string CalcUniversalIdentifier(UUID id, string agentsURI, string name) | ||
2845 | { | ||
2846 | return id.ToString() + ";" + agentsURI + ";" + name; | ||
2847 | } | ||
2848 | |||
2849 | /// <summary> | ||
2850 | /// Produces a universal (HG) user-facing name given the information | ||
2851 | /// </summary> | ||
2852 | /// <param name="firstName"></param> | ||
2853 | /// <param name="lastName"></param> | ||
2854 | /// <param name="homeURI"></param> | ||
2855 | /// <returns>string of the form first.last @foo.com or first last</returns> | ||
2856 | public static string UniversalName(String firstName, String lastName, String homeURI) | ||
2857 | { | ||
2858 | Uri uri = null; | ||
2859 | try | ||
2860 | { | ||
2861 | uri = new Uri(homeURI); | ||
2862 | } | ||
2863 | catch (UriFormatException) | ||
2864 | { | ||
2865 | return firstName + " " + lastName; | ||
2866 | } | ||
2867 | return firstName + "." + lastName + " " + "@" + uri.Authority; | ||
2868 | } | ||
2869 | #endregion | ||
2870 | |||
2871 | /// <summary> | ||
2872 | /// Escapes the special characters used in "LIKE". | ||
2873 | /// </summary> | ||
2874 | /// <remarks> | ||
2875 | /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz" | ||
2876 | /// </remarks> | ||
2877 | public static string EscapeForLike(string str) | ||
2878 | { | ||
2879 | return str.Replace("_", "\\_").Replace("%", "\\%"); | ||
2880 | } | ||
2881 | |||
2882 | /// <summary> | ||
2883 | /// Returns the name of the user's viewer. | ||
2884 | /// </summary> | ||
2885 | /// <remarks> | ||
2886 | /// This method handles two ways that viewers specify their name: | ||
2887 | /// 1. Viewer = "Firestorm-Release 4.4.2.34167", Channel = "(don't care)" -> "Firestorm-Release 4.4.2.34167" | ||
2888 | /// 2. Viewer = "4.5.1.38838", Channel = "Firestorm-Beta" -> "Firestorm-Beta 4.5.1.38838" | ||
2889 | /// </remarks> | ||
2890 | public static string GetViewerName(AgentCircuitData agent) | ||
2891 | { | ||
2892 | string name = agent.Viewer; | ||
2893 | if (name == null) | ||
2894 | name = ""; | ||
2895 | else | ||
2896 | name = name.Trim(); | ||
2897 | |||
2898 | // Check if 'Viewer' is just a version number. If it's *not*, then we | ||
2899 | // assume that it contains the real viewer name, and we return it. | ||
2900 | foreach (char c in name) | ||
2901 | { | ||
2902 | if (Char.IsLetter(c)) | ||
2903 | return name; | ||
2904 | } | ||
2905 | |||
2906 | // The 'Viewer' string contains just a version number. If there's anything in | ||
2907 | // 'Channel' then assume that it's the viewer name. | ||
2908 | if ((agent.Channel != null) && (agent.Channel.Length > 0)) | ||
2909 | name = agent.Channel.Trim() + " " + name; | ||
2910 | |||
2911 | return name; | ||
2912 | } | ||
2913 | |||
2914 | public static void LogFailedXML(string message, string xml) | ||
2915 | { | ||
2916 | int length = xml.Length; | ||
2917 | if (length > 2000) | ||
2918 | xml = xml.Substring(0, 2000) + "..."; | ||
2919 | |||
2920 | m_log.ErrorFormat("{0} Failed XML ({1} bytes) = {2}", message, length, xml); | ||
2921 | } | ||
2922 | |||
2923 | } | ||
2924 | |||
2925 | public class DoubleQueue<T> where T:class | ||
2926 | { | ||
2927 | private Queue<T> m_lowQueue = new Queue<T>(); | ||
2928 | private Queue<T> m_highQueue = new Queue<T>(); | ||
2929 | |||
2930 | private object m_syncRoot = new object(); | ||
2931 | private Semaphore m_s = new Semaphore(0, 1); | ||
2932 | |||
2933 | public DoubleQueue() | ||
2934 | { | ||
2935 | } | ||
2936 | |||
2937 | public virtual int Count | ||
2938 | { | ||
2939 | get | ||
2940 | { | ||
2941 | lock (m_syncRoot) | ||
2942 | return m_highQueue.Count + m_lowQueue.Count; | ||
2943 | } | ||
2944 | } | ||
2945 | |||
2946 | public virtual void Enqueue(T data) | ||
2947 | { | ||
2948 | Enqueue(m_lowQueue, data); | ||
2949 | } | ||
2950 | |||
2951 | public virtual void EnqueueLow(T data) | ||
2952 | { | ||
2953 | Enqueue(m_lowQueue, data); | ||
2954 | } | ||
2955 | |||
2956 | public virtual void EnqueueHigh(T data) | ||
2957 | { | ||
2958 | Enqueue(m_highQueue, data); | ||
2959 | } | ||
2960 | |||
2961 | private void Enqueue(Queue<T> q, T data) | ||
2962 | { | ||
2963 | lock (m_syncRoot) | ||
2964 | { | ||
2965 | q.Enqueue(data); | ||
2966 | m_s.WaitOne(0); | ||
2967 | m_s.Release(); | ||
2968 | } | ||
2969 | } | ||
2970 | |||
2971 | public virtual T Dequeue() | ||
2972 | { | ||
2973 | return Dequeue(Timeout.Infinite); | ||
2974 | } | ||
2975 | |||
2976 | public virtual T Dequeue(int tmo) | ||
2977 | { | ||
2978 | return Dequeue(TimeSpan.FromMilliseconds(tmo)); | ||
2979 | } | ||
2980 | |||
2981 | public virtual T Dequeue(TimeSpan wait) | ||
2982 | { | ||
2983 | T res = null; | ||
2984 | |||
2985 | if (!Dequeue(wait, ref res)) | ||
2986 | return null; | ||
2987 | |||
2988 | return res; | ||
2989 | } | ||
2990 | |||
2991 | public bool Dequeue(int timeout, ref T res) | ||
2992 | { | ||
2993 | return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res); | ||
2994 | } | ||
2995 | |||
2996 | public bool Dequeue(TimeSpan wait, ref T res) | ||
2997 | { | ||
2998 | if (!m_s.WaitOne(wait)) | ||
2999 | return false; | ||
3000 | |||
3001 | lock (m_syncRoot) | ||
3002 | { | ||
3003 | if (m_highQueue.Count > 0) | ||
3004 | res = m_highQueue.Dequeue(); | ||
3005 | else if (m_lowQueue.Count > 0) | ||
3006 | res = m_lowQueue.Dequeue(); | ||
3007 | |||
3008 | if (m_highQueue.Count == 0 && m_lowQueue.Count == 0) | ||
3009 | return true; | ||
3010 | |||
3011 | try | ||
3012 | { | ||
3013 | m_s.Release(); | ||
3014 | } | ||
3015 | catch | ||
3016 | { | ||
3017 | } | ||
3018 | |||
3019 | return true; | ||
3020 | } | ||
3021 | } | ||
3022 | |||
3023 | public virtual void Clear() | ||
3024 | { | ||
3025 | |||
3026 | lock (m_syncRoot) | ||
3027 | { | ||
3028 | // Make sure sem count is 0 | ||
3029 | m_s.WaitOne(0); | ||
3030 | |||
3031 | m_lowQueue.Clear(); | ||
3032 | m_highQueue.Clear(); | ||
3033 | } | ||
3034 | } | ||
3035 | } | ||
3036 | |||
3037 | public class BetterRandom | ||
3038 | { | ||
3039 | private const int BufferSize = 1024; // must be a multiple of 4 | ||
3040 | private byte[] RandomBuffer; | ||
3041 | private int BufferOffset; | ||
3042 | private RNGCryptoServiceProvider rng; | ||
3043 | public BetterRandom() | ||
3044 | { | ||
3045 | RandomBuffer = new byte[BufferSize]; | ||
3046 | rng = new RNGCryptoServiceProvider(); | ||
3047 | BufferOffset = RandomBuffer.Length; | ||
3048 | } | ||
3049 | private void FillBuffer() | ||
3050 | { | ||
3051 | rng.GetBytes(RandomBuffer); | ||
3052 | BufferOffset = 0; | ||
3053 | } | ||
3054 | public int Next() | ||
3055 | { | ||
3056 | if (BufferOffset >= RandomBuffer.Length) | ||
3057 | { | ||
3058 | FillBuffer(); | ||
3059 | } | ||
3060 | int val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff; | ||
3061 | BufferOffset += sizeof(int); | ||
3062 | return val; | ||
3063 | } | ||
3064 | public int Next(int maxValue) | ||
3065 | { | ||
3066 | return Next() % maxValue; | ||
3067 | } | ||
3068 | public int Next(int minValue, int maxValue) | ||
3069 | { | ||
3070 | if (maxValue < minValue) | ||
3071 | { | ||
3072 | throw new ArgumentOutOfRangeException("maxValue must be greater than or equal to minValue"); | ||
3073 | } | ||
3074 | int range = maxValue - minValue; | ||
3075 | return minValue + Next(range); | ||
3076 | } | ||
3077 | public double NextDouble() | ||
3078 | { | ||
3079 | int val = Next(); | ||
3080 | return (double)val / int.MaxValue; | ||
3081 | } | ||
3082 | public void GetBytes(byte[] buff) | ||
3083 | { | ||
3084 | rng.GetBytes(buff); | ||
3085 | } | ||
3086 | } | ||
3087 | } | ||