clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 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 | |||
55 | namespace OpenSim.Framework |
||
56 | { |
||
57 | [Flags] |
||
58 | public enum PermissionMask : uint |
||
59 | { |
||
60 | None = 0, |
||
61 | Transfer = 1 << 13, |
||
62 | Modify = 1 << 14, |
||
63 | Copy = 1 << 15, |
||
64 | Export = 1 << 16, |
||
65 | Move = 1 << 19, |
||
66 | Damage = 1 << 20, |
||
67 | // All does not contain Export, which is special and must be |
||
68 | // explicitly given |
||
69 | All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19) |
||
70 | } |
||
71 | |||
72 | /// <summary> |
||
73 | /// The method used by Util.FireAndForget for asynchronously firing events |
||
74 | /// </summary> |
||
75 | /// <remarks> |
||
76 | /// None is used to execute the method in the same thread that made the call. It should only be used by regression |
||
77 | /// test code that relies on predictable event ordering. |
||
78 | /// RegressionTest is used by regression tests. It fires the call synchronously and does not catch any exceptions. |
||
79 | /// </remarks> |
||
80 | public enum FireAndForgetMethod |
||
81 | { |
||
82 | None, |
||
83 | RegressionTest, |
||
84 | UnsafeQueueUserWorkItem, |
||
85 | QueueUserWorkItem, |
||
86 | BeginInvoke, |
||
87 | SmartThreadPool, |
||
88 | Thread, |
||
89 | } |
||
90 | |||
91 | /// <summary> |
||
92 | /// Class for delivering SmartThreadPool statistical information |
||
93 | /// </summary> |
||
94 | /// <remarks> |
||
95 | /// We do it this way so that we do not directly expose STP. |
||
96 | /// </remarks> |
||
97 | public class STPInfo |
||
98 | { |
||
99 | public string Name { get; set; } |
||
100 | public STPStartInfo STPStartInfo { get; set; } |
||
101 | public WIGStartInfo WIGStartInfo { get; set; } |
||
102 | public bool IsIdle { get; set; } |
||
103 | public bool IsShuttingDown { get; set; } |
||
104 | public int MaxThreads { get; set; } |
||
105 | public int MinThreads { get; set; } |
||
106 | public int InUseThreads { get; set; } |
||
107 | public int ActiveThreads { get; set; } |
||
108 | public int WaitingCallbacks { get; set; } |
||
109 | public int MaxConcurrentWorkItems { get; set; } |
||
110 | } |
||
111 | |||
112 | /// <summary> |
||
113 | /// Miscellaneous utility functions |
||
114 | /// </summary> |
||
115 | public static class Util |
||
116 | { |
||
117 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
118 | |||
119 | private static uint nextXferID = 5000; |
||
120 | private static Random randomClass = new Random(); |
||
121 | |||
122 | // Get a list of invalid file characters (OS dependent) |
||
123 | private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; |
||
124 | private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]"; |
||
125 | private static object XferLock = new object(); |
||
126 | |||
127 | /// <summary> |
||
128 | /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used |
||
129 | /// </summary> |
||
130 | private static SmartThreadPool m_ThreadPool; |
||
131 | |||
132 | // 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. |
||
133 | public static readonly DateTime UnixEpoch = |
||
134 | DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); |
||
135 | |||
136 | private static readonly string rawUUIDPattern |
||
137 | = "[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}"; |
||
138 | public static readonly Regex PermissiveUUIDPattern = new Regex(rawUUIDPattern); |
||
139 | public static readonly Regex UUIDPattern = new Regex(string.Format("^{0}$", rawUUIDPattern)); |
||
140 | |||
141 | public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool; |
||
142 | public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod; |
||
143 | |||
144 | public static bool IsPlatformMono |
||
145 | { |
||
146 | get { return Type.GetType("Mono.Runtime") != null; } |
||
147 | } |
||
148 | |||
149 | /// <summary> |
||
150 | /// Gets the name of the directory where the current running executable |
||
151 | /// is located |
||
152 | /// </summary> |
||
153 | /// <returns>Filesystem path to the directory containing the current |
||
154 | /// executable</returns> |
||
155 | public static string ExecutingDirectory() |
||
156 | { |
||
157 | return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); |
||
158 | } |
||
159 | |||
160 | /// <summary> |
||
161 | /// Linear interpolates B<->C using percent A |
||
162 | /// </summary> |
||
163 | /// <param name="a"></param> |
||
164 | /// <param name="b"></param> |
||
165 | /// <param name="c"></param> |
||
166 | /// <returns></returns> |
||
167 | public static double lerp(double a, double b, double c) |
||
168 | { |
||
169 | return (b*a) + (c*(1 - a)); |
||
170 | } |
||
171 | |||
172 | /// <summary> |
||
173 | /// Bilinear Interpolate, see Lerp but for 2D using 'percents' X & Y. |
||
174 | /// Layout: |
||
175 | /// A B |
||
176 | /// C D |
||
177 | /// A<->C = Y |
||
178 | /// C<->D = X |
||
179 | /// </summary> |
||
180 | /// <param name="x"></param> |
||
181 | /// <param name="y"></param> |
||
182 | /// <param name="a"></param> |
||
183 | /// <param name="b"></param> |
||
184 | /// <param name="c"></param> |
||
185 | /// <param name="d"></param> |
||
186 | /// <returns></returns> |
||
187 | public static double lerp2D(double x, double y, double a, double b, double c, double d) |
||
188 | { |
||
189 | return lerp(y, lerp(x, a, b), lerp(x, c, d)); |
||
190 | } |
||
191 | |||
192 | public static Encoding UTF8 = Encoding.UTF8; |
||
193 | public static Encoding UTF8NoBomEncoding = new UTF8Encoding(false); |
||
194 | |||
195 | /// <value> |
||
196 | /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards) |
||
197 | /// </value> |
||
198 | public static UUID BLANK_TEXTURE_UUID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f"); |
||
199 | |||
200 | #region Vector Equations |
||
201 | |||
202 | /// <summary> |
||
203 | /// Get the distance between two 3d vectors |
||
204 | /// </summary> |
||
205 | /// <param name="a">A 3d vector</param> |
||
206 | /// <param name="b">A 3d vector</param> |
||
207 | /// <returns>The distance between the two vectors</returns> |
||
208 | public static double GetDistanceTo(Vector3 a, Vector3 b) |
||
209 | { |
||
210 | float dx = a.X - b.X; |
||
211 | float dy = a.Y - b.Y; |
||
212 | float dz = a.Z - b.Z; |
||
213 | return Math.Sqrt(dx * dx + dy * dy + dz * dz); |
||
214 | } |
||
215 | |||
216 | /// <summary> |
||
217 | /// Returns true if the distance beween A and B is less than amount. Significantly faster than GetDistanceTo since it eliminates the Sqrt. |
||
218 | /// </summary> |
||
219 | /// <param name="a"></param> |
||
220 | /// <param name="b"></param> |
||
221 | /// <param name="amount"></param> |
||
222 | /// <returns></returns> |
||
223 | public static bool DistanceLessThan(Vector3 a, Vector3 b, double amount) |
||
224 | { |
||
225 | float dx = a.X - b.X; |
||
226 | float dy = a.Y - b.Y; |
||
227 | float dz = a.Z - b.Z; |
||
228 | return (dx*dx + dy*dy + dz*dz) < (amount*amount); |
||
229 | } |
||
230 | |||
231 | /// <summary> |
||
232 | /// Get the magnitude of a 3d vector |
||
233 | /// </summary> |
||
234 | /// <param name="a">A 3d vector</param> |
||
235 | /// <returns>The magnitude of the vector</returns> |
||
236 | public static double GetMagnitude(Vector3 a) |
||
237 | { |
||
238 | return Math.Sqrt((a.X * a.X) + (a.Y * a.Y) + (a.Z * a.Z)); |
||
239 | } |
||
240 | |||
241 | /// <summary> |
||
242 | /// Get a normalized form of a 3d vector |
||
243 | /// </summary> |
||
244 | /// <param name="a">A 3d vector</param> |
||
245 | /// <returns>A new vector which is normalized form of the vector</returns> |
||
246 | /// <remarks>The vector paramater cannot be <0,0,0></remarks> |
||
247 | public static Vector3 GetNormalizedVector(Vector3 a) |
||
248 | { |
||
249 | if (IsZeroVector(a)) |
||
250 | throw new ArgumentException("Vector paramater cannot be a zero vector."); |
||
251 | |||
252 | float Mag = (float) GetMagnitude(a); |
||
253 | return new Vector3(a.X / Mag, a.Y / Mag, a.Z / Mag); |
||
254 | } |
||
255 | |||
256 | /// <summary> |
||
257 | /// Returns if a vector is a zero vector (has all zero components) |
||
258 | /// </summary> |
||
259 | /// <returns></returns> |
||
260 | public static bool IsZeroVector(Vector3 v) |
||
261 | { |
||
262 | if (v.X == 0 && v.Y == 0 && v.Z == 0) |
||
263 | { |
||
264 | return true; |
||
265 | } |
||
266 | |||
267 | return false; |
||
268 | } |
||
269 | |||
270 | # endregion |
||
271 | |||
272 | public static Quaternion Axes2Rot(Vector3 fwd, Vector3 left, Vector3 up) |
||
273 | { |
||
274 | float s; |
||
275 | float tr = (float) (fwd.X + left.Y + up.Z + 1.0); |
||
276 | |||
277 | if (tr >= 1.0) |
||
278 | { |
||
279 | s = (float) (0.5 / Math.Sqrt(tr)); |
||
280 | return new Quaternion( |
||
281 | (left.Z - up.Y) * s, |
||
282 | (up.X - fwd.Z) * s, |
||
283 | (fwd.Y - left.X) * s, |
||
284 | (float) 0.25 / s); |
||
285 | } |
||
286 | else |
||
287 | { |
||
288 | float max = (left.Y > up.Z) ? left.Y : up.Z; |
||
289 | |||
290 | if (max < fwd.X) |
||
291 | { |
||
292 | s = (float) (Math.Sqrt(fwd.X - (left.Y + up.Z) + 1.0)); |
||
293 | float x = (float) (s * 0.5); |
||
294 | s = (float) (0.5 / s); |
||
295 | return new Quaternion( |
||
296 | x, |
||
297 | (fwd.Y + left.X) * s, |
||
298 | (up.X + fwd.Z) * s, |
||
299 | (left.Z - up.Y) * s); |
||
300 | } |
||
301 | else if (max == left.Y) |
||
302 | { |
||
303 | s = (float) (Math.Sqrt(left.Y - (up.Z + fwd.X) + 1.0)); |
||
304 | float y = (float) (s * 0.5); |
||
305 | s = (float) (0.5 / s); |
||
306 | return new Quaternion( |
||
307 | (fwd.Y + left.X) * s, |
||
308 | y, |
||
309 | (left.Z + up.Y) * s, |
||
310 | (up.X - fwd.Z) * s); |
||
311 | } |
||
312 | else |
||
313 | { |
||
314 | s = (float) (Math.Sqrt(up.Z - (fwd.X + left.Y) + 1.0)); |
||
315 | float z = (float) (s * 0.5); |
||
316 | s = (float) (0.5 / s); |
||
317 | return new Quaternion( |
||
318 | (up.X + fwd.Z) * s, |
||
319 | (left.Z + up.Y) * s, |
||
320 | z, |
||
321 | (fwd.Y - left.X) * s); |
||
322 | } |
||
323 | } |
||
324 | } |
||
325 | |||
326 | public static Random RandomClass |
||
327 | { |
||
328 | get { return randomClass; } |
||
329 | } |
||
330 | |||
331 | public static ulong UIntsToLong(uint X, uint Y) |
||
332 | { |
||
333 | return Utils.UIntsToLong(X, Y); |
||
334 | } |
||
335 | |||
336 | public static T Clamp<T>(T x, T min, T max) |
||
337 | where T : IComparable<T> |
||
338 | { |
||
339 | return x.CompareTo(max) > 0 ? max : |
||
340 | x.CompareTo(min) < 0 ? min : |
||
341 | x; |
||
342 | } |
||
343 | |||
344 | // Clamp the maximum magnitude of a vector |
||
345 | public static Vector3 ClampV(Vector3 x, float max) |
||
346 | { |
||
347 | float lenSq = x.LengthSquared(); |
||
348 | if (lenSq > (max * max)) |
||
349 | { |
||
350 | x = x / x.Length() * max; |
||
351 | } |
||
352 | |||
353 | return x; |
||
354 | } |
||
355 | |||
356 | // Inclusive, within range test (true if equal to the endpoints) |
||
357 | public static bool InRange<T>(T x, T min, T max) |
||
358 | where T : IComparable<T> |
||
359 | { |
||
360 | return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0; |
||
361 | } |
||
362 | |||
363 | public static uint GetNextXferID() |
||
364 | { |
||
365 | uint id = 0; |
||
366 | lock (XferLock) |
||
367 | { |
||
368 | id = nextXferID; |
||
369 | nextXferID++; |
||
370 | } |
||
371 | return id; |
||
372 | } |
||
373 | |||
374 | public static string GetFileName(string file) |
||
375 | { |
||
376 | // Return just the filename on UNIX platforms |
||
377 | // TODO: this should be customisable with a prefix, but that's something to do later. |
||
378 | if (Environment.OSVersion.Platform == PlatformID.Unix) |
||
379 | { |
||
380 | return file; |
||
381 | } |
||
382 | |||
383 | // Return %APPDATA%/OpenSim/file for 2K/XP/NT/2K3/VISTA |
||
384 | // TODO: Switch this to System.Enviroment.SpecialFolders.ApplicationData |
||
385 | if (Environment.OSVersion.Platform == PlatformID.Win32NT) |
||
386 | { |
||
387 | if (!Directory.Exists("%APPDATA%\\OpenSim\\")) |
||
388 | { |
||
389 | Directory.CreateDirectory("%APPDATA%\\OpenSim"); |
||
390 | } |
||
391 | |||
392 | return "%APPDATA%\\OpenSim\\" + file; |
||
393 | } |
||
394 | |||
395 | // Catch all - covers older windows versions |
||
396 | // (but those probably wont work anyway) |
||
397 | return file; |
||
398 | } |
||
399 | |||
400 | /// <summary> |
||
401 | /// Debug utility function to convert OSD into formatted XML for debugging purposes. |
||
402 | /// </summary> |
||
403 | /// <param name="osd"> |
||
404 | /// A <see cref="OSD"/> |
||
405 | /// </param> |
||
406 | /// <returns> |
||
407 | /// A <see cref="System.String"/> |
||
408 | /// </returns> |
||
409 | public static string GetFormattedXml(OSD osd) |
||
410 | { |
||
411 | return GetFormattedXml(OSDParser.SerializeLLSDXmlString(osd)); |
||
412 | } |
||
413 | |||
414 | /// <summary> |
||
415 | /// Debug utility function to convert unbroken strings of XML into something human readable for occasional debugging purposes. |
||
416 | /// </summary> |
||
417 | /// <remarks> |
||
418 | /// Please don't delete me even if I appear currently unused! |
||
419 | /// </remarks> |
||
420 | /// <param name="rawXml"></param> |
||
421 | /// <returns></returns> |
||
422 | public static string GetFormattedXml(string rawXml) |
||
423 | { |
||
424 | XmlDocument xd = new XmlDocument(); |
||
425 | xd.LoadXml(rawXml); |
||
426 | |||
427 | StringBuilder sb = new StringBuilder(); |
||
428 | StringWriter sw = new StringWriter(sb); |
||
429 | |||
430 | XmlTextWriter xtw = new XmlTextWriter(sw); |
||
431 | xtw.Formatting = Formatting.Indented; |
||
432 | |||
433 | try |
||
434 | { |
||
435 | xd.WriteTo(xtw); |
||
436 | } |
||
437 | finally |
||
438 | { |
||
439 | xtw.Close(); |
||
440 | } |
||
441 | |||
442 | return sb.ToString(); |
||
443 | } |
||
444 | |||
445 | /// <summary> |
||
446 | /// Is the platform Windows? |
||
447 | /// </summary> |
||
448 | /// <returns>true if so, false otherwise</returns> |
||
449 | public static bool IsWindows() |
||
450 | { |
||
451 | PlatformID platformId = Environment.OSVersion.Platform; |
||
452 | |||
453 | return (platformId == PlatformID.Win32NT |
||
454 | || platformId == PlatformID.Win32S |
||
455 | || platformId == PlatformID.Win32Windows |
||
456 | || platformId == PlatformID.WinCE); |
||
457 | } |
||
458 | |||
459 | public static bool LoadArchSpecificWindowsDll(string libraryName) |
||
460 | { |
||
461 | // We do this so that OpenSimulator on Windows loads the correct native library depending on whether |
||
462 | // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports |
||
463 | // will find it already loaded later on. |
||
464 | // |
||
465 | // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be |
||
466 | // controlled in config files. |
||
467 | string nativeLibraryPath; |
||
468 | |||
469 | if (Util.Is64BitProcess()) |
||
470 | nativeLibraryPath = "lib64/" + libraryName; |
||
471 | else |
||
472 | nativeLibraryPath = "lib32/" + libraryName; |
||
473 | |||
474 | m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath); |
||
475 | |||
476 | if (Util.LoadLibrary(nativeLibraryPath) == IntPtr.Zero) |
||
477 | { |
||
478 | m_log.ErrorFormat( |
||
479 | "[UTIL]: Couldn't find native Windows library at {0}", nativeLibraryPath); |
||
480 | |||
481 | return false; |
||
482 | } |
||
483 | else |
||
484 | { |
||
485 | return true; |
||
486 | } |
||
487 | } |
||
488 | |||
489 | public static bool IsEnvironmentSupported(ref string reason) |
||
490 | { |
||
491 | // Must have .NET 2.0 (Generics / libsl) |
||
492 | if (Environment.Version.Major < 2) |
||
493 | { |
||
494 | reason = ".NET 1.0/1.1 lacks components that is used by OpenSim"; |
||
495 | return false; |
||
496 | } |
||
497 | |||
498 | // Windows 95/98/ME are unsupported |
||
499 | if (Environment.OSVersion.Platform == PlatformID.Win32Windows && |
||
500 | Environment.OSVersion.Platform != PlatformID.Win32NT) |
||
501 | { |
||
502 | reason = "Windows 95/98/ME will not run OpenSim"; |
||
503 | return false; |
||
504 | } |
||
505 | |||
506 | // Windows 2000 / Pre-SP2 XP |
||
507 | if (Environment.OSVersion.Version.Major == 5 && |
||
508 | Environment.OSVersion.Version.Minor == 0) |
||
509 | { |
||
510 | reason = "Please update to Windows XP Service Pack 2 or Server2003"; |
||
511 | return false; |
||
512 | } |
||
513 | |||
514 | return true; |
||
515 | } |
||
516 | |||
517 | public static int UnixTimeSinceEpoch() |
||
518 | { |
||
519 | return ToUnixTime(DateTime.UtcNow); |
||
520 | } |
||
521 | |||
522 | public static int ToUnixTime(DateTime stamp) |
||
523 | { |
||
524 | TimeSpan t = stamp.ToUniversalTime() - UnixEpoch; |
||
525 | return (int)t.TotalSeconds; |
||
526 | } |
||
527 | |||
528 | public static DateTime ToDateTime(ulong seconds) |
||
529 | { |
||
530 | return UnixEpoch.AddSeconds(seconds); |
||
531 | } |
||
532 | |||
533 | public static DateTime ToDateTime(int seconds) |
||
534 | { |
||
535 | return UnixEpoch.AddSeconds(seconds); |
||
536 | } |
||
537 | |||
538 | /// <summary> |
||
539 | /// Return an md5 hash of the given string |
||
540 | /// </summary> |
||
541 | /// <param name="data"></param> |
||
542 | /// <returns></returns> |
||
543 | public static string Md5Hash(string data) |
||
544 | { |
||
545 | byte[] dataMd5 = ComputeMD5Hash(data); |
||
546 | StringBuilder sb = new StringBuilder(); |
||
547 | for (int i = 0; i < dataMd5.Length; i++) |
||
548 | sb.AppendFormat("{0:x2}", dataMd5[i]); |
||
549 | return sb.ToString(); |
||
550 | } |
||
551 | |||
552 | private static byte[] ComputeMD5Hash(string data) |
||
553 | { |
||
554 | MD5 md5 = MD5.Create(); |
||
555 | return md5.ComputeHash(Encoding.Default.GetBytes(data)); |
||
556 | } |
||
557 | |||
558 | /// <summary> |
||
559 | /// Return an SHA1 hash |
||
560 | /// </summary> |
||
561 | /// <param name="data"></param> |
||
562 | /// <returns></returns> |
||
563 | public static string SHA1Hash(string data) |
||
564 | { |
||
565 | return SHA1Hash(Encoding.Default.GetBytes(data)); |
||
566 | } |
||
567 | |||
568 | /// <summary> |
||
569 | /// Return an SHA1 hash |
||
570 | /// </summary> |
||
571 | /// <param name="data"></param> |
||
572 | /// <returns></returns> |
||
573 | public static string SHA1Hash(byte[] data) |
||
574 | { |
||
575 | byte[] hash = ComputeSHA1Hash(data); |
||
576 | return BitConverter.ToString(hash).Replace("-", String.Empty); |
||
577 | } |
||
578 | |||
579 | private static byte[] ComputeSHA1Hash(byte[] src) |
||
580 | { |
||
581 | SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider(); |
||
582 | return SHA1.ComputeHash(src); |
||
583 | } |
||
584 | |||
585 | public static int fast_distance2d(int x, int y) |
||
586 | { |
||
587 | x = Math.Abs(x); |
||
588 | y = Math.Abs(y); |
||
589 | |||
590 | int min = Math.Min(x, y); |
||
591 | |||
592 | return (x + y - (min >> 1) - (min >> 2) + (min >> 4)); |
||
593 | } |
||
594 | |||
595 | /// <summary> |
||
596 | /// Determines whether a point is inside a bounding box. |
||
597 | /// </summary> |
||
598 | /// <param name='v'></param> |
||
599 | /// <param name='min'></param> |
||
600 | /// <param name='max'></param> |
||
601 | /// <returns></returns> |
||
602 | public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max) |
||
603 | { |
||
604 | return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z |
||
605 | && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z; |
||
606 | } |
||
607 | |||
608 | /// <summary> |
||
609 | /// Are the co-ordinates of the new region visible from the old region? |
||
610 | /// </summary> |
||
611 | /// <param name="oldx">Old region x-coord</param> |
||
612 | /// <param name="newx">New region x-coord</param> |
||
613 | /// <param name="oldy">Old region y-coord</param> |
||
614 | /// <param name="newy">New region y-coord</param> |
||
615 | /// <returns></returns> |
||
616 | public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy) |
||
617 | { |
||
618 | int dd = (int)((drawdist + Constants.RegionSize - 1) / Constants.RegionSize); |
||
619 | |||
620 | int startX = (int)oldx - dd; |
||
621 | int startY = (int)oldy - dd; |
||
622 | |||
623 | int endX = (int)oldx + dd; |
||
624 | int endY = (int)oldy + dd; |
||
625 | |||
626 | return (newx < startX || endX < newx || newy < startY || endY < newy); |
||
627 | } |
||
628 | |||
629 | public static string FieldToString(byte[] bytes) |
||
630 | { |
||
631 | return FieldToString(bytes, String.Empty); |
||
632 | } |
||
633 | |||
634 | /// <summary> |
||
635 | /// Convert a variable length field (byte array) to a string, with a |
||
636 | /// field name prepended to each line of the output |
||
637 | /// </summary> |
||
638 | /// <remarks>If the byte array has unprintable characters in it, a |
||
639 | /// hex dump will be put in the string instead</remarks> |
||
640 | /// <param name="bytes">The byte array to convert to a string</param> |
||
641 | /// <param name="fieldName">A field name to prepend to each line of output</param> |
||
642 | /// <returns>An ASCII string or a string containing a hex dump, minus |
||
643 | /// the null terminator</returns> |
||
644 | public static string FieldToString(byte[] bytes, string fieldName) |
||
645 | { |
||
646 | // Check for a common case |
||
647 | if (bytes.Length == 0) return String.Empty; |
||
648 | |||
649 | StringBuilder output = new StringBuilder(); |
||
650 | bool printable = true; |
||
651 | |||
652 | for (int i = 0; i < bytes.Length; ++i) |
||
653 | { |
||
654 | // Check if there are any unprintable characters in the array |
||
655 | if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09 |
||
656 | && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00) |
||
657 | { |
||
658 | printable = false; |
||
659 | break; |
||
660 | } |
||
661 | } |
||
662 | |||
663 | if (printable) |
||
664 | { |
||
665 | if (fieldName.Length > 0) |
||
666 | { |
||
667 | output.Append(fieldName); |
||
668 | output.Append(": "); |
||
669 | } |
||
670 | |||
671 | output.Append(CleanString(Util.UTF8.GetString(bytes, 0, bytes.Length - 1))); |
||
672 | } |
||
673 | else |
||
674 | { |
||
675 | for (int i = 0; i < bytes.Length; i += 16) |
||
676 | { |
||
677 | if (i != 0) |
||
678 | output.Append(Environment.NewLine); |
||
679 | if (fieldName.Length > 0) |
||
680 | { |
||
681 | output.Append(fieldName); |
||
682 | output.Append(": "); |
||
683 | } |
||
684 | |||
685 | for (int j = 0; j < 16; j++) |
||
686 | { |
||
687 | if ((i + j) < bytes.Length) |
||
688 | output.Append(String.Format("{0:X2} ", bytes[i + j])); |
||
689 | else |
||
690 | output.Append(" "); |
||
691 | } |
||
692 | |||
693 | for (int j = 0; j < 16 && (i + j) < bytes.Length; j++) |
||
694 | { |
||
695 | if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E) |
||
696 | output.Append((char) bytes[i + j]); |
||
697 | else |
||
698 | output.Append("."); |
||
699 | } |
||
700 | } |
||
701 | } |
||
702 | |||
703 | return output.ToString(); |
||
704 | } |
||
705 | |||
706 | /// <summary> |
||
707 | /// Converts a URL to a IPAddress |
||
708 | /// </summary> |
||
709 | /// <param name="url">URL Standard Format</param> |
||
710 | /// <returns>A resolved IP Address</returns> |
||
711 | public static IPAddress GetHostFromURL(string url) |
||
712 | { |
||
713 | return GetHostFromDNS(url.Split(new char[] {'/', ':'})[3]); |
||
714 | } |
||
715 | |||
716 | /// <summary> |
||
717 | /// Returns a IP address from a specified DNS, favouring IPv4 addresses. |
||
718 | /// </summary> |
||
719 | /// <param name="dnsAddress">DNS Hostname</param> |
||
720 | /// <returns>An IP address, or null</returns> |
||
721 | public static IPAddress GetHostFromDNS(string dnsAddress) |
||
722 | { |
||
723 | // Is it already a valid IP? No need to look it up. |
||
724 | IPAddress ipa; |
||
725 | if (IPAddress.TryParse(dnsAddress, out ipa)) |
||
726 | return ipa; |
||
727 | |||
728 | IPAddress[] hosts = null; |
||
729 | |||
730 | // Not an IP, lookup required |
||
731 | try |
||
732 | { |
||
733 | hosts = Dns.GetHostEntry(dnsAddress).AddressList; |
||
734 | } |
||
735 | catch (Exception e) |
||
736 | { |
||
737 | m_log.WarnFormat("[UTIL]: An error occurred while resolving host name {0}, {1}", dnsAddress, e); |
||
738 | |||
739 | // Still going to throw the exception on for now, since this was what was happening in the first place |
||
740 | throw e; |
||
741 | } |
||
742 | |||
743 | foreach (IPAddress host in hosts) |
||
744 | { |
||
745 | if (host.AddressFamily == AddressFamily.InterNetwork) |
||
746 | { |
||
747 | return host; |
||
748 | } |
||
749 | } |
||
750 | |||
751 | if (hosts.Length > 0) |
||
752 | return hosts[0]; |
||
753 | |||
754 | return null; |
||
755 | } |
||
756 | |||
757 | public static Uri GetURI(string protocol, string hostname, int port, string path) |
||
758 | { |
||
759 | return new UriBuilder(protocol, hostname, port, path).Uri; |
||
760 | } |
||
761 | |||
762 | /// <summary> |
||
763 | /// Gets a list of all local system IP addresses |
||
764 | /// </summary> |
||
765 | /// <returns></returns> |
||
766 | public static IPAddress[] GetLocalHosts() |
||
767 | { |
||
768 | return Dns.GetHostAddresses(Dns.GetHostName()); |
||
769 | } |
||
770 | |||
771 | public static IPAddress GetLocalHost() |
||
772 | { |
||
773 | IPAddress[] iplist = GetLocalHosts(); |
||
774 | |||
775 | if (iplist.Length == 0) // No accessible external interfaces |
||
776 | { |
||
777 | IPAddress[] loopback = Dns.GetHostAddresses("localhost"); |
||
778 | IPAddress localhost = loopback[0]; |
||
779 | |||
780 | return localhost; |
||
781 | } |
||
782 | |||
783 | foreach (IPAddress host in iplist) |
||
784 | { |
||
785 | if (!IPAddress.IsLoopback(host) && host.AddressFamily == AddressFamily.InterNetwork) |
||
786 | { |
||
787 | return host; |
||
788 | } |
||
789 | } |
||
790 | |||
791 | if (iplist.Length > 0) |
||
792 | { |
||
793 | foreach (IPAddress host in iplist) |
||
794 | { |
||
795 | if (host.AddressFamily == AddressFamily.InterNetwork) |
||
796 | return host; |
||
797 | } |
||
798 | // Well all else failed... |
||
799 | return iplist[0]; |
||
800 | } |
||
801 | |||
802 | return null; |
||
803 | } |
||
804 | |||
805 | /// <summary> |
||
806 | /// Removes all invalid path chars (OS dependent) |
||
807 | /// </summary> |
||
808 | /// <param name="path">path</param> |
||
809 | /// <returns>safe path</returns> |
||
810 | public static string safePath(string path) |
||
811 | { |
||
812 | return Regex.Replace(path, regexInvalidPathChars, String.Empty); |
||
813 | } |
||
814 | |||
815 | /// <summary> |
||
816 | /// Removes all invalid filename chars (OS dependent) |
||
817 | /// </summary> |
||
818 | /// <param name="path">filename</param> |
||
819 | /// <returns>safe filename</returns> |
||
820 | public static string safeFileName(string filename) |
||
821 | { |
||
822 | return Regex.Replace(filename, regexInvalidFileChars, String.Empty); |
||
823 | ; |
||
824 | } |
||
825 | |||
826 | // |
||
827 | // directory locations |
||
828 | // |
||
829 | |||
830 | public static string homeDir() |
||
831 | { |
||
832 | string temp; |
||
833 | // string personal=(Environment.GetFolderPath(Environment.SpecialFolder.Personal)); |
||
834 | // temp = Path.Combine(personal,".OpenSim"); |
||
835 | temp = "."; |
||
836 | return temp; |
||
837 | } |
||
838 | |||
839 | public static string assetsDir() |
||
840 | { |
||
841 | return Path.Combine(configDir(), "assets"); |
||
842 | } |
||
843 | |||
844 | public static string inventoryDir() |
||
845 | { |
||
846 | return Path.Combine(configDir(), "inventory"); |
||
847 | } |
||
848 | |||
849 | public static string configDir() |
||
850 | { |
||
851 | return "."; |
||
852 | } |
||
853 | |||
854 | public static string dataDir() |
||
855 | { |
||
856 | return "."; |
||
857 | } |
||
858 | |||
859 | public static string logFile() |
||
860 | { |
||
861 | foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) |
||
862 | { |
||
863 | if (appender is FileAppender) |
||
864 | { |
||
865 | return ((FileAppender)appender).File; |
||
866 | } |
||
867 | } |
||
868 | |||
869 | return "./OpenSim.log"; |
||
870 | } |
||
871 | |||
872 | public static string logDir() |
||
873 | { |
||
874 | return Path.GetDirectoryName(logFile()); |
||
875 | } |
||
876 | |||
877 | // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html |
||
878 | public static string GetUniqueFilename(string FileName) |
||
879 | { |
||
880 | int count = 0; |
||
881 | string Name; |
||
882 | |||
883 | if (File.Exists(FileName)) |
||
884 | { |
||
885 | FileInfo f = new FileInfo(FileName); |
||
886 | |||
887 | if (!String.IsNullOrEmpty(f.Extension)) |
||
888 | { |
||
889 | Name = f.FullName.Substring(0, f.FullName.LastIndexOf('.')); |
||
890 | } |
||
891 | else |
||
892 | { |
||
893 | Name = f.FullName; |
||
894 | } |
||
895 | |||
896 | while (File.Exists(FileName)) |
||
897 | { |
||
898 | count++; |
||
899 | FileName = Name + count + f.Extension; |
||
900 | } |
||
901 | } |
||
902 | return FileName; |
||
903 | } |
||
904 | |||
905 | #region Nini (config) related Methods |
||
906 | public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) |
||
907 | { |
||
908 | if (!File.Exists(fileName)) |
||
909 | { |
||
910 | //create new file |
||
911 | } |
||
912 | XmlConfigSource config = new XmlConfigSource(fileName); |
||
913 | AddDataRowToConfig(config, row); |
||
914 | config.Save(); |
||
915 | |||
916 | return config; |
||
917 | } |
||
918 | |||
919 | public static void AddDataRowToConfig(IConfigSource config, DataRow row) |
||
920 | { |
||
921 | config.Configs.Add((string) row[0]); |
||
922 | for (int i = 0; i < row.Table.Columns.Count; i++) |
||
923 | { |
||
924 | config.Configs[(string) row[0]].Set(row.Table.Columns[i].ColumnName, row[i]); |
||
925 | } |
||
926 | } |
||
927 | |||
928 | public static string GetConfigVarWithDefaultSection(IConfigSource config, string varname, string section) |
||
929 | { |
||
930 | // First, check the Startup section, the default section |
||
931 | IConfig cnf = config.Configs["Startup"]; |
||
932 | if (cnf == null) |
||
933 | return string.Empty; |
||
934 | string val = cnf.GetString(varname, string.Empty); |
||
935 | |||
936 | // Then check for an overwrite of the default in the given section |
||
937 | if (!string.IsNullOrEmpty(section)) |
||
938 | { |
||
939 | cnf = config.Configs[section]; |
||
940 | if (cnf != null) |
||
941 | val = cnf.GetString(varname, val); |
||
942 | } |
||
943 | |||
944 | return val; |
||
945 | } |
||
946 | |||
947 | /// <summary> |
||
948 | /// Gets the value of a configuration variable by looking into |
||
949 | /// multiple sections in order. The latter sections overwrite |
||
950 | /// any values previously found. |
||
951 | /// </summary> |
||
952 | /// <typeparam name="T">Type of the variable</typeparam> |
||
953 | /// <param name="config">The configuration object</param> |
||
954 | /// <param name="varname">The configuration variable</param> |
||
955 | /// <param name="sections">Ordered sequence of sections to look at</param> |
||
956 | /// <returns></returns> |
||
957 | public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections) |
||
958 | { |
||
959 | return GetConfigVarFromSections<T>(config, varname, sections, default(T)); |
||
960 | } |
||
961 | |||
962 | /// <summary> |
||
963 | /// Gets the value of a configuration variable by looking into |
||
964 | /// multiple sections in order. The latter sections overwrite |
||
965 | /// any values previously found. |
||
966 | /// </summary> |
||
967 | /// <remarks> |
||
968 | /// If no value is found then the given default value is returned |
||
969 | /// </remarks> |
||
970 | /// <typeparam name="T">Type of the variable</typeparam> |
||
971 | /// <param name="config">The configuration object</param> |
||
972 | /// <param name="varname">The configuration variable</param> |
||
973 | /// <param name="sections">Ordered sequence of sections to look at</param> |
||
974 | /// <param name="val">Default value</param> |
||
975 | /// <returns></returns> |
||
976 | public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val) |
||
977 | { |
||
978 | foreach (string section in sections) |
||
979 | { |
||
980 | IConfig cnf = config.Configs[section]; |
||
981 | if (cnf == null) |
||
982 | continue; |
||
983 | |||
984 | if (typeof(T) == typeof(String)) |
||
985 | val = cnf.GetString(varname, (string)val); |
||
986 | else if (typeof(T) == typeof(Boolean)) |
||
987 | val = cnf.GetBoolean(varname, (bool)val); |
||
988 | else if (typeof(T) == typeof(Int32)) |
||
989 | val = cnf.GetInt(varname, (int)val); |
||
990 | else if (typeof(T) == typeof(float)) |
||
991 | val = cnf.GetFloat(varname, (int)val); |
||
992 | else |
||
993 | m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T)); |
||
994 | } |
||
995 | |||
996 | return (T)val; |
||
997 | } |
||
998 | |||
999 | #endregion |
||
1000 | |||
1001 | public static float Clip(float x, float min, float max) |
||
1002 | { |
||
1003 | return Math.Min(Math.Max(x, min), max); |
||
1004 | } |
||
1005 | |||
1006 | public static int Clip(int x, int min, int max) |
||
1007 | { |
||
1008 | return Math.Min(Math.Max(x, min), max); |
||
1009 | } |
||
1010 | |||
1011 | public static Vector3 Clip(Vector3 vec, float min, float max) |
||
1012 | { |
||
1013 | return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max), |
||
1014 | Clip(vec.Z, min, max)); |
||
1015 | } |
||
1016 | |||
1017 | /// <summary> |
||
1018 | /// Convert an UUID to a raw uuid string. Right now this is a string without hyphens. |
||
1019 | /// </summary> |
||
1020 | /// <param name="UUID"></param> |
||
1021 | /// <returns></returns> |
||
1022 | public static String ToRawUuidString(UUID UUID) |
||
1023 | { |
||
1024 | return UUID.Guid.ToString("n"); |
||
1025 | } |
||
1026 | |||
1027 | public static string CleanString(string input) |
||
1028 | { |
||
1029 | if (input.Length == 0) |
||
1030 | return input; |
||
1031 | |||
1032 | int clip = input.Length; |
||
1033 | |||
1034 | // Test for ++ string terminator |
||
1035 | int pos = input.IndexOf("\0"); |
||
1036 | if (pos != -1 && pos < clip) |
||
1037 | clip = pos; |
||
1038 | |||
1039 | // Test for CR |
||
1040 | pos = input.IndexOf("\r"); |
||
1041 | if (pos != -1 && pos < clip) |
||
1042 | clip = pos; |
||
1043 | |||
1044 | // Test for LF |
||
1045 | pos = input.IndexOf("\n"); |
||
1046 | if (pos != -1 && pos < clip) |
||
1047 | clip = pos; |
||
1048 | |||
1049 | // Truncate string before first end-of-line character found |
||
1050 | return input.Substring(0, clip); |
||
1051 | } |
||
1052 | |||
1053 | /// <summary> |
||
1054 | /// returns the contents of /etc/issue on Unix Systems |
||
1055 | /// Use this for where it's absolutely necessary to implement platform specific stuff |
||
1056 | /// </summary> |
||
1057 | /// <returns></returns> |
||
1058 | public static string ReadEtcIssue() |
||
1059 | { |
||
1060 | try |
||
1061 | { |
||
1062 | StreamReader sr = new StreamReader("/etc/issue.net"); |
||
1063 | string issue = sr.ReadToEnd(); |
||
1064 | sr.Close(); |
||
1065 | return issue; |
||
1066 | } |
||
1067 | catch (Exception) |
||
1068 | { |
||
1069 | return ""; |
||
1070 | } |
||
1071 | } |
||
1072 | |||
1073 | public static void SerializeToFile(string filename, Object obj) |
||
1074 | { |
||
1075 | IFormatter formatter = new BinaryFormatter(); |
||
1076 | Stream stream = null; |
||
1077 | |||
1078 | try |
||
1079 | { |
||
1080 | stream = new FileStream( |
||
1081 | filename, FileMode.Create, |
||
1082 | FileAccess.Write, FileShare.None); |
||
1083 | |||
1084 | formatter.Serialize(stream, obj); |
||
1085 | } |
||
1086 | catch (Exception e) |
||
1087 | { |
||
1088 | m_log.Error(e.ToString()); |
||
1089 | } |
||
1090 | finally |
||
1091 | { |
||
1092 | if (stream != null) |
||
1093 | { |
||
1094 | stream.Close(); |
||
1095 | } |
||
1096 | } |
||
1097 | } |
||
1098 | |||
1099 | public static Object DeserializeFromFile(string filename) |
||
1100 | { |
||
1101 | IFormatter formatter = new BinaryFormatter(); |
||
1102 | Stream stream = null; |
||
1103 | Object ret = null; |
||
1104 | |||
1105 | try |
||
1106 | { |
||
1107 | stream = new FileStream( |
||
1108 | filename, FileMode.Open, |
||
1109 | FileAccess.Read, FileShare.None); |
||
1110 | |||
1111 | ret = formatter.Deserialize(stream); |
||
1112 | } |
||
1113 | catch (Exception e) |
||
1114 | { |
||
1115 | m_log.Error(e.ToString()); |
||
1116 | } |
||
1117 | finally |
||
1118 | { |
||
1119 | if (stream != null) |
||
1120 | { |
||
1121 | stream.Close(); |
||
1122 | } |
||
1123 | } |
||
1124 | |||
1125 | return ret; |
||
1126 | } |
||
1127 | |||
1128 | public static string Compress(string text) |
||
1129 | { |
||
1130 | byte[] buffer = Util.UTF8.GetBytes(text); |
||
1131 | MemoryStream memory = new MemoryStream(); |
||
1132 | using (GZipStream compressor = new GZipStream(memory, CompressionMode.Compress, true)) |
||
1133 | { |
||
1134 | compressor.Write(buffer, 0, buffer.Length); |
||
1135 | } |
||
1136 | |||
1137 | memory.Position = 0; |
||
1138 | |||
1139 | byte[] compressed = new byte[memory.Length]; |
||
1140 | memory.Read(compressed, 0, compressed.Length); |
||
1141 | |||
1142 | byte[] compressedBuffer = new byte[compressed.Length + 4]; |
||
1143 | Buffer.BlockCopy(compressed, 0, compressedBuffer, 4, compressed.Length); |
||
1144 | Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, compressedBuffer, 0, 4); |
||
1145 | return Convert.ToBase64String(compressedBuffer); |
||
1146 | } |
||
1147 | |||
1148 | public static string Decompress(string compressedText) |
||
1149 | { |
||
1150 | byte[] compressedBuffer = Convert.FromBase64String(compressedText); |
||
1151 | using (MemoryStream memory = new MemoryStream()) |
||
1152 | { |
||
1153 | int msgLength = BitConverter.ToInt32(compressedBuffer, 0); |
||
1154 | memory.Write(compressedBuffer, 4, compressedBuffer.Length - 4); |
||
1155 | |||
1156 | byte[] buffer = new byte[msgLength]; |
||
1157 | |||
1158 | memory.Position = 0; |
||
1159 | using (GZipStream decompressor = new GZipStream(memory, CompressionMode.Decompress)) |
||
1160 | { |
||
1161 | decompressor.Read(buffer, 0, buffer.Length); |
||
1162 | } |
||
1163 | |||
1164 | return Util.UTF8.GetString(buffer); |
||
1165 | } |
||
1166 | } |
||
1167 | |||
1168 | /// <summary> |
||
1169 | /// Copy data from one stream to another, leaving the read position of both streams at the beginning. |
||
1170 | /// </summary> |
||
1171 | /// <param name='inputStream'> |
||
1172 | /// Input stream. Must be seekable. |
||
1173 | /// </param> |
||
1174 | /// <exception cref='ArgumentException'> |
||
1175 | /// Thrown if the input stream is not seekable. |
||
1176 | /// </exception> |
||
1177 | public static Stream Copy(Stream inputStream) |
||
1178 | { |
||
1179 | if (!inputStream.CanSeek) |
||
1180 | throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek"); |
||
1181 | |||
1182 | const int readSize = 256; |
||
1183 | byte[] buffer = new byte[readSize]; |
||
1184 | MemoryStream ms = new MemoryStream(); |
||
1185 | |||
1186 | int count = inputStream.Read(buffer, 0, readSize); |
||
1187 | |||
1188 | while (count > 0) |
||
1189 | { |
||
1190 | ms.Write(buffer, 0, count); |
||
1191 | count = inputStream.Read(buffer, 0, readSize); |
||
1192 | } |
||
1193 | |||
1194 | ms.Position = 0; |
||
1195 | inputStream.Position = 0; |
||
1196 | |||
1197 | return ms; |
||
1198 | } |
||
1199 | |||
1200 | public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args) |
||
1201 | { |
||
1202 | return SendXmlRpcCommand(url, methodName, args); |
||
1203 | } |
||
1204 | |||
1205 | public static XmlRpcResponse SendXmlRpcCommand(string url, string methodName, object[] args) |
||
1206 | { |
||
1207 | XmlRpcRequest client = new XmlRpcRequest(methodName, args); |
||
1208 | return client.Send(url, 6000); |
||
1209 | } |
||
1210 | |||
1211 | /// <summary> |
||
1212 | /// Returns an error message that the user could not be found in the database |
||
1213 | /// </summary> |
||
1214 | /// <returns>XML string consisting of a error element containing individual error(s)</returns> |
||
1215 | public static XmlRpcResponse CreateUnknownUserErrorResponse() |
||
1216 | { |
||
1217 | XmlRpcResponse response = new XmlRpcResponse(); |
||
1218 | Hashtable responseData = new Hashtable(); |
||
1219 | responseData["error_type"] = "unknown_user"; |
||
1220 | responseData["error_desc"] = "The user requested is not in the database"; |
||
1221 | |||
1222 | response.Value = responseData; |
||
1223 | return response; |
||
1224 | } |
||
1225 | |||
1226 | /// <summary> |
||
1227 | /// Converts a byte array in big endian order into an ulong. |
||
1228 | /// </summary> |
||
1229 | /// <param name="bytes"> |
||
1230 | /// The array of bytes |
||
1231 | /// </param> |
||
1232 | /// <returns> |
||
1233 | /// The extracted ulong |
||
1234 | /// </returns> |
||
1235 | public static ulong BytesToUInt64Big(byte[] bytes) |
||
1236 | { |
||
1237 | if (bytes.Length < 8) return 0; |
||
1238 | return ((ulong)bytes[0] << 56) | ((ulong)bytes[1] << 48) | ((ulong)bytes[2] << 40) | ((ulong)bytes[3] << 32) | |
||
1239 | ((ulong)bytes[4] << 24) | ((ulong)bytes[5] << 16) | ((ulong)bytes[6] << 8) | (ulong)bytes[7]; |
||
1240 | } |
||
1241 | |||
1242 | // used for RemoteParcelRequest (for "About Landmark") |
||
1243 | public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y) |
||
1244 | { |
||
1245 | byte[] bytes = |
||
1246 | { |
||
1247 | (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), |
||
1248 | (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), |
||
1249 | (byte)x, (byte)(x >> 8), 0, 0, |
||
1250 | (byte)y, (byte)(y >> 8), 0, 0 }; |
||
1251 | return new UUID(bytes, 0); |
||
1252 | } |
||
1253 | |||
1254 | public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y, uint z) |
||
1255 | { |
||
1256 | byte[] bytes = |
||
1257 | { |
||
1258 | (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), |
||
1259 | (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), |
||
1260 | (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), |
||
1261 | (byte)y, (byte)(y >> 8), 0, 0 }; |
||
1262 | return new UUID(bytes, 0); |
||
1263 | } |
||
1264 | |||
1265 | public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) |
||
1266 | { |
||
1267 | byte[] bytes = parcelID.GetBytes(); |
||
1268 | regionHandle = Utils.BytesToUInt64(bytes); |
||
1269 | x = Utils.BytesToUInt(bytes, 8) & 0xffff; |
||
1270 | y = Utils.BytesToUInt(bytes, 12) & 0xffff; |
||
1271 | } |
||
1272 | |||
1273 | public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z) |
||
1274 | { |
||
1275 | byte[] bytes = parcelID.GetBytes(); |
||
1276 | regionHandle = Utils.BytesToUInt64(bytes); |
||
1277 | x = Utils.BytesToUInt(bytes, 8) & 0xffff; |
||
1278 | z = (Utils.BytesToUInt(bytes, 8) & 0xffff0000) >> 16; |
||
1279 | y = Utils.BytesToUInt(bytes, 12) & 0xffff; |
||
1280 | } |
||
1281 | |||
1282 | public static void FakeParcelIDToGlobalPosition(UUID parcelID, out uint x, out uint y) |
||
1283 | { |
||
1284 | ulong regionHandle; |
||
1285 | uint rx, ry; |
||
1286 | |||
1287 | ParseFakeParcelID(parcelID, out regionHandle, out x, out y); |
||
1288 | Utils.LongToUInts(regionHandle, out rx, out ry); |
||
1289 | |||
1290 | x += rx; |
||
1291 | y += ry; |
||
1292 | } |
||
1293 | |||
1294 | /// <summary> |
||
1295 | /// Get operating system information if available. Returns only the first 45 characters of information |
||
1296 | /// </summary> |
||
1297 | /// <returns> |
||
1298 | /// Operating system information. Returns an empty string if none was available. |
||
1299 | /// </returns> |
||
1300 | public static string GetOperatingSystemInformation() |
||
1301 | { |
||
1302 | string os = String.Empty; |
||
1303 | |||
1304 | if (Environment.OSVersion.Platform != PlatformID.Unix) |
||
1305 | { |
||
1306 | os = Environment.OSVersion.ToString(); |
||
1307 | } |
||
1308 | else |
||
1309 | { |
||
1310 | os = ReadEtcIssue(); |
||
1311 | } |
||
1312 | |||
1313 | if (os.Length > 45) |
||
1314 | { |
||
1315 | os = os.Substring(0, 45); |
||
1316 | } |
||
1317 | |||
1318 | return os; |
||
1319 | } |
||
1320 | |||
1321 | public static string GetRuntimeInformation() |
||
1322 | { |
||
1323 | string ru = String.Empty; |
||
1324 | |||
1325 | if (Environment.OSVersion.Platform == PlatformID.Unix) |
||
1326 | ru = "Unix/Mono"; |
||
1327 | else |
||
1328 | if (Environment.OSVersion.Platform == PlatformID.MacOSX) |
||
1329 | ru = "OSX/Mono"; |
||
1330 | else |
||
1331 | { |
||
1332 | if (IsPlatformMono) |
||
1333 | ru = "Win/Mono"; |
||
1334 | else |
||
1335 | ru = "Win/.NET"; |
||
1336 | } |
||
1337 | |||
1338 | return ru; |
||
1339 | } |
||
1340 | |||
1341 | /// <summary> |
||
1342 | /// Is the given string a UUID? |
||
1343 | /// </summary> |
||
1344 | /// <param name="s"></param> |
||
1345 | /// <returns></returns> |
||
1346 | public static bool isUUID(string s) |
||
1347 | { |
||
1348 | return UUIDPattern.IsMatch(s); |
||
1349 | } |
||
1350 | |||
1351 | public static string GetDisplayConnectionString(string connectionString) |
||
1352 | { |
||
1353 | int passPosition = 0; |
||
1354 | int passEndPosition = 0; |
||
1355 | string displayConnectionString = null; |
||
1356 | |||
1357 | // hide the password in the connection string |
||
1358 | passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase); |
||
1359 | passPosition = connectionString.IndexOf("=", passPosition); |
||
1360 | if (passPosition < connectionString.Length) |
||
1361 | passPosition += 1; |
||
1362 | passEndPosition = connectionString.IndexOf(";", passPosition); |
||
1363 | |||
1364 | displayConnectionString = connectionString.Substring(0, passPosition); |
||
1365 | displayConnectionString += "***"; |
||
1366 | displayConnectionString += connectionString.Substring(passEndPosition, connectionString.Length - passEndPosition); |
||
1367 | |||
1368 | return displayConnectionString; |
||
1369 | } |
||
1370 | |||
1371 | public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass) |
||
1372 | { |
||
1373 | Type settingsType = settingsClass.GetType(); |
||
1374 | |||
1375 | FieldInfo[] fieldInfos = settingsType.GetFields(); |
||
1376 | foreach (FieldInfo fieldInfo in fieldInfos) |
||
1377 | { |
||
1378 | if (!fieldInfo.IsStatic) |
||
1379 | { |
||
1380 | if (fieldInfo.FieldType == typeof(System.String)) |
||
1381 | { |
||
1382 | fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass))); |
||
1383 | } |
||
1384 | else if (fieldInfo.FieldType == typeof(System.Boolean)) |
||
1385 | { |
||
1386 | fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass))); |
||
1387 | } |
||
1388 | else if (fieldInfo.FieldType == typeof(System.Int32)) |
||
1389 | { |
||
1390 | fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass))); |
||
1391 | } |
||
1392 | else if (fieldInfo.FieldType == typeof(System.Single)) |
||
1393 | { |
||
1394 | fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass))); |
||
1395 | } |
||
1396 | else if (fieldInfo.FieldType == typeof(System.UInt32)) |
||
1397 | { |
||
1398 | fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString()))); |
||
1399 | } |
||
1400 | } |
||
1401 | } |
||
1402 | |||
1403 | PropertyInfo[] propertyInfos = settingsType.GetProperties(); |
||
1404 | foreach (PropertyInfo propInfo in propertyInfos) |
||
1405 | { |
||
1406 | if ((propInfo.CanRead) && (propInfo.CanWrite)) |
||
1407 | { |
||
1408 | if (propInfo.PropertyType == typeof(System.String)) |
||
1409 | { |
||
1410 | propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null); |
||
1411 | } |
||
1412 | else if (propInfo.PropertyType == typeof(System.Boolean)) |
||
1413 | { |
||
1414 | propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null); |
||
1415 | } |
||
1416 | else if (propInfo.PropertyType == typeof(System.Int32)) |
||
1417 | { |
||
1418 | propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null); |
||
1419 | } |
||
1420 | else if (propInfo.PropertyType == typeof(System.Single)) |
||
1421 | { |
||
1422 | propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null); |
||
1423 | } |
||
1424 | if (propInfo.PropertyType == typeof(System.UInt32)) |
||
1425 | { |
||
1426 | propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null); |
||
1427 | } |
||
1428 | } |
||
1429 | } |
||
1430 | |||
1431 | return settingsClass; |
||
1432 | } |
||
1433 | |||
1434 | public static string Base64ToString(string str) |
||
1435 | { |
||
1436 | Decoder utf8Decode = Encoding.UTF8.GetDecoder(); |
||
1437 | |||
1438 | byte[] todecode_byte = Convert.FromBase64String(str); |
||
1439 | int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length); |
||
1440 | char[] decoded_char = new char[charCount]; |
||
1441 | utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0); |
||
1442 | string result = new String(decoded_char); |
||
1443 | return result; |
||
1444 | } |
||
1445 | |||
1446 | public static Guid GetHashGuid(string data, string salt) |
||
1447 | { |
||
1448 | byte[] hash = ComputeMD5Hash(data + salt); |
||
1449 | |||
1450 | //string s = BitConverter.ToString(hash); |
||
1451 | |||
1452 | Guid guid = new Guid(hash); |
||
1453 | |||
1454 | return guid; |
||
1455 | } |
||
1456 | |||
1457 | public static byte ConvertMaturityToAccessLevel(uint maturity) |
||
1458 | { |
||
1459 | byte retVal = 0; |
||
1460 | switch (maturity) |
||
1461 | { |
||
1462 | case 0: //PG |
||
1463 | retVal = 13; |
||
1464 | break; |
||
1465 | case 1: //Mature |
||
1466 | retVal = 21; |
||
1467 | break; |
||
1468 | case 2: // Adult |
||
1469 | retVal = 42; |
||
1470 | break; |
||
1471 | } |
||
1472 | |||
1473 | return retVal; |
||
1474 | |||
1475 | } |
||
1476 | |||
1477 | public static uint ConvertAccessLevelToMaturity(byte maturity) |
||
1478 | { |
||
1479 | if (maturity <= 13) |
||
1480 | return 0; |
||
1481 | else if (maturity <= 21) |
||
1482 | return 1; |
||
1483 | else |
||
1484 | return 2; |
||
1485 | } |
||
1486 | |||
1487 | /// <summary> |
||
1488 | /// Produces an OSDMap from its string representation on a stream |
||
1489 | /// </summary> |
||
1490 | /// <param name="data">The stream</param> |
||
1491 | /// <param name="length">The size of the data on the stream</param> |
||
1492 | /// <returns>The OSDMap or an exception</returns> |
||
1493 | public static OSDMap GetOSDMap(Stream stream, int length) |
||
1494 | { |
||
1495 | byte[] data = new byte[length]; |
||
1496 | stream.Read(data, 0, length); |
||
1497 | string strdata = Util.UTF8.GetString(data); |
||
1498 | OSDMap args = null; |
||
1499 | OSD buffer; |
||
1500 | buffer = OSDParser.DeserializeJson(strdata); |
||
1501 | if (buffer.Type == OSDType.Map) |
||
1502 | { |
||
1503 | args = (OSDMap)buffer; |
||
1504 | return args; |
||
1505 | } |
||
1506 | return null; |
||
1507 | } |
||
1508 | |||
1509 | public static OSDMap GetOSDMap(string data) |
||
1510 | { |
||
1511 | OSDMap args = null; |
||
1512 | try |
||
1513 | { |
||
1514 | OSD buffer; |
||
1515 | // We should pay attention to the content-type, but let's assume we know it's Json |
||
1516 | buffer = OSDParser.DeserializeJson(data); |
||
1517 | if (buffer.Type == OSDType.Map) |
||
1518 | { |
||
1519 | args = (OSDMap)buffer; |
||
1520 | return args; |
||
1521 | } |
||
1522 | else |
||
1523 | { |
||
1524 | // uh? |
||
1525 | m_log.Debug(("[UTILS]: Got OSD of unexpected type " + buffer.Type.ToString())); |
||
1526 | return null; |
||
1527 | } |
||
1528 | } |
||
1529 | catch (Exception ex) |
||
1530 | { |
||
1531 | m_log.Debug("[UTILS]: exception on GetOSDMap " + ex.Message); |
||
1532 | return null; |
||
1533 | } |
||
1534 | } |
||
1535 | |||
1536 | public static string[] Glob(string path) |
||
1537 | { |
||
1538 | string vol=String.Empty; |
||
1539 | |||
1540 | if (Path.VolumeSeparatorChar != Path.DirectorySeparatorChar) |
||
1541 | { |
||
1542 | string[] vcomps = path.Split(new char[] {Path.VolumeSeparatorChar}, 2, StringSplitOptions.RemoveEmptyEntries); |
||
1543 | |||
1544 | if (vcomps.Length > 1) |
||
1545 | { |
||
1546 | path = vcomps[1]; |
||
1547 | vol = vcomps[0]; |
||
1548 | } |
||
1549 | } |
||
1550 | |||
1551 | string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); |
||
1552 | |||
1553 | // Glob |
||
1554 | |||
1555 | path = vol; |
||
1556 | if (vol != String.Empty) |
||
1557 | path += new String(new char[] {Path.VolumeSeparatorChar, Path.DirectorySeparatorChar}); |
||
1558 | else |
||
1559 | path = new String(new char[] {Path.DirectorySeparatorChar}); |
||
1560 | |||
1561 | List<string> paths = new List<string>(); |
||
1562 | List<string> found = new List<string>(); |
||
1563 | paths.Add(path); |
||
1564 | |||
1565 | int compIndex = -1; |
||
1566 | foreach (string c in comps) |
||
1567 | { |
||
1568 | compIndex++; |
||
1569 | |||
1570 | List<string> addpaths = new List<string>(); |
||
1571 | foreach (string p in paths) |
||
1572 | { |
||
1573 | string[] dirs = Directory.GetDirectories(p, c); |
||
1574 | |||
1575 | if (dirs.Length != 0) |
||
1576 | { |
||
1577 | foreach (string dir in dirs) |
||
1578 | addpaths.Add(Path.Combine(path, dir)); |
||
1579 | } |
||
1580 | |||
1581 | // Only add files if that is the last path component |
||
1582 | if (compIndex == comps.Length - 1) |
||
1583 | { |
||
1584 | string[] files = Directory.GetFiles(p, c); |
||
1585 | foreach (string f in files) |
||
1586 | found.Add(f); |
||
1587 | } |
||
1588 | } |
||
1589 | paths = addpaths; |
||
1590 | } |
||
1591 | |||
1592 | return found.ToArray(); |
||
1593 | } |
||
1594 | |||
1595 | public static string ServerURI(string uri) |
||
1596 | { |
||
1597 | if (uri == string.Empty) |
||
1598 | return string.Empty; |
||
1599 | |||
1600 | // Get rid of eventual slashes at the end |
||
1601 | uri = uri.TrimEnd('/'); |
||
1602 | |||
1603 | IPAddress ipaddr1 = null; |
||
1604 | string port1 = ""; |
||
1605 | try |
||
1606 | { |
||
1607 | ipaddr1 = Util.GetHostFromURL(uri); |
||
1608 | } |
||
1609 | catch { } |
||
1610 | |||
1611 | try |
||
1612 | { |
||
1613 | port1 = uri.Split(new char[] { ':' })[2]; |
||
1614 | } |
||
1615 | catch { } |
||
1616 | |||
1617 | // We tried our best to convert the domain names to IP addresses |
||
1618 | return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; |
||
1619 | } |
||
1620 | |||
1621 | /// <summary> |
||
1622 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. |
||
1623 | /// </summary> |
||
1624 | /// <param name="str"> |
||
1625 | /// If null or empty, then an bytes[0] is returned. |
||
1626 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] |
||
1627 | /// </param> |
||
1628 | /// <param name="args"> |
||
1629 | /// Arguments to substitute into the string via the {} mechanism. |
||
1630 | /// </param> |
||
1631 | /// <returns></returns> |
||
1632 | public static byte[] StringToBytes256(string str, params object[] args) |
||
1633 | { |
||
1634 | return StringToBytes256(string.Format(str, args)); |
||
1635 | } |
||
1636 | |||
1637 | /// <summary> |
||
1638 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. |
||
1639 | /// </summary> |
||
1640 | /// <param name="str"> |
||
1641 | /// If null or empty, then an bytes[0] is returned. |
||
1642 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] |
||
1643 | /// </param> |
||
1644 | /// <returns></returns> |
||
1645 | public static byte[] StringToBytes256(string str) |
||
1646 | { |
||
1647 | if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } |
||
1648 | if (str.Length > 254) str = str.Remove(254); |
||
1649 | if (!str.EndsWith("\0")) { str += "\0"; } |
||
1650 | |||
1651 | // Because this is UTF-8 encoding and not ASCII, it's possible we |
||
1652 | // might have gotten an oversized array even after the string trim |
||
1653 | byte[] data = UTF8.GetBytes(str); |
||
1654 | if (data.Length > 256) |
||
1655 | { |
||
1656 | Array.Resize<byte>(ref data, 256); |
||
1657 | data[255] = 0; |
||
1658 | } |
||
1659 | |||
1660 | return data; |
||
1661 | } |
||
1662 | |||
1663 | /// <summary> |
||
1664 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary. |
||
1665 | /// </summary> |
||
1666 | /// <param name="str"> |
||
1667 | /// If null or empty, then an bytes[0] is returned. |
||
1668 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] |
||
1669 | /// </param> |
||
1670 | /// <param name="args"> |
||
1671 | /// Arguments to substitute into the string via the {} mechanism. |
||
1672 | /// </param> |
||
1673 | /// <returns></returns> |
||
1674 | public static byte[] StringToBytes1024(string str, params object[] args) |
||
1675 | { |
||
1676 | return StringToBytes1024(string.Format(str, args)); |
||
1677 | } |
||
1678 | |||
1679 | /// <summary> |
||
1680 | /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary. |
||
1681 | /// </summary> |
||
1682 | /// <param name="str"> |
||
1683 | /// If null or empty, then an bytes[0] is returned. |
||
1684 | /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0] |
||
1685 | /// </param> |
||
1686 | /// <returns></returns> |
||
1687 | public static byte[] StringToBytes1024(string str) |
||
1688 | { |
||
1689 | if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } |
||
1690 | if (str.Length > 1023) str = str.Remove(1023); |
||
1691 | if (!str.EndsWith("\0")) { str += "\0"; } |
||
1692 | |||
1693 | // Because this is UTF-8 encoding and not ASCII, it's possible we |
||
1694 | // might have gotten an oversized array even after the string trim |
||
1695 | byte[] data = UTF8.GetBytes(str); |
||
1696 | if (data.Length > 1024) |
||
1697 | { |
||
1698 | Array.Resize<byte>(ref data, 1024); |
||
1699 | data[1023] = 0; |
||
1700 | } |
||
1701 | |||
1702 | return data; |
||
1703 | } |
||
1704 | |||
1705 | /// <summary> |
||
1706 | /// Used to trigger an early library load on Windows systems. |
||
1707 | /// </summary> |
||
1708 | /// <remarks> |
||
1709 | /// Required to get 32-bit and 64-bit processes to automatically use the |
||
1710 | /// appropriate native library. |
||
1711 | /// </remarks> |
||
1712 | /// <param name="dllToLoad"></param> |
||
1713 | /// <returns></returns> |
||
1714 | [DllImport("kernel32.dll")] |
||
1715 | public static extern IntPtr LoadLibrary(string dllToLoad); |
||
1716 | |||
1717 | /// <summary> |
||
1718 | /// Determine whether the current process is 64 bit |
||
1719 | /// </summary> |
||
1720 | /// <returns>true if so, false if not</returns> |
||
1721 | public static bool Is64BitProcess() |
||
1722 | { |
||
1723 | return IntPtr.Size == 8; |
||
1724 | } |
||
1725 | |||
1726 | #region FireAndForget Threading Pattern |
||
1727 | |||
1728 | /// <summary> |
||
1729 | /// Created to work around a limitation in Mono with nested delegates |
||
1730 | /// </summary> |
||
1731 | private sealed class FireAndForgetWrapper |
||
1732 | { |
||
1733 | private static volatile FireAndForgetWrapper instance; |
||
1734 | private static object syncRoot = new Object(); |
||
1735 | |||
1736 | public static FireAndForgetWrapper Instance { |
||
1737 | get { |
||
1738 | |||
1739 | if (instance == null) |
||
1740 | { |
||
1741 | lock (syncRoot) |
||
1742 | { |
||
1743 | if (instance == null) |
||
1744 | { |
||
1745 | instance = new FireAndForgetWrapper(); |
||
1746 | } |
||
1747 | } |
||
1748 | } |
||
1749 | |||
1750 | return instance; |
||
1751 | } |
||
1752 | } |
||
1753 | |||
1754 | public void FireAndForget(System.Threading.WaitCallback callback) |
||
1755 | { |
||
1756 | callback.BeginInvoke(null, EndFireAndForget, callback); |
||
1757 | } |
||
1758 | |||
1759 | public void FireAndForget(System.Threading.WaitCallback callback, object obj) |
||
1760 | { |
||
1761 | callback.BeginInvoke(obj, EndFireAndForget, callback); |
||
1762 | } |
||
1763 | |||
1764 | private static void EndFireAndForget(IAsyncResult ar) |
||
1765 | { |
||
1766 | System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; |
||
1767 | |||
1768 | try { callback.EndInvoke(ar); } |
||
1769 | catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } |
||
1770 | |||
1771 | ar.AsyncWaitHandle.Close(); |
||
1772 | } |
||
1773 | } |
||
1774 | |||
1775 | public static void FireAndForget(System.Threading.WaitCallback callback) |
||
1776 | { |
||
1777 | FireAndForget(callback, null); |
||
1778 | } |
||
1779 | |||
1780 | public static void InitThreadPool(int minThreads, int maxThreads) |
||
1781 | { |
||
1782 | if (maxThreads < 2) |
||
1783 | throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); |
||
1784 | if (minThreads > maxThreads || minThreads < 2) |
||
1785 | throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); |
||
1786 | if (m_ThreadPool != null) |
||
1787 | throw new InvalidOperationException("SmartThreadPool is already initialized"); |
||
1788 | |||
1789 | STPStartInfo startInfo = new STPStartInfo(); |
||
1790 | startInfo.ThreadPoolName = "Util"; |
||
1791 | startInfo.IdleTimeout = 2000; |
||
1792 | startInfo.MaxWorkerThreads = maxThreads; |
||
1793 | startInfo.MinWorkerThreads = minThreads; |
||
1794 | |||
1795 | m_ThreadPool = new SmartThreadPool(startInfo); |
||
1796 | } |
||
1797 | |||
1798 | public static int FireAndForgetCount() |
||
1799 | { |
||
1800 | const int MAX_SYSTEM_THREADS = 200; |
||
1801 | |||
1802 | switch (FireAndForgetMethod) |
||
1803 | { |
||
1804 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: |
||
1805 | case FireAndForgetMethod.QueueUserWorkItem: |
||
1806 | case FireAndForgetMethod.BeginInvoke: |
||
1807 | int workerThreads, iocpThreads; |
||
1808 | ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); |
||
1809 | return workerThreads; |
||
1810 | case FireAndForgetMethod.SmartThreadPool: |
||
1811 | return m_ThreadPool.MaxThreads - m_ThreadPool.InUseThreads; |
||
1812 | case FireAndForgetMethod.Thread: |
||
1813 | return MAX_SYSTEM_THREADS - System.Diagnostics.Process.GetCurrentProcess().Threads.Count; |
||
1814 | default: |
||
1815 | throw new NotImplementedException(); |
||
1816 | } |
||
1817 | } |
||
1818 | |||
1819 | public static void FireAndForget(System.Threading.WaitCallback callback, object obj) |
||
1820 | { |
||
1821 | WaitCallback realCallback; |
||
1822 | |||
1823 | if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) |
||
1824 | { |
||
1825 | // If we're running regression tests, then we want any exceptions to rise up to the test code. |
||
1826 | realCallback = o => { Culture.SetCurrentCulture(); callback(o); }; |
||
1827 | } |
||
1828 | else |
||
1829 | { |
||
1830 | // When OpenSim interacts with a database or sends data over the wire, it must send this in en_US culture |
||
1831 | // so that we don't encounter problems where, for instance, data is saved with a culture that uses commas |
||
1832 | // for decimals places but is read by a culture that treats commas as number seperators. |
||
1833 | realCallback = o => |
||
1834 | { |
||
1835 | Culture.SetCurrentCulture(); |
||
1836 | |||
1837 | try |
||
1838 | { |
||
1839 | callback(o); |
||
1840 | } |
||
1841 | catch (Exception e) |
||
1842 | { |
||
1843 | m_log.ErrorFormat( |
||
1844 | "[UTIL]: Continuing after async_call_method thread terminated with exception {0}{1}", |
||
1845 | e.Message, e.StackTrace); |
||
1846 | } |
||
1847 | }; |
||
1848 | } |
||
1849 | |||
1850 | switch (FireAndForgetMethod) |
||
1851 | { |
||
1852 | case FireAndForgetMethod.RegressionTest: |
||
1853 | case FireAndForgetMethod.None: |
||
1854 | realCallback.Invoke(obj); |
||
1855 | break; |
||
1856 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: |
||
1857 | ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); |
||
1858 | break; |
||
1859 | case FireAndForgetMethod.QueueUserWorkItem: |
||
1860 | ThreadPool.QueueUserWorkItem(realCallback, obj); |
||
1861 | break; |
||
1862 | case FireAndForgetMethod.BeginInvoke: |
||
1863 | FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; |
||
1864 | wrapper.FireAndForget(realCallback, obj); |
||
1865 | break; |
||
1866 | case FireAndForgetMethod.SmartThreadPool: |
||
1867 | if (m_ThreadPool == null) |
||
1868 | InitThreadPool(2, 15); |
||
1869 | m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); |
||
1870 | break; |
||
1871 | case FireAndForgetMethod.Thread: |
||
1872 | Thread thread = new Thread(delegate(object o) { realCallback(o); }); |
||
1873 | thread.Start(obj); |
||
1874 | break; |
||
1875 | default: |
||
1876 | throw new NotImplementedException(); |
||
1877 | } |
||
1878 | } |
||
1879 | |||
1880 | /// <summary> |
||
1881 | /// Get information about the current state of the smart thread pool. |
||
1882 | /// </summary> |
||
1883 | /// <returns> |
||
1884 | /// null if this isn't the pool being used for non-scriptengine threads. |
||
1885 | /// </returns> |
||
1886 | public static STPInfo GetSmartThreadPoolInfo() |
||
1887 | { |
||
1888 | if (m_ThreadPool == null) |
||
1889 | return null; |
||
1890 | |||
1891 | STPInfo stpi = new STPInfo(); |
||
1892 | stpi.Name = m_ThreadPool.Name; |
||
1893 | stpi.STPStartInfo = m_ThreadPool.STPStartInfo; |
||
1894 | stpi.IsIdle = m_ThreadPool.IsIdle; |
||
1895 | stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown; |
||
1896 | stpi.MaxThreads = m_ThreadPool.MaxThreads; |
||
1897 | stpi.MinThreads = m_ThreadPool.MinThreads; |
||
1898 | stpi.InUseThreads = m_ThreadPool.InUseThreads; |
||
1899 | stpi.ActiveThreads = m_ThreadPool.ActiveThreads; |
||
1900 | stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks; |
||
1901 | stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency; |
||
1902 | |||
1903 | return stpi; |
||
1904 | } |
||
1905 | |||
1906 | #endregion FireAndForget Threading Pattern |
||
1907 | |||
1908 | /// <summary> |
||
1909 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive |
||
1910 | /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap |
||
1911 | /// for the callers. |
||
1912 | /// This trims it to a 12 day interval so don't let your frame time get too long. |
||
1913 | /// </summary> |
||
1914 | /// <returns></returns> |
||
1915 | public static Int32 EnvironmentTickCount() |
||
1916 | { |
||
1917 | return Environment.TickCount & EnvironmentTickCountMask; |
||
1918 | } |
||
1919 | const Int32 EnvironmentTickCountMask = 0x3fffffff; |
||
1920 | |||
1921 | /// <summary> |
||
1922 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive |
||
1923 | /// and negative every 24.9 days. Subtracts the passed value (previously fetched by |
||
1924 | /// 'EnvironmentTickCount()') and accounts for any wrapping. |
||
1925 | /// </summary> |
||
1926 | /// <param name="newValue"></param> |
||
1927 | /// <param name="prevValue"></param> |
||
1928 | /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns> |
||
1929 | public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue) |
||
1930 | { |
||
1931 | Int32 diff = newValue - prevValue; |
||
1932 | return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); |
||
1933 | } |
||
1934 | |||
1935 | /// <summary> |
||
1936 | /// Environment.TickCount is an int but it counts all 32 bits so it goes positive |
||
1937 | /// and negative every 24.9 days. Subtracts the passed value (previously fetched by |
||
1938 | /// 'EnvironmentTickCount()') and accounts for any wrapping. |
||
1939 | /// </summary> |
||
1940 | /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns> |
||
1941 | public static Int32 EnvironmentTickCountSubtract(Int32 prevValue) |
||
1942 | { |
||
1943 | return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue); |
||
1944 | } |
||
1945 | |||
1946 | // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount |
||
1947 | // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount(). |
||
1948 | // A positive return value indicates A occured later than B |
||
1949 | public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB) |
||
1950 | { |
||
1951 | // A, B and TC are all between 0 and 0x3fffffff |
||
1952 | int tc = EnvironmentTickCount(); |
||
1953 | |||
1954 | if (tc - tcA >= 0) |
||
1955 | tcA += EnvironmentTickCountMask + 1; |
||
1956 | |||
1957 | if (tc - tcB >= 0) |
||
1958 | tcB += EnvironmentTickCountMask + 1; |
||
1959 | |||
1960 | return tcA - tcB; |
||
1961 | } |
||
1962 | |||
1963 | /// <summary> |
||
1964 | /// Prints the call stack at any given point. Useful for debugging. |
||
1965 | /// </summary> |
||
1966 | public static void PrintCallStack() |
||
1967 | { |
||
1968 | PrintCallStack(m_log.DebugFormat); |
||
1969 | } |
||
1970 | |||
1971 | public delegate void DebugPrinter(string msg, params Object[] parm); |
||
1972 | public static void PrintCallStack(DebugPrinter printer) |
||
1973 | { |
||
1974 | StackTrace stackTrace = new StackTrace(true); // get call stack |
||
1975 | StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) |
||
1976 | |||
1977 | // write call stack method names |
||
1978 | foreach (StackFrame stackFrame in stackFrames) |
||
1979 | { |
||
1980 | MethodBase mb = stackFrame.GetMethod(); |
||
1981 | printer("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name |
||
1982 | } |
||
1983 | } |
||
1984 | |||
1985 | /// <summary> |
||
1986 | /// Gets the client IP address |
||
1987 | /// </summary> |
||
1988 | /// <param name="xff"></param> |
||
1989 | /// <returns></returns> |
||
1990 | public static IPEndPoint GetClientIPFromXFF(string xff) |
||
1991 | { |
||
1992 | if (xff == string.Empty) |
||
1993 | return null; |
||
1994 | |||
1995 | string[] parts = xff.Split(new char[] { ',' }); |
||
1996 | if (parts.Length > 0) |
||
1997 | { |
||
1998 | try |
||
1999 | { |
||
2000 | return new IPEndPoint(IPAddress.Parse(parts[0]), 0); |
||
2001 | } |
||
2002 | catch (Exception e) |
||
2003 | { |
||
2004 | m_log.WarnFormat("[UTIL]: Exception parsing XFF header {0}: {1}", xff, e.Message); |
||
2005 | } |
||
2006 | } |
||
2007 | |||
2008 | return null; |
||
2009 | } |
||
2010 | |||
2011 | public static string GetCallerIP(Hashtable req) |
||
2012 | { |
||
2013 | if (req.ContainsKey("headers")) |
||
2014 | { |
||
2015 | try |
||
2016 | { |
||
2017 | Hashtable headers = (Hashtable)req["headers"]; |
||
2018 | if (headers.ContainsKey("remote_addr") && headers["remote_addr"] != null) |
||
2019 | return headers["remote_addr"].ToString(); |
||
2020 | } |
||
2021 | catch (Exception e) |
||
2022 | { |
||
2023 | m_log.WarnFormat("[UTIL]: exception in GetCallerIP: {0}", e.Message); |
||
2024 | } |
||
2025 | } |
||
2026 | return string.Empty; |
||
2027 | } |
||
2028 | |||
2029 | #region Xml Serialization Utilities |
||
2030 | public static bool ReadBoolean(XmlTextReader reader) |
||
2031 | { |
||
2032 | reader.ReadStartElement(); |
||
2033 | bool result = Boolean.Parse(reader.ReadContentAsString().ToLower()); |
||
2034 | reader.ReadEndElement(); |
||
2035 | |||
2036 | return result; |
||
2037 | } |
||
2038 | |||
2039 | public static UUID ReadUUID(XmlTextReader reader, string name) |
||
2040 | { |
||
2041 | UUID id; |
||
2042 | string idStr; |
||
2043 | |||
2044 | reader.ReadStartElement(name); |
||
2045 | |||
2046 | if (reader.Name == "Guid") |
||
2047 | idStr = reader.ReadElementString("Guid"); |
||
2048 | else if (reader.Name == "UUID") |
||
2049 | idStr = reader.ReadElementString("UUID"); |
||
2050 | else // no leading tag |
||
2051 | idStr = reader.ReadContentAsString(); |
||
2052 | UUID.TryParse(idStr, out id); |
||
2053 | reader.ReadEndElement(); |
||
2054 | |||
2055 | return id; |
||
2056 | } |
||
2057 | |||
2058 | public static Vector3 ReadVector(XmlTextReader reader, string name) |
||
2059 | { |
||
2060 | Vector3 vec; |
||
2061 | |||
2062 | reader.ReadStartElement(name); |
||
2063 | vec.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // X or x |
||
2064 | vec.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Y or y |
||
2065 | vec.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Z or z |
||
2066 | reader.ReadEndElement(); |
||
2067 | |||
2068 | return vec; |
||
2069 | } |
||
2070 | |||
2071 | public static Quaternion ReadQuaternion(XmlTextReader reader, string name) |
||
2072 | { |
||
2073 | Quaternion quat = new Quaternion(); |
||
2074 | |||
2075 | reader.ReadStartElement(name); |
||
2076 | while (reader.NodeType != XmlNodeType.EndElement) |
||
2077 | { |
||
2078 | switch (reader.Name.ToLower()) |
||
2079 | { |
||
2080 | case "x": |
||
2081 | quat.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); |
||
2082 | break; |
||
2083 | case "y": |
||
2084 | quat.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); |
||
2085 | break; |
||
2086 | case "z": |
||
2087 | quat.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); |
||
2088 | break; |
||
2089 | case "w": |
||
2090 | quat.W = reader.ReadElementContentAsFloat(reader.Name, String.Empty); |
||
2091 | break; |
||
2092 | } |
||
2093 | } |
||
2094 | |||
2095 | reader.ReadEndElement(); |
||
2096 | |||
2097 | return quat; |
||
2098 | } |
||
2099 | |||
2100 | public static T ReadEnum<T>(XmlTextReader reader, string name) |
||
2101 | { |
||
2102 | string value = reader.ReadElementContentAsString(name, String.Empty); |
||
2103 | // !!!!! to deal with flags without commas |
||
2104 | if (value.Contains(" ") && !value.Contains(",")) |
||
2105 | value = value.Replace(" ", ", "); |
||
2106 | |||
2107 | return (T)Enum.Parse(typeof(T), value); ; |
||
2108 | } |
||
2109 | #endregion |
||
2110 | |||
2111 | #region Universal User Identifiers |
||
2112 | /// <summary> |
||
2113 | /// </summary> |
||
2114 | /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param> |
||
2115 | /// <param name="uuid">the uuid part</param> |
||
2116 | /// <param name="url">the endpoint part (e.g. http://foo.com)</param> |
||
2117 | /// <param name="firstname">the first name part (e.g. Test)</param> |
||
2118 | /// <param name="lastname">the last name part (e.g User)</param> |
||
2119 | /// <param name="secret">the secret part</param> |
||
2120 | public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) |
||
2121 | { |
||
2122 | uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty; |
||
2123 | |||
2124 | string[] parts = value.Split(';'); |
||
2125 | if (parts.Length >= 1) |
||
2126 | if (!UUID.TryParse(parts[0], out uuid)) |
||
2127 | return false; |
||
2128 | |||
2129 | if (parts.Length >= 2) |
||
2130 | url = parts[1]; |
||
2131 | |||
2132 | if (parts.Length >= 3) |
||
2133 | { |
||
2134 | string[] name = parts[2].Split(); |
||
2135 | if (name.Length == 2) |
||
2136 | { |
||
2137 | firstname = name[0]; |
||
2138 | lastname = name[1]; |
||
2139 | } |
||
2140 | } |
||
2141 | if (parts.Length >= 4) |
||
2142 | secret = parts[3]; |
||
2143 | |||
2144 | return true; |
||
2145 | } |
||
2146 | |||
2147 | /// <summary> |
||
2148 | /// Produces a universal (HG) system-facing identifier given the information |
||
2149 | /// </summary> |
||
2150 | /// <param name="acircuit"></param> |
||
2151 | /// <returns>uuid[;homeURI[;first last]]</returns> |
||
2152 | public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit) |
||
2153 | { |
||
2154 | if (acircuit.ServiceURLs.ContainsKey("HomeURI")) |
||
2155 | return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString()); |
||
2156 | else |
||
2157 | return acircuit.AgentID.ToString(); |
||
2158 | } |
||
2159 | |||
2160 | /// <summary> |
||
2161 | /// Produces a universal (HG) system-facing identifier given the information |
||
2162 | /// </summary> |
||
2163 | /// <param name="id">UUID of the user</param> |
||
2164 | /// <param name="firstName">first name (e.g Test)</param> |
||
2165 | /// <param name="lastName">last name (e.g. User)</param> |
||
2166 | /// <param name="homeURI">homeURI (e.g. http://foo.com)</param> |
||
2167 | /// <returns>a string of the form uuid[;homeURI[;first last]]</returns> |
||
2168 | public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI) |
||
2169 | { |
||
2170 | string agentsURI = homeURI; |
||
2171 | if (!agentsURI.EndsWith("/")) |
||
2172 | agentsURI += "/"; |
||
2173 | |||
2174 | // This is ugly, but there's no other way, given that the name is changed |
||
2175 | // in the agent circuit data for foreigners |
||
2176 | if (lastName.Contains("@")) |
||
2177 | { |
||
2178 | string[] parts = firstName.Split(new char[] { '.' }); |
||
2179 | if (parts.Length == 2) |
||
2180 | return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; |
||
2181 | } |
||
2182 | return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName; |
||
2183 | |||
2184 | } |
||
2185 | |||
2186 | /// <summary> |
||
2187 | /// Produces a universal (HG) user-facing name given the information |
||
2188 | /// </summary> |
||
2189 | /// <param name="firstName"></param> |
||
2190 | /// <param name="lastName"></param> |
||
2191 | /// <param name="homeURI"></param> |
||
2192 | /// <returns>string of the form first.last @foo.com or first last</returns> |
||
2193 | public static string UniversalName(String firstName, String lastName, String homeURI) |
||
2194 | { |
||
2195 | Uri uri = null; |
||
2196 | try |
||
2197 | { |
||
2198 | uri = new Uri(homeURI); |
||
2199 | } |
||
2200 | catch (UriFormatException) |
||
2201 | { |
||
2202 | return firstName + " " + lastName; |
||
2203 | } |
||
2204 | return firstName + "." + lastName + " " + "@" + uri.Authority; |
||
2205 | } |
||
2206 | #endregion |
||
2207 | |||
2208 | /// <summary> |
||
2209 | /// Escapes the special characters used in "LIKE". |
||
2210 | /// </summary> |
||
2211 | /// <remarks> |
||
2212 | /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz" |
||
2213 | /// </remarks> |
||
2214 | public static string EscapeForLike(string str) |
||
2215 | { |
||
2216 | return str.Replace("_", "\\_").Replace("%", "\\%"); |
||
2217 | } |
||
2218 | } |
||
2219 | |||
2220 | public class DoubleQueue<T> where T:class |
||
2221 | { |
||
2222 | private Queue<T> m_lowQueue = new Queue<T>(); |
||
2223 | private Queue<T> m_highQueue = new Queue<T>(); |
||
2224 | |||
2225 | private object m_syncRoot = new object(); |
||
2226 | private Semaphore m_s = new Semaphore(0, 1); |
||
2227 | |||
2228 | public DoubleQueue() |
||
2229 | { |
||
2230 | } |
||
2231 | |||
2232 | public virtual int Count |
||
2233 | { |
||
2234 | get { return m_highQueue.Count + m_lowQueue.Count; } |
||
2235 | } |
||
2236 | |||
2237 | public virtual void Enqueue(T data) |
||
2238 | { |
||
2239 | Enqueue(m_lowQueue, data); |
||
2240 | } |
||
2241 | |||
2242 | public virtual void EnqueueLow(T data) |
||
2243 | { |
||
2244 | Enqueue(m_lowQueue, data); |
||
2245 | } |
||
2246 | |||
2247 | public virtual void EnqueueHigh(T data) |
||
2248 | { |
||
2249 | Enqueue(m_highQueue, data); |
||
2250 | } |
||
2251 | |||
2252 | private void Enqueue(Queue<T> q, T data) |
||
2253 | { |
||
2254 | lock (m_syncRoot) |
||
2255 | { |
||
2256 | q.Enqueue(data); |
||
2257 | m_s.WaitOne(0); |
||
2258 | m_s.Release(); |
||
2259 | } |
||
2260 | } |
||
2261 | |||
2262 | public virtual T Dequeue() |
||
2263 | { |
||
2264 | return Dequeue(Timeout.Infinite); |
||
2265 | } |
||
2266 | |||
2267 | public virtual T Dequeue(int tmo) |
||
2268 | { |
||
2269 | return Dequeue(TimeSpan.FromMilliseconds(tmo)); |
||
2270 | } |
||
2271 | |||
2272 | public virtual T Dequeue(TimeSpan wait) |
||
2273 | { |
||
2274 | T res = null; |
||
2275 | |||
2276 | if (!Dequeue(wait, ref res)) |
||
2277 | return null; |
||
2278 | |||
2279 | return res; |
||
2280 | } |
||
2281 | |||
2282 | public bool Dequeue(int timeout, ref T res) |
||
2283 | { |
||
2284 | return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res); |
||
2285 | } |
||
2286 | |||
2287 | public bool Dequeue(TimeSpan wait, ref T res) |
||
2288 | { |
||
2289 | if (!m_s.WaitOne(wait)) |
||
2290 | return false; |
||
2291 | |||
2292 | lock (m_syncRoot) |
||
2293 | { |
||
2294 | if (m_highQueue.Count > 0) |
||
2295 | res = m_highQueue.Dequeue(); |
||
2296 | else if (m_lowQueue.Count > 0) |
||
2297 | res = m_lowQueue.Dequeue(); |
||
2298 | |||
2299 | if (m_highQueue.Count == 0 && m_lowQueue.Count == 0) |
||
2300 | return true; |
||
2301 | |||
2302 | try |
||
2303 | { |
||
2304 | m_s.Release(); |
||
2305 | } |
||
2306 | catch |
||
2307 | { |
||
2308 | } |
||
2309 | |||
2310 | return true; |
||
2311 | } |
||
2312 | } |
||
2313 | |||
2314 | public virtual void Clear() |
||
2315 | { |
||
2316 | |||
2317 | lock (m_syncRoot) |
||
2318 | { |
||
2319 | // Make sure sem count is 0 |
||
2320 | m_s.WaitOne(0); |
||
2321 | |||
2322 | m_lowQueue.Clear(); |
||
2323 | m_highQueue.Clear(); |
||
2324 | } |
||
2325 | } |
||
2326 | } |
||
2327 | } |